50368046bc
* Simplify TMOP kernels, fix unit tests to run --all tests with adjusted tolerance * make style * Split TMOP h3s file with metrics * TMOP kernel MFEM_HOST_DEVICE fix * Cleanup TMOP CUDA kernels from base class * Added TMOP PA metrics directory * meld toward master * [tmop] struct to class friends * Simplify tmop file names * make style * Cleanup * Style and vscode gitignore * WIP resolve conflicts * 2024 headers * Tmop pass * All tmop tests * make style * Add astyle to clang format * make style * Fix class visibility * Include cleanup * real_t pass * style * MFEM_REGISTER_KERNELS for TMOPAssembleGradPA_001 * make style * add config files * Update config * metric_t * wip with T * wip * wip T Specialization * c++20, fmt make_format_args * print types and values * wip Kernel<decltype(M)> * wip * Working with metric_t, int, int * C++20 ok * C++17 cleaned * Rename tmop files * Sync TMOP kernels with dispatch * make style * Cleanup metrics * Use TMOPKernel * 3D metrics standalone * Chdir assemble * tmop 2d/3d directories * TMOP assemble using specializations * All TMOP kernel specializations * MFEM_REPORT_KERNELS * make style * Sync with master * Sync with master * make style * make style * Removed 2d/3d TMOP sub-directories * CMake TMOP file list update * makefile directories order * With style * Re-enable vscode gitignore * Fix merge conflicts * make style * Sync * Meld toward master * Changes toward master * make style * Meld back fem tmop files * Fix TMOP_Integrator friends * PA tests fix & history bump * Cleanup test tmop and fix energy2 metric data * Update copyright 2010-2025 * 2D energy metrics * 3D energy metrics * make style * Simplify metric registration * TMOP fem kernels with double buffering * grad3, grad3_coef * grad3_coef, grad3, mult3_coefs, mult3 * TMOP sm kernels tools * Rename kernels smem and use regs * Grad3 w/ vector reg grad * Kernel register cleanup * Add MAX_TMOP_1D and HIP tmop ctests * Add kernels_foreach * Add kernels foreach * Prefix foreach_thread * Kernels regs w/ foreach threads * Swap Y and X in forward only * Backward kernels_regs * Use simplified grad3d * Wip D1D Q1D * Runtime D1D Q1D * Remove T1D * Cleanup * AddKernelSpecializations * Sync with SetMaxOf * Rename to LoadDofs and use deduced templated parameters * Grad2d & factorization * Eval3d for grad3 coef * Eval2d for grad2 coef * Cleanup TMOP_SetupGradPA_C0_2D * Use Bld and B * Use other accessors * TMOPAddMultPA3D * TMOP_AddMultPA_C0_2D * TMOP_AddMultGradPA_3D * TMOP_AddMultGradPA_2D * TMOP_AddMultGradPA_C0_3D * AddMultGradPA_C0_2D * TMOP_AssembleDiagonalPA_2D * Wip TMOP_AssembleDiagonalPA_C0_3D * TMOP_AssembleDiagonalPA_3D * TMOP_MinDetJpr_3D * TMOP_EnergyPA_C0_2D * TMOPEnergyPA3D * TMOP_TcIdealShapeGivenSize_3D * TMOP_DatcSize_3D * Remove MAX_TMOP_1D * Remove smem kernels * TMOP cleanup * TMOP - solve for displacements #4694 changes * Cleanup and move verifications * Rename TMOP Assemble kernels * Move kernel regs to TMOP pa * make style * Meld back toward master * Meld back to master * Use static constexpr * Temporary branch-history * Help msvc with namespaces * MSVC inner static constexpr * Move regs to mfem namespace * MSVC all static constexpr * TMOP_AssembleDiagPA_C0_3D w/o regs * Avoid set but unused variable * MSVC TMOP_AssembleDiagPA_C0_3D ternary test try * MSVC MFEM_TMOP_REGISTER_MDQ_KERNEL * Switch to MFEM_TMOP_MDQ_REGISTER * MSVC help with static constexpr * MSVC conversions try * MSVC as_regs2d_ref * MSVC Explicitly bind as reference * MSCV with reinterpret_cast * MSVC avoiding required l-values * MSVC avoid explicit ref bindings * MSVC avoid explicit ref bindings 2D * Cleanup * Enable MFEM_TMOP_PA_DEVICE with makefile * TMOP tests w/o Kernel Specializations * TMOP re-enable kernels specializations * TMOP PA tests tolerances * TMOP tests adjustments * Fix transposed eval regs access * MSVC remove not allowed dllimport definitions * MSVC linalg vector warning fix * MSVC avoiding definition of dllimport function not allowed * Re-enable DetKernels specializations * Sync latest TMOP changes * TMOP PA tests normalization wip * Sync TMOP tests * Remove debug file * Meld back toward master * Add missing tmop make source dir * tmop shadowing, CMake & make mpi tests * TMOP periodic tests, shadowing fix * TMOP pa mpi tests, fix shadowing * TMOP tighten Square01 + Combo tests * TMOP MSVC include ordering * Revert TMOP MPI debug device tests * Add TMOP_DatcSize_2D * Use mfem::future for tensor * Move TMOP PA specific kernels to sync'ed fem kernels * makefile source dirs fix * use explicit namespace to avoid clash (swap) * Revert to MFEM_FOREACH_THREAD Use scalar/vector regs types * Sync kernels * Sync kernels * Avoid applying non-zero offset to null pointer runtime error * Remove debug include * TMOP rename coef to limit * Comments. * minor * changelog * Replace TMOP's MFEM_FOREACH_THREAD with MFEM_FOREACH_THREAD_DIRECT * add some missing metric IDs * Revert branch-history * Add missing MFEM_SYNC_THREAD in kernels Verify TMOP isfinite energy * UseDevice for local vectors * make style * No grids in TMOP_DatcSize kernels * Remove isfinite assertions Cleanup unused header files Add 3D energy finite verifications * Filter out TMOP PA tests --------- Co-authored-by: camierjs <camierjs@Io> Co-authored-by: Vladimir Z Tomov <tomov2@llnl.gov> Co-authored-by: Mittal, Ketan <mittal3@llnl.gov>
1346 lines
44 KiB
C++
1346 lines
44 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.
|
|
|
|
#define CATCH_CONFIG_RUNNER
|
|
#include "mfem.hpp"
|
|
#include "run_unit_tests.hpp"
|
|
|
|
#ifdef _WIN32
|
|
#define _USE_MATH_DEFINES
|
|
#include <cmath>
|
|
#else
|
|
// Avoiding MSVC error C2491: 'definition of dllimport function not allowed'
|
|
#include "fem/qinterp/det.hpp" // IWYU pragma: keep
|
|
#include "fem/qinterp/grad.hpp" // IWYU pragma: keep
|
|
#include "fem/qinterp/eval.hpp" // IWYU pragma: keep
|
|
#include "fem/integ/bilininteg_mass_kernels.hpp" // IWYU pragma: keep
|
|
#endif
|
|
|
|
#include <iostream>
|
|
#include <list>
|
|
#include <memory>
|
|
|
|
#include "miniapps/meshing/mesh-optimizer.hpp"
|
|
|
|
#if defined(MFEM_TMOP_PA_MPI) && !defined(MFEM_USE_MPI)
|
|
#error "Cannot use MFEM_TMOP_PA_MPI without MFEM_USE_MPI!"
|
|
#endif
|
|
|
|
#if defined(MFEM_USE_MPI) && defined(MFEM_TMOP_PA_MPI)
|
|
#define PFesGetParMeshGetComm(pfes) pfes.GetComm()
|
|
#define SetDiscreteTargetSize SetParDiscreteTargetSize
|
|
#define SetDiscreteTargetAspectRatio SetParDiscreteTargetAspectRatio
|
|
#define GradientClass HypreParMatrix
|
|
#else
|
|
#define ParMesh Mesh
|
|
#define ParGridFunction GridFunction
|
|
#define ParNonlinearForm NonlinearForm
|
|
#define ParFiniteElementSpace FiniteElementSpace
|
|
#define GetParGridFunctionEnergy GetGridFunctionEnergy
|
|
#define PFesGetParMeshGetComm(...)
|
|
#define MPI_Allreduce(src, dst, ...) *dst = *src
|
|
#define SetDiscreteTargetSize SetSerialDiscreteTargetSize
|
|
#define SetDiscreteTargetAspectRatio SetSerialDiscreteTargetAspectRatio
|
|
#define GradientClass SparseMatrix
|
|
#define ParEnableNormalization EnableNormalization
|
|
#endif
|
|
|
|
using namespace mfem;
|
|
|
|
namespace mfem
|
|
{
|
|
|
|
struct Req
|
|
{
|
|
real_t dot;
|
|
real_t diag;
|
|
real_t min_detJ;
|
|
real_t bal_weights;
|
|
real_t met_normal, lim_normal;
|
|
real_t init_energy, final_energy;
|
|
};
|
|
|
|
int tmop(int id, Req &res, int argc, char *argv[])
|
|
{
|
|
bool pa = false;
|
|
const char *mesh_file = nullptr;
|
|
int mesh_poly_deg = 1;
|
|
int rs_levels = 0;
|
|
int metric_id = 1;
|
|
int target_id = 1;
|
|
int quad_type = 1;
|
|
int quad_order = 2;
|
|
int newton_iter = 100;
|
|
real_t newton_rtol = 1e-10;
|
|
real_t linsol_rtol = 1e-10;
|
|
int lin_solver = 2;
|
|
int max_lin_iter = 100;
|
|
real_t lim_const = 0.0;
|
|
int lim_type = 0;
|
|
bool normalization = false;
|
|
real_t jitter = 0.0;
|
|
bool diag = true;
|
|
int newton_loop = 1;
|
|
int combomet = 0;
|
|
bool bal_expl_combo = false;
|
|
bool periodic = false;
|
|
const int mesh_node_order = Ordering::byNODES;
|
|
|
|
constexpr int verbosity_level = 0;
|
|
constexpr int seed = 0x100001b3;
|
|
constexpr bool move_bnd = false;
|
|
constexpr bool fdscheme = false;
|
|
constexpr bool integ_over_targ = true;
|
|
constexpr bool exactaction = false;
|
|
|
|
OptionsParser args(argc, argv);
|
|
args.AddOption(&pa, "-pa", "--pa", "-no-pa", "--no-pa", "");
|
|
args.AddOption(&mesh_file, "-m", "--mesh", "");
|
|
args.AddOption(&mesh_poly_deg, "-o", "--order", "");
|
|
args.AddOption(&rs_levels, "-rs", "--refine-serial", "");
|
|
args.AddOption(&metric_id, "-mid", "--metric-id", "");
|
|
args.AddOption(&target_id, "-tid", "--target-id", "");
|
|
args.AddOption(&quad_type, "-qt", "--quad-type", "");
|
|
args.AddOption(&quad_order, "-qo", "--quad_order", "");
|
|
args.AddOption(&newton_iter, "-ni", "--newton-iters", "");
|
|
args.AddOption(&newton_loop, "-nl", "--newton-loops", "");
|
|
args.AddOption(&newton_rtol, "-nrtol", "--newton-rel-tolerance", "");
|
|
args.AddOption(&linsol_rtol, "-lrtol", "--linsol-rel-tolerance", "");
|
|
args.AddOption(&lin_solver, "-ls", "--lin-solver", "");
|
|
args.AddOption(&max_lin_iter, "-li", "--lin-iter", "");
|
|
args.AddOption(&lim_const, "-lc", "--limit-const", "");
|
|
args.AddOption(&lim_type, "-lt", "--limit-type", "");
|
|
args.AddOption(&normalization, "-nor", "--normalization",
|
|
"-no-nor", "--no-normalization", "");
|
|
args.AddOption(&jitter, "-ji", "--jitter", "");
|
|
args.AddOption(&diag, "-diag", "--diag", "-no-diag", "--no-diag", "");
|
|
args.AddOption(&combomet, "-cmb", "--combo-type", "");
|
|
args.AddOption(&bal_expl_combo, "-bec", "--balance-explicit-combo",
|
|
"-no-bec", "--no-balance-explicit-combo", "");
|
|
args.AddOption(&periodic, "-per", "--periodic", "-no-per", "--no-periodic", "");
|
|
args.Parse();
|
|
if (!args.Good())
|
|
{
|
|
args.PrintOptions(mfem::out);
|
|
if (id == 0) { args.PrintUsage(cout); }
|
|
return 1;
|
|
}
|
|
if (verbosity_level > 0)
|
|
{
|
|
if (id == 0) { args.PrintOptions(cout); }
|
|
}
|
|
|
|
// Initialize and refine the starting mesh.
|
|
Mesh smesh(mesh_file, 1, 1, false);
|
|
for (int lev = 0; lev < rs_levels; lev++) { smesh.UniformRefinement(); }
|
|
const int dim = smesh.Dimension();
|
|
|
|
if (periodic)
|
|
{
|
|
auto s = smesh.GetNodalFESpace();
|
|
REQUIRE((s && s->IsDGSpace()));
|
|
}
|
|
|
|
ParMesh mesh([](Mesh &mesh)
|
|
{
|
|
#if defined(MFEM_USE_MPI) && defined(MFEM_TMOP_PA_MPI)
|
|
return ParMesh(MPI_COMM_WORLD, mesh);
|
|
#else
|
|
return Mesh(mesh);
|
|
#endif
|
|
} (smesh));
|
|
smesh.Clear();
|
|
|
|
// Define a FE space on the mesh, based on the input order.
|
|
REQUIRE(mesh_poly_deg > 0);
|
|
std::unique_ptr<FiniteElementCollection> fec;
|
|
if (periodic)
|
|
{
|
|
fec.reset(new L2_FECollection(mesh_poly_deg, dim, BasisType::GaussLobatto));
|
|
}
|
|
else { fec.reset(new H1_FECollection(mesh_poly_deg, dim)); }
|
|
ParFiniteElementSpace fespace(&mesh, fec.get(), dim, mesh_node_order);
|
|
|
|
// Make the starting mesh curved.
|
|
mesh.SetNodalFESpace(&fespace);
|
|
|
|
// Get the mesh nodes (vertices and other DOFs in the FE space)
|
|
ParGridFunction x(&fespace), x0_before_jitter(&fespace);
|
|
mesh.SetNodalGridFunction(&x);
|
|
|
|
// When the target is GIVEN_SHAPE_AND_SIZE, we want to call tc->SetNodes()
|
|
// with something other than x0 (otherwise all metrics would be 0).
|
|
x0_before_jitter = x;
|
|
|
|
// We create an H1 space for the mesh displacement.
|
|
H1_FECollection fec_h1(mesh_poly_deg, dim);
|
|
ParFiniteElementSpace fes_h1(&mesh, &fec_h1, dim, mesh_node_order);
|
|
ParGridFunction dx(&fes_h1); dx = 0.0;
|
|
|
|
// Define a vector representing the minimal local mesh size in the mesh nodes.
|
|
// In addition, compute average mesh size and total volume.
|
|
Vector h0(fes_h1.GetNDofs());
|
|
h0 = infinity();
|
|
real_t mesh_volume = 0.0;
|
|
Array<int> dofs;
|
|
for (int i = 0; i < mesh.GetNE(); i++)
|
|
{
|
|
// Get the local scalar element degrees of freedom in dofs.
|
|
fes_h1.GetElementDofs(i, dofs);
|
|
// Adjust the value of h0 in dofs based on the local mesh size.
|
|
const real_t hi = mesh.GetElementSize(i);
|
|
for (int j = 0; j < dofs.Size(); j++)
|
|
{
|
|
h0(dofs[j]) = min(h0(dofs[j]), hi);
|
|
}
|
|
mesh_volume += mesh.GetElementVolume(i);
|
|
}
|
|
const real_t small_phys_size = pow(mesh_volume, 1.0 / dim) / 100.0;
|
|
|
|
// Add a random perturbation to the nodes in the interior of the domain.
|
|
if (jitter > 0)
|
|
{
|
|
ParGridFunction rdm(&fes_h1);
|
|
rdm.Randomize(seed);
|
|
rdm -= 0.25;
|
|
rdm *= jitter;
|
|
rdm.HostReadWrite();
|
|
// Scale the random values to be of order of the local mesh size.
|
|
for (int i = 0; i < fes_h1.GetNDofs(); i++)
|
|
{
|
|
for (int d = 0; d < dim; d++)
|
|
{
|
|
rdm(fes_h1.DofToVDof(i, d)) *= h0(i);
|
|
}
|
|
}
|
|
// Set the boundary values to zero.
|
|
Array<int> vdofs;
|
|
for (int i = 0; i < fes_h1.GetNBE(); i++)
|
|
{
|
|
fes_h1.GetBdrElementVDofs(i, vdofs);
|
|
for (int j = 0; j < vdofs.Size(); j++) { rdm(vdofs[j]) = 0.0; }
|
|
}
|
|
|
|
if (periodic)
|
|
{
|
|
// For H1 the perturbation is controlled by the true nodes.
|
|
rdm.SetFromTrueVector();
|
|
ParGridFunction rdm_l2(&fespace);
|
|
rdm_l2.ProjectGridFunction(rdm);
|
|
x -= rdm_l2;
|
|
}
|
|
else
|
|
{
|
|
x -= rdm;
|
|
// For H1 the perturbation is controlled by the true nodes.
|
|
x.SetTrueVector();
|
|
x.SetFromTrueVector();
|
|
}
|
|
}
|
|
|
|
// Store the starting (prior to the optimization) positions.
|
|
ParGridFunction x0(x);
|
|
|
|
// Form the integrator that uses the chosen metric and target.
|
|
std::unique_ptr<TMOP_QualityMetric> metric;
|
|
switch (metric_id)
|
|
{
|
|
case 1: metric.reset(new TMOP_Metric_001); break;
|
|
case 2: metric.reset(new TMOP_Metric_002); break;
|
|
case 7: metric.reset(new TMOP_Metric_007); break;
|
|
case 56: metric.reset(new TMOP_Metric_056); break;
|
|
case 77: metric.reset(new TMOP_Metric_077); break;
|
|
case 80: metric.reset(new TMOP_Metric_080(0.5)); break; // combo
|
|
case 94: metric.reset(new TMOP_Metric_094); break; // combo
|
|
case 302: metric.reset(new TMOP_Metric_302); break;
|
|
case 303: metric.reset(new TMOP_Metric_303); break;
|
|
case 315: metric.reset(new TMOP_Metric_315); break;
|
|
case 318: metric.reset(new TMOP_Metric_318); break;
|
|
case 321: metric.reset(new TMOP_Metric_321); break;
|
|
case 332: metric.reset(new TMOP_Metric_332(0.5)); break; // combo
|
|
case 338: metric.reset(new TMOP_Metric_338); break; // combo
|
|
default:
|
|
{
|
|
cout << "Unknown metric_id: " << metric_id << endl;
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
TargetConstructor::TargetType target_t;
|
|
std::unique_ptr<TargetConstructor> target_c = nullptr;
|
|
std::unique_ptr<HessianCoefficient> adapt_coeff = nullptr;
|
|
const int ind_fec_order =
|
|
(target_id >= 5 && target_id <= 8 && !fdscheme) ?
|
|
1 : mesh_poly_deg;
|
|
H1_FECollection ind_fec(ind_fec_order, dim);
|
|
ParFiniteElementSpace ind_fes(&mesh, &ind_fec);
|
|
ParFiniteElementSpace ind_fesv(&mesh, &ind_fec, dim);
|
|
ParGridFunction size(&ind_fes), ori(&ind_fes);
|
|
ParGridFunction aspr3d(&ind_fesv);
|
|
|
|
const AssemblyLevel al =
|
|
pa ? AssemblyLevel::PARTIAL : AssemblyLevel::LEGACY;
|
|
|
|
switch (target_id)
|
|
{
|
|
case 1: target_t = TargetConstructor::IDEAL_SHAPE_UNIT_SIZE; break;
|
|
case 2: target_t = TargetConstructor::IDEAL_SHAPE_EQUAL_SIZE; break;
|
|
case 3: target_t = TargetConstructor::IDEAL_SHAPE_GIVEN_SIZE; break;
|
|
case 4: // Analytic
|
|
{
|
|
target_t = TargetConstructor::GIVEN_FULL;
|
|
auto tc = new AnalyticAdaptTC(target_t);
|
|
adapt_coeff.reset(new HessianCoefficient(dim, metric_id));
|
|
tc->SetAnalyticTargetSpec(nullptr, nullptr, adapt_coeff.get());
|
|
target_c.reset(tc);
|
|
break;
|
|
}
|
|
case 5: // Discrete size 2D or 3D
|
|
{
|
|
target_t = TargetConstructor::IDEAL_SHAPE_GIVEN_SIZE;
|
|
auto tc = new DiscreteAdaptTC(target_t);
|
|
tc->SetAdaptivityEvaluator(new AdvectorCG(al));
|
|
ConstructSizeGF(size);
|
|
tc->SetDiscreteTargetSize(size);
|
|
tc->SetMinSizeForTargets(size.Min());
|
|
target_c.reset(tc);
|
|
break;
|
|
}
|
|
case 7: // Discrete aspect-ratio 3D
|
|
{
|
|
target_t = TargetConstructor::GIVEN_SHAPE_AND_SIZE;
|
|
auto tc = new DiscreteAdaptTC(target_t);
|
|
tc->SetAdaptivityEvaluator(new AdvectorCG(al));
|
|
VectorFunctionCoefficient fd_aspr3d(dim, discrete_aspr_3d);
|
|
aspr3d.ProjectCoefficient(fd_aspr3d);
|
|
tc->SetDiscreteTargetAspectRatio(aspr3d);
|
|
target_c.reset(tc);
|
|
break;
|
|
}
|
|
case 8: // fully specified through the initial mesh, 2D or 3D.
|
|
{
|
|
target_t = TargetConstructor::GIVEN_SHAPE_AND_SIZE;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
cout << "Unknown target_id: " << target_id << endl;
|
|
return 3;
|
|
}
|
|
}
|
|
#if defined(MFEM_USE_MPI) && defined(MFEM_TMOP_PA_MPI)
|
|
if (target_c == nullptr)
|
|
{
|
|
target_c.reset(new TargetConstructor(target_t, MPI_COMM_WORLD));
|
|
}
|
|
#else
|
|
if (target_c == nullptr) { target_c.reset(new TargetConstructor(target_t)); }
|
|
#endif
|
|
target_c->SetNodes(x0_before_jitter);
|
|
|
|
auto tmop_integ = new TMOP_Integrator(metric.get(), target_c.get());
|
|
tmop_integ->IntegrateOverTarget(integ_over_targ);
|
|
tmop_integ->SetExactActionFlag(exactaction);
|
|
|
|
// Setup the quadrature rules for the TMOP integrator.
|
|
IntegrationRules *irules = nullptr;
|
|
IntegrationRules IntRulesLo(0, Quadrature1D::GaussLobatto);
|
|
IntegrationRules IntRulesCU(0, Quadrature1D::ClosedUniform);
|
|
switch (quad_type)
|
|
{
|
|
case 1: irules = &IntRulesLo; break;
|
|
case 2: irules = &IntRules; break;
|
|
case 3: irules = &IntRulesCU; break;
|
|
default: cout << "Unknown quad_type: " << quad_type << endl; return 3;
|
|
}
|
|
tmop_integ->SetIntegrationRules(*irules, quad_order);
|
|
|
|
// Automatically balanced gamma in composite metrics.
|
|
res.bal_weights = 0.0;
|
|
auto metric_combo = dynamic_cast<TMOP_Combo_QualityMetric *>(metric.get());
|
|
if (metric_combo && bal_expl_combo)
|
|
{
|
|
Vector bal_weights;
|
|
auto ir = irules->Get(mesh.GetTypicalElementGeometry(), quad_order);
|
|
metric_combo->ComputeBalancedWeights(x, *target_c, bal_weights, pa, &ir);
|
|
metric_combo->SetWeights(bal_weights);
|
|
res.bal_weights = bal_weights.Norml2();
|
|
}
|
|
|
|
// Limit the node movement.
|
|
// The limiting distances can be given by a general function of space.
|
|
ParFiniteElementSpace dist_fespace(&mesh, &fec_h1); // scalar space
|
|
ParGridFunction dist(&dist_fespace);
|
|
dist = 1.0;
|
|
// The small_phys_size is relevant only with proper normalization.
|
|
if (normalization) { dist = small_phys_size; }
|
|
auto coeff_lim_func = [&](const Vector &x) { return x(0) + lim_const; };
|
|
FunctionCoefficient lim_coeff(coeff_lim_func);
|
|
if (lim_const != 0.0)
|
|
{
|
|
if (lim_type == 0) { tmop_integ->EnableLimiting(x0, dist, lim_coeff); }
|
|
else
|
|
{
|
|
tmop_integ->EnableLimiting(x0, dist, lim_coeff,
|
|
new TMOP_ExponentialLimiter);
|
|
}
|
|
}
|
|
|
|
// Setup the NonlinearForm which defines the integral of interest.
|
|
ParNonlinearForm a(&fes_h1);
|
|
a.SetAssemblyLevel(pa ? AssemblyLevel::PARTIAL : AssemblyLevel::LEGACY);
|
|
|
|
std::unique_ptr<FunctionCoefficient> metric_coeff1 = nullptr;
|
|
std::unique_ptr<TMOP_QualityMetric> metric2 = nullptr;
|
|
std::unique_ptr<TargetConstructor> target_c2 = nullptr;
|
|
FunctionCoefficient metric_coeff2(weight_fun);
|
|
TMOPComboIntegrator *combo = nullptr;
|
|
|
|
if (combomet > 0)
|
|
{
|
|
// First metric.
|
|
auto coeff_1_func = [&](const Vector &x) { return x(0) + M_PI; };
|
|
metric_coeff1.reset(new FunctionCoefficient(coeff_1_func));
|
|
tmop_integ->SetCoefficient(*metric_coeff1);
|
|
|
|
// Second metric.
|
|
if (dim == 2) { metric2.reset(new TMOP_Metric_077); }
|
|
else { metric2.reset(new TMOP_Metric_315); }
|
|
TMOP_Integrator *tmop_integ2 = nullptr;
|
|
if (combomet == 1)
|
|
{
|
|
target_c2.reset(
|
|
new TargetConstructor(TargetConstructor::IDEAL_SHAPE_EQUAL_SIZE));
|
|
target_c2->SetVolumeScale(0.01);
|
|
target_c2->SetNodes(x0); assert(false && "?");
|
|
tmop_integ2 = new TMOP_Integrator(metric2.get(), target_c2.get());
|
|
tmop_integ2->SetCoefficient(metric_coeff2);
|
|
}
|
|
else { tmop_integ2 = new TMOP_Integrator(metric2.get(), target_c.get()); }
|
|
tmop_integ2->IntegrateOverTarget(integ_over_targ);
|
|
tmop_integ2->SetIntegrationRules(*irules, quad_order);
|
|
if (fdscheme) { tmop_integ2->EnableFiniteDifferences(x); }
|
|
tmop_integ2->SetExactActionFlag(exactaction);
|
|
|
|
combo = new TMOPComboIntegrator;
|
|
combo->AddTMOPIntegrator(tmop_integ);
|
|
combo->AddTMOPIntegrator(tmop_integ2);
|
|
|
|
if (normalization) { combo->ParEnableNormalization(x0); }
|
|
if (lim_const != 0.0) { combo->EnableLimiting(x0, dist, lim_coeff); }
|
|
|
|
a.AddDomainIntegrator(combo);
|
|
}
|
|
else { a.AddDomainIntegrator(tmop_integ); }
|
|
|
|
// The PA setup must be performed after all integrators have been added.
|
|
if (pa) { a.Setup(); }
|
|
|
|
// Has to be after the enabling of the limiting / alignment, as it computes
|
|
// normalization factors for these terms as well.
|
|
if (normalization)
|
|
{
|
|
tmop_integ->ParEnableNormalization(x0);
|
|
if (combomet > 0) { combo->ParEnableNormalization(x0); }
|
|
}
|
|
real_t unused;
|
|
tmop_integ->GetNormalizationFactors(res.met_normal, res.lim_normal, unused);
|
|
|
|
// Compute the minimum det(J) of the starting mesh.
|
|
real_t min_detJ = infinity();
|
|
const int NE = mesh.GetNE();
|
|
for (int i = 0; i < NE; i++)
|
|
{
|
|
const IntegrationRule &ir =
|
|
irules->Get(fespace.GetFE(i)->GetGeomType(), quad_order);
|
|
auto transf = mesh.GetElementTransformation(i);
|
|
for (int j = 0; j < ir.GetNPoints(); j++)
|
|
{
|
|
transf->SetIntPoint(&ir.IntPoint(j));
|
|
min_detJ = min(min_detJ, transf->Jacobian().Det());
|
|
}
|
|
}
|
|
real_t minJ0;
|
|
MPI_Allreduce(&min_detJ, &minJ0, 1, MPITypeMap<real_t>::mpi_type, MPI_MIN,
|
|
MPI_COMM_WORLD);
|
|
min_detJ = minJ0;
|
|
REQUIRE(min_detJ > 0.0);
|
|
res.min_detJ = min_detJ;
|
|
|
|
if (periodic) { tmop_integ->SetInitialMeshPos(&x0); }
|
|
const real_t init_energy = a.GetParGridFunctionEnergy(periodic ? dx : x);
|
|
res.init_energy = init_energy;
|
|
|
|
// Fix all boundary nodes
|
|
REQUIRE(move_bnd == false);
|
|
Array<int> ess_bdr(periodic ? 0 : mesh.bdr_attributes.Max());
|
|
ess_bdr = 1;
|
|
if (!periodic) { a.SetEssentialBC(ess_bdr); }
|
|
|
|
// Diagonal test, skip if combo
|
|
Vector &xt(x.GetTrueVector());
|
|
Vector d(fespace.GetTrueVSize());
|
|
d.UseDevice(true);
|
|
res.diag = 0.0;
|
|
if (diag && combomet == 0)
|
|
{
|
|
if (pa) { a.GetGradient(xt).AssembleDiagonal(d); }
|
|
else
|
|
{
|
|
ParNonlinearForm nlf_fa(&fes_h1);
|
|
auto *nlfi_fa = new TMOP_Integrator(metric.get(), target_c.get());
|
|
nlfi_fa->SetIntegrationRules(*irules, quad_order);
|
|
if (normalization) { nlfi_fa->ParEnableNormalization(x0); }
|
|
if (lim_const != 0.0)
|
|
{
|
|
if (lim_type == 0) { nlfi_fa->EnableLimiting(x0, dist, lim_coeff); }
|
|
else
|
|
{
|
|
nlfi_fa->EnableLimiting(x0, dist, lim_coeff,
|
|
new TMOP_ExponentialLimiter);
|
|
}
|
|
}
|
|
nlf_fa.AddDomainIntegrator(nlfi_fa);
|
|
nlf_fa.SetEssentialBC(ess_bdr);
|
|
dynamic_cast<GradientClass &>(nlf_fa.GetGradient(xt)).GetDiag(d);
|
|
}
|
|
res.diag = d * d;
|
|
}
|
|
|
|
// Linear solver for the system's Jacobian
|
|
std::unique_ptr<Solver> S = nullptr, S_prec = nullptr;
|
|
if (lin_solver == 0) { S.reset(new DSmoother(1, 1.0, max_lin_iter)); }
|
|
else if (lin_solver == 1)
|
|
{
|
|
auto cg = new CGSolver(PFesGetParMeshGetComm(fes_h1));
|
|
cg->SetMaxIter(max_lin_iter);
|
|
cg->SetRelTol(linsol_rtol);
|
|
cg->SetAbsTol(0.0);
|
|
cg->SetPrintLevel(verbosity_level >= 2 ? 3 : -1);
|
|
S.reset(cg);
|
|
}
|
|
else
|
|
{
|
|
auto minres = new MINRESSolver(PFesGetParMeshGetComm(fes_h1));
|
|
minres->SetMaxIter(max_lin_iter);
|
|
minres->SetRelTol(linsol_rtol);
|
|
minres->SetAbsTol(0.0);
|
|
minres->SetPrintLevel(verbosity_level >= 2 ? 3 : -1);
|
|
if (lin_solver == 3 || lin_solver == 4)
|
|
{
|
|
if (pa)
|
|
{
|
|
MFEM_VERIFY(lin_solver != 4, "PA l1-Jacobi is not implemented");
|
|
auto js = new OperatorJacobiSmoother;
|
|
js->SetPositiveDiagonal(true);
|
|
S_prec.reset(js);
|
|
}
|
|
#if defined(MFEM_USE_MPI) && defined(MFEM_TMOP_PA_MPI)
|
|
else
|
|
{
|
|
auto hs = new HypreSmoother;
|
|
hs->SetType((lin_solver == 3)
|
|
? HypreSmoother::Jacobi
|
|
: HypreSmoother::l1Jacobi, 1);
|
|
S_prec.reset(hs);
|
|
}
|
|
#else
|
|
else
|
|
{
|
|
auto ds = new DSmoother((lin_solver == 3) ? 0 : 1, 1.0, 1);
|
|
ds->SetPositiveDiagonal(true);
|
|
S_prec.reset(ds);
|
|
}
|
|
#endif
|
|
minres->SetPreconditioner(*S_prec);
|
|
}
|
|
S.reset(minres);
|
|
}
|
|
|
|
// Perform the nonlinear optimization.
|
|
const IntegrationRule &ir =
|
|
irules->Get(mesh.GetTypicalElementGeometry(), quad_order);
|
|
#if defined(MFEM_USE_MPI) && defined(MFEM_TMOP_PA_MPI)
|
|
TMOPNewtonSolver solver(PFesGetParMeshGetComm(fes_h1), ir);
|
|
#else
|
|
TMOPNewtonSolver solver(ir);
|
|
#endif
|
|
// Provide all integration rules in case of a mixed mesh.
|
|
solver.SetIntegrationRules(*irules, quad_order);
|
|
// Specify linear solver when we use a Newton-based solver.
|
|
solver.SetPreconditioner(*S);
|
|
solver.SetMinDetPtr(&min_detJ);
|
|
solver.SetMaxIter(newton_iter);
|
|
solver.SetRelTol(newton_rtol);
|
|
solver.SetAbsTol(0.0);
|
|
solver.SetPrintLevel(verbosity_level >= 1 ? 3 : -1);
|
|
solver.SetOperator(a);
|
|
|
|
Vector x_init(x), b(0);
|
|
b.UseDevice(true);
|
|
|
|
for (int i = 0; i < newton_loop; i++)
|
|
{
|
|
x = x_init;
|
|
x.SetTrueVector();
|
|
|
|
auto datc = dynamic_cast<DiscreteAdaptTC *>(target_c.get());
|
|
if (datc && target_id == 5) { datc->SetDiscreteTargetSize(size); }
|
|
if (datc && target_id == 7)
|
|
{
|
|
datc->SetDiscreteTargetAspectRatio(aspr3d);
|
|
}
|
|
|
|
dist *= 0.93;
|
|
if (normalization) { dist = small_phys_size; }
|
|
|
|
if (lim_const != 0.0)
|
|
{
|
|
if (lim_type == 0) { tmop_integ->EnableLimiting(x0, dist, lim_coeff); }
|
|
else
|
|
{
|
|
tmop_integ->EnableLimiting(x0, dist, lim_coeff,
|
|
new TMOP_ExponentialLimiter);
|
|
}
|
|
}
|
|
|
|
a.Setup();
|
|
|
|
if (normalization) { tmop_integ->ParEnableNormalization(x); }
|
|
|
|
solver.Mult(b, x.GetTrueVector());
|
|
x.SetFromTrueVector();
|
|
|
|
REQUIRE(solver.GetConverged());
|
|
|
|
// Report the final energy of the functional.
|
|
if (periodic)
|
|
{
|
|
ParGridFunction dx_L2(x); dx_L2 -= x0;
|
|
dx.ProjectGridFunction(dx_L2);
|
|
tmop_integ->SetInitialMeshPos(&x0);
|
|
res.final_energy = a.GetParGridFunctionEnergy(dx);
|
|
}
|
|
else
|
|
{
|
|
res.final_energy = a.GetParGridFunctionEnergy(x);
|
|
}
|
|
}
|
|
|
|
Vector &x_t(x.GetTrueVector());
|
|
real_t x_t_dot = x_t * x_t, dot;
|
|
MPI_Allreduce(&x_t_dot, &dot, 1, MPITypeMap<real_t>::mpi_type, MPI_SUM,
|
|
MPI_COMM_WORLD);
|
|
res.dot = dot;
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
} // namespace mfem
|
|
|
|
static inline int argn(const char *argv[], int argc = 0)
|
|
{
|
|
while (argv[argc]) { argc += 1; }
|
|
return argc;
|
|
}
|
|
|
|
static inline void req_tmop(int id, const char *args[], Req &res)
|
|
{
|
|
REQUIRE(tmop(id, res, argn(args), const_cast<char **>(args)) == 0);
|
|
}
|
|
|
|
#define DEFAULT_ARGS const char *args[] = { "tmop_pa_tests", "-pa", "-m", "mesh", \
|
|
"-o", "0", "-rs", "0", "-mid", "0", "-tid", "0", "-qt", "1", "-qo", "0", \
|
|
"-ni", "10", "-nl", "1", "-nrtol", "1e-8", "-lrtol", "1e-12", "-ls", "2", "-li", "100", "-lc", "0", \
|
|
"-lt", "0", "-no-nor", "-ji", "0", "-diag", "-cmb", "0", "-no-bec", "-no-per", nullptr }
|
|
|
|
constexpr int ALV = 1, MSH = 3, POR = 5, RS = 7, MID = 9, TID = 11, QTY = 13,
|
|
QOR = 15, NI = 17, NL = 19, NRTOL = 21, LRTOL = 23, LS = 25, LI = 27, LC = 29,
|
|
LT = 31, NOR = 32, JI = 34, DIAG = 35, CMB = 37, BEC = 38, PER = 39;
|
|
|
|
static inline void dump_args(int id, const char *args[])
|
|
{
|
|
if (id != 0) { return; }
|
|
const char *format =
|
|
"tmop_pa_tests %6.6s -m %s -o %s -rs %s -mid %s -tid %s -qt %s -qo %s "
|
|
"-ni %s -nl %s -nrtol %s -lrtol %s -ls %s -li %s -lc %s -lt %s %s -ji %s "
|
|
"%s -cmb %s %s %s\n";
|
|
printf(format, args[ALV], args[MSH], args[POR], args[RS], args[MID], args[TID],
|
|
args[QTY], args[QOR], args[NI], args[NL], args[NRTOL], args[LRTOL],
|
|
args[LS], args[LI], args[LC], args[LT], args[NOR], args[JI], args[DIAG],
|
|
args[CMB], args[BEC], args[PER]);
|
|
fflush(nullptr);
|
|
}
|
|
|
|
static inline void tmop_require(int id, const char *args[])
|
|
{
|
|
Req res[2];
|
|
constexpr real_t eps = 4e-12;
|
|
(args[ALV] = "-pa", dump_args(id, args), req_tmop(id, args, res[0]));
|
|
(args[ALV] = "-no-pa", dump_args(id, args), req_tmop(id, args, res[1]));
|
|
REQUIRE(res[0].dot == MFEM_Approx(res[1].dot));
|
|
REQUIRE(res[0].diag == MFEM_Approx(res[1].diag));
|
|
REQUIRE(res[0].min_detJ == MFEM_Approx(res[1].min_detJ));
|
|
REQUIRE(res[0].met_normal == MFEM_Approx(res[1].met_normal));
|
|
REQUIRE(res[0].lim_normal == MFEM_Approx(res[1].lim_normal));
|
|
REQUIRE(res[0].bal_weights == MFEM_Approx(res[1].bal_weights));
|
|
REQUIRE(res[0].init_energy == MFEM_Approx(res[1].init_energy));
|
|
REQUIRE(res[0].final_energy == MFEM_Approx(res[1].final_energy, eps));
|
|
}
|
|
|
|
static constexpr int SZ = 32;
|
|
|
|
static inline const char *itoa(const int i, char *buf)
|
|
{
|
|
const int rtn = std::snprintf(buf, SZ, "%d", i);
|
|
if (rtn < 0) { MFEM_ABORT("snprintf error!"); }
|
|
MFEM_ASSERT(rtn < SZ, "snprintf overflow!");
|
|
return buf;
|
|
}
|
|
|
|
static inline const char *dtoa(const real_t d, char *buf)
|
|
{
|
|
const int rtn = std::snprintf(buf, SZ, "%g", d);
|
|
if (rtn < 0) { MFEM_ABORT("snprintf error!"); }
|
|
MFEM_ASSERT(rtn < SZ, "snprintf overflow!");
|
|
return buf;
|
|
}
|
|
|
|
class Launch
|
|
{
|
|
using list_t = std::list<int>;
|
|
|
|
public:
|
|
class Args
|
|
{
|
|
friend class Launch;
|
|
|
|
private:
|
|
const char *name = nullptr;
|
|
const char *mesh = "../../data/star.mesh";
|
|
int newton_iter = 100;
|
|
real_t newton_rtol = 1e-6;
|
|
real_t linsol_rtol = 1e-8;
|
|
int rs_levels = 0;
|
|
int linsol_iter = 100;
|
|
int combo = 0;
|
|
bool diag = true;
|
|
bool bal_expl_combo = false;
|
|
bool normalization = false;
|
|
bool periodic = false;
|
|
real_t lim_const = 0.0;
|
|
int lim_type = 0;
|
|
real_t jitter = 0.0;
|
|
list_t order = { 1, 2, 3, 4 };
|
|
list_t target_id = { 1, 2, 3 };
|
|
list_t metric_id = { 1, 2 };
|
|
list_t quad_order = { 2, 4, 8 };
|
|
list_t lin_solver = { 3, 2, 1 };
|
|
list_t newton_loop = { 1, 3 };
|
|
|
|
public:
|
|
Args(const char *name = nullptr): name(name) {}
|
|
Args &MESH(const char *arg) { mesh = arg; return *this; }
|
|
// int
|
|
Args &NEWTON_ITERATIONS(const int arg) { newton_iter = arg; return *this; }
|
|
Args &LINSOL_ITERATIONS(const int arg) { linsol_iter = arg; return *this; }
|
|
Args &REFINE(const int arg) { rs_levels = arg; return *this; }
|
|
Args &CMB(const int arg) { combo = arg; return *this; }
|
|
Args &LIMIT_TYPE(const int arg) { lim_type = arg; return *this; }
|
|
// bool
|
|
Args &NORMALIZATION() { normalization = true; return *this; }
|
|
Args &DIAGONAL(const bool arg) { diag = arg; return *this; }
|
|
Args &BALANCE_EXPLICIT_COMBO() { bal_expl_combo = true; return *this; }
|
|
Args &PERIODIC() { periodic = true; return *this; }
|
|
// real_t
|
|
Args &NEWTON_RTOLERANCE(const real_t arg) { newton_rtol = arg; return *this; }
|
|
Args &LINSOL_RTOLERANCE(const real_t arg) { linsol_rtol = arg; return *this; }
|
|
Args &LIMITING(const real_t arg) { lim_const = arg; return *this; }
|
|
Args &JI(const real_t arg) { jitter = arg; return *this; }
|
|
// lists
|
|
Args &POR(list_t arg) { order = arg; return *this; }
|
|
Args &TID(list_t arg) { target_id = arg; return *this; }
|
|
Args &MID(list_t arg) { metric_id = arg; return *this; }
|
|
Args &QOR(list_t arg) { quad_order = arg; return *this; }
|
|
Args &LS(list_t arg) { lin_solver = arg; return *this; }
|
|
Args &NL(list_t arg) { newton_loop = arg; return *this; }
|
|
};
|
|
const char *name, *mesh;
|
|
int NEWTON_ITERATIONS, LINSOL_ITERATIONS, REFINE, COMBO, LIMIT_TYPE;
|
|
bool NORMALIZATION, DIAGONAL, BAL_EXPL_COMBO, PERIODIC;
|
|
real_t NEWTON_RTOLERANCE, LINSOL_RTOLERANCE, LIMITING, JITTER;
|
|
list_t P_ORDERS, TARGET_IDS, METRIC_IDS, Q_ORDERS, LINEAR_SOLVERS, NEWTON_LOOPS;
|
|
|
|
public:
|
|
Launch(Args a = Args()):
|
|
name(a.name), mesh(a.mesh),
|
|
// int
|
|
NEWTON_ITERATIONS(a.newton_iter),
|
|
LINSOL_ITERATIONS(a.linsol_iter),
|
|
REFINE(a.rs_levels), COMBO(a.combo), LIMIT_TYPE(a.lim_type),
|
|
// bool
|
|
NORMALIZATION(a.normalization),
|
|
DIAGONAL(a.diag),
|
|
BAL_EXPL_COMBO(a.bal_expl_combo),
|
|
PERIODIC(a.periodic),
|
|
// real_t
|
|
NEWTON_RTOLERANCE(a.newton_rtol),
|
|
LINSOL_RTOLERANCE(a.linsol_rtol),
|
|
LIMITING(a.lim_const),
|
|
JITTER(a.jitter),
|
|
// lists
|
|
P_ORDERS(a.order),
|
|
TARGET_IDS(a.target_id),
|
|
METRIC_IDS(a.metric_id),
|
|
Q_ORDERS(a.quad_order),
|
|
LINEAR_SOLVERS(a.lin_solver),
|
|
NEWTON_LOOPS(a.newton_loop) { }
|
|
|
|
void Run(const int id = 0, bool all = false) const
|
|
{
|
|
if ((id == 0) && name) { mfem::out << "[" << name << "]" << std::endl; }
|
|
DEFAULT_ARGS;
|
|
char ni[SZ] {}, nrtol[SZ] {}, lrtol[SZ] {}, rs[SZ] {}, li[SZ] {},
|
|
lc[SZ] {}, lt[SZ] {}, ji[SZ] {}, cmb[SZ] {};
|
|
args[MSH] = mesh;
|
|
// int
|
|
args[NI] = itoa(NEWTON_ITERATIONS, ni);
|
|
args[LI] = itoa(LINSOL_ITERATIONS, li);
|
|
args[RS] = itoa(REFINE, rs);
|
|
args[CMB] = itoa(COMBO, cmb);
|
|
args[LT] = itoa(LIMIT_TYPE, lt);
|
|
// bool
|
|
args[NOR] = NORMALIZATION ? "-nor" : "-no-nor";
|
|
args[DIAG] = DIAGONAL ? "-diag" : "-no-diag";
|
|
args[BEC] = BAL_EXPL_COMBO ? "-bec" : "-no-bec";
|
|
args[PER] = PERIODIC ? "-per" : "-no-per";
|
|
// real_t
|
|
args[NRTOL] = dtoa(NEWTON_RTOLERANCE, nrtol);
|
|
args[LRTOL] = dtoa(LINSOL_RTOLERANCE, lrtol);
|
|
args[LC] = dtoa(LIMITING, lc);
|
|
args[JI] = dtoa(JITTER, ji);
|
|
|
|
for (int p : P_ORDERS)
|
|
{
|
|
char por[SZ] {};
|
|
args[POR] = itoa(p, por);
|
|
for (int t : TARGET_IDS)
|
|
{
|
|
char tid[SZ] {};
|
|
args[TID] = itoa(t, tid);
|
|
for (int m : METRIC_IDS)
|
|
{
|
|
char mid[SZ] {};
|
|
args[MID] = itoa(m, mid);
|
|
for (int q : Q_ORDERS)
|
|
{
|
|
if (q <= p) { continue; }
|
|
char qor[SZ] {};
|
|
args[QOR] = itoa(q, qor);
|
|
for (int ls : LINEAR_SOLVERS)
|
|
{
|
|
// skip some linear solver & metric combinations
|
|
// that lead to non positive definite operators
|
|
if (ls == 1 && m != 1) { continue; }
|
|
char lsb[SZ] {};
|
|
args[LS] = itoa(ls, lsb);
|
|
for (int n : NEWTON_LOOPS)
|
|
{
|
|
char nl[SZ] {};
|
|
args[NL] = itoa(n, nl);
|
|
tmop_require(id, args);
|
|
if (!all) { break; }
|
|
}
|
|
if (!all) { break; }
|
|
}
|
|
if (!all) { break; }
|
|
}
|
|
if (!all) { break; }
|
|
}
|
|
if (!all) { break; }
|
|
}
|
|
if (!all) { break; }
|
|
}
|
|
}
|
|
};
|
|
|
|
// id: MPI rank, all: launch all non-regression tests
|
|
static void tmop_tests(int id = 0, bool all = false)
|
|
{
|
|
#if defined(MFEM_TMOP_PA_MPI)
|
|
if (HypreUsingGPU())
|
|
{
|
|
cout << "\nAs of mfem-4.3 and hypre-2.22.0 (July 2021) this unit test\n"
|
|
<< "is NOT supported with the GPU version of hypre.\n\n";
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#ifndef _WIN32
|
|
{
|
|
using Det = QuadratureInterpolator::DetKernels;
|
|
Det::Specialization<2, 2, 3, 3>::Add();
|
|
Det::Specialization<2, 2, 5, 5>::Add();
|
|
Det::Specialization<3, 3, 2, 3>::Add();
|
|
Det::Specialization<3, 3, 3, 4>::Add();
|
|
Det::Specialization<3, 3, 4, 6>::Add();
|
|
|
|
using Grad = QuadratureInterpolator::GradKernels;
|
|
Grad::Specialization<2, QVectorLayout::byNODES, false, 2, 3, 5>::Add();
|
|
Grad::Specialization<2, QVectorLayout::byNODES, false, 2, 5, 5>::Add();
|
|
Grad::Specialization<2, QVectorLayout::byNODES, false, 2, 6, 6>::Add();
|
|
Grad::Specialization<3, QVectorLayout::byNODES, false, 3, 4, 5>::Add();
|
|
|
|
using TensorEval = QuadratureInterpolator::TensorEvalKernels;
|
|
TensorEval::Specialization<2, QVectorLayout::byVDIM, 2, 2, 2>::Opt<4>::Add();
|
|
TensorEval::Specialization<2, QVectorLayout::byVDIM, 2, 3, 3>::Opt<4>::Add();
|
|
TensorEval::Specialization<2, QVectorLayout::byVDIM, 2, 4, 4>::Opt<2>::Add();
|
|
TensorEval::Specialization<2, QVectorLayout::byVDIM, 2, 5, 5>::Opt<2>::Add();
|
|
TensorEval::Specialization<3, QVectorLayout::byVDIM, 3, 2, 3>::Opt<2>::Add();
|
|
TensorEval::Specialization<3, QVectorLayout::byVDIM, 3, 3, 4>::Opt<1>::Add();
|
|
TensorEval::Specialization<3, QVectorLayout::byVDIM, 3, 4, 6>::Opt<1>::Add();
|
|
|
|
using MassDiagonal = MassIntegrator::DiagonalPAKernels;
|
|
MassDiagonal::Specialization<2, 2, 3>::Add();
|
|
MassDiagonal::Specialization<2, 2, 4>::Add();
|
|
MassDiagonal::Specialization<2, 2, 5>::Add();
|
|
MassDiagonal::Specialization<3, 2, 4>::Add();
|
|
MassDiagonal::Specialization<3, 2, 6>::Add();
|
|
|
|
using MassApply = MassIntegrator::ApplyPAKernels;
|
|
MassApply::Specialization<2, 2, 3>::Add();
|
|
MassApply::Specialization<2, 2, 4>::Add();
|
|
MassApply::Specialization<2, 2, 5>::Add();
|
|
MassApply::Specialization<3, 2, 4>::Add();
|
|
MassApply::Specialization<3, 2, 6>::Add();
|
|
}
|
|
#endif
|
|
|
|
const real_t jitter = 1. / (M_PI * M_PI);
|
|
|
|
Launch(Launch::Args("2D Periodic + adapted discrete size")
|
|
.MESH("../../data/periodic-square.mesh")
|
|
.PERIODIC()
|
|
.REFINE(1)
|
|
.NORMALIZATION()
|
|
.MID({ 94 })
|
|
.TID({ 5 })
|
|
.LS({ 3 })
|
|
.NEWTON_RTOLERANCE(1e-6)
|
|
.LINSOL_RTOLERANCE(1e-8)
|
|
.LINSOL_ITERATIONS(150)
|
|
.POR({ 1, 2, 3, 4 })
|
|
.QOR({ 4, 8 })
|
|
.NL({ 3 })
|
|
.DIAGONAL(false))
|
|
.Run(id, all);
|
|
|
|
Launch(Launch::Args("3D Periodic + adapted discrete size")
|
|
.MESH("../../data/periodic-cube.mesh")
|
|
.PERIODIC()
|
|
.MID({ 338 })
|
|
.TID({ 5 })
|
|
.LS({ 2 })
|
|
.NORMALIZATION()
|
|
.NEWTON_RTOLERANCE(1e-5)
|
|
.LINSOL_RTOLERANCE(1e-10)
|
|
.LINSOL_ITERATIONS(200)
|
|
.POR({ 1, 2 })
|
|
.QOR({ 4 })
|
|
.NL({ 1 })
|
|
.DIAGONAL(false))
|
|
.Run(id, all);
|
|
|
|
Launch(Launch::Args("2D + Combo + Balance")
|
|
.MESH("../../miniapps/meshing/square01.mesh")
|
|
.REFINE(1)
|
|
.JI(jitter)
|
|
.NORMALIZATION()
|
|
.TID({ 5 })
|
|
.MID({ 80, 94 })
|
|
.LS({ 2 })
|
|
.LINSOL_RTOLERANCE(1e-10)
|
|
.POR({ 2 })
|
|
.QOR({ 6 })
|
|
.CMB(2)
|
|
.BALANCE_EXPLICIT_COMBO()
|
|
)
|
|
.Run(id, all);
|
|
|
|
Launch(Launch::Args("3D + Combo + Balance")
|
|
.MESH("../../miniapps/meshing/cube.mesh")
|
|
.REFINE(1)
|
|
.JI(jitter)
|
|
.NORMALIZATION()
|
|
.TID({ 5 })
|
|
.MID({ 302, 338 })
|
|
.LS({ 2 })
|
|
.POR({ 1, 2 })
|
|
.QOR({ 2, 8 })
|
|
.CMB(2)
|
|
.BALANCE_EXPLICIT_COMBO())
|
|
.Run(id, all);
|
|
|
|
Launch(Launch::Args("TC_IDEAL_SHAPE_UNIT_SIZE_2D_KERNEL")
|
|
.MESH("../../data/star.mesh")
|
|
.REFINE(1)
|
|
.JI(jitter)
|
|
.POR({ 1, 2 })
|
|
.QOR({ 2, 3 })
|
|
.TID({ 1 })
|
|
.MID({ 2 }))
|
|
.Run(id, all);
|
|
|
|
Launch(Launch::Args("TC_IDEAL_SHAPE_GIVEN_SIZE_2D_KERNEL")
|
|
.MESH("../../data/star.mesh")
|
|
.REFINE(1)
|
|
.JI(jitter)
|
|
.POR({ 1, 2 })
|
|
.QOR({ 2, 3 })
|
|
.TID({ 3 })
|
|
.MID({ 2 }))
|
|
.Run(id, all);
|
|
|
|
Launch(Launch::Args("TC_GIVEN_SHAPE_AND_SIZE_2D_KERNEL")
|
|
.MESH("../../data/star.mesh")
|
|
.REFINE(1)
|
|
.JI(jitter)
|
|
.NORMALIZATION()
|
|
.POR({ 1, 2 })
|
|
.QOR({ 2, 3 })
|
|
.TID({ 8 })
|
|
.MID({ 94 })
|
|
.LS({ 3 }))
|
|
.Run(id, all);
|
|
|
|
Launch(Launch::Args("TC_GIVEN_SHAPE_AND_SIZE_3D_KERNEL")
|
|
.MESH("../../data/toroid-hex.mesh")
|
|
.LIMITING(M_PI)
|
|
.LIMIT_TYPE(1)
|
|
.REFINE(1)
|
|
.JI(jitter)
|
|
.NORMALIZATION()
|
|
.POR({ 2 })
|
|
.QOR({ 4 })
|
|
.TID({ 8 })
|
|
.MID({ 338 })
|
|
.LS({ 3 }))
|
|
.Run(id, all);
|
|
|
|
Launch(Launch::Args("TC_IDEAL_SHAPE_UNIT_SIZE_3D_KERNEL")
|
|
.MESH("../../miniapps/meshing/cube.mesh")
|
|
.REFINE(1)
|
|
.JI(jitter)
|
|
.POR({ 1, 2 })
|
|
.QOR({ 2, 3 })
|
|
.TID({ 1 })
|
|
.MID({ 302 }))
|
|
.Run(id, all);
|
|
|
|
Launch(Launch::Args("TC_IDEAL_SHAPE_GIVEN_SIZE_3D_KERNEL")
|
|
.MESH("../../miniapps/meshing/cube.mesh")
|
|
.REFINE(1)
|
|
.JI(jitter)
|
|
.POR({ 1, 2 })
|
|
.QOR({ 2, 6 })
|
|
.TID({ 3 })
|
|
.MID({ 302 }))
|
|
.Run(id, all);
|
|
|
|
Launch(Launch::Args("Star")
|
|
.MESH("../../data/star.mesh")
|
|
.POR({ 1, 2, 3, 4 })
|
|
.QOR({ 2, 4, 8 })
|
|
.TID({ 1, 2, 3 })
|
|
.MID({ 1, 2 }))
|
|
.Run(id, all);
|
|
|
|
Launch(Launch::Args("Square01 + Adapted analytic Hessian")
|
|
.MESH("../../miniapps/meshing/square01.mesh")
|
|
.REFINE(1)
|
|
.POR({ 1, 2 })
|
|
.QOR({ 2, 4 })
|
|
.TID({ 4 })
|
|
.MID({ 1, 2 }))
|
|
.Run(id, all);
|
|
|
|
Launch(Launch::Args("Square01 + Adapted discrete size")
|
|
.MESH("../../miniapps/meshing/square01.mesh")
|
|
.REFINE(1)
|
|
.NORMALIZATION()
|
|
.POR({ 1 })
|
|
.QOR({ 4, 6 })
|
|
.LINSOL_RTOLERANCE(1e-12)
|
|
.LINSOL_ITERATIONS(150)
|
|
.TID({ 5 })
|
|
.MID({ 80, 94 })
|
|
.LS({ 3 }))
|
|
.Run(id, all);
|
|
|
|
Launch(Launch::Args("Blade")
|
|
.MESH("../../miniapps/meshing/blade.mesh")
|
|
.POR({ 1, 2 })
|
|
.QOR({ 2, 4 })
|
|
.NEWTON_RTOLERANCE(1e-13)
|
|
.TID({ 1, 2, 3 })
|
|
.MID({ 2 })
|
|
.LS({ 2 }))
|
|
.Run(id, all);
|
|
|
|
Launch(Launch::Args("Blade + normalization")
|
|
.MESH("../../miniapps/meshing/blade.mesh")
|
|
.NORMALIZATION()
|
|
.POR({ 1, 2 })
|
|
.QOR({ 2, 4 })
|
|
.LINSOL_ITERATIONS(200)
|
|
.NEWTON_RTOLERANCE(1e-12)
|
|
.NL({ 2 })
|
|
.TID({ 1, 2, 3 })
|
|
.MID({ 2 }))
|
|
.Run(id, all);
|
|
|
|
Launch(Launch::Args("Blade + limiting + normalization")
|
|
.MESH("../../miniapps/meshing/blade.mesh")
|
|
.NORMALIZATION()
|
|
.LIMITING(M_PI)
|
|
.POR({ 1, 2 })
|
|
.QOR({ 2, 4 })
|
|
.LINSOL_ITERATIONS(200)
|
|
.NEWTON_RTOLERANCE(1e-12)
|
|
.TID({ 1, 2, 3 })
|
|
.MID({ 2 }))
|
|
.Run(id, all);
|
|
|
|
Launch(Launch::Args("Blade + limiting_expo + normalization")
|
|
.MESH("../../miniapps/meshing/blade.mesh")
|
|
.NORMALIZATION()
|
|
.LIMITING(M_PI)
|
|
.LIMIT_TYPE(1)
|
|
.LINSOL_ITERATIONS(200)
|
|
.NEWTON_RTOLERANCE(1e-12)
|
|
.POR({ 1, 2 })
|
|
.QOR({ 2, 4 })
|
|
.TID({ 1, 2, 3 })
|
|
.MID({ 2 }))
|
|
.Run(id, all);
|
|
|
|
Launch(Launch::Args("Cube")
|
|
.MESH("../../miniapps/meshing/cube.mesh")
|
|
.REFINE(1)
|
|
.JI(jitter)
|
|
.POR({ 1, 2 })
|
|
.QOR({ 2, 4 })
|
|
.TID({ 2, 3 })
|
|
.MID({ 302, 303 }))
|
|
.Run(id, all);
|
|
|
|
Launch(
|
|
Launch::Args("Cube + Discrete size & aspect + normalization + limiting")
|
|
.MESH("../../miniapps/meshing/cube.mesh")
|
|
.NORMALIZATION()
|
|
.LIMITING(M_PI)
|
|
.POR({ 1, 2 })
|
|
.QOR({ 4, 2 })
|
|
.TID({ 7 })
|
|
.MID({ 302, 321 }))
|
|
.Run(id, all);
|
|
|
|
Launch(Launch::Args("Cube + Discrete size + normalization")
|
|
.MESH("../../miniapps/meshing/cube.mesh")
|
|
.NORMALIZATION()
|
|
.POR({ 1 })
|
|
.QOR({ 4, 2 })
|
|
.NEWTON_RTOLERANCE(1e-12)
|
|
.TID({ 5 })
|
|
.MID({ 315, 318, 332, 338 }))
|
|
.Run(id, all);
|
|
|
|
// Note: order 1 has no interior nodes, so all residuals are zero and the
|
|
// Newton iteration exits immediately.
|
|
// Note: In parallel, orders > 1 fail with: Initial mesh was valid,
|
|
// but intermediate mesh is invalid. Contact TMOP Developers.
|
|
Launch(Launch::Args("Toroid-Hex")
|
|
.MESH("../../data/toroid-hex.mesh")
|
|
.POR({ 1, 2 })
|
|
.QOR({ 2, 4, 8 })
|
|
.TID({ 1, 2, 3 })
|
|
.MID({ 302, 303, 321 }))
|
|
.Run(id, all);
|
|
|
|
Launch(Launch::Args("Toroid-Hex + limiting")
|
|
.MESH("../../data/toroid-hex.mesh")
|
|
.LIMITING(M_PI)
|
|
.POR({ 1, 2 })
|
|
.QOR({ 2, 4 })
|
|
.NL({ 3, 1 })
|
|
.TID({ 1, 2 })
|
|
.MID({ 321, 338 }))
|
|
.Run(id, all);
|
|
|
|
Launch(Launch::Args("Toroid-Hex + limiting + norm.")
|
|
.MESH("../../data/toroid-hex.mesh")
|
|
.LIMITING(M_PI)
|
|
.NORMALIZATION()
|
|
.POR({ 1, 2 })
|
|
.QOR({ 2, 4 })
|
|
.TID({ 1, 2 })
|
|
.MID({ 321, 315, 318, 332, 338 }))
|
|
.Run(id, all);
|
|
|
|
Launch(Launch::Args("Toroid-Hex + limiting_expo + norm.")
|
|
.MESH("../../data/toroid-hex.mesh")
|
|
.LIMITING(M_PI)
|
|
.LIMIT_TYPE(1)
|
|
.NORMALIZATION()
|
|
.POR({ 1, 2 })
|
|
.QOR({ 2, 4 })
|
|
.TID({ 1, 2 })
|
|
.MID({ 321 }))
|
|
.Run(id, all);
|
|
|
|
// -m cube.mesh -rs 1 -tid 5 -mid 321 -ni 5 -ls 3 -li 100 -lc 1.0 -nor
|
|
Launch(Launch::Args("Cube + Blast options")
|
|
.MESH("../../miniapps/meshing/cube.mesh")
|
|
.REFINE(1)
|
|
.TID({ 5 })
|
|
.MID({ 321 })
|
|
.LS({ 3 })
|
|
.NEWTON_ITERATIONS(10)
|
|
.NEWTON_RTOLERANCE(1e-10)
|
|
.LINSOL_RTOLERANCE(1e-14)
|
|
.LIMITING(M_PI)
|
|
.NORMALIZATION()
|
|
.POR({ 1, 2, 3 })
|
|
.QOR({ 3, 6 })
|
|
.NL({ 1, 2 }))
|
|
.Run(id, all);
|
|
|
|
// Combo 2D
|
|
Launch(Launch::Args("Square01 + Combo")
|
|
.MESH("../../miniapps/meshing/square01.mesh")
|
|
.REFINE(1)
|
|
.JI(jitter)
|
|
.NORMALIZATION()
|
|
.LINSOL_RTOLERANCE(1e-10)
|
|
.TID({ 5 })
|
|
.MID({ 2 })
|
|
.LS({ 2 })
|
|
.POR({ 2 })
|
|
.QOR({ 8 })
|
|
.CMB(2))
|
|
.Run(id, all);
|
|
|
|
// Combo 3D
|
|
Launch(Launch::Args("Cube + Combo")
|
|
.MESH("../../miniapps/meshing/cube.mesh")
|
|
.REFINE(1)
|
|
.JI(jitter)
|
|
.NORMALIZATION()
|
|
.TID({ 5 })
|
|
.MID({ 302 })
|
|
.LS({ 2 })
|
|
.POR({ 1, 2 })
|
|
.QOR({ 2, 8 })
|
|
.CMB(2))
|
|
.Run(id, all);
|
|
|
|
// NURBS
|
|
Launch(Launch::Args("2D Nurbs")
|
|
.MESH("../../data/square-disc-nurbs.mesh")
|
|
.REFINE(1)
|
|
.JI(jitter)
|
|
.POR({ 1, 2 })
|
|
.QOR({ 2, 4 })
|
|
.TID({ 1, 2, 3 })
|
|
.MID({ 1, 2 }))
|
|
.Run(id, all);
|
|
|
|
Launch(Launch::Args("3D Nurbs")
|
|
.MESH("../../data/beam-hex-nurbs.mesh")
|
|
.REFINE(1)
|
|
.JI(jitter)
|
|
.POR({ 1, 2 })
|
|
.QOR({ 2, 4 })
|
|
.TID({ 1, 2, 3 })
|
|
.MID({ 302, 321 }))
|
|
.Run(id, all);
|
|
|
|
// The following tests need more iterations to converge between PA & non-PA
|
|
// They can only be launched with the `--all` command line option
|
|
|
|
if (!all) { return; }
|
|
|
|
Launch(Launch::Args("Blade + Discrete size + normalization")
|
|
.MESH("../../miniapps/meshing/blade.mesh")
|
|
.LINSOL_ITERATIONS(1000)
|
|
.NORMALIZATION()
|
|
.NEWTON_RTOLERANCE(1e-14)
|
|
.LINSOL_RTOLERANCE(1e-14)
|
|
.POR({ 1 })
|
|
.QOR({ 2 })
|
|
.TID({ 5 })
|
|
.MID({ 7 })
|
|
.LS({ 2 })
|
|
.NL({ 4 }))
|
|
.Run(id, true);
|
|
|
|
Launch(Launch::Args("Blade + Discrete size + normalization")
|
|
.MESH("../../miniapps/meshing/blade.mesh")
|
|
.LINSOL_ITERATIONS(500)
|
|
.NORMALIZATION()
|
|
.NEWTON_RTOLERANCE(1e-12)
|
|
.LINSOL_RTOLERANCE(1e-10)
|
|
.POR({ 1 })
|
|
.QOR({ 2 })
|
|
.TID({ 5 })
|
|
.MID({ 2 }))
|
|
.Run(id, true);
|
|
}
|
|
|
|
#ifdef MFEM_TMOP_PA_MPI
|
|
TEST_CASE("tmop_pa", "[TMOP_PA], [Parallel]")
|
|
{
|
|
tmop_tests(Mpi::WorldRank(), launch_all_non_regression_tests);
|
|
}
|
|
#else
|
|
TEST_CASE("tmop_pa", "[TMOP_PA]")
|
|
{
|
|
tmop_tests(0, launch_all_non_regression_tests);
|
|
}
|
|
#endif
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
#ifdef MFEM_USE_SINGLE
|
|
std::cout << "\nThe TMOP unit tests are not supported in single"
|
|
" precision.\n\n";
|
|
return MFEM_SKIP_RETURN_VALUE;
|
|
#endif
|
|
|
|
#ifdef MFEM_TMOP_PA_MPI
|
|
mfem::Mpi::Init();
|
|
mfem::Hypre::Init();
|
|
#endif
|
|
#ifdef MFEM_TMOP_PA_DEVICE
|
|
Device device(MFEM_TMOP_PA_DEVICE);
|
|
#else
|
|
Device device("cpu"); // make sure hypre runs on CPU, if possible
|
|
#endif
|
|
device.Print();
|
|
|
|
#ifdef MFEM_TMOP_PA_MPI
|
|
return RunCatchSession(argc, argv, { "[Parallel]" }, Root());
|
|
#else
|
|
// Exclude parallel tests.
|
|
return RunCatchSession(argc, argv, { "~[Parallel]" });
|
|
#endif
|
|
}
|