From 66d33ff9e5cd7a9bef6e032d8321c5715a650fbc Mon Sep 17 00:00:00 2001 From: Arne Morten Kvarving Date: Fri, 2 Sep 2016 17:22:51 +0200 Subject: [PATCH] added: multi-patch model generator for 2D and 3D --- Apps/Common/MultiPatchModelGenerator.C | 463 +++++++++ Apps/Common/MultiPatchModelGenerator.h | 100 ++ .../Test/TestMultiPatchModelGenerator.C | 882 ++++++++++++++++++ 3 files changed, 1445 insertions(+) create mode 100644 Apps/Common/MultiPatchModelGenerator.C create mode 100644 Apps/Common/MultiPatchModelGenerator.h create mode 100644 Apps/Common/Test/TestMultiPatchModelGenerator.C diff --git a/Apps/Common/MultiPatchModelGenerator.C b/Apps/Common/MultiPatchModelGenerator.C new file mode 100644 index 00000000..0b24bd51 --- /dev/null +++ b/Apps/Common/MultiPatchModelGenerator.C @@ -0,0 +1,463 @@ +// $Id$ +//============================================================================== +//! +//! \file MultiPatchModelGenerator.C +//! +//! \date Sep 2 2016 +//! +//! \author Arne Morten Kvarving / SINTEF +//! +//! \brief Multi-patch model generators for NURBS-based FEM simulators. +//! +//============================================================================== + +#include "MultiPatchModelGenerator.h" +#include "ASMs2D.h" +#include "ASMs3D.h" +#include "IFEM.h" +#include "SIMbase.h" +#include "Utilities.h" +#include "Vec3.h" +#include "Vec3Oper.h" +#include "tinyxml.h" + + +MultiPatchModelGenerator2D::MultiPatchModelGenerator2D (const TiXmlElement* geo) : + ModelGenerator(geo) +{ + nx = ny = 1; + periodic_x = periodic_y = 0; + utl::getAttribute(geo,"nx",nx); + utl::getAttribute(geo,"ny",ny); + utl::getAttribute(geo,"periodic_x", periodic_x); + utl::getAttribute(geo,"periodic_y", periodic_y); +} + + +std::string MultiPatchModelGenerator2D::createG2 (int nsd) const +{ + bool rational=false; + utl::getAttribute(geo,"rational",rational); + if (rational) + IFEM::cout << "\t Rational basis\n"; + double scale = 1.0; + if (utl::getAttribute(geo,"scale",scale)) + IFEM::cout <<" Scale: "<< scale << std::endl; + + double Lx = 1.0, Ly = 1.0; + if (utl::getAttribute(geo,"Lx",Lx)) + IFEM::cout <<" Length in X: "<< Lx << std::endl; + Lx *= scale; + if (utl::getAttribute(geo,"Ly",Ly)) + IFEM::cout <<" Length in Y: "<< Ly << std::endl; + Ly *= scale; + + Vec3 X0; + std::string corner; + if (utl::getAttribute(geo,"X0",corner)) { + std::stringstream str(corner); str >> X0; + IFEM::cout <<" Corner: "<< X0 << std::endl; + } + + int nx = 1; + int ny = 1; + if (utl::getAttribute(geo,"nx",nx)) + IFEM::cout << " Split in X: " << nx << std::endl; + if (utl::getAttribute(geo,"ny",ny)) + IFEM::cout << " Split in Y: " << ny << std::endl; + + if (nx > 1) + Lx /= nx; + if (ny > 1) + Ly /= ny; + + std::string g2; + for (int y = 0; y < ny; ++y) { + for (int x = 0; x < nx; ++x) { + g2.append("200 1 0 0\n"); + g2.append(nsd > 2 ? "3" : "2"); + g2.append(rational?" 1":" 0"); + g2.append("\n2 2\n0 0 1 1\n2 2\n0 0 1 1"); + + std::stringstream str; + str <<"\n"<< X0.x+x*Lx <<" "<< X0.y+y*Ly; + if (nsd > 2) str <<" 0.0"; + if (rational) str << " 1.0"; + g2.append(str.str()); + str.str(""); + str <<"\n"<< X0.x+(x+1)*Lx <<" "<< X0.y+y*Ly; + if (nsd > 2) str <<" 0.0"; + if (rational) str << " 1.0"; + g2.append(str.str()); + str.str(""); + str <<"\n"<< X0.x+x*Lx <<" "<< X0.y+(y+1)*Ly; + if (nsd > 2) str <<" 0.0"; + if (rational) str << " 1.0"; + g2.append(str.str()); + str.str(""); + str <<"\n"<< X0.x+(x+1)*Lx <<" "<< X0.y+(y+1)*Ly; + if (nsd > 2) str <<" 0.0"; + if (rational) str << " 1.0"; + g2.append(str.str()); + g2.append("\n"); + } + } + + return g2; +} + + +SIMdependency::PatchVec +MultiPatchModelGenerator2D::createGeometry (const SIMbase& sim) const +{ + std::istringstream rect(this->createG2(sim.getNoSpaceDim())); + SIMdependency::PatchVec result; + sim.readPatches(rect,result,"\t"); + return result; +} + + +bool MultiPatchModelGenerator2D::createTopology (SIMbase& sim) const +{ + auto&& IJ = [this](int i, int j) { return 1 + j*nx + i; }; + + for (int j = 0; j < ny; ++j) + for (int i = 0; i < nx-1; ++i) + if (!sim.addConnection(IJ(i,j), IJ(i+1,j), 2, 1, 0)) + return false; + + for (int j = 0; j < ny-1; ++j) + for (int i = 0; i < nx; ++i) + if (!sim.addConnection(IJ(i,j), IJ(i,j+1), 4, 3, 0)) + return false; + + if (periodic_x) + for (int i = 0; i < ny; ++i) + if (nx > 1) { + if (!sim.addConnection(IJ(0, i), IJ(nx-1, i), 1, 2, 0, false)) + return false; + } else { + IFEM::cout <<"\tPeriodic I-direction P"<< IJ(0,i) << std::endl; + ASMs2D* pch = dynamic_cast(sim.getPatch(IJ(0,i), true)); + if (pch) + pch->closeEdges(1); + } + + if (periodic_y) + for (int i = 0; i < nx; ++i) + if (ny > 1) + if (!sim.addConnection(IJ(i,0), IJ(i,ny-1), 3, 4, 0, false)) + return false; + else { + IFEM::cout <<"\tPeriodic J-direction P"<< IJ(i,0)<< std::endl; + ASMs2D* pch = dynamic_cast(sim.getPatch(IJ(i,0), true)); + if (pch) + pch->closeEdges(2); + } + + return true; +} + + +TopologySet +MultiPatchModelGenerator2D::createTopologySets (const SIMbase& sim) const +{ + if (!sets) + return TopologySet(); + + TopologySet result; + TopEntity& e1 = result["Edge1"]; + TopEntity& e2 = result["Edge2"]; + TopEntity& e3 = result["Edge3"]; + TopEntity& e4 = result["Edge4"]; + TopEntity& e5 = result["Boundary"]; + + auto&& insertion = [&sim, &e5](TopEntity& e, TopItem top) + { + if ((top.patch = sim.getLocalPatchIndex(top.patch)) > 0) { + e.insert(top); + e5.insert(top); + } + }; + + for (int i = 0; i < ny; ++i) { + insertion(e1, TopItem(i*nx+1,1,1)); + insertion(e2, TopItem((i+1)*nx,2,1)); + } + for (int i = 0; i < nx; ++i) { + insertion(e3, TopItem(i+1,3,1)); + insertion(e4, TopItem(nx*(ny-1)+1+i,4,1)); + } + + TopEntity& c = result["Corners"]; + auto&& insertionv = [&sim, &c](TopEntity& e, TopItem top) + { + if ((top.patch = sim.getLocalPatchIndex(top.patch)) > 0) { + e.insert(top); + c.insert(top); + } + }; + + insertionv(result["Vertex1"], TopItem(1,1,0)); + insertionv(result["Vertex2"], TopItem(nx,2,0)); + insertionv(result["Vertex3"], TopItem(nx*(ny-1)+1,3,0)); + insertionv(result["Vertex4"], TopItem(nx*ny,4,0)); + + return result; +} + + +MultiPatchModelGenerator3D::MultiPatchModelGenerator3D (const TiXmlElement* geo) : + ModelGenerator(geo) +{ + nx = ny = nz = 1; + periodic_x = periodic_y = periodic_z = 0; + utl::getAttribute(geo,"nx",nx); + utl::getAttribute(geo,"ny",ny); + utl::getAttribute(geo,"nz",nz); + utl::getAttribute(geo,"periodic_x", periodic_x); + utl::getAttribute(geo,"periodic_y", periodic_y); + utl::getAttribute(geo,"periodic_z", periodic_z); +} + + +std::string MultiPatchModelGenerator3D::createG2 (int) const +{ + bool rational = false; + utl::getAttribute(geo,"rational",rational); + if (rational) + IFEM::cout <<" Rational basis"<< std::endl; + + double scale = 1.0; + if (utl::getAttribute(geo,"scale",scale)) + IFEM::cout <<" Scale: "<< scale << std::endl; + + double Lx = 1.0, Ly = 1.0, Lz = 1.0; + if (utl::getAttribute(geo,"Lx",Lx)) + IFEM::cout <<" Length in X: "<< Lx << std::endl; + Lx *= scale; + if (utl::getAttribute(geo,"Ly",Ly)) + IFEM::cout <<" Length in Y: "<< Ly << std::endl; + Ly *= scale; + if (utl::getAttribute(geo,"Lz",Lz)) + IFEM::cout <<" Length in Z: "<< Lz << std::endl; + Lz *= scale; + + int nx = 1; + int ny = 1; + int nz = 1; + if (utl::getAttribute(geo,"nx",nx)) + IFEM::cout << " Split in X: " << nx << std::endl; + if (utl::getAttribute(geo,"ny",ny)) + IFEM::cout << " Split in Y: " << ny << std::endl; + if (utl::getAttribute(geo,"nz",nz)) + IFEM::cout << " Split in Z: " << nz << std::endl; + + Lx /= nx; + Ly /= ny; + Lz /= nz; + + std::string corner; + Vec3 X0; + if (utl::getAttribute(geo,"X0",corner)) { + std::stringstream str(corner); + str >> X0; + IFEM::cout <<" Corner: "<< X0 << std::endl; + } + + std::array nodes = + {{ 0.0, 0.0, 0.0, + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 1.0, 1.0, 0.0, + 0.0, 0.0, 1.0, + 1.0, 0.0, 1.0, + 0.0, 1.0, 1.0, + 1.0, 1.0, 1.0 }}; + + std::string g2; + for (int z = 0; z < nz; ++z) { + for (int y = 0; y < ny; ++y) { + for (int x = 0; x < nx; ++x) { + g2.append("700 1 0 0\n3 "); + g2.append(rational ? "1\n" : "0\n"); + g2.append("2 2\n0 0 1 1\n" + "2 2\n0 0 1 1\n" + "2 2\n0 0 1 1\n"); + + for (size_t i = 0; i < nodes.size(); i += 3) + { + std::stringstream str; + std::array N {x,y,z}; + std::array L {Lx,Ly,Lz}; + for (size_t j = 0; j < 3; j++) + str << (j==0?"":" ") << X0[j]+N[j]*L[j]+nodes[i+j]*L[j]; + g2.append(str.str()); + g2.append(rational ? " 1.0\n" : "\n"); + } + } + } + } + + return g2; +} + + +SIMdependency::PatchVec +MultiPatchModelGenerator3D::createGeometry (const SIMbase& sim) const +{ + std::istringstream hex(this->createG2(sim.getNoSpaceDim())); + SIMdependency::PatchVec result; + sim.readPatches(hex,result,"\t"); + return result; +} + + +bool MultiPatchModelGenerator3D::createTopology (SIMbase& sim) const +{ + auto&& IJK = [this](int i, int j, int k) { return 1 + (k*ny+j)*nx + i; }; + + for (int k = 0; k < nz; ++k) + for (int j = 0; j < ny; ++j) + for (int i = 0; i < nx-1; ++i) + if (!sim.addConnection(IJK(i,j,k), IJK(i+1,j,k), 2, 1, 0)) + return false; + + for (int k = 0; k < nz; ++k) + for (int j = 0; j < ny-1; ++j) + for (int i = 0; i < nx; ++i) + if (!sim.addConnection(IJK(i,j,k), IJK(i,j+1,k), 4, 3, 0)) + return false; + + for (int k = 0; k < nz-1; ++k) + for (int j = 0; j < ny; ++j) + for (int i = 0; i < nx; ++i) + if (!sim.addConnection(IJK(i,j,k), IJK(i,j,k+1), 6, 5, 0)) + return false; + + if (periodic_x) + for (int k = 0; k < nz; ++k) + for (int j = 0; j < ny; ++j) + if (nx > 1) { + if (!sim.addConnection(IJK(0,j,k), IJK(nx-1,j,k), 1, 2, 0, false)) + return false; + } else { + IFEM::cout <<"\tPeriodic I-direction P"<< IJK(0,j,k) << std::endl; + ASMs3D* pch = dynamic_cast(sim.getPatch(IJK(0,j,k), true)); + if (pch) + pch->closeFaces(1); + } + + if (periodic_y) + for (int k = 0; k < nz; ++k) + for (int i = 0; i < nx; ++i) + if (ny > 1) { + if (!sim.addConnection(IJK(i,0,k), IJK(i,ny-1,k), 3, 4, 0, false)) + return false; + } else { + IFEM::cout <<"\tPeriodic J-direction P"<< IJK(i,0,k) << std::endl; + ASMs3D* pch = dynamic_cast(sim.getPatch(IJK(i,0,k), true)); + if (pch) + pch->closeFaces(2); + } + + if (periodic_z) + for (int j = 0; j < ny; ++j) + for (int i = 0; i < nx; ++i) + if (nz > 1) { + if (!sim.addConnection(IJK(i,j,0), IJK(i,j,nz-1), 5, 6, 0, false)) + return false; + } else { + IFEM::cout <<"\tPeriodic K-direction P"<< IJK(i,j,0) << std::endl; + ASMs3D* pch = dynamic_cast(sim.getPatch(IJK(i,j,0), true)); + if (pch) + pch->closeFaces(3); + } + + return true; + +} + +TopologySet +MultiPatchModelGenerator3D::createTopologySets (const SIMbase& sim) const +{ + if (!sets) + return TopologySet(); + + // 0-based -> 1-based IJK + auto&& IJK = [this](int i, int j, int k) { return 1 + (k*ny+j)*nx + i; }; + + // start/end IJK + auto&& IJK2 = [this,IJK](int i, int j, int k) { return IJK(i*(nx-1), j*(ny-1), k*(nz-1)); }; + + // start/end JK + auto&& IJKI = [this,IJK](int i, int j, int k) { return IJK(i, j*(ny-1), k*(nz-1)); }; + // start/end IK + auto&& IJKJ = [this,IJK](int i, int j, int k) { return IJK(i*(nx-1), j, k*(nz-1)); }; + // start/end IJ + auto&& IJKK = [this,IJK](int i, int j, int k) { return IJK(i*(nx-1), j*(ny-1), k); }; + + // start/end I + auto&& IJK2I = [this,IJK](int i, int j, int k) { return IJK(i*(nx-1), j, k); }; + // start/end J + auto&& IJK2J = [this,IJK](int i, int j, int k) { return IJK(i, j*(ny-1), k); }; + // start/end K + auto&& IJK2K = [this,IJK](int i, int j, int k) { return IJK(i, j, k*(nz-1)); }; + + TopologySet result; + + // insertion lambda + auto&& insertion = [&sim,&result](TopItem top, + const std::string& glob, + const std::string& type) + { + std::stringstream str; + str << type << top.item; + TopEntity& topI = result[str.str()]; + TopEntity& globI = result[glob]; + if ((top.patch = sim.getLocalPatchIndex(top.patch)) > 0) { + topI.insert(top); + globI.insert(top); + } + }; + + size_t r = 1; + for (int i = 0; i < 2; ++i, ++r) + for (int k = 0; k < nz; ++k) + for (int j = 0; j < ny; ++j) + insertion(TopItem(IJK2I(i,j,k),r,2), "Boundary", "Face"); + + for (int j = 0; j < 2; ++j, ++r) + for (int k = 0; k < nz; ++k) + for (int i = 0; i < nx; ++i) + insertion(TopItem(IJK2J(i,j,k),r,2), "Boundary", "Face"); + + for (int k = 0; k < 2; ++k, ++r) + for (int j = 0; j < ny; ++j) + for (int i = 0; i < nx; ++i) + insertion(TopItem(IJK2K(i,j,k),r,2), "Boundary", "Face"); + + r = 1; + for (int k = 0; k < 2; ++k) + for (int j = 0; j < 2; ++j) + for (int i = 0; i < 2; ++i, ++r) + insertion(TopItem(IJK2(i,j,k),r,0), "Corners", "Vertex"); + + r = 1; + for (int k = 0; k < 2; ++k) + for (int i = 0; i < 2; ++i, ++r) + for (int j = 0; j < ny; ++j) + insertion(TopItem(IJKJ(i,j,k),r,1), "Frame", "Edge"); + + for (int j = 0; j < 2; ++j) + for (int i = 0; i < 2; ++i, ++r) + for (int k = 0; k < nz; ++k) + insertion(TopItem(IJKK(i,j,k),r,1), "Frame", "Edge"); + + for (int k = 0; k < 2; ++k) + for (int j = 0; j < 2; ++j, ++r) + for (int i = 0; i < nx; ++i) + insertion(TopItem(IJKI(i,j,k),r,1), "Frame", "Edge"); + + return result; +} diff --git a/Apps/Common/MultiPatchModelGenerator.h b/Apps/Common/MultiPatchModelGenerator.h new file mode 100644 index 00000000..c54c47ac --- /dev/null +++ b/Apps/Common/MultiPatchModelGenerator.h @@ -0,0 +1,100 @@ +// $Id$ +//============================================================================== +//! +//! \file MultiPatchModelGenerator.h +//! +//! \date Sep 2 2016 +//! +//! \author Arne Morten Kvarving / SINTEF +//! +//! \brief Multi-patch model generators for NURBS-based FEM simulators. +//! +//============================================================================== + +#ifndef _MULTIPATCH_MODEL_GENERATOR_H +#define _MULTIPATCH_MODEL_GENERATOR_H + +#include "ModelGenerator.h" +#include + + +/*! + \brief 2D multi-patch model generator for FEM simulators. + \details Generate a rectangle split in a given number of blocks. +*/ + +class MultiPatchModelGenerator2D : public ModelGenerator +{ +public: + //! \brief Constructor initializes common members. + //!\ param[in] elem XML element to parse + MultiPatchModelGenerator2D(const TiXmlElement* elem); + + //! \brief Empty destructor. + virtual ~MultiPatchModelGenerator2D() {} + + //! \brief Creates a geometry. + //! \param[in] sim SIM with patch read function to use + SIMdependency::PatchVec createGeometry(const SIMbase& sim) const override; + + //! \brief Creates topology for geometry. + //! \param sim Simulator to apply topology to + bool createTopology(SIMbase& sim) const override; + + //! \brief Creates topology sets for geometry. + TopologySet createTopologySets(const SIMbase& sim) const override; + +protected: + //! \brief Generates the G2 description of the geometry. + //! \param nsd Number of spatial dimension + std::string createG2 (int nsd = 2) const; + + int nx; //!< Number of blocks in x + int ny; //!< Number of blocks in y + int periodic_x; //!< If non-zero, make model periodic in x for given bases + int periodic_y; //!< If non-zero, make model periodic in y for given bases +}; + + +/*! + \brief 3D multi-patch model generator for FEM simulators. + \details Generates a hexahedra split in a given number of blocks. + */ + +class MultiPatchModelGenerator3D : public ModelGenerator +{ +public: + //! \brief Constructor initializes common members. + //! \param[in] elem XML element to parse + MultiPatchModelGenerator3D(const TiXmlElement* geo); + + //! \brief Empty destructor. + virtual ~MultiPatchModelGenerator3D() {} + + //! \brief Creates a geometry. + //! \param[in] sim SIM with patch read function to use + SIMdependency::PatchVec createGeometry(const SIMbase& sim) const override; + + //! \brief Creates topology for geometry. + //! \param[in] geo XML element containing geometry defintion + //! \param sim Simulator to apply topology to + bool createTopology(SIMbase& sim) const override; + + //! \brief Creates topology sets for geometry. + //! \param[in] SIM Simulator with patch ownerships + virtual TopologySet createTopologySets(const SIMbase& sim) const; + +protected: + //! \brief Generates the G2 description of the geometry. + //! \param nsd Number of spatial dimension + std::string createG2 (int nsd = 3) const; + + int nx; //!< Number of blocks in x + int ny; //!< Number of blocks in y + int nz; //!< Number of blocks in z + int periodic_x; //!< If non-zero, make model periodic in x for given bases + int periodic_y; //!< If non-zero, make model periodic in y for given bases + int periodic_z; //!< If non-zero, make model periodic in z for given bases +}; + +#endif diff --git a/Apps/Common/Test/TestMultiPatchModelGenerator.C b/Apps/Common/Test/TestMultiPatchModelGenerator.C new file mode 100644 index 00000000..d3fddc3c --- /dev/null +++ b/Apps/Common/Test/TestMultiPatchModelGenerator.C @@ -0,0 +1,882 @@ +//============================================================================== +//! +//! \file TestMultiPatchModelGenerator.C +//! +//! \date Sep 2 2016 +//! +//! \author Arne Morten Kvarving / SINTEF +//! +//! \brief Tests for multi-patch model generators. +//! +//============================================================================== + +#include "IFEM.h" +#include "MultiPatchModelGenerator.h" +#include "SIM2D.h" +#include "SIM3D.h" +#include "TopologySet.h" + +#include "gtest/gtest.h" +#include "tinyxml.h" + + +template +class TestModelGeneratorWrapper : public Generator { +public: + TestModelGeneratorWrapper(const TiXmlElement* geo) : Generator(geo) {} + std::string createG2(int nsd) + { + return Generator::createG2(nsd); + } +}; + +struct GeomTest { + std::string xml; + int dim; + std::string g2; + std::string sets; +}; + + +class TestMultiPatchModelGenerator1D : + public testing::Test, + public testing::WithParamInterface +{ +}; + + +class TestMultiPatchModelGenerator2D : + public testing::Test, + public testing::WithParamInterface +{ +}; + + +class TestMultiPatchModelGenerator3D : + public testing::Test, + public testing::WithParamInterface +{ +}; + + +auto&& DoTest = [](const GeomTest& ref, const std::string& gen, + const TopologySet& sets) +{ + ASSERT_STREQ(gen.c_str(), ref.g2.c_str()); + + if (!ref.sets.empty()) { + std::string gsets; + for (auto& it : sets) { + gsets += it.first + ": "; + for (auto& it2 : it.second) { + std::stringstream str; + str << it2.patch << " " << it2.item << " " << it2.idim << " "; + gsets += str.str(); + } + gsets += "\n"; + } + ASSERT_STREQ(gsets.c_str(), ref.sets.c_str()); + } +}; + + + +TEST_P(TestMultiPatchModelGenerator2D, Generate) +{ + TiXmlDocument doc; + doc.Parse(GetParam().xml.c_str()); + TestModelGeneratorWrapper gen(doc.RootElement()); + std::string g2 = gen.createG2(GetParam().dim); + SIM2D sim; + TopologySet sets = gen.createTopologySets(sim); + DoTest(GetParam(), g2, sets); +} + + +TEST_P(TestMultiPatchModelGenerator3D, Generate) +{ + TiXmlDocument doc; + doc.Parse(GetParam().xml.c_str()); + TestModelGeneratorWrapper gen(doc.RootElement()); + std::string g2 = gen.createG2(GetParam().dim); + SIM3D sim; + TopologySet sets = gen.createTopologySets(sim); + DoTest(GetParam(), g2, sets); +} + + +const std::vector geometry2D = + {{"", 2, + "200 1 0 0\n" + "2 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0\n" + "1 0\n" + "0 1\n" + "1 1\n", + "Boundary: 1 1 1 1 2 1 1 3 1 1 4 1 \n" + "Corners: 1 1 0 1 2 0 1 3 0 1 4 0 \n" + "Edge1: 1 1 1 \n" + "Edge2: 1 2 1 \n" + "Edge3: 1 3 1 \n" + "Edge4: 1 4 1 \n" + "Vertex1: 1 1 0 \n" + "Vertex2: 1 2 0 \n" + "Vertex3: 1 3 0 \n" + "Vertex4: 1 4 0 \n"}, + + {"", 2, + "200 1 0 0\n" + "2 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0 1.0\n" + "1 0 1.0\n" + "0 1 1.0\n" + "1 1 1.0\n", ""}, + + {"", 2, + "200 1 0 0\n" + "2 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0\n" + "2 0\n" + "0 2\n" + "2 2\n", ""}, + + {"", 2, + "200 1 0 0\n" + "2 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 0\n" + "3 0\n" + "2 1\n" + "3 1\n"}, + + {"", 2, + "200 1 0 0\n" + "2 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 2\n" + "1 2\n" + "0 3\n" + "1 3\n", ""}, + + {"", 2, + "200 1 0 0\n" + "2 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0\n" + "2 0\n" + "0 1\n" + "2 1\n", ""}, + + {"", 2, + "200 1 0 0\n" + "2 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0\n" + "1 0\n" + "0 2\n" + "1 2\n", ""}, + + {"", 2, + "200 1 0 0\n" + "2 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0\n" + "0.5 0\n" + "0 1\n" + "0.5 1\n" + "200 1 0 0\n" + "2 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0.5 0\n" + "1 0\n" + "0.5 1\n" + "1 1\n", + "Boundary: 1 1 1 1 3 1 1 4 1 2 2 1 2 3 1 2 4 1 \n" + "Corners: 1 1 0 1 3 0 2 2 0 2 4 0 \n" + "Edge1: 1 1 1 \n" + "Edge2: 2 2 1 \n" + "Edge3: 1 3 1 2 3 1 \n" + "Edge4: 1 4 1 2 4 1 \n" + "Vertex1: 1 1 0 \n" + "Vertex2: 2 2 0 \n" + "Vertex3: 1 3 0 \n" + "Vertex4: 2 4 0 \n"}, + + {"", 2, + "200 1 0 0\n" + "2 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0\n" + "1 0\n" + "0 0.5\n" + "1 0.5\n" + "200 1 0 0\n" + "2 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0.5\n" + "1 0.5\n" + "0 1\n" + "1 1\n", + "Boundary: 1 1 1 1 2 1 1 3 1 2 1 1 2 2 1 2 4 1 \n" + "Corners: 1 1 0 1 2 0 2 3 0 2 4 0 \n" + "Edge1: 1 1 1 2 1 1 \n" + "Edge2: 1 2 1 2 2 1 \n" + "Edge3: 1 3 1 \n" + "Edge4: 2 4 1 \n" + "Vertex1: 1 1 0 \n" + "Vertex2: 1 2 0 \n" + "Vertex3: 2 3 0 \n" + "Vertex4: 2 4 0 \n"}, + + {"", 2, + "200 1 0 0\n" + "2 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0\n" + "0.5 0\n" + "0 0.5\n" + "0.5 0.5\n" + "200 1 0 0\n" + "2 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0.5 0\n" + "1 0\n" + "0.5 0.5\n" + "1 0.5\n" + "200 1 0 0\n" + "2 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0.5\n" + "0.5 0.5\n" + "0 1\n" + "0.5 1\n" + "200 1 0 0\n" + "2 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0.5 0.5\n" + "1 0.5\n" + "0.5 1\n" + "1 1\n", + "Boundary: 1 1 1 1 3 1 2 2 1 2 3 1 3 1 1 3 4 1 4 2 1 4 4 1 \n" + "Corners: 1 1 0 2 2 0 3 3 0 4 4 0 \n" + "Edge1: 1 1 1 3 1 1 \n" + "Edge2: 2 2 1 4 2 1 \n" + "Edge3: 1 3 1 2 3 1 \n" + "Edge4: 3 4 1 4 4 1 \n" + "Vertex1: 1 1 0 \n" + "Vertex2: 2 2 0 \n" + "Vertex3: 3 3 0 \n" + "Vertex4: 4 4 0 \n"}}; + + +INSTANTIATE_TEST_CASE_P(TestMultiPatchModelGenerator2D, + TestMultiPatchModelGenerator2D, + testing::ValuesIn(geometry2D)); + + +const std::vector geometry3D = + {{"", 3, + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0 0\n" + "1 0 0\n" + "0 1 0\n" + "1 1 0\n" + "0 0 1\n" + "1 0 1\n" + "0 1 1\n" + "1 1 1\n", + "Boundary: 1 1 2 1 2 2 1 3 2 1 4 2 1 5 2 1 6 2 \n" + "Corners: 1 1 0 1 2 0 1 3 0 1 4 0 1 5 0 1 6 0 1 7 0 1 8 0 \n" + "Edge1: 1 1 1 \n" + "Edge10: 1 10 1 \n" + "Edge11: 1 11 1 \n" + "Edge12: 1 12 1 \n" + "Edge2: 1 2 1 \n" + "Edge3: 1 3 1 \n" + "Edge4: 1 4 1 \n" + "Edge5: 1 5 1 \n" + "Edge6: 1 6 1 \n" + "Edge7: 1 7 1 \n" + "Edge8: 1 8 1 \n" + "Edge9: 1 9 1 \n" + "Face1: 1 1 2 \n" + "Face2: 1 2 2 \n" + "Face3: 1 3 2 \n" + "Face4: 1 4 2 \n" + "Face5: 1 5 2 \n" + "Face6: 1 6 2 \n" + "Frame: 1 1 1 1 2 1 1 3 1 1 4 1 1 5 1 1 6 1 1 7 1 1 8 1 1 9 1 1 10 1 1 11 1 1 12 1 \n" + "Vertex1: 1 1 0 \n" + "Vertex2: 1 2 0 \n" + "Vertex3: 1 3 0 \n" + "Vertex4: 1 4 0 \n" + "Vertex5: 1 5 0 \n" + "Vertex6: 1 6 0 \n" + "Vertex7: 1 7 0 \n" + "Vertex8: 1 8 0 \n"}, + + {"", 3, + "700 1 0 0\n" + "3 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0 0 1.0\n" + "1 0 0 1.0\n" + "0 1 0 1.0\n" + "1 1 0 1.0\n" + "0 0 1 1.0\n" + "1 0 1 1.0\n" + "0 1 1 1.0\n" + "1 1 1 1.0\n", ""}, + + {"", 3, + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0 0\n" + "2 0 0\n" + "0 2 0\n" + "2 2 0\n" + "0 0 2\n" + "2 0 2\n" + "0 2 2\n" + "2 2 2\n", ""}, + + {"", 3, + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 0 0\n" + "3 0 0\n" + "2 1 0\n" + "3 1 0\n" + "2 0 1\n" + "3 0 1\n" + "2 1 1\n" + "3 1 1\n", ""}, + + {"", 3, + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 2 0\n" + "1 2 0\n" + "0 3 0\n" + "1 3 0\n" + "0 2 1\n" + "1 2 1\n" + "0 3 1\n" + "1 3 1\n", ""}, + + {"", 3, + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0 2\n" + "1 0 2\n" + "0 1 2\n" + "1 1 2\n" + "0 0 3\n" + "1 0 3\n" + "0 1 3\n" + "1 1 3\n", ""}, + + {"", 3, + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0 0\n" + "2 0 0\n" + "0 1 0\n" + "2 1 0\n" + "0 0 1\n" + "2 0 1\n" + "0 1 1\n" + "2 1 1\n", ""}, + + {"", 3, + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0 0\n" + "1 0 0\n" + "0 2 0\n" + "1 2 0\n" + "0 0 1\n" + "1 0 1\n" + "0 2 1\n" + "1 2 1\n", ""}, + + {"", 3, + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0 0\n" + "1 0 0\n" + "0 1 0\n" + "1 1 0\n" + "0 0 2\n" + "1 0 2\n" + "0 1 2\n" + "1 1 2\n", ""}, + + {"", 3, + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0 0\n" + "0.5 0 0\n" + "0 1 0\n" + "0.5 1 0\n" + "0 0 1\n" + "0.5 0 1\n" + "0 1 1\n" + "0.5 1 1\n" + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0.5 0 0\n" + "1 0 0\n" + "0.5 1 0\n" + "1 1 0\n" + "0.5 0 1\n" + "1 0 1\n" + "0.5 1 1\n" + "1 1 1\n", + "Boundary: 1 1 2 1 3 2 1 4 2 1 5 2 1 6 2 2 2 2 2 3 2 2 4 2 2 5 2 2 6 2 \n" + "Corners: 1 1 0 1 3 0 1 5 0 1 7 0 2 2 0 2 4 0 2 6 0 2 8 0 \n" + "Edge1: 1 1 1 \n" + "Edge10: 1 10 1 2 10 1 \n" + "Edge11: 1 11 1 2 11 1 \n" + "Edge12: 1 12 1 2 12 1 \n" + "Edge2: 2 2 1 \n" + "Edge3: 1 3 1 \n" + "Edge4: 2 4 1 \n" + "Edge5: 1 5 1 \n" + "Edge6: 2 6 1 \n" + "Edge7: 1 7 1 \n" + "Edge8: 2 8 1 \n" + "Edge9: 1 9 1 2 9 1 \n" + "Face1: 1 1 2 \n" + "Face2: 2 2 2 \n" + "Face3: 1 3 2 2 3 2 \n" + "Face4: 1 4 2 2 4 2 \n" + "Face5: 1 5 2 2 5 2 \n" + "Face6: 1 6 2 2 6 2 \n" + "Frame: 1 1 1 1 3 1 1 5 1 1 7 1 1 9 1 1 10 1 1 11 1 1 12 1 2 2 1 2 4 1 2 6 1 2 8 1 2 9 1 2 10 1 2 11 1 2 12 1 \n" + "Vertex1: 1 1 0 \n" + "Vertex2: 2 2 0 \n" + "Vertex3: 1 3 0 \n" + "Vertex4: 2 4 0 \n" + "Vertex5: 1 5 0 \n" + "Vertex6: 2 6 0 \n" + "Vertex7: 1 7 0 \n" + "Vertex8: 2 8 0 \n"}, + + {"", 3, + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0 0\n" + "1 0 0\n" + "0 0.5 0\n" + "1 0.5 0\n" + "0 0 1\n" + "1 0 1\n" + "0 0.5 1\n" + "1 0.5 1\n" + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0.5 0\n" + "1 0.5 0\n" + "0 1 0\n" + "1 1 0\n" + "0 0.5 1\n" + "1 0.5 1\n" + "0 1 1\n" + "1 1 1\n", + "Boundary: 1 1 2 1 2 2 1 3 2 1 5 2 1 6 2 2 1 2 2 2 2 2 4 2 2 5 2 2 6 2 \n" + "Corners: 1 1 0 1 2 0 1 5 0 1 6 0 2 3 0 2 4 0 2 7 0 2 8 0 \n" + "Edge1: 1 1 1 2 1 1 \n" + "Edge10: 2 10 1 \n" + "Edge11: 1 11 1 \n" + "Edge12: 2 12 1 \n" + "Edge2: 1 2 1 2 2 1 \n" + "Edge3: 1 3 1 2 3 1 \n" + "Edge4: 1 4 1 2 4 1 \n" + "Edge5: 1 5 1 \n" + "Edge6: 1 6 1 \n" + "Edge7: 2 7 1 \n" + "Edge8: 2 8 1 \n" + "Edge9: 1 9 1 \n" + "Face1: 1 1 2 2 1 2 \n" + "Face2: 1 2 2 2 2 2 \n" + "Face3: 1 3 2 \n" + "Face4: 2 4 2 \n" + "Face5: 1 5 2 2 5 2 \n" + "Face6: 1 6 2 2 6 2 \n" + "Frame: 1 1 1 1 2 1 1 3 1 1 4 1 1 5 1 1 6 1 1 9 1 1 11 1 2 1 1 2 2 1 2 3 1 2 4 1 2 7 1 2 8 1 2 10 1 2 12 1 \n" + "Vertex1: 1 1 0 \n" + "Vertex2: 1 2 0 \n" + "Vertex3: 2 3 0 \n" + "Vertex4: 2 4 0 \n" + "Vertex5: 1 5 0 \n" + "Vertex6: 1 6 0 \n" + "Vertex7: 2 7 0 \n" + "Vertex8: 2 8 0 \n"}, + + {"", 3, + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0 0\n" + "1 0 0\n" + "0 1 0\n" + "1 1 0\n" + "0 0 0.5\n" + "1 0 0.5\n" + "0 1 0.5\n" + "1 1 0.5\n" + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0 0.5\n" + "1 0 0.5\n" + "0 1 0.5\n" + "1 1 0.5\n" + "0 0 1\n" + "1 0 1\n" + "0 1 1\n" + "1 1 1\n", + "Boundary: 1 1 2 1 2 2 1 3 2 1 4 2 1 5 2 2 1 2 2 2 2 2 3 2 2 4 2 2 6 2 \n" + "Corners: 1 1 0 1 2 0 1 3 0 1 4 0 2 5 0 2 6 0 2 7 0 2 8 0 \n" + "Edge1: 1 1 1 \n" + "Edge10: 1 10 1 \n" + "Edge11: 2 11 1 \n" + "Edge12: 2 12 1 \n" + "Edge2: 1 2 1 \n" + "Edge3: 2 3 1 \n" + "Edge4: 2 4 1 \n" + "Edge5: 1 5 1 2 5 1 \n" + "Edge6: 1 6 1 2 6 1 \n" + "Edge7: 1 7 1 2 7 1 \n" + "Edge8: 1 8 1 2 8 1 \n" + "Edge9: 1 9 1 \n" + "Face1: 1 1 2 2 1 2 \n" + "Face2: 1 2 2 2 2 2 \n" + "Face3: 1 3 2 2 3 2 \n" + "Face4: 1 4 2 2 4 2 \n" + "Face5: 1 5 2 \n" + "Face6: 2 6 2 \n" + "Frame: 1 1 1 1 2 1 1 5 1 1 6 1 1 7 1 1 8 1 1 9 1 1 10 1 2 3 1 2 4 1 2 5 1 2 6 1 2 7 1 2 8 1 2 11 1 2 12 1 \n" + "Vertex1: 1 1 0 \n" + "Vertex2: 1 2 0 \n" + "Vertex3: 1 3 0 \n" + "Vertex4: 1 4 0 \n" + "Vertex5: 2 5 0 \n" + "Vertex6: 2 6 0 \n" + "Vertex7: 2 7 0 \n" + "Vertex8: 2 8 0 \n"}, + + {"", 3, + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0 0\n" + "0.5 0 0\n" + "0 0.5 0\n" + "0.5 0.5 0\n" + "0 0 0.5\n" + "0.5 0 0.5\n" + "0 0.5 0.5\n" + "0.5 0.5 0.5\n" + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0.5 0 0\n" + "1 0 0\n" + "0.5 0.5 0\n" + "1 0.5 0\n" + "0.5 0 0.5\n" + "1 0 0.5\n" + "0.5 0.5 0.5\n" + "1 0.5 0.5\n" + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0.5 0\n" + "0.5 0.5 0\n" + "0 1 0\n" + "0.5 1 0\n" + "0 0.5 0.5\n" + "0.5 0.5 0.5\n" + "0 1 0.5\n" + "0.5 1 0.5\n" + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0.5 0.5 0\n" + "1 0.5 0\n" + "0.5 1 0\n" + "1 1 0\n" + "0.5 0.5 0.5\n" + "1 0.5 0.5\n" + "0.5 1 0.5\n" + "1 1 0.5\n" + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0 0.5\n" + "0.5 0 0.5\n" + "0 0.5 0.5\n" + "0.5 0.5 0.5\n" + "0 0 1\n" + "0.5 0 1\n" + "0 0.5 1\n" + "0.5 0.5 1\n" + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0.5 0 0.5\n" + "1 0 0.5\n" + "0.5 0.5 0.5\n" + "1 0.5 0.5\n" + "0.5 0 1\n" + "1 0 1\n" + "0.5 0.5 1\n" + "1 0.5 1\n" + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0 0.5 0.5\n" + "0.5 0.5 0.5\n" + "0 1 0.5\n" + "0.5 1 0.5\n" + "0 0.5 1\n" + "0.5 0.5 1\n" + "0 1 1\n" + "0.5 1 1\n" + "700 1 0 0\n" + "3 0\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "2 2\n" + "0 0 1 1\n" + "0.5 0.5 0.5\n" + "1 0.5 0.5\n" + "0.5 1 0.5\n" + "1 1 0.5\n" + "0.5 0.5 1\n" + "1 0.5 1\n" + "0.5 1 1\n" + "1 1 1\n", + "Boundary: 1 1 2 1 3 2 1 5 2 " + "2 2 2 2 3 2 2 5 2 " + "3 1 2 3 4 2 3 5 2 " + "4 2 2 4 4 2 4 5 2 " + "5 1 2 5 3 2 5 6 2 " + "6 2 2 6 3 2 6 6 2 " + "7 1 2 7 4 2 7 6 2 " + "8 2 2 8 4 2 8 6 2 \n" + "Corners: 1 1 0 2 2 0 3 3 0 4 4 0 5 5 0 6 6 0 7 7 0 8 8 0 \n" + "Edge1: 1 1 1 3 1 1 \n" + "Edge10: 3 10 1 4 10 1 \n" + "Edge11: 5 11 1 6 11 1 \n" + "Edge12: 7 12 1 8 12 1 \n" + "Edge2: 2 2 1 4 2 1 \n" + "Edge3: 5 3 1 7 3 1 \n" + "Edge4: 6 4 1 8 4 1 \n" + "Edge5: 1 5 1 5 5 1 \n" + "Edge6: 2 6 1 6 6 1 \n" + "Edge7: 3 7 1 7 7 1 \n" + "Edge8: 4 8 1 8 8 1 \n" + "Edge9: 1 9 1 2 9 1 \n" + "Face1: 1 1 2 3 1 2 5 1 2 7 1 2 \n" + "Face2: 2 2 2 4 2 2 6 2 2 8 2 2 \n" + "Face3: 1 3 2 2 3 2 5 3 2 6 3 2 \n" + "Face4: 3 4 2 4 4 2 7 4 2 8 4 2 \n" + "Face5: 1 5 2 2 5 2 3 5 2 4 5 2 \n" + "Face6: 5 6 2 6 6 2 7 6 2 8 6 2 \n" + "Frame: 1 1 1 1 5 1 1 9 1 " + "2 2 1 2 6 1 2 9 1 " + "3 1 1 3 7 1 3 10 1 " + "4 2 1 4 8 1 4 10 1 " + "5 3 1 5 5 1 5 11 1 " + "6 4 1 6 6 1 6 11 1 " + "7 3 1 7 7 1 7 12 1 " + "8 4 1 8 8 1 8 12 1 \n" + "Vertex1: 1 1 0 \n" + "Vertex2: 2 2 0 \n" + "Vertex3: 3 3 0 \n" + "Vertex4: 4 4 0 \n" + "Vertex5: 5 5 0 \n" + "Vertex6: 6 6 0 \n" + "Vertex7: 7 7 0 \n" + "Vertex8: 8 8 0 \n"}}; + + +INSTANTIATE_TEST_CASE_P(TestMultiPatchModelGenerator3D, + TestMultiPatchModelGenerator3D, + testing::ValuesIn(geometry3D));