Skip to content

Commit 1e447f2

Browse files
garth-wellsschnellerhase
authored andcommitted
Add C++ demo that shows mixed elements and consistency fixes for element spaces (FEniCS#3500)
* Start on C++ mixed method example * Tidy up demo * Undo change * Lint fix * Tidy up * Simplification * Fix forwarding * Remove redundant function argument * Formatting * Wrapper update * Consistency fixes * Fix * Undo changes * Consistency fixes * Undo some changes * Updates * Update * Small fix * More updates * Enable demo * Updates * Improve readability * Small updates * Add CMake file * Solver fix * Change solver * Try mumps * Verbose test * Action update * Print matrix norm * Test image path update * Update solve * Docker update * CI update * Demo update * Split create FunctionSpace function * Updates * Update * Revert * Revert * Updates * Tidy up * Small fixes * Small edits * Simplifications * Logic fix * Attempt fix * Revert * Change solver * Updates * Update * Simplify * Simplify * Update test * Test update * Enable demo * Tidy up * Simplification * Simplification * Demo update * Doc fix * Updates * Wrapper fix * Test updates * Quad element update * Update test * Test fix * Updates * Test update * Remove value_shape from function space * Lint * Constructor fix * Tidy * Tidy up * Tidy * Work on docs * Doc updates * Doc updates * Simplification * Revert some lines * Add comment * Small fix * Tidy up * Simplify * Work on doc * Type fix * Doc updates * Update docs * Doc improvements * Type fix * Switch solver for PETSc 64-bit int * Small fix * Small change
1 parent 5470ff4 commit 1e447f2

53 files changed

Lines changed: 1312 additions & 682 deletions

Some content is hidden

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

.github/workflows/ccpp.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ jobs:
207207
cmake --build build/demo --parallel 3
208208
cd build/demo
209209
ctest -V -R demo -R serial
210-
ctest -V -R demo -R mpi_2
210+
ctest --output-on-failure -V -R demo -R mpi_2
211211
212212
- name: Install Python build dependencies
213213
run: pip install -r python/build-requirements.txt

cpp/demo/CMakeLists.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ endmacro(add_demo_subdirectory)
1919
add_demo_subdirectory(biharmonic)
2020
add_demo_subdirectory(codim_0_assembly)
2121
add_demo_subdirectory(custom_kernel)
22-
add_demo_subdirectory(poisson)
23-
add_demo_subdirectory(poisson_matrix_free)
2422
add_demo_subdirectory(hyperelasticity)
2523
add_demo_subdirectory(interpolation-io)
2624
add_demo_subdirectory(interpolation_different_meshes)
25+
add_demo_subdirectory(mixed_poisson)
26+
add_demo_subdirectory(poisson)
27+
add_demo_subdirectory(poisson_matrix_free)

cpp/demo/biharmonic/main.cpp

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@
134134
#include <dolfinx/common/types.h>
135135
#include <dolfinx/fem/Constant.h>
136136
#include <dolfinx/fem/petsc.h>
137+
#include <memory>
137138
#include <numbers>
138139
#include <utility>
139140
#include <vector>
@@ -169,8 +170,9 @@ int main(int argc, char* argv[])
169170
basix::element::dpc_variant::unset, false);
170171

171172
// Create function space
172-
auto V = std::make_shared<fem::FunctionSpace<U>>(
173-
fem::create_functionspace(mesh, element));
173+
auto V
174+
= std::make_shared<fem::FunctionSpace<U>>(fem::create_functionspace<U>(
175+
mesh, std::make_shared<fem::FiniteElement<U>>(element)));
174176

175177
// The source function $f$ and the penalty term $\alpha$ are
176178
// declared:
@@ -190,10 +192,10 @@ int main(int argc, char* argv[])
190192
auto alpha = std::make_shared<fem::Constant<T>>(8.0);
191193

