Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom C/C++ kernel demo #2983

Merged
merged 49 commits into from
Jan 21, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
05f964f
Add start of demo for custom kernel in C++.
jhale Jan 11, 2024
55cf9db
Form with C++ kernel.
jhale Jan 12, 2024
320f26e
Few more additions.
jhale Jan 12, 2024
f67ce51
clang-format
jhale Jan 12, 2024
1e5c375
Issue with missing sparsity pattern entries upon assembly.
jhale Jan 12, 2024
d7a10cf
Fix sparsity pattern build.
jhale Jan 12, 2024
d51bf7e
Copy and paste FFCx kernel, could do something nicer in C++.
jhale Jan 12, 2024
22f0bcd
Merge branch 'main' into jhale/custom-cell-kernel-cpp
jhale Jan 16, 2024
7b83f85
More template type deduction.
jhale Jan 16, 2024
73a14d7
More use of generic types.
jhale Jan 16, 2024
1f5b1b8
Some work towards using Basix.
jhale Jan 16, 2024
9b8348a
More const.
jhale Jan 16, 2024
1061437
Merge remote-tracking branch 'origin/main' into jhale/custom-cell-ker…
jhale Jan 16, 2024
41375a4
Basix for quadrature.
jhale Jan 16, 2024
0ece7ef
Cannot get e.tabulate call signature correct.
jhale Jan 16, 2024
01f26c8
Tidy. Still not working.
jhale Jan 17, 2024
4eab682
Fix.
jhale Jan 17, 2024
0db48b2
Still needs scaling by determinant of Jacobian.
jhale Jan 17, 2024
82b3999
Fix.
jhale Jan 17, 2024
7b3e588
Add note.
jhale Jan 17, 2024
0a52367
Add Jacobian determinant computation.
jhale Jan 18, 2024
dede3d3
Generalise.
jhale Jan 18, 2024
6901321
Match with Python implementation using codegen.
jhale Jan 18, 2024
aea1e36
Tidy.
jhale Jan 18, 2024
c024486
Merge branch 'main' into jhale/custom-cell-kernel-cpp
jhale Jan 18, 2024
b1f1c9a
Response to basic feedback.
jhale Jan 18, 2024
44ea39c
Few more small tweaks.
jhale Jan 18, 2024
19c0841
Tweaks.
jhale Jan 18, 2024
9af8b84
Remove another const.
jhale Jan 18, 2024
09c449d
Merge branch 'main' into jhale/custom-cell-kernel-cpp
garth-wells Jan 18, 2024
9c81aec
Add raw assembler to demo.
garth-wells Jan 18, 2024
dda707f
Modularise with functions
garth-wells Jan 18, 2024
a66f011
Add RHS
garth-wells Jan 18, 2024
c65ddb6
Get RHS working
garth-wells Jan 19, 2024
2babb9f
Merge branch 'main' into jhale/custom-cell-kernel-cpp
garth-wells Jan 19, 2024
530ea0d
Tidy up
garth-wells Jan 19, 2024
a13b8f6
Simplify Form domain ids
garth-wells Jan 20, 2024
0a8e98f
Undo so changes
garth-wells Jan 20, 2024
a550fac
Simplify
garth-wells Jan 20, 2024
1c79fc6
Bring back simplifications
garth-wells Jan 20, 2024
bae9bcd
Tidy up
garth-wells Jan 20, 2024
f981578
Doxygen fix
garth-wells Jan 20, 2024
16dc137
Small optimisation
garth-wells Jan 20, 2024
5634d63
Tidy up
garth-wells Jan 20, 2024
be82c95
Sort imports
garth-wells Jan 20, 2024
232b019
Name update
garth-wells Jan 20, 2024
bff5b44
Merge branch 'main' into jhale/custom-cell-kernel-cpp
garth-wells Jan 20, 2024
3eebd0d
Re-run cmake create script
garth-wells Jan 20, 2024
73710d0
Update demo doc
garth-wells Jan 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions cpp/demo/custom_cell_kernel/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# This file was generated by running
#
# python cmake/scripts/generate-cmakefiles from dolfinx/cpp
#
cmake_minimum_required(VERSION 3.19)

set(PROJECT_NAME demo_custom_cell_kernel)
project(${PROJECT_NAME} LANGUAGES C CXX)

# Set C++20 standard
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

if(NOT TARGET dolfinx)
find_package(DOLFINX REQUIRED)
endif()

#add_custom_command(
# OUTPUT poisson.c
# COMMAND ffcx ${CMAKE_CURRENT_SOURCE_DIR}/poisson.py ${SCALAR_TYPE}
# VERBATIM
# DEPENDS poisson.py
# COMMENT "Compile poisson.py using FFCx"
#)
jhale marked this conversation as resolved.
Show resolved Hide resolved

set(CMAKE_INCLUDE_CURRENT_DIR ON)

add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} dolfinx)

# Do not throw error for 'multi-line comments' (these are typical in rst which
# includes LaTeX)
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("-Wno-comment" HAVE_NO_MULTLINE)
set_source_files_properties(
main.cpp
PROPERTIES
COMPILE_FLAGS
"$<$<BOOL:${HAVE_NO_MULTLINE}>:-Wno-comment -Wall -Wextra -pedantic -Werror>"
)

