Newer
Older
// $Id: Generator.cpp,v 1.143 2008-05-04 08:31:15 geuzaine Exp $
// Copyright (C) 1997-2008 C. Geuzaine, J.-F. Remacle
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software

Christophe Geuzaine
committed
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA.
//
#include "Numeric.h"
#include "Context.h"
#include "BoundaryLayers.h"

Christophe Geuzaine
committed
#if !defined(HAVE_NO_POST)
#include "PView.h"
#include "PViewData.h"

Christophe Geuzaine
committed
#endif
extern Context_T CTX;
double &gamma, double &gammaMin, double &gammaMax,
double &eta, double &etaMin, double &etaMax,
double &rho, double &rhoMin, double &rhoMax,
double quality[3][100])
for(unsigned int i = 0; i < ele.size(); i++){
double g = ele[i]->gammaShapeMeasure();
gamma += g;
gammaMin = std::min(gammaMin, g);
gammaMax = std::max(gammaMax, g);
double e = ele[i]->etaShapeMeasure();
eta += e;
etaMin = std::min(etaMin, e);
etaMax = std::max(etaMax, e);
double r = ele[i]->rhoShapeMeasure();
rho += r;
rhoMin = std::min(rhoMin, r);
rhoMax = std::max(rhoMax, r);
for(int j = 0; j < 100; j++){
if(g > j / 100. && g <= (j + 1) / 100.) quality[0][j]++;
if(e > j / 100. && e <= (j + 1) / 100.) quality[1][j]++;
if(r > j / 100. && r <= (j + 1) / 100.) quality[2][j]++;
}
}
void GetStatistics(double stat[50], double quality[3][100])
for(int i = 0; i < 50; i++) stat[i] = 0.;
stat[0] = m->getNumVertices();
stat[1] = m->getNumEdges();
stat[2] = m->getNumFaces();
stat[3] = m->getNumRegions();
std::map<int, std::vector<GEntity*> > physicals[4];
stat[45] = physicals[0].size() + physicals[1].size() +
physicals[2].size() + physicals[3].size();
for(GModel::eiter it = m->firstEdge(); it != m->lastEdge(); ++it)
stat[4] += (*it)->mesh_vertices.size();
for(GModel::fiter it = m->firstFace(); it != m->lastFace(); ++it){
stat[5] += (*it)->mesh_vertices.size();
stat[7] += (*it)->triangles.size();
stat[8] += (*it)->quadrangles.size();
}
for(GModel::riter it = m->firstRegion(); it != m->lastRegion(); ++it){
stat[6] += (*it)->mesh_vertices.size();
stat[9] += (*it)->tetrahedra.size();
stat[10] += (*it)->hexahedra.size();
stat[11] += (*it)->prisms.size();
stat[12] += (*it)->pyramids.size();
}
stat[13] = CTX.mesh_timer[0];
stat[14] = CTX.mesh_timer[1];
stat[15] = CTX.mesh_timer[2];
if(quality){
for(int i = 0; i < 3; i++)
for(int j = 0; j < 100; j++)
double gamma=0., gammaMin=1., gammaMax=0.;
double eta=0., etaMin=1., etaMax=0.;
double rho=0., rhoMin=1., rhoMax=0.;
for(GModel::riter it = m->firstRegion(); it != m->lastRegion(); ++it){
GetQualityMeasure((*it)->tetrahedra, gamma, gammaMin, gammaMax,
GetQualityMeasure((*it)->hexahedra, gamma, gammaMin, gammaMax,
GetQualityMeasure((*it)->prisms, gamma, gammaMin, gammaMax,
GetQualityMeasure((*it)->pyramids, gamma, gammaMin, gammaMax,
double N = stat[9] + stat[10] + stat[11] + stat[12];
stat[17] = N ? gamma / N : 0.;
stat[18] = gammaMin;
stat[19] = gammaMax;
stat[20] = N ? eta / N : 0.;
stat[21] = etaMin;
stat[22] = etaMax;
stat[23] = N ? rho / N : 0;
stat[24] = rhoMin;
stat[25] = rhoMax;

Christophe Geuzaine
committed
#if !defined(HAVE_NO_POST)
stat[26] = PView::list.size();
for(unsigned int i = 0; i < PView::list.size(); i++) {
PViewData *data = PView::list[i]->getData();
stat[27] += data->getNumPoints();
stat[28] += data->getNumLines();
stat[29] += data->getNumTriangles();
stat[30] += data->getNumQuadrangles();
stat[31] += data->getNumTetrahedra();
stat[32] += data->getNumHexahedra();
stat[33] += data->getNumPrisms();
stat[34] += data->getNumPyramids();

Christophe Geuzaine
committed
#endif
if(CTX.expert_mode || !m->getNumVertices()) return false;
// try to detect obvious mistakes in characteristic lenghts (one of
// the most common cause for erroneous bug reports on the mailing
// list)
for(GModel::viter it = m->firstVertex(); it != m->lastVertex(); ++it)
sumAllLc += (*it)->prescribedMeshSizeAtVertex();
sumAllLc /= (double)m->getNumVertices();
if(!sumAllLc || pow(CTX.lc / sumAllLc, dim) > 1.e10)
return !Msg::GetBinaryAnswer
("Your choice of characteristic lengths will likely produce\n"
"a very large mesh. Do you really want to continue?\n\n"
"(To disable this warning in the future, select `Enable\n"
"expert mode' in the option dialog.)",
"Continue", "Cancel");
}
Msg::Status(1, true, "Meshing 1D...");
double t1 = Cpu();
std::for_each(m->firstEdge(), m->lastEdge(), meshGEdge());
double t2 = Cpu();
Msg::Info("Mesh 1D complete (%g s)", CTX.mesh_timer[0]);
Msg::Status(1, true, "Mesh");
}
if(CTX.create_append_statreport == 1)
statreport = fopen(CTX.statreport, "w");
else if(CTX.create_append_statreport == 2)
statreport = fopen(CTX.statreport, "a");
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;
int nUnmeshed = 0, numFaces = 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.status == GFace::FAILED ||
nTotT += (*it)->meshStatistics.nbTriangle;
nTotE += (*it)->meshStatistics.nbEdge;
nTotGoodLength += (*it)->meshStatistics.nbGoodLength;
nTotGoodQuality += (*it)->meshStatistics.nbGoodQuality;
numFaces++;
}
if(CTX.create_append_statreport == 1){
fprintf(statreport, "2D stats\tname\t\t#faces\t\t#fail\t\t"
"#t\t\tQavg\t\tQbest\t\tQworst\t\t#Q>90\t\t#Q>90/#t\t"
"#e\t\ttau\t\t#Egood\t\t#Egood/#e\tCPU\n");
fprintf(statreport,"\t%16s\t%d\t\t%d\t\t", CTX.base_filename, numFaces, nUnmeshed);
fprintf(statreport,"%d\t\t%8.7f\t%8.7f\t%8.7f\t%d\t\t%8.7f\t",
nTotT, avg / (double)nTotT, best, worst, nTotGoodQuality,
(double)nTotGoodQuality / nTotT);
fprintf(statreport,"%d\t\t%8.7f\t%d\t\t%8.7f\t%8.1f\n",
nTotE, exp(e_avg / (double)nTotE), nTotGoodLength,
(double)nTotGoodLength / nTotE, CTX.mesh_timer[1]);
if(!CTX.expert_mode && (CTX.mesh.algo2d == ALGO_2D_DELAUNAY ||
CTX.mesh.algo2d == ALGO_2D_FRONTAL)){
if(!Msg::GetBinaryAnswer
("The 2D Delaunay and Frontal algorithms are still experimental\n"
"and produce triangles with random orientations. Do you really\n"
"want to continue?\n\n"
"(To disable this warning in the future, select `Enable expert\n"
"mode' in the option dialog.)",
"Continue", "Cancel")) return;
Msg::Status(1, true, "Meshing 2D...");
double t1 = Cpu();
// boundary layers are special: their generation (including vertices
// and curve meshes) is global as it depends on a smooth normal
// field generated from the surface mesh of the source surfaces
if(!Mesh2DWithBoundaryLayers(m)){
std::for_each(m->firstFace(), m->lastFace(), meshGFace());
int nIter = 0;
while(1){
meshGFace mesher;
int nbPending = 0;
for(GModel::fiter it = m->firstFace() ; it!=m->lastFace(); ++it){
if ((*it)->meshStatistics.status == GFace::PENDING){
mesher(*it);
nbPending++;
}
double t2 = Cpu();
Msg::Info("Mesh 2D complete (%g s)", CTX.mesh_timer[1]);
Msg::Status(1, true, "Mesh");
}
{
// FIXME: need to split region vector into connected components here!
connected.push_back(delaunay);
}
Msg::Status(1, true, "Meshing 3D...");
double t1 = Cpu();
std::for_each(m->firstRegion(), m->lastRegion(), meshGRegionExtruded());
// then subdivide if necessary (unfortunately the subdivision is a
// global operation, which can require changing the surface mesh!)
// then mesh all the non-delaunay regions
std::vector<GRegion*> delaunay;
std::for_each(m->firstRegion(), m->lastRegion(), meshGRegion(delaunay));
// and finally mesh the delaunay regions (again, this is global; but
// we mesh each connected part separately for performance and mesh
// quality reasons)
std::vector<std::vector<GRegion*> > connected;
FindConnectedRegions(delaunay, connected);
for(unsigned int i = 0; i < connected.size(); i++)
MeshDelaunayVolume(connected[i]);
double t2 = Cpu();
Msg::Info("Mesh 3D complete (%g s)", CTX.mesh_timer[2]);
Msg::Status(1, true, "Mesh");
}
void OptimizeMeshNetgen(GModel *m)
{
Msg::Status(1, true, "Optimizing 3D with Netgen...");
double t1 = Cpu();
std::for_each(m->firstRegion(), m->lastRegion(), optimizeMeshGRegionNetgen());
double t2 = Cpu();
Msg::Info("Mesh 3D optimization with Netgen complete (%g s)", t2 - t1);
Msg::Status(1, true, "Mesh");
Msg::Status(1, true, "Optimizing 3D...");
std::for_each(m->firstRegion(), m->lastRegion(), optimizeMeshGRegionGmsh());
Msg::Info("Mesh 3D optimization complete (%g s)", t2 - t1);
Msg::Status(1, true, "Mesh");
Msg::Status(1, true, "Adapting the 3D Mesh...");
double t1 = Cpu();
if(CTX.threads_lock) {
Msg::Info("I'm busy! Ask me that later...");
return;
}
CTX.threads_lock = 1;
std::for_each(m->firstRegion(), m->lastRegion(), adaptMeshGRegion());
std::for_each(m->firstRegion(), m->lastRegion(), adaptMeshGRegion());
std::for_each(m->firstRegion(), m->lastRegion(), adaptMeshGRegion());
std::for_each(m->firstRegion(), m->lastRegion(), adaptMeshGRegion());
std::for_each(m->firstRegion(), m->lastRegion(), adaptMeshGRegion());
std::for_each(m->firstRegion(), m->lastRegion(), adaptMeshGRegion());
std::for_each(m->firstRegion(), m->lastRegion(), adaptMeshGRegion());
std::for_each(m->firstRegion(), m->lastRegion(), adaptMeshGRegion());
std::for_each(m->firstRegion(), m->lastRegion(), adaptMeshGRegion());
std::for_each(m->firstRegion(), m->lastRegion(), adaptMeshGRegion());
double t2 = Cpu();
Msg::Info("Mesh Adaptation complete (%g s)", t2 - t1);
Msg::Status(1, true, "Mesh");
Msg::Info("I'm busy! Ask me that later...");
Msg::ResetErrorCounter();
// Initialize pseudo random mesh generator with the same seed
srand(1);
std::for_each(m->firstRegion(), m->lastRegion(), deMeshGRegion());
std::for_each(m->firstFace(), m->lastFace(), deMeshGFace());
}
std::for_each(m->firstRegion(), m->lastRegion(), deMeshGRegion());
}
}
if(m->getMeshStatus() >= 2)
std::for_each(m->firstFace(), m->lastFace(), orientMeshGFace());
// Optimize quality of 3D tet mesh
if(m->getMeshStatus() == 3){
for(int i = 0; i < std::max(CTX.mesh.optimize, CTX.mesh.optimize_netgen); i++){
if(CTX.mesh.optimize > i) OptimizeMesh(m);
if(CTX.mesh.optimize_netgen > i) OptimizeMeshNetgen(m);
}
if(m->getMeshStatus() && CTX.mesh.order > 1)
SetOrderN(m, CTX.mesh.order, CTX.mesh.second_order_linear,
Msg::Info("%d vertices %d elements",
m->getNumMeshVertices(), m->getNumMeshElements());
Msg::PrintErrorCounter("Mesh generation error summary");