192194
// Define variational forms
193-
auto a = std::make_shared<fem::Form<T>>(fem::create_form<T>(
194-
*form_biharmonic_a, {V, V}, {}, {{"alpha", alpha}}, {}, {}));
195-
auto L = std::make_shared<fem::Form<T>>(
196-
fem::create_form<T>(*form_biharmonic_L, {V}, {{"f", f}}, {}, {}, {}));
195+
fem::Form<T> a = fem::create_form<T>(*form_biharmonic_a, {V, V}, {},
196+
{{"alpha", alpha}}, {}, {});
197+
fem::Form<T> L
198+
= fem::create_form<T>(*form_biharmonic_L, {V}, {{"f", f}}, {}, {}, {});
197199

198200
// Now, the Dirichlet boundary condition ($u = 0$) can be
199201
// created using the class {cpp:class}`DirichletBC`. A
@@ -210,7 +212,7 @@ int main(int argc, char* argv[])
210212
auto facets = mesh::exterior_facet_indices(*mesh->topology());
211213
const auto bdofs = fem::locate_dofs_topological(
212214
*V->mesh()->topology_mutable(), *V->dofmap(), 1, facets);
213-
auto bc = std::make_shared<const fem::DirichletBC<T>>(0.0, bdofs, V);
215+
fem::DirichletBC<T> bc(0.0, bdofs, V);
214216

215217
// Now, we have specified the variational forms and can consider
216218
// the solution of the variational problem. First, we need to
@@ -221,13 +223,13 @@ int main(int argc, char* argv[])
221223

222224
// Compute solution
223225
fem::Function<T> u(V);
224-
auto A = la::petsc::Matrix(fem::petsc::create_matrix(*a), false);
225-
la::Vector<T> b(L->function_spaces()[0]->dofmap()->index_map,
226-
L->function_spaces()[0]->dofmap()->index_map_bs());
226+
auto A = la::petsc::Matrix(fem::petsc::create_matrix(a), false);
227+
la::Vector<T> b(L.function_spaces()[0]->dofmap()->index_map,
228+
L.function_spaces()[0]->dofmap()->index_map_bs());
227229

228230
MatZeroEntries(A.mat());
229231
fem::assemble_matrix(la::petsc::Matrix::set_block_fn(A.mat(), ADD_VALUES),
230-
*a, {bc});
232+
a, {bc});
231233
MatAssemblyBegin(A.mat(), MAT_FLUSH_ASSEMBLY);
232234
MatAssemblyEnd(A.mat(), MAT_FLUSH_ASSEMBLY);
233235
fem::set_diagonal<T>(la::petsc::Matrix::set_fn(A.mat(), INSERT_VALUES), *V,
@@ -236,10 +238,10 @@ int main(int argc, char* argv[])
236238
MatAssemblyEnd(A.mat(), MAT_FINAL_ASSEMBLY);
237239

238240
b.set(0.0);
239-
fem::assemble_vector(b.mutable_array(), *L);
241+
fem::assemble_vector(b.mutable_array(), L);
240242
fem::apply_lifting<T, U>(b.mutable_array(), {a}, {{bc}}, {}, T(1.0));
241243
b.scatter_rev(std::plus<T>());
242-
bc->set(b.mutable_array(), std::nullopt);
244+
bc.set(b.mutable_array(), std::nullopt);
243245

244246
la::petsc::KrylovSolver lu(MPI_COMM_WORLD);
245247
la::petsc::options::set("ksp_type", "preonly");

cpp/demo/codim_0_assembly/main.cpp

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,9 @@ int main(int argc, char* argv[])
4141
basix::element::lagrange_variant::unset,
4242
basix::element::dpc_variant::unset, false);
4343

44-
auto V = std::make_shared<fem::FunctionSpace<U>>(
45-
fem::create_functionspace(mesh, element));
44+
auto V
45+
= std::make_shared<fem::FunctionSpace<U>>(fem::create_functionspace<U>(
46+
mesh, std::make_shared<fem::FiniteElement<U>>(element)));
4647

4748
// Next we find all cells of the mesh with y<0.5
4849
const int tdim = mesh->topology()->dim();
@@ -85,8 +86,9 @@ int main(int argc, char* argv[])
8586
}
8687

