2022-01-17 18:59:23 +05:00
|
|
|
|
#include <set>
|
|
|
|
|
|
2009-01-13 04:40:13 +05:00
|
|
|
|
#include <mystdlib.h>
|
|
|
|
|
#include "meshing.hpp"
|
2021-06-16 17:04:06 +05:00
|
|
|
|
#include "debugging.hpp"
|
2009-01-13 04:40:13 +05:00
|
|
|
|
|
|
|
|
|
namespace netgen
|
|
|
|
|
{
|
|
|
|
|
extern const char * tetrules[];
|
|
|
|
|
// extern const char * tetrules2[];
|
|
|
|
|
extern const char * prismrules2[];
|
|
|
|
|
extern const char * pyramidrules[];
|
|
|
|
|
extern const char * pyramidrules2[];
|
2016-04-05 20:15:31 +05:00
|
|
|
|
extern const char * hexrules[];
|
2009-01-13 04:40:13 +05:00
|
|
|
|
|
2021-06-16 17:04:06 +05:00
|
|
|
|
struct MeshingData
|
|
|
|
|
{
|
|
|
|
|
int domain;
|
|
|
|
|
|
Fix various typos
Found via `codespell -q 3 -S ./external_dependencies/pybind11 -L alledges,allright,ane,anormal,ans,apoints,ba,boxs,cancle,childs,co-ordinate,co-ordinates,daty,enty,filld,hel,identifyable,ist,linz,lod,ned,nd,selt,statics,suround,thev,thist,thisy,timere,upto,wel`
2022-03-26 03:21:48 +05:00
|
|
|
|
// mesh for one domain (contains all adjacent surface elements)
|
2021-06-22 14:16:28 +05:00
|
|
|
|
unique_ptr<Mesh> mesh;
|
2021-06-16 17:04:06 +05:00
|
|
|
|
|
2021-06-22 14:25:50 +05:00
|
|
|
|
// maps from local (domain) mesh to global mesh
|
2021-06-16 17:04:06 +05:00
|
|
|
|
Array<PointIndex, PointIndex> pmap;
|
|
|
|
|
|
2021-07-16 14:30:11 +05:00
|
|
|
|
// Array<INDEX_2> connected_pairs;
|
2021-06-16 17:04:06 +05:00
|
|
|
|
|
|
|
|
|
MeshingParameters mp;
|
|
|
|
|
|
|
|
|
|
unique_ptr<Meshing3> meshing;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// extract surface meshes belonging to individual domains
|
2021-06-22 14:25:50 +05:00
|
|
|
|
Array<MeshingData> DivideMesh(Mesh & mesh, const MeshingParameters & mp)
|
2021-06-16 17:04:06 +05:00
|
|
|
|
{
|
|
|
|
|
static Timer timer("DivideMesh"); RegionTimer rt(timer);
|
|
|
|
|
|
|
|
|
|
Array<MeshingData> ret;
|
|
|
|
|
auto num_domains = mesh.GetNDomains();
|
2021-06-22 14:25:50 +05:00
|
|
|
|
|
|
|
|
|
if(num_domains==1 || mp.only3D_domain_nr)
|
|
|
|
|
{
|
2021-07-16 20:18:41 +05:00
|
|
|
|
ret.SetSize(1);
|
2021-06-22 14:25:50 +05:00
|
|
|
|
// no need to divide mesh, just fill in meshing data
|
|
|
|
|
ret[0].domain = 1;
|
|
|
|
|
if(mp.only3D_domain_nr)
|
|
|
|
|
ret[0].domain = mp.only3D_domain_nr;
|
|
|
|
|
|
|
|
|
|
ret[0].mesh.reset(&mesh); // careful, this unique_ptr must not delete &mesh! (it will be released in MergeMeshes after meshing)
|
|
|
|
|
ret[0].mp = mp;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2021-07-16 20:18:41 +05:00
|
|
|
|
ret.SetSize(num_domains);
|
2021-06-16 17:04:06 +05:00
|
|
|
|
|
|
|
|
|
Array<Array<PointIndex, PointIndex>> ipmap;
|
|
|
|
|
ipmap.SetSize(num_domains);
|
2023-08-05 15:01:01 +05:00
|
|
|
|
// auto dim = mesh.GetDimension();
|
2021-06-16 17:04:06 +05:00
|
|
|
|
auto num_points = mesh.GetNP();
|
|
|
|
|
auto num_facedescriptors = mesh.GetNFD();
|
|
|
|
|
|
|
|
|
|
for(auto i : Range(ret))
|
|
|
|
|
{
|
|
|
|
|
auto & md = ret[i];
|
|
|
|
|
md.domain = i+1;
|
|
|
|
|
|
|
|
|
|
md.mp = mp;
|
|
|
|
|
md.mp.maxh = min2 (mp.maxh, mesh.MaxHDomain(md.domain));
|
|
|
|
|
|
2021-06-22 14:16:28 +05:00
|
|
|
|
ret[i].mesh = make_unique<Mesh>();
|
|
|
|
|
auto & m = *ret[i].mesh;
|
2021-06-16 17:04:06 +05:00
|
|
|
|
|
2021-07-16 20:18:41 +05:00
|
|
|
|
m.SetLocalH(mesh.GetLocalH());
|
2021-06-16 17:04:06 +05:00
|
|
|
|
|
|
|
|
|
ipmap[i].SetSize(num_points);
|
|
|
|
|
ipmap[i] = PointIndex::INVALID;
|
|
|
|
|
m.SetDimension( mesh.GetDimension() );
|
2021-07-16 14:30:11 +05:00
|
|
|
|
m.SetGeometry( mesh.GetGeometry() );
|
2021-06-16 17:04:06 +05:00
|
|
|
|
|
|
|
|
|
for(auto i : Range(1, num_facedescriptors+1))
|
|
|
|
|
m.AddFaceDescriptor( mesh.GetFaceDescriptor(i) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// mark used points for each domain, add surface elements (with wrong point numbers) to domain mesh
|
|
|
|
|
for(const auto & sel : mesh.SurfaceElements())
|
|
|
|
|
{
|
|
|
|
|
const auto & fd = mesh.GetFaceDescriptor(sel.GetIndex());
|
|
|
|
|
int dom_in = fd.DomainIn();
|
|
|
|
|
int dom_out = fd.DomainOut();
|
|
|
|
|
|
|
|
|
|
for( auto dom : {dom_in, dom_out} )
|
|
|
|
|
{
|
|
|
|
|
if(dom==0)
|
|
|
|
|
continue;
|
|
|
|
|
|
2021-06-22 14:16:28 +05:00
|
|
|
|
auto & sels = ret[dom-1].mesh->SurfaceElements();
|
2021-06-16 17:04:06 +05:00
|
|
|
|
for(auto pi : sel.PNums())
|
|
|
|
|
ipmap[dom-1][pi] = 1;
|
|
|
|
|
sels.Append(sel);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-03 15:22:06 +05:00
|
|
|
|
// mark used points for already existing volume elements, add them (with wrong point numbers) to domain mesh
|
|
|
|
|
for(const auto & el : mesh.VolumeElements())
|
|
|
|
|
{
|
|
|
|
|
auto dom = el.GetIndex();
|
|
|
|
|
|
|
|
|
|
auto & els = ret[dom-1].mesh->VolumeElements();
|
|
|
|
|
for(auto pi : el.PNums())
|
|
|
|
|
ipmap[dom-1][pi] = 1;
|
|
|
|
|
els.Append(el);
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-22 14:35:44 +05:00
|
|
|
|
// mark locked/fixed points for each domain TODO: domain bounding box to add only relevant points?
|
|
|
|
|
for(auto pi : mesh.LockedPoints())
|
|
|
|
|
for(auto i : Range(ret))
|
2022-03-02 15:34:02 +05:00
|
|
|
|
ipmap[i][pi] = 2;
|
2021-06-22 14:35:44 +05:00
|
|
|
|
|
2021-06-16 17:04:06 +05:00
|
|
|
|
// add used points to domain mesh, build point mapping
|
|
|
|
|
for(auto i : Range(ret))
|
|
|
|
|
{
|
2021-06-22 14:16:28 +05:00
|
|
|
|
auto & m = *ret[i].mesh;
|
2021-06-16 17:04:06 +05:00
|
|
|
|
auto & pmap = ret[i].pmap;
|
|
|
|
|
|
|
|
|
|
for(auto pi : Range(ipmap[i]))
|
|
|
|
|
if(ipmap[i][pi])
|
|
|
|
|
{
|
2022-03-02 15:34:02 +05:00
|
|
|
|
const auto& mp = mesh[pi];
|
|
|
|
|
auto pi_new = m.AddPoint( mp, mp.GetLayer(), mp.Type() );
|
|
|
|
|
if(ipmap[i][pi] == 2)
|
|
|
|
|
mesh.AddLockedPoint(pi_new);
|
2021-06-16 17:04:06 +05:00
|
|
|
|
ipmap[i][pi] = pi_new;
|
|
|
|
|
pmap.Append( pi );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Fix various typos
Found via `codespell -q 3 -S ./external_dependencies/pybind11 -L alledges,allright,ane,anormal,ans,apoints,ba,boxs,cancle,childs,co-ordinate,co-ordinates,daty,enty,filld,hel,identifyable,ist,linz,lod,ned,nd,selt,statics,suround,thev,thist,thisy,timere,upto,wel`
2022-03-26 03:21:48 +05:00
|
|
|
|
// add segments
|
2021-07-16 20:18:41 +05:00
|
|
|
|
for(auto i : Range(ret))
|
|
|
|
|
{
|
|
|
|
|
auto & imap = ipmap[i];
|
|
|
|
|
auto & m = *ret[i].mesh;
|
|
|
|
|
for(auto seg : mesh.LineSegments())
|
|
|
|
|
if(imap[seg[0]].IsValid() && imap[seg[1]].IsValid())
|
|
|
|
|
{
|
|
|
|
|
seg[0] = imap[seg[0]];
|
|
|
|
|
seg[1] = imap[seg[1]];
|
|
|
|
|
m.AddSegment(seg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto & identifications = mesh.GetIdentifications();
|
|
|
|
|
|
2021-06-16 17:04:06 +05:00
|
|
|
|
for(auto i : Range(ret))
|
|
|
|
|
{
|
2021-06-22 14:16:28 +05:00
|
|
|
|
auto & m = *ret[i].mesh;
|
2021-07-16 14:30:11 +05:00
|
|
|
|
auto & imap = ipmap[i];
|
|
|
|
|
auto nmax = identifications.GetMaxNr ();
|
|
|
|
|
auto & m_ident = m.GetIdentifications();
|
2021-06-22 14:25:50 +05:00
|
|
|
|
|
2021-06-16 17:04:06 +05:00
|
|
|
|
for (auto & sel : m.SurfaceElements())
|
|
|
|
|
for(auto & pi : sel.PNums())
|
|
|
|
|
pi = imap[pi];
|
|
|
|
|
|
2022-03-03 15:22:06 +05:00
|
|
|
|
for (auto & el : m.VolumeElements())
|
|
|
|
|
for(auto & pi : el.PNums())
|
|
|
|
|
pi = imap[pi];
|
|
|
|
|
|
2021-07-16 14:30:11 +05:00
|
|
|
|
for(auto n : Range(1,nmax+1))
|
|
|
|
|
{
|
|
|
|
|
NgArray<INDEX_2> pairs;
|
|
|
|
|
identifications.GetPairs(n, pairs);
|
|
|
|
|
|
|
|
|
|
for(auto pair : pairs)
|
|
|
|
|
{
|
2021-07-16 20:18:41 +05:00
|
|
|
|
auto pi0 = imap[pair[0]];
|
|
|
|
|
auto pi1 = imap[pair[1]];
|
|
|
|
|
if(!pi0.IsValid() || !pi1.IsValid())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
m_ident.Add(pi0, pi1, n);
|
2021-07-16 14:30:11 +05:00
|
|
|
|
}
|
2021-07-16 20:18:41 +05:00
|
|
|
|
m_ident.SetType( n, identifications.GetType(n) );
|
2021-07-16 14:30:11 +05:00
|
|
|
|
}
|
2021-06-16 17:04:06 +05:00
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-14 16:50:40 +05:00
|
|
|
|
// Add between identified surface elements (only consider closesurface identifications)
|
|
|
|
|
void FillCloseSurface( MeshingData & md)
|
|
|
|
|
{
|
|
|
|
|
static Timer timer("FillCloseSurface"); RegionTimer rtimer(timer);
|
|
|
|
|
|
2022-01-17 18:59:23 +05:00
|
|
|
|
auto & mesh = *md.mesh;
|
|
|
|
|
auto & identifications = mesh.GetIdentifications();
|
2021-12-14 16:50:40 +05:00
|
|
|
|
auto nmax = identifications.GetMaxNr();
|
|
|
|
|
|
|
|
|
|
bool have_closesurfaces = false;
|
|
|
|
|
for(auto i : Range(1,nmax+1))
|
|
|
|
|
if(identifications.GetType(i) == Identifications::CLOSESURFACES)
|
|
|
|
|
have_closesurfaces = true;
|
|
|
|
|
if(!have_closesurfaces)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
NgArray<int, PointIndex::BASE> map;
|
2022-06-09 18:32:17 +05:00
|
|
|
|
std::set<std::tuple<int,int,int>> hex_faces;
|
2021-12-14 16:50:40 +05:00
|
|
|
|
for(auto identnr : Range(1,nmax+1))
|
|
|
|
|
{
|
|
|
|
|
if(identifications.GetType(identnr) != Identifications::CLOSESURFACES)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
identifications.GetMap(identnr, map);
|
2022-05-05 14:21:48 +05:00
|
|
|
|
mesh.FindOpenElements(md.domain);
|
2021-12-14 16:50:40 +05:00
|
|
|
|
|
2022-05-05 14:21:48 +05:00
|
|
|
|
for(auto & sel : mesh.OpenElements())
|
2021-12-14 16:50:40 +05:00
|
|
|
|
{
|
2022-06-09 18:32:17 +05:00
|
|
|
|
// For quads: check if this open element is already closed by a hex
|
2022-09-07 04:43:32 +05:00
|
|
|
|
// this happens when we have identifications in two directions
|
2022-06-09 18:32:17 +05:00
|
|
|
|
if(sel.GetNP() == 4)
|
|
|
|
|
{
|
|
|
|
|
Element2d face = sel;
|
|
|
|
|
face.NormalizeNumbering();
|
|
|
|
|
if(hex_faces.count({face[0], face[1], face[2]}))
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2021-12-14 16:50:40 +05:00
|
|
|
|
bool is_mapped = true;
|
|
|
|
|
for(auto pi : sel.PNums())
|
|
|
|
|
if(!PointIndex(map[pi]).IsValid())
|
|
|
|
|
is_mapped = false;
|
|
|
|
|
|
|
|
|
|
if(!is_mapped)
|
|
|
|
|
continue;
|
|
|
|
|
|
2022-01-17 18:59:23 +05:00
|
|
|
|
// insert prism/hex
|
2021-12-14 16:50:40 +05:00
|
|
|
|
auto np = sel.GetNP();
|
|
|
|
|
Element el(2*np);
|
2022-01-17 18:59:23 +05:00
|
|
|
|
std::set<int> pis;
|
2021-12-14 16:50:40 +05:00
|
|
|
|
for(auto i : Range(np))
|
|
|
|
|
{
|
|
|
|
|
el[i] = sel[i];
|
|
|
|
|
el[i+np] = map[sel[i]];
|
2022-01-17 18:59:23 +05:00
|
|
|
|
pis.insert(sel[i]);
|
|
|
|
|
pis.insert(map[sel[i]]);
|
2021-12-14 16:50:40 +05:00
|
|
|
|
}
|
2021-12-16 00:05:18 +05:00
|
|
|
|
|
Fix various typos
Found via `codespell -q 3 -S ./external_dependencies/pybind11 -L alledges,allright,ane,anormal,ans,apoints,ba,boxs,cancle,childs,co-ordinate,co-ordinates,daty,enty,filld,hel,identifyable,ist,linz,lod,ned,nd,selt,statics,suround,thev,thist,thisy,timere,upto,wel`
2022-03-26 03:21:48 +05:00
|
|
|
|
// degenerate element (mapped element onto itself, might happen for surface elements connecting two identified faces)
|
2022-01-17 18:59:23 +05:00
|
|
|
|
if(pis.size() < 2*np)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// check if new element is inside current domain
|
|
|
|
|
auto p0 = mesh[sel[0]];
|
2022-06-09 18:32:17 +05:00
|
|
|
|
Vec<3> n = -Cross(mesh[sel[1]] - p0, mesh[sel[2]] - p0 );
|
2022-01-17 18:59:23 +05:00
|
|
|
|
|
|
|
|
|
if(n*(mesh[el[np]]-p0) < 0.0)
|
|
|
|
|
continue;
|
|
|
|
|
|
2021-12-14 16:50:40 +05:00
|
|
|
|
el.SetIndex(md.domain);
|
2022-01-17 18:59:23 +05:00
|
|
|
|
mesh.AddVolumeElement(el);
|
2022-06-09 18:32:17 +05:00
|
|
|
|
if(el.NP()==8)
|
|
|
|
|
{
|
|
|
|
|
// remember all adjacent faces of the new hex (to skip corresponding openelements accordingly)
|
|
|
|
|
for(auto facei : Range(1,7))
|
|
|
|
|
{
|
|
|
|
|
Element2d face;
|
|
|
|
|
el.GetFace(facei, face);
|
|
|
|
|
face.NormalizeNumbering();
|
|
|
|
|
hex_faces.insert({face[0], face[1], face[2]});
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-12-14 16:50:40 +05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-16 17:04:06 +05:00
|
|
|
|
void CloseOpenQuads( MeshingData & md)
|
|
|
|
|
{
|
2022-01-17 18:59:23 +05:00
|
|
|
|
static Timer t("CloseOpenQuads"); RegionTimer rt(t);
|
2021-06-22 14:16:28 +05:00
|
|
|
|
auto & mesh = *md.mesh;
|
2021-06-16 17:04:06 +05:00
|
|
|
|
auto domain = md.domain;
|
|
|
|
|
MeshingParameters & mp = md.mp;
|
|
|
|
|
|
|
|
|
|
int oldne;
|
|
|
|
|
if (multithread.terminate)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
mesh.CalcSurfacesOfNode();
|
2021-06-16 18:35:50 +05:00
|
|
|
|
mesh.FindOpenElements(domain);
|
2021-06-16 17:04:06 +05:00
|
|
|
|
|
|
|
|
|
if (!mesh.GetNOpenElements())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
for (int qstep = 0; qstep <= 3; qstep++)
|
|
|
|
|
{
|
|
|
|
|
if (qstep == 0 && !mp.try_hexes) continue;
|
|
|
|
|
|
|
|
|
|
if (mesh.HasOpenQuads())
|
|
|
|
|
{
|
|
|
|
|
string rulefile = ngdir;
|
|
|
|
|
|
|
|
|
|
const char ** rulep = NULL;
|
|
|
|
|
switch (qstep)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
rulep = hexrules;
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
rulep = prismrules2;
|
|
|
|
|
break;
|
|
|
|
|
case 2: // connect pyramid to triangle
|
|
|
|
|
rulep = pyramidrules2;
|
|
|
|
|
break;
|
|
|
|
|
case 3: // connect to vis-a-vis point
|
|
|
|
|
rulep = pyramidrules;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Meshing3 meshing(rulep);
|
|
|
|
|
|
|
|
|
|
MeshingParameters mpquad = mp;
|
|
|
|
|
|
2022-02-15 13:38:20 +05:00
|
|
|
|
mpquad.giveuptol = mp.giveuptolopenquads;
|
2021-06-16 17:04:06 +05:00
|
|
|
|
mpquad.baseelnp = 4;
|
|
|
|
|
mpquad.starshapeclass = 1000;
|
|
|
|
|
mpquad.check_impossible = qstep == 1; // for prisms only (air domain in trafo)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (PointIndex pi : mesh.Points().Range())
|
|
|
|
|
meshing.AddPoint (mesh[pi], pi);
|
|
|
|
|
|
2021-07-16 14:30:11 +05:00
|
|
|
|
NgArray<INDEX_2> connectednodes;
|
|
|
|
|
for (int nr = 1; nr <= mesh.GetIdentifications().GetMaxNr(); nr++)
|
|
|
|
|
if (mesh.GetIdentifications().GetType(nr) != Identifications::PERIODIC)
|
|
|
|
|
{
|
|
|
|
|
mesh.GetIdentifications().GetPairs (nr, connectednodes);
|
|
|
|
|
for (auto pair : connectednodes)
|
2022-01-17 18:59:23 +05:00
|
|
|
|
{
|
2021-07-16 14:30:11 +05:00
|
|
|
|
meshing.AddConnectedPair (pair);
|
2022-01-17 18:59:23 +05:00
|
|
|
|
meshing.AddConnectedPair ({pair[1], pair[0]});
|
|
|
|
|
}
|
2021-07-16 14:30:11 +05:00
|
|
|
|
}
|
2021-06-16 17:04:06 +05:00
|
|
|
|
|
|
|
|
|
for (int i = 1; i <= mesh.GetNOpenElements(); i++)
|
|
|
|
|
{
|
|
|
|
|
Element2d hel = mesh.OpenElement(i);
|
|
|
|
|
meshing.AddBoundaryElement (hel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
oldne = mesh.GetNE();
|
|
|
|
|
|
|
|
|
|
meshing.GenerateMesh (mesh, mpquad);
|
|
|
|
|
|
|
|
|
|
for (int i = oldne + 1; i <= mesh.GetNE(); i++)
|
|
|
|
|
mesh.VolumeElement(i).SetIndex (domain);
|
|
|
|
|
|
|
|
|
|
(*testout)
|
|
|
|
|
<< "mesh has " << mesh.GetNE() << " prism/pyramid elements" << endl;
|
|
|
|
|
|
2021-06-16 18:35:50 +05:00
|
|
|
|
mesh.FindOpenElements(domain);
|
2021-06-16 17:04:06 +05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (mesh.HasOpenQuads())
|
|
|
|
|
{
|
2022-10-04 15:26:02 +05:00
|
|
|
|
if(debugparam.write_mesh_on_error)
|
|
|
|
|
md.mesh->Save("open_quads_"+ToString(md.domain)+".vol.gz");
|
2021-06-16 17:04:06 +05:00
|
|
|
|
PrintSysError ("mesh has still open quads");
|
|
|
|
|
throw NgException ("Stop meshing since too many attempts");
|
|
|
|
|
// return MESHING3_GIVEUP;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-16 18:05:58 +05:00
|
|
|
|
|
2021-07-16 21:49:05 +05:00
|
|
|
|
void MeshDomain( MeshingData & md)
|
|
|
|
|
{
|
2021-06-22 14:16:28 +05:00
|
|
|
|
auto & mesh = *md.mesh;
|
2021-07-16 21:49:05 +05:00
|
|
|
|
auto domain = md.domain;
|
|
|
|
|
MeshingParameters & mp = md.mp;
|
2021-06-16 18:05:58 +05:00
|
|
|
|
|
|
|
|
|
mesh.CalcSurfacesOfNode();
|
|
|
|
|
mesh.FindOpenElements(md.domain);
|
|
|
|
|
|
2021-07-16 21:49:05 +05:00
|
|
|
|
md.meshing = make_unique<Meshing3>(nullptr);
|
2021-06-16 18:05:58 +05:00
|
|
|
|
for (PointIndex pi : mesh.Points().Range())
|
|
|
|
|
md.meshing->AddPoint (mesh[pi], pi);
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i <= mesh.GetNOpenElements(); i++)
|
|
|
|
|
md.meshing->AddBoundaryElement (mesh.OpenElement(i));
|
|
|
|
|
|
2021-06-16 17:04:06 +05:00
|
|
|
|
if (mp.delaunay && mesh.GetNOpenElements())
|
|
|
|
|
{
|
|
|
|
|
int oldne = mesh.GetNE();
|
|
|
|
|
|
|
|
|
|
md.meshing->Delaunay (mesh, domain, mp);
|
|
|
|
|
|
|
|
|
|
for (int i = oldne + 1; i <= mesh.GetNE(); i++)
|
|
|
|
|
mesh.VolumeElement(i).SetIndex (domain);
|
|
|
|
|
|
|
|
|
|
PrintMessage (3, mesh.GetNP(), " points, ",
|
|
|
|
|
mesh.GetNE(), " elements");
|
2023-04-21 15:52:17 +05:00
|
|
|
|
mesh.FindOpenElements(domain);
|
2021-06-16 17:04:06 +05:00
|
|
|
|
}
|
|
|
|
|
|
2021-06-16 18:05:58 +05:00
|
|
|
|
Box<3> domain_bbox( Box<3>::EMPTY_BOX );
|
|
|
|
|
|
|
|
|
|
for (auto & sel : mesh.SurfaceElements())
|
|
|
|
|
{
|
|
|
|
|
if (sel.IsDeleted() ) continue;
|
|
|
|
|
|
|
|
|
|
for (auto pi : sel.PNums())
|
|
|
|
|
domain_bbox.Add (mesh[pi]);
|
|
|
|
|
}
|
|
|
|
|
domain_bbox.Increase (0.01 * domain_bbox.Diam());
|
|
|
|
|
|
2021-06-16 17:04:06 +05:00
|
|
|
|
int cntsteps = 0;
|
|
|
|
|
int meshed;
|
|
|
|
|
if (mesh.GetNOpenElements())
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
if (multithread.terminate)
|
|
|
|
|
break;
|
|
|
|
|
|
2021-06-16 18:05:58 +05:00
|
|
|
|
mesh.FindOpenElements(domain);
|
2021-06-16 17:04:06 +05:00
|
|
|
|
PrintMessage (5, mesh.GetNOpenElements(), " open faces");
|
2021-09-21 17:39:35 +05:00
|
|
|
|
// GetOpenElements( mesh, domain )->Save("open_"+ToString(cntsteps)+".vol");
|
2021-06-16 17:04:06 +05:00
|
|
|
|
cntsteps++;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (cntsteps > mp.maxoutersteps)
|
2022-10-04 15:26:02 +05:00
|
|
|
|
{
|
|
|
|
|
if(debugparam.write_mesh_on_error)
|
|
|
|
|
md.mesh->Save("meshing_error_domain_"+ToString(md.domain)+".vol.gz");
|
|
|
|
|
throw NgException ("Stop meshing since too many attempts in domain " + ToString(md.domain));
|
|
|
|
|
}
|
2021-06-16 17:04:06 +05:00
|
|
|
|
|
|
|
|
|
PrintMessage (1, "start tetmeshing");
|
|
|
|
|
|
|
|
|
|
Meshing3 meshing(tetrules);
|
|
|
|
|
|
|
|
|
|
Array<PointIndex, PointIndex> glob2loc(mesh.GetNP());
|
|
|
|
|
glob2loc = PointIndex::INVALID;
|
|
|
|
|
|
2021-06-16 18:05:58 +05:00
|
|
|
|
for (PointIndex pi : mesh.Points().Range())
|
|
|
|
|
if (domain_bbox.IsIn (mesh[pi]))
|
|
|
|
|
glob2loc[pi] = meshing.AddPoint (mesh[pi], pi);
|
|
|
|
|
|
2021-06-16 17:04:06 +05:00
|
|
|
|
for (auto sel : mesh.OpenElements() )
|
|
|
|
|
{
|
|
|
|
|
for(auto & pi : sel.PNums())
|
|
|
|
|
pi = glob2loc[pi];
|
|
|
|
|
meshing.AddBoundaryElement (sel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int oldne = mesh.GetNE();
|
|
|
|
|
|
|
|
|
|
mp.giveuptol = 15 + 10 * cntsteps;
|
|
|
|
|
mp.sloppy = 5;
|
|
|
|
|
meshing.GenerateMesh (mesh, mp);
|
|
|
|
|
|
|
|
|
|
for (ElementIndex ei = oldne; ei < mesh.GetNE(); ei++)
|
|
|
|
|
mesh[ei].SetIndex (domain);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mesh.CalcSurfacesOfNode();
|
2021-06-16 18:05:58 +05:00
|
|
|
|
mesh.FindOpenElements(domain);
|
2021-06-16 17:04:06 +05:00
|
|
|
|
|
|
|
|
|
// teterrpow = 2;
|
|
|
|
|
if (mesh.GetNOpenElements() != 0)
|
|
|
|
|
{
|
|
|
|
|
meshed = 0;
|
|
|
|
|
PrintMessage (5, mesh.GetNOpenElements(), " open faces found");
|
|
|
|
|
|
|
|
|
|
MeshOptimize3d optmesh(mp);
|
|
|
|
|
|
|
|
|
|
const char * optstr = "mcmstmcmstmcmstmcm";
|
|
|
|
|
for (size_t j = 1; j <= strlen(optstr); j++)
|
|
|
|
|
{
|
2022-04-14 20:40:22 +05:00
|
|
|
|
mesh.FindOpenElements();
|
2021-06-16 17:04:06 +05:00
|
|
|
|
mesh.CalcSurfacesOfNode();
|
|
|
|
|
mesh.FreeOpenElementsEnvironment(2);
|
|
|
|
|
mesh.CalcSurfacesOfNode();
|
|
|
|
|
|
|
|
|
|
switch (optstr[j-1])
|
|
|
|
|
{
|
|
|
|
|
case 'c': optmesh.CombineImprove(mesh, OPT_REST); break;
|
|
|
|
|
case 'd': optmesh.SplitImprove(mesh, OPT_REST); break;
|
|
|
|
|
case 's': optmesh.SwapImprove(mesh, OPT_REST); break;
|
|
|
|
|
case 't': optmesh.SwapImprove2(mesh, OPT_REST); break;
|
|
|
|
|
case 'm': mesh.ImproveMesh(mp, OPT_REST); break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-14 20:40:22 +05:00
|
|
|
|
mesh.FindOpenElements(domain);
|
2021-06-16 17:04:06 +05:00
|
|
|
|
PrintMessage (3, "Call remove problem");
|
|
|
|
|
RemoveProblem (mesh, domain);
|
2022-04-14 20:40:22 +05:00
|
|
|
|
mesh.FindOpenElements(domain);
|
2021-06-16 17:04:06 +05:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
meshed = 1;
|
|
|
|
|
PrintMessage (1, "Success !");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
while (!meshed);
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
PrintMessage (3, "Check subdomain ", domain, " / ", mesh.GetNDomains());
|
|
|
|
|
|
2021-06-16 18:05:58 +05:00
|
|
|
|
mesh.FindOpenElements(domain);
|
2021-06-16 17:04:06 +05:00
|
|
|
|
|
|
|
|
|
bool res = (mesh.CheckConsistentBoundary() != 0);
|
|
|
|
|
if (res)
|
2009-10-28 04:04:42 +05:00
|
|
|
|
{
|
2022-10-04 15:26:02 +05:00
|
|
|
|
if(debugparam.write_mesh_on_error)
|
|
|
|
|
md.mesh->Save("inconsistent_surface_domain_"+ToString(md.domain)+".vol.gz");
|
2009-10-28 04:04:42 +05:00
|
|
|
|
PrintError ("Surface mesh not consistent");
|
2021-06-11 20:57:45 +05:00
|
|
|
|
throw NgException ("Stop meshing since surface mesh not consistent");
|
2009-10-28 04:04:42 +05:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-11 20:57:45 +05:00
|
|
|
|
}
|
2009-10-28 04:04:42 +05:00
|
|
|
|
|
2021-06-16 17:04:06 +05:00
|
|
|
|
void MergeMeshes( Mesh & mesh, Array<MeshingData> & md )
|
|
|
|
|
{
|
|
|
|
|
// todo: optimize: count elements, alloc all memory, copy vol elements in parallel
|
|
|
|
|
static Timer t("MergeMeshes"); RegionTimer rt(t);
|
2021-06-22 14:25:50 +05:00
|
|
|
|
if(md.Size()==1)
|
|
|
|
|
{
|
|
|
|
|
// assume that mesh was never divided, no need to do anything
|
|
|
|
|
if(&mesh != md[0].mesh.get())
|
|
|
|
|
throw Exception("Illegal Mesh pointer in MeshingData");
|
|
|
|
|
|
|
|
|
|
md[0].mesh.release();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-21 15:53:05 +05:00
|
|
|
|
mesh.VolumeElements().DeleteAll();
|
2021-06-16 17:04:06 +05:00
|
|
|
|
for(auto & m_ : md)
|
|
|
|
|
{
|
|
|
|
|
auto first_new_pi = m_.pmap.Range().Next();
|
2021-06-22 14:16:28 +05:00
|
|
|
|
auto & m = *m_.mesh;
|
2021-06-16 17:04:06 +05:00
|
|
|
|
Array<PointIndex, PointIndex> pmap(m.Points().Size());
|
|
|
|
|
for(auto pi : Range(PointIndex(PointIndex::BASE), first_new_pi))
|
|
|
|
|
pmap[pi] = m_.pmap[pi];
|
|
|
|
|
|
|
|
|
|
for (auto pi : Range(first_new_pi, m.Points().Range().Next()))
|
|
|
|
|
pmap[pi] = mesh.AddPoint(m[pi]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for ( auto el : m.VolumeElements() )
|
|
|
|
|
{
|
|
|
|
|
for (auto i : Range(el.GetNP()))
|
|
|
|
|
el[i] = pmap[el[i]];
|
|
|
|
|
el.SetIndex(m_.domain);
|
|
|
|
|
mesh.AddVolumeElement(el);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-11 20:57:45 +05:00
|
|
|
|
void MergeMeshes( Mesh & mesh, FlatArray<Mesh> meshes, PointIndex first_new_pi )
|
|
|
|
|
{
|
2021-06-14 12:15:43 +05:00
|
|
|
|
// todo: optimize: count elements, alloc all memory, copy vol elements in parallel
|
2021-06-11 20:57:45 +05:00
|
|
|
|
static Timer t("MergeMeshes"); RegionTimer rt(t);
|
|
|
|
|
for(auto & m : meshes)
|
2009-10-28 04:04:42 +05:00
|
|
|
|
{
|
2021-06-11 20:57:45 +05:00
|
|
|
|
Array<PointIndex, PointIndex> pmap(m.Points().Size());
|
|
|
|
|
for(auto pi : Range(PointIndex(PointIndex::BASE), first_new_pi))
|
|
|
|
|
pmap[pi] = pi;
|
2009-01-13 04:40:13 +05:00
|
|
|
|
|
2021-06-11 20:57:45 +05:00
|
|
|
|
for (auto pi : Range(first_new_pi, m.Points().Range().Next()))
|
|
|
|
|
pmap[pi] = mesh.AddPoint(m[pi]);
|
2009-01-13 04:40:13 +05:00
|
|
|
|
|
2009-10-28 04:04:42 +05:00
|
|
|
|
|
2021-06-11 20:57:45 +05:00
|
|
|
|
for ( auto el : m.VolumeElements() )
|
|
|
|
|
{
|
|
|
|
|
for (auto i : Range(el.GetNP()))
|
|
|
|
|
el[i] = pmap[el[i]];
|
|
|
|
|
mesh.AddVolumeElement(el);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-10-28 04:04:42 +05:00
|
|
|
|
|
2021-06-11 20:57:45 +05:00
|
|
|
|
// extern double teterrpow;
|
|
|
|
|
MESHING3_RESULT MeshVolume (const MeshingParameters & mp, Mesh& mesh3d)
|
2021-06-16 17:04:06 +05:00
|
|
|
|
{
|
|
|
|
|
static Timer t("MeshVolume"); RegionTimer reg(t);
|
|
|
|
|
|
|
|
|
|
mesh3d.Compress();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(mesh3d.GetNDomains()==0)
|
|
|
|
|
return MESHING3_OK;
|
|
|
|
|
|
2021-07-16 21:59:18 +05:00
|
|
|
|
if (!mesh3d.HasLocalHFunction())
|
2021-06-16 18:49:23 +05:00
|
|
|
|
mesh3d.CalcLocalH(mp.grading);
|
2021-06-16 17:04:06 +05:00
|
|
|
|
|
|
|
|
|
auto md = DivideMesh(mesh3d, mp);
|
|
|
|
|
|
2022-08-04 13:31:33 +05:00
|
|
|
|
try
|
|
|
|
|
{
|
2021-06-16 17:04:06 +05:00
|
|
|
|
ParallelFor( md.Range(), [&](int i)
|
|
|
|
|
{
|
2021-09-02 00:05:12 +05:00
|
|
|
|
if (mp.checkoverlappingboundary)
|
|
|
|
|
if (md[i].mesh->CheckOverlappingBoundary())
|
2022-10-04 15:26:02 +05:00
|
|
|
|
{
|
|
|
|
|
if(debugparam.write_mesh_on_error)
|
|
|
|
|
md[i].mesh->Save("overlapping_mesh_domain_"+ToString(md[i].domain)+".vol.gz");
|
2021-09-02 00:05:12 +05:00
|
|
|
|
throw NgException ("Stop meshing since boundary mesh is overlapping");
|
2022-10-04 15:26:02 +05:00
|
|
|
|
}
|
2022-06-09 18:32:17 +05:00
|
|
|
|
|
|
|
|
|
if(md[i].mesh->GetGeometry()->GetGeomType() == Mesh::GEOM_OCC)
|
|
|
|
|
FillCloseSurface( md[i] );
|
2021-06-16 17:04:06 +05:00
|
|
|
|
CloseOpenQuads( md[i] );
|
2021-06-16 18:05:58 +05:00
|
|
|
|
MeshDomain(md[i]);
|
2022-04-15 13:28:08 +05:00
|
|
|
|
}, md.Size());
|
2022-08-04 13:31:33 +05:00
|
|
|
|
}
|
|
|
|
|
catch(...)
|
|
|
|
|
{
|
|
|
|
|
MergeMeshes(mesh3d, md);
|
|
|
|
|
return MESHING3_GIVEUP;
|
|
|
|
|
}
|
2021-06-16 17:04:06 +05:00
|
|
|
|
|
|
|
|
|
MergeMeshes(mesh3d, md);
|
|
|
|
|
|
|
|
|
|
MeshQuality3d (mesh3d);
|
|
|
|
|
|
|
|
|
|
return MESHING3_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-08-06 13:42:53 +05:00
|
|
|
|
MESHING3_RESULT OptimizeVolume (const MeshingParameters & mp,
|
2009-01-13 04:40:13 +05:00
|
|
|
|
Mesh & mesh3d)
|
|
|
|
|
// const CSGeometry * geometry)
|
|
|
|
|
{
|
2019-01-31 22:41:20 +05:00
|
|
|
|
static Timer t("OptimizeVolume"); RegionTimer reg(t);
|
2023-09-05 20:13:29 +05:00
|
|
|
|
#ifndef EMSCRIPTEN
|
2019-09-26 19:15:50 +05:00
|
|
|
|
RegionTaskManager rtm(mp.parallel_meshing ? mp.nthreads : 0);
|
2023-09-05 20:13:29 +05:00
|
|
|
|
#endif // EMSCRIPTEN
|
2019-10-31 19:17:28 +05:00
|
|
|
|
const char* savetask = multithread.task;
|
|
|
|
|
multithread.task = "Optimize Volume";
|
2019-01-31 22:41:20 +05:00
|
|
|
|
|
2023-08-05 15:01:01 +05:00
|
|
|
|
// int i;
|
2009-01-13 04:40:13 +05:00
|
|
|
|
|
|
|
|
|
PrintMessage (1, "Volume Optimization");
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
if (!mesh3d.PureTetMesh())
|
|
|
|
|
return MESHING3_OK;
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// (*mycout) << "optstring = " << mp.optimize3d << endl;
|
|
|
|
|
/*
|
|
|
|
|
const char * optstr = globflags.GetStringFlag ("optimize3d", "cmh");
|
|
|
|
|
int optsteps = int (globflags.GetNumFlag ("optsteps3d", 2));
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
mesh3d.CalcSurfacesOfNode();
|
2019-10-31 19:17:28 +05:00
|
|
|
|
for (auto i : Range(mp.optsteps3d))
|
2009-01-13 04:40:13 +05:00
|
|
|
|
{
|
|
|
|
|
if (multithread.terminate)
|
|
|
|
|
break;
|
|
|
|
|
|
2011-07-25 17:33:19 +06:00
|
|
|
|
MeshOptimize3d optmesh(mp);
|
2009-01-13 04:40:13 +05:00
|
|
|
|
|
2011-01-11 01:18:01 +05:00
|
|
|
|
// teterrpow = mp.opterrpow;
|
2014-08-31 15:14:18 +06:00
|
|
|
|
// for (size_t j = 1; j <= strlen(mp.optimize3d); j++)
|
2019-10-31 19:17:28 +05:00
|
|
|
|
for (auto j : Range(mp.optimize3d.size()))
|
2009-01-13 04:40:13 +05:00
|
|
|
|
{
|
2019-10-31 19:17:28 +05:00
|
|
|
|
multithread.percent = 100.* (double(j)/mp.optimize3d.size() + i)/mp.optsteps3d;
|
2009-01-13 04:40:13 +05:00
|
|
|
|
if (multithread.terminate)
|
|
|
|
|
break;
|
|
|
|
|
|
2019-10-31 19:17:28 +05:00
|
|
|
|
switch (mp.optimize3d[j])
|
2009-01-13 04:40:13 +05:00
|
|
|
|
{
|
|
|
|
|
case 'c': optmesh.CombineImprove(mesh3d, OPT_REST); break;
|
2020-07-23 15:24:43 +05:00
|
|
|
|
case 'd': optmesh.SplitImprove(mesh3d); break;
|
2020-07-23 23:12:20 +05:00
|
|
|
|
case 'D': optmesh.SplitImprove2(mesh3d); break;
|
2009-01-13 04:40:13 +05:00
|
|
|
|
case 's': optmesh.SwapImprove(mesh3d); break;
|
|
|
|
|
// case 'u': optmesh.SwapImproveSurface(mesh3d); break;
|
|
|
|
|
case 't': optmesh.SwapImprove2(mesh3d); break;
|
|
|
|
|
#ifdef SOLIDGEOM
|
|
|
|
|
case 'm': mesh3d.ImproveMesh(*geometry); break;
|
|
|
|
|
case 'M': mesh3d.ImproveMesh(*geometry); break;
|
|
|
|
|
#else
|
2011-07-25 17:33:19 +06:00
|
|
|
|
case 'm': mesh3d.ImproveMesh(mp); break;
|
|
|
|
|
case 'M': mesh3d.ImproveMesh(mp); break;
|
2009-01-13 04:40:13 +05:00
|
|
|
|
#endif
|
2011-07-25 17:33:19 +06:00
|
|
|
|
case 'j': mesh3d.ImproveMeshJacobian(mp); break;
|
2009-01-13 04:40:13 +05:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-07-29 20:46:09 +05:00
|
|
|
|
// mesh3d.mglevels = 1;
|
2009-01-13 04:40:13 +05:00
|
|
|
|
MeshQuality3d (mesh3d);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-31 19:17:28 +05:00
|
|
|
|
multithread.task = savetask;
|
2009-01-13 04:40:13 +05:00
|
|
|
|
return MESHING3_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void RemoveIllegalElements (Mesh & mesh3d)
|
|
|
|
|
{
|
2019-01-31 22:41:20 +05:00
|
|
|
|
static Timer t("RemoveIllegalElements"); RegionTimer reg(t);
|
|
|
|
|
|
2009-01-13 04:40:13 +05:00
|
|
|
|
int it = 10;
|
|
|
|
|
int nillegal, oldn;
|
|
|
|
|
|
|
|
|
|
PrintMessage (1, "Remove Illegal Elements");
|
|
|
|
|
// return, if non-pure tet-mesh
|
|
|
|
|
/*
|
|
|
|
|
if (!mesh3d.PureTetMesh())
|
|
|
|
|
return;
|
|
|
|
|
*/
|
|
|
|
|
mesh3d.CalcSurfacesOfNode();
|
|
|
|
|
|
|
|
|
|
nillegal = mesh3d.MarkIllegalElements();
|
|
|
|
|
|
2011-07-25 17:33:19 +06:00
|
|
|
|
MeshingParameters dummymp;
|
|
|
|
|
MeshOptimize3d optmesh(dummymp);
|
2009-01-13 04:40:13 +05:00
|
|
|
|
while (nillegal && (it--) > 0)
|
|
|
|
|
{
|
|
|
|
|
if (multithread.terminate)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
PrintMessage (5, nillegal, " illegal tets");
|
2016-12-12 00:17:07 +05:00
|
|
|
|
optmesh.SplitImprove (mesh3d, OPT_LEGAL);
|
2009-01-13 04:40:13 +05:00
|
|
|
|
|
|
|
|
|
mesh3d.MarkIllegalElements(); // test
|
|
|
|
|
optmesh.SwapImprove (mesh3d, OPT_LEGAL);
|
|
|
|
|
mesh3d.MarkIllegalElements(); // test
|
|
|
|
|
optmesh.SwapImprove2 (mesh3d, OPT_LEGAL);
|
|
|
|
|
|
|
|
|
|
oldn = nillegal;
|
|
|
|
|
nillegal = mesh3d.MarkIllegalElements();
|
|
|
|
|
|
|
|
|
|
if (oldn != nillegal)
|
|
|
|
|
it = 10;
|
|
|
|
|
}
|
|
|
|
|
PrintMessage (5, nillegal, " illegal tets");
|
|
|
|
|
}
|
|
|
|
|
}
|