Skip to content

Commit e6d78d5

Browse files
author
FEniCS GitHub Actions
committed
1 parent 5c52000 commit e6d78d5

File tree

1,018 files changed

+192642
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,018 files changed

+192642
-0
lines changed
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
// # Poisson equation
2+
//
3+
// This demo illustrates how to:
4+
//
5+
// * Solve a linear partial differential equation
6+
// * Create and apply Dirichlet boundary conditions
7+
// * Define Expressions
8+
// * Define a FunctionSpace
9+
//
10+
// ## Equation and problem definition
11+
//
12+
// The Poisson equation is the canonical elliptic partial differential
13+
// equation. For a domain $\Omega \subset \mathbb{R}^n$ with boundary
14+
// $\partial \Omega = \Gamma_{D} \cup \Gamma_{N}$, the Poisson equation
15+
// with particular boundary conditions reads:
16+
//
17+
// \begin{align*}
18+
// - \nabla^{2} u &= f \quad {\rm in} \ \Omega, \\
19+
// u &= 0 \quad {\rm on} \ \Gamma_{D}, \\
20+
// \nabla u \cdot n &= g \quad {\rm on} \ \Gamma_{N}. \\
21+
// \end{align*}
22+
//
23+
// Here, $f$ and $g$ are input data and $n$ denotes the outward directed
24+
// boundary normal. The most standard variational form of Poisson
25+
// equation reads: find $u \in V$ such that
26+
//
27+
// $$
28+
// a(u, v) = L(v) \quad \forall \ v \in V,
29+
// $$
30+
// where $V$ is a suitable function space and
31+
//
32+
// \begin{align*}
33+
// a(u, v) &= \int_{\Omega} \nabla u \cdot \nabla v \, {\rm d} x, \\
34+
// L(v) &= \int_{\Omega} f v \, {\rm d} x
35+
// + \int_{\Gamma_{N}} g v \, {\rm d} s.
36+
// \end{align*}
37+
//
38+
// The expression $a(u, v)$ is the bilinear form and $L(v)$ is the
39+
// linear form. It is assumed that all functions in $V$ satisfy the
40+
// Dirichlet boundary conditions ($u = 0 \ {\rm on} \ \Gamma_{D}$).
41+
//
42+
// In this demo, we shall consider the following definitions of the
43+
// input functions, the domain, and the boundaries:
44+
//
45+
// * $\Omega = [0,1] \times [0,1]$ (a unit square)
46+
// * $\Gamma_{D} = \{(0, y) \cup (1, y) \subset \partial \Omega\}$
47+
// (Dirichlet boundary)
48+
// * $\Gamma_{N} = \{(x, 0) \cup (x, 1) \subset \partial \Omega\}$
49+
// (Neumann boundary)
50+
// * $g = \sin(5x)$ (normal derivative)
51+
// * $f = 10\exp(-((x - 0.5)^2 + (y - 0.5)^2) / 0.02)$ (source term)
52+
//
53+
//
54+
// ## Implementation
55+
//
56+
// The implementation is split in two files: a file containing the
57+
// definition of the variational forms expressed in UFL and a C++ file
58+
// containing the actual solver.
59+
//
60+
// Running this demo requires the files: {download}`demo_poisson/main.cpp`,
61+
// {download}`demo_poisson/poisson.py` and
62+
// {download}`demo_poisson/CMakeLists.txt`.
63+
//
64+
// ### UFL code
65+
//
66+
// The UFL code is implemented in {download}`demo_poisson/poisson.py`.
67+
// ````{admonition} UFL code implemented in Python
68+
// :class: dropdown
69+
// ![ufl-code]
70+
// ````
71+
//
72+
// ### C++ program
73+
//
74+
// The main solver is implemented in the
75+
// {download}`demo_poisson/main.cpp` file.
76+
//
77+
// At the top we include the DOLFINx header file and the generated
78+
// header file "Poisson.h" containing the variational forms for the
79+
// Poisson equation. For convenience we also include the DOLFINx
80+
// namespace.
81+
82+
#include "poisson.h"
83+
#include <basix/finite-element.h>
84+
#include <cmath>
85+
#include <dolfinx.h>
86+
#include <dolfinx/fem/Constant.h>
87+
#include <dolfinx/fem/petsc.h>
88+
#include <dolfinx/la/petsc.h>
89+
#include <petscmat.h>
90+
#include <petscsys.h>
91+
#include <petscsystypes.h>
92+
#include <utility>
93+
#include <vector>
94+
95+
using namespace dolfinx;
96+
using T = PetscScalar;
97+
using U = typename dolfinx::scalar_value_t<T>;
98+
99+
// Then follows the definition of the coefficient functions (for $f$ and
100+
// $g$), which are derived from the {cpp:class}`Expression` class in
101+
// DOLFINx
102+
103+
// Inside the `main` function, we begin by defining a mesh of the
104+
// domain. As the unit square is a very standard domain, we can use a
105+
// built-in mesh provided by the {cpp:class}`UnitSquareMesh` factory. In
106+
// order to create a mesh consisting of 32 x 32 squares with each square
107+
// divided into two triangles, and the finite element space (specified
108+
// in the form file) defined relative to this mesh, we do as follows:
109+
110+
int main(int argc, char* argv[])
111+
{
112+
dolfinx::init_logging(argc, argv);
113+
PetscInitialize(&argc, &argv, nullptr, nullptr);
114+
115+
{
116+
// Create mesh and function space
117+
auto part = mesh::create_cell_partitioner(mesh::GhostMode::shared_facet);
118+
auto mesh = std::make_shared<mesh::Mesh<U>>(
119+
mesh::create_rectangle<U>(MPI_COMM_WORLD, {{{0.0, 0.0}, {2.0, 1.0}}},
120+
{32, 16}, mesh::CellType::triangle, part));
121+
122+
auto element = basix::create_element<U>(
123+
basix::element::family::P, basix::cell::type::triangle, 1,
124+
basix::element::lagrange_variant::unset,
125+
basix::element::dpc_variant::unset, false);
126+
127+
auto V
128+
= std::make_shared<fem::FunctionSpace<U>>(fem::create_functionspace<U>(
129+
mesh, std::make_shared<fem::FiniteElement<U>>(element)));
130+
131+
// Next, we define the variational formulation by initializing the
132+
// bilinear and linear forms ($a$, $L$) using the previously
133+
// defined {cpp:class}`FunctionSpace` `V`. Then we can create the
134+
// source and boundary flux term ($f$, $g$) and attach these to the
135+
// linear form.
136+
137+
// Prepare and set Constants for the bilinear form
138+
auto kappa = std::make_shared<fem::Constant<T>>(2.0);
139+
auto f = std::make_shared<fem::Function<T>>(V);
140+
auto g = std::make_shared<fem::Function<T>>(V);
141+
142+
// Define variational forms
143+
fem::Form<T> a = fem::create_form<T>(*form_poisson_a, {V, V}, {},
144+
{{"kappa", kappa}}, {}, {});
145+
fem::Form<T> L = fem::create_form<T>(*form_poisson_L, {V},
146+
{{"f", f}, {"g", g}}, {}, {}, {});
147+
148+
// Now, the Dirichlet boundary condition ($u = 0$) can be created
149+
// using the class {cpp:class}`DirichletBC`. A
150+
// {cpp:class}`DirichletBC` takes two arguments: the value of the
151+
// boundary condition, and the part of the boundary on which the
152+
// condition applies. In our example, the value of the boundary
153+
// condition (0) can represented using a {cpp:class}`Function`,
154+
// and the Dirichlet boundary is defined by the indices of degrees
155+
// of freedom to which the boundary condition applies. The
156+
// definition of the Dirichlet boundary condition then looks as
157+
// follows:
158+
159+
// Define boundary condition
160+
161+
std::vector facets = mesh::locate_entities_boundary(
162+
*mesh, 1,
163+
[](auto x)
164+
{
165+
using U = typename decltype(x)::value_type;
166+
constexpr U eps = 1.0e-8;
167+
std::vector<std::int8_t> marker(x.extent(1), false);
168+
for (std::size_t p = 0; p < x.extent(1); ++p)
169+
{
170+
auto x0 = x(0, p);
171+
if (std::abs(x0) < eps or std::abs(x0 - 2) < eps)
172+
marker[p] = true;
173+
}
174+
return marker;
175+
});
176+
std::vector bdofs = fem::locate_dofs_topological(
177+
*V->mesh()->topology_mutable(), *V->dofmap(), 1, facets);
178+
fem::DirichletBC<T> bc(0, bdofs, V);
179+
180+
f->interpolate(
181+
[](auto x) -> std::pair<std::vector<T>, std::vector<std::size_t>>
182+
{
183+
std::vector<T> f;
184+
for (std::size_t p = 0; p < x.extent(1); ++p)
185+
{
186+
auto dx = (x(0, p) - 0.5) * (x(0, p) - 0.5);
187+
auto dy = (x(1, p) - 0.5) * (x(1, p) - 0.5);
188+
f.push_back(10 * std::exp(-(dx + dy) / 0.02));
189+
}
190+
191+
return {f, {f.size()}};
192+
});
193+
194+
g->interpolate(
195+
[](auto x) -> std::pair<std::vector<T>, std::vector<std::size_t>>
196+
{
197+
std::vector<T> f;
198+
for (std::size_t p = 0; p < x.extent(1); ++p)
199+
f.push_back(std::sin(5 * x(0, p)));
200+
return {f, {f.size()}};
201+
});
202+
203+
// Now, we have specified the variational forms and can consider
204+
// the solution of the variational problem. First, we need to
205+
// define a {cpp:class}`Function` `u` to store the solution. (Upon
206+
// initialization, it is simply set to the zero function.) Next, we
207+
// can call the `solve` function with the arguments `a == L`, `u`
208+
// and `bc` as follows:
209+
210+
auto u = std::make_shared<fem::Function<T>>(V);
211+
la::petsc::Matrix A(fem::petsc::create_matrix(a), false);
212+
la::Vector<T> b(L.function_spaces()[0]->dofmap()->index_map,
213+
L.function_spaces()[0]->dofmap()->index_map_bs());
214+
215+
MatZeroEntries(A.mat());
216+
fem::assemble_matrix(la::petsc::Matrix::set_block_fn(A.mat(), ADD_VALUES),
217+
a, {bc});
218+
MatAssemblyBegin(A.mat(), MAT_FLUSH_ASSEMBLY);
219+
MatAssemblyEnd(A.mat(), MAT_FLUSH_ASSEMBLY);
220+
fem::set_diagonal<T>(la::petsc::Matrix::set_fn(A.mat(), INSERT_VALUES), *V,
221+
{bc});
222+
MatAssemblyBegin(A.mat(), MAT_FINAL_ASSEMBLY);
223+
MatAssemblyEnd(A.mat(), MAT_FINAL_ASSEMBLY);
224+
225+
std::ranges::fill(b.array(), 0);
226+
fem::assemble_vector(b.array(), L);
227+
fem::apply_lifting(b.array(), {a}, {{bc}}, {}, T(1));
228+
b.scatter_rev(std::plus<T>());
229+
bc.set(b.array(), std::nullopt);
230+
231+
la::petsc::KrylovSolver lu(MPI_COMM_WORLD);
232+
la::petsc::options::set("ksp_type", "preonly");
233+
la::petsc::options::set("pc_type", "lu");
234+
lu.set_from_options();
235+
236+
lu.set_operator(A.mat());
237+
la::petsc::Vector _u(la::petsc::create_vector_wrap(*u->x()), false);
238+
la::petsc::Vector _b(la::petsc::create_vector_wrap(b), false);
239+
lu.solve(_u.vec(), _b.vec());
240+
241+
// Update ghost values before output
242+
u->x()->scatter_fwd();
243+
244+
// The function `u` will be modified during the call to solve. A
245+
// {cpp:class}`Function` can be saved to a file. Here, we output
246+
// the solution to a `VTK` file (specified using the suffix `.pvd`)
247+
// for visualisation in an external program such as Paraview.
248+
249+
// Save solution in VTK format
250+
io::VTKFile file(MPI_COMM_WORLD, "u.pvd", "w");
251+
file.write<T>({*u}, 0);
252+
253+
#ifdef HAS_ADIOS2
254+
// Save solution in VTX format
255+
io::VTXWriter<U> vtx(MPI_COMM_WORLD, "u.bp", {u}, "bp4");
256+
vtx.write(0);
257+
#endif
258+
}
259+
260+
PetscFinalize();
261+
262+
return 0;
263+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# This file was generated by running
2+
#
3+
# python cmake/scripts/generate-cmakefiles from dolfinx/cpp
4+
#
5+
cmake_minimum_required(VERSION 3.21)
6+
7+
set(PROJECT_NAME demo_poisson)
8+
project(${PROJECT_NAME} LANGUAGES C CXX)
9+
10+
if(NOT TARGET dolfinx)
11+
find_package(DOLFINX REQUIRED)
12+
endif()
13+
14+
include(CheckSymbolExists)
15+
set(CMAKE_REQUIRED_INCLUDES ${PETSC_INCLUDE_DIRS})
16+
check_symbol_exists(PETSC_USE_COMPLEX petscsystypes.h PETSC_SCALAR_COMPLEX)
17+
check_symbol_exists(PETSC_USE_REAL_DOUBLE petscsystypes.h PETSC_REAL_DOUBLE)
18+
19+
# Add target to compile UFL files
20+
if(PETSC_SCALAR_COMPLEX EQUAL 1)
21+
if(PETSC_REAL_DOUBLE EQUAL 1)
22+
set(SCALAR_TYPE "--scalar_type=complex128")
23+
else()
24+
set(SCALAR_TYPE "--scalar_type=complex64")
25+
endif()
26+
else()
27+
if(PETSC_REAL_DOUBLE EQUAL 1)
28+
set(SCALAR_TYPE "--scalar_type=float64")
29+
else()
30+
set(SCALAR_TYPE "--scalar_type=float32")
31+
endif()
32+
endif()
33+
add_custom_command(
34+
OUTPUT poisson.c
35+
COMMAND ffcx ${CMAKE_CURRENT_SOURCE_DIR}/poisson.py ${SCALAR_TYPE}
36+
VERBATIM
37+
DEPENDS poisson.py
38+
COMMENT "Compile poisson.py using FFCx"
39+
)
40+
41+
set(CMAKE_INCLUDE_CURRENT_DIR ON)
42+
43+
add_executable(${PROJECT_NAME} main.cpp ${CMAKE_CURRENT_BINARY_DIR}/poisson.c)
44+
target_link_libraries(${PROJECT_NAME} dolfinx)
45+
46+
# Set C++20 standard
47+
set(CMAKE_CXX_EXTENSIONS OFF)
48+
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20)
49+
50+
# Do not throw error for 'multi-line comments' (these are typical in rst which
51+
# includes LaTeX)
52+
include(CheckCXXCompilerFlag)
53+
check_cxx_compiler_flag("-Wno-comment" HAVE_NO_MULTLINE)
54+
set_source_files_properties(
55+
main.cpp
56+
PROPERTIES
57+
COMPILE_FLAGS
58+
"$<$<BOOL:${HAVE_NO_MULTLINE}>:-Wno-comment -Wall -Wextra -pedantic -Werror>"
59+
)
60+
61+
# Test targets (used by DOLFINx testing system)
62+
set(TEST_PARAMETERS2 -np 2 "./${PROJECT_NAME}")
63+
set(TEST_PARAMETERS3 -np 3 "./${PROJECT_NAME}")
64+
add_test(NAME ${PROJECT_NAME}_mpi_2 COMMAND "mpirun" ${TEST_PARAMETERS2})
65+
add_test(NAME ${PROJECT_NAME}_mpi_3 COMMAND "mpirun" ${TEST_PARAMETERS3})
66+
add_test(NAME ${PROJECT_NAME}_serial COMMAND ${PROJECT_NAME})

0 commit comments

Comments
 (0)