8788
// We create the function space used for the trial space
88-
auto W = std::make_shared<fem::FunctionSpace<U>>(
89-
fem::create_functionspace(submesh, element, {}));
89+
auto W
90+
= std::make_shared<fem::FunctionSpace<U>>(fem::create_functionspace<U>(
91+
submesh, std::make_shared<fem::FiniteElement<U>>(element)));
9092

9193
// A mixed-domain form has functions defined over different meshes.
9294
// The mesh associated with the measure (dx, ds, etc.) is called the
@@ -108,33 +110,32 @@ int main(int argc, char* argv[])
108110
// Next we compute the integration entities on the integration
109111
// domain `mesh`
110112
std::vector<std::int32_t> integration_entities
111-
= fem::compute_integration_domains(fem::IntegralType::cell,
112-
*mesh->topology(),
113-
cell_marker.find(2), tdim);
113+
= fem::compute_integration_domains(
114+
fem::IntegralType::cell, *mesh->topology(), cell_marker.find(2));
114115
std::map<
115116
fem::IntegralType,
116117
std::vector<std::pair<std::int32_t, std::span<const std::int32_t>>>>
117118
subdomain_data
118119
= {{fem::IntegralType::cell, {{3, integration_entities}}}};
119120

120121
// We can now create the bilinear form
121-
auto a_mixed = std::make_shared<fem::Form<T>>(
122-
fem::create_form<T>(*form_mixed_codim0_a_mixed, {V, W}, {}, {},
123-
subdomain_data, entity_maps, V->mesh()));
122+
fem::Form<T> a_mixed
123+
= fem::create_form<T>(*form_mixed_codim0_a_mixed, {V, W}, {}, {},
124+
subdomain_data, entity_maps, V->mesh());
124125

125-
la::SparsityPattern sp_mixed = fem::create_sparsity_pattern(*a_mixed);
126+
la::SparsityPattern sp_mixed = fem::create_sparsity_pattern(a_mixed);
126127
sp_mixed.finalize();
127128
la::MatrixCSR<PetscScalar> A_mixed(sp_mixed);
128-
fem::assemble_matrix(A_mixed.mat_add_values(), *a_mixed, {});
129+
fem::assemble_matrix(A_mixed.mat_add_values(), a_mixed, {});
129130
A_mixed.scatter_rev();
130131

131-
auto a = std::make_shared<fem::Form<T>>(
132-
fem::create_form<T>(*form_mixed_codim0_a, {W, W}, {}, {}, {}, {}));
132+
fem::Form<T> a
133+
= fem::create_form<T>(*form_mixed_codim0_a, {W, W}, {}, {}, {}, {});
133134

134-
la::SparsityPattern sp = fem::create_sparsity_pattern(*a);
135+
la::SparsityPattern sp = fem::create_sparsity_pattern(a);
135136
sp.finalize();
136137
la::MatrixCSR<PetscScalar> A(sp);
137-
fem::assemble_matrix(A.mat_add_values(), *a, {});
138+
fem::assemble_matrix(A.mat_add_values(), a, {});
138139
A.scatter_rev();
139140

140141
std::vector<T> A_mixed_flattened = A_mixed.to_dense();

cpp/demo/custom_kernel/main.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,8 @@ void assemble(MPI_Comm comm)
209209
mdspand_t<const T, 2> X(X_b.data(), weights.size(), 2);
210210

211211
// Create a scalar function space
212-
auto V = std::make_shared<fem::FunctionSpace<T>>(
213-
fem::create_functionspace(mesh, e));
212+
auto V = std::make_shared<fem::FunctionSpace<T>>(fem::create_functionspace<T>(
213+
mesh, std::make_shared<fem::FiniteElement<T>>(e)));
214214

215215
// Build list of cells to assembler over (all cells owned by this
216216
// rank)

