Changed: Public method SIMinput::addConnection no longer virtual. It now invokes the virtual method connectPatches using an ASM::Interface object to pass the connectivity definition instead, for convenience. Added: Offset attribute on the topology tag.
890 lines
27 KiB
C
890 lines
27 KiB
C
// $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 "ASMs1D.h"
|
|
#include "ASMs2D.h"
|
|
#include "ASMs3D.h"
|
|
#include "IFEM.h"
|
|
#include "SIMinput.h"
|
|
#include "Utilities.h"
|
|
#include "Vec3.h"
|
|
#include "Vec3Oper.h"
|
|
#include "tinyxml.h"
|
|
#include <array>
|
|
|
|
|
|
std::string MultiPatchModelGenerator1D::createG2 (int nsd, bool rational) const
|
|
{
|
|
if (rational)
|
|
IFEM::cout <<"\tRational basis.";
|
|
double scale = 1.0;
|
|
if (utl::getAttribute(geo,"scale",scale))
|
|
IFEM::cout <<"\n\tScale: "<< scale;
|
|
|
|
double Lx = 1.0;
|
|
if (utl::getAttribute(geo,"Lx",Lx))
|
|
IFEM::cout <<"\n\tLength in X: "<< Lx << std::endl;
|
|
Lx *= scale;
|
|
|
|
Vec3 X0;
|
|
std::string corner;
|
|
if (utl::getAttribute(geo,"X0",corner)) {
|
|
std::stringstream str(corner); str >> X0;
|
|
IFEM::cout <<"\n\tCorner: "<< X0;
|
|
}
|
|
|
|
int nx_mp = 1;
|
|
if (!subdivision) {
|
|
IFEM::cout <<"\n\tSplit in X = "<< nx;
|
|
|
|
Lx /= nx;
|
|
|
|
nx_mp = nx;
|
|
}
|
|
|
|
std::string g2;
|
|
for (int x = 0; x < nx_mp; ++x) {
|
|
g2.append("100 1 0 0\n");
|
|
g2.append(std::to_string(nsd));
|
|
g2.append(rational?" 1":" 0");
|
|
g2.append("\n2 2\n0 0 1 1");
|
|
|
|
std::stringstream str;
|
|
str <<"\n"<< X0.x+x*Lx;
|
|
if (nsd > 1) str <<" 0.0";
|
|
if (nsd > 2) str <<" 0.0";
|
|
if (rational) str << " 1.0";
|
|
g2.append(str.str());
|
|
str.str("");
|
|
str <<"\n"<< X0.x+(x+1)*Lx;
|
|
if (nsd > 1) str <<" 0.0";
|
|
if (nsd > 2) str <<" 0.0";
|
|
if (rational) str << " 1.0";
|
|
g2.append(str.str());
|
|
g2.append("\n");
|
|
}
|
|
|
|
return g2;
|
|
}
|
|
|
|
|
|
bool MultiPatchModelGenerator1D::createGeometry (SIMinput& sim) const
|
|
{
|
|
bool rational = sim.opt.discretization == ASM::LRNurbs;
|
|
utl::getAttribute(geo,"rational",rational);
|
|
std::istringstream line(this->createG2(sim.getNoSpaceDim(),rational));
|
|
|
|
if (!subdivision)
|
|
return sim.readPatches(line,"\t");
|
|
|
|
// for now this consists of a single patch. do refine / raiseorder
|
|
// to obtain knot vector, then split in pieces. written this way
|
|
// so code can be used with .g2 files.
|
|
|
|
IFEM::cout << " Subdivision in X: " << nx << std::endl;
|
|
ASMs1D pch;
|
|
pch.read(line);
|
|
|
|
// Parse XML input
|
|
const TiXmlElement* sub = geo->FirstChildElement("subdivision")->FirstChildElement();
|
|
for (; sub; sub = sub->NextSiblingElement())
|
|
if (strcasecmp(sub->Value(),"raiseorder") == 0) {
|
|
int nu;
|
|
utl::getAttribute(sub,"u",nu);
|
|
pch.raiseOrder(nu);
|
|
} else if (strcasecmp(sub->Value(),"refine") == 0) {
|
|
int nu;
|
|
utl::getAttribute(sub,"u",nu);
|
|
pch.uniformRefine(nu);
|
|
}
|
|
|
|
// Compute parameters
|
|
const Go::SplineCurve* cur = pch.getCurve();
|
|
size_t p = cur->order() - 1;
|
|
size_t nelems = cur->numCoefs() - p;
|
|
size_t nelems_sub = nelems / nx;
|
|
size_t nelems_rem = nelems % nx;
|
|
std::string header = "100 1 0 0\n";
|
|
|
|
// Extract subpatches
|
|
std::stringstream str;
|
|
for (size_t i = 0; i < nx; ++i) {
|
|
size_t ni = nelems_sub + (i < nelems_rem ? 1 : 0);
|
|
size_t i0 = ni*i + (i < nelems_rem ? 0 : nelems_rem);
|
|
size_t di = ni + p;
|
|
|
|
Go::SplineCurve subcur = getSubPatch(cur, i0, di, p+1);
|
|
IFEM::cout << " Number of knot spans in patch " << i << ": "
|
|
<< subcur.numCoefs()-subcur.order()+1 << std::endl;
|
|
str << header << subcur;
|
|
}
|
|
|
|
return sim.readPatches(str,"\t");
|
|
}
|
|
|
|
|
|
Go::SplineCurve
|
|
MultiPatchModelGenerator1D::getSubPatch(const Go::SplineCurve* cur,
|
|
const size_t startu,
|
|
const size_t numcoefsu, const int orderu)
|
|
{
|
|
size_t d = cur->dimension() + (cur->rational()?1:0); // #coefs per control point
|
|
bool rat = cur->rational();
|
|
std::vector<double>::const_iterator i0 = cur->basis().begin() + startu;
|
|
std::vector<double>::const_iterator j0 = rat ? cur->rcoefs_begin() : cur->coefs_begin();
|
|
return Go::SplineCurve(numcoefsu, orderu, i0, j0 + d*startu, cur->dimension(), rat);
|
|
}
|
|
|
|
|
|
MultiPatchModelGenerator1D::MultiPatchModelGenerator1D (const TiXmlElement* geo) :
|
|
ModelGenerator(geo)
|
|
{
|
|
nx = 1;
|
|
periodic_x = 0;
|
|
subdivision = false;
|
|
if (!geo) return;
|
|
utl::getAttribute(geo,"nx",nx);
|
|
utl::getAttribute(geo,"subdivision",subdivision);
|
|
utl::getAttribute(geo,"periodic_x", periodic_x);
|
|
const TiXmlElement* subd = geo->FirstChildElement("subdivision");
|
|
if (subd) {
|
|
subdivision = true;
|
|
utl::getAttribute(subd,"nx",nx);
|
|
}
|
|
}
|
|
|
|
|
|
bool MultiPatchModelGenerator1D::createTopology (SIMinput& sim) const
|
|
{
|
|
if (!sim.createFEMmodel())
|
|
return false;
|
|
|
|
int p1=2;
|
|
if (subdivision)
|
|
sim.getPatch(1)->getOrder(p1,p1,p1);
|
|
|
|
for (size_t i = 0; i < nx-1; ++i)
|
|
if (!sim.addConnection(i+1, i+2, 2, 1, true, 0, p1-1))
|
|
return false;
|
|
|
|
if (periodic_x)
|
|
if (nx <= 1) {
|
|
IFEM::cout <<"\tPeriodic I-direction P0\n";
|
|
sim.getPatch(1,true)->closeBoundaries();
|
|
}
|
|
else if (!sim.addConnection(1, nx, 1, 2, false, 0, p1-1))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool MultiPatchModelGenerator1D::createTopologySets (SIMinput& sim) const
|
|
{
|
|
if (!this->topologySets())
|
|
return false;
|
|
|
|
TopEntity& v1 = sim.topology("Vertex1");
|
|
TopEntity& v2 = sim.topology("Vertex2");
|
|
TopEntity& v3 = sim.topology("Boundary");
|
|
|
|
auto&& insertion = [&sim, &v3](TopEntity& v, TopItem top)
|
|
{
|
|
if ((top.patch = sim.getLocalPatchIndex(top.patch)) > 0) {
|
|
v.insert(top);
|
|
v3.insert(top);
|
|
}
|
|
};
|
|
|
|
insertion(v1, TopItem(1,1,0));
|
|
insertion(v2, TopItem(nx,2,0));
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
MultiPatchModelGenerator2D::MultiPatchModelGenerator2D (const TiXmlElement* geo) :
|
|
ModelGenerator(geo)
|
|
{
|
|
nx = ny = 1;
|
|
periodic_x = periodic_y = 0;
|
|
subdivision = false;
|
|
if (!geo) return;
|
|
utl::getAttribute(geo,"nx",nx);
|
|
utl::getAttribute(geo,"ny",ny);
|
|
utl::getAttribute(geo,"subdivision",subdivision);
|
|
utl::getAttribute(geo,"periodic_x", periodic_x);
|
|
utl::getAttribute(geo,"periodic_y", periodic_y);
|
|
const TiXmlElement* subd = geo->FirstChildElement("subdivision");
|
|
if (subd) {
|
|
subdivision = true;
|
|
utl::getAttribute(subd,"nx",nx);
|
|
utl::getAttribute(subd,"ny",ny);
|
|
}
|
|
}
|
|
|
|
|
|
std::string MultiPatchModelGenerator2D::createG2 (int nsd, bool rational) const
|
|
{
|
|
if (rational)
|
|
IFEM::cout <<"\tRational basis.";
|
|
double scale = 1.0;
|
|
if (utl::getAttribute(geo,"scale",scale))
|
|
IFEM::cout <<"\n\tScale = "<< scale;
|
|
|
|
double Lx = 1.0, Ly = 1.0;
|
|
if (utl::getAttribute(geo,"Lx",Lx))
|
|
IFEM::cout <<"\n\tLength in X = "<< Lx;
|
|
Lx *= scale;
|
|
if (utl::getAttribute(geo,"Ly",Ly))
|
|
IFEM::cout <<"\n\tLength in Y = "<< Ly;
|
|
Ly *= scale;
|
|
|
|
Vec3 X0;
|
|
std::string corner;
|
|
if (utl::getAttribute(geo,"X0",corner)) {
|
|
std::stringstream str(corner); str >> X0;
|
|
IFEM::cout <<"\n\tCorner = "<< X0;
|
|
}
|
|
|
|
int nx_mp = 1, ny_mp = 1;
|
|
if (!subdivision) {
|
|
IFEM::cout <<"\n\tSplit in X = "<< nx;
|
|
IFEM::cout <<"\n\tSplit in Y = "<< ny;
|
|
|
|
Lx /= nx;
|
|
Ly /= ny;
|
|
|
|
nx_mp = nx;
|
|
ny_mp = ny;
|
|
}
|
|
|
|
std::string g2;
|
|
for (int y = 0; y < ny_mp; ++y) {
|
|
for (int x = 0; x < nx_mp; ++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");
|
|
}
|
|
}
|
|
|
|
IFEM::cout << std::endl;
|
|
return g2;
|
|
}
|
|
|
|
|
|
bool MultiPatchModelGenerator2D::createGeometry (SIMinput& sim) const
|
|
{
|
|
bool rational = sim.opt.discretization == ASM::LRNurbs;
|
|
utl::getAttribute(geo,"rational",rational);
|
|
std::istringstream rect(this->createG2(sim.getNoSpaceDim(),rational));
|
|
if (!subdivision)
|
|
return sim.readPatches(rect,"\t");
|
|
|
|
// for now this consists of a single patch. do refine / raiseorder
|
|
// to obtain knot vector, then split in pieces. written this way
|
|
// so code can be used with .g2 files.
|
|
IFEM::cout << " Subdivision in X: " << nx << std::endl;
|
|
IFEM::cout << " Subdivision in Y: " << ny << std::endl;
|
|
ASMs2D pch;
|
|
pch.read(rect);
|
|
|
|
// Parse XML input
|
|
const TiXmlElement* sub = geo->FirstChildElement("subdivision")->FirstChildElement();
|
|
for (; sub; sub = sub->NextSiblingElement())
|
|
if (strcasecmp(sub->Value(),"raiseorder") == 0) {
|
|
int nu, nv;
|
|
utl::getAttribute(sub,"u",nu);
|
|
utl::getAttribute(sub,"v",nv);
|
|
pch.raiseOrder(nu,nv);
|
|
} else if (strcasecmp(sub->Value(),"refine") == 0) {
|
|
int nu, nv;
|
|
utl::getAttribute(sub,"u",nu);
|
|
utl::getAttribute(sub,"v",nv);
|
|
pch.uniformRefine(0,nu);
|
|
pch.uniformRefine(1,nv);
|
|
}
|
|
|
|
// Compute parameters
|
|
const Go::SplineSurface* srf = pch.getSurface();
|
|
size_t px = srf->order_u()-1;
|
|
size_t py = srf->order_v()-1;
|
|
size_t nelemsx = srf->numCoefs_u() - px;
|
|
size_t nelemsy = srf->numCoefs_v() - py;
|
|
size_t nelemsx_sub = nelemsx / nx;
|
|
size_t nelemsy_sub = nelemsy / ny;
|
|
size_t nelemsx_rem = nelemsx % nx;
|
|
size_t nelemsy_rem = nelemsy % ny;
|
|
std::string header = "200 1 0 0\n";
|
|
|
|
// Extract subpatches
|
|
std::stringstream str;
|
|
for (size_t j = 0; j < ny; ++j) {
|
|
size_t nj = nelemsy_sub + (j < nelemsy_rem ? 1 : 0);
|
|
size_t j0 = nj*j + (j < nelemsy_rem ? 0 : nelemsy_rem);
|
|
size_t dj = nj + py;
|
|
for (size_t i = 0; i < nx; ++i) {
|
|
size_t ni = nelemsx_sub + (i < nelemsx_rem ? 1 : 0);
|
|
size_t i0 = ni*i + (i < nelemsx_rem ? 0 : nelemsx_rem);
|
|
size_t di = ni + px;
|
|
|
|
Go::SplineSurface subsrf = this->getSubPatch(srf, {{i0, j0}}, {{di, dj}},
|
|
{{px+1, py+1}});
|
|
IFEM::cout << " Number of knot spans in patch (" << i << ", " << j << "): "
|
|
<< subsrf.numCoefs_u()-subsrf.order_u()+1 << "x"
|
|
<< subsrf.numCoefs_v()-subsrf.order_v()+1 << std::endl;
|
|
str << header << subsrf;
|
|
}
|
|
}
|
|
|
|
return sim.readPatches(str,"\t");
|
|
}
|
|
|
|
|
|
Go::SplineSurface
|
|
MultiPatchModelGenerator2D::getSubPatch(const Go::SplineSurface* srf,
|
|
const std::array<size_t,2>& start,
|
|
const std::array<size_t,2>& numcoefs,
|
|
const std::array<size_t,2>& order)
|
|
{
|
|
size_t d = srf->dimension() + (srf->rational()?1:0); // #coefs per control point
|
|
bool rat = srf->rational();
|
|
size_t I = srf->numCoefs_u();
|
|
std::vector<double>::const_iterator i0 = srf->basis_u().begin() + start[0];
|
|
std::vector<double>::const_iterator j0 = srf->basis_v().begin() + start[1];
|
|
std::vector<double>::const_iterator k0 = rat ? srf->rcoefs_begin() : srf->coefs_begin();
|
|
|
|
std::vector<double> subcoefs;
|
|
for (size_t j = start[1]; j != start[1] + numcoefs[1]; j++)
|
|
for (size_t i = start[0]; i != start[0] + numcoefs[0]; i++)
|
|
for (size_t k = 0; k != d; k++)
|
|
subcoefs.push_back(*(k0 + k + d*i + d*I*j));
|
|
return Go::SplineSurface(numcoefs[0], numcoefs[1], order[0], order[1], i0, j0,
|
|
subcoefs.begin(), srf->dimension(), rat);
|
|
}
|
|
|
|
|
|
bool MultiPatchModelGenerator2D::createTopology (SIMinput& sim) const
|
|
{
|
|
if (!sim.createFEMmodel())
|
|
return false;
|
|
|
|
auto&& IJ = [this](int i, int j) { return 1 + j*nx + i; };
|
|
|
|
int p1=2, p2=2;
|
|
if (subdivision)
|
|
sim.getPatch(1)->getOrder(p1,p2,p2);
|
|
|
|
for (size_t j = 0; j < ny; ++j)
|
|
for (size_t i = 0; i < nx-1; ++i)
|
|
if (!sim.addConnection(IJ(i,j), IJ(i+1,j), 2, 1, true, 1, p1-1))
|
|
return false;
|
|
|
|
for (size_t j = 0; j < ny-1; ++j)
|
|
for (size_t i = 0; i < nx; ++i)
|
|
if (!sim.addConnection(IJ(i,j), IJ(i,j+1), 4, 3, true, 1, p2-1))
|
|
return false;
|
|
|
|
if (periodic_x)
|
|
for (size_t j = 0; j < ny; ++j)
|
|
if (nx <= 1) {
|
|
IFEM::cout <<"\tPeriodic I-direction P"<< IJ(0,j) << std::endl;
|
|
sim.getPatch(IJ(0,j),true)->closeBoundaries(1);
|
|
}
|
|
else if (!sim.addConnection(IJ(0,j), IJ(nx-1,j), 1, 2))
|
|
return false;
|
|
|
|
if (periodic_y)
|
|
for (size_t i = 0; i < nx; ++i)
|
|
if (ny <= 1) {
|
|
IFEM::cout <<"\tPeriodic J-direction P"<< IJ(i,0)<< std::endl;
|
|
sim.getPatch(IJ(i,0),true)->closeBoundaries(2);
|
|
}
|
|
else if (!sim.addConnection(IJ(i,0), IJ(i,ny-1), 3, 4))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool MultiPatchModelGenerator2D::createTopologySets (SIMinput& sim) const
|
|
{
|
|
if (!this->topologySets())
|
|
return false;
|
|
|
|
TopEntity& e1 = sim.topology("Edge1");
|
|
TopEntity& e2 = sim.topology("Edge2");
|
|
TopEntity& e3 = sim.topology("Edge3");
|
|
TopEntity& e4 = sim.topology("Edge4");
|
|
TopEntity& e5 = sim.topology("Boundary");
|
|
TopEntity& e1f = sim.topology("Edge1Patches");
|
|
TopEntity& e2f = sim.topology("Edge2Patches");
|
|
TopEntity& e3f = sim.topology("Edge3Patches");
|
|
TopEntity& e4f = sim.topology("Edge4Patches");
|
|
|
|
auto&& insertion = [&sim, &e5](TopEntity& e, TopEntity& ef, TopItem top)
|
|
{
|
|
if ((top.patch = sim.getLocalPatchIndex(top.patch)) > 0) {
|
|
e.insert(top);
|
|
ef.insert(TopItem(top.patch,0,2));
|
|
e5.insert(top);
|
|
}
|
|
};
|
|
|
|
for (size_t i = 0; i < ny; ++i) {
|
|
insertion(e1, e1f, TopItem(i*nx+1,1,1));
|
|
insertion(e2, e2f, TopItem((i+1)*nx,2,1));
|
|
}
|
|
for (size_t i = 0; i < nx; ++i) {
|
|
insertion(e3, e3f, TopItem(i+1,3,1));
|
|
insertion(e4, e4f, TopItem(nx*(ny-1)+1+i,4,1));
|
|
}
|
|
|
|
TopEntity& c = sim.topology("Corners");
|
|
auto&& insertionv = [&sim, &c](TopEntity& e, TopItem top)
|
|
{
|
|
if ((top.patch = sim.getLocalPatchIndex(top.patch)) > 0) {
|
|
e.insert(top);
|
|
c.insert(top);
|
|
}
|
|
};
|
|
|
|
insertionv(sim.topology("Vertex1"), TopItem(1,1,0));
|
|
insertionv(sim.topology("Vertex2"), TopItem(nx,2,0));
|
|
insertionv(sim.topology("Vertex3"), TopItem(nx*(ny-1)+1,3,0));
|
|
insertionv(sim.topology("Vertex4"), TopItem(nx*ny,4,0));
|
|
|
|
std::set<int> used;
|
|
for (size_t i = 1; i <= 4; ++i) {
|
|
std::stringstream str;
|
|
str << "Edge" << i << "Patches";
|
|
for (const TopItem& top : sim.topology(str.str()))
|
|
used.insert(top.patch);
|
|
}
|
|
|
|
TopEntity& innerp = sim.topology("InnerPatches");
|
|
for (size_t i = 1; i <= nx*ny; ++i) {
|
|
size_t j = sim.getLocalPatchIndex(i);
|
|
if (j > 0 && used.find(j) == used.end())
|
|
innerp.insert(TopItem(j, 0, 2));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
MultiPatchModelGenerator3D::MultiPatchModelGenerator3D (const TiXmlElement* geo) :
|
|
ModelGenerator(geo)
|
|
{
|
|
nx = ny = nz = 1;
|
|
periodic_x = periodic_y = periodic_z = 0;
|
|
subdivision = false;
|
|
if (!geo) return;
|
|
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);
|
|
utl::getAttribute(geo,"subdivision", subdivision);
|
|
const TiXmlElement* subd = geo->FirstChildElement("subdivision");
|
|
if (subd) {
|
|
subdivision = true;
|
|
utl::getAttribute(subd,"nx",nx);
|
|
utl::getAttribute(subd,"ny",ny);
|
|
utl::getAttribute(subd,"nz",nz);
|
|
}
|
|
}
|
|
|
|
|
|
std::string MultiPatchModelGenerator3D::createG2 (int, bool rational) const
|
|
{
|
|
if (rational)
|
|
IFEM::cout <<"\tRational basis.";
|
|
|
|
double scale = 1.0;
|
|
if (utl::getAttribute(geo,"scale",scale))
|
|
IFEM::cout <<"\n\tScale = "<< scale;
|
|
|
|
double Lx = 1.0, Ly = 1.0, Lz = 1.0;
|
|
if (utl::getAttribute(geo,"Lx",Lx))
|
|
IFEM::cout <<"\n\tLength in X = "<< Lx;
|
|
Lx *= scale;
|
|
if (utl::getAttribute(geo,"Ly",Ly))
|
|
IFEM::cout <<"\n\tLength in Y = "<< Ly;
|
|
Ly *= scale;
|
|
if (utl::getAttribute(geo,"Lz",Lz))
|
|
IFEM::cout <<"\n\tLength in Z = "<< Lz;
|
|
Lz *= scale;
|
|
|
|
int nx_mp = 1, ny_mp = 1, nz_mp = 1;
|
|
if (!subdivision) {
|
|
IFEM::cout <<"\n\tSplit in X = "<< nx;
|
|
IFEM::cout <<"\n\tSplit in Y = "<< ny;
|
|
IFEM::cout <<"\n\tSplit in Z = "<< nz;
|
|
|
|
Lx /= nx;
|
|
Ly /= ny;
|
|
Lz /= nz;
|
|
|
|
nx_mp = nx;
|
|
ny_mp = ny;
|
|
nz_mp = nz;
|
|
}
|
|
|
|
std::string corner;
|
|
Vec3 X0;
|
|
if (utl::getAttribute(geo,"X0",corner)) {
|
|
std::stringstream str(corner);
|
|
str >> X0;
|
|
IFEM::cout <<"\n\tCorner = "<< X0;
|
|
}
|
|
|
|
std::array<double,24> 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_mp; ++z) {
|
|
for (int y = 0; y < ny_mp; ++y) {
|
|
for (int x = 0; x < nx_mp; ++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<int,3> N = {{x,y,z}};
|
|
std::array<double,3> 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");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
IFEM::cout << std::endl;
|
|
return g2;
|
|
}
|
|
|
|
|
|
bool MultiPatchModelGenerator3D::createGeometry (SIMinput& sim) const
|
|
{
|
|
bool rational = sim.opt.discretization == ASM::LRNurbs;
|
|
utl::getAttribute(geo,"rational",rational);
|
|
std::istringstream cube(this->createG2(sim.getNoSpaceDim(),rational));
|
|
if (!subdivision)
|
|
return sim.readPatches(cube,"\t");
|
|
|
|
// for now this consists of a single patch. do refine / raiseorder
|
|
// to obtain knot vector, then split in pieces. written this way
|
|
// so code can be used with .g2 files.
|
|
IFEM::cout << " Subdivision in X: " << nx << std::endl;
|
|
IFEM::cout << " Subdivision in Y: " << ny << std::endl;
|
|
IFEM::cout << " Subdivision in Z: " << nz << std::endl;
|
|
ASMs3D pch;
|
|
pch.read(cube);
|
|
|
|
// Parse XML input
|
|
const TiXmlElement* sub = geo->FirstChildElement("subdivision")->FirstChildElement();
|
|
for (; sub; sub = sub->NextSiblingElement())
|
|
if (strcasecmp(sub->Value(),"raiseorder") == 0) {
|
|
int nu, nv, nw;
|
|
utl::getAttribute(sub,"u",nu);
|
|
utl::getAttribute(sub,"v",nv);
|
|
utl::getAttribute(sub,"w",nw);
|
|
pch.raiseOrder(nu,nv,nw);
|
|
} else if (strcasecmp(sub->Value(),"refine") == 0) {
|
|
int nu, nv, nw;
|
|
utl::getAttribute(sub,"u",nu);
|
|
utl::getAttribute(sub,"v",nv);
|
|
utl::getAttribute(sub,"w",nw);
|
|
pch.uniformRefine(0,nu);
|
|
pch.uniformRefine(1,nv);
|
|
pch.uniformRefine(2,nw);
|
|
}
|
|
|
|
// Compute parameters
|
|
const Go::SplineVolume* vol = pch.getVolume();
|
|
size_t px = vol->order(0)-1;
|
|
size_t py = vol->order(1)-1;
|
|
size_t pz = vol->order(2)-1;
|
|
size_t nelemsx = vol->numCoefs(0) - px;
|
|
size_t nelemsy = vol->numCoefs(1) - py;
|
|
size_t nelemsz = vol->numCoefs(2) - pz;
|
|
size_t nelemsx_sub = nelemsx / nx;
|
|
size_t nelemsy_sub = nelemsy / ny;
|
|
size_t nelemsz_sub = nelemsz / nz;
|
|
size_t nelemsx_rem = nelemsx % nx;
|
|
size_t nelemsy_rem = nelemsy % ny;
|
|
size_t nelemsz_rem = nelemsz % nz;
|
|
std::string header = "700 1 0 0\n";
|
|
|
|
// Extract subpatches
|
|
std::stringstream str;
|
|
for (size_t k = 0; k < nz; ++k) {
|
|
size_t nk = nelemsz_sub + (k < nelemsz_rem ? 1 : 0);
|
|
size_t k0 = nk*k + (k < nelemsz_rem ? 0 : nelemsz_rem);
|
|
size_t dk = nk + pz;
|
|
for (size_t j = 0; j < ny; ++j) {
|
|
size_t nj = nelemsy_sub + (j < nelemsy_rem ? 1 : 0);
|
|
size_t j0 = nj*j + (j < nelemsy_rem ? 0 : nelemsy_rem);
|
|
size_t dj = nj + py;
|
|
for (size_t i = 0; i < nx; ++i) {
|
|
size_t ni = nelemsx_sub + (i < nelemsx_rem ? 1 : 0);
|
|
size_t i0 = ni*i + (i < nelemsx_rem ? 0 : nelemsx_rem);
|
|
size_t di = ni + px;
|
|
|
|
Go::SplineVolume subvol = getSubPatch(vol, {{i0, j0, k0}},
|
|
{{di, dj, dk}},
|
|
{{px+1, py+1, pz+1}});
|
|
IFEM::cout << " Number of knot spans in patch (" << i << ", " << j << ", " << k << "): "
|
|
<< subvol.numCoefs(0)-subvol.order(0)+1 << "x"
|
|
<< subvol.numCoefs(1)-subvol.order(1)+1 << "x"
|
|
<< subvol.numCoefs(2)-subvol.order(2)+1 << std::endl;
|
|
str << header << subvol;
|
|
}
|
|
}
|
|
}
|
|
|
|
return sim.readPatches(str,"\t");
|
|
}
|
|
|
|
|
|
Go::SplineVolume
|
|
MultiPatchModelGenerator3D::getSubPatch(const Go::SplineVolume* vol,
|
|
const std::array<size_t,3>& start,
|
|
const std::array<size_t,3>& numcoefs,
|
|
const std::array<size_t,3>& order)
|
|
{
|
|
bool rat = vol->rational();
|
|
size_t I = vol->numCoefs(0);
|
|
size_t J = vol->numCoefs(1);
|
|
size_t N = vol->dimension() + (rat?1:0); // #coefs per control point
|
|
std::vector<double>::const_iterator i0 = vol->basis(0).begin() + start[0];
|
|
std::vector<double>::const_iterator j0 = vol->basis(1).begin() + start[1];
|
|
std::vector<double>::const_iterator k0 = vol->basis(2).begin() + start[2];
|
|
std::vector<double>::const_iterator n0 = rat ? vol->rcoefs_begin() : vol->coefs_begin();
|
|
|
|
std::vector<double> subcoefs;
|
|
for (size_t k = start[2]; k != start[2] + numcoefs[2]; k++)
|
|
for (size_t j = start[1]; j != start[1] + numcoefs[1]; j++)
|
|
for (size_t i = start[0]; i != start[0] + numcoefs[0]; i++)
|
|
for (size_t n=0; n!=N; n++)
|
|
subcoefs.push_back(*(n0 + n + N*i + N*I*j + N*I*J*k));
|
|
|
|
return Go::SplineVolume(numcoefs[0], numcoefs[1], numcoefs[2],
|
|
order[0], order[1], order[2],
|
|
i0, j0, k0, subcoefs.begin(), vol->dimension(), rat);
|
|
}
|
|
|
|
|
|
bool MultiPatchModelGenerator3D::createTopology (SIMinput& sim) const
|
|
{
|
|
if (!sim.createFEMmodel())
|
|
return false;
|
|
|
|
auto&& IJK = [this](int i, int j, int k) { return 1 + (k*ny+j)*nx + i; };
|
|
|
|
int p1=2, p2=2, p3=2;
|
|
if (subdivision)
|
|
sim.getPatch(1)->getOrder(p1,p2,p3);
|
|
|
|
for (size_t k = 0; k < nz; ++k)
|
|
for (size_t j = 0; j < ny; ++j)
|
|
for (size_t i = 0; i < nx-1; ++i)
|
|
if (!sim.addConnection(IJK(i,j,k), IJK(i+1,j,k), 2, 1, true, 2, p1-1))
|
|
return false;
|
|
|
|
for (size_t k = 0; k < nz; ++k)
|
|
for (size_t j = 0; j < ny-1; ++j)
|
|
for (size_t i = 0; i < nx; ++i)
|
|
if (!sim.addConnection(IJK(i,j,k), IJK(i,j+1,k), 4, 3, true, 2, p2-1))
|
|
return false;
|
|
|
|
for (size_t k = 0; k < nz-1; ++k)
|
|
for (size_t j = 0; j < ny; ++j)
|
|
for (size_t i = 0; i < nx; ++i)
|
|
if (!sim.addConnection(IJK(i,j,k), IJK(i,j,k+1), 6, 5, true, 2, p3-1))
|
|
return false;
|
|
|
|
if (periodic_x)
|
|
for (size_t k = 0; k < nz; ++k)
|
|
for (size_t j = 0; j < ny; ++j)
|
|
if (nx <= 1) {
|
|
IFEM::cout <<"\tPeriodic I-direction P"<< IJK(0,j,k) << std::endl;
|
|
sim.getPatch(IJK(0,j,k),true)->closeBoundaries(1);
|
|
}
|
|
else if (!sim.addConnection(IJK(0,j,k), IJK(nx-1,j,k), 1, 2, false, 2))
|
|
return false;
|
|
|
|
if (periodic_y)
|
|
for (size_t k = 0; k < nz; ++k)
|
|
for (size_t i = 0; i < nx; ++i)
|
|
if (ny <= 1) {
|
|
IFEM::cout <<"\tPeriodic J-direction P"<< IJK(i,0,k) << std::endl;
|
|
sim.getPatch(IJK(i,0,k),true)->closeBoundaries(2);
|
|
}
|
|
else if (!sim.addConnection(IJK(i,0,k), IJK(i,ny-1,k), 3, 4, false, 2))
|
|
return false;
|
|
|
|
if (periodic_z)
|
|
for (size_t j = 0; j < ny; ++j)
|
|
for (size_t i = 0; i < nx; ++i)
|
|
if (nz <= 1) {
|
|
IFEM::cout <<"\tPeriodic K-direction P"<< IJK(i,j,0) << std::endl;
|
|
sim.getPatch(IJK(i,j,0),true)->closeBoundaries(3);
|
|
}
|
|
else if (!sim.addConnection(IJK(i,j,0), IJK(i,j,nz-1), 5, 6, false, 2))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool MultiPatchModelGenerator3D::createTopologySets (SIMinput& sim) const
|
|
{
|
|
if (!this->topologySets())
|
|
return false;
|
|
|
|
// 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)); };
|
|
|
|
// insertion lambda
|
|
auto&& insertion = [&sim](TopItem top,
|
|
const std::string& glob,
|
|
const std::string& type)
|
|
{
|
|
std::stringstream str;
|
|
str << type << top.item;
|
|
TopEntity& topI = sim.topology(str.str());
|
|
TopEntity* topIf=nullptr;
|
|
if (type == "Face")
|
|
topIf = &sim.topology(str.str()+"Patches");
|
|
TopEntity& globI = sim.topology(glob);
|
|
if ((top.patch = sim.getLocalPatchIndex(top.patch)) > 0) {
|
|
topI.insert(top);
|
|
globI.insert(top);
|
|
if (topIf)
|
|
topIf->insert(TopItem(top.patch, 0, 3));
|
|
}
|
|
};
|
|
|
|
size_t r = 1;
|
|
for (size_t i = 0; i < 2; ++i, ++r)
|
|
for (size_t k = 0; k < nz; ++k)
|
|
for (size_t j = 0; j < ny; ++j)
|
|
insertion(TopItem(IJK2I(i,j,k),r,2), "Boundary", "Face");
|
|
|
|
for (size_t j = 0; j < 2; ++j, ++r)
|
|
for (size_t k = 0; k < nz; ++k)
|
|
for (size_t i = 0; i < nx; ++i)
|
|
insertion(TopItem(IJK2J(i,j,k),r,2), "Boundary", "Face");
|
|
|
|
for (size_t k = 0; k < 2; ++k, ++r)
|
|
for (size_t j = 0; j < ny; ++j)
|
|
for (size_t i = 0; i < nx; ++i)
|
|
insertion(TopItem(IJK2K(i,j,k),r,2), "Boundary", "Face");
|
|
|
|
r = 1;
|
|
for (size_t k = 0; k < 2; ++k)
|
|
for (size_t j = 0; j < 2; ++j)
|
|
for (size_t i = 0; i < 2; ++i, ++r)
|
|
insertion(TopItem(IJK2(i,j,k),r,0), "Corners", "Vertex");
|
|
|
|
r = 1;
|
|
for (size_t k = 0; k < 2; ++k)
|
|
for (size_t i = 0; i < 2; ++i, ++r)
|
|
for (size_t j = 0; j < ny; ++j)
|
|
insertion(TopItem(IJKJ(i,j,k),r,1), "Frame", "Edge");
|
|
|
|
for (size_t j = 0; j < 2; ++j)
|
|
for (size_t i = 0; i < 2; ++i, ++r)
|
|
for (size_t k = 0; k < nz; ++k)
|
|
insertion(TopItem(IJKK(i,j,k),r,1), "Frame", "Edge");
|
|
|
|
for (size_t k = 0; k < 2; ++k)
|
|
for (size_t j = 0; j < 2; ++j, ++r)
|
|
for (size_t i = 0; i < nx; ++i)
|
|
insertion(TopItem(IJKI(i,j,k),r,1), "Frame", "Edge");
|
|
|
|
std::set<int> used;
|
|
for (size_t i = 1; i <= 6; ++i) {
|
|
std::stringstream str;
|
|
str << "Face" << i << "Patches";
|
|
for (const TopItem& top : sim.topology(str.str()))
|
|
used.insert(top.patch);
|
|
}
|
|
|
|
TopEntity& innerp = sim.topology("InnerPatches");
|
|
for (size_t i = 1; i <= nx*ny*nz; ++i) {
|
|
size_t j = sim.getLocalPatchIndex(i);
|
|
if (j > 0 && used.find(j) == used.end())
|
|
innerp.insert(TopItem(j, 0, 3));
|
|
}
|
|
|
|
return true;
|
|
}
|