# Test targets (used by DOLFINx testing system)
set(TEST_PARAMETERS2 -np 2 ${MPIEXEC_PARAMS} "./${PROJECT_NAME}")
set(TEST_PARAMETERS3 -np 3 ${MPIEXEC_PARAMS} "./${PROJECT_NAME}")
add_test(NAME ${PROJECT_NAME}_mpi_2 COMMAND "mpirun" ${TEST_PARAMETERS2})
add_test(NAME ${PROJECT_NAME}_mpi_3 COMMAND "mpirun" ${TEST_PARAMETERS3})
add_test(NAME ${PROJECT_NAME}_serial COMMAND ${PROJECT_NAME})
125 changes: 125 additions & 0 deletions cpp/demo/custom_cell_kernel/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Custom cell kernel (C++)
// .. code-block:: cpp

#include <basix/finite-element.h>
#include <cmath>
#include <dolfinx.h>
#include <dolfinx/la/MatrixCSR.h>
#include <dolfinx/la/SparsityPattern.h>
#include <utility>
#include <vector>

using namespace dolfinx;

using T = double;
using U = typename dolfinx::scalar_value_type_t<T>;

// .. code-block:: cpp

int main(int argc, char* argv[])
{
dolfinx::init_logging(argc, argv);
MPI_Init(&argc, &argv);
{
// Create mesh and function space
auto part = mesh::create_cell_partitioner(mesh::GhostMode::shared_facet);
auto mesh = std::make_shared<mesh::Mesh<U>>(
mesh::create_rectangle<U>(MPI_COMM_WORLD, {{{0.0, 0.0}, {2.0, 1.0}}},
{32, 16}, mesh::CellType::triangle, part));

// Would it be possible just to define instead of (T, V, L) (L) alone?
basix::FiniteElement e = basix::create_element<U>(
basix::element::family::P,
mesh::cell_type_to_basix_type(mesh::CellType::triangle), 1,
basix::element::lagrange_variant::unset,
basix::element::dpc_variant::unset, false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe Basix should use an enum for the last arg - booleans are hard to understand.


// Create a scalar function space
auto V = std::make_shared<fem::FunctionSpace<U>>(
fem::create_functionspace(mesh, e));

// Create default domain integral on all local cells
std::int32_t size_local
= mesh->topology()->index_map(mesh->topology()->dim())->size_local();
std::vector<std::int32_t> cells(size_local);
std::iota(cells.begin(), cells.end(), 0);

// Define element kernel
auto mass_cell_kernel = [](double* A, const double*, const double*,
const double* coordinate_dofs, const int*,
const u_int8_t*) { // Quadrature rules
static const double weights_48e[3]
= {0.1666666666666667, 0.1666666666666667, 0.1666666666666667};
// Precomputed values of basis functions and precomputations
// FE* dimensions: [permutation][entities][points][dofs]
static const double FE0_C0_Q48e[1][1][3][3]
= {{{{0.6666666666666667, 0.1666666666666667, 0.1666666666666666},
{0.1666666666666667, 0.1666666666666666, 0.6666666666666666},
{0.1666666666666668, 0.6666666666666666, 0.1666666666666666}}}};
static const double FE1_C0_D10_Q48e[1][1][1][3] = {{{{-1.0, 1.0, 0.0}}}};
static const double FE1_C1_D01_Q48e[1][1][1][3] = {{{{-1.0, 0.0, 1.0}}}};
// Quadrature loop independent computations for quadrature rule 48e
double J_c0 = 0.0;
double J_c3 = 0.0;
double J_c1 = 0.0;
double J_c2 = 0.0;
for (int ic = 0; ic < 3; ++ic)
{
J_c0 += coordinate_dofs[(ic) * 3] * FE1_C0_D10_Q48e[0][0][0][ic];
J_c3 += coordinate_dofs[(ic) * 3 + 1] * FE1_C1_D01_Q48e[0][0][0][ic];
J_c1 += coordinate_dofs[(ic) * 3] * FE1_C1_D01_Q48e[0][0][0][ic];
J_c2 += coordinate_dofs[(ic) * 3 + 1] * FE1_C0_D10_Q48e[0][0][0][ic];
}
double sp_48e[4];
sp_48e[0] = J_c0 * J_c3;
sp_48e[1] = J_c1 * J_c2;
sp_48e[2] = sp_48e[0] - sp_48e[1];
sp_48e[3] = fabs(sp_48e[2]);
for (int iq = 0; iq < 3; ++iq)
{
double fw0 = sp_48e[3] * weights_48e[iq];
double t0[3];
for (int i = 0; i < 3; ++i)
{
t0[i] = fw0 * FE0_C0_Q48e[0][0][iq][i];
}
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 3; ++j)
{
A[3 * (i) + (j)] += FE0_C0_Q48e[0][0][iq][j] * t0[i];
}
}
}
};
// More automatic type inference?
const std::map<
fem::IntegralType,
std::vector<std::tuple<
std::int32_t,
std::function<void(double*, const double*, const double*,
const double*, const int*, const u_int8_t*)>,
std::vector<std::int32_t>>>>
integrals
= {{fem::IntegralType::cell, {{-1, mass_cell_kernel, cells}}}};

// Define form from integral
// NOTE: Cannot be done through create_form which is recommended in docs.
auto a = std::make_shared<fem::Form<T>>(
fem::Form<T>({V, V}, integrals, {}, {}, false, mesh));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not clear what false is - should we make it an enum?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mscroggs What do you think?


auto sparsity = la::SparsityPattern(
MPI_COMM_WORLD, {V->dofmap()->index_map, V->dofmap()->index_map},
{V->dofmap()->index_map_bs(), V->dofmap()->index_map_bs()});
fem::sparsitybuild::cells(sparsity, cells, {*V->dofmap(), *V->dofmap()});
sparsity.finalize();
auto A = la::MatrixCSR<double>(sparsity);

auto mat_add_values = A.mat_add_values();
assemble_matrix(mat_add_values, *a, {});
A.scatter_rev();
}

MPI_Finalize();
return 0;
}
Loading