cpp/demo/hyperelasticity/main.cpp

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,15 @@ class HyperElasticProblem
4343
{
4444
public:
4545
/// Constructor
46-
HyperElasticProblem(
47-
std::shared_ptr<fem::Form<T>> L, std::shared_ptr<fem::Form<T>> J,
48-
std::vector<std::shared_ptr<const fem::DirichletBC<T>>> bcs)
49-
: _l(L), _j(J), _bcs(bcs),
50-
_b(L->function_spaces()[0]->dofmap()->index_map,
51-
L->function_spaces()[0]->dofmap()->index_map_bs()),
52-
_matA(la::petsc::Matrix(fem::petsc::create_matrix(*J, "aij"), false))
46+
HyperElasticProblem(fem::Form<T>& L, fem::Form<T>& J,
47+
const std::vector<fem::DirichletBC<T>>& bcs)
48+
: _l(L), _j(J), _bcs(bcs.begin(), bcs.end()),
49+
_b(L.function_spaces()[0]->dofmap()->index_map,
50+
L.function_spaces()[0]->dofmap()->index_map_bs()),
51+
_matA(la::petsc::Matrix(fem::petsc::create_matrix(J, "aij"), false))
5352
{
54-
auto map = L->function_spaces()[0]->dofmap()->index_map;
55-
const int bs = L->function_spaces()[0]->dofmap()->index_map_bs();
53+
auto map = L.function_spaces()[0]->dofmap()->index_map;
54+
const int bs = L.function_spaces()[0]->dofmap()->index_map_bs();
5655
std::int32_t size_local = bs * map->size_local();
5756

5857
std::vector<PetscInt> ghosts(map->ghosts().begin(), map->ghosts().end());
@@ -88,7 +87,7 @@ class HyperElasticProblem
8887
// Assemble b and update ghosts
8988
std::span b(_b.mutable_array());
9089
std::ranges::fill(b, 0);
91-
fem::assemble_vector<T>(b, *_l);
90+
fem::assemble_vector<T>(b, _l);
9291
VecGhostUpdateBegin(_b_petsc, ADD_VALUES, SCATTER_REVERSE);
9392
VecGhostUpdateEnd(_b_petsc, ADD_VALUES, SCATTER_REVERSE);
9493

@@ -100,7 +99,7 @@ class HyperElasticProblem
10099
const T* _x = nullptr;
101100
VecGetArrayRead(x_local, &_x);
102101
std::ranges::for_each(_bcs, [b, x = std::span(_x, n)](auto& bc)
103-
{ bc->set(b, x, -1); });
102+
{ bc.get().set(b, x, -1); });
104103
VecRestoreArrayRead(x_local, &_x);
105104
};
106105
}
@@ -111,12 +110,12 @@ class HyperElasticProblem
111110
return [&](const Vec, Mat A)
112111
{
113112
MatZeroEntries(A);
114-
fem::assemble_matrix(la::petsc::Matrix::set_block_fn(A, ADD_VALUES), *_j,
113+
fem::assemble_matrix(la::petsc::Matrix::set_block_fn(A, ADD_VALUES), _j,
115114
_bcs);
116115
MatAssemblyBegin(A, MAT_FLUSH_ASSEMBLY);
117116
MatAssemblyEnd(A, MAT_FLUSH_ASSEMBLY);
118117
fem::set_diagonal(la::petsc::Matrix::set_fn(A, INSERT_VALUES),
119-
*_j->function_spaces()[0], _bcs);
118+
*_j.function_spaces()[0], _bcs);
120119
MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY);
121120
MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY);
122121
};
@@ -129,8 +128,9 @@ class HyperElasticProblem
129128
Mat matrix() { return _matA.mat(); }
130129

131130
private:
132-
std::shared_ptr<fem::Form<T>> _l, _j;
133-
std::vector<std::shared_ptr<const fem::DirichletBC<T>>> _bcs;
131+
fem::Form<T>& _l;
132+
fem::Form<T>& _j;
133+
std::vector<std::reference_wrapper<const fem::DirichletBC<T>>> _bcs;
134134
la::Vector<T> _b;
135135
Vec _b_petsc = nullptr;
136136
la::petsc::Matrix _matA;
@@ -166,20 +166,22 @@ int main(int argc, char* argv[])
166166
basix::element::lagrange_variant::unset,
167167
basix::element::dpc_variant::unset, false);
168168

169-
auto V = std::make_shared<fem::FunctionSpace<U>>(
170-
fem::create_functionspace(mesh, element, {3}));
169+
auto V
170+
= std::make_shared<fem::FunctionSpace<U>>(fem::create_functionspace<U>(
171+
mesh, std::make_shared<fem::FiniteElement<U>>(
172+
element, std::vector<std::size_t>{3})));
171173

