Skip to content

Commit b1d653b

Browse files
authored
Merge pull request #28 from LEoPart-project/nate/transfer-fixes
Add particle deficiency test
2 parents 1d66830 + 5f957ba commit b1d653b

File tree

4 files changed

+92
-0
lines changed

4 files changed

+92
-0
lines changed

demo/demo_rayleigh-taylor-instability/demo_rayleigh-taylor-instability.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ def velocity(t):
157157
pyleopart.rk(mesh._cpp_object, ptcls, tableau,
158158
velocity, t, dt)
159159

160+
deficient_cells = pyleopart.find_deficient_cells(phi._cpp_object, ptcls)
161+
if len(deficient_cells) > 0:
162+
pprint(f"Particle deficient cells found: {deficient_cells}",
163+
rank=mesh.comm.rank)
160164
pyleopart.transfer_to_function(phi._cpp_object, ptcls, ptcls.field("phi"))
161165
ptcl_file.write_particles(ptcls, t, ["phi"])
162166
phi_file.write_function(phi, t=t)

leopart/cpp/transfer.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,50 @@ void transfer_to_particles(const Particles<T>& particles, Field<T>& field,
4949
f->eval(x, xshape, p2c, u, ushape);
5050
}
5151

52+
/// Transfer the provided particle field data to the finite element
53+
/// function using local l_2 projection.
54+
/// We solve the problem: find \f(u_h \in V\f) such that
55+
///
56+
/// \f[
57+
/// u_h(x_p) v(x_p) = u_p v(x_p) \quad \forall v \in V, \; p = 1,\ldots,n_p.
58+
/// \f]
59+
///
60+
/// Here \f(u_p\f) is the \f(p\f)th particle's data, \f(x_p\f) is the \f(p\f)th
61+
/// particle's position, \f(n_p\f) is the total number of particles
62+
/// and \f(V\f) is the function space to which the provided finite element
63+
/// function belongs.
64+
///
65+
/// @tparam T The function scalar type
66+
/// @tparam U The function geometry type
67+
/// @param f The finite element function
68+
/// @param pax The particles collection
69+
/// @param field The field data to be transferred
70+
template <dolfinx::scalar T, std::floating_point U>
71+
std::vector<std::int32_t> find_deficient_cells(
72+
std::shared_ptr<dolfinx::fem::Function<T>> f,
73+
const Particles<T>& pax)
74+
{
75+
std::shared_ptr<const dolfinx::mesh::Mesh<U>> mesh
76+
= f->function_space()->mesh();
77+
std::shared_ptr<const dolfinx::fem::FunctionSpace<T>> V
78+
= f->function_space();
79+
const int tdim = mesh->topology()->dim();
80+
std::int32_t ncells = mesh->topology()->index_map(tdim)->size_local();
81+
std::shared_ptr<const dolfinx::fem::FiniteElement<T>> element
82+
= f->function_space()->element();
83+
84+
const std::vector<std::vector<std::size_t>>& c2p
85+
= pax.cell_to_particle();
86+
87+
std::vector<std::int32_t> deficient_cells;
88+
for (std::int32_t c = 0; c < ncells; ++c)
89+
{
90+
const std::size_t ncdof = V->dofmap()->cell_dofs(c).size();
91+
if (c2p[c].size() < ncdof)
92+
deficient_cells.push_back(c);
93+
}
94+
return deficient_cells;
95+
}
5296

