322 lines
11 KiB
C++
322 lines
11 KiB
C++
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
|
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
|
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
|
//
|
|
// This file is part of the MFEM library. For more information and source code
|
|
// availability visit https://mfem.org.
|
|
//
|
|
// MFEM is free software; you can redistribute it and/or modify it under the
|
|
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
|
// CONTRIBUTING.md for details.
|
|
//
|
|
// ------------------------------------------------------
|
|
// Mesh Quality Miniapp: Visualize and Check Mesh Quality
|
|
// ------------------------------------------------------
|
|
//
|
|
// This miniapp extracts geometric parameters from the Jacobian transformation
|
|
// at each degree-of-freedom of the mesh, and visualizes it. The geometric
|
|
// parameters size, skewness, and aspect-ratio can help assess the quality of a
|
|
// mesh.
|
|
//
|
|
// Compile with: make mesh-quality
|
|
//
|
|
// Sample runs:
|
|
// mesh-quality -m blade.mesh -size -aspr -skew -vis -visit
|
|
// mesh-quality -m ../../data/square-disc.mesh -o 2 -size -aspr -skew -vis
|
|
|
|
#include "mfem.hpp"
|
|
#include <iostream>
|
|
#include "../common/mfem-common.hpp"
|
|
|
|
using namespace std;
|
|
using namespace mfem;
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
// 1. Parse command-line options.
|
|
const char *mesh_file = "../../data/inline-quad.mesh";
|
|
int order = -1;
|
|
int ref_levels = 0;
|
|
bool visualization = true;
|
|
bool visit = false;
|
|
bool size = true;
|
|
bool aspect_ratio = true;
|
|
bool skewness = true;
|
|
|
|
OptionsParser args(argc, argv);
|
|
args.AddOption(&mesh_file, "-m", "--mesh",
|
|
"Mesh file to use.");
|
|
args.AddOption(&order, "-o", "--order",
|
|
"Finite element order for visualization"
|
|
"(defaults to order of the mesh).");
|
|
args.AddOption(&ref_levels, "-r", "--ref-levels",
|
|
"Number of initial uniform refinement levels.");
|
|
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
|
"--no-visualization",
|
|
"Enable or disable GLVis visualization.");
|
|
args.AddOption(&visit, "-visit", "--visit", "-no-visit",
|
|
"--no-visit",
|
|
"Enable or disable VisIt.");
|
|
args.AddOption(&size, "-size", "--size", "-no-size",
|
|
"--no-size",
|
|
"Visualize size parameter.");
|
|
args.AddOption(&aspect_ratio, "-aspr", "--aspect-ratio", "-no-aspr",
|
|
"--no-aspect-ratio",
|
|
"Visualize aspect-ratio parameter.");
|
|
args.AddOption(&skewness, "-skew", "--skew", "-no-skew",
|
|
"--no-skew",
|
|
"Visualize skewness parameter.");
|
|
args.Parse();
|
|
if (!args.Good())
|
|
{
|
|
args.PrintUsage(cout);
|
|
return 1;
|
|
}
|
|
args.PrintOptions(cout);
|
|
|
|
Mesh *mesh = new Mesh(mesh_file, 1, 1, false);
|
|
for (int lev = 0; lev < ref_levels; lev++)
|
|
{
|
|
mesh->UniformRefinement();
|
|
}
|
|
const int dim = mesh->Dimension();
|
|
|
|
int nSize = 1, nAspr = 1, nSkew = 1;
|
|
if (dim == 3)
|
|
{
|
|
nAspr = 2;
|
|
nSkew = 3;
|
|
}
|
|
|
|
// Total number of geometric parameters; for now we skip orientation.
|
|
const int nTotalParams = nSize + nAspr + nSkew;
|
|
if (order < 0)
|
|
{
|
|
order = mesh->GetNodalFESpace() == NULL ? 1 :
|
|
mesh->GetNodalFESpace()->GetMaxElementOrder();
|
|
}
|
|
|
|
// Define a GridFunction for all geometric parameters associated with the
|
|
// mesh.
|
|
L2_FECollection l2fec(order, mesh->Dimension());
|
|
FiniteElementSpace fespace(mesh, &l2fec, nTotalParams); // must order byNodes
|
|
GridFunction quality(&fespace);
|
|
|
|
DenseMatrix jacobian(dim);
|
|
Vector geomParams(nTotalParams);
|
|
Array<int> vdofs;
|
|
Vector allVals;
|
|
// Compute the geometric parameter at the dofs of each element.
|
|
for (int e = 0; e < mesh->GetNE(); e++)
|
|
{
|
|
const FiniteElement *fe = fespace.GetFE(e);
|
|
const IntegrationRule &ir = fe->GetNodes();
|
|
fespace.GetElementVDofs(e, vdofs);
|
|
allVals.SetSize(vdofs.Size());
|
|
for (int q = 0; q < ir.GetNPoints(); q++)
|
|
{
|
|
const IntegrationPoint &ip = ir.IntPoint(q);
|
|
mesh->GetElementJacobian(e, jacobian, &ip);
|
|
real_t sizeVal;
|
|
Vector asprVals, skewVals, oriVals;
|
|
mesh->GetGeometricParametersFromJacobian(jacobian, sizeVal,
|
|
asprVals, skewVals, oriVals);
|
|
allVals(q + 0) = sizeVal;
|
|
for (int n = 0; n < nAspr; n++)
|
|
{
|
|
allVals(q + (n+1)*ir.GetNPoints()) = asprVals(n);
|
|
}
|
|
for (int n = 0; n < nSkew; n++)
|
|
{
|
|
allVals(q + (n+1+nAspr)*ir.GetNPoints()) = skewVals(n);
|
|
}
|
|
}
|
|
quality.SetSubVector(vdofs, allVals);
|
|
}
|
|
|
|
VisItDataCollection visit_dc("quality", mesh);
|
|
|
|
// Visualize different parameters
|
|
int visw = 400;
|
|
int vish = 400;
|
|
int cx = 0;
|
|
int cy = 0;
|
|
int gap = 10;
|
|
FiniteElementSpace scfespace(mesh, &l2fec);
|
|
int ndofs = scfespace.GetNDofs();
|
|
if (dim == 2)
|
|
{
|
|
int idx = 0;
|
|
GridFunction size_gf, aspr_gf, skew_gf;
|
|
socketstream vis1, vis2, vis3;
|
|
if (size)
|
|
{
|
|
size_gf.MakeRef(&scfespace, quality.GetData());
|
|
if (visit) { visit_dc.RegisterField("Size", &size_gf); }
|
|
if (visualization)
|
|
{
|
|
common::VisualizeField(vis1, "localhost", 19916, size_gf,
|
|
"Size", cx, cy, visw, vish, "Rjmc");
|
|
}
|
|
real_t min_size = size_gf.Min(),
|
|
max_size = size_gf.Max();
|
|
cout << "Min size: " << min_size << endl;
|
|
cout << "Max size: " << max_size << endl;
|
|
}
|
|
idx++;
|
|
if (aspect_ratio)
|
|
{
|
|
aspr_gf.MakeRef(&scfespace, quality.GetData() + idx*ndofs);
|
|
if (visit) { visit_dc.RegisterField("Aspect-Ratio", &aspr_gf); }
|
|
if (visualization)
|
|
{
|
|
cx += gap+visw;
|
|
common::VisualizeField(vis2, "localhost", 19916, aspr_gf,
|
|
"Aspect-Ratio", cx, cy, visw, vish, "Rjmc");
|
|
}
|
|
real_t min_aspr = aspr_gf.Min(),
|
|
max_aspr = aspr_gf.Max();
|
|
max_aspr = std::max((real_t) 1.0/min_aspr, max_aspr);
|
|
cout << "Worst aspect-ratio: " << max_aspr << endl;
|
|
cout << "(in any direction)" << endl;
|
|
}
|
|
idx++;
|
|
if (skewness)
|
|
{
|
|
skew_gf.MakeRef(&scfespace, quality.GetData() + idx*ndofs);
|
|
if (visit) { visit_dc.RegisterField("Skewness", &skew_gf); }
|
|
if (visualization)
|
|
{
|
|
cx += gap+visw;
|
|
common::VisualizeField(vis3, "localhost", 19916, skew_gf,
|
|
"Skewness (radians)", cx, cy, visw, vish,
|
|
"Rjmc");
|
|
}
|
|
real_t min_skew = skew_gf.Min(),
|
|
max_skew = skew_gf.Max();
|
|
cout << "Min skew (in deg): " << min_skew*180/M_PI << endl;
|
|
cout << "Max skew (in deg): " << max_skew*180/M_PI << endl;
|
|
}
|
|
if (visit)
|
|
{
|
|
visit_dc.SetFormat(DataCollection::SERIAL_FORMAT);
|
|
visit_dc.Save();
|
|
}
|
|
}
|
|
else if (dim == 3)
|
|
{
|
|
int idx = 0;
|
|
GridFunction size_gf, aspr_gf1, aspr_gf2,
|
|
skew_gf1, skew_gf2, skew_gf3;
|
|
socketstream vis1, vis2, vis3, vis4, vis5, vis6;
|
|
|
|
if (size)
|
|
{
|
|
size_gf.MakeRef(&scfespace, quality.GetData());
|
|
if (visit) { visit_dc.RegisterField("Size", &size_gf); }
|
|
if (visualization)
|
|
{
|
|
common::VisualizeField(vis1, "localhost", 19916, size_gf,
|
|
"Size", cx, cy, visw, vish, "Rjmc");
|
|
}
|
|
real_t min_size = size_gf.Min(),
|
|
max_size = size_gf.Max();
|
|
cout << "Min size: " << min_size << endl;
|
|
cout << "Max size: " << max_size << endl;
|
|
}
|
|
idx++;
|
|
|
|
if (aspect_ratio)
|
|
{
|
|
aspr_gf1.MakeRef(&scfespace, quality.GetData() + (idx++)*ndofs);
|
|
aspr_gf2.MakeRef(&scfespace, quality.GetData() + (idx++)*ndofs);
|
|
if (visit)
|
|
{
|
|
visit_dc.RegisterField("Aspect-Ratio", &aspr_gf1);
|
|
visit_dc.RegisterField("Aspect-Ratio2", &aspr_gf2);
|
|
}
|
|
if (visualization)
|
|
{
|
|
cx += gap+visw;
|
|
common::VisualizeField(vis2, "localhost", 19916, aspr_gf1,
|
|
"Aspect-Ratio", cx, cy, visw, vish, "Rjmc");
|
|
cx += gap+visw;
|
|
common::VisualizeField(vis3, "localhost", 19916, aspr_gf2,
|
|
"Aspect-Ratio2", cx, cy, visw, vish, "Rjmc");
|
|
}
|
|
real_t min_aspr1 = aspr_gf1.Min(),
|
|
max_aspr1 = aspr_gf1.Max();
|
|
max_aspr1 = std::max((real_t) 1.0/min_aspr1, max_aspr1);
|
|
|
|
real_t min_aspr2 = aspr_gf2.Min(),
|
|
max_aspr2 = aspr_gf2.Max();
|
|
max_aspr2 = std::max((real_t) 1.0/min_aspr2, max_aspr2);
|
|
real_t max_aspr = max(max_aspr1, max_aspr2);
|
|
|
|
Vector aspr_gf3(aspr_gf1.Size());
|
|
for (int i = 0; i < aspr_gf1.Size(); i++)
|
|
{
|
|
aspr_gf3(i) = 1.0/(aspr_gf1(i)*aspr_gf2(i));
|
|
}
|
|
real_t min_aspr3 = aspr_gf3.Min(),
|
|
max_aspr3 = aspr_gf3.Max();
|
|
max_aspr3 = std::max((real_t) 1.0/min_aspr3, max_aspr3);
|
|
max_aspr = std::max(max_aspr, max_aspr3);
|
|
|
|
cout << "Worst aspect-ratio: " << max_aspr << endl;
|
|
cout << "(in any direction)" << endl;
|
|
}
|
|
else { idx += 2; }
|
|
|
|
if (skewness)
|
|
{
|
|
skew_gf1.MakeRef(&scfespace, quality.GetData() + (idx++)*ndofs);
|
|
skew_gf2.MakeRef(&scfespace, quality.GetData() + (idx++)*ndofs);
|
|
skew_gf3.MakeRef(&scfespace, quality.GetData() + (idx++)*ndofs);
|
|
if (visit)
|
|
{
|
|
visit_dc.RegisterField("Skewness (radians)", &skew_gf1);
|
|
visit_dc.RegisterField("Skewness2 (radians)", &skew_gf2);
|
|
visit_dc.RegisterField("Dihedral (radians)", &skew_gf3);
|
|
}
|
|
if (visualization)
|
|
{
|
|
cx = 0;
|
|
cy += 10*gap+vish;
|
|
common::VisualizeField(vis4, "localhost", 19916, skew_gf1,
|
|
"Skewness", cx, cy, visw, vish, "Rjmc");
|
|
cx += gap+visw;
|
|
common::VisualizeField(vis5, "localhost", 19916, skew_gf2,
|
|
"Skewness2", cx, cy, visw, vish, "Rjmc");
|
|
cx += gap+visw;
|
|
common::VisualizeField(vis6, "localhost", 19916, skew_gf3,
|
|
"Dihedral", cx, cy, visw, vish, "Rjmc");
|
|
}
|
|
real_t min_skew1 = skew_gf1.Min(),
|
|
max_skew1 = skew_gf1.Max();
|
|
real_t min_skew2 = skew_gf2.Min(),
|
|
max_skew2 = skew_gf2.Max();
|
|
real_t min_skew3 = skew_gf3.Min(),
|
|
max_skew3 = skew_gf3.Max();
|
|
cout << "Min skew 1 (in deg): " << min_skew1*180/M_PI << endl;
|
|
cout << "Max skew 1 (in deg): " << max_skew1*180/M_PI << endl;
|
|
|
|
cout << "Min skew 2 (in deg): " << min_skew2*180/M_PI << endl;
|
|
cout << "Max skew 2 (in deg): " << max_skew2*180/M_PI << endl;
|
|
|
|
cout << "Min skew 3 (in deg): " << min_skew3*180/M_PI << endl;
|
|
cout << "Max skew 3 (in deg): " << max_skew3*180/M_PI << endl;
|
|
}
|
|
else { idx += 3; }
|
|
|
|
if (visit)
|
|
{
|
|
visit_dc.SetFormat(DataCollection::SERIAL_FORMAT);
|
|
visit_dc.Save();
|
|
}
|
|
}
|
|
|
|
delete mesh;
|
|
return 0;
|
|
}
|