172174
auto B = std::make_shared<fem::Constant<T>>(std::vector<T>{0, 0, 0});
173175
auto traction = std::make_shared<fem::Constant<T>>(std::vector<T>{0, 0, 0});
174176

175177
// Define solution function
176178
auto u = std::make_shared<fem::Function<T>>(V);
177-
auto a = std::make_shared<fem::Form<T>>(
178-
fem::create_form<T>(*form_hyperelasticity_J_form, {V, V}, {{"u", u}},
179-
{{"B", B}, {"T", traction}}, {}, {}));
180-
auto L = std::make_shared<fem::Form<T>>(
181-
fem::create_form<T>(*form_hyperelasticity_F_form, {V}, {{"u", u}},
182-
{{"B", B}, {"T", traction}}, {}, {}));
179+
fem::Form<T> a
180+
= fem::create_form<T>(*form_hyperelasticity_J_form, {V, V}, {{"u", u}},
181+
{{"B", B}, {"T", traction}}, {}, {});
182+
fem::Form<T> L
183+
= fem::create_form<T>(*form_hyperelasticity_F_form, {V}, {{"u", u}},
184+
{{"B", B}, {"T", traction}}, {}, {});
183185

184186
auto u_rotation = std::make_shared<fem::Function<T>>(V);
185187
u_rotation->interpolate(
@@ -243,10 +245,9 @@ int main(int argc, char* argv[])
243245
}
244246
return marker;
245247
});
246-
std::vector bcs = {
247-
std::make_shared<const fem::DirichletBC<T>>(std::vector<T>{0, 0, 0},
248-
bdofs_left, V),
249-
std::make_shared<const fem::DirichletBC<T>>(u_rotation, bdofs_right)};
248+
std::vector bcs
249+
= {fem::DirichletBC<T>(std::vector<T>{0, 0, 0}, bdofs_left, V),
250+
fem::DirichletBC<T>(u_rotation, bdofs_right)};
250251

251252
HyperElasticProblem problem(L, a, bcs);
252253
nls::petsc::NewtonSolver newton_solver(mesh->comm());
@@ -262,6 +263,9 @@ int main(int argc, char* argv[])
262263

263264
// Compute Cauchy stress. Construct appropriate Basix element for
264265
// stress.
266+
fem::Expression sigma_expression = fem::create_expression<T, U>(
267+
*expression_hyperelasticity_sigma, {{"u", u}}, {});
268+
265269
constexpr auto family = basix::element::family::P;
266270
auto cell_type
267271
= mesh::cell_type_to_basix_type(mesh->topology()->cell_type());
@@ -270,12 +274,12 @@ int main(int argc, char* argv[])
270274
basix::FiniteElement S_element = basix::create_element<U>(
271275
family, cell_type, k, basix::element::lagrange_variant::unset,
272276
basix::element::dpc_variant::unset, discontinuous);
273-
auto S = std::make_shared<fem::FunctionSpace<U>>(fem::create_functionspace(
274-
mesh, S_element, std::vector<std::size_t>{3, 3}));
275-
auto sigma_expression = fem::create_expression<T, U>(
276-
*expression_hyperelasticity_sigma, {{"u", u}}, {});
277+
auto S
278+
= std::make_shared<fem::FunctionSpace<U>>(fem::create_functionspace<U>(
279+
mesh, std::make_shared<fem::FiniteElement<U>>(
280+
S_element, std::vector<std::size_t>{3, 3})));
277281

278-
auto sigma = fem::Function<T>(S);
282+
fem::Function<T> sigma(S);
279283
sigma.name = "cauchy_stress";
280284
sigma.interpolate(sigma_expression);
281285

cpp/demo/interpolation-io/main.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ void interpolate_scalar(std::shared_ptr<mesh::Mesh<U>> mesh,
4646
basix::element::dpc_variant::unset, false);
4747

