Files
mfem/fem/kdtree.hpp
T

176 lines
6.1 KiB
C++

// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_KDTREE_PROJECTION
#define MFEM_KDTREE_PROJECTION
#include "../general/kdtree.hpp"
#include "gridfunc.hpp"
namespace mfem
{
/// Base class for KDTreeNodalProjection.
class BaseKDTreeNodalProjection
{
public:
virtual ~BaseKDTreeNodalProjection()
{}
/// The projection method can be called as many time as necessary with
/// different sets of coordinates and corresponding values. For vector
/// grid function, users have to specify the data ordering and for all
/// cases the user can modify the error tolerance err to smaller or
/// bigger value. A node in the target grid function is matching
/// a point with coordinates specified in the vector coords if the
/// distance between them is smaller than lerr.
virtual
void Project(const Vector& coords,const Vector& src,
int ordering, real_t lerr) = 0;
/// The project method can be called as many times as necessary with
/// different grid functions gf. A node in the target grid function is
/// matching a node from the source grid function if the distance
/// between them is smaller than lerr.
virtual
void Project(const GridFunction& gf, real_t lerr) = 0;
};
/// The class provides methods for projecting function values evaluated on a
/// set of points to a grid function. The values are directly copied to the
/// nodal values of the target grid function if any of the points is matching
/// a node of the grid function. For example, if a parallel grid function is
/// saved in parallel, every saved chunk can be read on every other process
/// and mapped to a local grid function that does not have the same structure
/// as the original one. The functionality is based on a kd-tree search in a
/// cloud of points.
template<int kdim=3>
class KDTreeNodalProjection : public BaseKDTreeNodalProjection
{
private:
/// Pointer to the KDTree
std::unique_ptr<KDTree<int,real_t,kdim>> kdt;
/// Pointer to the target grid function
GridFunction* dest;
/// Upper corner of the bounding box
Vector maxbb;
/// Lower corner of the bounding box
Vector minbb;
public:
/// The constructor takes as input an L2 or H1 grid function (it can be
/// a vector grid function). The Project method copies a set of values
/// to the grid function.
KDTreeNodalProjection(GridFunction& dest_)
{
dest=&dest_;
FiniteElementSpace* space=dest->FESpace();
MFEM_VERIFY(
dynamic_cast<const H1_FECollection*>(space->FEColl()) != nullptr ||
dynamic_cast<const L2_FECollection*>(space->FEColl()) != nullptr,
"Error!");
Mesh* mesh=space->GetMesh();
const int dim=mesh->SpaceDimension();
MFEM_VERIFY(kdim==dim, "GridFunction dimension does not match!");
kdt=std::unique_ptr<KDTree<int,real_t,kdim>>(
new KDTree<int,real_t,kdim>());
std::vector<bool> indt;
indt.resize(space->GetVSize()/space->GetVDim(), true);
minbb.SetSize(dim);
maxbb.SetSize(dim);
// set the local coordinates
{
ElementTransformation *trans;
const IntegrationRule* ir=nullptr;
Array<int> vdofs;
DenseMatrix elco;
int isca=1;
if (space->GetOrdering()==Ordering::byVDIM)
{
isca=space->GetVDim();
}
// intialize the bounding box
const FiniteElement* el = space->GetTypicalFE();
trans = mesh->GetTypicalElementTransformation();
ir=&(el->GetNodes());
space->GetElementVDofs(0,vdofs);
elco.SetSize(dim,ir->GetNPoints());
trans->Transform(*ir,elco);
for (int d=0; d<dim; d++)
{
minbb[d]=elco(d,0);
maxbb[d]=elco(d,0);
}
for (int i=0; i<space->GetNE(); i++)
{
el=space->GetFE(i);
// get the element transformation
trans = space->GetElementTransformation(i);
ir=&(el->GetNodes());
space->GetElementVDofs(i,vdofs);
elco.SetSize(dim,ir->GetNPoints());
trans->Transform(*ir,elco);
for (int p=0; p<ir->GetNPoints(); p++)
{
int bind=vdofs[p]/isca;
if (indt[bind]==true)
{
kdt->AddPoint(elco.GetColumn(p),bind);
indt[bind]=false;
for (int d=0; d<kdim; d++)
{
if (minbb[d]>elco(d,p)) {minbb[d]=elco(d,p);}
if (maxbb[d]<elco(d,p)) {maxbb[d]=elco(d,p);}
}
}
}
}
}
// build the KDTree
kdt->Sort();
}
/// The projection method can be called as many time as necessary with
/// different sets of coordinates and corresponding values. For vector
/// grid function, users have to specify the data ordering and for all
/// cases the user can modify the error tolerance err to smaller or
/// bigger value. A node in the target grid function is matching
/// a point with coordinates specified in the vector coords if the
/// distance between them is smaller than lerr.
void Project(const Vector& coords,const Vector& src,
int ordering=Ordering::byNODES, real_t lerr=1e-8) override;
/// The project method can be called as many times as necessary with
/// different grid functions gf. A node in the target grid function is
/// matching a node from the source grid function if the distance
/// between them is smaller than lerr.
void Project(const GridFunction& gf, real_t lerr=1e-8) override;
};
} // namespace mfem
#endif // MFEM_KDTREE_PROJECTION