netgen/libsrc/meshing/meshclass.cpp
Monty Montgomery de7ffc5906 Eliminate a "C++ initialization order fiasco" for geometryregister
Current initialization of the global geometryregister suffers from a
classic 'initialization order fiasco'.  Depending on the order the
compilation units are loaded/linked, the initialization of the global
geometryregisterarray is not guaranteed to happen (and indeed often
does not happen) before it is used.  This leads to entries being
appended before it's initialized (usually 'suceeding, but potentially
causing memory corruption if the segment at that point isn't zeroed),
initialization then happening halfway through (wiping the initial
entries) and then the last entries being the only ones that show up.

The net effect is either a crash at startup, or several geometry types
seeming to be missing.  Eg, step files will oad, but STL files are
just ignored.  The bug is actively observed on, eg, Linux.

This patch implements a simple 'initialize at first access' convention
for the array, eliminating the ordering problem.

I've not reviewed the rest of the source for other potential examples
of the fiasco pattern; this fixes only the geometryregister, since
that was actively biting.
2022-05-22 11:29:10 -04:00

7438 lines
204 KiB
C++

#include <mystdlib.h>
#include <atomic>
#include <set>
#include "meshing.hpp"
#include "../general/gzstream.h"
#ifdef NG_PYTHON
// must be included to instantiate Archive::Shallow(NetgenGeometry&)
#include <core/python_ngcore.hpp>
#endif
namespace netgen
{
int Find3dElement (const Mesh& mesh,
const netgen::Point<3> & p,
double * lami,
const NgArray<int> * const indices,
BoxTree<3> * searchtree,
const bool allowindex = true)
{
int ne = 0;
NgArray<int> locels;
if (searchtree)
{
searchtree->GetIntersecting (p, p, locels);
ne = locels.Size();
}
else
ne = mesh.GetNE();
for (int i = 1; i <= ne; i++)
{
int ii;
if (searchtree)
ii = locels.Get(i);
else
ii = i;
if(indices != NULL && indices->Size() > 0)
{
bool contained = indices->Contains(mesh.VolumeElement(ii).GetIndex());
if((allowindex && !contained) || (!allowindex && contained)) continue;
}
if(mesh.PointContainedIn3DElement(p,lami,ii))
return ii;
}
// Not found, try uncurved variant:
for (int i = 1; i <= ne; i++)
{
int ii;
if (searchtree)
ii = locels.Get(i);
else
ii = i;
if(indices != NULL && indices->Size() > 0)
{
bool contained = indices->Contains(mesh.VolumeElement(ii).GetIndex());
if((allowindex && !contained) || (!allowindex && contained)) continue;
}
if(mesh.PointContainedIn3DElementOld(p,lami,ii))
{
(*testout) << "WARNING: found element of point " << p <<" only for uncurved mesh" << endl;
return ii;
}
}
return 0;
}
int Find2dElement (const Mesh& mesh,
const netgen::Point<3> & p,
double * lami,
const NgArray<int> * const indices,
BoxTree<3> * searchtree,
const bool allowindex = true)
{
double vlam[3];
int velement = 0;
if(mesh.GetNE())
velement = Find3dElement(mesh, p,vlam,NULL,searchtree,allowindex);
//(*testout) << "p " << p << endl;
//(*testout) << "velement " << velement << endl;
// first try to find a volume element containing p and project to face
if(velement!=0)
{
auto & topology = mesh.GetTopology();
// NgArray<int> faces;
// topology.GetElementFaces(velement,faces);
auto faces = Array<int> (topology.GetFaces(ElementIndex(velement-1)));
//(*testout) << "faces " << faces << endl;
for(int i=0; i<faces.Size(); i++)
faces[i] = topology.GetFace2SurfaceElement(faces[i]+1);
//(*testout) << "surfel " << faces << endl;
for(int i=0; i<faces.Size(); i++)
{
if(faces[i] == 0)
continue;
auto sel = mesh.SurfaceElement(faces[i]);
if(indices && indices->Size() != 0 && !indices->Contains(sel.GetIndex()))
continue;
auto & el = mesh.VolumeElement(velement);
if (el.GetType() == TET)
{
double lam4[4] = { vlam[0], vlam[1], vlam[2], 1.0-vlam[0]-vlam[1]-vlam[2] };
double face_lam = lam4[i];
if(face_lam < 1e-5)
{
// found volume point very close to a face -> use barycentric coordinates directly
lami[2] = 0.0;
for(auto j : Range(1,3))
for(auto k : Range(4))
if(sel[j] == el[k])
lami[j-1] = lam4[k]/(1.0-face_lam);
return faces[i];
}
}
if(mesh.PointContainedIn2DElement(p,lami,faces[i],true))
return faces[i];
}
}
// Did't find any matching face of a volume element, search 2d elements directly
int ne;
NgArray<int> locels;
// TODO: build search tree for surface elements
if (!mesh.GetNE() && searchtree)
{
searchtree->GetIntersecting (p, p, locels);
ne = locels.Size();
}
else
ne = mesh.GetNSE();
for (int i = 1; i <= ne; i++)
{
int ii;
if (locels.Size())
ii = locels.Get(i);
else
ii = i;
if(indices != NULL && indices->Size() > 0)
{
bool contained = indices->Contains(mesh.SurfaceElement(ii).GetIndex());
if((allowindex && !contained) || (!allowindex && contained)) continue;
}
if(mesh.PointContainedIn2DElement(p,lami,ii)) return ii;
}
return 0;
}
int Find1dElement (const Mesh& mesh,
const netgen::Point<3> & p,
double * lami,
const NgArray<int> * const indices,
BoxTree<3> * searchtree,
const bool allowindex = true)
{
double vlam[3];
int velement = Find2dElement(mesh, p, vlam, NULL, searchtree, allowindex);
if(velement == 0)
return 0;
vlam[2] = 1.-vlam[0] - vlam[1];
NgArray<int> edges;
auto & topology = mesh.GetTopology();
topology.GetSurfaceElementEdges(velement, edges);
Array<SegmentIndex> segs(edges.Size());
for(auto i : Range(edges))
segs[i] = topology.GetSegmentOfEdge(edges[i]);
for(auto i : Range(segs))
{
if(IsInvalid(segs[i]))
continue;
auto& el = mesh.SurfaceElement(velement);
if(el.GetType() == TRIG)
{
double seg_lam;
double lam;
auto seg = mesh.LineSegment(segs[i]);
for(auto k : Range(3))
{
if(seg[0] == el[k])
lam = vlam[k];
if(seg[1] == el[k])
seg_lam = vlam[k];
}
if(1.- seg_lam - lam < 1e-5)
{
// found point close to segment -> use barycentric coordinates directly
lami[0] = lam;
return int(segs[i])+1;
}
}
else
throw NgException("Quad not implemented yet!");
}
return 0;
}
static mutex buildsearchtree_mutex;
Mesh :: Mesh ()
: topology(*this), surfarea(*this)
{
boundaryedges = nullptr;
surfelementht = nullptr;
segmentht = nullptr;
lochfunc = {nullptr};
// mglevels = 1;
elementsearchtree = nullptr;
elementsearchtreets = NextTimeStamp();
majortimestamp = timestamp = NextTimeStamp();
hglob = 1e10;
hmin = 0;
numvertices = -1;
dimension = 3;
curvedelems = make_unique<CurvedElements> (*this);
clusters = make_unique<AnisotropicClusters> (*this);
ident = make_unique<Identifications> (*this);
hpelements = NULL;
coarsemesh = NULL;
ps_startelement = 0;
geomtype = NO_GEOM;
bcnames.SetSize(0);
cd2names.SetSize(0);
// this->comm = netgen :: ng_comm;
#ifdef PARALLEL
paralleltop = make_unique<ParallelMeshTopology> (*this);
#endif
}
Mesh :: ~Mesh()
{
// delete lochfunc;
// delete boundaryedges;
// delete surfelementht;
// delete segmentht;
// delete curvedelems;
// delete clusters;
// delete ident;
// delete elementsearchtree;
// delete coarsemesh;
// delete hpelements;
for (int i = 0; i < materials.Size(); i++)
delete materials[i];
for(int i = 0; i < userdata_int.Size(); i++)
delete userdata_int[i];
for(int i = 0; i < userdata_double.Size(); i++)
delete userdata_double[i];
for (int i = 0; i < bcnames.Size(); i++ )
delete bcnames[i];
for (int i = 0; i < cd2names.Size(); i++)
delete cd2names[i];
for (int i = 0; i < cd3names.Size(); i++)
delete cd3names[i];
// #ifdef PARALLEL
// delete paralleltop;
// #endif
}
void Mesh :: SetCommunicator(NgMPI_Comm acomm)
{
this->comm = acomm;
}
Mesh & Mesh :: operator= (const Mesh & mesh2)
{
geometry = mesh2.geometry;
dimension = mesh2.dimension;
points = mesh2.points;
segments = mesh2.segments;
surfelements = mesh2.surfelements;
volelements = mesh2.volelements;
lockedpoints = mesh2.lockedpoints;
facedecoding = mesh2.facedecoding;
dimension = mesh2.dimension;
hglob = mesh2.hglob;
hmin = mesh2.hmin;
maxhdomain = mesh2.maxhdomain;
materials.SetSize( mesh2.materials.Size() );
for ( int i = 0; i < mesh2.materials.Size(); i++ )
if ( mesh2.materials[i] ) materials[i] = new string ( *mesh2.materials[i] );
else materials[i] = 0;
std::map<const string*, string*> bcmap;
bcnames.SetSize( mesh2.bcnames.Size() );
for ( int i = 0; i < mesh2.bcnames.Size(); i++ )
{
if ( mesh2.bcnames[i] ) bcnames[i] = new string ( *mesh2.bcnames[i] );
else bcnames[i] = 0;
bcmap[mesh2.bcnames[i]] = bcnames[i];
}
// Remap string* members in FaceDescriptor to new mesh
for (auto & f : facedecoding)
f.SetBCName( bcmap[&f.GetBCName()] );
cd2names.SetSize(mesh2.cd2names.Size());
for (int i=0; i < mesh2.cd2names.Size(); i++)
if (mesh2.cd2names[i]) cd2names[i] = new string(*mesh2.cd2names[i]);
else cd2names[i] = 0;
cd3names.SetSize(mesh2.cd3names.Size());
for (int i=0; i < mesh2.cd3names.Size(); i++)
if (mesh2.cd3names[i]) cd3names[i] = new string(*mesh2.cd3names[i]);
else cd3names[i] = 0;
numvertices = mesh2.numvertices;
return *this;
}
void Mesh :: DeleteMesh()
{
NgLock lock(mutex);
lock.Lock();
points.SetSize(0);
segments.SetSize(0);
surfelements.SetSize(0);
volelements.SetSize(0);
lockedpoints.SetSize(0);
// surfacesonnode.SetSize(0);
// delete boundaryedges;
boundaryedges = nullptr;
segmentht = nullptr;
surfelementht = nullptr;
openelements.SetSize(0);
facedecoding.SetSize(0);
ident = make_unique<Identifications> (*this);
topology = MeshTopology (*this);
curvedelems = make_unique<CurvedElements> (*this);
clusters = make_unique<AnisotropicClusters> (*this);
for ( int i = 0; i < bcnames.Size(); i++ )
if ( bcnames[i] ) delete bcnames[i];
for (int i= 0; i< cd2names.Size(); i++)
if (cd2names[i]) delete cd2names[i];
#ifdef PARALLEL
paralleltop = make_unique<ParallelMeshTopology> (*this);
#endif
lock.UnLock();
timestamp = NextTimeStamp();
}
void Mesh :: ClearSurfaceElements()
{
surfelements.SetSize(0);
/*
for (int i = 0; i < facedecoding.Size(); i++)
facedecoding[i].firstelement = -1;
*/
for (auto & fd : facedecoding)
fd.firstelement = -1;
timestamp = NextTimeStamp();
}
PointIndex Mesh :: AddPoint (const Point3d & p, int layer)
{
return AddPoint (p, layer, INNERPOINT);
}
PointIndex Mesh :: AddPoint (const Point3d & p, int layer, POINTTYPE type)
{
// PointIndex pi = points.End();
PointIndex pi = *points.Range().end();
if (points.Size() == points.AllocSize())
{
NgLock lock(mutex);
lock.Lock();
points.Append ( MeshPoint (p, layer, type) );
lock.UnLock();
}
else
{
points.Append ( MeshPoint (p, layer, type) );
}
timestamp = NextTimeStamp();
return pi;
}
SegmentIndex Mesh :: AddSegment (const Segment & s)
{
NgLock lock(mutex);
lock.Lock();
timestamp = NextTimeStamp();
int maxn = max2 (s[0], s[1]);
maxn += 1-PointIndex::BASE;
/*
if (maxn > ptyps.Size())
{
int maxo = ptyps.Size();
ptyps.SetSize (maxn);
for (int i = maxo; i < maxn; i++)
ptyps[i] = INNERPOINT;
}
if (ptyps[s[0]] > EDGEPOINT) ptyps[s[0]] = EDGEPOINT;
if (ptyps[s[1]] > EDGEPOINT) ptyps[s[1]] = EDGEPOINT;
*/
if (maxn <= points.Size())
{
if (points[s[0]].Type() > EDGEPOINT)
points[s[0]].SetType (EDGEPOINT);
if (points[s[1]].Type() > EDGEPOINT)
points[s[1]].SetType (EDGEPOINT);
}
/*
else
{
cerr << "edge points nrs > points.Size" << endl;
}
*/
SegmentIndex si = segments.Size();
segments.Append (s);
lock.UnLock();
return si;
}
SurfaceElementIndex Mesh :: AddSurfaceElement (const Element2d & el)
{
timestamp = NextTimeStamp();
PointIndex maxn = el[0];
for (int i = 1; i < el.GetNP(); i++)
if (el[i] > maxn) maxn = el[i];
/*
maxn += 1-PointIndex::BASE;
if (maxn <= points.Size())
{
for (int i = 0; i < el.GetNP(); i++)
if (points[el[i]].Type() > SURFACEPOINT)
points[el[i]].SetType(SURFACEPOINT);
}
*/
// if (maxn < points.End())
if (maxn < *points.Range().end())
for (PointIndex pi : el.PNums())
if (points[pi].Type() > SURFACEPOINT)
points[pi].SetType(SURFACEPOINT);
SurfaceElementIndex si = surfelements.Size();
if (surfelements.AllocSize() == surfelements.Size())
{
NgLock lock(mutex);
lock.Lock();
surfelements.Append (el);
lock.UnLock();
}
else
{
surfelements.Append (el);
}
if (el.index<=0 || el.index > facedecoding.Size())
cerr << "has no facedecoding: fd.size = " << facedecoding.Size() << ", ind = " << el.index << endl;
surfelements.Last().next = facedecoding[el.index-1].firstelement;
facedecoding[el.index-1].firstelement = si;
if (SurfaceArea().Valid())
SurfaceArea().Add (el);
return si;
}
void Mesh :: SetSurfaceElement (SurfaceElementIndex sei, const Element2d & el)
{
int maxn = el[0];
for (int i = 1; i < el.GetNP(); i++)
if (el[i] > maxn) maxn = el[i];
maxn += 1-PointIndex::BASE;
if (maxn <= points.Size())
{
for (int i = 0; i < el.GetNP(); i++)
if (points[el[i]].Type() > SURFACEPOINT)
points[el[i]].SetType(SURFACEPOINT);
}
surfelements[sei] = el;
if (el.index > facedecoding.Size())
cerr << "has no facedecoding: fd.size = " << facedecoding.Size() << ", ind = " << el.index << endl;
// add lock-free to list ... slow, call RebuildSurfaceElementLists later
/*
surfelements[sei].next = facedecoding[el.index-1].firstelement;
auto & head = reinterpret_cast<atomic<SurfaceElementIndex>&> (facedecoding[el.index-1].firstelement);
while (!head.compare_exchange_weak (surfelements[sei].next, sei))
;
*/
/*
if (SurfaceArea().Valid())
SurfaceArea().Add (el);
*/
}
ElementIndex Mesh :: AddVolumeElement (const Element & el)
{
/*
int maxn = el[0];
for (int i = 1; i < el.GetNP(); i++)
if (el[i] > maxn) maxn = el[i];
maxn += 1-PointIndex::BASE;
*/
/*
if (maxn > ptyps.Size())
{
int maxo = ptyps.Size();
ptyps.SetSize (maxn);
for (i = maxo+PointIndex::BASE;
i < maxn+PointIndex::BASE; i++)
ptyps[i] = INNERPOINT;
}
*/
/*
if (maxn > points.Size())
{
cerr << "add vol element before point" << endl;
}
*/
int ve = volelements.Size();
if (volelements.Size() == volelements.AllocSize())
{
NgLock lock(mutex);
lock.Lock();
volelements.Append (el);
lock.UnLock();
}
else
{
volelements.Append (el);
}
volelements.Last().flags.illegal_valid = 0;
volelements.Last().flags.fixed = 0;
volelements.Last().flags.deleted = 0;
// while (volelements.Size() > eltyps.Size())
// eltyps.Append (FREEELEMENT);
timestamp = NextTimeStamp();
return ve;
}
void Mesh :: SetVolumeElement (ElementIndex ei, const Element & el)
{
/*
int maxn = el[0];
for (int i = 1; i < el.GetNP(); i++)
if (el[i] > maxn) maxn = el[i];
maxn += 1-PointIndex::BASE;
*/
volelements[ei] = el;
volelements[ei].flags.illegal_valid = 0;
volelements[ei].flags.fixed = 0;
volelements[ei].flags.deleted = 0;
}
void Mesh :: Save (const filesystem::path & filename) const
{
string ext0 = filename.stem().extension().string();
string ext = filename.extension().string();
if (ext0 == ".vol" && ext == ".bin")
{
BinaryOutArchive in(filename);
in & const_cast<Mesh&>(*this);
return;
}
ostream * outfile;
if (ext0 == ".vol" && ext == ".gz")
outfile = new ogzstream(filename);
else if (ext == ".vol")
outfile = new ofstream(filename);
else
outfile = new ogzstream(filesystem::path(filename).concat(".vol.gz"));
Save(*outfile);
delete outfile;
}
void Mesh :: Save (ostream & outfile) const
{
static Timer timer("Mesh::Save"); RegionTimer rt(timer);
int i, j;
double scale = 1; // globflags.GetNumFlag ("scale", 1);
int inverttets = 0; // globflags.GetDefineFlag ("inverttets");
int invertsurf = 0; // globflags.GetDefineFlag ("invertsurfacemesh");
outfile << "# Generated by NETGEN " << GetLibraryVersion("netgen") << endl << endl;
outfile << "mesh3d" << "\n";
outfile << "dimension\n" << GetDimension() << "\n";
outfile << "geomtype\n" << int(geomtype) << "\n";
outfile << "\n";
outfile << "# surfnr\tdomin\tdomout\ttlosurf\tbcprop\n";
outfile << "facedescriptors\n";
outfile << GetNFD() << "\n";
for(auto & fd : FaceDescriptors())
outfile << fd.SurfNr() << ' ' << fd.DomainIn() << ' ' << fd.DomainOut() << ' ' << fd.TLOSurface() << ' ' << fd.BCProperty() << '\n';
outfile << "\n";
outfile << "# surfnr bcnr domin domout np p1 p2 p3"
<< "\n";
switch (geomtype)
{
case GEOM_STL:
outfile << "surfaceelementsgi" << "\n";
break;
case GEOM_OCC: case GEOM_ACIS:
outfile << "surfaceelementsuv" << "\n";
break;
default:
outfile << "surfaceelements" << "\n";
}
outfile << GetNSE() << "\n";
for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
{
if ((*this)[sei].GetIndex())
{
outfile << " " << GetFaceDescriptor((*this)[sei].GetIndex ()).SurfNr()+1;
outfile << " " << GetFaceDescriptor((*this)[sei].GetIndex ()).BCProperty();
outfile << " " << GetFaceDescriptor((*this)[sei].GetIndex ()).DomainIn();
outfile << " " << GetFaceDescriptor((*this)[sei].GetIndex ()).DomainOut();
}
else
outfile << " 0 0 0";
Element2d sel = (*this)[sei];
if (invertsurf)
sel.Invert();
outfile << " " << sel.GetNP();
for (j = 0; j < sel.GetNP(); j++)
outfile << " " << sel[j];
switch (geomtype)
{
case GEOM_STL:
for (j = 1; j <= sel.GetNP(); j++)
outfile << " " << sel.GeomInfoPi(j).trignum;
break;
case GEOM_OCC: case GEOM_ACIS:
for (j = 1; j <= sel.GetNP(); j++)
{
outfile << " " << sel.GeomInfoPi(j).u;
outfile << " " << sel.GeomInfoPi(j).v;
}
break;
default:
;
}
outfile << "\n";
}
outfile << "\n" << "\n";
outfile << "# matnr np p1 p2 p3 p4" << "\n";
outfile << "volumeelements" << "\n";
outfile << GetNE() << "\n";
for (ElementIndex ei = 0; ei < GetNE(); ei++)
{
outfile << (*this)[ei].GetIndex();
outfile << " " << (*this)[ei].GetNP();
Element el = (*this)[ei];
if (inverttets) el.Invert();
for (j = 0; j < el.GetNP(); j++)
outfile << " " << el[j];
outfile << "\n";
}
outfile << "\n" << "\n";
// outfile << " surf1 surf2 p1 p2" << "\n";
outfile << "# surfid 0 p1 p2 trignum1 trignum2 domin/surfnr1 domout/surfnr2 ednr1 dist1 ednr2 dist2 \n";
outfile << "edgesegmentsgi2" << "\n";
outfile << GetNSeg() << "\n";
for (i = 1; i <= GetNSeg(); i++)
{
const Segment & seg = LineSegment (i);
outfile.width(8);
outfile << seg.si; // 2D: bc number, 3D: wievielte Kante
outfile.width(8);
outfile << 0;
outfile.width(8);
outfile << seg[0];
outfile.width(8);
outfile << seg[1];
outfile << " ";
outfile.width(8);
outfile << seg.geominfo[0].trignum; // stl dreiecke
outfile << " ";
outfile.width(8);
outfile << seg.geominfo[1].trignum; // << endl; // stl dreieck
if (dimension == 3)
{
outfile << " ";
outfile.width(8);
outfile << seg.surfnr1+1;
outfile << " ";
outfile.width(8);
outfile << seg.surfnr2+1;
}
else
{
outfile << " ";
outfile.width(8);
outfile << seg.domin;
outfile << " ";
outfile.width(8);
outfile << seg.domout;
}
outfile << " ";
outfile.width(8);
outfile << seg.edgenr;
outfile << " ";
outfile.width(12);
outfile.precision(16);
outfile << seg.epgeominfo[0].dist; // splineparameter (2D)
outfile << " ";
outfile.width(8);
outfile.precision(16);
outfile << seg.epgeominfo[1].edgenr; // geometry dependent
outfile << " ";
outfile.width(12);
outfile << seg.epgeominfo[1].dist;
outfile << "\n";
}
outfile << "\n" << "\n";
outfile << "# X Y Z" << "\n";
outfile << "points" << "\n";
outfile << GetNP() << "\n";
outfile.precision(16);
outfile.setf (ios::fixed, ios::floatfield);
outfile.setf (ios::showpoint);
PointIndex pi;
for (pi = PointIndex::BASE;
pi < GetNP()+PointIndex::BASE; pi++)
{
outfile.width(22);
outfile << (*this)[pi](0)/scale << " ";
outfile.width(22);
outfile << (*this)[pi](1)/scale << " ";
outfile.width(22);
outfile << (*this)[pi](2)/scale << "\n";
}
outfile << "\n" << "\n";
outfile << "# pnum index" << "\n";
outfile << "pointelements" << "\n";
outfile << pointelements.Size() << "\n";
for (i = 0; i < pointelements.Size(); i++)
{
outfile.width(8);
outfile << pointelements[i].pnum << " ";
outfile.width(8);
outfile << pointelements[i].index << "\n";
}
if (ident -> GetMaxNr() > 0)
{
outfile << "identifications\n";
NgArray<INDEX_2> identpairs;
int cnt = 0;
for (i = 1; i <= ident -> GetMaxNr(); i++)
{
ident -> GetPairs (i, identpairs);
cnt += identpairs.Size();
}
outfile << cnt << "\n";
for (i = 1; i <= ident -> GetMaxNr(); i++)
{
ident -> GetPairs (i, identpairs);
for (j = 1; j <= identpairs.Size(); j++)
{
outfile.width (8);
outfile << identpairs.Get(j).I1();
outfile.width (8);
outfile << identpairs.Get(j).I2();
outfile.width (8);
outfile << i << "\n";
}
}
outfile << "identificationtypes\n";
outfile << ident -> GetMaxNr() << "\n";
for (i = 1; i <= ident -> GetMaxNr(); i++)
{
int type = ident -> GetType(i);
outfile << " " << type;
}
outfile << "\n";
}
int cntmat = 0;
for (i = 1; i <= materials.Size(); i++)
if (materials.Get(i) && materials.Get(i)->length())
cntmat++;
if (cntmat)
{
outfile << "materials" << endl;
outfile << cntmat << endl;
for (i = 1; i <= materials.Size(); i++)
if (materials.Get(i) && materials.Get(i)->length())
outfile << i << " " << *materials.Get(i) << endl;
}
int cntbcnames = 0;
for ( int ii = 0; ii < bcnames.Size(); ii++ )
if ( bcnames[ii] ) cntbcnames++;
if ( cntbcnames )
{
outfile << "\n\nbcnames" << endl << bcnames.Size() << endl;
for ( i = 0; i < bcnames.Size(); i++ )
outfile << i+1 << "\t" << GetBCName(i) << endl;
outfile << endl << endl;
}
int cntcd2names = 0;
for (int ii = 0; ii<cd2names.Size(); ii++)
if(cd2names[ii]) cntcd2names++;
if(cntcd2names)
{
outfile << "\n\ncd2names" << endl << cd2names.Size() << endl;
for (i=0; i<cd2names.Size(); i++)
outfile << i+1 << "\t" << GetCD2Name(i) << endl;
outfile << endl << endl;
}
int cntcd3names = 0;
for (int ii = 0; ii<cd3names.Size(); ii++)
if(cd3names[ii]) cntcd3names++;
if(cntcd3names)
{
outfile << "\n\ncd3names" << endl << cd3names.Size() << endl;
for (i=0; i<cd3names.Size(); i++)
outfile << i+1 << "\t" << GetCD3Name(i) << endl;
outfile << endl << endl;
}
/*
if ( GetDimension() == 2 )
{
for (i = 1; i <= GetNSeg(); i++)
{
const Segment & seg = LineSegment (i);
if ( ! bcprops.Contains(seg.si) && seg.GetBCName() != "" )
{
bcprops.Append(seg.si);
cntbcnames++;
}
}
}
else
{
for (sei = 0; sei < GetNSE(); sei++)
{
if ((*this)[sei].GetIndex())
{
int bcp = GetFaceDescriptor((*this)[sei].GetIndex ()).BCProperty();
string name = GetFaceDescriptor((*this)[sei].GetIndex ()).BCName();
if ( !bcprops.Contains(bcp) &&
name != "" )
{
bcprops.Append(bcp);
cntbcnames++;
}
}
}
}
bcprops.SetSize(0);
if ( cntbcnames )
{
outfile << "\nbcnames" << endl << cntbcnames << endl;
if ( GetDimension() == 2 )
{
for (i = 1; i <= GetNSeg(); i++)
{
const Segment & seg = LineSegment (i);
if ( ! bcprops.Contains(seg.si) && seg.GetBCName() != "" )
{
bcprops.Append(seg.si);
outfile << seg.si << "\t" << seg.GetBCName() << endl;
}
}
}
else
{
for (sei = 0; sei < GetNSE(); sei++)
{
if ((*this)[sei].GetIndex())
{
int bcp = GetFaceDescriptor((*this)[sei].GetIndex ()).BCProperty();
string name = GetFaceDescriptor((*this)[sei].GetIndex ()).BCName();
if ( !bcprops.Contains(bcp) &&
name != "" )
{
bcprops.Append(bcp);
outfile << bcp << "\t" << name << endl;
}
}
}
}
outfile << endl << endl;
}
*/
int cnt_sing = 0;
// for (PointIndex pi = points.Begin(); pi < points.End(); pi++)
// if ((*this)[pi].Singularity()>=1.) cnt_sing++;
for (auto & p : points)
if (p.Singularity() >= 1.) cnt_sing++;
if (cnt_sing)
{
outfile << "singular_points" << endl << cnt_sing << endl;
// for (PointIndex pi = points.Begin(); pi < points.End(); pi++)
for (PointIndex pi : points.Range())
if ((*this)[pi].Singularity()>=1.)
outfile << int(pi) << "\t" << (*this)[pi].Singularity() << endl;
}
cnt_sing = 0;
for (SegmentIndex si = 0; si < GetNSeg(); si++)
if ( segments[si].singedge_left ) cnt_sing++;
if (cnt_sing)
{
outfile << "singular_edge_left" << endl << cnt_sing << endl;
for (SegmentIndex si = 0; si < GetNSeg(); si++)
if ( segments[si].singedge_left )
outfile << int(si) << "\t" << segments[si].singedge_left << endl;
}
cnt_sing = 0;
for (SegmentIndex si = 0; si < GetNSeg(); si++)
if ( segments[si].singedge_right ) cnt_sing++;
if (cnt_sing)
{
outfile << "singular_edge_right" << endl << cnt_sing << endl;
for (SegmentIndex si = 0; si < GetNSeg(); si++)
if ( segments[si].singedge_right )
outfile << int(si) << "\t" << segments[si].singedge_right << endl;
}
cnt_sing = 0;
for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
if ( GetFaceDescriptor ((*this)[sei].GetIndex()).domin_singular)
cnt_sing++;
if (cnt_sing)
{
outfile << "singular_face_inside" << endl << cnt_sing << endl;
for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
if ( GetFaceDescriptor ((*this)[sei].GetIndex()).domin_singular)
outfile << int(sei) << "\t" <<
GetFaceDescriptor ((*this)[sei].GetIndex()).domin_singular << endl;
}
cnt_sing = 0;
for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
if ( GetFaceDescriptor ((*this)[sei].GetIndex()).domout_singular) cnt_sing++;
if (cnt_sing)
{
outfile << "singular_face_outside" << endl << cnt_sing << endl;
for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
if ( GetFaceDescriptor ((*this)[sei].GetIndex()).domout_singular)
outfile << int(sei) << "\t"
<< GetFaceDescriptor ((*this)[sei].GetIndex()).domout_singular << endl;
}
// Philippose - 09/07/2009
// Add mesh face colours to Netgen Vol file format
// The colours are saved in RGB triplets
int cnt_facedesc = GetNFD();
if (cnt_facedesc)
{
outfile << endl << endl << "# Surfnr Red Green Blue" << endl;
outfile << "face_colours" << endl << cnt_facedesc << endl;
outfile.precision(8);
outfile.setf(ios::fixed, ios::floatfield);
outfile.setf(ios::showpoint);
for(i = 1; i <= cnt_facedesc; i++)
{
outfile.width(8);
outfile << GetFaceDescriptor(i).SurfNr()+1 << " ";
outfile.width(12);
outfile << GetFaceDescriptor(i).SurfColour()[0] << " ";
outfile.width(12);
outfile << GetFaceDescriptor(i).SurfColour()[1] << " ";
outfile.width(12);
outfile << GetFaceDescriptor(i).SurfColour()[2];
outfile << endl;
}
outfile << "face_transparencies" << endl << cnt_facedesc << endl;
for(i = 1; i <= cnt_facedesc; i++)
{
outfile.width(8);
outfile << GetFaceDescriptor(i).SurfNr()+1 << " ";
outfile.width(12);
outfile << GetFaceDescriptor(i).SurfColour()[3] << endl;
}
}
outfile << endl << endl << "endmesh" << endl << endl;
if (geometry)
geometry -> SaveToMeshFile (outfile);
}
void Mesh :: Load (const filesystem::path & filename)
{
PrintMessage (1, "filename = ", filename);
string ext0 = filename.stem().extension().string();
string ext = filename.extension().string();
if (ext0 == ".vol" && ext == ".bin")
{
BinaryInArchive in(filename);
in & (*this);
return;
}
istream * infile = NULL;
if (ext0 == ".vol" && ext == ".gz")
infile = new igzstream (filename);
else
infile = new ifstream (filename);
if (! (infile -> good()) )
throw NgException ("mesh file not found");
Load(*infile);
delete infile;
}
// Reads mandatory integer and optional string token from input stream
// used for parsing bcnames, cd2names etc.
void ReadNumberAndName( istream & infile, int & i, string & s )
{
string line;
std::istringstream iline;
bool empty_line = true;
while(empty_line && infile)
{
std::getline(infile, line);
iline = std::istringstream{line};
iline >> i;
if(iline)
empty_line = false;
iline >> s;
}
if(!infile)
throw Exception("Reached end of file while parsing");
}
void Mesh :: Load (istream & infile)
{
static Timer timer("Mesh::Load"); RegionTimer rt(timer);
if (! (infile.good()) )
{
cout << "cannot load mesh" << endl;
throw NgException ("mesh file not found");
}
int rank = GetCommunicator().Rank();
int ntasks = GetCommunicator().Size();
char str[100];
int i, n;
double scale = 1; // globflags.GetNumFlag ("scale", 1);
int inverttets = 0; // globflags.GetDefineFlag ("inverttets");
int invertsurf = 0; // globflags.GetDefineFlag ("invertsurfacemesh");
facedecoding.SetSize(0);
bool endmesh = false;
while (infile.good() && !endmesh)
{
infile >> str;
if (strcmp (str, "dimension") == 0)
{
infile >> dimension;
}
if (strcmp (str, "geomtype") == 0)
{
int hi;
infile >> hi;
geomtype = GEOM_TYPE(hi);
}
if (strcmp (str, "facedescriptors") == 0)
{
int nfd;
infile >> nfd;
for(auto i : Range(nfd))
{
int surfnr, domin, domout, tlosurf, bcprop;
infile >> surfnr >> domin >> domout >> tlosurf >> bcprop;
auto faceind = AddFaceDescriptor (FaceDescriptor(surfnr, domin, domout, tlosurf));
GetFaceDescriptor(faceind).SetBCProperty(bcprop);
}
}
if (strcmp (str, "surfaceelements") == 0 || strcmp (str, "surfaceelementsgi")==0 || strcmp (str, "surfaceelementsuv") == 0)
{
static Timer t1("read surface elements"); RegionTimer rt1(t1);
infile >> n;
PrintMessage (3, n, " surface elements");
bool geominfo = strcmp (str, "surfaceelementsgi") == 0;
bool uv = strcmp (str, "surfaceelementsuv") == 0;
for (i = 1; i <= n; i++)
{
int surfnr, bcp, domin, domout, nep, faceind = 0;
infile >> surfnr >> bcp >> domin >> domout;
surfnr--;
bool invert_el = false;
/*
if (domin == 0)
{
invert_el = true;
Swap (domin, domout);
}
*/
for (int j = 1; j <= facedecoding.Size(); j++)
if (GetFaceDescriptor(j).SurfNr() == surfnr &&
GetFaceDescriptor(j).BCProperty() == bcp &&
GetFaceDescriptor(j).DomainIn() == domin &&
GetFaceDescriptor(j).DomainOut() == domout)
faceind = j;
// if (facedecoding.Size()) faceind = 1; // for timing
if (!faceind)
{
faceind = AddFaceDescriptor (FaceDescriptor(surfnr, domin, domout, 0));
GetFaceDescriptor(faceind).SetBCProperty (bcp);
}
infile >> nep;
if (!nep) nep = 3;
Element2d tri(nep);
tri.SetIndex(faceind);
for (int j = 1; j <= nep; j++)
infile >> tri.PNum(j);
if (geominfo)
for (int j = 1; j <= nep; j++)
infile >> tri.GeomInfoPi(j).trignum;
if (uv)
for (int j = 1; j <= nep; j++)
infile >> tri.GeomInfoPi(j).u >> tri.GeomInfoPi(j).v;
if (invertsurf) tri.Invert();
if (invert_el) tri.Invert();
AddSurfaceElement (tri);
}
}
if (strcmp (str, "volumeelements") == 0)
{
static Timer t1("read volume elements"); RegionTimer rt1(t1);
infile >> n;
PrintMessage (3, n, " volume elements");
for (i = 1; i <= n; i++)
{
Element el(TET);
int hi, nep;
infile >> hi;
if (hi == 0) hi = 1;
el.SetIndex(hi);
infile >> nep;
el.SetNP(nep);
el.SetCurved (nep != 4);
for (int j = 0; j < nep; j++)
infile >> (int&)(el[j]);
if (inverttets)
el.Invert();
AddVolumeElement (el);
}
}
if (strcmp (str, "edgesegments") == 0)
{
static Timer t1("read edge segments"); RegionTimer rt1(t1);
infile >> n;
for (i = 1; i <= n; i++)
{
Segment seg;
int hi;
infile >> seg.si >> hi >> seg[0] >> seg[1];
AddSegment (seg);
}
}
if (strcmp (str, "edgesegmentsgi") == 0)
{
static Timer t1("read edge segmentsgi"); RegionTimer rt1(t1);
infile >> n;
for (i = 1; i <= n; i++)
{
Segment seg;
int hi;
infile >> seg.si >> hi >> seg[0] >> seg[1]
>> seg.geominfo[0].trignum
>> seg.geominfo[1].trignum;
AddSegment (seg);
}
}
if (strcmp (str, "edgesegmentsgi2") == 0)
{
static Timer t1("read edge segmentsgi2"); RegionTimer rt1(t1);
int a;
infile >> a;
n=a;
PrintMessage (3, n, " curve elements");
for (i = 1; i <= n; i++)
{
Segment seg;
int hi;
infile >> seg.si >> hi >> seg[0] >> seg[1]
>> seg.geominfo[0].trignum
>> seg.geominfo[1].trignum
>> seg.surfnr1 >> seg.surfnr2
>> seg.edgenr
>> seg.epgeominfo[0].dist
>> seg.epgeominfo[1].edgenr
>> seg.epgeominfo[1].dist;
seg.epgeominfo[0].edgenr = seg.epgeominfo[1].edgenr;
seg.domin = seg.surfnr1;
seg.domout = seg.surfnr2;
seg.surfnr1--;
seg.surfnr2--;
AddSegment (seg);
}
}
if (strcmp (str, "points") == 0)
{
static Timer t1("read points"); RegionTimer rt1(t1);
infile >> n;
PrintMessage (3, n, " points");
for (i = 1; i <= n; i++)
{
Point3d p;
infile >> p.X() >> p.Y() >> p.Z();
p.X() *= scale;
p.Y() *= scale;
p.Z() *= scale;
AddPoint (p);
}
PrintMessage (3, n, " points done");
}
if (strcmp (str, "pointelements") == 0)
{
static Timer t1("read point elements"); RegionTimer rt1(t1);
infile >> n;
PrintMessage (3, n, " pointelements");
for (i = 1; i <= n; i++)
{
Element0d el;
infile >> el.pnum >> el.index;
pointelements.Append (el);
}
PrintMessage (3, n, " pointelements done");
}
if (strcmp (str, "identifications") == 0)
{
infile >> n;
PrintMessage (3, n, " identifications");
for (i = 1; i <= n; i++)
{
PointIndex pi1, pi2;
int ind;
infile >> pi1 >> pi2 >> ind;
ident -> Add (pi1, pi2, ind);
}
}
if (strcmp (str, "identificationtypes") == 0)
{
infile >> n;
PrintMessage (3, n, " identificationtypes");
for (i = 1; i <= n; i++)
{
int type;
infile >> type;
ident -> SetType(i,Identifications::ID_TYPE(type));
}
}
if (strcmp (str, "materials") == 0)
{
infile >> n;
for ( auto i : Range(n) )
{
int nr;
string mat;
ReadNumberAndName( infile, nr, mat );
SetMaterial (nr, mat.c_str());
}
}
if ( strcmp (str, "bcnames" ) == 0 )
{
infile >> n;
Array<int> bcnrs(n);
SetNBCNames(n);
for ( auto i : Range(n) )
{
string nextbcname;
ReadNumberAndName( infile, bcnrs[i], nextbcname );
bcnames[bcnrs[i]-1] = new string(nextbcname);
}
if ( GetDimension() == 3 )
{
for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
{
if ((*this)[sei].GetIndex())
{
int bcp = GetFaceDescriptor((*this)[sei].GetIndex ()).BCProperty();
if ( bcp <= n )
GetFaceDescriptor((*this)[sei].GetIndex ()).SetBCName(bcnames[bcp-1]);
else
GetFaceDescriptor((*this)[sei].GetIndex ()).SetBCName(0);
}
}
}
}
if ( strcmp (str, "cd2names" ) == 0)
{
infile >> n;
Array<int> cd2nrs(n);
SetNCD2Names(n);
for ( auto i : Range(n) )
{
string nextcd2name;
ReadNumberAndName( infile, cd2nrs[i], nextcd2name );
cd2names[cd2nrs[i]-1] = new string(nextcd2name);
}
if (GetDimension() < 2)
{
throw NgException("co dim 2 elements not implemented for dimension < 2");
}
}
if ( strcmp (str, "cd3names" ) == 0)
{
infile >> n;
Array<int> cd3nrs(n);
SetNCD3Names(n);
for( auto i : Range(n) )
{
string nextcd3name;
ReadNumberAndName( infile, cd3nrs[i], nextcd3name );
cd3names[cd3nrs[i]-1] = new string(nextcd3name);
}
if (GetDimension() < 3)
{
throw NgException("co dim 3 elements not implemented for dimension < 3");
}
}
if (strcmp (str, "singular_points") == 0)
{
infile >> n;
for (i = 1; i <= n; i++)
{
PointIndex pi;
double s;
infile >> pi;
infile >> s;
(*this)[pi].Singularity (s);
}
}
if (strcmp (str, "singular_edge_left") == 0)
{
infile >> n;
for (i = 1; i <= n; i++)
{
SegmentIndex si;
double s;
infile >> si;
infile >> s;
(*this)[si].singedge_left = s;
}
}
if (strcmp (str, "singular_edge_right") == 0)
{
infile >> n;
for (i = 1; i <= n; i++)
{
SegmentIndex si;
double s;
infile >> si;
infile >> s;
(*this)[si].singedge_right = s;
}
}
if (strcmp (str, "singular_face_inside") == 0)
{
infile >> n;
for (i = 1; i <= n; i++)
{
SurfaceElementIndex sei;
double s;
infile >> sei;
infile >> s;
GetFaceDescriptor((*this)[sei].GetIndex()).domin_singular = s;
}
}
if (strcmp (str, "singular_face_outside") == 0)
{
infile >> n;
for (i = 1; i <= n; i++)
{
SurfaceElementIndex sei;
double s;
infile >> sei;
infile >> s;
GetFaceDescriptor((*this)[sei].GetIndex()).domout_singular = s;
}
}
// Philippose - 09/07/2009
// Add mesh face colours to Netgen Vol file format
// The colours are read in as RGB triplets
if (strcmp (str, "face_colours") == 0)
{
int cnt_facedesc = GetNFD();
infile >> n;
if(n == cnt_facedesc)
{
for(i = 1; i <= n; i++)
{
int surfnr = 0;
Vec<4> surfcolour(0.0,1.0,0.0,1.0);
infile >> surfnr
>> surfcolour[0]
>> surfcolour[1]
>> surfcolour[2];
surfnr--;
if(surfnr > 0)
{
for(int facedesc = 1; facedesc <= cnt_facedesc; facedesc++)
{
if(surfnr == GetFaceDescriptor(facedesc).SurfNr())
{
GetFaceDescriptor(facedesc).SetSurfColour(surfcolour);
}
}
}
}
}
}
if (strcmp (str, "face_transparencies") == 0)
{
int cnt_facedesc = GetNFD();
infile >> n;
int index = 1;
if(n == cnt_facedesc)
{
for(int index = 1; index <= n; index++)
{
int surfnr;
double transp;
infile >> surfnr >> transp;
surfnr--;
if(surfnr > 0)
{
for(int facedesc = 1; facedesc <= cnt_facedesc; facedesc++)
{
if(surfnr == GetFaceDescriptor(facedesc).SurfNr())
{
auto& fd = GetFaceDescriptor(facedesc);
auto scol = fd.SurfColour();
scol[3] = transp;
fd.SetSurfColour(scol);
}
}
}
}
}
}
if (strcmp (str, "endmesh") == 0)
endmesh = true;
strcpy (str, "");
}
CalcSurfacesOfNode ();
if (ntasks == 1) // sequential run only
{
topology.Update();
clusters -> Update();
}
GeometryRegisterArray& gra = FetchGeometryRegisterArray();
auto geo = gra.LoadFromMeshFile (infile);
if(geo)
geometry = geo;
SetNextMajorTimeStamp();
// PrintMemInfo (cout);
}
void Mesh :: DoArchive (Archive & archive)
{
static Timer t("Mesh::Archive"); RegionTimer r(t);
#ifdef PARALLEL
auto comm = GetCommunicator();
if (archive.IsParallel() && comm.Size() > 1)
{ // parallel pickling supported only for output archives
if (comm.Rank() == 0)
archive & dimension;
auto rank = comm.Rank();
auto & partop = GetParallelTopology();
// global enumration of points:
// not used now, but will be needed for refined meshes
// GridFunciton pickling is not compatible, now
// should go to paralleltopology
// merge points
Array<PointIndex, PointIndex> globnum(points.Size());
PointIndex maxglob = -1;
for (auto pi : Range(points))
{
globnum[pi] = partop.GetGlobalPNum(pi);
// globnum[pi] = global_pnums[pi];
maxglob = max(globnum[pi], maxglob);
}
maxglob = comm.AllReduce (maxglob, MPI_MAX);
int numglob = maxglob+1-PointIndex::BASE;
if (comm.Rank() > 0)
{
comm.Send (globnum, 0, 200);
comm.Send (points, 0, 200);
}
else
{
Array<PointIndex, PointIndex> globnumi;
Array<MeshPoint, PointIndex> pointsi;
Array<MeshPoint, PointIndex> globpoints(numglob);
for (int j = 1; j < comm.Size(); j++)
{
comm.Recv (globnumi, j, 200);
comm.Recv (pointsi, j, 200);
for (auto i : Range(globnumi))
globpoints[globnumi[i]] = pointsi[i];
}
archive & globpoints;
}
// sending surface elements
auto copy_el2d (surfelements);
for (auto & el : copy_el2d)
for (auto & pi : el.PNums())
pi = globnum[pi];
if (comm.Rank() > 0)
comm.Send(copy_el2d, 0, 200);
else
{
Array<Element2d, SurfaceElementIndex> el2di;
for (int j = 1; j < comm.Size(); j++)
{
comm.Recv(el2di, j, 200);
for (auto & el : el2di)
copy_el2d += el;
}
archive & copy_el2d;
}
// sending volume elements
auto copy_el3d (volelements);
for (auto & el : copy_el3d)
for (auto & pi : el.PNums())
pi = globnum[pi];
if (comm.Rank() > 0)
comm.Send(copy_el3d, 0, 200);
else
{
Array<Element, ElementIndex> el3di;
for (int j = 1; j < comm.Size(); j++)
{
comm.Recv(el3di, j, 200);
for (auto & el : el3di)
copy_el3d += el;
}
archive & copy_el3d;
}
// sending 1D elements
auto copy_el1d (segments);
for (auto & el : copy_el1d)
for (auto & pi : el.pnums)
if (pi != PointIndex(PointIndex::INVALID))
pi = globnum[pi];
if (comm.Rank() > 0)
comm.Send(copy_el1d, 0, 200);
else
{
Array<Segment, SegmentIndex> el1di;
for (int j = 1; j < comm.Size(); j++)
{
comm.Recv(el1di, j, 200);
for (auto & el : el1di)
copy_el1d += el;
}
archive & copy_el1d;
}
if (comm.Rank() == 0)
{
archive & facedecoding;
archive & materials & bcnames & cd2names & cd3names;
auto mynv = numglob;
archive & mynv; // numvertices;
archive & *ident;
if(archive.GetVersion("netgen") >= "v6.2.2103-1")
{
archive.NeedsVersion("netgen", "v6.2.2103-1");
archive & vol_partition & surf_partition & seg_partition;
}
archive.Shallow(geometry);
archive & *curvedelems;
}
if (comm.Rank() == 0)
return;
}
#endif
archive & dimension;
archive & points;
archive & surfelements;
archive & volelements;
archive & segments;
archive & facedecoding;
archive & materials & bcnames & cd2names & cd3names;
archive & numvertices;
archive & *ident;
// cout << "archive, ngsversion = " << archive.GetVersion("netgen") << endl;
if(archive.GetVersion("netgen") >= "v6.2.2103-1")
{
// cout << "do the partition" << endl;
archive.NeedsVersion("netgen", "v6.2.2103-1");
archive & vol_partition & surf_partition & seg_partition;
}
// else
// cout << "no partition" << endl;
archive.Shallow(geometry);
archive & *curvedelems;
if (archive.Input())
{
int rank = GetCommunicator().Rank();
int ntasks = GetCommunicator().Size();
RebuildSurfaceElementLists();
CalcSurfacesOfNode ();
if (ntasks == 1) // sequential run only
{
topology.Update();
clusters -> Update();
}
SetNextMajorTimeStamp();
}
}
void Mesh :: Merge (const filesystem::path & filename, const int surfindex_offset)
{
ifstream infile(filename);
if (!infile.good())
throw NgException ("mesh file not found");
Merge(infile,surfindex_offset);
}
void Mesh :: Merge (istream & infile, const int surfindex_offset)
{
char str[100];
int i, n;
int inverttets = 0; // globflags.GetDefineFlag ("inverttets");
int oldnp = GetNP();
int oldne = GetNSeg();
int oldnd = GetNDomains();
for(SurfaceElementIndex si = 0; si < GetNSE(); si++)
for(int j=1; j<=(*this)[si].GetNP(); j++) (*this)[si].GeomInfoPi(j).trignum = -1;
int max_surfnr = 0;
for (i = 1; i <= GetNFD(); i++)
max_surfnr = max2 (max_surfnr, GetFaceDescriptor(i).SurfNr());
max_surfnr++;
if(max_surfnr < surfindex_offset) max_surfnr = surfindex_offset;
bool endmesh = false;
while (infile.good() && !endmesh)
{
infile >> str;
if (strcmp (str, "surfaceelementsgi") == 0 || strcmp (str, "surfaceelements") == 0)
{
infile >> n;
PrintMessage (3, n, " surface elements");
for (i = 1; i <= n; i++)
{
int j;
int surfnr, bcp, domin, domout, nep, faceind = 0;
infile >> surfnr >> bcp >> domin >> domout;
surfnr--;
if(domin > 0) domin += oldnd;
if(domout > 0) domout += oldnd;
surfnr += max_surfnr;
for (j = 1; j <= facedecoding.Size(); j++)
if (GetFaceDescriptor(j).SurfNr() == surfnr &&
GetFaceDescriptor(j).BCProperty() == bcp &&
GetFaceDescriptor(j).DomainIn() == domin &&
GetFaceDescriptor(j).DomainOut() == domout)
faceind = j;
if (!faceind)
{
faceind = AddFaceDescriptor (FaceDescriptor(surfnr, domin, domout, 0));
if(GetDimension() == 2) bcp++;
GetFaceDescriptor(faceind).SetBCProperty (bcp);
}
infile >> nep;
if (!nep) nep = 3;
Element2d tri(nep);
tri.SetIndex(faceind);
for (j = 1; j <= nep; j++)
{
infile >> tri.PNum(j);
tri.PNum(j) = tri.PNum(j) + oldnp;
}
if (strcmp (str, "surfaceelementsgi") == 0)
for (j = 1; j <= nep; j++)
{
infile >> tri.GeomInfoPi(j).trignum;
tri.GeomInfoPi(j).trignum = -1;
}
AddSurfaceElement (tri);
}
}
if (strcmp (str, "edgesegments") == 0)
{
infile >> n;
for (i = 1; i <= n; i++)
{
Segment seg;
int hi;
infile >> seg.si >> hi >> seg[0] >> seg[1];
seg[0] = seg[0] + oldnp;
seg[1] = seg[1] + oldnp;
AddSegment (seg);
}
}
if (strcmp (str, "edgesegmentsgi") == 0)
{
infile >> n;
for (i = 1; i <= n; i++)
{
Segment seg;
int hi;
infile >> seg.si >> hi >> seg[0] >> seg[1]
>> seg.geominfo[0].trignum
>> seg.geominfo[1].trignum;
seg[0] = seg[0] + oldnp;
seg[1] = seg[1] + oldnp;
AddSegment (seg);
}
}
if (strcmp (str, "edgesegmentsgi2") == 0)
{
infile >> n;
PrintMessage (3, n, " curve elements");
for (i = 1; i <= n; i++)
{
Segment seg;
int hi;
infile >> seg.si >> hi >> seg[0] >> seg[1]
>> seg.geominfo[0].trignum
>> seg.geominfo[1].trignum
>> seg.surfnr1 >> seg.surfnr2
>> seg.edgenr
>> seg.epgeominfo[0].dist
>> seg.epgeominfo[1].edgenr
>> seg.epgeominfo[1].dist;
seg.epgeominfo[0].edgenr = seg.epgeominfo[1].edgenr;
seg.surfnr1--;
seg.surfnr2--;
if(seg.surfnr1 >= 0) seg.surfnr1 = seg.surfnr1 + max_surfnr;
if(seg.surfnr2 >= 0) seg.surfnr2 = seg.surfnr2 + max_surfnr;
seg[0] = seg[0] +oldnp;
seg[1] = seg[1] +oldnp;
*testout << "old edgenr: " << seg.edgenr << endl;
seg.edgenr = seg.edgenr + oldne;
*testout << "new edgenr: " << seg.edgenr << endl;
seg.epgeominfo[1].edgenr = seg.epgeominfo[1].edgenr + oldne;
AddSegment (seg);
}
}
if (strcmp (str, "volumeelements") == 0)
{
infile >> n;
PrintMessage (3, n, " volume elements");
for (i = 1; i <= n; i++)
{
Element el(TET);
int hi, nep;
infile >> hi;
if (hi == 0) hi = 1;
el.SetIndex(hi+oldnd);
infile >> nep;
el.SetNP(nep);
for (int j = 0; j < nep; j++)
{
infile >> (int&)(el[j]);
el[j] = el[j]+oldnp;
}
if (inverttets)
el.Invert();
AddVolumeElement (el);
}
}
if (strcmp (str, "points") == 0)
{
infile >> n;
PrintMessage (3, n, " points");
for (i = 1; i <= n; i++)
{
Point3d p;
infile >> p.X() >> p.Y() >> p.Z();
AddPoint (p);
}
}
if (strcmp (str, "endmesh") == 0)
{
endmesh = true;
}
if (strcmp (str, "materials") == 0)
{
infile >> n;
for (i = 1; i <= n; i++)
{
int nr;
string mat;
infile >> nr >> mat;
SetMaterial (nr+oldnd, mat.c_str());
}
}
strcpy (str, "");
}
CalcSurfacesOfNode ();
topology.Update();
clusters -> Update();
SetNextMajorTimeStamp();
}
bool Mesh :: TestOk () const
{
for (ElementIndex ei = 0; ei < volelements.Size(); ei++)
{
for (int j = 0; j < 4; j++)
if ( (*this)[ei][j] <= PointIndex::BASE-1)
{
(*testout) << "El " << ei << " has 0 nodes: ";
for (int k = 0; k < 4; k++)
(*testout) << (*this)[ei][k];
break;
}
}
CheckMesh3D (*this);
return 1;
}
void Mesh :: SetAllocSize(int nnodes, int nsegs, int nsel, int nel)
{
points.SetAllocSize(nnodes);
segments.SetAllocSize(nsegs);
surfelements.SetAllocSize(nsel);
volelements.SetAllocSize(nel);
}
void Mesh :: BuildBoundaryEdges(bool rebuild)
{
static Timer t("Mesh::BuildBoundaryEdges"); RegionTimer reg(t);
if(!rebuild && boundaryedges)
return;
boundaryedges = make_unique<INDEX_2_CLOSED_HASHTABLE<int>>
(3 * (GetNSE() + GetNOpenElements()) + GetNSeg() + 1);
for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
{
const Element2d & sel = surfelements[sei];
if (sel.IsDeleted()) continue;
// int si = sel.GetIndex();
if (sel.GetNP() <= 4)
for (int j = 0; j < sel.GetNP(); j++)
{
INDEX_2 i2;
i2.I1() = sel.PNumMod(j+1);
i2.I2() = sel.PNumMod(j+2);
i2.Sort();
boundaryedges->Set (i2, 1);
}
else if (sel.GetType()==TRIG6)
{
for (int j = 0; j < 3; j++)
{
INDEX_2 i2;
i2.I1() = sel[j];
i2.I2() = sel[(j+1)%3];
i2.Sort();
boundaryedges->Set (i2, 1);
}
}
else
cerr << "illegal element for buildboundaryedges" << endl;
}
for (int i = 0; i < openelements.Size(); i++)
{
const Element2d & sel = openelements[i];
for (int j = 0; j < sel.GetNP(); j++)
{
INDEX_2 i2;
i2.I1() = sel.PNumMod(j+1);
i2.I2() = sel.PNumMod(j+2);
i2.Sort();
boundaryedges->Set (i2, 1);
points[sel[j]].SetType(FIXEDPOINT);
}
}
for (int i = 0; i < GetNSeg(); i++)
{
const Segment & seg = segments[i];
INDEX_2 i2(seg[0], seg[1]);
i2.Sort();
boundaryedges -> Set (i2, 2);
//segmentht -> Set (i2, i);
}
}
void Mesh :: CalcSurfacesOfNode ()
{
static Timer t("Mesh::CalcSurfacesOfNode"); RegionTimer reg (t);
static Timer tn2se("Mesh::CalcSurfacesOfNode - surf on node");
static Timer tht("Mesh::CalcSurfacesOfNode - surfelementht");
// surfacesonnode.SetSize (GetNP());
TABLE<int,PointIndex::BASE> surfacesonnode(GetNP());
// delete boundaryedges;
// boundaryedges = NULL;
boundaryedges = nullptr;
// delete surfelementht;
// surfelementht = nullptr;
surfelementht = nullptr;
// delete segmentht;
/*
surfelementht = new INDEX_3_HASHTABLE<int> (GetNSE()/4 + 1);
segmentht = new INDEX_2_HASHTABLE<int> (GetNSeg() + 1);
*/
if (dimension == 3)
surfelementht = make_unique<INDEX_3_CLOSED_HASHTABLE<int>> (3*GetNSE() + 1);
segmentht = make_unique<INDEX_2_CLOSED_HASHTABLE<int>> (3*GetNSeg() + 1);
tn2se.Start();
if (dimension == 3)
/*
for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
{
const Element2d & sel = surfelements[sei];
*/
for (const Element2d & sel : surfelements)
{
if (sel.IsDeleted()) continue;
int si = sel.GetIndex();
/*
for (int j = 0; j < sel.GetNP(); j++)
{
PointIndex pi = sel[j];
*/
for (PointIndex pi : sel.PNums())
{
if (!surfacesonnode[pi].Contains(si))
surfacesonnode.Add (pi, si);
/*
bool found = 0;
for (int k = 0; k < surfacesonnode[pi].Size(); k++)
if (surfacesonnode[pi][k] == si)
{
found = 1;
break;
}
if (!found)
surfacesonnode.Add (pi, si);
*/
}
}
/*
for (sei = 0; sei < GetNSE(); sei++)
{
const Element2d & sel = surfelements[sei];
if (sel.IsDeleted()) continue;
INDEX_3 i3;
i3.I1() = sel.PNum(1);
i3.I2() = sel.PNum(2);
i3.I3() = sel.PNum(3);
i3.Sort();
surfelementht -> PrepareSet (i3);
}
surfelementht -> AllocateElements();
*/
tn2se.Stop();
tht.Start();
if (dimension==3)
for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
{
const Element2d & sel = surfelements[sei];
if (sel.IsDeleted()) continue;
INDEX_3 i3;
i3.I1() = sel.PNum(1);
i3.I2() = sel.PNum(2);
i3.I3() = sel.PNum(3);
i3.Sort();
surfelementht -> Set (i3, sei); // war das wichtig ??? sel.GetIndex());
}
tht.Stop();
// int np = GetNP();
if (dimension == 3)
{
static Timer t("Mesh::CalcSurfacesOfNode, pointloop"); RegionTimer reg (t);
/*
for (PointIndex pi = points.Begin(); pi < points.End(); pi++)
points[pi].SetType (INNERPOINT);
*/
for (auto & p : points)
p.SetType (INNERPOINT);
if (GetNFD() == 0)
{
for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
{
const Element2d & sel = surfelements[sei];
if (sel.IsDeleted()) continue;
for (int j = 0; j < sel.GetNP(); j++)
{
PointIndex pi = SurfaceElement(sei)[j];
points[pi].SetType(FIXEDPOINT);
}
}
}
else
{
for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
{
const Element2d & sel = surfelements[sei];
if (sel.IsDeleted()) continue;
for (int j = 0; j < sel.GetNP(); j++)
{
PointIndex pi = sel[j];
int ns = surfacesonnode[pi].Size();
if (ns == 1)
points[pi].SetType(SURFACEPOINT);
if (ns == 2)
points[pi].SetType(EDGEPOINT);
if (ns >= 3)
points[pi].SetType(FIXEDPOINT);
}
}
}
}
/*
for (int i = 0; i < segments.Size(); i++)
{
const Segment & seg = segments[i];
*/
for (const Segment & seg : segments)
{
for (int j = 1; j <= 2; j++)
{
PointIndex hi = (j == 1) ? seg[0] : seg[1];
if (points[hi].Type() == INNERPOINT ||
points[hi].Type() == SURFACEPOINT)
points[hi].SetType(EDGEPOINT);
}
}
for (int i = 0; i < lockedpoints.Size(); i++)
points[lockedpoints[i]].SetType(FIXEDPOINT);
for(const auto& pointel : pointelements)
points[pointel.pnum].SetType(FIXEDPOINT);
/*
for (i = 0; i < openelements.Size(); i++)
{
const Element2d & sel = openelements[i];
for (j = 0; j < sel.GetNP(); j++)
{
INDEX_2 i2;
i2.I1() = sel.PNumMod(j+1);
i2.I2() = sel.PNumMod(j+2);
i2.Sort();
boundaryedges->Set (i2, 1);
points[sel[j]].SetType(FIXEDPOINT);
}
}
*/
// eltyps.SetSize (GetNE());
// eltyps = FREEELEMENT;
for (int i = 0; i < GetNSeg(); i++)
{
const Segment & seg = segments[i];
INDEX_2 i2(seg[0], seg[1]);
i2.Sort();
//boundaryedges -> Set (i2, 2);
segmentht -> Set (i2, i);
}
}
// NgBitArray base is PointIndex::BASE ...
void Mesh :: FixPoints (const NgBitArray & fixpoints)
{
if (fixpoints.Size() != GetNP())
{
cerr << "Mesh::FixPoints: sizes don't fit" << endl;
return;
}
int np = GetNP();
/*
for (int i = 1; i <= np; i++)
if (fixpoints.Test(i))
{
points.Elem(i).SetType (FIXEDPOINT);
}
*/
for (PointIndex pi : points.Range())
if (fixpoints.Test(pi))
points[pi].SetType(FIXEDPOINT);
}
void Mesh :: FindOpenElements (int dom)
{
static Timer t("Mesh::FindOpenElements"); RegionTimer reg (t);
static Timer t_table("Mesh::FindOpenElements - build table");
static Timer t_pointloop("Mesh::FindOpenElements - pointloop");
int np = GetNP();
int ne = GetNE();
int nse = GetNSE();
t_table.Start();
auto elsonpoint = ngcore::CreateSortedTable<ElementIndex, PointIndex>( volelements.Range(),
[&](auto & table, ElementIndex ei)
{
const Element & el = (*this)[ei];
if (dom == 0 || dom == el.GetIndex())
{
if (el.GetNP() == 4)
{
INDEX_4 i4(el[0], el[1], el[2], el[3]);
i4.Sort();
table.Add (PointIndex(i4.I1()), ei);
table.Add (PointIndex(i4.I2()), ei);
}
else
{
for (PointIndex pi : el.PNums())
table.Add(pi, ei);
}
}
}, GetNP());
NgArray<int,PointIndex::BASE> numonpoint(np);
/*
numonpoint = 0;
for (ElementIndex ei = 0; ei < ne; ei++)
{
const Element & el = (*this)[ei];
if (dom == 0 || dom == el.GetIndex())
{
if (el.GetNP() == 4)
{
INDEX_4 i4(el[0], el[1], el[2], el[3]);
i4.Sort();
numonpoint[i4.I1()]++;
numonpoint[i4.I2()]++;
}
else
for (int j = 0; j < el.GetNP(); j++)
numonpoint[el[j]]++;
}
}
TABLE<ElementIndex,PointIndex::BASE> elsonpoint(numonpoint);
for (ElementIndex ei = 0; ei < ne; ei++)
{
const Element & el = (*this)[ei];
if (dom == 0 || dom == el.GetIndex())
{
if (el.GetNP() == 4)
{
INDEX_4 i4(el[0], el[1], el[2], el[3]);
i4.Sort();
elsonpoint.Add (i4.I1(), ei);
elsonpoint.Add (i4.I2(), ei);
}
else
for (int j = 0; j < el.GetNP(); j++)
elsonpoint.Add (el[j], ei);
}
}
*/
t_table.Stop();
NgArray<bool, 1> hasface(GetNFD());
for (int i = 1; i <= GetNFD(); i++)
{
int domin = GetFaceDescriptor(i).DomainIn();
int domout = GetFaceDescriptor(i).DomainOut();
hasface[i] =
( dom == 0 && (domin != 0 || domout != 0) ) ||
( dom != 0 && (domin == dom || domout == dom) );
}
numonpoint = 0;
for (SurfaceElementIndex sii = 0; sii < nse; sii++)
{
int ind = surfelements[sii].GetIndex();
/*
if (
GetFaceDescriptor(ind).DomainIn() &&
(dom == 0 || dom == GetFaceDescriptor(ind).DomainIn())
||
GetFaceDescriptor(ind).DomainOut() &&
(dom == 0 || dom == GetFaceDescriptor(ind).DomainOut())
)
*/
if (hasface[ind])
{
/*
Element2d hel = surfelements[i];
hel.NormalizeNumbering();
numonpoint[hel[0]]++;
*/
const Element2d & hel = surfelements[sii];
int mini = 0;
for (int j = 1; j < hel.GetNP(); j++)
if (hel[j] < hel[mini])
mini = j;
numonpoint[hel[mini]]++;
}
}
TABLE<SurfaceElementIndex,PointIndex::BASE> selsonpoint(numonpoint);
for (SurfaceElementIndex sii = 0; sii < nse; sii++)
{
int ind = surfelements[sii].GetIndex();
/*
if (
GetFaceDescriptor(ind).DomainIn() &&
(dom == 0 || dom == GetFaceDescriptor(ind).DomainIn())
||
GetFaceDescriptor(ind).DomainOut() &&
(dom == 0 || dom == GetFaceDescriptor(ind).DomainOut())
)
*/
if (hasface[ind])
{
/*
Element2d hel = surfelements[i];
hel.NormalizeNumbering();
selsonpoint.Add (hel[0], i);
*/
const Element2d & hel = surfelements[sii];
int mini = 0;
for (int j = 1; j < hel.GetNP(); j++)
if (hel[j] < hel[mini])
mini = j;
selsonpoint.Add (hel[mini], sii);
}
}
// PointIndex pi;
// SurfaceElementIndex sei;
// Element2d hel;
struct tval { int index; PointIndex p4; };
openelements.SetSize(0);
t_pointloop.Start();
/*
INDEX_3_CLOSED_HASHTABLE<tval> faceht(100);
for (PointIndex pi : points.Range())
if (selsonpoint[pi].Size()+elsonpoint[pi].Size())
{
faceht.SetSize (2 * selsonpoint[pi].Size() + 4 * elsonpoint[pi].Size());
for (SurfaceElementIndex sei : selsonpoint[pi])
{
Element2d hel = SurfaceElement(sei);
if (hel.GetType() == TRIG6) hel.SetType(TRIG);
int ind = hel.GetIndex();
if (GetFaceDescriptor(ind).DomainIn() &&
(dom == 0 || dom == GetFaceDescriptor(ind).DomainIn()) )
{
hel.NormalizeNumbering();
if (hel.PNum(1) == pi)
{
INDEX_3 i3(hel[0], hel[1], hel[2]);
tval i2;
i2.index = GetFaceDescriptor(ind).DomainIn();
i2.p4 = (hel.GetNP() == 3)
? PointIndex (PointIndex::INVALID)
: hel.PNum(4);
faceht.Set (i3, i2);
}
}
if (GetFaceDescriptor(ind).DomainOut() &&
(dom == 0 || dom == GetFaceDescriptor(ind).DomainOut()) )
{
hel.Invert();
hel.NormalizeNumbering();
if (hel.PNum(1) == pi)
{
INDEX_3 i3(hel[0], hel[1], hel[2]);
tval i2;
i2.index = GetFaceDescriptor(ind).DomainOut();
i2.p4 = (hel.GetNP() == 3)
? PointIndex (PointIndex::INVALID)
: hel.PNum(4);
faceht.Set (i3, i2);
}
}
}
for (ElementIndex ei : elsonpoint[pi])
{
const Element & el = VolumeElement(ei);
if (dom == 0 || el.GetIndex() == dom)
{
for (int j = 1; j <= el.GetNFaces(); j++)
{
Element2d hel(TRIG);
el.GetFace (j, hel);
hel.Invert();
hel.NormalizeNumbering();
if (hel[0] == pi)
{
INDEX_3 i3(hel[0], hel[1], hel[2]);
if (faceht.Used (i3))
{
tval i2 = faceht.Get(i3);
if (i2.index == el.GetIndex())
{
i2.index = PointIndex::BASE-1;
faceht.Set (i3, i2);
}
else
{
if (i2.index == 0)
{
PrintSysError ("more elements on face");
(*testout) << "more elements on face!!!" << endl;
(*testout) << "el = " << el << endl;
(*testout) << "hel = " << hel << endl;
(*testout) << "face = " << i3 << endl;
(*testout) << "points = " << endl;
for (int jj = 1; jj <= 3; jj++)
(*testout) << "p = " << Point(i3.I(jj)) << endl;
}
}
}
else
{
hel.Invert();
hel.NormalizeNumbering();
INDEX_3 i3(hel[0], hel[1], hel[2]);
tval i2;
i2.index = el.GetIndex();
i2.p4 = (hel.GetNP() == 3)
? PointIndex (PointIndex::INVALID)
: hel[3];
faceht.Set (i3, i2);
}
}
}
}
}
for (int i = 0; i < faceht.Size(); i++)
if (faceht.UsedPos (i))
{
INDEX_3 i3;
//INDEX_2 i2;
tval i2;
faceht.GetData (i, i3, i2);
if (i2.index != PointIndex::BASE-1)
{
Element2d tri ( (i2.p4 == PointIndex::BASE-1) ? TRIG : QUAD);
for (int l = 0; l < 3; l++)
tri[l] = i3.I(l+1);
tri.PNum(4) = i2.p4;
tri.SetIndex (i2.index);
openelements.Append (tri);
}
}
}
*/
size_t numtasks = 4*ngcore::TaskManager::GetNumThreads();
Array<Array<Element2d>> thread_openelements(numtasks);
ParallelJob
( [&](TaskInfo & ti)
{
auto myrange = points.Range().Split(ti.task_nr, ti.ntasks);
INDEX_3_CLOSED_HASHTABLE<tval> faceht(100);
for (PointIndex pi : myrange)
if (selsonpoint[pi].Size()+elsonpoint[pi].Size())
{
faceht.SetSize (2 * selsonpoint[pi].Size() + 4 * elsonpoint[pi].Size());
for (SurfaceElementIndex sei : selsonpoint[pi])
{
Element2d hel = SurfaceElement(sei);
if (hel.GetType() == TRIG6) hel.SetType(TRIG);
int ind = hel.GetIndex();
if (GetFaceDescriptor(ind).DomainIn() &&
(dom == 0 || dom == GetFaceDescriptor(ind).DomainIn()) )
{
hel.NormalizeNumbering();
if (hel.PNum(1) == pi)
{
INDEX_3 i3(hel[0], hel[1], hel[2]);
tval i2;
i2.index = GetFaceDescriptor(ind).DomainIn();
i2.p4 = (hel.GetNP() == 3)
? PointIndex (PointIndex::INVALID)
: hel.PNum(4);
faceht.Set (i3, i2);
}
}
if (GetFaceDescriptor(ind).DomainOut() &&
(dom == 0 || dom == GetFaceDescriptor(ind).DomainOut()) )
{
hel.Invert();
hel.NormalizeNumbering();
if (hel.PNum(1) == pi)
{
INDEX_3 i3(hel[0], hel[1], hel[2]);
tval i2;
i2.index = GetFaceDescriptor(ind).DomainOut();
i2.p4 = (hel.GetNP() == 3)
? PointIndex (PointIndex::INVALID)
: hel.PNum(4);
faceht.Set (i3, i2);
}
}
}
for (ElementIndex ei : elsonpoint[pi])
{
const Element & el = VolumeElement(ei);
if (dom == 0 || el.GetIndex() == dom)
{
for (int j = 1; j <= el.GetNFaces(); j++)
{
Element2d hel(TRIG);
el.GetFace (j, hel);
hel.Invert();
hel.NormalizeNumbering();
if (hel[0] == pi)
{
INDEX_3 i3(hel[0], hel[1], hel[2]);
if (faceht.Used (i3))
{
tval i2 = faceht.Get(i3);
if (i2.index == el.GetIndex())
{
i2.index = PointIndex::BASE-1;
faceht.Set (i3, i2);
}
else
{
if (i2.index == 0)
{
PrintSysError ("more elements on face");
(*testout) << "more elements on face!!!" << endl;
(*testout) << "el = " << el << endl;
(*testout) << "hel = " << hel << endl;
(*testout) << "face = " << i3 << endl;
(*testout) << "points = " << endl;
for (int jj = 1; jj <= 3; jj++)
(*testout) << "p = " << Point(i3.I(jj)) << endl;
}
}
}
else
{
hel.Invert();
hel.NormalizeNumbering();
INDEX_3 i3(hel[0], hel[1], hel[2]);
tval i2;
i2.index = el.GetIndex();
i2.p4 = (hel.GetNP() == 3)
? PointIndex (PointIndex::INVALID)
: hel[3];
faceht.Set (i3, i2);
}
}
}
}
}
for (int i = 0; i < faceht.Size(); i++)
if (faceht.UsedPos (i))
{
INDEX_3 i3;
tval i2;
faceht.GetData (i, i3, i2);
if (i2.index != PointIndex::BASE-1)
{
Element2d tri ( (i2.p4 == PointIndex::BASE-1) ? TRIG : QUAD);
for (int l = 0; l < 3; l++)
tri[l] = i3.I(l+1);
tri.PNum(4) = i2.p4;
tri.SetIndex (i2.index);
thread_openelements[ti.task_nr].Append (tri);
}
}
}}, numtasks);
for (auto & a : thread_openelements)
for (auto & el : a)
openelements.Append (el);
t_pointloop.Stop();
int cnt3 = 0;
for (int i = 0; i < openelements.Size(); i++)
if (openelements[i].GetNP() == 3)
cnt3++;
int cnt4 = openelements.Size() - cnt3;
MyStr treequad;
if (cnt4)
treequad = MyStr(" (") + MyStr(cnt3) + MyStr (" + ") +
MyStr(cnt4) + MyStr(")");
PrintMessage (5, openelements.Size(), treequad, " open elements");
BuildBoundaryEdges();
for (int i = 1; i <= openelements.Size(); i++)
{
const Element2d & sel = openelements.Get(i);
if (boundaryedges)
for (int j = 1; j <= sel.GetNP(); j++)
{
INDEX_2 i2;
i2.I1() = sel.PNumMod(j);
i2.I2() = sel.PNumMod(j+1);
i2.Sort();
boundaryedges->Set (i2, 1);
}
for (int j = 1; j <= 3; j++)
{
PointIndex pi = sel.PNum(j);
// if (pi < points.End())
if (pi < *points.Range().end())
points[pi].SetType (FIXEDPOINT);
}
}
/*
for (i = 1; i <= GetNSeg(); i++)
{
const Segment & seg = LineSegment(i);
INDEX_2 i2(seg[0], seg[1]);
i2.Sort();
if (!boundaryedges->Used (i2))
cerr << "WARNING: no boundedge, but seg edge: " << i2 << endl;
boundaryedges -> Set (i2, 2);
segmentht -> Set (i2, i-1);
}
*/
}
bool Mesh :: HasOpenQuads () const
{
int no = GetNOpenElements();
for (int i = 0; i < no; i++)
if (openelements[i].GetNP() == 4)
return true;
return false;
}
void Mesh :: FindOpenSegments (int surfnr)
{
// int i, j, k;
// new version, general elements
// hash index: pnum1-2, surfnr
// hash data : surfel-nr (pos) or segment nr(neg)
INDEX_3_HASHTABLE<int> faceht(4 * GetNSE()+GetNSeg()+1);
PrintMessage (5, "Test Opensegments");
for (int i = 1; i <= GetNSeg(); i++)
{
const Segment & seg = LineSegment (i);
if (surfnr == 0 || seg.si == surfnr)
{
INDEX_3 key(seg[0], seg[1], seg.si);
int data = -i;
if (faceht.Used (key))
{
cerr << "ERROR: Segment " << seg << " already used" << endl;
(*testout) << "ERROR: Segment " << seg << " already used" << endl;
}
faceht.Set (key, data);
}
}
/*
// not possible with surfnr as hash-index
for (int i = 1; i <= GetNSeg(); i++)
{
const Segment & seg = LineSegment (i);
if (surfnr == 0 || seg.si == surfnr)
{
INDEX_2 key(seg[1], seg[0]);
if (!faceht.Used(key))
{
cerr << "ERROR: Segment " << seg << " brother not used" << endl;
(*testout) << "ERROR: Segment " << seg << " brother not used" << endl;
}
}
}
*/
// bool buggy = false;
// ofstream bout("buggy.out");
for (int i = 1; i <= GetNSE(); i++)
{
const Element2d & el = SurfaceElement(i);
if (el.IsDeleted()) continue;
if (surfnr == 0 || el.GetIndex() == surfnr)
{
for (int j = 1; j <= el.GetNP(); j++)
{
INDEX_3 seg (el.PNumMod(j), el.PNumMod(j+1), el.GetIndex());
int data;
if (seg.I1() < PointIndex::BASE || seg.I2() < PointIndex::BASE)
cerr << "seg = " << seg << endl;
if (faceht.Used(seg))
{
faceht.Set (seg, 0);
/*
data = faceht.Get(seg);
if (data.I1() == el.GetIndex())
{
data.I1() = 0;
faceht.Set (seg, data);
}
else
{
// buggy = true;
PrintWarning ("hash table si not fitting for segment: ",
seg.I1(), "-", seg.I2(), " other = ",
data.I2(), ", surfnr = ", surfnr);
}
*/
}
else
{
Swap (seg.I1(), seg.I2());
// data.I1() = el.GetIndex();
// data.I2() = i;
faceht.Set (seg, i);
}
}
}
}
/*
if (buggy)
{
for (int i = 1; i <= GetNSeg(); i++)
bout << "seg" << i << " " << LineSegment(i) << endl;
for (int i = 1; i <= GetNSE(); i++)
bout << "sel" << i << " " << SurfaceElement(i) << " ind = "
<< SurfaceElement(i).GetIndex() << endl;
bout << "hashtable: " << endl;
for (int j = 1; j <= faceht.GetNBags(); j++)
{
bout << "bag " << j << ":" << endl;
for (int k = 1; k <= faceht.GetBagSize(j); k++)
{
INDEX_2 i2, data;
faceht.GetData (j, k, i2, data);
bout << "key = " << i2 << ", data = " << data << endl;
}
}
exit(1);
}
*/
(*testout) << "open segments: " << endl;
opensegments.SetSize(0);
for (int i = 1; i <= faceht.GetNBags(); i++)
for (int j = 1; j <= faceht.GetBagSize(i); j++)
{
INDEX_3 i2;
int data;
faceht.GetData (i, j, i2, data);
if (data) // surfnr
{
Segment seg;
seg[0] = i2.I1();
seg[1] = i2.I2();
seg.si = i2.I3();
// find geomdata:
if (data > 0)
{
// segment due to triangle
const Element2d & el = SurfaceElement (data);
for (int k = 1; k <= el.GetNP(); k++)
{
if (seg[0] == el.PNum(k))
seg.geominfo[0] = el.GeomInfoPi(k);
if (seg[1] == el.PNum(k))
seg.geominfo[1] = el.GeomInfoPi(k);
}
(*testout) << "trig seg: ";
}
else
{
// segment due to line
const Segment & lseg = LineSegment (-data);
seg.geominfo[0] = lseg.geominfo[0];
seg.geominfo[1] = lseg.geominfo[1];
(*testout) << "line seg: ";
}
(*testout) << seg[0] << " - " << seg[1]
<< " len = " << Dist (Point(seg[0]), Point(seg[1]))
<< endl;
opensegments.Append (seg);
if (seg.geominfo[0].trignum <= 0 || seg.geominfo[1].trignum <= 0)
{
(*testout) << "Problem with open segment: " << seg << endl;
}
}
}
PrintMessage (3, opensegments.Size(), " open segments found");
(*testout) << opensegments.Size() << " open segments found" << endl;
/*
ptyps.SetSize (GetNP());
for (i = 1; i <= ptyps.Size(); i++)
ptyps.Elem(i) = SURFACEPOINT;
for (i = 1; i <= GetNSeg(); i++)
{
const Segment & seg = LineSegment (i);
ptyps.Elem(seg[0]) = EDGEPOINT;
ptyps.Elem(seg[1]) = EDGEPOINT;
}
for (i = 1; i <= GetNOpenSegments(); i++)
{
const Segment & seg = GetOpenSegment (i);
ptyps.Elem(seg[0]) = EDGEPOINT;
ptyps.Elem(seg[1]) = EDGEPOINT;
}
*/
/*
for (int i = 1; i <= points.Size(); i++)
points.Elem(i).SetType(SURFACEPOINT);
*/
for (auto & p : points)
p.SetType (SURFACEPOINT);
for (int i = 1; i <= GetNSeg(); i++)
{
const Segment & seg = LineSegment (i);
points[seg[0]].SetType(EDGEPOINT);
points[seg[1]].SetType(EDGEPOINT);
}
for (int i = 1; i <= GetNOpenSegments(); i++)
{
const Segment & seg = GetOpenSegment (i);
points[seg[0]].SetType (EDGEPOINT);
points[seg[1]].SetType (EDGEPOINT);
}
/*
for (i = 1; i <= openelements.Size(); i++)
{
const Element2d & sel = openelements.Get(i);
if (boundaryedges)
for (j = 1; j <= sel.GetNP(); j++)
{
INDEX_2 i2;
i2.I1() = sel.PNumMod(j);
i2.I2() = sel.PNumMod(j+1);
i2.Sort();
boundaryedges->Set (i2, 1);
}
for (j = 1; j <= 3; j++)
{
int pi = sel.PNum(j);
if (pi <= ptyps.Size())
ptyps.Elem(pi) = FIXEDPOINT;
}
}
*/
}
void Mesh :: RemoveOneLayerSurfaceElements ()
{
int np = GetNP();
FindOpenSegments();
NgBitArray frontpoints(np+1); // for 0- and 1-based
frontpoints.Clear();
for (int i = 1; i <= GetNOpenSegments(); i++)
{
const Segment & seg = GetOpenSegment(i);
frontpoints.Set (seg[0]);
frontpoints.Set (seg[1]);
}
for (int i = 1; i <= GetNSE(); i++)
{
Element2d & sel = surfelements[i-1];
bool remove = false;
for (int j = 1; j <= sel.GetNP(); j++)
if (frontpoints.Test(sel.PNum(j)))
remove = true;
if (remove)
sel.PNum(1).Invalidate();
}
for (int i = surfelements.Size(); i >= 1; i--)
{
if (!surfelements[i-1].PNum(1).IsValid())
{
surfelements[i-1] = surfelements.Last();
surfelements.DeleteLast();
}
}
RebuildSurfaceElementLists ();
/*
for (int i = 0; i < facedecoding.Size(); i++)
facedecoding[i].firstelement = -1;
for (int i = surfelements.Size()-1; i >= 0; i--)
{
int ind = surfelements[i].GetIndex();
surfelements[i].next = facedecoding[ind-1].firstelement;
facedecoding[ind-1].firstelement = i;
}
*/
timestamp = NextTimeStamp();
// Compress();
}
void Mesh :: FreeOpenElementsEnvironment (int layers)
{
static Timer timer("FreeOpenElementsEnvironment"); RegionTimer rt(timer);
int i, j, k;
PointIndex pi;
const int large = 9999;
NgArray<int,PointIndex::BASE> dist(GetNP());
dist = large;
for (int i = 1; i <= GetNOpenElements(); i++)
{
const Element2d & face = OpenElement(i);
for (j = 0; j < face.GetNP(); j++)
dist[face[j]] = 1;
}
for (k = 1; k <= layers; k++)
for (i = 1; i <= GetNE(); i++)
{
const Element & el = VolumeElement(i);
if (el[0] == -1 || el.IsDeleted()) continue;
int elmin = large;
for (j = 0; j < el.GetNP(); j++)
if (dist[el[j]] < elmin)
elmin = dist[el[j]];
if (elmin < large)
{
for (j = 0; j < el.GetNP(); j++)
if (dist[el[j]] > elmin+1)
dist[el[j]] = elmin+1;
}
}
int cntfree = 0;
for (i = 1; i <= GetNE(); i++)
{
Element & el = VolumeElement(i);
if (el[0] == -1 || el.IsDeleted()) continue;
int elmin = large;
for (j = 0; j < el.GetNP(); j++)
if (dist[el[j]] < elmin)
elmin = dist[el[j]];
el.flags.fixed = elmin > layers;
// eltyps.Elem(i) = (elmin <= layers) ?
// FREEELEMENT : FIXEDELEMENT;
if (elmin <= layers)
cntfree++;
}
PrintMessage (5, "free: ", cntfree, ", fixed: ", GetNE()-cntfree);
(*testout) << "free: " << cntfree << ", fixed: " << GetNE()-cntfree << endl;
for (pi = PointIndex::BASE;
pi < GetNP()+PointIndex::BASE; pi++)
{
if (dist[pi] > layers+1)
points[pi].SetType(FIXEDPOINT);
}
}
void Mesh :: SetLocalH (netgen::Point<3> pmin, netgen::Point<3> pmax, double grading, int layer)
{
using netgen::Point;
Point<3> c = Center (pmin, pmax);
double d = max3 (pmax(0)-pmin(0),
pmax(1)-pmin(1),
pmax(2)-pmin(2));
d /= 2;
Point<3> pmin2 = c - Vec<3> (d, d, d);
Point<3> pmax2 = c + Vec<3> (d, d, d);
SetLocalH(make_unique<LocalH> (pmin2, pmax2, grading, dimension), layer);
}
void Mesh :: RestrictLocalH (const Point3d & p, double hloc, int layer)
{
if(hloc < hmin)
hloc = hmin;
//cout << "restrict h in " << p << " to " << hloc << endl;
if (!lochfunc[layer-1])
{
PrintWarning("RestrictLocalH called, creating mesh-size tree");
Point3d boxmin, boxmax;
GetBox (boxmin, boxmax);
SetLocalH (boxmin, boxmax, 0.8, layer);
}
lochfunc[layer-1] -> SetH (p, hloc);
}
void Mesh :: RestrictLocalHLine (const Point3d & p1,
const Point3d & p2,
double hloc, int layer)
{
if(hloc < hmin)
hloc = hmin;
// cout << "restrict h along " << p1 << " - " << p2 << " to " << hloc << endl;
int i;
int steps = int (Dist (p1, p2) / hloc) + 2;
Vec3d v(p1, p2);
for (i = 0; i <= steps; i++)
{
Point3d p = p1 + (double(i)/double(steps) * v);
RestrictLocalH (p, hloc, layer);
}
}
void Mesh :: SetMinimalH (double h)
{
hmin = h;
}
void Mesh :: SetGlobalH (double h)
{
hglob = h;
}
double Mesh :: MaxHDomain (int dom) const
{
if (maxhdomain.Size())
return maxhdomain.Get(dom);
else
return 1e10;
}
void Mesh :: SetMaxHDomain (const NgArray<double> & mhd)
{
maxhdomain.SetSize(mhd.Size());
for (int i = 1; i <= mhd.Size(); i++)
maxhdomain.Elem(i) = mhd.Get(i);
}
double Mesh :: GetH (const Point3d & p, int layer) const
{
const auto& lh = GetLocalH(layer);
double hmin = hglob;
if (lh)
{
double hl = lh->GetH (p);
if (hl < hglob)
hmin = hl;
}
return hmin;
}
double Mesh :: GetMinH (const Point3d & pmin, const Point3d & pmax, int layer)
{
const auto& lh = GetLocalH(layer);
double hmin = hglob;
if (lh)
{
double hl = lh->GetMinH (pmin, pmax);
if (hl < hmin)
hmin = hl;
}
return hmin;
}
double Mesh :: AverageH (int surfnr) const
{
int i, j, n;
double hi, hsum;
double maxh = 0, minh = 1e10;
hsum = 0;
n = 0;
for (i = 1; i <= GetNSE(); i++)
{
const Element2d & el = SurfaceElement(i);
if (surfnr == 0 || el.GetIndex() == surfnr)
{
for (j = 1; j <= 3; j++)
{
hi = Dist (Point (el.PNumMod(j)),
Point (el.PNumMod(j+1)));
hsum += hi;
if (hi > maxh) maxh = hi;
if (hi < minh) minh = hi;
n++;
}
}
}
PrintMessage (5, "minh = ", minh, " avh = ", (hsum/n), " maxh = ", maxh);
return (hsum / n);
}
void Mesh :: CalcLocalH (double grading, int layer)
{
static Timer t("Mesh::CalcLocalH"); RegionTimer reg(t);
if (!lochfunc[layer-1])
{
Point3d pmin, pmax;
GetBox (pmin, pmax);
// SetLocalH (pmin, pmax, mparam.grading);
SetLocalH (pmin, pmax, grading, layer);
}
PrintMessage (3,
"CalcLocalH: ",
GetNP(), " Points ",
GetNE(), " Elements ",
GetNSE(), " Surface Elements");
for (int i = 0; i < GetNSE(); i++)
{
const Element2d & el = surfelements[i];
int j;
if (el.GetNP() == 3)
{
double hel = -1;
for (j = 1; j <= 3; j++)
{
const Point3d & p1 = points[el.PNumMod(j)];
const Point3d & p2 = points[el.PNumMod(j+1)];
/*
INDEX_2 i21(el.PNumMod(j), el.PNumMod(j+1));
INDEX_2 i22(el.PNumMod(j+1), el.PNumMod(j));
if (! identifiedpoints->Used (i21) &&
! identifiedpoints->Used (i22) )
*/
if (!ident -> UsedSymmetric (el.PNumMod(j),
el.PNumMod(j+1)))
{
double hedge = Dist (p1, p2);
if (hedge > hel)
hel = hedge;
// lochfunc->SetH (Center (p1, p2), 2 * Dist (p1, p2));
// (*testout) << "trigseth, p1,2 = " << el.PNumMod(j) << ", " << el.PNumMod(j+1)
// << " h = " << (2 * Dist(p1, p2)) << endl;
}
}
if (hel > 0)
{
const Point3d & p1 = points[el.PNum(1)];
const Point3d & p2 = points[el.PNum(2)];
const Point3d & p3 = points[el.PNum(3)];
lochfunc[layer-1]->SetH (Center (p1, p2, p3), hel);
}
}
else
{
{
const Point3d & p1 = points[el.PNum(1)];
const Point3d & p2 = points[el.PNum(2)];
lochfunc[layer-1]->SetH (Center (p1, p2), 2 * Dist (p1, p2));
}
{
const Point3d & p1 = points[el.PNum(3)];
const Point3d & p2 = points[el.PNum(4)];
lochfunc[layer-1]->SetH (Center (p1, p2), 2 * Dist (p1, p2));
}
}
}
for (int i = 0; i < GetNSeg(); i++)
{
const Segment & seg = segments[i];
const Point3d & p1 = points[seg[0]];
const Point3d & p2 = points[seg[1]];
/*
INDEX_2 i21(seg[0], seg[1]);
INDEX_2 i22(seg[1], seg[0]);
if (identifiedpoints)
if (!identifiedpoints->Used (i21) && !identifiedpoints->Used (i22))
*/
if (!ident -> UsedSymmetric (seg[0], seg[1]))
{
lochfunc[layer-1]->SetH (Center (p1, p2), Dist (p1, p2));
}
}
/*
cerr << "do vol" << endl;
for (i = 1; i <= GetNE(); i++)
{
const Element & el = VolumeElement(i);
if (el.GetType() == TET)
{
int j, k;
for (j = 2; j <= 4; j++)
for (k = 1; k < j; k++)
{
const Point3d & p1 = Point (el.PNum(j));
const Point3d & p2 = Point (el.PNum(k));
lochfunc->SetH (Center (p1, p2), 2 * Dist (p1, p2));
(*testout) << "set vol h to " << (2 * Dist (p1, p2)) << endl;
}
}
}
*/
/*
const char * meshsizefilename =
globflags.GetStringFlag ("meshsize", NULL);
if (meshsizefilename)
{
ifstream msf(meshsizefilename);
if (msf)
{
int nmsp;
msf >> nmsp;
for (i = 1; i <= nmsp; i++)
{
Point3d pi;
double hi;
msf >> pi.X() >> pi.Y() >> pi.Z();
msf >> hi;
lochfunc->SetH (pi, hi);
}
}
}
*/
// lochfunc -> Convexify();
// lochfunc -> PrintMemInfo (cout);
}
void Mesh :: CalcLocalHFromPointDistances(double grading, int layer)
{
PrintMessage (3, "Calculating local h from point distances");
if (!lochfunc[layer-1])
{
Point3d pmin, pmax;
GetBox (pmin, pmax);
// SetLocalH (pmin, pmax, mparam.grading);
SetLocalH (pmin, pmax, grading, layer);
}
PointIndex i,j;
double hl;
for (i = PointIndex::BASE;
i < GetNP()+PointIndex::BASE; i++)
{
for(j=i+1; j<GetNP()+PointIndex::BASE; j++)
{
const Point3d & p1 = points[i];
const Point3d & p2 = points[j];
hl = Dist(p1,p2);
RestrictLocalH(p1,hl);
RestrictLocalH(p2,hl);
//cout << "restricted h at " << p1 << " and " << p2 << " to " << hl << endl;
}
}
}
void Mesh :: CalcLocalHFromSurfaceCurvature (double grading, double elperr, int layer)
{
PrintMessage (3, "Calculating local h from surface curvature");
if (!lochfunc[layer-1])
{
Point3d pmin, pmax;
GetBox (pmin, pmax);
// SetLocalH (pmin, pmax, mparam.grading);
SetLocalH (pmin, pmax, grading, layer);
}
INDEX_2_HASHTABLE<int> edges(3 * GetNP() + 2);
INDEX_2_HASHTABLE<int> bedges(GetNSeg() + 2);
int i, j;
for (i = 1; i <= GetNSeg(); i++)
{
const Segment & seg = LineSegment(i);
INDEX_2 i2(seg[0], seg[1]);
i2.Sort();
bedges.Set (i2, 1);
}
for (i = 1; i <= GetNSE(); i++)
{
const Element2d & sel = SurfaceElement(i);
if (!sel.PNum(1))
continue;
for (j = 1; j <= 3; j++)
{
INDEX_2 i2(sel.PNumMod(j), sel.PNumMod(j+1));
i2.Sort();
if (bedges.Used(i2)) continue;
if (edges.Used(i2))
{
int other = edges.Get(i2);
const Element2d & elother = SurfaceElement(other);
int pi3 = 1;
while ( (sel.PNum(pi3) == i2.I1()) ||
(sel.PNum(pi3) == i2.I2()))
pi3++;
pi3 = sel.PNum(pi3);
int pi4 = 1;
while ( (elother.PNum(pi4) == i2.I1()) ||
(elother.PNum(pi4) == i2.I2()))
pi4++;
pi4 = elother.PNum(pi4);
double rad = ComputeCylinderRadius (Point (PointIndex(i2.I1())),
Point (PointIndex(i2.I2())),
Point (PointIndex(pi3)),
Point (PointIndex(pi4)));
RestrictLocalHLine (Point(PointIndex(i2.I1())), Point(PointIndex(i2.I2())), rad/elperr);
/*
(*testout) << "pi1,2, 3, 4 = " << i2.I1() << ", " << i2.I2() << ", " << pi3 << ", " << pi4
<< " p1 = " << Point(i2.I1())
<< ", p2 = " << Point(i2.I2())
// << ", p3 = " << Point(pi3)
// << ", p4 = " << Point(pi4)
<< ", rad = " << rad << endl;
*/
}
else
edges.Set (i2, i);
}
}
// Restrict h due to line segments
for (i = 1; i <= GetNSeg(); i++)
{
const Segment & seg = LineSegment(i);
const Point3d & p1 = Point(seg[0]);
const Point3d & p2 = Point(seg[1]);
RestrictLocalH (Center (p1, p2), Dist (p1, p2));
}
/*
int i, j;
int np = GetNP();
int nseg = GetNSeg();
int nse = GetNSE();
NgArray<Vec3d> normals(np);
NgBitArray linepoint(np);
linepoint.Clear();
for (i = 1; i <= nseg; i++)
{
linepoint.Set (LineSegment(i)[0]);
linepoint.Set (LineSegment(i)[1]);
}
for (i = 1; i <= np; i++)
normals.Elem(i) = Vec3d(0,0,0);
for (i = 1; i <= nse; i++)
{
Element2d & el = SurfaceElement(i);
Vec3d nf = Cross (Vec3d (Point (el.PNum(1)), Point(el.PNum(2))),
Vec3d (Point (el.PNum(1)), Point(el.PNum(3))));
for (j = 1; j <= 3; j++)
normals.Elem(el.PNum(j)) += nf;
}
for (i = 1; i <= np; i++)
normals.Elem(i) /= (1e-12 + normals.Elem(i).Length());
for (i = 1; i <= nse; i++)
{
Element2d & el = SurfaceElement(i);
Vec3d nf = Cross (Vec3d (Point (el.PNum(1)), Point(el.PNum(2))),
Vec3d (Point (el.PNum(1)), Point(el.PNum(3))));
nf /= nf.Length();
Point3d c = Center (Point(el.PNum(1)),
Point(el.PNum(2)),
Point(el.PNum(3)));
for (j = 1; j <= 3; j++)
{
if (!linepoint.Test (el.PNum(j)))
{
double dist = Dist (c, Point(el.PNum(j)));
double dn = (nf - normals.Get(el.PNum(j))).Length();
RestrictLocalH (Point(el.PNum(j)), dist / (dn+1e-12) /elperr);
}
}
}
*/
}
void Mesh :: RestrictLocalH (resthtype rht, int nr, double loch)
{
int i;
switch (rht)
{
case RESTRICTH_FACE:
{
for (i = 1; i <= GetNSE(); i++)
{
const Element2d & sel = SurfaceElement(i);
if (sel.GetIndex() == nr)
RestrictLocalH (RESTRICTH_SURFACEELEMENT, i, loch);
}
break;
}
case RESTRICTH_EDGE:
{
for (i = 1; i <= GetNSeg(); i++)
{
const Segment & seg = LineSegment(i);
if (seg.edgenr == nr)
RestrictLocalH (RESTRICTH_SEGMENT, i, loch);
}
break;
}
case RESTRICTH_POINT:
{
RestrictLocalH (Point (nr), loch);
break;
}
case RESTRICTH_SURFACEELEMENT:
{
const Element2d & sel = SurfaceElement(nr);
Point3d p = Center (Point(sel.PNum(1)),
Point(sel.PNum(2)),
Point(sel.PNum(3)));
RestrictLocalH (p, loch);
break;
}
case RESTRICTH_SEGMENT:
{
const Segment & seg = LineSegment(nr);
RestrictLocalHLine (Point (seg[0]), Point(seg[1]), loch);
break;
}
}
}
void Mesh :: LoadLocalMeshSize (const filesystem::path & meshsizefilename)
{
// Philippose - 10/03/2009
// Improve error checking when loading and reading
// the local mesh size file
if (meshsizefilename.empty()) return;
ifstream msf(meshsizefilename);
// Philippose - 09/03/2009
// Adding print message information in case the specified
// does not exist, or does not load successfully due to
// other reasons such as access rights, etc...
if (!msf)
{
PrintMessage(3, "Error loading mesh size file: ", meshsizefilename, "....","Skipping!");
return;
}
PrintMessage (3, "Load local mesh-size file: ", meshsizefilename);
int nmsp = 0;
int nmsl = 0;
msf >> nmsp;
if(!msf.good())
throw NgException ("Mesh-size file error: No points found\n");
if(nmsp > 0)
PrintMessage (4, "Number of mesh-size restriction points: ", nmsp);
for (int i = 0; i < nmsp; i++)
{
Point3d pi;
double hi;
msf >> pi.X() >> pi.Y() >> pi.Z();
msf >> hi;
if (!msf.good())
throw NgException ("Mesh-size file error: Number of points don't match specified list size\n");
RestrictLocalH (pi, hi);
}
msf >> nmsl;
if(!msf.good())
throw NgException ("Mesh-size file error: No line definitions found\n");
if(nmsl > 0)
PrintMessage (4, "Number of mesh-size restriction lines: ", nmsl);
for (int i = 0; i < nmsl; i++)
{
Point3d p1, p2;
double hi;
msf >> p1.X() >> p1.Y() >> p1.Z();
msf >> p2.X() >> p2.Y() >> p2.Z();
msf >> hi;
if (!msf.good())
throw NgException ("Mesh-size file error: Number of line definitions don't match specified list size\n");
RestrictLocalHLine (p1, p2, hi);
}
msf.close();
}
void Mesh :: SetLocalH(shared_ptr<LocalH> loch, int layer)
{
if(layer>lochfunc.Size())
{
auto pre_size = lochfunc.Size();
lochfunc.SetSize(layer);
for(auto & func : lochfunc.Range(pre_size, layer-1))
func = lochfunc[0];
}
lochfunc[layer-1] = loch;
}
void Mesh :: GetBox (Point3d & pmin, Point3d & pmax, int dom) const
{
if (points.Size() == 0)
{
pmin = pmax = Point3d(0,0,0);
return;
}
if (dom <= 0)
{
pmin = Point3d (1e10, 1e10, 1e10);
pmax = Point3d (-1e10, -1e10, -1e10);
// for (PointIndex pi = points.Begin(); pi < points.End(); pi++)
for (PointIndex pi : points.Range())
{
pmin.SetToMin ( (*this) [pi] );
pmax.SetToMax ( (*this) [pi] );
}
}
else
{
int j, nse = GetNSE();
SurfaceElementIndex sei;
pmin = Point3d (1e10, 1e10, 1e10);
pmax = Point3d (-1e10, -1e10, -1e10);
for (sei = 0; sei < nse; sei++)
{
const Element2d & el = (*this)[sei];
if (el.IsDeleted() ) continue;
if (dom == -1 || el.GetIndex() == dom)
{
for (j = 0; j < 3; j++)
{
pmin.SetToMin ( (*this) [el[j]] );
pmax.SetToMax ( (*this) [el[j]] );
}
}
}
}
if (pmin.X() > 0.5e10)
{
pmin = pmax = Point3d(0,0,0);
}
}
void Mesh :: GetBox (Point3d & pmin, Point3d & pmax, POINTTYPE ptyp) const
{
if (points.Size() == 0)
{
pmin = pmax = Point3d(0,0,0);
return;
}
pmin = Point3d (1e10, 1e10, 1e10);
pmax = Point3d (-1e10, -1e10, -1e10);
// for (PointIndex pi = points.Begin(); pi < points.End(); pi++)
for (PointIndex pi : points.Range())
if (points[pi].Type() <= ptyp)
{
pmin.SetToMin ( (*this) [pi] );
pmax.SetToMax ( (*this) [pi] );
}
}
double Mesh :: ElementError (int eli, const MeshingParameters & mp) const
{
const Element & el = volelements[eli-1];
return CalcTetBadness (points[el[0]], points[el[1]],
points[el[2]], points[el[3]], -1, mp);
}
void Mesh :: AddLockedPoint (PointIndex pi)
{
lockedpoints.Append (pi);
}
void Mesh :: ClearLockedPoints ()
{
lockedpoints.SetSize (0);
}
void Mesh :: Compress ()
{
static Timer t("Mesh::Compress"); RegionTimer reg(t);
NgLock lock(mutex);
lock.Lock();
Array<PointIndex,PointIndex> op2np(GetNP());
Array<bool, PointIndex> pused(GetNP());
/*
(*testout) << "volels: " << endl;
for (i = 1; i <= volelements.Size(); i++)
{
for (j = 1; j <= volelements.Get(i).GetNP(); j++)
(*testout) << volelements.Get(i).PNum(j) << " ";
(*testout) << endl;
}
(*testout) << "np: " << GetNP() << endl;
*/
for (int i = 0; i < volelements.Size(); i++)
if (volelements[i][0] <= PointIndex::BASE-1 ||
volelements[i].IsDeleted())
{
volelements.DeleteElement(i);
i--;
}
for (int i = 0; i < surfelements.Size(); i++)
if (surfelements[i].IsDeleted())
{
surfelements.DeleteElement(i);
i--;
}
for (int i = 0; i < segments.Size(); i++)
if (segments[i][0] <= PointIndex::BASE-1)
{
segments.DeleteElement(i);
i--;
}
for(int i=0; i < segments.Size(); i++)
if(segments[i].edgenr < 0)
segments.DeleteElement(i--);
pused = false;
/*
for (int i = 0; i < volelements.Size(); i++)
{
const Element & el = volelements[i];
for (int j = 0; j < el.GetNP(); j++)
pused[el[j]] = true;
}
*/
/*
for (const Element & el : volelements)
for (PointIndex pi : el.PNums())
pused[pi] = true;
*/
ParallelForRange
(volelements.Range(), [&] (auto myrange)
{
for (const Element & el : volelements.Range(myrange))
for (PointIndex pi : el.PNums())
pused[pi] = true;
});
/*
for (int i = 0; i < surfelements.Size(); i++)
{
const Element2d & el = surfelements[i];
for (int j = 0; j < el.GetNP(); j++)
pused[el[j]] = true;
}
*/
ParallelForRange
(surfelements.Range(), [&] (auto myrange)
{
for (const Element2d & el : surfelements.Range(myrange))
for (PointIndex pi : el.PNums())
pused[pi] = true;
});
for (int i = 0; i < segments.Size(); i++)
{
const Segment & seg = segments[i];
for (int j = 0; j < seg.GetNP(); j++)
pused[seg[j]] = true;
}
for (int i = 0; i < openelements.Size(); i++)
{
const Element2d & el = openelements[i];
for (int j = 0; j < el.GetNP(); j++)
pused[el[j]] = true;
}
for (int i = 0; i < lockedpoints.Size(); i++)
pused[lockedpoints[i]] = true;
/*
// compress points doesn't work for identified points !
if (identifiedpoints)
{
for (i = 1; i <= identifiedpoints->GetNBags(); i++)
if (identifiedpoints->GetBagSize(i))
{
pused.Set ();
break;
}
}
*/
// pused.Set();
{
Array<MeshPoint> hpoints;
int npi = PointIndex::BASE;
for (PointIndex pi : points.Range())
if (pused[pi])
{
op2np[pi] = npi;
npi++;
hpoints.Append (points[pi]);
}
else
{
op2np[pi].Invalidate();
}
points.SetSize(0);
for (int i = 0; i < hpoints.Size(); i++)
points.Append (hpoints[i]);
}
/*
for (int i = 1; i <= volelements.Size(); i++)
{
Element & el = VolumeElement(i);
for (int j = 0; j < el.GetNP(); j++)
el[j] = op2np[el[j]];
}
*/
ParallelForRange
(volelements.Range(), [&] (auto myrange)
{
for (Element & el : volelements.Range(myrange))
for (PointIndex & pi : el.PNums())
pi = op2np[pi];
});
/*
for (int i = 1; i <= surfelements.Size(); i++)
{
Element2d & el = SurfaceElement(i);
for (int j = 0; j < el.GetNP(); j++)
el[j] = op2np[el[j]];
}
*/
ParallelForRange
(surfelements.Range(), [&] (auto myrange)
{
for (Element2d & el : surfelements.Range(myrange))
for (PointIndex & pi : el.PNums())
pi = op2np[pi];
});
for (int i = 0; i < segments.Size(); i++)
{
Segment & seg = segments[i];
for (int j = 0; j < seg.GetNP(); j++)
seg[j] = op2np[seg[j]];
}
for (int i = 1; i <= openelements.Size(); i++)
{
Element2d & el = openelements.Elem(i);
for (int j = 0; j < el.GetNP(); j++)
el[j] = op2np[el[j]];
}
for (int i = 0; i < lockedpoints.Size(); i++)
lockedpoints[i] = op2np[lockedpoints[i]];
/*
for (int i = 0; i < facedecoding.Size(); i++)
facedecoding[i].firstelement = -1;
for (int i = surfelements.Size()-1; i >= 0; i--)
{
int ind = surfelements[i].GetIndex();
surfelements[i].next = facedecoding[ind-1].firstelement;
facedecoding[ind-1].firstelement = i;
}
*/
RebuildSurfaceElementLists ();
CalcSurfacesOfNode();
// FindOpenElements();
timestamp = NextTimeStamp();
lock.UnLock();
}
void Mesh :: OrderElements()
{
for (auto & el : surfelements)
{
if (el.GetType() == TRIG)
while (el[0] > el[1] || el[0] > el[2])
{ // rotate element
auto hp = el[0];
el[0] = el[1];
el[1] = el[2];
el[2] = hp;
auto hgi = el.GeomInfoPi(1);
el.GeomInfoPi(1) = el.GeomInfoPi(2);
el.GeomInfoPi(2) = el.GeomInfoPi(3);
el.GeomInfoPi(3) = hgi;
}
}
for (auto & el : volelements)
if (el.GetType() == TET)
{
// lowest index first ...
int mini = 0;
for (int i = 1; i < 4; i++)
if (el[i] < el[mini]) mini = i;
if (mini != 0)
{ // swap 0 with mini, and the other two ...
int i3 = -1, i4 = -1;
for (int i = 1; i < 4; i++)
if (i != mini)
{
i4 = i3;
i3 = i;
}
swap (el[0], el[mini]);
swap (el[i3], el[i4]);
}
while (el[1] > el[2] || el[1] > el[3])
{ // rotate element to move second index to second position
auto hp = el[1];
el[1] = el[2];
el[2] = el[3];
el[3] = hp;
}
}
}
int Mesh :: CheckConsistentBoundary () const
{
int nf = GetNOpenElements();
INDEX_2_HASHTABLE<int> edges(nf+2);
INDEX_2 i2, i2s, edge;
int err = 0;
for (int i = 1; i <= nf; i++)
{
const Element2d & sel = OpenElement(i);
for (int j = 1; j <= sel.GetNP(); j++)
{
i2.I1() = sel.PNumMod(j);
i2.I2() = sel.PNumMod(j+1);
int sign = (i2.I2() > i2.I1()) ? 1 : -1;
i2.Sort();
if (!edges.Used (i2))
edges.Set (i2, 0);
edges.Set (i2, edges.Get(i2) + sign);
}
}
for (int i = 1; i <= edges.GetNBags(); i++)
for (int j = 1; j <= edges.GetBagSize(i); j++)
{
int cnt = 0;
edges.GetData (i, j, i2, cnt);
if (cnt)
{
PrintError ("Edge ", i2.I1() , " - ", i2.I2(), " multiple times in surface mesh");
(*testout) << "Edge " << i2 << " multiple times in surface mesh" << endl;
i2s = i2;
i2s.Sort();
for (int k = 1; k <= nf; k++)
{
const Element2d & sel = OpenElement(k);
for (int l = 1; l <= sel.GetNP(); l++)
{
edge.I1() = sel.PNumMod(l);
edge.I2() = sel.PNumMod(l+1);
edge.Sort();
if (edge == i2s)
(*testout) << "edge of element " << sel << endl;
}
}
err = 2;
}
}
return err;
}
int Mesh :: CheckOverlappingBoundary ()
{
static Timer t("Mesh::CheckOverlappingBoundary"); RegionTimer reg(t);
Point3d pmin, pmax;
GetBox (pmin, pmax);
BoxTree<3, SurfaceElementIndex> setree(pmin, pmax);
// NgArray<SurfaceElementIndex> inters;
bool overlap = 0;
bool incons_layers = 0;
for (Element2d & el : SurfaceElements())
el.badel = false;
for (SurfaceElementIndex sei : Range(SurfaceElements()))
{
const Element2d & tri = SurfaceElement(sei);
Box<3> box(Box<3>::EMPTY_BOX);
for (PointIndex pi : tri.PNums())
box.Add (Point(pi));
box.Increase(1e-3*box.Diam());
setree.Insert (box, sei);
}
std::mutex m;
// for (SurfaceElementIndex sei : Range(SurfaceElements()))
ParallelForRange
(Range(SurfaceElements()), [&] (auto myrange)
{
for (SurfaceElementIndex sei : myrange)
{
const Element2d & tri = SurfaceElement(sei);
Box<3> box(Box<3>::EMPTY_BOX);
for (PointIndex pi : tri.PNums())
box.Add (Point(pi));
setree.GetFirstIntersecting
(box.PMin(), box.PMax(),
[&] (SurfaceElementIndex sej)
{
const Element2d & tri2 = SurfaceElement(sej);
if ( (*this)[tri[0]].GetLayer() != (*this)[tri2[0]].GetLayer())
return false;
if ( (*this)[tri[0]].GetLayer() != (*this)[tri[1]].GetLayer() ||
(*this)[tri[0]].GetLayer() != (*this)[tri[2]].GetLayer())
{
incons_layers = 1;
// cout << "inconsistent layers in triangle" << endl;
}
const netgen::Point<3> *trip1[3], *trip2[3];
for (int k = 0; k < 3; k++)
{
trip1[k] = &Point (tri[k]);
trip2[k] = &Point (tri2[k]);
}
if (IntersectTriangleTriangle (&trip1[0], &trip2[0]))
{
overlap = 1;
lock_guard<std::mutex> guard(m);
if(!incons_layers)
{
PrintWarning ("Intersecting elements "
,int(sei), " and ", int(sej));
(*testout) << "Intersecting: " << endl;
(*testout) << "openelement " << sei << " with open element " << sej << endl;
cout << "el1 = " << tri << endl;
cout << "el2 = " << tri2 << endl;
cout << "layer1 = " << (*this)[tri[0]].GetLayer() << endl;
cout << "layer2 = " << (*this)[tri2[0]].GetLayer() << endl;
}
for (int k = 1; k <= 3; k++)
(*testout) << tri.PNum(k) << " ";
(*testout) << endl;
for (int k = 1; k <= 3; k++)
(*testout) << tri2.PNum(k) << " ";
(*testout) << endl;
for (int k = 0; k <= 2; k++)
(*testout) << *trip1[k] << " ";
(*testout) << endl;
for (int k = 0; k <= 2; k++)
(*testout) << *trip2[k] << " ";
(*testout) << endl;
(*testout) << "Face1 = " << GetFaceDescriptor(tri.GetIndex()) << endl;
(*testout) << "Face1 = " << GetFaceDescriptor(tri2.GetIndex()) << endl;
SurfaceElement(sei).badel = 1;
SurfaceElement(sej).badel = 1;
}
return false;
});
}
});
// bug 'fix'
if (incons_layers) overlap = 0;
return overlap;
}
int Mesh :: CheckVolumeMesh () const
{
PrintMessage (3, "Checking volume mesh");
int ne = GetNE();
DenseMatrix dtrans(3,3);
int i, j;
PrintMessage (5, "elements: ", ne);
for (i = 1; i <= ne; i++)
{
Element & el = (Element&) VolumeElement(i);
el.flags.badel = 0;
int nip = el.GetNIP();
for (j = 1; j <= nip; j++)
{
el.GetTransformation (j, Points(), dtrans);
double det = dtrans.Det();
if (det > 0)
{
PrintError ("Element ", i , " has wrong orientation");
el.flags.badel = 1;
}
}
}
return 0;
}
// Search for surface trigs with same vertices ( may happen for instance with close surfaces in stl geometies )
int Mesh :: FindIllegalTrigs ()
{
// Temporary table to store the vertex numbers of all triangles
INDEX_3_CLOSED_HASHTABLE<int> temp_tab(3*GetNSE() + 1);
size_t cnt = 0;
for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
{
const Element2d & sel = surfelements[sei];
if (sel.IsDeleted()) continue;
INDEX_3 i3(sel[0], sel[1], sel[2]);
i3.Sort();
if(temp_tab.Used(i3))
{
temp_tab.Set (i3, -1);
cnt++;
}
else
{
temp_tab.Set (i3, sei);
}
}
illegal_trigs = make_unique<INDEX_3_CLOSED_HASHTABLE<int>> (2*cnt+1);
for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
{
const Element2d & sel = surfelements[sei];
if (sel.IsDeleted()) continue;
INDEX_3 i3(sel[0], sel[1], sel[2]);
i3.Sort();
if(temp_tab.Get(i3)==-1)
illegal_trigs -> Set (i3, 1);
}
return cnt;
}
bool Mesh :: LegalTrig (const Element2d & el) const
{
if(illegal_trigs)
{
INDEX_3 i3 (el[0], el[1], el[2]);
i3.Sort();
if(illegal_trigs->Used(i3))
return false;
}
return 1;
if ( /* hp */ 1) // needed for old, simple hp-refinement
{
// trigs with 2 or more segments are illegal
int i;
int nseg = 0;
if (!segmentht)
{
cerr << "no segmentht allocated" << endl;
return 0;
}
// Point3d cp(0.5, 0.5, 0.5);
for (i = 1; i <= 3; i++)
{
INDEX_2 i2(el.PNumMod (i), el.PNumMod (i+1));
i2.Sort();
if (segmentht -> Used (i2))
nseg++;
}
if (nseg >= 2)
return 0;
}
return 1;
}
double Mesh :: CalcTotalBad (const MeshingParameters & mp )
{
static Timer t("CalcTotalBad"); RegionTimer reg(t);
static constexpr int n_classes = 20;
double sum = 0;
tets_in_qualclass.SetSize(n_classes);
tets_in_qualclass = 0;
ParallelForRange( IntRange(volelements.Size()), [&] (auto myrange)
{
double local_sum = 0.0;
double teterrpow = mp.opterrpow;
std::array<int,n_classes> classes_local{};
for (auto i : myrange)
{
double elbad = pow (max2(CalcBad (points, volelements[i], 0, mp),1e-10), 1/teterrpow);
int qualclass = int (n_classes / elbad + 1);
if (qualclass < 1) qualclass = 1;
if (qualclass > n_classes) qualclass = n_classes;
classes_local[qualclass-1]++;
local_sum += elbad;
}
AtomicAdd(sum, local_sum);
for (auto i : Range(n_classes))
AsAtomic(tets_in_qualclass[i]) += classes_local[i];
});
return sum;
}
///
bool Mesh :: LegalTet2 (Element & el) const
{
// static int timer1 = NgProfiler::CreateTimer ("Legaltet2");
// Test, whether 4 points have a common surface plus
// at least 4 edges at the boundary
if(!boundaryedges)
const_cast<Mesh *>(this)->BuildBoundaryEdges();
// non-tets are always legal
if (el.GetType() != TET)
{
el.SetLegal (1);
return 1;
}
POINTTYPE pointtype[4];
for(int i = 0; i < 4; i++)
pointtype[i] = (*this)[el[i]].Type();
// element has at least 2 inner points ---> legal
int cnti = 0;
for (int j = 0; j < 4; j++)
if ( pointtype[j] == INNERPOINT)
{
cnti++;
if (cnti >= 2)
{
el.SetLegal (1);
return 1;
}
}
// which faces are boundary faces ?
int bface[4];
for (int i = 0; i < 4; i++)
{
bface[i] = surfelementht->Used (INDEX_3::Sort(el[gftetfacesa[i][0]],
el[gftetfacesa[i][1]],
el[gftetfacesa[i][2]]));
}
int bedge[4][4];
int segedge[4][4];
static const int pi3map[4][4] = { { -1, 2, 1, 1 },
{ 2, -1, 0, 0 },
{ 1, 0, -1, 0 },
{ 1, 0, 0, -1 } };
static const int pi4map[4][4] = { { -1, 3, 3, 2 },
{ 3, -1, 3, 2 },
{ 3, 3, -1, 1 },
{ 2, 2, 1, -1 } };
for (int i = 0; i < 4; i++)
for (int j = 0; j < i; j++)
{
bool sege = false, be = false;
int pos = boundaryedges -> Position0(INDEX_2::Sort(el[i], el[j]));
if (pos != -1)
{
be = true;
if (boundaryedges -> GetData0(pos) == 2)
sege = true;
}
segedge[j][i] = segedge[i][j] = sege;
bedge[j][i] = bedge[i][j] = be;
}
// two boundary faces and no edge is illegal
for (int i = 0; i < 3; i++)
for (int j = i+1; j < 4; j++)
{
if (bface[i] && bface[j])
if (!segedge[pi3map[i][j]][pi4map[i][j]])
{
// 2 boundary faces without edge in between
el.SetLegal (0);
return 0;
}
}
// three boundary edges meeting in a Surface point
for (int i = 0; i < 4; i++)
{
if ( pointtype[i] == SURFACEPOINT)
{
bool alledges = 1;
for (int j = 0; j < 4; j++)
if (j != i && !bedge[i][j])
{
alledges = 0;
break;
}
if (alledges)
{
// cout << "tet illegal due to unmarked node" << endl;
el.SetLegal (0);
return 0;
}
}
}
for (int fnr = 0; fnr < 4; fnr++)
if (!bface[fnr])
for (int i = 0; i < 4; i++)
if (i != fnr)
{
int pi1 = pi3map[i][fnr];
int pi2 = pi4map[i][fnr];
if ( pointtype[i] == SURFACEPOINT)
{
// two connected edges on surface, but no face
if (bedge[i][pi1] && bedge[i][pi2])
{
el.SetLegal (0);
return 0;
}
}
if ( pointtype[i] == EDGEPOINT)
{
// connected surface edge and edge edge, but no face
if ( (bedge[i][pi1] && segedge[i][pi2]) ||
(bedge[i][pi2] && segedge[i][pi1]) )
{
el.SetLegal (0);
return 0;
}
}
}
el.SetLegal (1);
return 1;
}
int Mesh :: GetNDomains() const
{
int ndom = 0;
for (int k = 0; k < facedecoding.Size(); k++)
{
if (facedecoding[k].DomainIn() > ndom)
ndom = facedecoding[k].DomainIn();
if (facedecoding[k].DomainOut() > ndom)
ndom = facedecoding[k].DomainOut();
}
return ndom;
}
void Mesh :: SetDimension (int dim)
{
if (dimension == 3 && dim == 2)
{
// change mesh-dim from 3 to 2 (currently needed for OCC)
for (auto str : materials)
delete str;
materials.SetSize(0);
for (auto str : bcnames)
materials.Append(str);
bcnames.SetSize(0);
for (auto str : cd2names)
bcnames.Append(str);
cd2names.SetSize(0);
for (auto str : cd3names)
cd2names.Append(str);
cd3names.SetSize(0);
for (auto & seg : LineSegments())
seg.si = seg.edgenr;
}
dimension = dim;
}
void Mesh :: SurfaceMeshOrientation ()
{
int i, j;
int nse = GetNSE();
NgBitArray used(nse);
used.Clear();
INDEX_2_HASHTABLE<int> edges(nse+1);
bool haschanged = 0;
const Element2d & tri = SurfaceElement(1);
for (j = 1; j <= 3; j++)
{
INDEX_2 i2(tri.PNumMod(j), tri.PNumMod(j+1));
edges.Set (i2, 1);
}
used.Set(1);
bool unused;
do
{
bool changed;
do
{
changed = 0;
for (i = 1; i <= nse; i++)
if (!used.Test(i))
{
Element2d & el = surfelements[i-1];
int found = 0, foundrev = 0;
for (j = 1; j <= 3; j++)
{
INDEX_2 i2(el.PNumMod(j), el.PNumMod(j+1));
if (edges.Used(i2))
foundrev = 1;
swap (i2.I1(), i2.I2());
if (edges.Used(i2))
found = 1;
}
if (found || foundrev)
{
if (foundrev)
swap (el.PNum(2), el.PNum(3));
changed = 1;
for (j = 1; j <= 3; j++)
{
INDEX_2 i2(el.PNumMod(j), el.PNumMod(j+1));
edges.Set (i2, 1);
}
used.Set (i);
}
}
if (changed)
haschanged = 1;
}
while (changed);
unused = 0;
for (i = 1; i <= nse; i++)
if (!used.Test(i))
{
unused = 1;
const Element2d & tri = SurfaceElement(i);
for (j = 1; j <= 3; j++)
{
INDEX_2 i2(tri.PNumMod(j), tri.PNumMod(j+1));
edges.Set (i2, 1);
}
used.Set(i);
break;
}
}
while (unused);
if (haschanged)
timestamp = NextTimeStamp();
}
void Mesh :: Split2Tets()
{
PrintMessage (1, "Split To Tets");
bool has_prisms = 0;
int oldne = GetNE();
for (int i = 1; i <= oldne; i++)
{
Element el = VolumeElement(i);
if (el.GetType() == PRISM)
{
// prism, to 3 tets
// make minimal node to node 1
int minpi=0;
PointIndex minpnum;
minpnum = GetNP() + 1;
for (int j = 1; j <= 6; j++)
{
if (el.PNum(j) < minpnum)
{
minpnum = el.PNum(j);
minpi = j;
}
}
if (minpi >= 4)
{
for (int j = 1; j <= 3; j++)
swap (el.PNum(j), el.PNum(j+3));
minpi -= 3;
}
while (minpi > 1)
{
int hi = 0;
for (int j = 0; j <= 3; j+= 3)
{
hi = el.PNum(1+j);
el.PNum(1+j) = el.PNum(2+j);
el.PNum(2+j) = el.PNum(3+j);
el.PNum(3+j) = hi;
}
minpi--;
}
/*
version 1: edge from pi2 to pi6,
version 2: edge from pi3 to pi5,
*/
static const int ntets[2][12] =
{ { 1, 4, 5, 6, 1, 2, 3, 6, 1, 2, 5, 6 },
{ 1, 4, 5, 6, 1, 2, 3, 5, 3, 1, 5, 6 } };
const int * min2pi;
if (min2 (el.PNum(2), el.PNum(6)) <
min2 (el.PNum(3), el.PNum(5)))
{
min2pi = &ntets[0][0];
// (*testout) << "version 1 ";
}
else
{
min2pi = &ntets[1][0];
// (*testout) << "version 2 ";
}
int firsttet = 1;
for (int j = 1; j <= 3; j++)
{
Element nel(TET);
for (int k = 1; k <= 4; k++)
nel.PNum(k) = el.PNum(min2pi[4 * j + k - 5]);
nel.SetIndex (el.GetIndex());
int legal = 1;
for (int k = 1; k <= 3; k++)
for (int l = k+1; l <= 4; l++)
if (nel.PNum(k) == nel.PNum(l))
legal = 0;
// (*testout) << nel << " ";
if (legal)
{
if (firsttet)
{
VolumeElement(i) = nel;
firsttet = 0;
}
else
{
AddVolumeElement(nel);
}
}
}
if (firsttet) cout << "no legal";
(*testout) << endl;
}
else if (el.GetType() == HEX)
{
// hex to A) 2 prisms or B) to 5 tets
// make minimal node to node 1
int minpi=0;
PointIndex minpnum;
minpnum = GetNP() + 1;
for (int j = 1; j <= 8; j++)
{
if (el.PNum(j) < minpnum)
{
minpnum = el.PNum(j);
minpi = j;
}
}
if (minpi >= 5)
{
for (int j = 1; j <= 4; j++)
swap (el.PNum(j), el.PNum(j+4));
minpi -= 4;
}
while (minpi > 1)
{
int hi = 0;
for (int j = 0; j <= 4; j+= 4)
{
hi = el.PNum(1+j);
el.PNum(1+j) = el.PNum(2+j);
el.PNum(2+j) = el.PNum(3+j);
el.PNum(3+j) = el.PNum(4+j);
el.PNum(4+j) = hi;
}
minpi--;
}
static const int to_prisms[3][12] =
{ { 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7 },
{ 0, 1, 5, 3, 2, 6, 0, 5, 4, 3, 6, 7 },
{ 0, 7, 4, 1, 6, 5, 0, 3, 7, 1, 2, 6 },
};
const int * min2pi = 0;
if (min2 (el[4], el[6]) < min2 (el[5], el[7]))
min2pi = &to_prisms[0][0];
else if (min2 (el[3], el[6]) < min2 (el[2], el[7]))
min2pi = &to_prisms[1][0];
else if (min2 (el[1], el[6]) < min2 (el[2], el[5]))
min2pi = &to_prisms[2][0];
if (min2pi)
{
has_prisms = 1;
for (int j = 0; j < 2; j++)
{
Element nel(PRISM);
for (int k = 0; k < 6; k++)
nel[k] = el[min2pi[6*j + k]];
nel.SetIndex (el.GetIndex());
if (j == 0)
VolumeElement(i) = nel;
else
AddVolumeElement(nel);
}
}
else
{
// split to 5 tets
static const int to_tets[20] =
{
1, 2, 0, 5,
3, 0, 2, 7,
4, 5, 7, 0,
6, 7, 5, 2,
0, 2, 7, 5
};
for (int j = 0; j < 5; j++)
{
Element nel(TET);
for (int k = 0; k < 4; k++)
nel[k] = el[to_tets[4*j + k]];
nel.SetIndex (el.GetIndex());
if (j == 0)
VolumeElement(i) = nel;
else
AddVolumeElement(nel);
}
}
}
else if (el.GetType() == PYRAMID)
{
// pyramid, to 2 tets
// cout << "pyramid: " << el << endl;
static const int ntets[2][8] =
{ { 1, 2, 3, 5, 1, 3, 4, 5 },
{ 1, 2, 4, 5, 4, 2, 3, 5 }};
const int * min2pi;
if (min2 (el[0], el[2]) < min2 (el[1], el[3]))
min2pi = &ntets[0][0];
else
min2pi = &ntets[1][0];
bool firsttet = 1;
for (int j = 0; j < 2; j++)
{
Element nel(TET);
for (int k = 0; k < 4; k++)
nel[k] = el[min2pi[4*j + k]-1];
nel.SetIndex (el.GetIndex());
// cout << "pyramid-tet: " << nel << endl;
bool legal = 1;
for (int k = 0; k < 3; k++)
for (int l = k+1; l < 4; l++)
if (nel[k] == nel[l])
legal = 0;
if (legal)
{
(*testout) << nel << " ";
if (firsttet)
VolumeElement(i) = nel;
else
AddVolumeElement(nel);
firsttet = 0;
}
}
if (firsttet) cout << "no legal";
(*testout) << endl;
}
}
int oldnse = GetNSE();
for (int i = 1; i <= oldnse; i++)
{
Element2d el = SurfaceElement(i);
if (el.GetNP() == 4)
{
(*testout) << "split el: " << el << " to ";
static const int ntris[2][6] =
{ { 1, 2, 3, 1, 3, 4 },
{ 1, 2, 4, 4, 2, 3 }};
const int * min2pi;
if (min2 (el.PNum(1), el.PNum(3)) <
min2 (el.PNum(2), el.PNum(4)))
min2pi = &ntris[0][0];
else
min2pi = &ntris[1][0];
for (int j = 0; j <6; j++)
(*testout) << min2pi[j] << " ";
int firsttri = 1;
for (int j = 1; j <= 2; j++)
{
Element2d nel(3);
for (int k = 1; k <= 3; k++)
nel.PNum(k) = el.PNum(min2pi[3 * j + k - 4]);
nel.SetIndex (el.GetIndex());
int legal = 1;
for (int k = 1; k <= 2; k++)
for (int l = k+1; l <= 3; l++)
if (nel.PNum(k) == nel.PNum(l))
legal = 0;
if (legal)
{
(*testout) << nel << " ";
if (firsttri)
{
SurfaceElement(i) = nel;
firsttri = 0;
}
else
{
AddSurfaceElement(nel);
}
}
}
(*testout) << endl;
}
}
if (has_prisms)
Split2Tets();
else
{
for (int i = 1; i <= GetNE(); i++)
{
Element & el = VolumeElement(i);
const Point3d & p1 = Point (el.PNum(1));
const Point3d & p2 = Point (el.PNum(2));
const Point3d & p3 = Point (el.PNum(3));
const Point3d & p4 = Point (el.PNum(4));
double vol = (Vec3d (p1, p2) *
Cross (Vec3d (p1, p3), Vec3d(p1, p4)));
if (vol > 0)
swap (el.PNum(3), el.PNum(4));
}
UpdateTopology();
timestamp = NextTimeStamp();
}
RebuildSurfaceElementLists();
}
void Mesh :: BuildElementSearchTree ()
{
if (elementsearchtreets == GetTimeStamp()) return;
{
std::lock_guard<std::mutex> guard(buildsearchtree_mutex);
if (elementsearchtreets != GetTimeStamp())
{
NgLock lock(mutex);
lock.Lock();
PrintMessage (4, "Rebuild element searchtree");
elementsearchtree = nullptr;
int ne = (dimension == 2) ? GetNSE() : GetNE();
if (dimension == 3 && !GetNE() && GetNSE())
ne = GetNSE();
if (ne)
{
if (dimension == 2 || (dimension == 3 && !GetNE()) )
{
Box<3> box (Box<3>::EMPTY_BOX);
for (SurfaceElementIndex sei = 0; sei < ne; sei++)
// box.Add (points[surfelements[sei].PNums()]);
for (auto pi : surfelements[sei].PNums())
box.Add (points[pi]);
box.Increase (1.01 * box.Diam());
elementsearchtree = make_unique<BoxTree<3>> (box);
for (SurfaceElementIndex sei = 0; sei < ne; sei++)
{
// box.Set (points[surfelements[sei].PNums()]);
Box<3> box (Box<3>::EMPTY_BOX);
for (auto pi : surfelements[sei].PNums())
box.Add (points[pi]);
auto & el = surfelements[sei];
if(el.IsCurved() && curvedelems->IsSurfaceElementCurved(sei))
{
netgen::Point<2> lami [4] = {netgen::Point<2>(0.5,0), netgen::Point<2>(0,0.5), netgen::Point<2>(0.5,0.5), netgen::Point<2>(1./3,1./3)};
for (auto lam : lami)
{
netgen::Point<3> x;
Mat<3,2> Jac;
curvedelems->CalcSurfaceTransformation(lam,sei,x,Jac);
box.Add (x);
}
box.Scale(1.2);
}
elementsearchtree -> Insert (box, sei+1);
}
}
else
{
Box<3> box (Box<3>::EMPTY_BOX);
for (ElementIndex ei = 0; ei < ne; ei++)
// box.Add (points[volelements[ei].PNums()]);
for (auto pi : volelements[ei].PNums())
box.Add (points[pi]);
box.Increase (1.01 * box.Diam());
elementsearchtree = make_unique<BoxTree<3>> (box);
for (ElementIndex ei = 0; ei < ne; ei++)
{
// box.Set (points[volelements[ei].PNums()]);
Box<3> box (Box<3>::EMPTY_BOX);
for (auto pi : volelements[ei].PNums())
box.Add (points[pi]);
auto & el = volelements[ei];
if(el.IsCurved() && curvedelems->IsElementCurved(ei))
box.Scale(1.2);
elementsearchtree -> Insert (box, ei+1);
}
}
elementsearchtreets = GetTimeStamp();
}
}
}
}
int SolveLinearSystemLS (const Vec3d & col1,
const Vec3d & col2,
const Vec3d & rhs,
Vec2d & sol)
{
double a11 = col1 * col1;
double a12 = col1 * col2;
double a22 = col2 * col2;
double det = a11 * a22 - a12 * a12;
if (det*det <= 1e-24 * a11 * a22)
{
sol = Vec2d (0, 0);
return 1;
}
Vec2d aTrhs;
aTrhs.X() = col1*rhs;
aTrhs.Y() = col2*rhs;
sol.X() = ( a22 * aTrhs.X() - a12 * aTrhs.Y()) / det;
sol.Y() = (-a12 * aTrhs.X() + a11 * aTrhs.Y()) / det;
return 0;
}
bool ValidBarCoord(double lami[3], double eps=1e-12)
{
return (lami[0]<=1.+eps && lami[0]>=0.-eps && lami[1]<=1.+eps && lami[1]>=0.-eps && lami[2]<=1.+eps && lami[2]>=0.-eps );
}
bool Mesh :: PointContainedIn2DElement(const Point3d & p,
double lami[3],
const int element,
bool consider3D) const
{
Vec3d col1, col2, col3;
Vec3d rhs, sol;
const double eps = 1e-6;
NgArray<Element2d> loctrigs;
//SZ
if(SurfaceElement(element).GetType()==QUAD)
{
const Element2d & el = SurfaceElement(element);
const Point3d & p1 = Point(el.PNum(1));
const Point3d & p2 = Point(el.PNum(2));
const Point3d & p3 = Point(el.PNum(3));
const Point3d & p4 = Point(el.PNum(4));
// Coefficients of Bilinear Mapping from Ref-Elem to global Elem
// X = a + b x + c y + d x y
Vec3d a = p1;
Vec3d b = p2 - a;
Vec3d c = p4 - a;
Vec3d d = p3 - a - b - c;
/*cout << "p = " << p << endl;
cout << "p1 = " << p1 << endl;
cout << "p2 = " << p2 << endl;
cout << "p3 = " << p3 << endl;
cout << "p4 = " << p4 << endl;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
cout << "d = " << d << endl;*/
Vec3d pa = p-a;
double dxb = d.X()*b.Y()-d.Y()*b.X();
double dxc = d.X()*c.Y()-d.Y()*c.X();
double bxc = b.X()*c.Y()-b.Y()*c.X();
double bxpa = b.X()*pa.Y()-b.Y()*pa.X();
double cxpa = c.X()*pa.Y()-c.Y()*pa.X();
double dxpa = d.X()*pa.Y()-d.Y()*pa.X();
/*cout << "dxb = " << dxb << endl;
cout << "dxc = " << dxc << endl;
cout << "bxc = " << bxc << endl;
cout << "bxpa = " << bxpa << endl;
cout << "cxpa = " << cxpa << endl;
cout << "dxpa = " << dxpa << endl;*/
/*
P = a + b x + c y + d x y
1) P1 = a1 + b1 x + c1 y + d1 x y
2) P2 = a2 + b2 x + c2 y + d2 x y
-> det(x,d) = det(a,d) + det(b,d) x + det(c,d) y
-> x = 1/det(b,d) *( det(P-a,d)-det(c,d) y )
-> y = 1/det(c,d) *( det(P-a,d)-det(b,d) x )
-> x = (P1 - a1 - c1 y)/(b1 + d1 y)
-> det(c,d) y**2 + [det(d,P-a) + det(c,b)] y + det(b,P-a) = 0
( same if we express x = (P2 - a2 - c2 y)/(b2 + d2 y) )
-> y = (P1 - a1 - b1 x)/(c1 + d1 x)
-> det(b,d) x**2 + [det(d,P-a) + det(b,c)] x + det(c,P-a) = 0
( same if we express y = (P2 - a2 - b2 x)/(c2 + d2 x)
*/
lami[2]=0.;
double eps = 1.E-12;
double c1,c2,r;
//First check if point is "exactly" a vertex point
Vec3d d1 = p-p1;
Vec3d d2 = p-p2;
Vec3d d3 = p-p3;
Vec3d d4 = p-p4;
//cout << " d1 = " << d1 << ", d2 = " << d2 << ", d3 = " << d3 << ", d4 = " << d4 << endl;
if (d1.Length2() < sqr(eps)*d2.Length2() && d1.Length2() < sqr(eps)*d3.Length2() && d1.Length2() < sqr(eps)*d4.Length2())
{
lami[0] = lami[1] = 0.;
return true;
}
else if (d2.Length2() < sqr(eps)*d1.Length2() && d2.Length2() < sqr(eps)*d3.Length2() && d2.Length2() < sqr(eps)*d4.Length2())
{
lami[0] = 1.;
lami[1] = 0.;
return true;
}
else if (d3.Length2() < sqr(eps)*d1.Length2() && d3.Length2() < sqr(eps)*d2.Length2() && d3.Length2() < sqr(eps)*d4.Length2())
{
lami[0] = lami[1] = 1.;
return true;
}
else if (d4.Length2() < sqr(eps)*d1.Length2() && d4.Length2() < sqr(eps)*d2.Length2() && d4.Length2() < sqr(eps)*d3.Length2())
{
lami[0] = 0.;
lami[1] = 1.;
return true;
}//if d is nearly 0: solve resulting linear system
else if (d.Length2() < sqr(eps)*b.Length2() && d.Length2() < sqr(eps)*c.Length2())
{
Vec2d sol;
SolveLinearSystemLS (b, c, p-a, sol);
lami[0] = sol.X();
lami[1] = sol.Y();
return ValidBarCoord(lami, eps);
}// if dxc is nearly 0: solve resulting linear equation for y and compute x
else if (fabs(dxc) < sqr(eps))
{
lami[1] = -bxpa/(dxpa-bxc);
lami[0] = (dxpa-dxc*lami[1])/dxb;
return ValidBarCoord(lami, eps);
}// if dxb is nearly 0: solve resulting linear equation for x and compute y
else if (fabs(dxb) < sqr(eps))
{
lami[0] = -cxpa/(dxpa+bxc);
lami[1] = (dxpa-dxb*lami[0])/dxc;
return ValidBarCoord(lami, eps);
}//if dxb >= dxc: solve quadratic equation in y and compute x
else if (fabs(dxb) >= fabs(dxc))
{
c1 = (bxc-dxpa)/dxc;
c2 = -bxpa/dxc;
r = c1*c1/4.0-c2;
//quadratic equation has only 1 (unstable) solution
if (fabs(r) < eps) //not eps^2!
{
lami[1] = -c1/2;
lami[0] = (dxpa-dxc*lami[1])/dxb;
return ValidBarCoord(lami, eps);
}
if (r < 0) return false;
lami[1] = -c1/2+sqrt(r);
lami[0] = (dxpa-dxc*lami[1])/dxb;
if (ValidBarCoord(lami, eps))
return true;
else
{
lami[1] = -c1/2-sqrt(r);
lami[0] = (dxpa-dxc*lami[1])/dxb;
return ValidBarCoord(lami, eps);
}
}//if dxc > dxb: solve quadratic equation in x and compute y
else
{
c1 = (-bxc-dxpa)/dxb;
c2 = -cxpa/dxb;
r = c1*c1/4.0-c2;
//quadratic equation has only 1 (unstable) solution
if (fabs(r) < eps) //not eps^2!
{
lami[0] = -c1/2;
lami[1] = (dxpa-dxb*lami[0])/dxc;
return ValidBarCoord(lami, eps);
}
if (r < 0) return false;
lami[0] = -c1/2+sqrt(r);
lami[1] = (dxpa-dxb*lami[0])/dxc;
if (ValidBarCoord(lami, eps))
return true;
else
{
lami[0] = -c1/2-sqrt(r);
lami[1] = (dxpa-dxb*lami[0])/dxc;
return ValidBarCoord(lami, eps);
}
}
/*
double dxa = d.X()*a.Y()-d.Y()*a.X();
double dxp = d.X()*p.Y()-d.Y()*p.X();
double c0,c1,c2; // ,rt;
Vec3d dp13 = p3-p1;
Vec3d dp24 = p4-p2;
double d1 = dp13.Length2();
double d2 = dp24.Length2();
// if(fabs(d.X()) <= eps && fabs(d.Y())<= eps)
//if (d.Length2() < sqr(eps))
if (d.Length2() < sqr(eps)*d1 && d.Length2() < sqr(eps)*d2)
{
//Solve Linear System
Vec2d sol;
SolveLinearSystemLS (b, c, p-a, sol);
lami[0] = sol.X();
lami[1] = sol.Y();
if(lami[1]<=1.+eps && lami[1]>=0.-eps && lami[0]<=1.+eps && lami[0]>=0.-eps)
return true;
//lami[0]=(c.Y()*(p.X()-a.X())-c.X()*(p.Y()-a.Y()))/
//(b.X()*c.Y() -b.Y()*c.X());
//lami[1]=(-b.Y()*(p.X()-a.X())+b.X()*(p.Y()-a.Y()))/
// (b.X()*c.Y() -b.Y()*c.X());
}
else
if(fabs(dxb) <= eps*fabs(dxc))
{
lami[1] = (dxp-dxa)/dxc;
if(fabs(b.X()+d.X()*lami[1])>=fabs(b.Y()+d.Y()*lami[1]))
lami[0] = (p.X()-a.X() - c.X()*lami[1])/(b.X()+d.X()*lami[1]);
else
lami[0] = (p.Y()-a.Y() - c.Y()*lami[1])/(b.Y()+d.Y()*lami[1]);
if(lami[1]<=1.+eps && lami[1]>=0.-eps && lami[0]<=1.+eps && lami[0]>=0.-eps)
return true;
}
else
if(fabs(dxc) <= eps*fabs(dxb))
{
lami[0] = (dxp-dxa)/dxb;
if(fabs(c.X()+d.X()*lami[0])>=fabs(c.Y()+d.Y()*lami[0]))
lami[1] = (p.X()-a.X() - b.X()*lami[0])/(c.X()+d.X()*lami[0]);
else
lami[1] = (p.Y()-a.Y() - b.Y()*lami[0])/(c.Y()+d.Y()*lami[0]);
if(lami[1]<=1.+eps && lami[1]>=0.-eps && lami[0]<=1.+eps && lami[0]>=0.-eps)
return true;
}
else //Solve quadratic equation
{
c2 = -d.X()*dxb;
c1 = b.X()*dxc - c.X()*dxb + d.X()*(dxp-dxa);
c0 = c.X()*(dxp-dxa) + (a.X()-p.X())*dxc;
double rt = c1*c1 - 4*c2*c0;
if (rt < 0.) return false;
lami[1] = (-c1 + sqrt(rt))/2/c2;
if(lami[1]<=1.+eps && lami[1]>=0.-eps)
{
lami[0] = (dxp - dxa -dxb*lami[1])/dxc;
if(lami[0]<=1.+eps && lami[0]>=0.-eps)
return true;
}
lami[1] = (-c1 - sqrt(rt))/2/c2;
lami[0] = (dxp - dxa -dxb*lami[1])/dxc;
if(lami[1]<=1.+eps && lami[1]>=0.-eps && lami[0]<=1.+eps && lami[0]>=0.-eps)
return true;
c2 = d.Y()*dxb;
c1 = b.Y()*dxc - c.Y()*dxb + d.Y()*(dxp-dxa);
c0 = c.Y()*(dxp -dxa) + (a.Y()-p.Y())*dxc;
rt = c1*c1 - 4*c2*c0;
if (rt < 0.) return false;
lami[1] = (-c1 + sqrt(rt))/2/c2;
if(lami[1]<=1.+eps && lami[1]>=0.-eps)
{
lami[0] = (dxp - dxa -dxb*lami[1])/dxc;
if(lami[0]<=1.+eps && lami[0]>=0.-eps)
return true;
}
lami[1] = (-c1 - sqrt(rt))/2/c2;
lami[0] = (dxp - dxa -dxb*lami[1])/dxc;
if(lami[1]<=1.+eps && lami[1]>=0.-eps && lami[0]<=1.+eps && lami[0]>=0.-eps)
return true;
c2 = -d.X()*dxc;
c1 = -b.X()*dxc + c.X()*dxb + d.X()*(dxp-dxa);
c0 = b.X()*(dxp -dxa) + (a.X()-p.X())*dxb;
rt = c1*c1 - 4*c2*c0;
if (rt < 0.) return false;
lami[1] = (-c1 + sqrt(rt))/2/c2;
if(lami[1]<=1.+eps && lami[1]>=0.-eps)
{
lami[0] = (dxp - dxa -dxc*lami[1])/dxb;
if(lami[0]<=1.+eps && lami[0]>=0.-eps)
return true;
}
lami[1] = (-c1 - sqrt(rt))/2/c2;
lami[0] = (dxp - dxa -dxc*lami[1])/dxb;
if(lami[1]<=1.+eps && lami[1]>=0.-eps && lami[0]<=1.+eps && lami[0]>=0.-eps)
return true;
}*/
//cout << "lam0,1 = " << lami[0] << ", " << lami[1] << endl;
/*if( lami[0] <= 1.+eps && lami[0] >= -eps && lami[1]<=1.+eps && lami[1]>=-eps)
{
if(consider3D)
{
Vec3d n = Cross(b,c);
lami[2] = 0;
for(int i=1; i<=3; i++)
lami[2] +=(p.X(i)-a.X(i)-lami[0]*b.X(i)-lami[1]*c.X(i)) * n.X(i);
if(lami[2] >= -eps && lami[2] <= eps)
return true;
}
else
return true;
}*/
return false;
}
else
{
// SurfaceElement(element).GetTets (loctets);
loctrigs.SetSize(1);
loctrigs.Elem(1) = SurfaceElement(element);
for (int j = 1; j <= loctrigs.Size(); j++)
{
const Element2d & el = loctrigs.Get(j);
const Point3d & p1 = Point(el.PNum(1));
const Point3d & p2 = Point(el.PNum(2));
const Point3d & p3 = Point(el.PNum(3));
/*
Box3d box;
box.SetPoint (p1);
box.AddPoint (p2);
box.AddPoint (p3);
box.AddPoint (p4);
if (!box.IsIn (p))
continue;
*/
col1 = p2-p1;
col2 = p3-p1;
col3 = Cross(col1,col2);
//col3 = Vec3d(0, 0, 1);
rhs = p - p1;
// int retval =
SolveLinearSystem (col1, col2, col3, rhs, sol);
//(*testout) << "retval " << retval << endl;
//(*testout) << "col1 " << col1 << " col2 " << col2 << " col3 " << col3 << " rhs " << rhs << endl;
//(*testout) << "sol " << sol << endl;
if (SurfaceElement(element).GetType() ==TRIG6 || curvedelems->IsSurfaceElementCurved(element-1))
{
netgen::Point<2> lam(1./3,1./3);
Vec<3> rhs;
Vec<2> deltalam;
netgen::Point<3> x;
Mat<3,2> Jac,Jact;
double delta=1;
bool retval;
int i = 0;
const int maxits = 30;
while(delta > 1e-16 && i<maxits)
{
curvedelems->CalcSurfaceTransformation(lam,element-1,x,Jac);
rhs = p-x;
Jac.Solve(rhs,deltalam);
lam += deltalam;
delta = deltalam.Length2();
i++;
//(*testout) << "pcie i " << i << " delta " << delta << " p " << p << " x " << x << " lam " << lam << endl;
//<< "Jac " << Jac << endl;
}
if(i==maxits)
return false;
sol.X() = lam(0);
sol.Y() = lam(1);
if (SurfaceElement(element).GetType() !=TRIG6 )
{
sol.Z() = sol.X();
sol.X() = sol.Y();
sol.Y() = 1.0 - sol.Z() - sol.X();
}
}
if (sol.X() >= -eps && sol.Y() >= -eps &&
sol.X() + sol.Y() <= 1+eps)
{
if(!consider3D || (sol.Z() >= -eps && sol.Z() <= eps))
{
lami[0] = sol.X();
lami[1] = sol.Y();
lami[2] = sol.Z();
return true;
}
}
}
}
return false;
}
bool Mesh :: PointContainedIn3DElement(const Point3d & p,
double lami[3],
const int element) const
{
//bool oldresult = PointContainedIn3DElementOld(p,lami,element);
//(*testout) << "old result: " << oldresult
// << " lam " << lami[0] << " " << lami[1] << " " << lami[2] << endl;
//if(!curvedelems->IsElementCurved(element-1))
// return PointContainedIn3DElementOld(p,lami,element);
const double eps = 1.e-4;
const Element & el = VolumeElement(element);
netgen::Point<3> lam = 0.0;
if (el.GetType() == TET || el.GetType() == TET10)
{
lam = 0.25;
}
else if (el.GetType() == PRISM)
{
lam(0) = 0.33; lam(1) = 0.33; lam(2) = 0.5;
}
else if (el.GetType() == PYRAMID)
{
lam(0) = 0.4; lam(1) = 0.4; lam(2) = 0.2;
}
else if (el.GetType() == HEX)
{
lam = 0.5;
}
Vec<3> deltalam,rhs;
netgen::Point<3> x;
Mat<3,3> Jac,Jact;
double delta=1;
bool retval;
int i = 0;
const int maxits = 30;
while(delta > 1e-16 && i<maxits)
{
curvedelems->CalcElementTransformation(lam,element-1,x,Jac);
rhs = p-x;
Jac.Solve(rhs,deltalam);
lam += deltalam;
delta = deltalam.Length2();
i++;
//(*testout) << "pcie i " << i << " delta " << delta << " p " << p << " x " << x << " lam " << lam << endl;
//<< "Jac " << Jac << endl;
}
if(i==maxits)
return false;
for(i=0; i<3; i++)
lami[i] = lam(i);
if (el.GetType() == TET || el.GetType() == TET10)
{
retval = (lam(0) > -eps &&
lam(1) > -eps &&
lam(2) > -eps &&
lam(0) + lam(1) + lam(2) < 1+eps);
}
else if (el.GetType() == PRISM || el.GetType() == PRISM15)
{
retval = (lam(0) > -eps &&
lam(1) > -eps &&
lam(2) > -eps &&
lam(2) < 1+eps &&
lam(0) + lam(1) < 1+eps);
}
else if (el.GetType() == PYRAMID || el.GetType() == PYRAMID13)
{
retval = (lam(0) > -eps &&
lam(1) > -eps &&
lam(2) > -eps &&
lam(0) + lam(2) < 1+eps &&
lam(1) + lam(2) < 1+eps);
}
else if (el.GetType() == HEX || el.GetType() == HEX20)
{
retval = (lam(0) > -eps && lam(0) < 1+eps &&
lam(1) > -eps && lam(1) < 1+eps &&
lam(2) > -eps && lam(2) < 1+eps);
}
else
throw NgException("Da haun i wos vagessn");
return retval;
}
bool Mesh :: PointContainedIn3DElementOld(const Point3d & p,
double lami[3],
const int element) const
{
Vec3d col1, col2, col3;
Vec3d rhs, sol;
const double eps = 1.e-4;
NgArray<Element> loctets;
VolumeElement(element).GetTets (loctets);
for (int j = 1; j <= loctets.Size(); j++)
{
const Element & el = loctets.Get(j);
const Point3d & p1 = Point(el.PNum(1));
const Point3d & p2 = Point(el.PNum(2));
const Point3d & p3 = Point(el.PNum(3));
const Point3d & p4 = Point(el.PNum(4));
Box3d box;
box.SetPoint (p1);
box.AddPoint (p2);
box.AddPoint (p3);
box.AddPoint (p4);
if (!box.IsIn (p))
continue;
col1 = p2-p1;
col2 = p3-p1;
col3 = p4-p1;
rhs = p - p1;
SolveLinearSystem (col1, col2, col3, rhs, sol);
if (sol.X() >= -eps && sol.Y() >= -eps && sol.Z() >= -eps &&
sol.X() + sol.Y() + sol.Z() <= 1+eps)
{
NgArray<Element> loctetsloc;
NgArray<netgen::Point<3> > pointsloc;
VolumeElement(element).GetTetsLocal (loctetsloc);
VolumeElement(element).GetNodesLocalNew (pointsloc);
const Element & le = loctetsloc.Get(j);
Point3d pp =
pointsloc.Get(le.PNum(1))
+ sol.X() * Vec3d (pointsloc.Get(le.PNum(1)), pointsloc.Get(le.PNum(2)))
+ sol.Y() * Vec3d (pointsloc.Get(le.PNum(1)), pointsloc.Get(le.PNum(3)))
+ sol.Z() * Vec3d (pointsloc.Get(le.PNum(1)), pointsloc.Get(le.PNum(4))) ;
lami[0] = pp.X();
lami[1] = pp.Y();
lami[2] = pp.Z();
return true;
}
}
return false;
}
int Mesh :: GetElementOfPoint (const netgen::Point<3> & p,
double lami[3],
bool build_searchtree,
const int index,
const bool allowindex) const
{
if(index != -1)
{
NgArray<int> dummy(1);
dummy[0] = index;
return GetElementOfPoint(p,lami,&dummy,build_searchtree,allowindex);
}
else
return GetElementOfPoint(p,lami,NULL,build_searchtree,allowindex);
}
int Mesh :: GetElementOfPoint (const netgen::Point<3> & p,
double lami[3],
const NgArray<int> * const indices,
bool build_searchtree,
const bool allowindex) const
{
if ( (dimension == 2 && !GetNSE()) ||
(dimension == 3 && !GetNE() && !GetNSE()) )
return -1;
if (build_searchtree)
const_cast<Mesh&>(*this).BuildElementSearchTree ();
if (dimension == 2 || (dimension==3 && !GetNE() && GetNSE()))
return Find2dElement(*this, p, lami, indices, elementsearchtree.get(), allowindex);
return Find3dElement(*this, p, lami, indices, elementsearchtree.get(), allowindex);
}
int Mesh :: GetSurfaceElementOfPoint (const netgen::Point<3> & p,
double lami[3],
bool build_searchtree,
const int index,
const bool allowindex) const
{
if(index != -1)
{
NgArray<int> dummy(1);
dummy[0] = index;
return GetSurfaceElementOfPoint(p,lami,&dummy,build_searchtree,allowindex);
}
else
return GetSurfaceElementOfPoint(p,lami,NULL,build_searchtree,allowindex);
}
int Mesh :: GetSurfaceElementOfPoint (const netgen::Point<3> & p,
double lami[3],
const NgArray<int> * const indices,
bool build_searchtree,
const bool allowindex) const
{
if (!GetNE() && build_searchtree)
const_cast<Mesh&>(*this).BuildElementSearchTree ();
if (dimension == 2)
return Find1dElement(*this, p, lami, indices, elementsearchtree.get(), allowindex);
else
return Find2dElement(*this, p, lami, indices, elementsearchtree.get(), allowindex);
return 0;
}
void Mesh::GetIntersectingVolEls(const Point3d& p1, const Point3d& p2,
NgArray<int> & locels) const
{
elementsearchtree->GetIntersecting (p1, p2, locels);
}
void Mesh :: SplitIntoParts()
{
int i, j, dom;
int ne = GetNE();
int np = GetNP();
int nse = GetNSE();
NgBitArray surfused(nse);
NgBitArray pused (np);
surfused.Clear();
dom = 0;
while (1)
{
int cntd = 1;
dom++;
pused.Clear();
int found = 0;
for (i = 1; i <= nse; i++)
if (!surfused.Test(i))
{
SurfaceElement(i).SetIndex (dom);
for (j = 1; j <= 3; j++)
pused.Set (SurfaceElement(i).PNum(j));
found = 1;
cntd = 1;
surfused.Set(i);
break;
}
if (!found)
break;
int change;
do
{
change = 0;
for (i = 1; i <= nse; i++)
{
int is = 0, isnot = 0;
for (j = 1; j <= 3; j++)
if (pused.Test(SurfaceElement(i).PNum(j)))
is = 1;
else
isnot = 1;
if (is && isnot)
{
change = 1;
for (j = 1; j <= 3; j++)
pused.Set (SurfaceElement(i).PNum(j));
}
if (is)
{
if (!surfused.Test(i))
{
surfused.Set(i);
SurfaceElement(i).SetIndex (dom);
cntd++;
}
}
}
for (i = 1; i <= ne; i++)
{
int is = 0, isnot = 0;
for (j = 1; j <= 4; j++)
if (pused.Test(VolumeElement(i).PNum(j)))
is = 1;
else
isnot = 1;
if (is && isnot)
{
change = 1;
for (j = 1; j <= 4; j++)
pused.Set (VolumeElement(i).PNum(j));
}
if (is)
{
VolumeElement(i).SetIndex (dom);
}
}
}
while (change);
PrintMessage (3, "domain ", dom, " has ", cntd, " surfaceelements");
}
/*
facedecoding.SetSize (dom);
for (i = 1; i <= dom; i++)
{
facedecoding.Elem(i).surfnr = 0;
facedecoding.Elem(i).domin = i;
facedecoding.Elem(i).domout = 0;
}
*/
ClearFaceDescriptors();
for (i = 1; i <= dom; i++)
AddFaceDescriptor (FaceDescriptor (0, i, 0, 0));
CalcSurfacesOfNode();
timestamp = NextTimeStamp();
}
void Mesh :: SplitSeparatedFaces ()
{
PrintMessage (3, "SplitSeparateFaces");
int fdi;
int np = GetNP();
NgBitArray usedp(np);
Array<SurfaceElementIndex> els_of_face;
fdi = 1;
while (fdi <= GetNFD())
{
GetSurfaceElementsOfFace (fdi, els_of_face);
if (els_of_face.Size() == 0)
{
fdi++;
continue;
}
SurfaceElementIndex firstel = els_of_face[0];
usedp.Clear();
for (int j = 1; j <= SurfaceElement(firstel).GetNP(); j++)
usedp.Set (SurfaceElement(firstel).PNum(j));
bool changed;
do
{
changed = false;
for (int i = 0; i < els_of_face.Size(); i++)
{
const Element2d & el = SurfaceElement(els_of_face[i]);
bool has = 0;
bool hasno = 0;
for (int j = 0; j < el.GetNP(); j++)
{
if (usedp.Test(el[j]))
has = true;
else
hasno = true;
}
if (has && hasno)
changed = true;
if (has)
for (int j = 0; j < el.GetNP(); j++)
usedp.Set (el[j]);
}
}
while (changed);
int nface = 0;
for (int i = 0; i < els_of_face.Size(); i++)
{
Element2d & el = SurfaceElement(els_of_face[i]);
int hasno = 0;
for (int j = 1; j <= el.GetNP(); j++)
if (!usedp.Test(el.PNum(j)))
hasno = 1;
if (hasno)
{
if (!nface)
{
FaceDescriptor nfd = GetFaceDescriptor(fdi);
nface = AddFaceDescriptor (nfd);
}
el.SetIndex (nface);
}
}
// reconnect list
if (nface)
{
facedecoding[nface-1].firstelement = -1;
facedecoding[fdi-1].firstelement = -1;
for (int i = 0; i < els_of_face.Size(); i++)
{
int ind = SurfaceElement(els_of_face[i]).GetIndex();
SurfaceElement(els_of_face[i]).next = facedecoding[ind-1].firstelement;
facedecoding[ind-1].firstelement = els_of_face[i];
}
// map the segments
for(auto& seg : segments)
if(!usedp.Test(seg[0]) || !usedp.Test(seg[1]))
if(seg.si == fdi)
seg.si = nface;
}
fdi++;
}
/*
fdi = 1;
while (fdi <= GetNFD())
{
int firstel = 0;
for (int i = 1; i <= GetNSE(); i++)
if (SurfaceElement(i).GetIndex() == fdi)
{
firstel = i;
break;
}
if (!firstel) continue;
usedp.Clear();
for (int j = 1; j <= SurfaceElement(firstel).GetNP(); j++)
usedp.Set (SurfaceElement(firstel).PNum(j));
int changed;
do
{
changed = 0;
for (int i = 1; i <= GetNSE(); i++)
{
const Element2d & el = SurfaceElement(i);
if (el.GetIndex() != fdi)
continue;
int has = 0;
int hasno = 0;
for (int j = 1; j <= el.GetNP(); j++)
{
if (usedp.Test(el.PNum(j)))
has = 1;
else
hasno = 1;
}
if (has && hasno)
changed = 1;
if (has)
for (int j = 1; j <= el.GetNP(); j++)
usedp.Set (el.PNum(j));
}
}
while (changed);
int nface = 0;
for (int i = 1; i <= GetNSE(); i++)
{
Element2d & el = SurfaceElement(i);
if (el.GetIndex() != fdi)
continue;
int hasno = 0;
for (int j = 1; j <= el.GetNP(); j++)
{
if (!usedp.Test(el.PNum(j)))
hasno = 1;
}
if (hasno)
{
if (!nface)
{
FaceDescriptor nfd = GetFaceDescriptor(fdi);
nface = AddFaceDescriptor (nfd);
}
el.SetIndex (nface);
}
}
fdi++;
}
*/
}
void Mesh :: ZRefine(const string& name, const Array<double>& slices)
{
auto nr = GetIdentifications().GetNr(name);
auto& identpts = GetIdentifications().GetIdentifiedPoints();
UpdateTopology();
std::map<std::pair<PointIndex, PointIndex>,
Array<PointIndex>> inserted_points;
BitArray mapped_points(GetNV()+1);
mapped_points = false;
// Add new points
for(auto [p1p2, idnr] : identpts)
{
if(idnr != nr)
continue;
auto& ipts = inserted_points[{p1p2.I1(), p1p2.I2()}];
auto p1 = Point(p1p2.I1());
auto p2 = Point(p1p2.I2());
ipts.Append(p1p2.I1());
mapped_points.SetBit(p1p2.I1());
for(auto slice : slices)
{
auto np = p1 + slice * (p2-p1);
auto npi = AddPoint(np);
ipts.Append(npi);
}
ipts.Append(p1p2.I2());
}
// Split segments
for(auto si : Range(segments))
{
auto& seg = segments[si];
auto p1 = seg[0];
auto p2 = seg[1];
auto c1 = inserted_points.count({p1, p2});
auto c2 = inserted_points.count({p2, p1});
if(c1 == 0 && c2 == 0)
continue;
if(c2)
Swap(p1,p2);
const auto& ipts = inserted_points[{p1,p2}];
if(c2)
seg[1] = ipts[ipts.Size()-2];
else
seg[1] = ipts[1];
for(auto i : Range(size_t(1), ipts.Size()-1))
{
Segment snew = seg;
if(c2)
{
seg[0] = ipts[ipts.Size()-1-i];
seg[1] = ipts[ipts.Size()-2-i];
}
else
{
snew[0] = ipts[i];
snew[1] = ipts[i+1];
}
AddSegment(snew);
}
}
BitArray sel_done(surfelements.Size());
sel_done = false;
// Split surface elements
auto p2sel = CreatePoint2SurfaceElementTable();
for(const auto& [pair, inserted] : inserted_points)
{
for(auto si : p2sel[pair.first])
{
if(sel_done[si])
continue;
sel_done.SetBit(si);
auto sel = surfelements[si];
map<PointIndex, Array<PointIndex>> mapped_points;
int nmapped = 0;
for(auto i : Range(sel.GetNP()))
{
auto p1 = sel[i];
auto p2 = sel[(i+1)%sel.GetNP()];
auto c1 = inserted_points.count({p1, p2});
auto c2 = inserted_points.count({p2, p1});
if(c1 == 0 && c2 == 0)
continue;
if(c2)
Swap(p1, p2);
auto& ipts = inserted_points[{p1, p2}];
auto& a1 = mapped_points[p1];
auto& a2 = mapped_points[p2];
a1 = ipts.Range(0, ipts.Size()-1);
a2 = ipts.Range(1, ipts.Size());
nmapped = ipts.Size()-1;
}
for(auto i : Range(nmapped))
{
Element2d nsel = sel;
for(auto& pi : nsel.PNums())
if(mapped_points.count(pi))
pi = mapped_points[pi][i];
AddSurfaceElement(nsel);
}
if(nmapped)
surfelements[si].Delete();
}
}
// Split volume elements
BitArray vol_done(volelements.Size());
vol_done = false;
auto p2el = CreatePoint2ElementTable(); // mapped_points);
for(const auto& [pair, inserted] : inserted_points)
{
for(auto ei : p2el[pair.first])
{
if(vol_done[ei])
continue;
vol_done.SetBit(ei);
auto el = volelements[ei];
map<PointIndex, Array<PointIndex>> mapped_points;
int nmapped = 0;
// NgArray<int> eledges;
// topology.GetElementEdges(ei+1, eledges);
// for(auto edgei : eledges)
for(auto edgei : topology.GetEdges(ElementIndex(ei)))
{
int p1, p2;
topology.GetEdgeVertices(edgei+1, p1, p2);
auto c1 = inserted_points.count({p1, p2});
auto c2 = inserted_points.count({p2, p1});
if(c1 == 0 && c2 == 0)
continue;
if(c2)
Swap(p1, p2);
auto& ipts = inserted_points[{p1, p2}];
auto& a1 = mapped_points[p1];
auto& a2 = mapped_points[p2];
a1 = ipts.Range(0, ipts.Size()-1);
a2 = ipts.Range(1, ipts.Size());
nmapped = ipts.Size()-1;
}
for(auto i : Range(nmapped))
{
Element nel = el;
for(auto& pi : nel.PNums())
if(mapped_points.count(pi))
pi = mapped_points[pi][i];
AddVolumeElement(nel);
}
if(nmapped)
volelements[ei].Delete();
}
}
Compress();
SetNextMajorTimeStamp();
}
void Mesh :: RebuildSurfaceElementLists ()
{
static Timer t("Mesh::LinkSurfaceElements"); RegionTimer reg (t);
for (int i = 0; i < facedecoding.Size(); i++)
facedecoding[i].firstelement = -1;
for (int i = surfelements.Size()-1; i >= 0; i--)
{
int ind = surfelements[i].GetIndex();
surfelements[i].next = facedecoding[ind-1].firstelement;
facedecoding[ind-1].firstelement = i;
}
}
void Mesh :: GetSurfaceElementsOfFace (int facenr, Array<SurfaceElementIndex> & sei) const
{
static int timer = NgProfiler::CreateTimer ("GetSurfaceElementsOfFace");
NgProfiler::RegionTimer reg (timer);
if(facenr==0)
{
sei.SetSize(GetNSE());
ParallelForRange( IntRange(GetNSE()), [&sei] (auto myrange)
{
for(auto i : myrange)
sei[i] = i;
});
return;
}
sei.SetSize(0);
SurfaceElementIndex si = facedecoding[facenr-1].firstelement;
while (si != -1)
{
if ( (*this)[si].GetIndex () == facenr && (*this)[si][0] >= PointIndex::BASE &&
!(*this)[si].IsDeleted() )
{
sei.Append (si);
}
si = (*this)[si].next;
}
}
void Mesh :: CalcMinMaxAngle (double badellimit, double * retvalues)
{
int i, j;
int lpi1, lpi2, lpi3, lpi4;
double phimax = 0, phimin = 10;
double facephimax = 0, facephimin = 10;
int illegaltets = 0, negativetets = 0, badtets = 0;
for (i = 1; i <= GetNE(); i++)
{
int badel = 0;
Element & el = VolumeElement(i);
if (el.GetType() != TET)
{
VolumeElement(i).flags.badel = 0;
continue;
}
if (el.Volume(Points()) < 0)
{
badel = 1;
negativetets++;
}
if (!LegalTet (el))
{
badel = 1;
illegaltets++;
(*testout) << "illegal tet: " << i << " ";
for (j = 1; j <= el.GetNP(); j++)
(*testout) << el.PNum(j) << " ";
(*testout) << endl;
}
// angles between faces
for (lpi1 = 1; lpi1 <= 3; lpi1++)
for (lpi2 = lpi1+1; lpi2 <= 4; lpi2++)
{
lpi3 = 1;
while (lpi3 == lpi1 || lpi3 == lpi2)
lpi3++;
lpi4 = 10 - lpi1 - lpi2 - lpi3;
const Point3d & p1 = Point (el.PNum(lpi1));
const Point3d & p2 = Point (el.PNum(lpi2));
const Point3d & p3 = Point (el.PNum(lpi3));
const Point3d & p4 = Point (el.PNum(lpi4));
Vec3d n(p1, p2);
n /= n.Length();
Vec3d v1(p1, p3);
Vec3d v2(p1, p4);
v1 -= (n * v1) * n;
v2 -= (n * v2) * n;
double cosphi = (v1 * v2) / (v1.Length() * v2.Length());
double phi = acos (cosphi);
if (phi > phimax) phimax = phi;
if (phi < phimin) phimin = phi;
if ((180/M_PI) * phi > badellimit)
badel = 1;
}
// angles in faces
for (j = 1; j <= 4; j++)
{
Element2d face(TRIG);
el.GetFace (j, face);
for (lpi1 = 1; lpi1 <= 3; lpi1++)
{
lpi2 = lpi1 % 3 + 1;
lpi3 = lpi2 % 3 + 1;
const Point3d & p1 = Point (el.PNum(lpi1));
const Point3d & p2 = Point (el.PNum(lpi2));
const Point3d & p3 = Point (el.PNum(lpi3));
Vec3d v1(p1, p2);
Vec3d v2(p1, p3);
double cosphi = (v1 * v2) / (v1.Length() * v2.Length());
double phi = acos (cosphi);
if (phi > facephimax) facephimax = phi;
if (phi < facephimin) facephimin = phi;
if ((180/M_PI) * phi > badellimit)
badel = 1;
}
}
VolumeElement(i).flags.badel = badel;
if (badel) badtets++;
}
if (!GetNE())
{
phimin = phimax = facephimin = facephimax = 0;
}
if (!retvalues)
{
PrintMessage (1, "");
PrintMessage (1, "between planes: phimin = ", (180/M_PI) * phimin,
" phimax = ", (180/M_PI) *phimax);
PrintMessage (1, "inside planes: phimin = ", (180/M_PI) * facephimin,
" phimax = ", (180/M_PI) * facephimax);
PrintMessage (1, "");
}
else
{
retvalues[0] = (180/M_PI) * facephimin;
retvalues[1] = (180/M_PI) * facephimax;
retvalues[2] = (180/M_PI) * phimin;
retvalues[3] = (180/M_PI) * phimax;
}
PrintMessage (3, "negative tets: ", negativetets);
PrintMessage (3, "illegal tets: ", illegaltets);
PrintMessage (3, "bad tets: ", badtets);
}
int Mesh :: MarkIllegalElements ()
{
if(!boundaryedges)
BuildBoundaryEdges();
atomic<int> cnt = 0;
ParallelForRange( Range(volelements), [&] (auto myrange)
{
int cnt_local = 0;
for(auto & el : volelements.Range(myrange))
if (!LegalTet (el))
cnt_local++;
cnt += cnt_local;
});
return cnt;
}
// #ifdef NONE
// void Mesh :: AddIdentification (int pi1, int pi2, int identnr)
// {
// INDEX_2 pair(pi1, pi2);
// // pair.Sort();
// identifiedpoints->Set (pair, identnr);
// if (identnr > maxidentnr)
// maxidentnr = identnr;
// timestamp = NextTimeStamp();
// }
// int Mesh :: GetIdentification (int pi1, int pi2) const
// {
// INDEX_2 pair(pi1, pi2);
// if (identifiedpoints->Used (pair))
// return identifiedpoints->Get(pair);
// else
// return 0;
// }
// int Mesh :: GetIdentificationSym (int pi1, int pi2) const
// {
// INDEX_2 pair(pi1, pi2);
// if (identifiedpoints->Used (pair))
// return identifiedpoints->Get(pair);
// pair = INDEX_2 (pi2, pi1);
// if (identifiedpoints->Used (pair))
// return identifiedpoints->Get(pair);
// return 0;
// }
// void Mesh :: GetIdentificationMap (int identnr, NgArray<int> & identmap) const
// {
// int i, j;
// identmap.SetSize (GetNP());
// for (i = 1; i <= identmap.Size(); i++)
// identmap.Elem(i) = 0;
// for (i = 1; i <= identifiedpoints->GetNBags(); i++)
// for (j = 1; j <= identifiedpoints->GetBagSize(i); j++)
// {
// INDEX_2 i2;
// int nr;
// identifiedpoints->GetData (i, j, i2, nr);
// if (nr == identnr)
// {
// identmap.Elem(i2.I1()) = i2.I2();
// }
// }
// }
// void Mesh :: GetIdentificationPairs (int identnr, NgArray<INDEX_2> & identpairs) const
// {
// int i, j;
// identpairs.SetSize(0);
// for (i = 1; i <= identifiedpoints->GetNBags(); i++)
// for (j = 1; j <= identifiedpoints->GetBagSize(i); j++)
// {
// INDEX_2 i2;
// int nr;
// identifiedpoints->GetData (i, j, i2, nr);
// if (identnr == 0 || nr == identnr)
// identpairs.Append (i2);
// }
// }
// #endif
int Mesh::IdentifyPeriodicBoundaries(const string &s1,
const string &s2,
const Transformation<3> &mapping,
double pointTolerance)
{
auto nr = ident->GetMaxNr() + 1;
ident->SetType(nr, Identifications::PERIODIC);
double lami[4];
set<int> identified_points;
if(pointTolerance < 0.)
{
Point3d pmin, pmax;
GetBox(pmin, pmax);
pointTolerance = 1e-8 * (pmax-pmin).Length();
}
for(const auto& se : surfelements)
{
if(GetBCName(se.index-1) != s1)
continue;
for(const auto& pi : se.PNums())
{
if(identified_points.find(pi) != identified_points.end())
continue;
auto pt = (*this)[pi];
auto mapped_pt = mapping(pt);
auto other_nr = GetElementOfPoint(mapped_pt, lami, true);
int index = -1;
if(other_nr != 0)
{
auto other_el = VolumeElement(other_nr);
for(auto i : Range(other_el.PNums().Size()))
if((mapped_pt - (*this)[other_el.PNums()[i]]).Length() < pointTolerance)
{
index = i;
break;
}
if(index == -1)
{
cout << "point coordinates = " << pt << endl;
cout << "mapped coordinates = " << mapped_pt << endl;
throw Exception("Did not find mapped point with nr " + ToString(pi) + ", are you sure your mesh is periodic?");
}
auto other_pi = other_el.PNums()[index];
identified_points.insert(pi);
ident->Add(pi, other_pi, nr);
}
else
{
cout << "point coordinates = " << pt << endl;
cout << "mapped coordinates = " << mapped_pt << endl;
throw Exception("Mapped point with nr " + ToString(pi) + " is outside of mesh, are you sure your mesh is periodic?");
}
}
}
return nr;
}
void Mesh :: InitPointCurve(double red, double green, double blue) const
{
pointcurves_startpoint.Append(pointcurves.Size());
pointcurves_red.Append(red);
pointcurves_green.Append(green);
pointcurves_blue.Append(blue);
}
void Mesh :: AddPointCurvePoint(const Point3d & pt) const
{
pointcurves.Append(pt);
}
int Mesh :: GetNumPointCurves(void) const
{
return pointcurves_startpoint.Size();
}
int Mesh :: GetNumPointsOfPointCurve(int curve) const
{
if(curve == pointcurves_startpoint.Size()-1)
return (pointcurves.Size() - pointcurves_startpoint.Last());
else
return (pointcurves_startpoint[curve+1]-pointcurves_startpoint[curve]);
}
Point3d & Mesh :: GetPointCurvePoint(int curve, int n) const
{
return pointcurves[pointcurves_startpoint[curve]+n];
}
void Mesh :: GetPointCurveColor(int curve, double & red, double & green, double & blue) const
{
red = pointcurves_red[curve];
green = pointcurves_green[curve];
blue = pointcurves_blue[curve];
}
void Mesh :: ComputeNVertices ()
{
numvertices = 0;
for (const Element & el : VolumeElements())
for (PointIndex v : el.Vertices())
if (v > numvertices) numvertices = v;
for (const Element2d & el : SurfaceElements())
for (PointIndex v : el.Vertices())
if (v > numvertices) numvertices = v;
numvertices += 1-PointIndex::BASE;
}
int Mesh :: GetNV () const
{
if (numvertices < 0)
return GetNP();
else
return numvertices;
}
void Mesh :: SetNP (int np)
{
points.SetSize(np);
// ptyps.SetSize(np);
int mlold = mlbetweennodes.Size();
mlbetweennodes.SetSize(np);
if (np > mlold)
for (int i = mlold+PointIndex::BASE;
i < np+PointIndex::BASE; i++)
{
mlbetweennodes[i].I1() = PointIndex::BASE-1;
mlbetweennodes[i].I2() = PointIndex::BASE-1;
}
GetIdentifications().SetMaxPointNr (np + PointIndex::BASE-1);
}
Table<ElementIndex, PointIndex> Mesh :: CreatePoint2ElementTable(std::optional<BitArray> points) const
{
if(points)
{
const auto & free_points = *points;
return ngcore::CreateSortedTable<ElementIndex, PointIndex>( volelements.Range(),
[&](auto & table, ElementIndex ei)
{
const auto & el = (*this)[ei];
if(el.IsDeleted())
return;
for (PointIndex pi : el.PNums())
if(free_points[pi])
table.Add (pi, ei);
}, GetNP());
}
else
return ngcore::CreateSortedTable<ElementIndex, PointIndex>( volelements.Range(),
[&](auto & table, ElementIndex ei)
{
const auto & el = (*this)[ei];
if(el.IsDeleted())
return;
for (PointIndex pi : el.PNums())
table.Add (pi, ei);
}, GetNP());
}
Table<SurfaceElementIndex, PointIndex> Mesh :: CreatePoint2SurfaceElementTable( int faceindex ) const
{
static Timer timer("Mesh::CreatePoint2SurfaceElementTable"); RegionTimer rt(timer);
if(faceindex==0)
{
return ngcore::CreateSortedTable<SurfaceElementIndex, PointIndex>( surfelements.Range(),
[&](auto & table, SurfaceElementIndex ei)
{
for (PointIndex pi : (*this)[ei].PNums())
table.Add (pi, ei);
}, GetNP());
}
Array<SurfaceElementIndex> face_els;
GetSurfaceElementsOfFace(faceindex, face_els);
return ngcore::CreateSortedTable<SurfaceElementIndex, PointIndex>( face_els.Range(),
[&](auto & table, size_t i)
{
for (PointIndex pi : (*this)[face_els[i]].PNums())
table.Add (pi, face_els[i]);
}, GetNP());
}
/*
void Mesh :: BuildConnectedNodes ()
{
if (PureTetMesh())
{
connectedtonode.SetSize(0);
return;
}
int i, j, k;
int np = GetNP();
int ne = GetNE();
TABLE<int> conto(np);
for (i = 1; i <= ne; i++)
{
const Element & el = VolumeElement(i);
if (el.GetType() == PRISM)
{
for (j = 1; j <= 6; j++)
{
int n1 = el.PNum (j);
int n2 = el.PNum ((j+2)%6+1);
// if (n1 != n2)
{
int found = 0;
for (k = 1; k <= conto.EntrySize(n1); k++)
if (conto.Get(n1, k) == n2)
{
found = 1;
break;
}
if (!found)
conto.Add (n1, n2);
}
}
}
else if (el.GetType() == PYRAMID)
{
for (j = 1; j <= 4; j++)
{
int n1, n2;
switch (j)
{
case 1: n1 = 1; n2 = 4; break;
case 2: n1 = 4; n2 = 1; break;
case 3: n1 = 2; n2 = 3; break;
case 4: n1 = 3; n2 = 2; break;
}
int found = 0;
for (k = 1; k <= conto.EntrySize(n1); k++)
if (conto.Get(n1, k) == n2)
{
found = 1;
break;
}
if (!found)
conto.Add (n1, n2);
}
}
}
connectedtonode.SetSize(np);
for (i = 1; i <= np; i++)
connectedtonode.Elem(i) = 0;
for (i = 1; i <= np; i++)
if (connectedtonode.Elem(i) == 0)
{
connectedtonode.Elem(i) = i;
ConnectToNodeRec (i, i, conto);
}
}
void Mesh :: ConnectToNodeRec (int node, int tonode,
const TABLE<int> & conto)
{
int i, n2;
// (*testout) << "connect " << node << " to " << tonode << endl;
for (i = 1; i <= conto.EntrySize(node); i++)
{
n2 = conto.Get(node, i);
if (!connectedtonode.Get(n2))
{
connectedtonode.Elem(n2) = tonode;
ConnectToNodeRec (n2, tonode, conto);
}
}
}
*/
bool Mesh :: PureTrigMesh (int faceindex) const
{
// if (!faceindex) return !mparam.quad;
if (!faceindex)
{
for (int i = 1; i <= GetNSE(); i++)
if (SurfaceElement(i).GetNP() != 3)
return false;
return true;
}
for (int i = 1; i <= GetNSE(); i++)
if (SurfaceElement(i).GetIndex() == faceindex &&
SurfaceElement(i).GetNP() != 3)
return false;
return true;
}
bool Mesh :: PureTetMesh () const
{
for (ElementIndex ei = 0; ei < GetNE(); ei++)
if (VolumeElement(ei).GetNP() != 4)
return 0;
return 1;
}
void Mesh :: UpdateTopology (NgTaskManager tm,
NgTracer tracer)
{
static Timer t("Update Topology"); RegionTimer reg(t);
topology.Update(tm, tracer);
(*tracer)("call update clusters", false);
clusters->Update();
(*tracer)("call update clusters", true);
#ifdef PARALLEL
if (paralleltop)
{
paralleltop->Reset();
paralleltop->UpdateCoarseGrid();
}
#endif
updateSignal.Emit();
}
void Mesh :: BuildCurvedElements (const Refinement * ref, int aorder, bool arational)
{
GetCurvedElements().BuildCurvedElements (ref, aorder, arational);
for (SegmentIndex seg = 0; seg < GetNSeg(); seg++)
(*this)[seg].SetCurved (GetCurvedElements().IsSegmentCurved (seg));
for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
(*this)[sei].SetCurved (GetCurvedElements().IsSurfaceElementCurved (sei));
for (ElementIndex ei = 0; ei < GetNE(); ei++)
(*this)[ei].SetCurved (GetCurvedElements().IsElementCurved (ei));
SetNextMajorTimeStamp();
}
void Mesh :: BuildCurvedElements (int aorder)
{
if (!GetGeometry())
throw NgException ("don't have a geometry for mesh curving");
GetCurvedElements().BuildCurvedElements (&GetGeometry()->GetRefinement(), aorder, false);
for (SegmentIndex seg = 0; seg < GetNSeg(); seg++)
(*this)[seg].SetCurved (GetCurvedElements().IsSegmentCurved (seg));
for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
(*this)[sei].SetCurved (GetCurvedElements().IsSurfaceElementCurved (sei));
for (ElementIndex ei = 0; ei < GetNE(); ei++)
(*this)[ei].SetCurved (GetCurvedElements().IsElementCurved (ei));
SetNextMajorTimeStamp();
}
void Mesh :: SetMaterial (int domnr, const string & mat)
{
if (domnr > materials.Size())
{
int olds = materials.Size();
materials.SetSize (domnr);
for (int i = olds; i < domnr-1; i++)
materials[i] = new string("default");
}
/*
materials.Elem(domnr) = new char[strlen(mat)+1];
strcpy (materials.Elem(domnr), mat);
*/
materials.Elem(domnr) = new string(mat);
}
string Mesh :: defaultmat = "default";
const string & Mesh :: GetMaterial (int domnr) const
{
if (domnr <= materials.Size())
return *materials.Get(domnr);
static string emptystring("default");
return emptystring;
}
void Mesh ::SetNBCNames ( int nbcn )
{
if ( bcnames.Size() )
for ( int i = 0; i < bcnames.Size(); i++)
if ( bcnames[i] ) delete bcnames[i];
bcnames.SetSize(nbcn);
bcnames = 0;
}
void Mesh ::SetBCName ( int bcnr, const string & abcname )
{
if (bcnr >= bcnames.Size())
{
int oldsize = bcnames.Size();
bcnames.SetSize (bcnr+1); // keeps contents
for (int i = oldsize; i <= bcnr; i++)
bcnames[i] = new string("default");
}
if ( bcnames[bcnr] ) delete bcnames[bcnr];
bcnames[bcnr] = new string ( abcname );
for (auto & fd : facedecoding)
if (fd.BCProperty() <= bcnames.Size())
fd.SetBCName (bcnames[fd.BCProperty()-1]);
}
const string & Mesh ::GetBCName ( int bcnr ) const
{
static string defaultstring = "default";
if ( !bcnames.Size() )
return defaultstring;
if (bcnr < 0 || bcnr >= bcnames.Size())
throw RangeException("Illegal bc number ", bcnr, 0, bcnames.Size());
if ( bcnames[bcnr] )
return *bcnames[bcnr];
else
return defaultstring;
}
void Mesh :: SetNCD2Names( int ncd2n )
{
if (cd2names.Size())
for(int i=0; i<cd2names.Size(); i++)
if(cd2names[i]) delete cd2names[i];
cd2names.SetSize(ncd2n);
cd2names = 0;
}
void Mesh :: SetCD2Name ( int cd2nr, const string & abcname )
{
cd2nr--;
(*testout) << "setCD2Name on edge " << cd2nr << " to " << abcname << endl;
if (cd2nr >= cd2names.Size())
{
int oldsize = cd2names.Size();
cd2names.SetSize(cd2nr+1);
for(int i= oldsize; i<= cd2nr; i++)
cd2names[i] = nullptr;
}
//if (cd2names[cd2nr]) delete cd2names[cd2nr];
if (abcname != "default" && abcname != "")
cd2names[cd2nr] = new string(abcname);
else
cd2names[cd2nr] = nullptr;
}
string Mesh :: cd2_default_name = "default";
string Mesh :: default_bc = "default";
const string & Mesh :: GetCD2Name (int cd2nr) const
{
static string defaultstring = "default";
if (!cd2names.Size())
return defaultstring;
if (cd2nr < 0 || cd2nr >= cd2names.Size())
return defaultstring;
if (cd2names[cd2nr])
return *cd2names[cd2nr];
else
return defaultstring;
}
void Mesh :: SetNCD3Names( int ncd3n )
{
if (cd3names.Size())
for(int i=0; i<cd3names.Size(); i++)
if(cd3names[i]) delete cd3names[i];
cd3names.SetSize(ncd3n);
cd3names = 0;
}
void Mesh :: SetCD3Name ( int cd3nr, const string & abcname )
{
cd3nr--;
(*testout) << "setCD3Name on vertex " << cd3nr << " to " << abcname << endl;
if (cd3nr >= cd3names.Size())
{
int oldsize = cd3names.Size();
cd3names.SetSize(cd3nr+1);
for(int i= oldsize; i<= cd3nr; i++)
cd3names[i] = nullptr;
}
if (abcname != "default")
cd3names[cd3nr] = new string(abcname);
else
cd3names[cd3nr] = nullptr;
}
int Mesh :: AddCD3Name (const string & aname)
{
for (int i = 0; i < cd3names.Size(); i++)
if (*cd3names[i] == aname)
return i;
cd3names.Append (new string(aname));
return cd3names.Size()-1;
}
string Mesh :: cd3_default_name = "default";
const string & Mesh :: GetCD3Name (int cd3nr) const
{
static string defaultstring = "default";
if (!cd3names.Size())
return defaultstring;
if (cd3nr < 0 || cd3nr >= cd3names.Size())
return defaultstring;
if (cd3names[cd3nr])
return *cd3names[cd3nr];
else
return defaultstring;
}
NgArray<string*> & Mesh :: GetRegionNamesCD (int codim)
{
switch (codim)
{
case 0: return materials;
case 1: return bcnames;
case 2: return cd2names;
case 3: return cd3names;
default: throw Exception("don't have regions of co-dimension "+ToString(codim));
}
}
void Mesh :: SetUserData(const char * id, NgArray<int> & data)
{
if(userdata_int.Used(id))
delete userdata_int[id];
NgArray<int> * newdata = new NgArray<int>(data);
userdata_int.Set(id,newdata);
}
bool Mesh :: GetUserData(const char * id, NgArray<int> & data, int shift) const
{
if(userdata_int.Used(id))
{
if(data.Size() < (*userdata_int[id]).Size()+shift)
data.SetSize((*userdata_int[id]).Size()+shift);
for(int i=0; i<(*userdata_int[id]).Size(); i++)
data[i+shift] = (*userdata_int[id])[i];
return true;
}
else
{
data.SetSize(0);
return false;
}
}
void Mesh :: SetUserData(const char * id, NgArray<double> & data)
{
if(userdata_double.Used(id))
delete userdata_double[id];
NgArray<double> * newdata = new NgArray<double>(data);
userdata_double.Set(id,newdata);
}
bool Mesh :: GetUserData(const char * id, NgArray<double> & data, int shift) const
{
if(userdata_double.Used(id))
{
if(data.Size() < (*userdata_double[id]).Size()+shift)
data.SetSize((*userdata_double[id]).Size()+shift);
for(int i=0; i<(*userdata_double[id]).Size(); i++)
data[i+shift] = (*userdata_double[id])[i];
return true;
}
else
{
data.SetSize(0);
return false;
}
}
void Mesh :: PrintMemInfo (ostream & ost) const
{
ost << "Mesh Mem:" << endl;
ost << GetNP() << " Points, of size "
<< sizeof (Point3d) << " + " << sizeof(POINTTYPE) << " = "
<< GetNP() * (sizeof (Point3d) + sizeof(POINTTYPE)) << endl;
ost << GetNSE() << " Surface elements, of size "
<< sizeof (Element2d) << " = "
<< GetNSE() * sizeof(Element2d) << endl;
ost << GetNE() << " Volume elements, of size "
<< sizeof (Element) << " = "
<< GetNE() * sizeof(Element) << endl;
// ost << "surfs on node:";
// surfacesonnode.PrintMemInfo (cout);
ost << "boundaryedges: ";
if (boundaryedges)
boundaryedges->PrintMemInfo (cout);
ost << "surfelementht: ";
if (surfelementht)
surfelementht->PrintMemInfo (cout);
}
shared_ptr<Mesh> Mesh :: Mirror ( netgen::Point<3> p_plane, Vec<3> n_plane )
{
Mesh & m = *this;
auto nm_ = make_shared<Mesh>();
Mesh & nm = *nm_;
nm = m;
Point3d pmin, pmax;
GetBox(pmin, pmax);
auto v = pmax-pmin;
double eps = v.Length()*1e-8;
auto onPlane = [&] (const MeshPoint & p) -> bool
{
auto v = p_plane-p;
auto l = v.Length();
if(l<eps) return true;
auto ret = fabs(v*n_plane)/l;
return fabs(v*n_plane) < eps;
};
/*
auto mirror = [&] (PointIndex pi) -> PointIndex
{
auto & p = m[pi];
auto v = p_plane-p;
auto l = v.Length();
if(l<eps)
return pi;
if(fabs(v*n_plane)/l < eps)
return pi;
auto new_point = p + 2*(v*n_plane)*n_plane;
return nm.AddPoint( new_point, p.GetLayer(), p.Type() );
};
Array<PointIndex, PointIndex> point_map;
point_map.SetSize(GetNP());
point_map = -1;
for(auto pi : Range(points))
point_map[pi] = mirror(pi);
*/
Array<PointIndex, PointIndex> point_map(GetNP());
Array<PointIndex, PointIndex> point_map1(GetNP());
nm.Points().SetSize(0);
for(auto pi : Range(points))
{
auto & p = m[pi];
auto v = p_plane-p;
auto l = v.Length();
if(l < eps || fabs(v*n_plane)/l < eps)
{
auto npi = nm.AddPoint(p, p.GetLayer(), p.Type());
point_map[pi] = npi;
point_map1[pi] = npi;
}
else
{
auto new_point = p + 2*(v*n_plane)*n_plane;
point_map1[pi] = nm.AddPoint(p, p.GetLayer(), p.Type());
point_map[pi] = nm.AddPoint( new_point, p.GetLayer(), p.Type() );
}
}
for(auto & el : nm.VolumeElements())
for(auto i : Range(el.GetNP()))
el[i] = point_map1[el[i]];
for(auto & el : nm.SurfaceElements())
for(auto i : Range(el.GetNP()))
el[i] = point_map1[el[i]];
for(auto & el : nm.LineSegments())
for(auto i : Range(el.GetNP()))
el[i] = point_map1[el[i]];
for(auto & el : VolumeElements())
{
auto nel = el;
for(auto i : Range(el.GetNP()))
nel[i] = point_map[el[i]];
nm.AddVolumeElement(nel);
}
for (auto ei : Range(SurfaceElements()))
{
auto & el = m[ei];
auto nel = el;
for(auto i : Range(el.GetNP()))
nel[i] = point_map[el[i]];
if(!(nel==el))
{
nel.Invert();
nm.AddSurfaceElement(nel);
}
}
for (auto ei : Range(LineSegments()))
{
auto & el = LineSegments()[ei];
auto nel = el;
bool is_same = true;
for(auto i : Range(el.GetNP()))
{
auto pi = el[i];
nel[i] = point_map[pi];
if(point_map[pi]!=pi)
is_same = false;
}
if(!is_same)
nm.AddSegment(nel);
}
nm.ComputeNVertices();
return nm_;
}
}