Files
mfem/fem/kdtree.cpp
T

405 lines
11 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.
#include "kdtree.hpp"
namespace mfem
{
template<>
void KDTreeNodalProjection<2>::Project(const Vector& coords,const Vector& src,
int ordering, real_t lerr)
{
const int dim=dest->FESpace()->GetMesh()->SpaceDimension();
const int vd=dest->VectorDim(); // dimension of the vector field
const int np=src.Size()/vd; // number of points
int ind;
real_t dist;
bool pt_inside_bbox;
KDTree2D::PointND pnd;
for (int i=0; i<np; i++)
{
pnd.xx[0]=coords(i*dim+0);
pnd.xx[1]=coords(i*dim+1);
pt_inside_bbox=true;
for (int j=0; j<dim; j++)
{
if (pnd.xx[j]>(maxbb[j]+lerr)) {pt_inside_bbox=false; break;}
if (pnd.xx[j]<(minbb[j]-lerr)) {pt_inside_bbox=false; break;}
}
if (pt_inside_bbox)
{
kdt->FindClosestPoint(pnd,ind,dist);
if (dist<lerr)
{
if (dest->FESpace()->GetOrdering()==Ordering::byNODES)
{
if (ordering==Ordering::byNODES)
{
for (int di=0; di<vd; di++)
{
(*dest)[di*np+ind]=src[di*np+i];
}
}
else
{
for (int di=0; di<vd; di++)
{
(*dest)[di*np+ind]=src[di+i*vd];
}
}
}
else
{
if (ordering==Ordering::byNODES)
{
for (int di=0; di<vd; di++)
{
(*dest)[di+ind*vd]=src[di*np+i];
}
}
else
{
for (int di=0; di<vd; di++)
{
(*dest)[di+ind*vd]=src[di+i*vd];
}
}
}
}
}
}
}
template<>
void KDTreeNodalProjection<3>::Project(const Vector& coords,const Vector& src,
int ordering, real_t lerr)
{
const int dim=dest->FESpace()->GetMesh()->SpaceDimension();
const int vd=dest->VectorDim(); // dimension of the vector field
const int np=src.Size()/vd; // number of points
int ind;
real_t dist;
bool pt_inside_bbox;
KDTree3D::PointND pnd;
for (int i=0; i<np; i++)
{
pnd.xx[0]=coords(i*dim+0);
pnd.xx[1]=coords(i*dim+1);
pnd.xx[2]=coords(i*dim+2);
pt_inside_bbox=true;
for (int j=0; j<dim; j++)
{
if (pnd.xx[j]>(maxbb[j]+lerr)) {pt_inside_bbox=false; break;}
if (pnd.xx[j]<(minbb[j]-lerr)) {pt_inside_bbox=false; break;}
}
if (pt_inside_bbox)
{
kdt->FindClosestPoint(pnd,ind,dist);
if (dist<lerr)
{
if (dest->FESpace()->GetOrdering()==Ordering::byNODES)
{
if (ordering==Ordering::byNODES)
{
for (int di=0; di<vd; di++)
{
(*dest)[di*np+ind]=src[di*np+i];
}
}
else
{
for (int di=0; di<vd; di++)
{
(*dest)[di*np+ind]=src[di+i*vd];
}
}
}
else
{
if (ordering==Ordering::byNODES)
{
for (int di=0; di<vd; di++)
{
(*dest)[di+ind*vd]=src[di*np+i];
}
}
else
{
for (int di=0; di<vd; di++)
{
(*dest)[di+ind*vd]=src[di+i*vd];
}
}
}
}
}
}
}
template<>
void KDTreeNodalProjection<2>::Project(const GridFunction& gf, real_t lerr)
{
int ordering = gf.FESpace()->GetOrdering();
Vector coo;
int np=gf.FESpace()->GetVSize()/gf.FESpace()->GetVDim();
coo.SetSize(np*2);
int vd=dest->VectorDim();
int ind;
real_t dist;
Vector maxbb_src(2);
Vector minbb_src(2);
// extract the nodal coordinates from gf
{
ElementTransformation *trans;
const IntegrationRule* ir=nullptr;
Array<int> vdofs;
DenseMatrix elco;
int isca=1;
if (gf.FESpace()->GetOrdering()==Ordering::byVDIM)
{
isca=gf.FESpace()->GetVDim();
}
// initialize bbmax and bbmin
const FiniteElement* el=gf.FESpace()->GetFE(0);
trans = gf.FESpace()->GetElementTransformation(0);
ir=&(el->GetNodes());
gf.FESpace()->GetElementVDofs(0,vdofs);
elco.SetSize(2,ir->GetNPoints());
trans->Transform(*ir,elco);
for (int d=0; d<2; d++)
{
maxbb_src(d)=elco(d,0);
minbb_src(d)=elco(d,0);
}
for (int i=0; i<gf.FESpace()->GetNE(); i++)
{
el=gf.FESpace()->GetFE(i);
//get the element transformation
trans = gf.FESpace()->GetElementTransformation(i);
ir=&(el->GetNodes());
gf.FESpace()->GetElementVDofs(i,vdofs);
elco.SetSize(2,ir->GetNPoints());
trans->Transform(*ir,elco);
for (int p=0; p<ir->GetNPoints(); p++)
{
for (int d=0; d<2; d++)
{
coo[vdofs[p]*2/isca+d]=elco(d,p);
if (maxbb_src(d)<elco(d,p)) {maxbb_src(d)=elco(d,p);}
if (minbb_src(d)>elco(d,p)) {minbb_src(d)=elco(d,p);}
}
}
}
}
maxbb_src+=lerr;
minbb_src-=lerr;
// check for intersection
bool flag;
{
flag=true;
for (int i=0; i<2; i++)
{
if (minbb_src(i)>maxbb(i)) {flag=false;}
if (maxbb_src(i)<minbb(i)) {flag=false;}
}
if (flag==false) {return;}
}
{
KDTree2D::PointND pnd;
for (int i=0; i<np; i++)
{
pnd.xx[0]=coo(i*2+0);
pnd.xx[1]=coo(i*2+1);
kdt->FindClosestPoint(pnd,ind,dist);
if (dist<lerr)
{
if (dest->FESpace()->GetOrdering()==Ordering::byNODES)
{
if (ordering==Ordering::byNODES)
{
for (int di=0; di<vd; di++)
{
(*dest)[di*np+ind]=gf[di*np+i];
}
}
else
{
for (int di=0; di<vd; di++)
{
(*dest)[di*np+ind]=gf[di+i*vd];
}
}
}
else
{
if (ordering==Ordering::byNODES)
{
for (int di=0; di<vd; di++)
{
(*dest)[di+ind*vd]=gf[di*np+i];
}
}
else
{
for (int di=0; di<vd; di++)
{
(*dest)[di+ind*vd]=gf[di+i*vd];
}
}
}
}
}
}
}
template<>
void KDTreeNodalProjection<3>::Project(const GridFunction& gf, real_t lerr)
{
int ordering = gf.FESpace()->GetOrdering();
int dim=dest->FESpace()->GetMesh()->SpaceDimension();
Vector coo;
int np=gf.FESpace()->GetVSize()/gf.FESpace()->GetVDim();
coo.SetSize(np*dim);
int vd=dest->VectorDim();
int ind;
real_t dist;
Vector maxbb_src(dim);
Vector minbb_src(dim);
// extract the nodal coordinates from gf
{
ElementTransformation *trans;
const IntegrationRule* ir=nullptr;
Array<int> vdofs;
DenseMatrix elco;
int isca=1;
if (gf.FESpace()->GetOrdering()==Ordering::byVDIM)
{
isca=gf.FESpace()->GetVDim();
}
// initialize bbmax and bbmin
const FiniteElement* el=gf.FESpace()->GetFE(0);
trans = gf.FESpace()->GetElementTransformation(0);
ir=&(el->GetNodes());
gf.FESpace()->GetElementVDofs(0,vdofs);
elco.SetSize(dim,ir->GetNPoints());
trans->Transform(*ir,elco);
for (int d=0; d<dim; d++)
{
maxbb_src(d)=elco(d,0);
minbb_src(d)=elco(d,0);
}
for (int i=0; i<gf.FESpace()->GetNE(); i++)
{
el=gf.FESpace()->GetFE(i);
// get the element transformation
trans = gf.FESpace()->GetElementTransformation(i);
ir=&(el->GetNodes());
gf.FESpace()->GetElementVDofs(i,vdofs);
elco.SetSize(dim,ir->GetNPoints());
trans->Transform(*ir,elco);
for (int p=0; p<ir->GetNPoints(); p++)
{
for (int d=0; d<dim; d++)
{
coo[vdofs[p]*dim/isca+d]=elco(d,p);
if (maxbb_src(d)<elco(d,p)) {maxbb_src(d)=elco(d,p);}
if (minbb_src(d)>elco(d,p)) {minbb_src(d)=elco(d,p);}
}
}
}
}
maxbb_src+=lerr;
minbb_src-=lerr;
// check for intersection
bool flag;
{
flag=true;
for (int i=0; i<dim; i++)
{
if (minbb_src(i)>maxbb(i)) {flag=false;}
if (maxbb_src(i)<minbb(i)) {flag=false;}
}
if (flag==false) {return;}
}
{
KDTree3D::PointND pnd;
for (int i=0; i<np; i++)
{
pnd.xx[0]=coo(i*dim+0);
pnd.xx[1]=coo(i*dim+1);
pnd.xx[2]=coo(i*dim+2);
kdt->FindClosestPoint(pnd,ind,dist);
if (dist<lerr)
{
if (dest->FESpace()->GetOrdering()==Ordering::byNODES)
{
if (ordering==Ordering::byNODES)
{
for (int di=0; di<vd; di++)
{
(*dest)[di*np+ind]=gf[di*np+i];
}
}
else
{
for (int di=0; di<vd; di++)
{
(*dest)[di*np+ind]=gf[di+i*vd];
}
}
}
else
{
if (ordering==Ordering::byNODES)
{
for (int di=0; di<vd; di++)
{
(*dest)[di+ind*vd]=gf[di*np+i];
}
}
else
{
for (int di=0; di<vd; di++)
{
(*dest)[di+ind*vd]=gf[di+i*vd];
}
}
}
}
}
}
}
} // namespace mfem