Skip to content

Commit

Permalink
added: class for simple global node number establishment for LR splines
Browse files Browse the repository at this point in the history
  • Loading branch information
akva2 committed Jun 21, 2024
1 parent 93cfb72 commit 78e3280
Show file tree
Hide file tree
Showing 3 changed files with 311 additions and 0 deletions.
187 changes: 187 additions & 0 deletions src/ASM/LR/GlobalNodes.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
// $Id$
//==============================================================================
//!
//! \file GlobalNodes.C
//!
//! \date Mar 13 2018
//!
//! \author Arne Morten Kvarving / SINTEF
//!
//! \brief Simple global node establishment for unstructured FE models.
//!
//==============================================================================

#include "GlobalNodes.h"

#include "ASMLRSpline.h"
#include "Utilities.h"

#include <numeric>

namespace {

/*!
\brief Class for ordering interfaces in processing order.
*/

class InterfaceOrder {
public:
//! \brief Comparison operator for interfaces
//! \param A First interface
//! \param B Second interface
bool operator()(const ASM::Interface& A, const ASM::Interface& B) const
{
if (A.master != B.master)
return A.master < B.master;

if (A.slave != B.slave)
return A.slave < B.slave;

if (A.dim != B.dim)
return A.dim < B.dim;

return A.midx < B.midx;
}
};

}


GlobalNodes::IntVec
GlobalNodes::getBoundaryNodes (const LR::LRSpline& lr,
int dim, int lidx, int orient)
{
LR::parameterEdge edge{};
if (dim == 0) {
if (lr.nVariate() == 2) {
switch (lidx) {
case 1: edge = LR::WEST | LR::SOUTH; break;
case 2: edge = LR::EAST | LR::SOUTH; break;
case 3: edge = LR::WEST | LR::NORTH; break;
case 4: edge = LR::EAST | LR::NORTH; break;
}
} else {
switch (lidx) {
case 1: edge = LR::WEST | LR::SOUTH | LR::BOTTOM; break;
case 2: edge = LR::EAST | LR::SOUTH | LR::BOTTOM; break;
case 3: edge = LR::WEST | LR::NORTH | LR::BOTTOM; break;
case 4: edge = LR::EAST | LR::NORTH | LR::BOTTOM; break;
case 5: edge = LR::WEST | LR::SOUTH | LR::TOP; break;
case 6: edge = LR::EAST | LR::SOUTH | LR::TOP; break;
case 7: edge = LR::WEST | LR::NORTH | LR::TOP; break;
case 8: edge = LR::EAST | LR::NORTH | LR::TOP; break;
}
}
} else if (dim == 1) {
if (lr.nVariate() == 2) {
switch (lidx) {
case 1: edge = LR::WEST; break;
case 2: edge = LR::EAST; break;
case 3: edge = LR::SOUTH; break;
case 4: edge = LR::NORTH; break;
default: break;
}
} else {
switch (lidx) {
case 1: edge = LR::BOTTOM | LR::SOUTH; break;
case 2: edge = LR::BOTTOM | LR::NORTH; break;
case 3: edge = LR::TOP | LR::SOUTH; break;
case 4: edge = LR::TOP | LR::NORTH; break;
case 5: edge = LR::BOTTOM | LR::WEST; break;
case 6: edge = LR::BOTTOM | LR::EAST; break;
case 7: edge = LR::TOP | LR::WEST; break;
case 8: edge = LR::TOP | LR::WEST; break;
case 9: edge = LR::SOUTH | LR::WEST; break;
case 10: edge = LR::SOUTH | LR::EAST; break;
case 11: edge = LR::NORTH | LR::WEST; break;
case 12: edge = LR::NORTH | LR::EAST; break;
}
}
} else if (dim == 2) {
switch (lidx) {
case 1: edge = LR::WEST; break;
case 2: edge = LR::EAST; break;
case 3: edge = LR::SOUTH; break;
case 4: edge = LR::NORTH; break;
case 5: edge = LR::BOTTOM; break;
case 6: edge = LR::TOP; break;
}
}

std::vector<LR::Basisfunction*> edgeFunctions;
lr.getEdgeFunctions(edgeFunctions, edge);

if (dim == 1) {
if (lr.nVariate() == 2) {
int v = (lidx == 1 || lidx == 2) ? 0 : 1;
int u = 1-v;
ASMLRSpline::Sort(u, v, orient, edgeFunctions);
} else {
int dir = (lidx-1)/4;
int u = dir == 0;
int v = 1 + (dir != 2);
ASMLRSpline::Sort(u, v, orient, edgeFunctions);
}
} else if (dim == 2) {
int dir = (lidx-1)/2;
int u = dir == 0;
int v = 1 + (dir != 2);
ASMLRSpline::Sort(u, v, orient, edgeFunctions);
}

GlobalNodes::IntVec lNodes;
lNodes.reserve(edgeFunctions.size());
for (const LR::Basisfunction* func : edgeFunctions)
lNodes.push_back(func->getId());

return lNodes;
}


