src/dolfinx_access/function.cc

changeset 1
a4137aedcb3a
child 3
c3a4f4bb87f7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/dolfinx_access/function.cc	Thu Feb 26 09:32:12 2026 -0500
@@ -0,0 +1,167 @@
+#include <array>
+#include <cmath>
+#include <span>
+
+#include <basix/finite-element.h>
+#include <dolfinx/fem/Function.h>
+#include <nanobind/nanobind.h>
+
+#include "dolfinx_access/function.h"
+// #include "mpi_proto.h"
+#include "dolfinx/geometry/BoundingBoxTree.h"
+#include "pointsource_pde/src/dolfinx_access.rs.h"
+#include "rust/cxx.h"
+
+using namespace dolfinx::fem;
+using namespace dolfinx::geometry;
+using namespace basix::element;
+using namespace dolfinx::graph;
+using namespace dolfinx::la;
+namespace cell = basix::cell;
+
+namespace dolfinx_access {
+    extern void drop_Function_f64(Function_f64* f) {
+        delete f;
+    }
+
+    FunctionInfo info_Function_f64(Function_f64 const* f) {
+        auto fp = f->function_space();
+        auto el = fp->element()->basix_element();
+        return FunctionInfo{
+            .domain_dim = (uint32_t)fp->mesh()->geometry().dim(),
+            .codomain_dim = (uint32_t)fp->value_size(),
+            .order = (uint32_t)fp->element()->basix_element().degree(),
+            .triangular_mesh = el.cell_type() == cell::type::triangle,
+        };
+    }
+
+    void check_compat_Function_f64(Function_f64 const* f, Function_f64 const* g) {
+        if (f->function_space() != g->function_space()) {
+            throw "Incompatible function spaces";
+        }
+    }
+
+    std::map<std::weak_ptr<const mesh::Mesh<double>>, std::shared_ptr<BoundingBoxTree<double>>,
+             std::owner_less<std::weak_ptr<const mesh::Mesh<double>>>>
+        bb_tree_cache;
+
+    /// Returns the cell containg x
+    int32_t cell_Mesh_f64(std::shared_ptr<const mesh::Mesh<double>> mesh,
+                          const std::array<double, 3>& x) {
+
+        std::weak_ptr<const mesh::Mesh<double>> mesh_weak = mesh;
+        std::shared_ptr<BoundingBoxTree<double>> bb_tree = bb_tree_cache[mesh_weak];
+        if (bb_tree == nullptr) {
+            auto topo = mesh->topology();
+            // auto dim = mesh->geometry().dim();
+            auto dim = topo->dim();
+
+            // This fails as it inorrectly calls mesh.topology_mutable(), so need to
+            // copy-paste the implementation below, with the appropriate fix.
+            //*bb_tree = new BoundingBoxTree(*mesh, dim);
+            // And this also doesn't work due to the privacy of `range`.
+            //*bb_tree = new BoundingBoxTree(*mesh, dim, BoundingBoxTree::range(mesh->topology(),
+            // dim
+            auto index_map = topo->index_map(dim);
+            int local_cells = index_map->size_local();
+            // We don't really do MPI; pointsource_algs is not designed for it.
+            assert(local_cells == index_map->size_global());
+            std::vector<std::int32_t> r(local_cells);
+            std::iota(r.begin(), r.end(), 0);
+            bb_tree = std::make_shared<BoundingBoxTree<double>>(BoundingBoxTree(*mesh, dim, r));
+            bb_tree_cache[mesh_weak] = bb_tree;
+        }
+        // Find colliding bounding boxes. This is an AdjancencyTree.
+        //
+        // C++ and Dolfinx are just vomit-inducing 🤮. We need some weird const double span here,
+        // but then just standard x in compute_first_collding_cell below.
+        auto colliding_bbs_adj = compute_collisions(*bb_tree.get(), std::span<const double>(x));
+        // Since we compute the collisions for just one node, the `array` of all links
+        // of the adjancency list, will corresponding exactly the ccells whose bounding boxes
+        // collide with the point.
+        auto colliding_bbs = colliding_bbs_adj.array();
+        // Within the colliding bounding boxes, find the first cell that actually collides.
+        int32_t local_cell = compute_first_colliding_cell(*mesh, colliding_bbs, x, 1e-9);
+        // local_cell may be -1 if it was not found by the local process, so combine
+        // results with MPI_MAX.
+        int32_t global_cell = local_cell;
+        MPI_Allreduce(&local_cell, &global_cell, 1, MPI_INT32_T, MPI_MAX, mesh->comm());
+
+        return global_cell;
+    }
+
+    int32_t cell_FunctionSpace_f64(FunctionSpace<double> const* v,
+                                   const std::array<double, 3>& x) {
+        return cell_Mesh_f64(v->mesh(), x);
+    }
+
+    int32_t cell_Function_f64(Function_f64 const* f, const std::array<double, 3>& x) {
+        return cell_Mesh_f64(f->function_space()->mesh(), x);
+    }
+
+    std::array<double, 3 * 3> cell_coords_Function_f64_triangle(Function_f64 const* f,
+                                                                int32_t cell) {
+        auto geom = f->function_space()->mesh()->geometry();
+        auto gx = geom.x();
+        auto geom_dofmap = geom.dofmap();
+        assert(geom_dofmap.extent(1) == 3);
+
+        auto j0 = geom_dofmap(cell, 0);
+        auto j1 = geom_dofmap(cell, 1);
+        auto j2 = geom_dofmap(cell, 2);
+
+        // Construct list of coordinates. Due to f->eval, we construct this as a single list
+        // with all the Fenics weirdness of hard-coded 3D.
+        return {gx[3 * j0], gx[3 * j0 + 1], 0, /* */
+                gx[3 * j1], gx[3 * j1 + 1], 0, /* */
+                gx[3 * j2], gx[3 * j2 + 1], 0};
+    }
+
+    // We assume `f` to be real-valued.
+    double eval_Function_f64_cell(Function_f64 const* f, const std::array<double, 3>& x,
+                                  int32_t cell) {
+        if (cell < 0) {
+            return 0.0;
+        } else {
+            std::array<double, 1> res;
+            f->eval(x, {1, 3}, {{cell}}, std::span(res), {1, 1});
+            return res[0];
+        }
+    }
+
+    double eval_Function_f64(Function_f64 const* f, const std::array<double, 3>& x) {
+        return eval_Function_f64_cell(f, x, cell_Function_f64(f, x));
+    }
+
+    std::array<double, 6> eval_Function_f64_cell_6(Function_f64 const* f,
+                                                   const std::array<double, 3 * 6>& x,
+                                                   int32_t cell) {
+        std::array<double, 6> res;
+        auto i = cell;
+        f->eval(x, {6, 3}, {{i, i, i, i, i, i}}, std::span(res), {6, 1});
+        return res;
+    }
+
+    rust::Slice<const double> data_Function_f64(Function_f64 const* f) {
+        auto span = f->x()->array();
+        return rust::Slice(span.data(), span.size());
+    }
+
+    rust::Slice<double> data_mut_Function_f64(Function_f64* f) {
+        auto span = f->x()->mutable_array();
+        return rust::Slice(span.data(), span.size());
+    }
+
+    Function_f64* similar_Function_f64(Function_f64 const* f) {
+        return new Function_f64(f->function_space());
+    }
+
+    Function_f64* clone_Function_f64(Function_f64 const* f) {
+        return new Function_f64(f->function_space(), std::make_shared<Vector<double>>(*f->x()));
+    }
+
+    // PyObject* py_similar_Function_f64(Function_f64 const* f) {
+    //     return nanobind::cast(Function_f64(f->function_space())).release().ptr();
+    // }
+
+} // namespace dolfinx_access

mercurial