From 78e32803c8b9c6992e34dbc3c370a62dec355931 Mon Sep 17 00:00:00 2001 From: Arne Morten Kvarving Date: Wed, 14 Mar 2018 15:59:04 +0100 Subject: [PATCH] added: class for simple global node number establishment for LR splines --- src/ASM/LR/GlobalNodes.C | 187 ++++++++++++++++++++++++++++++ src/ASM/LR/GlobalNodes.h | 50 ++++++++ src/ASM/LR/Test/TestGlobalNodes.C | 74 ++++++++++++ 3 files changed, 311 insertions(+) create mode 100644 src/ASM/LR/GlobalNodes.C create mode 100644 src/ASM/LR/GlobalNodes.h create mode 100644 src/ASM/LR/Test/TestGlobalNodes.C diff --git a/src/ASM/LR/GlobalNodes.C b/src/ASM/LR/GlobalNodes.C new file mode 100644 index 000000000..d5ea012b2 --- /dev/null +++ b/src/ASM/LR/GlobalNodes.C @@ -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 + +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 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::calcGlobalNodes (const GlobalNodes::LRSplineVec& pchs, + const GlobalNodes::InterfaceVec& interfaces) +{ + // count total number of nodes + size_t nNodes = 0; + std::vector 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 ifset(ifOrder); + for (const ASM::Interface& it : interfaces) + ifset.insert(it); + for (size_t i = 0; i < pchs.size(); ++i) { + std::map 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; +} diff --git a/src/ASM/LR/GlobalNodes.h b/src/ASM/LR/GlobalNodes.h new file mode 100644 index 000000000..3e7a38461 --- /dev/null +++ b/src/ASM/LR/GlobalNodes.h @@ -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 + +#include + + +/*! + \brief Class establishing global node numbers for unstructed FE models. +*/ + +class GlobalNodes +{ +public: + using IntVec = std::vector; //!< Convenience typedef + using LRSplineVec = std::vector; //!< Convenience typedef + using InterfaceVec = std::vector; //!< 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 calcGlobalNodes(const LRSplineVec& pchs, + const InterfaceVec& interfaces); +}; + +#endif diff --git a/src/ASM/LR/Test/TestGlobalNodes.C b/src/ASM/LR/Test/TestGlobalNodes.C new file mode 100644 index 000000000..e705fd3a3 --- /dev/null +++ b/src/ASM/LR/Test/TestGlobalNodes.C @@ -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 splines{pch1.getBasis(), + pch2.getBasis(), + pch3.getBasis()}; + std::vector 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 splines{pch1.getBasis(), + pch2.getBasis(), + pch3.getBasis()}; + std::vector 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); +}