std::vector<GlobalNodes::IntVec>
GlobalNodes::calcGlobalNodes (const GlobalNodes::LRSplineVec& pchs,
const GlobalNodes::InterfaceVec& interfaces)
{
// count total number of nodes
size_t nNodes = 0;
std::vector<GlobalNodes::IntVec> result(pchs.size());
auto it = result.begin();
for (const auto& pch : pchs) {
it->resize(pch->nBasisFunctions());
std::iota(it->begin(), it->end(), nNodes);
nNodes += pch->nBasisFunctions();
++it;
}

// remap common nodes
InterfaceOrder ifOrder;
std::set<ASM::Interface, InterfaceOrder> ifset(ifOrder);
for (const ASM::Interface& it : interfaces)
ifset.insert(it);
for (size_t i = 0; i < pchs.size(); ++i) {
std::map<int,int> old2new;
for (const ASM::Interface& it : ifset) {
if (it.master != (int)i+1)
continue;

IntVec mNodes = getBoundaryNodes(*pchs[i], it.dim, it.midx, 0);
IntVec sNodes = getBoundaryNodes(*pchs[it.slave-1], it.dim, it.sidx, it.orient);
for (size_t n = 0; n < mNodes.size(); ++n)
old2new[result[it.slave-1][sNodes[n]]] = result[i][mNodes[n]];
}

// renumber
for (size_t j = i; j < pchs.size(); ++j)
for (int& it : result[j])
utl::renumber(it, old2new, false);

// compress
int maxNode = *std::max_element(result[i].begin(), result[i].end());
for (size_t j = i+1; j < pchs.size(); ++j)
for (int& n : result[j])
if (n > maxNode)
n = ++maxNode;
}

return result;
}
50 changes: 50 additions & 0 deletions src/ASM/LR/GlobalNodes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// $Id$
//==============================================================================
//!
//! \file GlobalNodes.h
//!
//! \date Mar 13 2018
//!
//! \author Arne Morten Kvarving / SINTEF
//!
//! \brief Simple global node establishment for unstructured FE models.
//!
//==============================================================================

#ifndef _GLOBAL_NODES_H_
#define _GLOBAL_NODES_H_

#include "Interface.h"
#include <LRSpline/LRSplineSurface.h>

#include <vector>


/*!
\brief Class establishing global node numbers for unstructed FE models.
*/

class GlobalNodes
{
public:
using IntVec = std::vector<int>; //!< Convenience typedef
using LRSplineVec = std::vector<const LR::LRSpline*>; //!< Convenience typedef
using InterfaceVec = std::vector<ASM::Interface>; //!< Convenience typedef

//! \brief Extract local boundary nodes for a LR spline.
//! \param lr The LR spline to extract boundary nodes for
//! \param dim The dimension of the boundary to extract
//! \param lidx The local index of the boundary to extract
//! \param orient Orientation of nodes on boundary
static IntVec getBoundaryNodes(const LR::LRSpline& lr,
int dim, int lidx, int orient);


//! \brief Calculate global node numbers for a FE model.
//! \param pchs The spline patches in the model
//! \param interfaces The topological connections for the spline patches
static std::vector<IntVec> calcGlobalNodes(const LRSplineVec& pchs,
const InterfaceVec& interfaces);
};

#endif
74 changes: 74 additions & 0 deletions src/ASM/LR/Test/TestGlobalNodes.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//==============================================================================
//!
//! \file TestGlobalNodes.C
//!
//! \date Jun 21 2024
//!
//! \author Arne Morten Kvarving / SINTEF
//!
//! \brief Tests for simple global node establishment for unstructured FE models.
//!
//==============================================================================

#include "GlobalNodes.h"
#include "ASMuCube.h"
#include "ASMuSquare.h"

#include "LRSpline/LRSplineVolume.h"

#include "gtest/gtest.h"


TEST(TestGlobalNodes, 2D)
{
ASMuSquare pch1;
pch1.generateFEMTopology();
ASMuSquare pch2(2, 1.0, 0.0);
pch2.generateFEMTopology();
ASMuSquare pch3(2, 1.0, 1.0);
pch3.generateFEMTopology();

std::vector<const LR::LRSpline*> splines{pch1.getBasis(),
pch2.getBasis(),
pch3.getBasis()};
std::vector<ASM::Interface> ifs;
ifs.push_back(ASM::Interface{1, 2, 2, 1, 0, 1, 1, 0});
ifs.push_back(ASM::Interface{2, 3, 4, 3, 0, 1, 1, 0});

auto nodes = GlobalNodes::calcGlobalNodes(splines, ifs);
const auto ref = std::vector{
GlobalNodes::IntVec{0, 1, 2, 3},
GlobalNodes::IntVec{1, 4, 3, 5},
GlobalNodes::IntVec{3, 5, 6, 7},
};

EXPECT_EQ(nodes, ref);
}


TEST(TestGlobalNodes, 3D)
{
ASMuCube pch1;
pch1.generateFEMTopology();
ASMuCube pch2(2, 1.0, 0.0, 0.0);
pch2.generateFEMTopology();
ASMuCube pch3(2, 1.0, 1.0, 1.0);
pch3.generateFEMTopology();

std::vector<const LR::LRSpline*> splines{pch1.getBasis(),
pch2.getBasis(),
pch3.getBasis()};
std::vector<ASM::Interface> ifs;
ifs.push_back(ASM::Interface{1, 2, 2, 1, 0, 2, 1, 0});
ifs.push_back(ASM::Interface{2, 3, 6, 5, 0, 2, 1, 0});

auto nodes = GlobalNodes::calcGlobalNodes(splines, ifs);

const auto ref = std::vector{
GlobalNodes::IntVec{0, 1, 2, 3, 4, 5, 6, 7},
GlobalNodes::IntVec{1, 8, 3, 9, 5, 10, 7, 11},
GlobalNodes::IntVec{5, 10, 7, 11, 12, 13, 14, 15},
};

EXPECT_EQ(nodes, ref);
}

0 comments on commit 78e3280

Please sign in to comment.