Merge branch 'fix_conform_segments' into 'master'

Fixes to conform to free segments

See merge request ngsolve/netgen!700
This commit is contained in:
Hochsteger, Matthias 2025-03-06 19:11:34 +01:00
commit 2778b934e6
3 changed files with 53 additions and 38 deletions

View File

@ -2194,7 +2194,7 @@ void MeshOptimize3d :: SwapImproveSurface (
double MeshOptimize3d :: SwapImprove2 ( ElementIndex eli1, int face, double MeshOptimize3d :: SwapImprove2 ( ElementIndex eli1, int face,
Table<ElementIndex, PointIndex> & elementsonnode, Table<ElementIndex, PointIndex> & elementsonnode,
DynamicTable<SurfaceElementIndex, PointIndex> & belementsonnode, bool check_only ) DynamicTable<SurfaceElementIndex, PointIndex> & belementsonnode, bool conform_segments, bool check_only )
{ {
PointIndex pi1, pi2, pi3, pi4, pi5; PointIndex pi1, pi2, pi3, pi4, pi5;
Element el21(TET), el22(TET), el31(TET), el32(TET), el33(TET); Element el21(TET), el22(TET), el31(TET), el32(TET), el33(TET);
@ -2228,6 +2228,8 @@ double MeshOptimize3d :: SwapImprove2 ( ElementIndex eli1, int face,
} }
if(!conform_segments)
{
bool bface = 0; bool bface = 0;
for (int k = 0; k < belementsonnode[pi1].Size(); k++) for (int k = 0; k < belementsonnode[pi1].Size(); k++)
{ {
@ -2250,6 +2252,7 @@ double MeshOptimize3d :: SwapImprove2 ( ElementIndex eli1, int face,
} }
if (bface) return 0.0; if (bface) return 0.0;
}
FlatArray<ElementIndex> row = elementsonnode[pi1]; FlatArray<ElementIndex> row = elementsonnode[pi1];
@ -2367,11 +2370,11 @@ double MeshOptimize3d :: SwapImprove2 ( ElementIndex eli1, int face,
2 -> 3 conversion 2 -> 3 conversion
*/ */
void MeshOptimize3d :: SwapImprove2 () void MeshOptimize3d :: SwapImprove2 (bool conform_segments)
{ {
static Timer t("MeshOptimize3d::SwapImprove2"); RegionTimer reg(t); static Timer t("MeshOptimize3d::SwapImprove2"); RegionTimer reg(t);
if (goal == OPT_CONFORM) return; if (!conform_segments && goal == OPT_CONFORM) return;
mesh.BuildBoundaryEdges(false); mesh.BuildBoundaryEdges(false);
@ -2433,7 +2436,7 @@ void MeshOptimize3d :: SwapImprove2 ()
for (int j = 0; j < 4; j++) for (int j = 0; j < 4; j++)
{ {
double d_badness = SwapImprove2( eli1, j, elementsonnode, belementsonnode, true); double d_badness = SwapImprove2( eli1, j, elementsonnode, belementsonnode, conform_segments, true);
if(d_badness<0.0) if(d_badness<0.0)
my_faces_with_improvement.Append( std::make_tuple(d_badness, eli1, j) ); my_faces_with_improvement.Append( std::make_tuple(d_badness, eli1, j) );
} }
@ -2449,7 +2452,7 @@ void MeshOptimize3d :: SwapImprove2 ()
{ {
if(mesh[eli].IsDeleted()) if(mesh[eli].IsDeleted())
continue; continue;
if(SwapImprove2( eli, j, elementsonnode, belementsonnode, false) < 0.0) if(SwapImprove2( eli, j, elementsonnode, belementsonnode, conform_segments, false) < 0.0)
cnt++; cnt++;
} }

View File

@ -45,8 +45,8 @@ public:
void SwapImprove (const TBitArray<ElementIndex> * working_elements = NULL); void SwapImprove (const TBitArray<ElementIndex> * working_elements = NULL);
void SwapImproveSurface (const TBitArray<ElementIndex> * working_elements = NULL, void SwapImproveSurface (const TBitArray<ElementIndex> * working_elements = NULL,
const NgArray< idmap_type* > * idmaps = NULL); const NgArray< idmap_type* > * idmaps = NULL);
void SwapImprove2 (); void SwapImprove2 (bool conform_segments = false);
double SwapImprove2 (ElementIndex eli1, int face, Table<ElementIndex, PointIndex> & elementsonnode, DynamicTable<SurfaceElementIndex, PointIndex> & belementsonnode, bool check_only=false ); double SwapImprove2 (ElementIndex eli1, int face, Table<ElementIndex, PointIndex> & elementsonnode, DynamicTable<SurfaceElementIndex, PointIndex> & belementsonnode, bool conform_segments, bool check_only=false );
void ImproveMesh() { mesh.ImproveMesh(mp, goal); } void ImproveMesh() { mesh.ImproveMesh(mp, goal); }

View File

@ -783,7 +783,7 @@ namespace netgen
free_segs.Append(segi); free_segs.Append(segi);
auto get_nonconforming = [&] (const auto & p2el) { auto get_nonconforming = [&] (const auto & p2el) {
Array<size_t> nonconforming; Array<SegmentIndex> nonconforming;
for (auto segi : free_segs) { for (auto segi : free_segs) {
auto seg = mesh[segi]; auto seg = mesh[segi];
@ -805,16 +805,6 @@ namespace netgen
auto split_segment = [&] (SegmentIndex segi, const auto & p2el) { auto split_segment = [&] (SegmentIndex segi, const auto & p2el) {
auto seg = mesh[segi]; auto seg = mesh[segi];
auto p_new = Center(mesh[seg[0]], mesh[seg[1]]); auto p_new = Center(mesh[seg[0]], mesh[seg[1]]);
auto pi_new = mesh.AddPoint(p_new);
auto seg_new0 = seg;
auto seg_new1 = seg;
seg_new0[1] = pi_new;
seg_new1[0] = pi_new;
mesh[segi][0] = PointIndex::INVALID;
mesh.AddSegment(seg_new0);
mesh.AddSegment(seg_new1);
double lam[3]; double lam[3];
ElementIndex ei_start = mesh.GetElementOfPoint(p_new, lam, false, domain); ElementIndex ei_start = mesh.GetElementOfPoint(p_new, lam, false, domain);
@ -824,6 +814,9 @@ namespace netgen
} }
ei_start -= 1; ei_start -= 1;
if(mesh[ei_start].IsDeleted())
return;
double max_inside = -1.; double max_inside = -1.;
ElementIndex ei_max_inside = -1; ElementIndex ei_max_inside = -1;
@ -832,6 +825,9 @@ namespace netgen
for(auto pi : mesh[ei_start].PNums()) { for(auto pi : mesh[ei_start].PNums()) {
for(auto ei1 : p2el[pi]) { for(auto ei1 : p2el[pi]) {
double lam[3]; double lam[3];
if(mesh[ei1].IsDeleted())
return;
if(!mesh.PointContainedIn3DElement(p_new, lam, ei1+1)) if(!mesh.PointContainedIn3DElement(p_new, lam, ei1+1))
continue; continue;
@ -843,18 +839,34 @@ namespace netgen
} }
} }
if(max_inside < 1e-4) {
PrintMessage(3, "Could not find volume element with new point inside");
return;
}
// split tet into 4 new tests, with new point inside // split tet into 4 new tests, with new point inside
auto el = mesh[ei_max_inside]; auto el = mesh[ei_max_inside];
if(el.GetNP() != 4) { if(el.GetNP() != 4) {
PrintMessage(1, "Only tet elements are supported to split around free segments"); PrintMessage(3, "Only tet elements are supported to split around free segments");
return; return;
} }
if(el.IsDeleted()) { if(el.IsDeleted()) {
PrintMessage(1,"Element to split is already deleted"); PrintMessage(3,"Element to split is already deleted");
return; return;
} }
auto pi_new = mesh.AddPoint(p_new);
auto seg_new0 = seg;
auto seg_new1 = seg;
seg_new0[1] = pi_new;
seg_new1[0] = pi_new;
mesh[segi][0] = PointIndex::INVALID;
mesh.AddSegment(seg_new0);
mesh.AddSegment(seg_new1);
int pmap[4][4] = { int pmap[4][4] = {
{0,1,2,4}, {0,1,2,4},
{1,3,2,4}, {1,3,2,4},
@ -897,7 +909,7 @@ namespace netgen
for ([[maybe_unused]] auto i : Range(3)) { for ([[maybe_unused]] auto i : Range(3)) {
optmesh.ImproveMesh(); optmesh.ImproveMesh();
optmesh.SwapImprove2 (); optmesh.SwapImprove2(true);
optmesh.ImproveMesh(); optmesh.ImproveMesh();
optmesh.SwapImprove(); optmesh.SwapImprove();
optmesh.ImproveMesh(); optmesh.ImproveMesh();
@ -910,7 +922,7 @@ namespace netgen
auto bad_segs = get_nonconforming(p2el); auto bad_segs = get_nonconforming(p2el);
if(bad_segs.Size() > 0) { if(bad_segs.Size() > 0) {
auto bad_seg = mesh[free_segs[bad_segs[0]]]; auto bad_seg = mesh[bad_segs[0]];
if(debugparam.write_mesh_on_error) if(debugparam.write_mesh_on_error)
mesh.Save("free_segment_not_conformed_dom_"+ToString(domain)+"_seg_"+ToString(bad_seg[0])+"_"+ToString(bad_seg[1])+".vol.gz"); mesh.Save("free_segment_not_conformed_dom_"+ToString(domain)+"_seg_"+ToString(bad_seg[0])+"_"+ToString(bad_seg[1])+".vol.gz");
throw Exception("Segment not resolved in volume mesh in domain " + ToString(domain)+ ", seg: " + ToString(bad_seg)); throw Exception("Segment not resolved in volume mesh in domain " + ToString(domain)+ ", seg: " + ToString(bad_seg));