5397
/// Transfer the provided particle field data to the finite element
5498
/// function using local l_2 projection. The solve_callback function
@@ -185,6 +229,7 @@ template <dolfinx::scalar T, std::floating_point U>
185229
void transfer_to_function(std::shared_ptr<dolfinx::fem::Function<T>> f,
186230
const Particles<T>& pax, const Field<T>& field)
187231
{
232+
// Simply solve the particle mass matrix / rhs system
188233
std::function<const std::vector<T>(mdspan_t<T, 2>, mdspan_t<T, 2>)>
189234
solve_function = [](mdspan_t<T, 2> QT_Q, mdspan_t<T, 2> QT_L)
190235
{
@@ -238,6 +283,7 @@ void transfer_to_function_constrained(
238283
ci0[i + space_dimension] = u;
239284
}
240285

286+
// Solve the mass matrix / rhs optimisation problem with quadprog
241287
std::function<std::vector<T>(mdspan_t<T, 2>, mdspan_t<T, 2>)>
242288
solve_function = [&CE, &ce0, &CI, &ci0](
243289
mdspan_t<T, 2> QT_Q, mdspan_t<T, 2> QT_L)

leopart/cpp/wrapper.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,11 @@ PYBIND11_MODULE(cpp, m)
168168
}, py::return_value_policy::move);
169169

170170
// Transfer functions
171+
m.def("find_deficient_cells",
172+
[](std::shared_ptr<dolfinx::fem::Function<dtype>> f,
173+
const Particles<dtype>& pax){
174+
return leopart::transfer::find_deficient_cells<dtype, dtype_geom>(f, pax);
175+
});
171176
m.def("transfer_to_particles", &leopart::transfer::transfer_to_particles<dtype>);
172177
m.def("transfer_to_function",
173178
[](std::shared_ptr<dolfinx::fem::Function<dtype>> f,

test/test_interpolate_project.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,43 @@ def create_mesh(cell_type, dtype, n):
2424
cell_type=cell_type, dtype=dtype)
2525

2626

27+
@pytest.mark.parametrize("k", [1, 2])
28+
@pytest.mark.parametrize("dtype", [np.float64])
29+
@pytest.mark.parametrize("cell_type", [dolfinx.mesh.CellType.triangle,
30+
dolfinx.mesh.CellType.tetrahedron,
31+
dolfinx.mesh.CellType.quadrilateral,
32+
dolfinx.mesh.CellType.hexahedron])
33+
@pytest.mark.parametrize("shape", [(1,), (2,)])
34+
def test_find_deficient_cells(k, dtype, cell_type, shape):
35+
mesh = create_mesh(cell_type, dtype, 4)
36+
Q = dolfinx.fem.FunctionSpace(mesh, ("DG", k, shape))
37+
38+
npart = Q.dofmap.dof_layout.num_entity_closure_dofs(mesh.topology.dim)
39+
x, c = pyleopart.mesh_fill(mesh._cpp_object, npart, seed=1)
40+
if mesh.geometry.dim == 2:
41+
x = np.c_[x, np.zeros_like(x[:, 0])]
42+
ptcls = pyleopart.Particles(x, c)
43+
44+
num_cells = mesh.topology.index_map(mesh.topology.dim).size_local
45+
cells_to_cull = [0, num_cells - 1]
46+
assert cells_to_cull[0] != cells_to_cull[1]
47+
48+
num_to_delete = 1
49+
for c in cells_to_cull:
50+
pidxs = ptcls.cell_to_particle()[c]
51+
for _ in range(num_to_delete):
52+
ptcls.delete_particle(c, 0)
53+
assert len(ptcls.cell_to_particle()[c]) == len(pidxs) - num_to_delete
54+
55+
u = dolfinx.fem.Function(Q)
56+
deficient_cells = pyleopart.find_deficient_cells(u._cpp_object, ptcls)
57+
58+
cells_to_cull = np.sort(np.array(cells_to_cull, dtype=np.int32))
59+
deficient_cells = np.sort(np.array(deficient_cells, dtype=np.int32))
60+
61+
assert np.all(cells_to_cull == deficient_cells)
62+
63+
2764
@pytest.mark.parametrize("k", [1, 2])
2865
@pytest.mark.parametrize("dtype", [np.float64])
2966
@pytest.mark.parametrize("cell_type", [dolfinx.mesh.CellType.triangle,

0 commit comments

Comments
 (0)