fixed: wrap the multi-patch refinement in a loop

if not, neighbour patches will miss the refinements
imposed by the extended refinement domain for a patch.
This commit is contained in:
Arne Morten Kvarving 2020-04-20 08:43:37 +02:00
parent 2d3aac464d
commit 6e5f087551
3 changed files with 188 additions and 36 deletions

View File

@ -27,7 +27,12 @@ install(FILES scripts/regtest.sh.in
WORLD_READ WORLD_EXECUTE)
# Unit tests
IFEM_add_test_app(${PROJECT_SOURCE_DIR}/Test/*.C
file(GLOB APPCOMMON_TEST_SRCS ${PROJECT_SOURCE_DIR}/Test/*.C)
if(LRSpline_FOUND)
list(APPEND APPCOMMON_TEST_SRCS ${PROJECT_SOURCE_DIR}/Test/LR/TestMultiPatchLRRefine.C)
endif()
IFEM_add_test_app("${APPCOMMON_TEST_SRCS}"
${PROJECT_SOURCE_DIR}/Test
AppCommon
IFEMAppCommon ${IFEM_LIBRARIES})

View File

@ -0,0 +1,131 @@
//==============================================================================
//!
//! \file TestMultiPatchLRRefine.C
//!
//! \date Mar 31 2020
//!
//! \author Arne Morten Kvarving / SINTEF
//!
//! \brief Tests for multi-patch LR refinement.
//!
//==============================================================================
#include "ASMbase.h"
#include "ASMunstruct.h"
#include "IntegrandBase.h"
#include "MultiPatchModelGenerator.h"
#include "SIMMultiPatchModelGen.h"
#include "SIM2D.h"
#include "SIM3D.h"
#include "tinyxml.h"
#include "gtest/gtest.h"
#include <fstream>
// Dummy SIM class.
template<class Dim>
class RefineSim : public SIMMultiPatchModelGen<Dim>
{
public:
RefineSim() : SIMMultiPatchModelGen<Dim>(Dim::dimension)
{
Dim::opt.discretization = ASM::LRSpline;
}
bool parse(const TiXmlElement* elem) override
{ return this->SIMMultiPatchModelGen<Dim>::parse(elem); }
virtual ~RefineSim() {}
};
class TestMultiPatchLRRefine2D :
public testing::Test,
public testing::WithParamInterface<int>
{
};
TEST_P(TestMultiPatchLRRefine2D, Refine)
{
RefineSim<SIM2D> sim;
TiXmlDocument doc;
std::stringstream str;
str << R"(<geometry dim="2" nx="2" ny="2">)"
<< R"( <raiseorder lowerpatch="1" upperpatch="4)"
<< R"(" u=")" << GetParam()
<< R"(" v=")" << GetParam() << '"' << "/>"
<< "</geometry>";
doc.Parse(str.str().c_str());
MultiPatchModelGenerator2D gen(doc.RootElement());
EXPECT_TRUE(sim.parse(doc.RootElement()));
EXPECT_TRUE(sim.preprocess());
srand(0);
for (size_t i = 0; i < 4; ++i) {
LR::RefineData prm;
sim.getPatch(1 + (rand() % 4))->getBoundaryNodes(1 + (rand() % 4), prm.elements);
prm.options.resize(3);
prm.options[0] = 1;
prm.options[1] = 1;
prm.options[2] = 2;
sim.refine(prm);
sim.clearProperties();
ASSERT_TRUE(gen.createTopology(sim) && sim.preprocess());
}
}
class TestMultiPatchLRRefine3D :
public testing::Test,
public testing::WithParamInterface<int>
{
};
TEST_P(TestMultiPatchLRRefine3D, Refine)
{
RefineSim<SIM3D> sim;
TiXmlDocument doc;
std::stringstream str;
str << R"(<geometry dim="3" nx="2" ny="2" nz="2">)"
<< R"( <raiseorder lowerpatch="1" upperpatch="8")"
<< R"( u=")" << GetParam()
<< R"(" v=")" << GetParam()
<< R"(" w=")" << GetParam()
<< '"' << "/>"
<< "</geometry>";
doc.Parse(str.str().c_str());
MultiPatchModelGenerator3D gen(doc.RootElement());
EXPECT_TRUE(sim.parse(doc.RootElement()));
EXPECT_TRUE(sim.preprocess());
srand(0);
for (size_t i = 0; i < 3; ++i) {
LR::RefineData prm;
sim.getPatch(1 + (rand() % 8))->getBoundaryNodes(1 + (rand() % 6), prm.elements);
prm.options.resize(3);
prm.options[0] = 1;
prm.options[1] = 1;
prm.options[2] = 2;
sim.refine(prm);
sim.clearProperties();
ASSERT_TRUE(gen.createTopology(sim) && sim.preprocess());
}
}
const std::vector<int> refValues = {0,1,2};
INSTANTIATE_TEST_CASE_P(TestMultiPatchLRRefine2D,
TestMultiPatchLRRefine2D,
testing::ValuesIn(refValues));
INSTANTIATE_TEST_CASE_P(TestMultiPatchLRRefine3D,
TestMultiPatchLRRefine3D,
testing::ValuesIn(refValues));

View File

@ -1424,52 +1424,68 @@ bool SIMinput::refine (const LR::RefineData& prm, Vectors& sol)
std::vector<LR::RefineData> prmloc(myModel.size(),LR::RefineData(prm));
std::vector<IntSet> refineIndices(myModel.size());
std::vector<IntSet> conformingIndices(myModel.size());
for (size_t i = 0; i < myModel.size(); i++)
{
// Extract local indices from the vector of global indices
int locId;
for (int k : prm.elements)
if ((locId = myModel[i]->getNodeIndex(k+1)) > 0)
refineIndices[i].insert(locId-1);
// fetch all boundary nodes covered (may need to pass this to other patches)
pch = dynamic_cast<ASMunstruct*>(myModel[i]);
IntVec bndry_nodes = pch->getBoundaryCovered(refineIndices[i]);
// DESIGN NOTE: It is tempting here to use patch connectivity information.
// However, this does not account (in the general case)
// for cross-connections in L-shape geometries, i.e.,
//
// +-----+
// | #1 |
// | | patch #1 (edge 3) connected to patch #2 (edge 4)
// +-----+-----+ patch #2 (edge 2) connected to patch #3 (edge 1)
// | #2 | #3 |
// | | | we need to pass the corner index of patch #3 (vertex 3)
// +-----+-----+ to patch #1 (vertex 2), but this connection is not
// guaranteed to appear in the input file
// for all boundary nodes, check if these appear on other patches
for (int k : bndry_nodes)
bool changed = true;
while (changed) {
changed = false;
for (size_t i = 0; i < myModel.size(); i++)
{
int globId = myModel[i]->getNodeID(k+1);
for (size_t j = 0; j < myModel.size(); j++)
if (j != i && (locId = myModel[j]->getNodeIndex(globId)) > 0)
{
conformingIndices[j].insert(locId-1);
conformingIndices[i].insert(k);
// Extract local indices from the vector of global indices
int locId;
for (int k : prm.elements)
if ((locId = myModel[i]->getNodeIndex(k+1)) > 0) {
if (refineIndices[i].count(locId-1) == 0) {
refineIndices[i].insert(locId-1);
changed = true;
}
}
// fetch all boundary nodes covered (may need to pass this to other patches)
pch = dynamic_cast<ASMunstruct*>(myModel[i]);
IntVec bndry_nodes = pch->getBoundaryCovered(refineIndices[i]);
// DESIGN NOTE: It is tempting here to use patch connectivity information.
// However, this does not account (in the general case)
// for cross-connections in L-shape geometries, i.e.,
//
// +-----+
// | #1 |
// | | patch #1 (edge 3) connected to patch #2 (edge 4)
// +-----+-----+ patch #2 (edge 2) connected to patch #3 (edge 1)
// | #2 | #3 |
// | | | we need to pass the corner index of patch #3 (vertex 3)
// +-----+-----+ to patch #1 (vertex 2), but this connection is not
// guaranteed to appear in the input file
// for all boundary nodes, check if these appear on other patches
for (int k : bndry_nodes)
{
int globId = myModel[i]->getNodeID(k+1);
for (size_t j = 0; j < myModel.size(); j++)
if (j != i && (locId = myModel[j]->getNodeIndex(globId)) > 0)
{
if (conformingIndices[j].count(locId-1) == 0) {
changed = true;
conformingIndices[j].insert(locId-1);
conformingIndices[i].insert(k);
}
}
}
}
for (size_t i = 0; i < myModel.size(); i++)
{
pch = dynamic_cast<ASMunstruct*>(myModel[i]);
pch->extendRefinementDomain(refineIndices[i],conformingIndices[i]);
}
}
Vectors lsols;
lsols.reserve(sol.size()*myModel.size());
size_t ngNodes = this->getNoNodes(1);
for (size_t i = 0; i < myModel.size(); i++)
{
pch = dynamic_cast<ASMunstruct*>(myModel[i]);
pch->extendRefinementDomain(refineIndices[i],conformingIndices[i]);
LR::RefineData prmloc(prm);
prmloc.elements = IntVec(refineIndices[i].begin(),refineIndices[i].end());