Files
mfem/miniapps/meshing/mesh-explorer.cpp
T

1020 lines
30 KiB
C++

// Copyright (c) 2010-2020, 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 Explorer Miniapp: Explore and manipulate meshes
// -----------------------------------------------------
//
// This miniapp is a handy tool to examine, visualize and manipulate a given
// mesh. Some of its features are:
//
// - visualizing of mesh materials and individual mesh elements
// - mesh scaling, randomization, and general transformation
// - manipulation of the mesh curvature
// - the ability to simulate parallel partitioning
// - quantitative and visual reports of mesh quality
//
// Compile with: make mesh-explorer
//
// Sample runs: mesh-explorer
// mesh-explorer -m ../../data/beam-tri.mesh
// mesh-explorer -m ../../data/star-q2.mesh
// mesh-explorer -m ../../data/disc-nurbs.mesh
// mesh-explorer -m ../../data/escher-p3.mesh
// mesh-explorer -m ../../data/mobius-strip.mesh
#include "mfem.hpp"
#include <fstream>
#include <limits>
#include <cstdlib>
using namespace mfem;
using namespace std;
// This tranformation can be applied to a mesh with the 't' menu option.
void transformation(const Vector &p, Vector &v)
{
// simple shear transformation
double s = 0.1;
if (p.Size() == 3)
{
v(0) = p(0) + s*p(1) + s*p(2);
v(1) = p(1) + s*p(2) + s*p(0);
v(2) = p(2);
}
else if (p.Size() == 2)
{
v(0) = p(0) + s*p(1);
v(1) = p(1) + s*p(0);
}
else
{
v = p;
}
}
// This function is used with the 'r' menu option, sub-option 'l' to refine a
// mesh locally in a region, defined by return values <= region_eps.
double region_eps = 1e-8;
double region(const Vector &p)
{
const double x = p(0), y = p(1);
// here we describe the region: (x <= 1/4) && (y >= 0) && (y <= 1)
return std::max(std::max(x - 0.25, -y), y - 1.0);
}
Mesh *read_par_mesh(int np, const char *mesh_prefix)
{
Mesh *mesh;
Array<Mesh *> mesh_array;
mesh_array.SetSize(np);
for (int p = 0; p < np; p++)
{
ostringstream fname;
fname << mesh_prefix << '.' << setfill('0') << setw(6) << p;
ifgzstream meshin(fname.str().c_str());
if (!meshin)
{
cerr << "Can not open mesh file: " << fname.str().c_str()
<< '!' << endl;
for (p--; p >= 0; p--)
{
delete mesh_array[p];
}
return NULL;
}
mesh_array[p] = new Mesh(meshin, 1, 0);
// set element and boundary attributes to be the processor number + 1
if (1)
{
for (int i = 0; i < mesh_array[p]->GetNE(); i++)
{
mesh_array[p]->GetElement(i)->SetAttribute(p+1);
}
for (int i = 0; i < mesh_array[p]->GetNBE(); i++)
{
mesh_array[p]->GetBdrElement(i)->SetAttribute(p+1);
}
}
}
mesh = new Mesh(mesh_array, np);
for (int p = 0; p < np; p++)
{
delete mesh_array[np-1-p];
}
mesh_array.DeleteAll();
return mesh;
}
// Given a 3D mesh, produce a 2D mesh consisting of its boundary elements.
Mesh *skin_mesh(Mesh *mesh)
{
// Determine mapping from vertex to boundary vertex
Array<int> v2v(mesh->GetNV());
v2v = -1;
for (int i = 0; i < mesh->GetNBE(); i++)
{
Element *el = mesh->GetBdrElement(i);
int *v = el->GetVertices();
int nv = el->GetNVertices();
for (int j = 0; j < nv; j++)
{
v2v[v[j]] = 0;
}
}
int nbvt = 0;
for (int i = 0; i < v2v.Size(); i++)
{
if (v2v[i] == 0)
{
v2v[i] = nbvt++;
}
}
// Create a new mesh for the boundary
Mesh * bmesh = new Mesh(mesh->Dimension() - 1, nbvt, mesh->GetNBE(),
0, mesh->SpaceDimension());
// Copy vertices to the boundary mesh
nbvt = 0;
for (int i = 0; i < v2v.Size(); i++)
{
if (v2v[i] >= 0)
{
double *c = mesh->GetVertex(i);
bmesh->AddVertex(c);
nbvt++;
}
}
// Copy elements to the boundary mesh
int bv[4];
for (int i = 0; i < mesh->GetNBE(); i++)
{
Element *el = mesh->GetBdrElement(i);
int *v = el->GetVertices();
int nv = el->GetNVertices();
for (int j = 0; j < nv; j++)
{
bv[j] = v2v[v[j]];
}
switch (el->GetGeometryType())
{
case Geometry::SEGMENT:
bmesh->AddSegment(bv, el->GetAttribute());
break;
case Geometry::TRIANGLE:
bmesh->AddTriangle(bv, el->GetAttribute());
break;
case Geometry::SQUARE:
bmesh->AddQuad(bv, el->GetAttribute());
break;
default:
break; /// This should not happen
}
}
bmesh->FinalizeTopology();
// Copy GridFunction describing nodes if present
if (mesh->GetNodes())
{
FiniteElementSpace *fes = mesh->GetNodes()->FESpace();
const FiniteElementCollection *fec = fes->FEColl();
if (dynamic_cast<const H1_FECollection*>(fec))
{
FiniteElementCollection *fec_copy =
FiniteElementCollection::New(fec->Name());
FiniteElementSpace *fes_copy =
new FiniteElementSpace(*fes, bmesh, fec_copy);
GridFunction *bdr_nodes = new GridFunction(fes_copy);
bdr_nodes->MakeOwner(fec_copy);
bmesh->NewNodes(*bdr_nodes, true);
Array<int> vdofs;
Array<int> bvdofs;
Vector v;
for (int i=0; i<mesh->GetNBE(); i++)
{
fes->GetBdrElementVDofs(i, vdofs);
mesh->GetNodes()->GetSubVector(vdofs, v);
fes_copy->GetElementVDofs(i, bvdofs);
bdr_nodes->SetSubVector(bvdofs, v);
}
}
else
{
cout << "\nDiscontinuous nodes not yet supported" << endl;
}
}
return bmesh;
}
int main (int argc, char *argv[])
{
int np = 0;
const char *mesh_file = "../../data/beam-hex.mesh";
bool refine = true;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to visualize.");
args.AddOption(&np, "-np", "--num-proc",
"Load mesh from multiple processors.");
args.AddOption(&refine, "-ref", "--refinement", "-no-ref", "--no-refinement",
"Prepare the mesh for refinement or not.");
args.Parse();
if (!args.Good())
{
if (!args.Help())
{
args.PrintError(cout);
cout << endl;
}
cout << "Visualize and manipulate a serial mesh:\n"
<< " mesh-explorer -m <mesh_file>\n"
<< "Visualize and manipulate a parallel mesh:\n"
<< " mesh-explorer -np <#proc> -m <mesh_prefix>\n" << endl
<< "All Options:\n";
args.PrintHelp(cout);
return 1;
}
args.PrintOptions(cout);
Mesh *mesh;
Mesh *bdr_mesh = NULL;
if (np <= 0)
{
mesh = new Mesh(mesh_file, 1, refine);
}
else
{
mesh = read_par_mesh(np, mesh_file);
if (mesh == NULL)
{
return 3;
}
}
int dim = mesh->Dimension();
int sdim = mesh->SpaceDimension();
FiniteElementCollection *bdr_attr_fec = NULL;
FiniteElementCollection *attr_fec;
if (dim == 2)
{
attr_fec = new Const2DFECollection;
}
else
{
bdr_attr_fec = new Const2DFECollection;
attr_fec = new Const3DFECollection;
}
int print_char = 1;
while (1)
{
if (print_char)
{
cout << endl;
mesh->PrintCharacteristics();
cout << "boundary attribs :";
for (int i = 0; i < mesh->bdr_attributes.Size(); i++)
{
cout << ' ' << mesh->bdr_attributes[i];
}
cout << '\n' << "material attribs :";
for (int i = 0; i < mesh->attributes.Size(); i++)
{
cout << ' ' << mesh->attributes[i];
}
cout << endl;
cout << "mesh curvature : ";
if (mesh->GetNodalFESpace() != NULL)
{
cout << mesh->GetNodalFESpace()->FEColl()->Name() << endl;
}
else
{
cout << "NONE" << endl;
}
}
print_char = 0;
cout << endl;
cout << "What would you like to do?\n"
"r) Refine\n"
"c) Change curvature\n"
"s) Scale\n"
"t) Transform\n"
"j) Jitter\n"
"v) View\n"
"m) View materials\n"
"b) View boundary\n"
"e) View elements\n"
"h) View element sizes, h\n"
"k) View element ratios, kappa\n"
"x) Print sub-element stats\n"
"f) Find physical point in reference space\n"
"p) Generate a partitioning\n"
"o) Reorder elements\n"
"S) Save in MFEM format\n"
"V) Save in VTK format (only linear and quadratic meshes)\n"
"q) Quit\n"
#ifdef MFEM_USE_ZLIB
"Z) Save in MFEM format with compression\n"
#endif
"--> " << flush;
char mk;
cin >> mk;
if (!cin) { break; }
if (mk == 'q')
{
break;
}
if (mk == 'r')
{
cout <<
"Choose type of refinement:\n"
"s) standard refinement with Mesh::UniformRefinement()\n"
"b) Mesh::UniformRefinement() (bisection for tet meshes)\n"
"u) uniform refinement with a factor\n"
"g) non-uniform refinement (Gauss-Lobatto) with a factor\n"
"l) refine locally using the region() function\n"
"--> " << flush;
char sk;
cin >> sk;
switch (sk)
{
case 's':
mesh->UniformRefinement();
// Make sure tet-only meshes are marked for local refinement.
mesh->Finalize(true);
break;
case 'b':
mesh->UniformRefinement(1); // ref_algo = 1
break;
case 'u':
case 'g':
{
cout << "enter refinement factor --> " << flush;
int ref_factor;
cin >> ref_factor;
if (ref_factor <= 1 || ref_factor > 32) { break; }
int ref_type = (sk == 'u') ? BasisType::ClosedUniform :
BasisType::GaussLobatto;
Mesh *rmesh = new Mesh(mesh, ref_factor, ref_type);
delete mesh;
mesh = rmesh;
break;
}
case 'l':
{
Vector pt;
Array<int> marked_elements;
for (int i = 0; i < mesh->GetNE(); i++)
{
// check all nodes of the element
IsoparametricTransformation T;
mesh->GetElementTransformation(i, &T);
for (int j = 0; j < T.GetPointMat().Width(); j++)
{
T.GetPointMat().GetColumnReference(j, pt);
if (region(pt) <= region_eps)
{
marked_elements.Append(i);
break;
}
}
}
mesh->GeneralRefinement(marked_elements);
break;
}
}
print_char = 1;
}
if (mk == 'c')
{
int p;
cout << "enter new order for mesh curvature --> " << flush;
cin >> p;
mesh->SetCurvature(p > 0 ? p : -p, p <= 0);
print_char = 1;
}
if (mk == 's')
{
double factor;
cout << "scaling factor ---> " << flush;
cin >> factor;
GridFunction *nodes = mesh->GetNodes();
if (nodes == NULL)
{
for (int i = 0; i < mesh->GetNV(); i++)
{
double *v = mesh->GetVertex(i);
v[0] *= factor;
v[1] *= factor;
if (dim == 3)
{
v[2] *= factor;
}
}
}
else
{
*nodes *= factor;
}
print_char = 1;
}
if (mk == 't')
{
mesh->Transform(transformation);
print_char = 1;
}
if (mk == 'j')
{
double jitter;
cout << "jitter factor ---> " << flush;
cin >> jitter;
GridFunction *nodes = mesh->GetNodes();
if (nodes == NULL)
{
cerr << "The mesh should have nodes, introduce curvature first!\n";
}
else
{
FiniteElementSpace *fespace = nodes->FESpace();
GridFunction rdm(fespace);
rdm.Randomize();
rdm -= 0.5; // shift to random values in [-0.5,0.5]
rdm *= jitter;
// compute minimal local mesh size
Vector h0(fespace->GetNDofs());
h0 = infinity();
{
Array<int> dofs;
for (int i = 0; i < fespace->GetNE(); i++)
{
fespace->GetElementDofs(i, dofs);
for (int j = 0; j < dofs.Size(); j++)
{
h0(dofs[j]) = std::min(h0(dofs[j]), mesh->GetElementSize(i));
}
}
}
// scale the random values to be of order of the local mesh size
for (int i = 0; i < fespace->GetNDofs(); i++)
{
for (int d = 0; d < dim; d++)
{
rdm(fespace->DofToVDof(i,d)) *= h0(i);
}
}
char move_bdr = 'n';
cout << "move boundary nodes? [y/n] ---> " << flush;
cin >> move_bdr;
// don't perturb the boundary
if (move_bdr == 'n')
{
Array<int> vdofs;
for (int i = 0; i < fespace->GetNBE(); i++)
{
fespace->GetBdrElementVDofs(i, vdofs);
for (int j = 0; j < vdofs.Size(); j++)
{
rdm(vdofs[j]) = 0.0;
}
}
}
*nodes += rdm;
}
print_char = 1;
}
if (mk == 'x')
{
int sd, nz = 0;
DenseMatrix J(dim);
double min_det_J, max_det_J, min_det_J_z, max_det_J_z;
double min_kappa, max_kappa, max_ratio_det_J_z;
min_det_J = min_kappa = infinity();
max_det_J = max_kappa = max_ratio_det_J_z = -infinity();
cout << "subdivision factor ---> " << flush;
cin >> sd;
Array<int> bad_elems_by_geom(Geometry::NumGeom);
bad_elems_by_geom = 0;
for (int i = 0; i < mesh->GetNE(); i++)
{
Geometry::Type geom = mesh->GetElementBaseGeometry(i);
ElementTransformation *T = mesh->GetElementTransformation(i);
RefinedGeometry *RefG = GlobGeometryRefiner.Refine(geom, sd, 1);
IntegrationRule &ir = RefG->RefPts;
min_det_J_z = infinity();
max_det_J_z = -infinity();
for (int j = 0; j < ir.GetNPoints(); j++)
{
T->SetIntPoint(&ir.IntPoint(j));
Geometries.JacToPerfJac(geom, T->Jacobian(), J);
double det_J = J.Det();
double kappa =
J.CalcSingularvalue(0) / J.CalcSingularvalue(dim-1);
min_det_J_z = fmin(min_det_J_z, det_J);
max_det_J_z = fmax(max_det_J_z, det_J);
min_kappa = fmin(min_kappa, kappa);
max_kappa = fmax(max_kappa, kappa);
}
max_ratio_det_J_z =
fmax(max_ratio_det_J_z, max_det_J_z/min_det_J_z);
min_det_J = fmin(min_det_J, min_det_J_z);
max_det_J = fmax(max_det_J, max_det_J_z);
if (min_det_J_z <= 0.0)
{
nz++;
bad_elems_by_geom[geom]++;
}
}
cout << "\nbad elements = " << nz;
if (nz)
{
cout << " -- ";
Mesh::PrintElementsByGeometry(dim, bad_elems_by_geom, cout);
}
cout << "\nmin det(J) = " << min_det_J
<< "\nmax det(J) = " << max_det_J
<< "\nglobal ratio = " << max_det_J/min_det_J
<< "\nmax el ratio = " << max_ratio_det_J_z
<< "\nmin kappa = " << min_kappa
<< "\nmax kappa = " << max_kappa << endl;
}
if (mk == 'f')
{
DenseMatrix point_mat(sdim,1);
cout << "\npoint in physical space ---> " << flush;
for (int i = 0; i < sdim; i++)
{
cin >> point_mat(i,0);
}
Array<int> elem_ids;
Array<IntegrationPoint> ips;
// physical -> reference space
mesh->FindPoints(point_mat, elem_ids, ips);
cout << "point in reference space:";
if (elem_ids[0] == -1)
{
cout << " NOT FOUND!\n";
}
else
{
cout << " element " << elem_ids[0] << ", ip =";
cout << " " << ips[0].x;
if (sdim > 1)
{
cout << " " << ips[0].y;
if (sdim > 2)
{
cout << " " << ips[0].z;
}
}
cout << endl;
}
}
if (mk == 'o')
{
cout << "What type of reordering?\n"
"g) Gecko edge-product minimization\n"
"h) Hilbert spatial sort\n"
"--> " << flush;
char rk;
cin >> rk;
Array<int> ordering, tentative;
if (rk == 'h')
{
mesh->GetHilbertElementOrdering(ordering);
mesh->ReorderElements(ordering);
}
else if (rk == 'g')
{
int outer, inner, window, period;
cout << "Enter number of outer iterations (default 5): " << flush;
cin >> outer;
cout << "Enter number of inner iterations (default 4): " << flush;
cin >> inner;
cout << "Enter window size (default 4, beware of exponential cost): "
<< flush;
cin >> window;
cout << "Enter period for window size increment (default 2): "
<< flush;
cin >> period;
double best_cost = infinity();
for (int i = 0; i < outer; i++)
{
int seed = i+1;
double cost = mesh->GetGeckoElementOrdering(
tentative, inner, window, period, seed, true);
if (cost < best_cost)
{
ordering = tentative;
best_cost = cost;
}
}
cout << "Final cost: " << best_cost << endl;
mesh->ReorderElements(ordering);
}
}
// These are the cases that open a new GLVis window
if (mk == 'm' || mk == 'b' || mk == 'e' || mk == 'v' || mk == 'h' ||
mk == 'k' || mk == 'p')
{
Array<int> bdr_part;
Array<int> part(mesh->GetNE());
FiniteElementSpace *bdr_attr_fespace = NULL;
FiniteElementSpace *attr_fespace =
new FiniteElementSpace(mesh, attr_fec);
GridFunction bdr_attr;
GridFunction attr(attr_fespace);
if (mk == 'm')
{
for (int i = 0; i < mesh->GetNE(); i++)
{
part[i] = (attr(i) = mesh->GetAttribute(i)) - 1;
}
}
if (mk == 'b')
{
if (dim == 3)
{
delete bdr_mesh;
bdr_mesh = skin_mesh(mesh);
bdr_attr_fespace =
new FiniteElementSpace(bdr_mesh, bdr_attr_fec);
bdr_part.SetSize(bdr_mesh->GetNE());
bdr_attr.SetSpace(bdr_attr_fespace);
for (int i = 0; i < bdr_mesh->GetNE(); i++)
{
bdr_part[i] = (bdr_attr(i) = bdr_mesh->GetAttribute(i)) - 1;
}
}
else
{
attr = 1.0;
}
}
if (mk == 'v')
{
attr = 1.0;
}
if (mk == 'e')
{
Array<int> coloring;
srand(time(0));
double a = double(rand()) / (double(RAND_MAX) + 1.);
int el0 = (int)floor(a * mesh->GetNE());
cout << "Generating coloring starting with element " << el0+1
<< " / " << mesh->GetNE() << endl;
mesh->GetElementColoring(coloring, el0);
for (int i = 0; i < coloring.Size(); i++)
{
attr(i) = coloring[i];
}
cout << "Number of colors: " << attr.Max() + 1 << endl;
for (int i = 0; i < mesh->GetNE(); i++)
{
// part[i] = i; // checkerboard element coloring
attr(i) = part[i] = i; // coloring by element number
}
}
if (mk == 'h')
{
DenseMatrix J(dim);
double h_min, h_max;
h_min = infinity();
h_max = -h_min;
for (int i = 0; i < mesh->GetNE(); i++)
{
int geom = mesh->GetElementBaseGeometry(i);
ElementTransformation *T = mesh->GetElementTransformation(i);
T->SetIntPoint(&Geometries.GetCenter(geom));
Geometries.JacToPerfJac(geom, T->Jacobian(), J);
attr(i) = J.Det();
if (attr(i) < 0.0)
{
attr(i) = -pow(-attr(i), 1.0/double(dim));
}
else
{
attr(i) = pow(attr(i), 1.0/double(dim));
}
h_min = min(h_min, attr(i));
h_max = max(h_max, attr(i));
}
cout << "h_min = " << h_min << ", h_max = " << h_max << endl;
}
if (mk == 'k')
{
DenseMatrix J(dim);
for (int i = 0; i < mesh->GetNE(); i++)
{
int geom = mesh->GetElementBaseGeometry(i);
ElementTransformation *T = mesh->GetElementTransformation(i);
T->SetIntPoint(&Geometries.GetCenter(geom));
Geometries.JacToPerfJac(geom, T->Jacobian(), J);
attr(i) = J.CalcSingularvalue(0) / J.CalcSingularvalue(dim-1);
}
}
if (mk == 'p')
{
int *partitioning = NULL, np;
cout << "What type of partitioning?\n"
"c) Cartesian\n"
"s) Simple 1D split of the element sequence\n"
"0) METIS_PartGraphRecursive (sorted neighbor lists)\n"
"1) METIS_PartGraphKway (sorted neighbor lists)"
" (default)\n"
"2) METIS_PartGraphVKway (sorted neighbor lists)\n"
"3) METIS_PartGraphRecursive\n"
"4) METIS_PartGraphKway\n"
"5) METIS_PartGraphVKway\n"
"--> " << flush;
char pk;
cin >> pk;
if (pk == 'c')
{
int nxyz[3];
cout << "Enter nx: " << flush;
cin >> nxyz[0]; np = nxyz[0];
if (mesh->Dimension() > 1)
{
cout << "Enter ny: " << flush;
cin >> nxyz[1]; np *= nxyz[1];
if (mesh->Dimension() > 2)
{
cout << "Enter nz: " << flush;
cin >> nxyz[2]; np *= nxyz[2];
}
}
partitioning = mesh->CartesianPartitioning(nxyz);
}
else if (pk == 's')
{
cout << "Enter number of processors: " << flush;
cin >> np;
partitioning = new int[mesh->GetNE()];
for (int i = 0; i < mesh->GetNE(); i++)
{
partitioning[i] = i * np / mesh->GetNE();
}
}
else
{
int part_method = pk - '0';
if (part_method < 0 || part_method > 5)
{
continue;
}
cout << "Enter number of processors: " << flush;
cin >> np;
partitioning = mesh->GeneratePartitioning(np, part_method);
}
if (partitioning)
{
const char part_file[] = "partitioning.txt";
ofstream opart(part_file);
opart << "number_of_elements " << mesh->GetNE() << '\n'
<< "number_of_processors " << np << '\n';
for (int i = 0; i < mesh->GetNE(); i++)
{
opart << partitioning[i] << '\n';
}
cout << "Partitioning file: " << part_file << endl;
Array<int> proc_el(np);
proc_el = 0;
for (int i = 0; i < mesh->GetNE(); i++)
{
proc_el[partitioning[i]]++;
}
int min_el = proc_el[0], max_el = proc_el[0];
for (int i = 1; i < np; i++)
{
if (min_el > proc_el[i])
{
min_el = proc_el[i];
}
if (max_el < proc_el[i])
{
max_el = proc_el[i];
}
}
cout << "Partitioning stats:\n"
<< " "
<< setw(12) << "minimum"
<< setw(12) << "average"
<< setw(12) << "maximum"
<< setw(12) << "total" << '\n';
cout << " elements "
<< setw(12) << min_el
<< setw(12) << double(mesh->GetNE())/np
<< setw(12) << max_el
<< setw(12) << mesh->GetNE() << endl;
}
else
{
continue;
}
for (int i = 0; i < mesh->GetNE(); i++)
{
attr(i) = part[i] = partitioning[i];
}
delete [] partitioning;
}
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
if (sol_sock.is_open())
{
sol_sock.precision(14);
if (sdim == 2)
{
sol_sock << "fem2d_gf_data_keys\n";
if (mk != 'p')
{
mesh->Print(sol_sock);
}
else
{
// NURBS meshes do not support PrintWithPartitioning
if (mesh->NURBSext)
{
mesh->Print(sol_sock);
for (int i = 0; i < mesh->GetNE(); i++)
{
attr(i) = part[i];
}
}
else
{
mesh->PrintWithPartitioning(part, sol_sock, 1);
}
}
attr.Save(sol_sock);
sol_sock << "RjlmAb***********";
if (mk == 'v')
{
sol_sock << "e";
}
else
{
sol_sock << "\n";
}
}
else
{
sol_sock << "fem3d_gf_data_keys\n";
if (mk == 'v' || mk == 'h' || mk == 'k')
{
mesh->Print(sol_sock);
}
else if (mk == 'b')
{
bdr_mesh->Print(sol_sock);
bdr_attr.Save(sol_sock);
sol_sock << "mcaaA";
// Switch to a discrete color scale
sol_sock << "pppppp" << "pppppp" << "pppppp";
}
else
{
// NURBS meshes do not support PrintWithPartitioning
if (mesh->NURBSext)
{
mesh->Print(sol_sock);
for (int i = 0; i < mesh->GetNE(); i++)
{
attr(i) = part[i];
}
}
else
{
mesh->PrintWithPartitioning(part, sol_sock);
}
}
if (mk != 'b')
{
attr.Save(sol_sock);
sol_sock << "maaA";
if (mk == 'v')
{
sol_sock << "aa";
}
else
{
sol_sock << "\n";
}
}
}
sol_sock << flush;
}
else
{
cout << "Unable to connect to "
<< vishost << ':' << visport << endl;
}
delete attr_fespace;
delete bdr_attr_fespace;
}
if (mk == 'S')
{
const char mesh_file[] = "mesh-explorer.mesh";
ofstream omesh(mesh_file);
omesh.precision(14);
mesh->Print(omesh);
cout << "New mesh file: " << mesh_file << endl;
}
if (mk == 'V')
{
const char mesh_file[] = "mesh-explorer.vtk";
ofstream omesh(mesh_file);
omesh.precision(14);
mesh->PrintVTK(omesh);
cout << "New VTK mesh file: " << mesh_file << endl;
}
#ifdef MFEM_USE_ZLIB
if (mk == 'Z')
{
const char mesh_file[] = "mesh-explorer.mesh.gz";
ofgzstream omesh(mesh_file, "zwb9");
omesh.precision(14);
mesh->Print(omesh);
cout << "New mesh file: " << mesh_file << endl;
}
#endif
}
delete bdr_attr_fec;
delete attr_fec;
delete bdr_mesh;
delete mesh;
return 0;
}