265 lines
9.1 KiB
C++
265 lines
9.1 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.
|
|
//
|
|
// ------------------------------------------------------
|
|
// Check Metric Miniapp: Check TMOP Metric Implementation
|
|
// ------------------------------------------------------
|
|
//
|
|
// This miniapp checks the evaluation, 1st, and 2nd derivatives of a TMOP
|
|
// metric. Works only in serial.
|
|
//
|
|
// Compile with: make tmop-check-metric
|
|
//
|
|
// Sample runs: tmop-check-metric -mid 360
|
|
|
|
#include "mfem.hpp"
|
|
#include <iostream>
|
|
|
|
using namespace mfem;
|
|
using namespace std;
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int metric_id = 2;
|
|
bool a_metric_version = false;
|
|
int convergence_iter = 10;
|
|
bool verbose = false;
|
|
|
|
// Choose metric.
|
|
OptionsParser args(argc, argv);
|
|
args.AddOption(&metric_id, "-mid", "--metric-id", "Metric id");
|
|
args.AddOption(&a_metric_version, "-A", "-Ametric", "-no-A", "--no-Ametric",
|
|
"Use the A-version of the metric, if available.");
|
|
args.AddOption(&verbose, "-v", "-verbose", "-no-v", "--no-verbose",
|
|
"Enable extra screen output.");
|
|
args.AddOption(&convergence_iter, "-i", "--iterations",
|
|
"Number of iterations to check convergence of derivatives.");
|
|
|
|
args.Parse();
|
|
if (!args.Good())
|
|
{
|
|
args.PrintUsage(cout);
|
|
return 1;
|
|
}
|
|
args.PrintOptions(cout);
|
|
|
|
// Setup metric.
|
|
real_t tauval = -0.1;
|
|
TMOP_QualityMetric *metric = NULL;
|
|
switch (metric_id)
|
|
{
|
|
// T-metrics
|
|
case 1: metric = new TMOP_Metric_001; break;
|
|
case 2: metric = new TMOP_Metric_002; break;
|
|
case 7: metric = new TMOP_Metric_007; break;
|
|
case 9: metric = new TMOP_Metric_009; break;
|
|
case 14:
|
|
if (a_metric_version) { metric = new TMOP_Metric_014; }
|
|
else { metric = new TMOP_AMetric_014; } break;
|
|
case 22: metric = new TMOP_Metric_022(tauval); break;
|
|
case 50:
|
|
if (a_metric_version) { metric = new TMOP_Metric_050; }
|
|
else { metric = new TMOP_AMetric_050; } break;
|
|
case 55: metric = new TMOP_Metric_055; break;
|
|
case 56: metric = new TMOP_Metric_056; break;
|
|
case 58: metric = new TMOP_Metric_058; break;
|
|
case 77: metric = new TMOP_Metric_077; break;
|
|
case 80: metric = new TMOP_Metric_080(0.5); break;
|
|
case 85: metric = new TMOP_Metric_085; break;
|
|
case 90: metric = new TMOP_Metric_090; break;
|
|
case 94: metric = new TMOP_Metric_094; break;
|
|
case 98: metric = new TMOP_Metric_098; break;
|
|
// case 211: metric = new TMOP_Metric_211; break;
|
|
// case 252: metric = new TMOP_Metric_252(tauval); break;
|
|
case 301: metric = new TMOP_Metric_301; break;
|
|
case 302: metric = new TMOP_Metric_302; break;
|
|
case 303: metric = new TMOP_Metric_303; break;
|
|
case 304: metric = new TMOP_Metric_304; break;
|
|
// case 311: metric = new TMOP_Metric_311; break;
|
|
case 313: metric = new TMOP_Metric_313(tauval); break;
|
|
case 315: metric = new TMOP_Metric_315; break;
|
|
case 316: metric = new TMOP_Metric_316; break;
|
|
case 318: metric = new TMOP_Metric_318; break;
|
|
case 321: metric = new TMOP_Metric_321; break;
|
|
case 322: metric = new TMOP_Metric_322; break;
|
|
case 323: metric = new TMOP_Metric_323; break;
|
|
case 328: metric = new TMOP_Metric_328; break;
|
|
case 332: metric = new TMOP_Metric_332(0.5); break;
|
|
case 333: metric = new TMOP_Metric_333(0.5); break;
|
|
case 334: metric = new TMOP_Metric_334(0.5); break;
|
|
case 338: metric = new TMOP_Metric_338; break;
|
|
case 342: metric = new TMOP_Metric_342; break;
|
|
case 347: metric = new TMOP_Metric_347(0.5); break;
|
|
// case 352: metric = new TMOP_Metric_352(tauval); break;
|
|
case 360: metric = new TMOP_Metric_360; break;
|
|
// A-metrics
|
|
case 11: metric = new TMOP_AMetric_011; break;
|
|
case 36: metric = new TMOP_AMetric_036; break;
|
|
case 51: metric = new TMOP_AMetric_051; break;
|
|
case 107: metric = new TMOP_AMetric_107; break;
|
|
case 126: metric = new TMOP_AMetric_126(0.9); break;
|
|
default: cout << "Unknown metric_id: " << metric_id << endl; return 3;
|
|
}
|
|
|
|
const int dim = (metric_id < 300) ? 2 : 3;
|
|
Mesh *mesh;
|
|
if (dim == 2)
|
|
{
|
|
mesh = new Mesh(Mesh::MakeCartesian2D(1, 1, Element::QUADRILATERAL));
|
|
}
|
|
else
|
|
{
|
|
mesh = new Mesh(Mesh::MakeCartesian3D(1, 1, 1, Element::HEXAHEDRON));
|
|
}
|
|
H1_FECollection fec(2, dim);
|
|
FiniteElementSpace fespace(mesh, &fec, dim);
|
|
|
|
DenseMatrix T(dim);
|
|
Vector T_vec(T.GetData(), dim * dim);
|
|
|
|
// Test evaluation.
|
|
int valid_cnt = 0, bad_cnt = 0;
|
|
for (int i = 0; i < 1000; i++)
|
|
{
|
|
T_vec.Randomize(i);
|
|
// Increase probability of det(T) > 0.
|
|
T(0, 0) += T_vec.Max();
|
|
if (T.Det() <= 0.0) { continue; }
|
|
|
|
auto W = Geometries.GetGeomToPerfGeomJac(fespace.GetFE(0)->GetGeomType());
|
|
metric->SetTargetJacobian(W);
|
|
|
|
const real_t i_form = metric->EvalW(T),
|
|
m_form = metric->EvalWMatrixForm(T);
|
|
const real_t diff = std::abs(i_form - m_form) / std::abs(m_form);
|
|
if (diff > 1e-8)
|
|
{
|
|
bad_cnt++;
|
|
if (verbose)
|
|
{
|
|
cout << "Wrong metric computation: "
|
|
<< i_form << " (invariant), " << m_form << " (matrix form) "
|
|
<< diff << " (normalized difference) " << endl;
|
|
}
|
|
}
|
|
valid_cnt++;
|
|
}
|
|
cout << "--- EvalW: " << bad_cnt << " errors out of "
|
|
<< valid_cnt << " comparisons with det(T) > 0.\n";
|
|
|
|
NonlinearForm a(&fespace);
|
|
mesh->SetNodalFESpace(&fespace);
|
|
GridFunction x(&fespace);
|
|
mesh->SetNodalGridFunction(&x);
|
|
x(0) = 0.25;
|
|
|
|
TargetConstructor tc(TargetConstructor::IDEAL_SHAPE_UNIT_SIZE);
|
|
tc.SetNodes(x);
|
|
auto integ = new TMOP_Integrator(metric, &tc, NULL);
|
|
a.AddDomainIntegrator(integ);
|
|
|
|
ElementTransformation &Tr = *mesh->GetElementTransformation(0);
|
|
const FiniteElement &fe = *fespace.GetFE(0);
|
|
Array<int> vdofs;
|
|
fespace.GetElementVDofs(0, vdofs);
|
|
Vector x_loc(x.Size());
|
|
x.GetSubVector(vdofs, x_loc);
|
|
|
|
// Test 1st derivative (assuming EvalW is correct). Should be 2nd order.
|
|
Vector dF_0;
|
|
const real_t F_0 = integ->GetElementEnergy(fe, Tr, x_loc);
|
|
integ->AssembleElementVector(fe, Tr, x_loc, dF_0);
|
|
if (verbose) { cout << "***\ndF = \n"; dF_0.Print(); cout << "***\n"; }
|
|
real_t dx = 0.1;
|
|
real_t rate_dF_sum = 0.0, err_old = 1.0;
|
|
for (int k = 0; k < convergence_iter; k++)
|
|
{
|
|
real_t err_k = 0.0;
|
|
for (int i = 0; i < x_loc.Size(); i++)
|
|
{
|
|
x_loc(i) += dx;
|
|
err_k = std::max(err_k, std::abs(F_0 + dF_0(i) * dx -
|
|
integ->GetElementEnergy(fe, Tr, x_loc)));
|
|
x_loc(i) -= dx;
|
|
}
|
|
dx *= 0.5;
|
|
|
|
if (verbose && k == 0)
|
|
{
|
|
std::cout << "dF error " << k << ": " << err_k << endl;
|
|
}
|
|
if (k > 0)
|
|
{
|
|
real_t r = log2(err_old / err_k);
|
|
rate_dF_sum += r;
|
|
if (verbose)
|
|
{
|
|
std::cout << "dF error " << k << ": " << err_k << " " << r << endl;
|
|
}
|
|
}
|
|
err_old = err_k;
|
|
}
|
|
std::cout << "--- EvalP: avg rate of convergence (should be 2): "
|
|
<< rate_dF_sum / (convergence_iter - 1) << endl;
|
|
|
|
// Test 2nd derivative (assuming EvalP is correct).
|
|
real_t min_avg_rate = 7.0;
|
|
DenseMatrix ddF_0;
|
|
integ->AssembleElementGrad(fe, Tr, x_loc, ddF_0);
|
|
if (verbose) { cout << "***\nddF = \n"; ddF_0.Print(); cout << "***\n"; }
|
|
for (int i = 0; i < x_loc.Size(); i++)
|
|
{
|
|
real_t rate_sum = 0.0;
|
|
dx = 0.1;
|
|
for (int k = 0; k < convergence_iter; k++)
|
|
{
|
|
real_t err_k = 0.0;
|
|
|
|
for (int j = 0; j < x_loc.Size(); j++)
|
|
{
|
|
x_loc(j) += dx;
|
|
Vector dF_dx;
|
|
integ->AssembleElementVector(fe, Tr, x_loc, dF_dx);
|
|
err_k = std::max(err_k, std::abs(dF_0(i) + ddF_0(i, j) * dx - dF_dx(i)));
|
|
x_loc(j) -= dx;
|
|
}
|
|
dx *= 0.5;
|
|
|
|
if (verbose && k == 0)
|
|
{
|
|
cout << "ddF error for dof " << i << ", " << k << ": "
|
|
<< err_k << endl;
|
|
}
|
|
if (k > 0)
|
|
{
|
|
real_t r = log2(err_old / err_k);
|
|
// Error is zero (2nd derivative is exact) -> put rate 2 (optimal).
|
|
if (err_k < 1e-14) { r = 2.0; }
|
|
rate_sum += r;
|
|
if (verbose)
|
|
{
|
|
cout << "ddF error for dof " << i << ", " << k << ": " << err_k
|
|
<< " " << r << endl;
|
|
}
|
|
|
|
}
|
|
err_old = err_k;
|
|
}
|
|
min_avg_rate = std::min(min_avg_rate, rate_sum / (convergence_iter - 1));
|
|
}
|
|
std::cout << "--- AssembleH: avg rate of convergence (should be 2): "
|
|
<< min_avg_rate << endl;
|
|
|
|
delete metric;
|
|
delete mesh;
|
|
return 0;
|
|
}
|