Files
mfem/miniapps/meshing/fit-node-position.cpp
T
2025-06-19 20:33:31 -07:00

230 lines
7.6 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.
//
// ------------------------------------------------------------------
// Fitting of Selected Mesh Nodes to Specified Physical Positions
// ------------------------------------------------------------------
//
// This example fits a selected set of the mesh nodes to given physical
// positions while maintaining a valid mesh with good quality.
//
// Sample runs:
// mpirun -np 4 fit-node-position
// mpirun -np 4 fit-node-position -m square01-tri.mesh
// mpirun -np 4 fit-node-position -m ./cube.mesh
// mpirun -np 4 fit-node-position -m ./cube-tet.mesh -rs 0
#include "mfem.hpp"
#include "../common/mfem-common.hpp"
using namespace mfem;
using namespace std;
char vishost[] = "localhost";
int wsize = 350;
int main (int argc, char *argv[])
{
// Initialize MPI.
Mpi::Init();
int myid = Mpi::WorldRank();
const char *mesh_file = "square01.mesh";
int rs_levels = 2;
int mesh_poly_deg = 2;
int quad_order = 5;
bool glvis = true;
int visport = 19916;
// Parse command-line options.
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&rs_levels, "-rs", "--refine-serial",
"Number of times to refine the mesh uniformly in serial.");
args.AddOption(&mesh_poly_deg, "-o", "--order",
"Polynomial degree of mesh finite element space.");
args.AddOption(&quad_order, "-qo", "--quad_order",
"Order of the quadrature rule.");
args.AddOption(&glvis, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&visport, "-p", "--send-port", "Socket for GLVis.");
args.Parse();
if (!args.Good())
{
if (myid == 0) { args.PrintUsage(cout); }
return 1;
}
if (myid == 0) { args.PrintOptions(cout); }
// Read and refine the mesh.
Mesh *mesh = new Mesh(mesh_file, 1, 1, false);
for (int lev = 0; lev < rs_levels; lev++) { mesh->UniformRefinement(); }
ParMesh pmesh(MPI_COMM_WORLD, *mesh);
delete mesh;
const int dim = pmesh.Dimension();
// Setup mesh curvature and GridFunction that stores the coordinates.
FiniteElementCollection *fec_mesh;
if (mesh_poly_deg <= 0)
{
fec_mesh = new QuadraticPosFECollection;
mesh_poly_deg = 2;
}
else { fec_mesh = new H1_FECollection(mesh_poly_deg, dim); }
ParFiniteElementSpace pfes_mesh(&pmesh, fec_mesh, dim);
pmesh.SetNodalFESpace(&pfes_mesh);
ParGridFunction coord(&pfes_mesh);
pmesh.SetNodalGridFunction(&coord);
ParGridFunction x0(coord);
// Pick which nodes to fit and select the target positions.
// (attribute 2 would have a prescribed deformation in y-direction, same x).
Array<bool> fit_marker(pfes_mesh.GetNDofs());
ParGridFunction fit_marker_vis_gf(&pfes_mesh);
ParGridFunction coord_target(&pfes_mesh);
Array<int> vdofs;
fit_marker = false;
coord_target = coord;
fit_marker_vis_gf = 0.0;
for (int e = 0; e < pmesh.GetNBE(); e++)
{
const int nd = pfes_mesh.GetBE(e)->GetDof();
const int attr = pmesh.GetBdrElement(e)->GetAttribute();
if (attr != 2) { continue; }
pfes_mesh.GetBdrElementVDofs(e, vdofs);
for (int j = 0; j < nd; j++)
{
int j_x = vdofs[j], j_y = vdofs[nd+j];
const real_t x = coord(j_x),
z = (dim == 2) ? 0.0 : coord(vdofs[2*nd + j]);
fit_marker[pfes_mesh.VDofToDof(j_x)] = true;
fit_marker_vis_gf(j_x) = 1.0;
if (coord(j_y) < 0.5)
{
coord_target(j_y) = 0.1 * sin(4 * M_PI * x) * cos(M_PI * z);
}
else
{
if (coord(j_x) < 0.5)
{
coord_target(j_y) = 1.0 + 0.1 * sin(2 * M_PI * x);
}
else
{
coord_target(j_y) = 1.0 + 0.1 * sin(2 * M_PI * (x + 0.5));
}
}
}
}
// Visualize the selected nodes and their target positions.
if (glvis)
{
socketstream vis1;
coord = coord_target;
common::VisualizeField(vis1, "localhost", 19916, fit_marker_vis_gf,
"Target positions (DOFS with value 1)",
0, 0, 400, 400, (dim == 2) ? "Rjm" : "");
coord = x0;
}
// Allow slipping along the remaining boundaries.
// (attributes 1 and 3 would slip, while 4 is completely fixed).
int n = 0;
for (int i = 0; i < pmesh.GetNBE(); i++)
{
const int nd = pfes_mesh.GetBE(i)->GetDof();
const int attr = pmesh.GetBdrElement(i)->GetAttribute();
MFEM_VERIFY(!(dim == 2 && attr == 3),
"Boundary attribute 3 must be used only for 3D meshes. "
"Adjust the attributes (1/2/3/4 for fixed x/y/z/all "
"components, rest for free nodes), or use -fix-bnd.");
if (attr == 1 || attr == 3) { n += nd; }
if (attr == 4) { n += nd * dim; }
}
Array<int> ess_vdofs(n);
n = 0;
for (int i = 0; i < pmesh.GetNBE(); i++)
{
const int nd = pfes_mesh.GetBE(i)->GetDof();
const int attr = pmesh.GetBdrElement(i)->GetAttribute();
pfes_mesh.GetBdrElementVDofs(i, vdofs);
if (attr == 1) // Fix x components.
{
for (int j = 0; j < nd; j++)
{ ess_vdofs[n++] = vdofs[j]; }
}
else if (attr == 3) // Fix z components.
{
for (int j = 0; j < nd; j++)
{ ess_vdofs[n++] = vdofs[j+2*nd]; }
}
else if (attr == 4) // Fix all components.
{
for (int j = 0; j < vdofs.Size(); j++)
{ ess_vdofs[n++] = vdofs[j]; }
}
}
// TMOP setup.
TMOP_QualityMetric *metric;
if (dim == 2) { metric = new TMOP_Metric_002; }
else { metric = new TMOP_Metric_302; }
TargetConstructor target(TargetConstructor::IDEAL_SHAPE_UNIT_SIZE,
pfes_mesh.GetComm());
ConstantCoefficient fit_weight(100.0);
auto integ = new TMOP_Integrator(metric, &target, nullptr);
integ->EnableSurfaceFitting(coord_target, fit_marker, fit_weight);
// Linear solver.
MINRESSolver minres(pfes_mesh.GetComm());
minres.SetMaxIter(100);
minres.SetRelTol(1e-12);
minres.SetAbsTol(0.0);
// Nonlinear solver.
ParNonlinearForm a(&pfes_mesh);
a.SetEssentialVDofs(ess_vdofs);
a.AddDomainIntegrator(integ);
const IntegrationRule &ir =
IntRules.Get(pmesh.GetTypicalElementGeometry(), quad_order);
TMOPNewtonSolver solver(pfes_mesh.GetComm(), ir, 0);
solver.SetOperator(a);
solver.SetPreconditioner(minres);
solver.SetPrintLevel(1);
solver.SetMaxIter(200);
solver.SetRelTol(1e-10);
solver.SetAbsTol(0.0);
solver.SetAdaptiveSurfaceFittingScalingFactor(10);
solver.SetTerminationWithMaxSurfaceFittingError(1e-3);
// Solve.
Vector b(0);
coord.SetTrueVector();
solver.Mult(b, coord.GetTrueVector());
coord.SetFromTrueVector();
if (glvis)
{
socketstream vis2;
common::VisualizeMesh(vis2, "localhost", 19916, pmesh, "Final mesh",
400, 0, 400, 400);
}
delete metric;
delete fec_mesh;
return 0;
}