4848
// Create a scalar function space
49-
auto V = std::make_shared<fem::FunctionSpace<U>>(
50-
fem::create_functionspace(mesh, e));
49+
auto V = std::make_shared<fem::FunctionSpace<U>>(fem::create_functionspace<U>(
50+
mesh, std::make_shared<fem::FiniteElement<U>>(e)));
5151

5252
// Create a finite element Function
5353
auto u = std::make_shared<fem::Function<T>>(V);
@@ -98,8 +98,8 @@ void interpolate_nedelec(std::shared_ptr<mesh::Mesh<U>> mesh,
9898
basix::element::dpc_variant::unset, false);
9999

100100
// Create a Nedelec function space
101-
auto V = std::make_shared<fem::FunctionSpace<U>>(
102-
fem::create_functionspace(mesh, e));
101+
auto V = std::make_shared<fem::FunctionSpace<U>>(fem::create_functionspace<U>(
102+
mesh, std::make_shared<fem::FiniteElement<U>>(e)));
103103

104104
// Create a Nedelec finite element Function
105105
auto u = std::make_shared<fem::Function<T>>(V);
@@ -114,7 +114,7 @@ void interpolate_nedelec(std::shared_ptr<mesh::Mesh<U>> mesh,
114114
// the Nedelec space when there are cell edges aligned to x0 = 0.5.
115115

116116
// Find cells with all vertices satisfying (0) x0 <= 0.5 and (1) x0 >= 0.5
117-
auto cells0
117+
std::vector cells0
118118
= mesh::locate_entities(*mesh, 2,
119119
[](auto x)
120120
{
@@ -123,7 +123,7 @@ void interpolate_nedelec(std::shared_ptr<mesh::Mesh<U>> mesh,
123123
marked.push_back(x(0, i) <= 0.5);
124124
return marked;
125125
});
126-
auto cells1
126+
std::vector cells1
127127
= mesh::locate_entities(*mesh, 2,
128128
[](auto x)
129129
{
@@ -167,8 +167,10 @@ void interpolate_nedelec(std::shared_ptr<mesh::Mesh<U>> mesh,
167167
basix::element::dpc_variant::unset, true);
168168

169169
// Create a function space
170-
auto V_l = std::make_shared<fem::FunctionSpace<U>>(
171-
fem::create_functionspace(mesh, e_l, std::vector<std::size_t>{2}));
170+
auto V_l
171+
= std::make_shared<fem::FunctionSpace<U>>(fem::create_functionspace<U>(
172+
mesh, std::make_shared<fem::FiniteElement<U>>(
173+
e_l, std::vector<std::size_t>{2})));
172174

173175
auto u_l = std::make_shared<fem::Function<T>>(V_l);
174176

cpp/demo/interpolation_different_meshes/main.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,17 @@ int main(int argc, char* argv[])
3636
mesh::cell_type_to_basix_type(mesh_tet->topology()->cell_type()), 1,
3737
basix::element::lagrange_variant::equispaced, false);
3838
auto V_tet = std::make_shared<fem::FunctionSpace<double>>(
39-
fem::create_functionspace(mesh_tet, element_tet,
40-
std::vector<std::size_t>{3}));
39+
fem::create_functionspace<double>(
40+
mesh_tet, std::make_shared<fem::FiniteElement<double>>(
41+
element_tet, std::vector<std::size_t>{3})));
4142

4243
basix::FiniteElement element_hex = basix::element::create_lagrange<double>(
4344
mesh::cell_type_to_basix_type(mesh_hex->topology()->cell_type()), 2,
4445
basix::element::lagrange_variant::equispaced, false);
4546
auto V_hex = std::make_shared<fem::FunctionSpace<double>>(
46-
fem::create_functionspace(mesh_hex, element_hex,
47-
std::vector<std::size_t>{3}));
47+
fem::create_functionspace<double>(
48+
mesh_hex, std::make_shared<fem::FiniteElement<double>>(
49+
element_hex, std::vector<std::size_t>{3})));
4850

4951
auto u_tet = std::make_shared<fem::Function<T>>(V_tet);
5052
auto u_hex = std::make_shared<fem::Function<T>>(V_hex);

0 commit comments

Comments
 (0)