diff --git a/Common/CommandLine.cpp b/Common/CommandLine.cpp index a2eba0339393a341034cd50a7f4d553d02fd80aa..defc56a3edacc85b28fc298bb74d29b2007e240f 100644 --- a/Common/CommandLine.cpp +++ b/Common/CommandLine.cpp @@ -1,4 +1,4 @@ -// $Id: CommandLine.cpp,v 1.106 2007-09-26 20:51:57 geuzaine Exp $ +// $Id: CommandLine.cpp,v 1.107 2007-11-04 21:03:16 remacle Exp $ // // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle // @@ -90,6 +90,7 @@ void Print_Usage(char *name){ Msg(DIRECT, " -clscale float Set characteristic length scaling factor"); Msg(DIRECT, " -clcurv Compute characteristic lengths from curvatures"); Msg(DIRECT, " -epslc1d Set the accuracy of the evaluation of the LCFIELD for 1D mesh"); + Msg(DIRECT, " -swapangle Set the treshold angle (in degree) between two adjacent faces below which a swap is allowed"); Msg(DIRECT, " -rand float Set random perturbation factor"); Msg(DIRECT, " -bgm file Load background mesh from file"); Msg(DIRECT, " -constrain Constrain background mesh with characteristic lengths"); @@ -368,6 +369,21 @@ void Get_Options(int argc, char *argv[]) exit(1); } } + else if(!strcmp(argv[i] + 1, "swapangle")) { + i++; + if(argv[i] != NULL) { + CTX.mesh.allow_swap_edge_angle = atof(argv[i++]); + if(CTX.mesh.allow_swap_edge_angle <= 0.0) { + fprintf(stderr, ERROR_STR + "Treshold angle for edge swap must be > 0\n"); + exit(1); + } + } + else { + fprintf(stderr, ERROR_STR "Missing number\n"); + exit(1); + } + } else if(!strcmp(argv[i] + 1, "clcurv")) { CTX.mesh.lc_from_curvature = 1; i++; diff --git a/Common/Context.h b/Common/Context.h index 858fc3e8e9ed06ea62f754ac3052e7cbbddd27a6..ca3d3d55cc83a277adce3ffb400382f8f3329296 100644 --- a/Common/Context.h +++ b/Common/Context.h @@ -195,6 +195,7 @@ public : char *triangle_options; int smooth_normals, reverse_all_normals; double angle_smooth_normals; + double allow_swap_edge_angle; } mesh; // post processing options diff --git a/Common/DefaultOptions.h b/Common/DefaultOptions.h index 0a9c4d0476b2aff17bc67823dc29bb288ba19561..53253eb6a25f4fc404be8d0c8523627fd1af954e 100644 --- a/Common/DefaultOptions.h +++ b/Common/DefaultOptions.h @@ -888,10 +888,10 @@ StringXNumber MeshOptions_Number[] = { "3D mesh algorithm (1=Tetgen+Delaunay, 4=Netgen)" }, { F|O, "AngleSmoothNormals" , opt_mesh_angle_smooth_normals , 30.0 , "Threshold angle below which normals are not smoothed" }, - + { F|O, "AllowSwapAngle" , opt_mesh_allow_swap_edge_angle , 10.0 , + "Treshold angle (in degrees) between faces normals under which we allow an edge swap" }, { F|O, "BdfFieldFormat" , opt_mesh_bdf_field_format , 1. , "Field format for Nastran BDF files (0=free, 1=small, 2=large)" }, - { F|O, "CharacteristicLengthFactor" , opt_mesh_lc_factor , 1.0 , "Factor applied to all characteristic lengths" }, { F|O, "CharacteristicLengthFromCurvature" , opt_mesh_lc_from_curvature , 0. , diff --git a/Common/Options.cpp b/Common/Options.cpp index 7a53f9232e51574f78a410e54e7df3f0836481dc..90742cbcfa208192403187cefdc68d41fe3d9f6d 100644 --- a/Common/Options.cpp +++ b/Common/Options.cpp @@ -1,4 +1,4 @@ -// $Id: Options.cpp,v 1.365 2007-10-14 09:51:17 geuzaine Exp $ +// $Id: Options.cpp,v 1.366 2007-11-04 21:03:16 remacle Exp $ // // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle // @@ -4865,6 +4865,13 @@ double opt_mesh_min_circ_points(OPT_ARGS_NUM) return CTX.mesh.min_circ_points; } +double opt_mesh_allow_swap_edge_angle(OPT_ARGS_NUM) +{ + if(action & GMSH_SET) + CTX.mesh.allow_swap_edge_angle = val; + return CTX.mesh.allow_swap_edge_angle; +} + double opt_mesh_min_curv_points(OPT_ARGS_NUM) { if(action & GMSH_SET) diff --git a/Common/Options.h b/Common/Options.h index 2449d31589e8292e8114541ef6897c64aaedf995..864b3aa168d074db3fb91d76678a2619b59c52ba 100644 --- a/Common/Options.h +++ b/Common/Options.h @@ -476,6 +476,7 @@ double opt_mesh_algo3d(OPT_ARGS_NUM); double opt_mesh_lc_integration_precision(OPT_ARGS_NUM); double opt_mesh_recombine_algo(OPT_ARGS_NUM); double opt_mesh_min_circ_points(OPT_ARGS_NUM); +double opt_mesh_allow_swap_edge_angle(OPT_ARGS_NUM); double opt_mesh_min_curv_points(OPT_ARGS_NUM); double opt_mesh_constrained_bgmesh(OPT_ARGS_NUM); double opt_mesh_order(OPT_ARGS_NUM); diff --git a/Geo/GFace.cpp b/Geo/GFace.cpp index 635b2e8abf4588d42e5303ca0fbdbfa60ea03494..7adce0a57ff53f95b4f2468f30465d7a5f561cf4 100644 --- a/Geo/GFace.cpp +++ b/Geo/GFace.cpp @@ -1,4 +1,4 @@ -// $Id: GFace.cpp,v 1.38 2007-10-16 20:00:06 geuzaine Exp $ +// $Id: GFace.cpp,v 1.39 2007-11-04 21:03:17 remacle Exp $ // // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle // @@ -39,6 +39,7 @@ extern Context_T CTX; GFace::GFace(GModel *model, int tag) : GEntity(model, tag), r1(0), r2(0) { + meshStatistics.status = GFace::PENDING; resetMeshAttributes(); } diff --git a/Geo/GFace.h b/Geo/GFace.h index 6305156aebf818388241f033099aff2a9175afb7..9fbf063fe41f2f960f3eb208615c3f0ba21674b3 100644 --- a/Geo/GFace.h +++ b/Geo/GFace.h @@ -179,6 +179,15 @@ class GFace : public GEntity // edge loops } meshAttributes ; + typedef enum {PENDING,DONE,FAILED} meshGenerationStatus ; + struct { + meshGenerationStatus status; + double worst_element_shape, best_element_shape, average_element_shape; + double smallest_edge_length, longest_edge_length, efficiency_index; + int nbEdge, nbTriangle; + int nbGoodQuality,nbGoodLength; + } meshStatistics; + // a crude graphical representation using a "cross" defined by pairs // of start/end points std::vector<SPoint3> cross; diff --git a/Geo/MElement.cpp b/Geo/MElement.cpp index 442984294ded35f32e96313dd54f361e8607844c..eabea2713ffc605b1c1114456f7ef0282bc9ff7c 100644 --- a/Geo/MElement.cpp +++ b/Geo/MElement.cpp @@ -1,4 +1,4 @@ -// $Id: MElement.cpp,v 1.44 2007-10-03 19:40:41 geuzaine Exp $ +// $Id: MElement.cpp,v 1.45 2007-11-04 21:03:17 remacle Exp $ // // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle // @@ -26,6 +26,7 @@ #include "Numeric.h" #include "Message.h" #include "Context.h" +#include "qualityMeasures.h" extern Context_T CTX; @@ -96,6 +97,11 @@ double MElement::rhoShapeMeasure() return 0.; } +double MTriangle::gammaShapeMeasure() +{ + return qmTriangle(this,QMTRI_RHO); +} + double MTetrahedron::gammaShapeMeasure() { double p0[3] = { _v[0]->x(), _v[0]->y(), _v[0]->z() }; diff --git a/Geo/MElement.h b/Geo/MElement.h index 79c9e77e0cfb1e32075a89bf098b4501abaf4a23..1b4630561e5475162585cfc193f9544f488b137f 100644 --- a/Geo/MElement.h +++ b/Geo/MElement.h @@ -326,6 +326,7 @@ class MTriangle : public MElement { mat[1][1] = _v[2]->y() - _v[0]->y(); } void circumcenterXY(double *res) const; + virtual double gammaShapeMeasure(); void circumcenterUV(GFace*,double *res); static void circumcenterXYZ(double *p1, double *p2, double *p3, double *res, double *uv = 0); diff --git a/Geo/OCCEdge.cpp b/Geo/OCCEdge.cpp index 67e1e0072854befcb0c393886d51cd15a56ba52f..cb5c930fbd3983f51563fed7a91b83a27874e61c 100644 --- a/Geo/OCCEdge.cpp +++ b/Geo/OCCEdge.cpp @@ -1,4 +1,4 @@ -// $Id: OCCEdge.cpp,v 1.24 2007-10-14 09:51:17 geuzaine Exp $ +// $Id: OCCEdge.cpp,v 1.25 2007-11-04 21:03:17 remacle Exp $ // // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle // @@ -58,6 +58,7 @@ void OCCEdge::setTrimmed (OCCFace *f) } } +// Plugin Netgen Salome ! SPoint2 OCCEdge::reparamOnFace(GFace *face, double epar,int dir) const { const TopoDS_Face *s = (TopoDS_Face*) face->getNativePtr(); @@ -106,9 +107,11 @@ int OCCEdge::isSeam(GFace *face) const { const TopoDS_Face *s = (TopoDS_Face*) face->getNativePtr(); BRepAdaptor_Surface surface(*s); - if(surface.IsUPeriodic() || surface.IsVPeriodic()){ + // printf("asking if edge %d is a seam of face %d\n",tag(),face->tag()); + // printf("periodic %d %d\n",surface.IsUPeriodic(),surface.IsVPeriodic()); + // if(surface.IsUPeriodic() || surface.IsVPeriodic()){ return BRep_Tool::IsClosed(c, *s); - } + // } return 0; } diff --git a/Mesh/BDS.cpp b/Mesh/BDS.cpp index f41a834dd2a1abc93934b3ca1646d6763af441f4..ab92212aa10d4b8f36b28ccfa3d685c00fa955eb 100644 --- a/Mesh/BDS.cpp +++ b/Mesh/BDS.cpp @@ -1,4 +1,4 @@ -// $Id: BDS.cpp,v 1.83 2007-10-14 19:54:16 remacle Exp $ +// $Id: BDS.cpp,v 1.84 2007-11-04 21:03:17 remacle Exp $ // // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle // @@ -26,6 +26,7 @@ #include "BDS.h" #include "Message.h" #include "GFace.h" +#include "qualityMeasures.h" bool test_move_point_parametric_triangle (BDS_Point * p, double u, double v, BDS_Face *t); @@ -82,12 +83,7 @@ void normal_triangle(BDS_Point * p1, BDS_Point * p2, BDS_Point * p3, double c[3]) { vector_triangle(p1, p2, p3, c); - double l = sqrt(c[0] * c[0] + c[1] * c[1] + c[2] * c[2]); - if(l == 0) - return; - c[0] /= l; - c[1] /= l; - c[2] /= l; + norme(c); } double surface_triangle(BDS_Point * p1, BDS_Point * p2, BDS_Point * p3) @@ -104,21 +100,6 @@ double surface_triangle_param(BDS_Point * p1, BDS_Point * p2, BDS_Point * p3) return (0.5 * c); } -double quality_triangle(BDS_Point * p1, BDS_Point * p2, BDS_Point * p3) -{ - double e12 = ((p1->X - p2->X) * (p1->X - p2->X) + - (p1->Y - p2->Y) * (p1->Y - p2->Y) + - (p1->Z - p2->Z) * (p1->Z - p2->Z)); - double e22 = ((p1->X - p3->X) * (p1->X - p3->X) + - (p1->Y - p3->Y) * (p1->Y - p3->Y) + - (p1->Z - p3->Z) * (p1->Z - p3->Z)); - double e32 = ((p3->X - p2->X) * (p3->X - p2->X) + - (p3->Y - p2->Y) * (p3->Y - p2->Y) + - (p3->Z - p2->Z) * (p3->Z - p2->Z)); - double a = surface_triangle(p1, p2, p3); - return a / (e12 + e22 + e32); -} - void BDS_Point::getTriangles(std::list < BDS_Face * >&t) const { t.clear(); @@ -530,7 +511,7 @@ BDS_Mesh ::~ BDS_Mesh () } -void BDS_Mesh::split_edge(BDS_Edge * e, BDS_Point *mid) +bool BDS_Mesh::split_edge(BDS_Edge * e, BDS_Point *mid) { /* p1 @@ -552,6 +533,13 @@ void BDS_Mesh::split_edge(BDS_Edge * e, BDS_Point *mid) BDS_Point *p1 = e->p1; BDS_Point *p2 = e->p2; + e->oppositeof(op); + +// double l1 = sqrt((op[0]->X-op[1]->X) *(op[0]->X-op[1]->X) + +// (op[0]->Y-op[1]->Y) *(op[0]->Y-op[1]->Y) + +// (op[0]->Z-op[1]->Z) *(op[0]->Z-op[1]->Z) ); +// if (l1 < 0.5* mid->lc()) return false; + BDS_Point *pts1[4]; e->faces(0)->getNodes(pts1); @@ -568,7 +556,6 @@ void BDS_Mesh::split_edge(BDS_Edge * e, BDS_Point *mid) } // we should project - e->oppositeof(op); BDS_GeomEntity *g1 = 0, *g2 = 0, *ge = e->g; BDS_Edge *p1_op1 = find_edge(p1, op[0], e->faces(0)); @@ -642,6 +629,7 @@ void BDS_Mesh::split_edge(BDS_Edge * e, BDS_Point *mid) p2->config_modified = true; op[0]->config_modified = true; op[1]->config_modified = true; + return true; } @@ -1160,16 +1148,32 @@ bool BDS_Mesh::smooth_point_centroid(BDS_Point * p, GFace *gf) V /= (3.*sTot); LC /= (3.*sTot); + GPoint gp = gf->point(U*scalingU,V*scalingV); + + const double oldX = p->X; + const double oldY = p->Y; + const double oldZ = p->Z; + + double oldWorst=1.; + double newWorst=1.; + it = ts.begin(); while(it != ite) { BDS_Face *t = *it; if (!test_move_point_parametric_triangle ( p, U, V, t)) - return false; + return false; +// // p->X = gp.x(); +// // p->Y = gp.y(); +// // p->Z = gp.z(); +// // newWorst = std::min(newWorst,qmTriangle(*it,QMTRI_RHO)); +// // p->X = oldX; +// // p->Y = oldY; +// // p->Z = oldZ; +// // oldWorst = std::min(oldWorst,qmTriangle(*it,QMTRI_RHO)); ++it; } + // if (oldWorst > newWorst)return false; - - GPoint gp = gf->point(U*scalingU,V*scalingV); p->u = U; p->v = V; p->lc() = LC; @@ -1190,10 +1194,10 @@ bool BDS_Mesh::smooth_point_parametric(BDS_Point * p, GFace *gf) if (!p->config_modified)return false; if(p->g && p->g->classif_degree <= 1) return false; - + double U = 0; double V = 0; - double tot_length = 0; + double tot_length = 0; double LC = 0; std::list < BDS_Edge * >::iterator eit = p->edges.begin(); @@ -1208,8 +1212,6 @@ bool BDS_Mesh::smooth_point_parametric(BDS_Point * p, GFace *gf) ++eit; } - //U /= (p->edges.size()); - //V /= (p->edges.size()); U /= tot_length; V /= tot_length; LC /= p->edges.size(); diff --git a/Mesh/BDS.h b/Mesh/BDS.h index d3b7c743103dc5e2595acf797392b84fd12df4ce..6043487b1888c5eaa5859a6e947bbabd33a4aacd 100644 --- a/Mesh/BDS.h +++ b/Mesh/BDS.h @@ -45,7 +45,6 @@ void vector_triangle(BDS_Point *p1, BDS_Point *p2, BDS_Point *p3, double c[3]); void normal_triangle(BDS_Point *p1, BDS_Point *p2, BDS_Point *p3, double c[3]); double surface_triangle(BDS_Point *p1, BDS_Point *p2, BDS_Point *p3); double surface_triangle_param(BDS_Point *p1, BDS_Point *p2, BDS_Point *p3); -double quality_triangle(BDS_Point *p1, BDS_Point *p2, BDS_Point *p3); class BDS_GeomEntity @@ -456,7 +455,7 @@ public: bool smooth_point_parametric(BDS_Point * p, GFace *gf); bool smooth_point_centroid(BDS_Point * p, GFace *gf); bool move_point(BDS_Point *p , double X, double Y, double Z); - void split_edge(BDS_Edge *, BDS_Point *); + bool split_edge(BDS_Edge *, BDS_Point *); void saturate_edge(BDS_Edge *, std::vector<BDS_Point *>&); bool edge_constraint ( BDS_Point *p1, BDS_Point *p2 ); bool recombine_edge ( BDS_Edge *e ); diff --git a/Mesh/Generator.cpp b/Mesh/Generator.cpp index 85604ef54ba52c025984d4821f1797a899e5ac45..1ac4bb2b3b9d60e840d8c472bff278157fda4cbb 100644 --- a/Mesh/Generator.cpp +++ b/Mesh/Generator.cpp @@ -1,4 +1,4 @@ -// $Id: Generator.cpp,v 1.123 2007-09-10 04:47:04 geuzaine Exp $ +// $Id: Generator.cpp,v 1.124 2007-11-04 21:03:17 remacle Exp $ // // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle // @@ -177,6 +177,42 @@ void Mesh1D(GModel *m) Msg(STATUS1, "Mesh"); } +void PrintMesh2dStatistics (GModel *m) +{ + double worst=1,best=0,avg=0; + double e_long=0,e_short=1.e22,e_avg=0; + int nTotT=0,nTotE=0,nTotGoodLength=0,nTotGoodQuality=0; + Msg(INFO,"2D Mesh Statistics :"); + for (GModel::fiter it = m->firstFace() ; it!=m->lastFace(); ++it) + { + worst = std::min((*it)->meshStatistics.worst_element_shape,worst); + best = std::max((*it)->meshStatistics.best_element_shape,best); + avg += (*it)->meshStatistics.average_element_shape * (*it)->meshStatistics.nbTriangle ; + + e_avg += (*it)->meshStatistics.efficiency_index ;//* (*it)->meshStatistics.nbEdge; + + e_long = std::max((*it)->meshStatistics.longest_edge_length,e_long); + e_short = std::min((*it)->meshStatistics.smallest_edge_length,e_short); + + if ((*it)->meshStatistics.worst_element_shape < 0.05) + Msg(INFO,"Badly Shaped Element (rho = %12.5E) on GFace %d (%d triangles)",(*it)->meshStatistics.worst_element_shape,(*it)->tag(),(*it)->meshStatistics.nbTriangle); + if ((*it)->meshStatistics.status == GFace::FAILED) + Msg(INFO,"GMSH was unable to mesh GFace %d ",(*it)->tag()); + + nTotT += (*it)->meshStatistics.nbTriangle ; + nTotE += (*it)->meshStatistics.nbEdge ; + nTotGoodLength += (*it)->meshStatistics.nbGoodLength ; + nTotGoodQuality+= (*it)->meshStatistics.nbGoodQuality ; + } + + Msg(INFO,"Element Quality (%d triangles) : avg %8.7f best %8.7f worst %8.7f greaterthan90 %d (%12.5E\\%)", + nTotT,avg/(double)nTotT,best,worst,nTotGoodQuality,(double)nTotGoodQuality/nTotT); + Msg(INFO,"Size Field Accuracy (%d edges) : %8.7f (should be as close as 1 as possible) in good brackets %d (%12.5E\%)", + nTotE,exp(e_avg/(double)nTotE),nTotGoodLength,(double)nTotGoodLength/nTotE); + + +} + void Mesh2D(GModel *m) { if(TooManyElements(m, 2)) return; @@ -198,6 +234,8 @@ void Mesh2D(GModel *m) // field generated from the surface mesh of the source surfaces if(!Mesh2DWithBoundaryLayers(m)) std::for_each(m->firstFace(), m->lastFace(), meshGFace()); + + PrintMesh2dStatistics(m); double t2 = Cpu(); CTX.mesh_timer[1] = t2 - t1; diff --git a/Mesh/Makefile b/Mesh/Makefile index 7131843c4332c1cc2e879825bd1d5ce042c6963e..f802a394610c51e312fbffe38363d2e7dfe9f94d 100644 --- a/Mesh/Makefile +++ b/Mesh/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.184 2007-09-22 20:35:18 geuzaine Exp $ +# $Id: Makefile,v 1.185 2007-11-04 21:03:17 remacle Exp $ # # Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle # @@ -46,6 +46,7 @@ SRC = Generator.cpp \ meshGRegionCarveHole.cpp \ DivideAndConquer.cpp \ BackgroundMesh.cpp \ + qualityMeasures.cpp \ BoundaryLayer.cpp \ BDS.cpp \ HighOrder.cpp diff --git a/Mesh/meshGFace.cpp b/Mesh/meshGFace.cpp index 60cd78c686aea8c13b77ace5d0778964ef0e9188..500f16f1b384398e4d1313d3e08f9cc415a9fe7e 100644 --- a/Mesh/meshGFace.cpp +++ b/Mesh/meshGFace.cpp @@ -1,4 +1,4 @@ -// $Id: meshGFace.cpp,v 1.99 2007-10-16 20:00:07 geuzaine Exp $ +// $Id: meshGFace.cpp,v 1.100 2007-11-04 21:03:17 remacle Exp $ // // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle // @@ -34,6 +34,7 @@ #include "Message.h" #include "Numeric.h" #include "BDS.h" +#include "qualityMeasures.h" extern Context_T CTX; @@ -263,35 +264,6 @@ double NewGetLc(BDS_Point *p1,BDS_Point *p2, GFace *f) return 2*linearLength / (l1 + l2); } -bool edgeSwapTest(BDS_Edge *e,GFace *gf) -{ - BDS_Point *op[2]; - - if(!e->p1->config_modified && ! e->p2->config_modified) return false; - - if(e->numfaces() != 2) return false; - - e->oppositeof (op); - - double edgeLength1, edgeLength2; - - edgeLength1 = computeEdgeLinearLength(e,gf); - edgeLength2 = computeEdgeLinearLength(op[0], op[1],gf); - double lp1 = NewGetLc(e->p1); - double lp2 = NewGetLc(e->p1); - double lo1 = NewGetLc(op[0]); - double lo2 = NewGetLc(op[1]); - - - double el1 = 2*edgeLength1 / (lp1 + lp2); - double el2 = 2*edgeLength2 / (lo1 + lo2); - - double q1 = fabs(1-el1); - double q2 = fabs(1-el2); - - return q2 < 0.5*q1; -} - void fourthPoint (double *p1, double *p2, double *p3, double *p4) { double c[3]; @@ -308,11 +280,49 @@ void fourthPoint (double *p1, double *p2, double *p3, double *p4) p4[2] = c[2] + R * vz[2]; } -bool edgeSwapTestDelaunay(BDS_Edge *e,GFace *gf) + +bool edgeSwapTestAngle(BDS_Edge *e, double min_cos) { + BDS_Face *f1 = e->faces(0); + BDS_Face *f2 = e->faces(1); + BDS_Point *n1[4]; + BDS_Point *n2[4]; + f1->getNodes(n1); + f2->getNodes(n2); + double norm1[3]; + double norm2[3]; + normal_triangle(n1[0],n1[1],n1[2],norm1); + normal_triangle(n2[0],n2[1],n2[2],norm2); + double cosa;prosca (norm1,norm2,&cosa); + return cosa > min_cos; +} + +int edgeSwapTestQuality(BDS_Edge *e, double fact = 1.1) +{ + BDS_Point *op[2]; + + if(!e->p1->config_modified && ! e->p2->config_modified) return 0; + + if(e->numfaces() != 2) return 0; + + e->oppositeof (op); - if (CTX.mesh.algo2d == ALGO_2D_MESHADAPT) - return edgeSwapTest (e,gf); + if (! edgeSwapTestAngle(e, cos(CTX.mesh.allow_swap_edge_angle*M_PI/180.)) ) return -1; + + + double qa1 = qmTriangle(e->p1, e->p2, op[0],QMTRI_RHO); + double qa2 = qmTriangle(e->p1, e->p2, op[1],QMTRI_RHO); + double qb1 = qmTriangle(e->p1, op[0], op[1],QMTRI_RHO); + double qb2 = qmTriangle(e->p2, op[0], op[1],QMTRI_RHO); + double qa = std::min(qa1,qa2); + double qb = std::min(qb1,qb2); + if(qb > fact*qa) return 1; + if(qb < qa/fact) return -1; + return 0; +} + +bool edgeSwapTestDelaunay(BDS_Edge *e,GFace *gf) +{ BDS_Point *op[2]; @@ -334,28 +344,6 @@ bool edgeSwapTestDelaunay(BDS_Edge *e,GFace *gf) return result > 0.; } - -int edgeSwapTestQuality(BDS_Edge *e, double fact = 1.1) -{ - BDS_Point *op[2]; - - if(!e->p1->config_modified && ! e->p2->config_modified) return false; - - if(e->numfaces() != 2) return false; - - e->oppositeof (op); - - double qa1 = quality_triangle(e->p1, e->p2, op[0]); - double qa2 = quality_triangle(e->p1, e->p2, op[1]); - double qb1 = quality_triangle(e->p1, op[0], op[1]); - double qb2 = quality_triangle(e->p2, op[0], op[1]); - double qa = (qa1<qa2) ? qa1 : qa2; - double qb = (qb1<qb2) ? qb1 : qb2; - if(qb > fact*qa) return 1; - if(qb < qa/fact) return -1; - return 0; -} - void OptimizeMesh(GFace *gf, BDS_Mesh &m, const int NIT) { // optimize @@ -387,10 +375,12 @@ void OptimizeMesh(GFace *gf, BDS_Mesh &m, const int NIT) if (NN2++ >= NN1)break; if (!(*it)->deleted) { - int result = edgeSwapTestQuality(*it,5); - if (result >= 0) - if(edgeSwapTestDelaunay(*it,gf)) - m.swap_edge ( *it , BDS_SwapEdgeTestParametric()); + const double qual = CTX.mesh.algo2d == ALGO_2D_MESHADAPT ? 1 : 5; + int result = edgeSwapTestQuality(*it,qual); + if (CTX.mesh.algo2d == ALGO_2D_MESHADAPT && result == 1) + m.swap_edge ( *it , BDS_SwapEdgeTestParametric()); + else if ( result >= 0 && edgeSwapTestDelaunay(*it,gf)) + m.swap_edge ( *it , BDS_SwapEdgeTestParametric()); } ++it; } @@ -412,13 +402,12 @@ void swapEdgePass ( GFace *gf, BDS_Mesh &m, int &nb_swap ) // result = 1 => oblige to swap because the quality is greatly improved if (!(*it)->deleted) { - int result = edgeSwapTestQuality(*it,5); - if (result >= 0) - { - if(edgeSwapTestDelaunay(*it,gf) || result > 0) - if (m.swap_edge ( *it , BDS_SwapEdgeTestParametric())) - nb_swap++; - } + const double qual = CTX.mesh.algo2d == ALGO_2D_MESHADAPT ? 1 : 5; + int result = edgeSwapTestQuality(*it,qual); + if (CTX.mesh.algo2d == ALGO_2D_MESHADAPT && result == 1) + { if (m.swap_edge ( *it , BDS_SwapEdgeTestParametric()))nb_swap++; } + else if ( result >= 0 && edgeSwapTestDelaunay(*it,gf)) + { if (m.swap_edge ( *it , BDS_SwapEdgeTestParametric())) nb_swap++; } } ++it; } @@ -455,8 +444,8 @@ void splitEdgePass ( GFace *gf, BDS_Mesh &m, double MAXE_, int &nb_split) mid->X,mid->Y,mid->Z); //mid->lc() = 2./ ( 1./(*it)->p1->lc() + 1./(*it)->p2->lc() ); mid->lc() = 0.5 * ( (*it)->p1->lc() + (*it)->p2->lc() ); - m.split_edge ( *it, mid ); - nb_split++; + if(!m.split_edge ( *it, mid )) m.del_point(mid); + else nb_split++; } } ++it; @@ -536,6 +525,77 @@ void collapseEdgePass ( GFace *gf, BDS_Mesh &m, double MINE_, int MAXNP, int &nb } +void computeMeshSizeFieldAccuracy (GFace *gf, BDS_Mesh &m, double &avg, double &max_e, double &min_e, int &nE, int &GS) +{ + std::list<BDS_Edge*>::iterator it = m.edges.begin(); + avg=0; + min_e = 1.e22; + max_e = 0; + nE = 0; + GS = 0; + double oneoversqr2 = 1./sqrt(2); + double sqr2 = sqrt(2); + while (it!= m.edges.end()) + { + if (!(*it)->deleted) + { + double lone = NewGetLc ( *it,gf); + if (lone > oneoversqr2 && lone < sqr2 ) GS++; + + avg+=lone>1 ? (1./lone) - 1. : lone - 1.; + max_e = std::max(max_e,lone); + min_e = std::min(min_e,lone); + nE++; + } + ++it; + } +} + +bool computeElementShapes (GFace *gf, BDS_Mesh &m, double &worst, double &avg, double &best, int &nT, int &nbGQ) +{ + std::list<BDS_Face*>::iterator it = m.triangles.begin(); + worst = 1.e22; + avg = 0.0; + best = 0.0; + nT = 0; + nbGQ = 0; + while (it!= m.triangles.end()) + { + if (!(*it)->deleted) + { + double q = qmTriangle(*it,QMTRI_RHO); + if (q>.9) nbGQ++; + avg+=q; + worst = std::min(worst,q); + best = std::max(best,q); + nT++; + } + ++it; + } + avg /= nT; +} + + +bool computeElementShapes (GFace *gf, double &worst, double &avg, double &best, int &nT, int &greaterThan ) +{ + worst = 1.e22; + avg = 0.0; + best = 0.0; + nT = 0; + greaterThan = 0; + for (int i=0;i<gf->triangles.size();i++) + { + double q = qmTriangle(gf->triangles[i],QMTRI_RHO); + if (q>.9)greaterThan++; + avg+=q; + worst = std::min(worst,q); + best = std::max(best,q); + nT++; + } + avg /= nT; +} + + void smoothVertexPass ( GFace *gf, BDS_Mesh &m, int &nb_smooth) { std::set<BDS_Point*,PointLessThan>::iterator itp = m.points.begin(); @@ -563,11 +623,16 @@ void RefineMesh ( GFace *gf, BDS_Mesh &m , const int NIT) std::list<BDS_Edge*>::iterator it = (*itp)->edges.begin(); std::list<BDS_Edge*>::iterator ite = (*itp)->edges.end(); double L = 1.e22; + int ne = 0; while(it!=ite){ double l = (*it)->length(); - if (l<L && (*it)->g && (*it)->g->classif_degree == 1)L=l; + if ((*it)->g && (*it)->g->classif_degree == 1){ + L=std::min(L,l); + ne++; + } ++it; } + if (!ne) L = 1.e22; if(!CTX.mesh.constrained_bgmesh) (*itp)->lc() = L; (*itp)->lcBGM() = L; @@ -581,6 +646,8 @@ void RefineMesh ( GFace *gf, BDS_Mesh &m , const int NIT) double t_spl=0, t_sw=0,t_col=0,t_sm=0; const double MINE_ = 0.67, MAXE_=1.4; + + double mesh_quality = 0; while (1) { // we count the number of local mesh modifs. @@ -622,7 +689,7 @@ void RefineMesh ( GFace *gf, BDS_Mesh &m , const int NIT) splitEdgePass ( gf, m, maxE, nb_split); //saturateEdgePass ( gf, m, maxE, nb_split); clock_t t2 = clock(); - // swapEdgePass ( gf, m, nb_swap); + swapEdgePass ( gf, m, nb_swap); clock_t t3 = clock(); collapseEdgePass ( gf, m, minE , MAXNP, nb_collaps); clock_t t4 = clock(); @@ -634,7 +701,6 @@ void RefineMesh ( GFace *gf, BDS_Mesh &m , const int NIT) clock_t t7 = clock(); // clean up the mesh - t_spl += (double)(t2-t1)/CLOCKS_PER_SEC; t_sw += (double)(t3-t2)/CLOCKS_PER_SEC; t_sw += (double)(t5-t4)/CLOCKS_PER_SEC; @@ -642,11 +708,15 @@ void RefineMesh ( GFace *gf, BDS_Mesh &m , const int NIT) t_col += (double)(t4-t3)/CLOCKS_PER_SEC; t_sm += (double)(t6-t5)/CLOCKS_PER_SEC; + double smallest, longest, old_mesh_quality = mesh_quality;int nE,NG; + computeMeshSizeFieldAccuracy (gf, m, mesh_quality, smallest, longest,nE,NG); + double worst,avg,best; int nT,GT; computeElementShapes (gf, m, worst, avg,best,nT,GT); m.cleanup(); IT++; - Msg(DEBUG1," iter %3d minL %8.3f/%8.3f maxL %8.3f/%8.3f : %6d splits, %6d swaps, %6d collapses, %6d moves",IT,minL,minE,maxL,maxE,nb_split,nb_swap,nb_collaps,nb_smooth); + Msg(DEBUG1," iter %3d minL %8.3f/%8.3f maxL %8.3f/%8.3f : %6d splits, %6d swaps, %6d collapses, %6d moves, accuracy %7.5f, shapes (worst %7.6f, avg %7.6f, best %7.6f) ",IT,minL,minE,maxL,maxE,nb_split,nb_swap,nb_collaps,nb_smooth, exp(mesh_quality/nE), worst,avg/nT,best); if (nb_split==0 && nb_collaps == 0)break; + if (mesh_quality < old_mesh_quality && worst > 0.1 && maxL < 1.45) break; } double t_total = t_spl + t_sw + t_col + t_sm; @@ -1139,9 +1209,9 @@ bool gmsh2DMeshGenerator ( GFace *gf , int RECUR_ITER, bool debug = true) if (!AlgoDelaunay2D ( gf )) { RefineMesh (gf,*m, CTX.mesh.refine_steps); -// OptimizeMesh(gf, *m, 2); -// RefineMesh (gf,*m, -CTX.mesh.refine_steps); -// OptimizeMesh(gf, *m, 2); + OptimizeMesh(gf, *m, 2); + RefineMesh (gf,*m, -CTX.mesh.refine_steps); + if (gf->meshAttributes.recombine) { m->recombineIntoQuads (gf->meshAttributes.recombineAngle,gf); @@ -1154,6 +1224,9 @@ bool gmsh2DMeshGenerator ( GFace *gf , int RECUR_ITER, bool debug = true) // outputScalarField(m->triangles, name,0); // fill the small gmsh structures + computeMeshSizeFieldAccuracy (gf,*m, gf->meshStatistics.efficiency_index,gf->meshStatistics.longest_edge_length,gf->meshStatistics.smallest_edge_length,gf->meshStatistics.nbEdge,gf->meshStatistics.nbGoodLength); + + gf->meshStatistics.status = GFace::DONE; { std::set<BDS_Point*,PointLessThan>::iterator itp = m->points.begin(); while (itp != m->points.end()) @@ -1216,6 +1289,8 @@ bool gmsh2DMeshGenerator ( GFace *gf , int RECUR_ITER, bool debug = true) delete [] U_; delete [] V_; + computeElementShapes (gf, gf->meshStatistics.worst_element_shape,gf->meshStatistics.average_element_shape,gf->meshStatistics.best_element_shape,gf->meshStatistics.nbTriangle,gf->meshStatistics.nbGoodQuality); + return true; } @@ -1239,7 +1314,7 @@ bool buildConsecutiveListOfVertices ( GFace *gf, SBoundingBox3d &bbox, BDS_Mesh *m, std::map<BDS_Point*,MVertex*> &recover_map, - int &count) + int &count, double tol) { // for each edge, we build a list of points that @@ -1249,7 +1324,9 @@ bool buildConsecutiveListOfVertices ( GFace *gf, // printf("new edge loop\n"); std::map<GEntity*,std::vector<SPoint2> > meshes; - std::map<GEntity*,std::vector<SPoint2> > meshes_seam; + std::map<GEntity*,std::vector<SPoint2> > meshes_seam; + + result.clear(); GEdgeLoop::iter it = gel.begin(); @@ -1294,8 +1371,6 @@ bool buildConsecutiveListOfVertices ( GFace *gf, SPoint2 last_coord(0,0); int counter = 0; - double tol = 1.e-7; - while (unordered.size()) { std::list<GEdgeSigned>::iterator it = unordered.begin(); @@ -1325,7 +1400,7 @@ bool buildConsecutiveListOfVertices ( GFace *gf, { SPoint2 first_coord = mesh1d[0]; double d = dist2(last_coord,first_coord); - // printf("d = %12.5E %d\n",d, coords.size()); + // printf("d = %12.5E %d\n",d, coords.size()); if (d < tol) { coords.clear(); @@ -1336,10 +1411,10 @@ bool buildConsecutiveListOfVertices ( GFace *gf, } SPoint2 first_coord_reversed = mesh1d_reversed[0]; double d_reversed = dist2(last_coord,first_coord_reversed); - // printf("d_r = %12.5E\n",d_reversed); + // printf("d_r = %12.5E\n",d_reversed); if (d_reversed < tol) { - // printf("d_r = %12.5E\n",d_reversed); + // printf("d_r = %12.5E\n",d_reversed); coords.clear(); coords = mesh1d_reversed; found = (GEdgeSigned(-1,ge)); @@ -1351,7 +1426,7 @@ bool buildConsecutiveListOfVertices ( GFace *gf, SPoint2 first_coord_seam = mesh1d_seam[0]; SPoint2 first_coord_seam_reversed = mesh1d_seam_reversed[0]; double d_seam = dist2(last_coord,first_coord_seam); - // printf("d_seam = %12.5E\n",d_seam); + // printf("d_seam = %12.5E\n",d_seam); if (d_seam < tol) { coords.clear(); @@ -1361,7 +1436,7 @@ bool buildConsecutiveListOfVertices ( GFace *gf, goto Finalize; } double d_seam_reversed = dist2(last_coord,first_coord_seam_reversed); - // printf("d_seam_reversed = %12.5E\n",d_seam_reversed); + // printf("d_seam_reversed = %12.5E\n",d_seam_reversed); if (d_seam_reversed < tol) { coords.clear(); @@ -1377,8 +1452,6 @@ bool buildConsecutiveListOfVertices ( GFace *gf, } Finalize: - - if (coords.size() == 0)return false; std::vector<MVertex*> edgeLoop; @@ -1455,27 +1528,26 @@ bool buildConsecutiveListOfVertices ( GFace *gf, bool gmsh2DMeshGeneratorPeriodic ( GFace *gf , bool debug = true) { - // if(gf->tag() != 6)return true; - + // if (gf->tag() != 32) return true; std::map<BDS_Point*,MVertex*> recover_map; Range<double> rangeU = gf->parBounds(0); Range<double> rangeV = gf->parBounds(1); - const double du = rangeU.high() -rangeU.low(); - const double dv = rangeV.high() -rangeV.low(); + double du = rangeU.high() -rangeU.low(); + double dv = rangeV.high() -rangeV.low(); const double LC2D = sqrt ( du*du + dv*dv ); - // printf("LC2D %g (%g,%g), (%g,%g)\n",LC2D,rangeU.high(),rangeU.low(),rangeV.high(),rangeV.low()); + // printf("LC2D %g du %g (%g %g) dv %g\n",LC2D,du,rangeU.high(),rangeU.low(),dv); // Buid a BDS_Mesh structure that is convenient for doing the actual meshing procedure BDS_Mesh *m = new BDS_Mesh; - m->scalingU = fabs(du); - m->scalingV = fabs(dv); - SCALINGU = m->scalingU; - SCALINGV = m->scalingV; + m->scalingU = fabs(du); + m->scalingV = fabs(dv); + SCALINGU = m->scalingU; + SCALINGV = m->scalingV; std::vector< std::vector<BDS_Point* > > edgeLoops_BDS; SBoundingBox3d bbox; int nbPointsTotal = 0; @@ -1483,7 +1555,14 @@ bool gmsh2DMeshGeneratorPeriodic ( GFace *gf , bool debug = true) for (std::list<GEdgeLoop>::iterator it = gf->edgeLoops.begin() ; it != gf->edgeLoops.end() ; it++) { std::vector<BDS_Point* > edgeLoop_BDS; - if(buildConsecutiveListOfVertices ( gf, *it , edgeLoop_BDS, bbox, m, recover_map , nbPointsTotal)==false)return false; + // if(buildConsecutiveListOfVertices ( gf, *it , edgeLoop_BDS, bbox, m, recover_map , nbPointsTotal, 1.e-7*LC2D)==false) + // if(buildConsecutiveListOfVertices ( gf, *it , edgeLoop_BDS, bbox, m, recover_map , nbPointsTotal, 1.e-6*LC2D)==false) + if(buildConsecutiveListOfVertices ( gf, *it , edgeLoop_BDS, bbox, m, recover_map , nbPointsTotal, 1.e-7*LC2D)==false) + { + gf->meshStatistics.status = GFace::FAILED; + Msg(GERROR,"The 1D Mesh seems not to be forming a closed loop"); + return false; + } edgeLoops_BDS.push_back(edgeLoop_BDS); } } @@ -1554,7 +1633,6 @@ bool gmsh2DMeshGeneratorPeriodic ( GFace *gf , bool debug = true) free (doc.delaunay); - // Recover the boundary edges // and compute characteristic lenghts using mesh edge spacing @@ -1593,6 +1671,7 @@ bool gmsh2DMeshGeneratorPeriodic ( GFace *gf , bool debug = true) if (!e) { Msg(GERROR,"impossible to recover the edge %d %d",edgeLoop_BDS[j]->iD,edgeLoop_BDS[(j+1)%edgeLoop_BDS.size()]->iD); + gf->meshStatistics.status = GFace::FAILED; SCALINGU = SCALINGV = 1; return false; } @@ -1693,10 +1772,13 @@ bool gmsh2DMeshGeneratorPeriodic ( GFace *gf , bool debug = true) { m->recombineIntoQuads (gf->meshAttributes.recombineAngle,gf); } + // compute mesh statistics + computeMeshSizeFieldAccuracy (gf,*m, gf->meshStatistics.efficiency_index,gf->meshStatistics.longest_edge_length,gf->meshStatistics.smallest_edge_length,gf->meshStatistics.nbEdge,gf->meshStatistics.nbGoodLength); + gf->meshStatistics.status = GFace::DONE; } - // fill the small gmsh structures - + + // fill the small gmsh structures { std::set<BDS_Point*,PointLessThan>::iterator itp = m->points.begin(); while (itp != m->points.end()) @@ -1760,6 +1842,7 @@ bool gmsh2DMeshGeneratorPeriodic ( GFace *gf , bool debug = true) delete m; SCALINGU = SCALINGV = 1; + computeElementShapes (gf, gf->meshStatistics.worst_element_shape,gf->meshStatistics.average_element_shape,gf->meshStatistics.best_element_shape,gf->meshStatistics.nbTriangle,gf->meshStatistics.nbGoodQuality); return true; } @@ -1775,6 +1858,10 @@ void deMeshGFace::operator() (GFace *gf) for (unsigned int i=0;i<gf->quadrangles.size();i++) delete gf->quadrangles[i]; gf->quadrangles.clear(); gf->deleteVertexArrays(); + + gf->meshStatistics.status = GFace::PENDING; + gf->meshStatistics.nbTriangle = gf->meshStatistics.nbEdge = 0; + } void meshGFace::operator() (GFace *gf) diff --git a/Mesh/meshGRegion.cpp b/Mesh/meshGRegion.cpp index 1c5cad48be07250633a89b628a5a9bfe33c04a4d..5af64a19655bd5c0ba08414cfa52fc37c6d7d482 100644 --- a/Mesh/meshGRegion.cpp +++ b/Mesh/meshGRegion.cpp @@ -1,4 +1,4 @@ -// $Id: meshGRegion.cpp,v 1.33 2007-09-12 20:14:35 geuzaine Exp $ +// $Id: meshGRegion.cpp,v 1.34 2007-11-04 21:03:17 remacle Exp $ // // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle // @@ -229,6 +229,7 @@ void MeshDelaunayVolume(std::vector<GRegion*> ®ions) // now do insertion of points insertVerticesInRegion(gr); + Msg(INFO, "Gmsh 3D Delaunay has generated %d tets", gr->tetrahedra.size()); #endif } diff --git a/Mesh/meshGRegionDelaunayInsertion.cpp b/Mesh/meshGRegionDelaunayInsertion.cpp index 7018ced6575d705915a8090e5791e083fb86acb3..ff243ebb1e6ec2fb0071debb858be9eba3877986 100644 --- a/Mesh/meshGRegionDelaunayInsertion.cpp +++ b/Mesh/meshGRegionDelaunayInsertion.cpp @@ -1,4 +1,4 @@ -// $Id: meshGRegionDelaunayInsertion.cpp,v 1.18 2007-09-04 13:47:02 remacle Exp $ +// $Id: meshGRegionDelaunayInsertion.cpp,v 1.19 2007-11-04 21:03:17 remacle Exp $ // // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle // @@ -130,6 +130,7 @@ void recurFindCavity ( std::list<faceXtet> & shell, bool insertVertex (MVertex *v , MTet4 *t , + MTet4Factory &myFactory, std::set<MTet4*,compareTet4Ptr> &allTets, std::vector<double> & vSizes) { @@ -211,7 +212,7 @@ bool insertVertex (MVertex *v , // it->v[2]->y(), // it->v[2]->z()); - MTet4 *t4 = new MTet4 ( tr , vSizes); + MTet4 *t4 = myFactory.Create ( tr , vSizes); t4->setOnWhat(t->onWhat()); newTets[k++]=t4; // all new tets are pushed front in order to @@ -237,14 +238,21 @@ bool insertVertex (MVertex *v , { connectTets ( new_cavity.begin(),new_cavity.end() ); allTets.insert(newTets,newTets+shell.size()); - //connectTets ( allTets.begin(),allTets.end() ); + +// ittet = cavity.begin(); +// ittete = cavity.end(); +// while ( ittet != ittete ) +// { +// myFactory.Free (*ittet); +// ++ittet; +// } delete [] newTets; return true; } // The cavity is NOT star shaped else { - for (unsigned int i=0;i<shell.size();i++)delete newTets[i]; + for (unsigned int i=0;i<shell.size();i++)myFactory.Free(newTets[i]); delete [] newTets; ittet = cavity.begin(); ittete = cavity.end(); @@ -410,10 +418,12 @@ void recur_classify ( MTet4 *t , void insertVerticesInRegion (GRegion *gr) { - std::set<MTet4*,compareTet4Ptr> allTets; - std::set<MTet4*> voidTets; + printf("sizeof MTet4 = %d sizeof MTetrahedron %d sizeof(MVertex) %d\n",sizeof(MTet4),sizeof(MTetrahedron), sizeof(MVertex)); + std::map<MVertex*,double> vSizesMap; std::vector<double> vSizes; + MTet4Factory myFactory(1600000) ; + std::set<MTet4*,compareTet4Ptr> &allTets = myFactory.getAllTets(); for (unsigned int i=0;i<gr->tetrahedra.size();i++)setLcs ( gr->tetrahedra[i] , vSizesMap); @@ -425,14 +435,14 @@ void insertVerticesInRegion (GRegion *gr) } for (unsigned int i=0;i<gr->tetrahedra.size();i++) - allTets.insert ( new MTet4 ( gr->tetrahedra[i] ,vSizes ) ); + allTets.insert(myFactory.Create ( gr->tetrahedra[i] ,vSizes )); gr->tetrahedra.clear(); connectTets ( allTets.begin(), allTets.end() ); // classify the tets on the right region // Msg (INFO,"reclassifying %d tets",allTets.size()); - for (std::set<MTet4*,compareTet4Ptr>::iterator it = allTets.begin();it!=allTets.end();++it) + for (MTet4Factory::iterator it = allTets.begin();it!=allTets.end();++it) { if (!(*it)->onWhat()) { @@ -452,12 +462,12 @@ void insertVerticesInRegion (GRegion *gr) } - for (std::set<MTet4*,compareTet4Ptr>::iterator it2 = allTets.begin();it2!=allTets.end();++it2) + for (MTet4Factory::iterator it = allTets.begin();it!=allTets.end();++it) { - (*it2)->setNeigh(0,0); - (*it2)->setNeigh(1,0); - (*it2)->setNeigh(2,0); - (*it2)->setNeigh(3,0); + (*it)->setNeigh(0,0); + (*it)->setNeigh(1,0); + (*it)->setNeigh(2,0); + (*it)->setNeigh(3,0); } connectTets ( allTets.begin(), allTets.end() ); Msg(DEBUG,"All %d tets were connected",allTets.size()); @@ -472,13 +482,12 @@ void insertVerticesInRegion (GRegion *gr) Msg(GERROR, "No tetrahedra in region %d", gr->tag()); break; } - + MTet4 *worst = *allTets.begin(); - + if (worst->isDeleted()) { - delete worst->tet(); - delete worst; + myFactory.Free(worst); allTets.erase(allTets.begin()); // Msg(INFO,"Worst tet is deleted"); } @@ -503,41 +512,48 @@ void insertVerticesInRegion (GRegion *gr) double lc = std::min(lc1,BGM_MeshSize(gr,0,0,center[0],center[1],center[2])); vSizes.push_back(lc); // compute mesh spacing there - if (!insertVertex ( v , worst, allTets,vSizes)) + if (!insertVertex ( v , worst, myFactory,allTets,vSizes)) { - allTets.erase(allTets.begin()); - worst->forceRadius(0); - allTets.insert(worst); + myFactory.changeTetRadius(allTets.begin(),0.0); delete v; } else - { - v->onWhat()->mesh_vertices.push_back(v); - } + v->onWhat()->mesh_vertices.push_back(v); } else + myFactory.changeTetRadius(allTets.begin(),0.0); + } + // Normally, a tet mesh contains about 5 to 6 times more tets than + // vertices + // This allows to clean up the set of tets when lots of deleted ones + // are present in the mesh + if(allTets.size() > 7 * vSizes.size()) + { + int n1 = allTets.size(); + for(std::set<MTet4*,compareTet4Ptr>::iterator itd = allTets.begin() ; itd !=allTets.end() ; ++itd) { - // Msg(INFO,"Point is outside"); - allTets.erase(allTets.begin()); - worst->forceRadius(0); - allTets.insert(worst); + if ((*itd)->isDeleted()) + { + myFactory.Free((*itd)); + std::set<MTet4*,compareTet4Ptr>::iterator itdb = itd; + itd++; + allTets.erase(itdb); + } } + Msg(INFO,"cleaning up the memory %d -> %d ",n1,allTets.size()); } } - + while (1) { if (allTets.begin() == allTets.end() ) break; MTet4 *worst = *allTets.begin(); - if (worst->isDeleted()) - { - delete worst->tet(); - } - else + if (!worst->isDeleted()) { worst->onWhat()->tetrahedra.push_back(worst->tet()); + worst->tet() = 0; } - delete worst; + myFactory.Free(worst); allTets.erase(allTets.begin()); } } diff --git a/Mesh/meshGRegionDelaunayInsertion.h b/Mesh/meshGRegionDelaunayInsertion.h index 11e972fa40878a915f9f95f3afd13f0fc30142a8..9309625ea2c4f683b443d0ed7d5899f4b21bf249 100644 --- a/Mesh/meshGRegionDelaunayInsertion.h +++ b/Mesh/meshGRegionDelaunayInsertion.h @@ -23,28 +23,46 @@ #include "MElement.h" #include <list> #include <set> +#include <stack> +//#define _GMSH_PRE_ALLOCATE_STRATEGY_ 1 class GRegion; +class MTet4Factory; + class MTet4 { + friend class MTet4Factory; + // a total of 36 Bytes for a MTet4 + // Normally it should take 36 MByte in excess per million of tets + // 36 MB for 10e6 Tets + // Each MTetrahedron has a size of 28 Bytes + // The total memory required for a tet is 64 Bytes + // i.e. 64 MB per 10e6 Tet + // Yet, we store also a rb tree containing all pointers + // sorted with respect to tet radius. + // each bucket of the tree contain 4 pointers, i.e. 16 Bytes plus + // the data. We have therefor an extra cost of 20 Bytes/Tet + // i.e. 84 MB/ million tets + // A MVertex has a cost of 40 Bytes and there are about 200000 of them + // per million tet: a new cost of 8MB/10e6 tet : total 92 MB/10e6 tet + // (I observe 160M MB !!!) + bool deleted; double circum_radius; MTetrahedron *base; MTet4 *neigh[4]; GRegion *gr; - public : - inline GRegion * onWhat () const {return gr;} - inline void setOnWhat (GRegion *g) {gr=g;} - - bool isDeleted () const {return deleted;} - void forceRadius (double r){circum_radius=r;} - inline double getRadius ()const {return circum_radius;} - + ~MTet4 (){} + MTet4 () : deleted(false), base (0), gr(0),circum_radius(0.0) + { + neigh[0] = neigh[1] = neigh[2] = neigh[3] = 0; + } - MTet4 ( MTetrahedron * t, std::vector<double> & sizes) : deleted(false), base (t), gr(0) + void setup ( MTetrahedron * t, std::vector<double> & sizes) { + base = t; neigh[0] = neigh[1] = neigh[2] = neigh[3] = 0; double center[3]; base->circumcenter(center); @@ -58,9 +76,22 @@ class MTet4 sizes [base->getVertex(2)->getNum()]+ sizes [base->getVertex(3)->getNum()]); circum_radius /= lc; + deleted = false; } + public : + + inline GRegion * onWhat () const {return gr;} + inline void setOnWhat (GRegion *g) {gr=g;} + + bool isDeleted () const {return deleted;} + void forceRadius (double r){circum_radius=r;} + inline double getRadius ()const {return circum_radius;} + + + inline MTetrahedron * tet() const {return base;} + inline MTetrahedron * &tet() {return base;} inline void setNeigh (int iN , MTet4 *n) {neigh[iN]=n;} inline MTet4 *getNeigh (int iN ) const {return neigh[iN];} int inCircumSphere ( const double *p ) const; @@ -77,6 +108,7 @@ class MTet4 double getVolume () const { return base -> getVolume() ; }; inline void setDeleted (bool d) { + // circum_radius = d ? fabs(circum_radius) : fabs(circum_radius) deleted = d; } inline bool assertNeigh() const @@ -110,4 +142,81 @@ class compareTet4Ptr } }; +class MTet4Factory +{ +public: + typedef std::set<MTet4*,compareTet4Ptr> container; + typedef container::iterator iterator; +private: + container allTets; +#ifdef _GMSH_PRE_ALLOCATE_STRATEGY_ + MTet4* allSlots; + int s_last, s_alloc; + std::stack<MTet4*> emptySlots; + + inline MTet4 * getANewSlot() + { + if (s_last >= s_alloc)return 0; + MTet4 * t = &(allSlots[s_last]); + s_last++; + return t; + } + inline MTet4 * getAnEmptySlot() + { + if(!emptySlots.empty()) + { + MTet4* t = emptySlots.top(); + emptySlots.pop(); + return t; + } + return getANewSlot(); + }; +#endif + + + public : + MTet4Factory (int _size = 1000000) + { +#ifdef _GMSH_PRE_ALLOCATE_STRATEGY_ + s_last=0; s_alloc=_size; + allSlots = new MTet4 [s_alloc]; +#endif + } + ~MTet4Factory () { +#ifdef _GMSH_PRE_ALLOCATE_STRATEGY_ + delete [] allSlots; +#endif + } + MTet4 * Create (MTetrahedron * t, std::vector<double> & sizes) + { +#ifdef _GMSH_PRE_ALLOCATE_STRATEGY_ + MTet4 * t4 = getAnEmptySlot(); +#else + MTet4 * t4 = new MTet4; +#endif + t4->setup(t,sizes); + return t4; + } + void Free (MTet4* t) + { + if (t->tet()) delete t->tet(); + t->tet() = 0; +#ifdef _GMSH_PRE_ALLOCATE_STRATEGY_ + emptySlots.push(t); + t->setDeleted(true); +#else + delete t; +#endif + } + + void changeTetRadius ( iterator it , double r) + { + allTets.erase(it); + (*it)->forceRadius(r); + allTets.insert(*it); + } + container & getAllTets () {return allTets;} + +}; + #endif diff --git a/Mesh/qualityMeasures.cpp b/Mesh/qualityMeasures.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a97e3d811d579486668d52a8594d3e6dfe033cea --- /dev/null +++ b/Mesh/qualityMeasures.cpp @@ -0,0 +1,66 @@ +#include "qualityMeasures.h" +#include "BDS.h" +#include "MVertex.h" +#include "MElement.h" +#include "Numeric.h" + + +double qmTriangle ( const BDS_Point *p1, const BDS_Point *p2, const BDS_Point *p3, const gmshQualityMeasure4Triangle &cr) +{ + return qmTriangle (p1->X,p1->Y,p1->Z,p2->X,p2->Y,p2->Z,p3->X,p3->Y,p3->Z,cr); +} + +double qmTriangle ( BDS_Face *t, const gmshQualityMeasure4Triangle &cr) +{ + BDS_Point *n[4]; + t->getNodes(n); + return qmTriangle (n[0],n[1],n[2],cr); +} + +double qmTriangle ( MTriangle*t, const gmshQualityMeasure4Triangle &cr) +{ + return qmTriangle (t->getVertex(0),t->getVertex(1),t->getVertex(2),cr); +} + +double qmTriangle ( const MVertex *v1, const MVertex *v2, const MVertex *v3, const gmshQualityMeasure4Triangle &cr) +{ + return qmTriangle (v1->x(),v1->y(),v1->z(),v2->x(),v2->y(),v2->z(),v3->x(),v3->y(),v3->z(),cr); +} + +// Triangle abc +// quality is between 0 and 1 + +double qmTriangle ( const double &xa, const double &ya, const double &za, + const double &xb, const double &yb, const double &zb, + const double &xc, const double &yc, const double &zc, + const gmshQualityMeasure4Triangle &cr) +{ + double quality; + switch(cr) + { + case QMTRI_RHO: + { + // quality = rho / R = 2 * inscribed radius / circumradius + + double a [3] = {xc-xb,yc-yb,zc-zb}; + double b [3] = {xa-xc,ya-yc,za-zc}; + double c [3] = {xb-xa,yb-ya,zb-za}; + + const double la = norme (a); + const double lb = norme (b); + const double lc = norme (c); + + double pva [3]; prodve (b,c,pva); const double sina = norm3 (pva); + double pvb [3]; prodve (c,a,pvb); const double sinb = norm3 (pvb); + double pvc [3]; prodve (a,b,pvc); const double sinc = norm3 (pvc); + + if (sina == 0.0 && sinb == 0.0 && sinc == 0.0) quality = 0.0; + else quality = 2 * (2*sina*sinb*sinc/(sina + sinb + sinc) ); + } + break; + default: + throw; + } + return quality; +} + diff --git a/Mesh/qualityMeasures.h b/Mesh/qualityMeasures.h new file mode 100644 index 0000000000000000000000000000000000000000..02429b4fb89da054386de2dde1ab5887fa3bd27e --- /dev/null +++ b/Mesh/qualityMeasures.h @@ -0,0 +1,20 @@ +#ifndef _QUALITY_MEASURES_H_ +#define _QUALITY_MEASURES_H_ + +class BDS_Point; +class BDS_Face; +class MVertex; +class MTriangle; +enum gmshQualityMeasure4Triangle {QMTRI_RHO}; + +double qmTriangle ( MTriangle *f, const gmshQualityMeasure4Triangle &cr); +double qmTriangle ( BDS_Face *f, const gmshQualityMeasure4Triangle &cr); +double qmTriangle ( const BDS_Point *p1, const BDS_Point *p2, const BDS_Point *p3, const gmshQualityMeasure4Triangle &cr); +double qmTriangle ( const MVertex *v1, const MVertex *v2, const MVertex *v3, const gmshQualityMeasure4Triangle &cr); +double qmTriangle ( const double *d1, const double *d2, const double *d3, const gmshQualityMeasure4Triangle &cr); +double qmTriangle ( const double &x1, const double &y1, const double &z1, + const double &x2, const double &y2, const double &z2, + const double &x3, const double &y3, const double &z3, + const gmshQualityMeasure4Triangle &cr); + +#endif