3388 lines
107 KiB
C++
3388 lines
107 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.
|
|
//
|
|
// 3point:
|
|
// explicit:
|
|
// mpirun -np 8 laghos -p 3 -glvis -tf 5.0 -av -av-type 7 -ov 4 -oe 3 -rs 1 -cfl 0.5 -s 2 -vs 50
|
|
// implicit:
|
|
// mpirun -np 8 laghos -p 3 -glvis -tf 5.0 -nmi 50 -pt 1 -kmi 20 -av -av-type 7 -ov 3 -oe 2 -rs 2 -cfl 32 -s 12 -vs 10
|
|
//
|
|
// Sedov:
|
|
// explicit:
|
|
// mpirun -np 8 laghos -p 1 -glvis -tf 0.8 -av -av-type 7 -ov 2 -oe 1 -s 3 -cfl 0.5 -rs 3 -vs 50
|
|
// implicit:
|
|
// mpirun -np 8 laghos -p 1 -glvis -tf 0.8 -nmi 50 -pt 1 -kmi 20 -av -av-type 7 -ov 2 -oe 1 -s 12 -cfl 8 -vs 10 -rs 3
|
|
//
|
|
// TG:
|
|
// explicit 2D, 3D:
|
|
// mpirun -np 8 laghos -p 0 -glvis -tf 0.75 -ov 3 -oe 2 -rs 2 -cfl 0.5 -s 4 -vs 100
|
|
// mpirun -np 8 laghos -m ./cube.mesh -p 0 -glvis -tf 0.75 -av -av-type 7 -ov 2 -oe 1 -rs 1 -cfl 0.5 -s 4 -vs 10
|
|
// implicit 2D, 3D:
|
|
// mpirun -np 8 laghos -p 0 -glvis -tf 0.75 -nmi 50 -pt 1 -kmi 20 -ov 3 -oe 2 -rs 3 -cfl 32 -s 12 -vs 10
|
|
// mpirun -np 8 laghos -m ./cube.mesh -p 0 -glvis -tf 0.75 -av -av-type 7 -nmi 50 -pt 1 -kmi 20 -ov 2 -oe 1 -rs 1 -cfl 32 -s 12 -vs 10
|
|
|
|
|
|
#include <mfem.hpp>
|
|
|
|
// TODO: Do we want this to be included from mfem.hpp automatically now?
|
|
#include "fem/dfem/doperator.hpp"
|
|
#include "linalg/tensor.hpp"
|
|
#ifdef MFEM_USE_PETSC
|
|
#include "linalg/petsc.hpp"
|
|
#include "petscmat.h"
|
|
#endif
|
|
|
|
#include <limits>
|
|
#include <memory>
|
|
#include <sstream>
|
|
#include <cstring>
|
|
#include <string>
|
|
|
|
using namespace mfem;
|
|
using mfem::internal::tensor;
|
|
|
|
constexpr int VELOCITY = 0;
|
|
constexpr int DENSITY0 = 1;
|
|
constexpr int COORDINATES0 = 2;
|
|
constexpr int COORDINATES = 3;
|
|
constexpr int MATERIAL = 4;
|
|
constexpr int SPECIFIC_INTERNAL_ENERGY = 5;
|
|
constexpr int DT_EST = 8;
|
|
constexpr int STRESS_TENSOR = 9;
|
|
|
|
enum EXT_DATA_IDX
|
|
{
|
|
CFL = 0,
|
|
ORDER_VEL,
|
|
VISCOSITY_FLAG,
|
|
VISCOSITY_TYPE,
|
|
VISC_Q1,
|
|
VISC_Q2,
|
|
H0,
|
|
DT_ESTIMATE,
|
|
COUNT
|
|
};
|
|
|
|
int problem = 0;
|
|
int petsc_lag_steps;
|
|
|
|
enum PRECONDITIONER_TYPE
|
|
{
|
|
SUPERLU,
|
|
BLOCK_DIAGONAL_AMG,
|
|
};
|
|
|
|
void threshold(Vector &v)
|
|
{
|
|
for (int i = 0; i < v.Size(); i++)
|
|
{
|
|
if (abs(v(i)) <= 1e-12)
|
|
{
|
|
v(i) = 0.0;
|
|
}
|
|
}
|
|
}
|
|
|
|
MFEM_HOST_DEVICE inline
|
|
real_t taylor_source(const Vector &x)
|
|
{
|
|
return 3.0 / 8.0 * M_PI * ( cos(3.0*M_PI*x(0)) * cos(M_PI*x(1)) -
|
|
cos(M_PI*x(0)) * cos(3.0*M_PI*x(1)) );
|
|
};
|
|
|
|
MFEM_HOST_DEVICE inline
|
|
real_t smoothmin(real_t a, real_t b, real_t k = 1e6)
|
|
{
|
|
return -1.0 / k * log(exp(-k * a) + exp(-k * b));
|
|
}
|
|
|
|
MFEM_HOST_DEVICE inline
|
|
real_t smoothmax(real_t a, real_t b, real_t k = 1e-6)
|
|
{
|
|
return 0.5 * (a + b + sqrt((a-b)*(a-b) + k*k));
|
|
}
|
|
|
|
MFEM_HOST_DEVICE inline
|
|
real_t smoothabs(real_t x, real_t k = 1e-6)
|
|
{
|
|
return sqrt(x * x + k);
|
|
}
|
|
|
|
template <typename T, int n>
|
|
MFEM_HOST_DEVICE inline
|
|
tensor<T, n> shift(const tensor<T, n> &v, T s)
|
|
{
|
|
tensor<T, n> sv;
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
sv(i) = v(i) - s;
|
|
}
|
|
return sv;
|
|
}
|
|
|
|
template <typename T, int n>
|
|
MFEM_HOST_DEVICE inline
|
|
T min(const tensor<T, n> &v)
|
|
{
|
|
T min = std::numeric_limits<T>::min();
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
if (v(i) < min)
|
|
{
|
|
min = v(i);
|
|
}
|
|
}
|
|
return min;
|
|
}
|
|
|
|
template <typename T, int n>
|
|
MFEM_HOST_DEVICE inline
|
|
std::tuple<T, tensor<T, n>> sinvpm(const tensor<T, n, n> &A, int maxit, T tol)
|
|
{
|
|
auto shift = [](const tensor<T, n, n> &A, const T& mu)
|
|
{
|
|
const auto I = mfem::internal::Identity<n>();
|
|
tensor<T, n, n> B = A;
|
|
B -= mu * I;
|
|
return B;
|
|
};
|
|
|
|
auto As = shift(A, tol);
|
|
auto mu = min(std::get<0>(eig(As)));
|
|
tensor<T, n> x = {};
|
|
x(0) = 1.0;
|
|
x = x / norm(x);
|
|
const auto Binv = inv(shift(A, mu));
|
|
auto y = Binv * x;
|
|
auto la = dot(y, x);
|
|
|
|
for (int i = 0; i < maxit; i++)
|
|
{
|
|
// const auto err = norm(y - la * x) / norm(y);
|
|
// if (err <= tol)
|
|
// {
|
|
// break;
|
|
// }
|
|
x = y / norm(y);
|
|
y = Binv * x;
|
|
la = dot(y, x);
|
|
}
|
|
|
|
return {mu + 1.0 / la, x};
|
|
}
|
|
|
|
// Smooth transition between 0 and 1 for x in [-eps, eps].
|
|
MFEM_HOST_DEVICE inline
|
|
real_t smooth_step_01(real_t x, real_t eps)
|
|
{
|
|
const real_t y = (x + eps) / (2.0 * eps);
|
|
if (y < 0.0) { return 0.0; }
|
|
if (y > 1.0) { return 1.0; }
|
|
return (3.0 - 2.0 * y) * y * y;
|
|
}
|
|
|
|
MFEM_HOST_DEVICE inline
|
|
void ComputeMaterialProperties(const real_t &gamma, const real_t &rho,
|
|
const real_t &E, real_t &p, real_t &cs)
|
|
{
|
|
p = (gamma - 1.0) * rho * E;
|
|
cs = sqrt(gamma * (gamma - 1.0) * E);
|
|
}
|
|
|
|
template <int DIM>
|
|
using vecd_t = tensor<real_t, DIM>;
|
|
|
|
template <int DIM>
|
|
using matd_t = tensor<real_t, DIM, DIM>;
|
|
|
|
template <int DIM>
|
|
struct TaylorSourceQFunction
|
|
{
|
|
using vecd = vecd_t<DIM>;
|
|
using matd = matd_t<DIM>;
|
|
|
|
TaylorSourceQFunction() = default;
|
|
|
|
MFEM_HOST_DEVICE inline
|
|
auto operator()(const vecd &x, const matd &J, const real_t &w) const
|
|
{
|
|
auto f = 3.0 / 8.0 * M_PI * ( cos(3.0*M_PI*x(0)) * cos(M_PI*x(1)) -
|
|
cos(M_PI*x(0)) * cos(3.0*M_PI*x(1)) );
|
|
|
|
return mfem::tuple{f * det(J) * w};
|
|
}
|
|
};
|
|
|
|
|
|
static inline MFEM_HOST_DEVICE
|
|
std::tuple<tensor<real_t, 2>, tensor<real_t, 2, 2>> eig2(
|
|
tensor<real_t, 2, 2> &A)
|
|
{
|
|
return eig(A);
|
|
}
|
|
|
|
static inline MFEM_HOST_DEVICE
|
|
std::tuple<tensor<real_t, 2>, tensor<real_t, 2, 2>> grad_eig2(
|
|
tensor<real_t, 2, 2> &A,
|
|
tensor<real_t, 2, 2> &dA)
|
|
{
|
|
return grad_eig(A, dA);
|
|
}
|
|
|
|
// void* __enzyme_register_derivative_eig[] =
|
|
// {
|
|
// (std::tuple<tensor<real_t, 2>, tensor<real_t, 2, 2>>*)eig2,
|
|
// (std::tuple<tensor<real_t, 2>, tensor<real_t, 2, 2>>*)grad_eig2,
|
|
// };
|
|
|
|
template <int DIM>
|
|
MFEM_HOST_DEVICE inline
|
|
matd_t<DIM> qdata_setup(
|
|
const matd_t<DIM> &dvdxi,
|
|
const real_t &rho0,
|
|
const matd_t<DIM> &J0,
|
|
const matd_t<DIM> &J,
|
|
const real_t &gamma,
|
|
const real_t &E,
|
|
const real_t &w,
|
|
const real_t &h0,
|
|
const real_t &order_v,
|
|
const real_t &cfl,
|
|
const bool &use_viscosity,
|
|
const int &viscosity_type,
|
|
const real_t &visc_q1,
|
|
const real_t &visc_q2,
|
|
real_t &dt_est)
|
|
{
|
|
using matd = matd_t<DIM>;
|
|
|
|
constexpr real_t eps = 1e-12;
|
|
constexpr real_t vorticity_coeff = 1.0;
|
|
real_t p, cs;
|
|
const real_t detJ = det(J);
|
|
const matd invJ = inv(J);
|
|
matd stress{{{0.0}}};
|
|
const real_t rho = rho0 * det(J0) / detJ;
|
|
const real_t Ez = smoothmax(0.0, E, 1e-6);
|
|
real_t dt_visc_coeff = 0.0;
|
|
|
|
ComputeMaterialProperties(gamma, rho, Ez, p, cs);
|
|
|
|
for (int d = 0; d < DIM; d++) { stress(d, d) = -p; }
|
|
|
|
if (use_viscosity)
|
|
{
|
|
auto softstep = [](const real_t &width, const real_t x)
|
|
{
|
|
// Clamping to prevend under/overflow.
|
|
// This does not affect smoothness, it's only for extreme values.
|
|
// [-40,40] covers most practical values.
|
|
//auto arg = std::max(std::min(x / (width + 1e-6), 40.0), -40.0);
|
|
|
|
// Smooth transition from 0 to 1; symmetric around the zero.
|
|
// width: controls the slope of the sigmoid; lower -> sharper.
|
|
// value(0) = 0.5, value(+width) ~ 0.73, value(-width) ~ 0.27.
|
|
//return 1.0 / (1.0 + std::exp(-arg));
|
|
|
|
// Value at x=0.
|
|
real_t y0 = width;
|
|
real_t shift = 2.0 * width * atanh(1.0 - 2.0 * y0);
|
|
return 0.5 + 0.5 * tanh(0.5 * (x-shift) / width);
|
|
};
|
|
|
|
// y0 is value at x=0.
|
|
auto softstep7 = [](const real_t &width, const real_t y0, const real_t x)
|
|
{
|
|
real_t shift = 2.0 * width * atanh(1.0 - 2.0 * y0);
|
|
return 0.5 + 0.5 * tanh(0.5 * (x-shift) / width);
|
|
};
|
|
|
|
auto softabs = [](const real_t &eps, const real_t x)
|
|
{
|
|
// Clamping to prevend under/overflow; doesn't affect smoothness.
|
|
//auto e = std::max(eps, 1e-6_r);
|
|
|
|
// Diffuse the kink at |x| ~ 0. Activates as |x| approaches eps.
|
|
//return sqrt(x * x + e * e);
|
|
|
|
return hypot(x, eps);
|
|
};
|
|
|
|
if (viscosity_type == 2)
|
|
{
|
|
// Default Laghos viscosity.
|
|
// auto symdvdx = sym(dvdxi * invJ);
|
|
// auto [eigvals, eigvecs] = eig(symdvdx);
|
|
|
|
// // Measure of maximal compression.
|
|
// const real_t mu = eigvals(0);
|
|
// vecd compr_dir = get_col(eigvecs, 0);
|
|
|
|
// auto ph_dir = (J * inv(J0)) * compr_dir;
|
|
// const real_t h = h0 * norm(ph_dir) / norm(compr_dir);
|
|
// auto visc_coeff = 2.0 * rho * h * h * fabs(mu);
|
|
// visc_coeff += 0.5 * rho * h * cs * vorticity_coeff *
|
|
// (1.0 - softstep(eps, mu - 2.0 * eps));
|
|
// stress += visc_coeff * symdvdx;
|
|
// dt_visc_coeff = visc_coeff;
|
|
}
|
|
else if (viscosity_type == 21)
|
|
{
|
|
MFEM_ABORT("works only in 2D!");
|
|
|
|
// Default Laghos viscosity through a power method to
|
|
// get the measure of maximal compression.
|
|
// auto symdvdx = sym(dvdxi * invJ);
|
|
// auto [mu, compr_dir] = sinvpm(symdvdx, 10, 1e-12);
|
|
|
|
// auto ph_dir = (J * inv(J0)) * compr_dir;
|
|
// const real_t h = h0 * norm(ph_dir) / norm(compr_dir);
|
|
// auto visc_coeff = 2.0 * rho * h * h * softabs(1e-6, mu);
|
|
// visc_coeff += 0.5 * rho * h * cs * vorticity_coeff *
|
|
// (1.0 - softstep(eps, mu - 2.0 * eps));
|
|
// stress += visc_coeff * symdvdx;
|
|
// dt_visc_coeff = visc_coeff;
|
|
}
|
|
else if (viscosity_type == 22)
|
|
{
|
|
MFEM_ABORT("works only in 2D!");
|
|
|
|
// const auto delta = 0.2 * cs;
|
|
|
|
// auto symdvdx = sym(dvdxi * invJ);
|
|
// auto [eigvals, eigvecs] = eig2(symdvdx);
|
|
|
|
// // Measure of maximal compression.
|
|
// const real_t mu = eigvals(0);
|
|
// vecd compr_dir = get_col(eigvecs, 0);
|
|
// for (int i = 0; i < compr_dir.first_dim; i++)
|
|
// {
|
|
// compr_dir(i) = softstep(delta, compr_dir(i));
|
|
// }
|
|
|
|
// auto ph_dir = (J * inv(J0)) * compr_dir;
|
|
// const real_t h = h0 * norm(ph_dir) / norm(compr_dir);
|
|
// auto visc_coeff = 2.0 * rho * h * h * softabs(delta, mu);
|
|
// visc_coeff += 0.5 * rho * h * cs * vorticity_coeff *
|
|
// (1.0 - softstep(eps, mu - 2.0 * eps));
|
|
// if (!std::isfinite(visc_coeff)) { out << "err\n"; exit(1); }
|
|
// stress += visc_coeff * symdvdx;
|
|
// dt_visc_coeff = visc_coeff;
|
|
}
|
|
else if (viscosity_type == 4)
|
|
{
|
|
MFEM_ABORT("works only in 2D!");
|
|
|
|
// Viscosity type 4 from the paper (use all eigenvalues).
|
|
// auto symdvdx = sym(dvdxi * invJ);
|
|
// auto [lam, s] = eig(symdvdx);
|
|
// const auto delta = 0.2 * cs;
|
|
|
|
// for (int k = 0; k < DIMENSION; k++)
|
|
// {
|
|
// const auto ph_dir = (J * inv(J0)) * s(k);
|
|
// const real_t h = h0 * norm(ph_dir) / norm(s(k));
|
|
// auto visc_coeff = 2.0 * rho * h * h * softabs(delta, lam(k));
|
|
// visc_coeff += 0.5 * rho * h * cs * vorticity_coeff *
|
|
// (1.0 - softstep(delta, lam(k) - 2.0 * eps));
|
|
// stress += visc_coeff * symdvdx;
|
|
// dt_visc_coeff += visc_coeff;
|
|
// }
|
|
}
|
|
else if (viscosity_type == 7)
|
|
{
|
|
// Smoother viscosity for well-defined gradients.
|
|
|
|
const auto dvdx = dvdxi * inv(J);
|
|
const auto h = h0 * pow(det(J) / det(J0), 1.0 / static_cast<real_t>(DIM));
|
|
const auto delta_v = h * tr(dvdx);
|
|
|
|
// Coarses mesh h for the domain.
|
|
const real_t h_coarse = 1.0;
|
|
real_t dv_scale = 2.0 * h / h_coarse * cs;
|
|
|
|
// Smooth activation switch.
|
|
// Motivation: in compression, delta_v < 0.
|
|
// in shocks, delta_v starts to be comparable to cs.
|
|
// -> this viscosity becomes noticable when delta_v > dv_scale.
|
|
const auto psi = softstep7(dv_scale, 0.5, -delta_v);
|
|
|
|
// Smooths the kink at |delta_v| ~ 0 with correct units scaling.
|
|
// The smoothing becomes active when |delta_v| approaches dv_scale.
|
|
const auto abs_delta_v = softabs(dv_scale, delta_v);
|
|
|
|
const auto mu = rho * h * (visc_q2 * abs_delta_v + psi * visc_q1 * cs);
|
|
stress += mu * sym(dvdx);
|
|
|
|
dt_visc_coeff = mu;
|
|
}
|
|
else { MFEM_ABORT("wrong viscosity option"); }
|
|
}
|
|
|
|
if (rho < 0.0)
|
|
{
|
|
//MFEM_ABORT("negative density on quadrature point \n" "detJ = " << detJ);
|
|
//exit(1);
|
|
dt_est = 0.0;
|
|
}
|
|
|
|
if (detJ < 0.0)
|
|
{
|
|
//MFEM_ABORT("inverted element detected in qdata_setup");
|
|
// This will force repetition of the step with smaller dt.
|
|
dt_est = 0.0;
|
|
}
|
|
else
|
|
{
|
|
real_t sv = calcsv(J, DIM-1);
|
|
|
|
const real_t hmin = sv / static_cast<real_t>(order_v);
|
|
const real_t ihmin = 1.0 / hmin;
|
|
const real_t irhoihminsq = ihmin * ihmin / rho;
|
|
const real_t idt = cs / hmin + 2.5 * dt_visc_coeff * irhoihminsq;
|
|
|
|
if (idt > 0.0)
|
|
{
|
|
dt_est = fmin(dt_est, cfl / idt);
|
|
}
|
|
}
|
|
|
|
matd stressJiT = stress * transpose(invJ) * detJ * w;
|
|
return stressJiT;
|
|
}
|
|
|
|
template <int DIM>
|
|
struct TimeStepEstimateQFunction
|
|
{
|
|
using matd = matd_t<DIM>;
|
|
|
|
TimeStepEstimateQFunction(real_t *external_data) :
|
|
external_data(external_data) {}
|
|
|
|
MFEM_HOST_DEVICE inline
|
|
auto operator()(const matd &dvdxi, const real_t &rho0, const matd &J0,
|
|
const matd &J, const real_t &gamma, const real_t &E,
|
|
const real_t &w) const
|
|
{
|
|
real_t dt_est = std::numeric_limits<real_t>::infinity();
|
|
|
|
// Reuse qdata_setup() for the dt estimate logic; ignore its stress output.
|
|
(void)qdata_setup<DIM>(
|
|
dvdxi, rho0, J0, J, gamma, E, w,
|
|
external_data[EXT_DATA_IDX::H0],
|
|
external_data[EXT_DATA_IDX::ORDER_VEL],
|
|
external_data[EXT_DATA_IDX::CFL],
|
|
static_cast<bool>(external_data[EXT_DATA_IDX::VISCOSITY_FLAG]),
|
|
static_cast<int>(external_data[EXT_DATA_IDX::VISCOSITY_TYPE]),
|
|
external_data[EXT_DATA_IDX::VISC_Q1],
|
|
external_data[EXT_DATA_IDX::VISC_Q2],
|
|
dt_est);
|
|
|
|
return mfem::tuple{dt_est};
|
|
}
|
|
|
|
real_t *external_data;
|
|
};
|
|
|
|
template <int DIM>
|
|
struct UpdateQuadratureDataQFunction
|
|
{
|
|
using matd = matd_t<DIM>;
|
|
|
|
UpdateQuadratureDataQFunction(real_t *external_data) :
|
|
external_data(external_data) {}
|
|
|
|
MFEM_HOST_DEVICE inline
|
|
auto operator()(const matd &dvdxi, const real_t &rho0, const matd &J0,
|
|
const matd &J, const real_t &gamma, const real_t &E,
|
|
const real_t &w) const
|
|
{
|
|
// Do not update the global dt estimate here; that will be computed
|
|
// through a proper reduction over quadrature points.
|
|
real_t dt_est_dummy = std::numeric_limits<real_t>::infinity();
|
|
auto stressJiT =
|
|
qdata_setup<DIM>(
|
|
dvdxi, rho0, J0, J, gamma, E, w,
|
|
external_data[EXT_DATA_IDX::H0],
|
|
external_data[EXT_DATA_IDX::ORDER_VEL],
|
|
external_data[EXT_DATA_IDX::CFL],
|
|
static_cast<bool>(external_data[EXT_DATA_IDX::VISCOSITY_FLAG]),
|
|
static_cast<int>(external_data[EXT_DATA_IDX::VISCOSITY_TYPE]),
|
|
external_data[EXT_DATA_IDX::VISC_Q1],
|
|
external_data[EXT_DATA_IDX::VISC_Q2],
|
|
dt_est_dummy);
|
|
return mfem::tuple{stressJiT};
|
|
}
|
|
|
|
real_t *external_data;
|
|
};
|
|
|
|
template <int DIM>
|
|
class MomentumQFunction
|
|
{
|
|
using matd = matd_t<DIM>;
|
|
|
|
public:
|
|
MomentumQFunction(real_t *external_data) :
|
|
external_data(external_data) {}
|
|
|
|
MFEM_HOST_DEVICE inline
|
|
auto operator()(const matd &dvdxi, const real_t &rho0, const matd &J0,
|
|
const matd &J, const real_t &gamma, const real_t &E,
|
|
const real_t &w) const
|
|
{
|
|
auto stressJiT =
|
|
qdata_setup<DIM>(
|
|
dvdxi, rho0, J0, J, gamma, E, w,
|
|
external_data[EXT_DATA_IDX::H0],
|
|
external_data[EXT_DATA_IDX::ORDER_VEL],
|
|
external_data[EXT_DATA_IDX::CFL],
|
|
static_cast<bool>(external_data[EXT_DATA_IDX::VISCOSITY_FLAG]),
|
|
external_data[EXT_DATA_IDX::VISCOSITY_TYPE],
|
|
external_data[EXT_DATA_IDX::VISC_Q1],
|
|
external_data[EXT_DATA_IDX::VISC_Q2],
|
|
dt_est_dummy);
|
|
return mfem::tuple{stressJiT};
|
|
}
|
|
|
|
mutable real_t dt_est_dummy = std::numeric_limits<real_t>::infinity();
|
|
real_t *external_data;
|
|
};
|
|
|
|
template <int DIM>
|
|
class MomentumPAQFunction
|
|
{
|
|
using matd = matd_t<DIM>;
|
|
|
|
public:
|
|
MomentumPAQFunction() = default;
|
|
|
|
MFEM_HOST_DEVICE inline
|
|
auto operator()(const matd &stressJiT) const
|
|
{
|
|
return mfem::tuple{stressJiT};
|
|
}
|
|
};
|
|
|
|
template <int DIM>
|
|
class EnergyConservationQFunction
|
|
{
|
|
using matd = matd_t<DIM>;
|
|
|
|
public:
|
|
EnergyConservationQFunction(const real_t *external_data) :
|
|
external_data(external_data) {}
|
|
|
|
MFEM_HOST_DEVICE inline
|
|
auto operator()(
|
|
const matd &dvdxi,
|
|
const real_t &rho0,
|
|
const matd &J0,
|
|
const matd &J,
|
|
const real_t &gamma,
|
|
const real_t &E,
|
|
const real_t &w) const
|
|
{
|
|
auto stressJiT =
|
|
qdata_setup<DIM>(
|
|
dvdxi, rho0, J0, J, gamma, E, w,
|
|
external_data[EXT_DATA_IDX::H0],
|
|
external_data[EXT_DATA_IDX::ORDER_VEL],
|
|
external_data[EXT_DATA_IDX::CFL],
|
|
static_cast<bool>(external_data[EXT_DATA_IDX::VISCOSITY_FLAG]),
|
|
external_data[EXT_DATA_IDX::VISCOSITY_TYPE],
|
|
external_data[EXT_DATA_IDX::VISC_Q1],
|
|
external_data[EXT_DATA_IDX::VISC_Q2],
|
|
dt_est);
|
|
return mfem::tuple{ddot(stressJiT, dvdxi)};
|
|
}
|
|
|
|
mutable real_t dt_est = std::numeric_limits<real_t>::infinity();
|
|
const real_t *external_data;
|
|
};
|
|
|
|
template <int DIM>
|
|
class EnergyConservationPAQFunction
|
|
{
|
|
using matd = matd_t<DIM>;
|
|
|
|
public:
|
|
EnergyConservationPAQFunction() = default;
|
|
|
|
MFEM_HOST_DEVICE inline
|
|
auto operator()(const matd &dvdxi, const matd &stressJiT) const
|
|
{
|
|
return mfem::tuple{ddot(stressJiT, dvdxi)};
|
|
}
|
|
};
|
|
|
|
template <int DIM>
|
|
class TotalInternalEnergyQFunction
|
|
{
|
|
using matd = matd_t<DIM>;
|
|
|
|
public:
|
|
TotalInternalEnergyQFunction() = default;
|
|
|
|
MFEM_HOST_DEVICE inline
|
|
auto operator() (
|
|
const real_t &E,
|
|
const real_t &rho0,
|
|
const matd &J0,
|
|
const real_t &w) const
|
|
{
|
|
return mfem::tuple{rho0 * E * det(J0) * w};
|
|
}
|
|
};
|
|
|
|
template <int DIM>
|
|
class TotalKineticEnergyQFunction
|
|
{
|
|
using vecd = vecd_t<DIM>;
|
|
using matd = matd_t<DIM>;
|
|
|
|
public:
|
|
TotalKineticEnergyQFunction() = default;
|
|
|
|
MFEM_HOST_DEVICE inline
|
|
auto operator() (
|
|
const vecd &v,
|
|
const real_t &rho0,
|
|
const matd &J0,
|
|
const real_t &w) const
|
|
{
|
|
return mfem::tuple{rho0 * 0.5 * v * v * det(J0) * w};
|
|
}
|
|
};
|
|
|
|
template <int DIM>
|
|
class DensityQFunction
|
|
{
|
|
using matd = matd_t<DIM>;
|
|
|
|
public:
|
|
DensityQFunction() = default;
|
|
|
|
MFEM_HOST_DEVICE inline
|
|
auto operator() (
|
|
const real_t &rho0,
|
|
const matd &J0,
|
|
const real_t &w) const
|
|
{
|
|
return mfem::tuple{rho0 * det(J0) * w};
|
|
}
|
|
};
|
|
|
|
|
|
struct QuadratureData
|
|
{
|
|
static constexpr int aux_dim = 1;
|
|
QuadratureData(const ParMesh &mesh, const IntegrationRule &ir) :
|
|
StressSpace(mesh.Dimension(), mesh.Dimension()*mesh.Dimension(),
|
|
ir.GetNPoints(),
|
|
mesh.Dimension()*mesh.Dimension()*ir.GetNPoints()*mesh.GetNE()),
|
|
stressp(StressSpace),
|
|
R(mesh.Dimension(),
|
|
aux_dim,
|
|
ir.GetNPoints(),
|
|
aux_dim*ir.GetNPoints()*mesh.GetNE()),
|
|
dt_est(R)
|
|
{
|
|
dt_est.UseDevice(true);
|
|
stressp.UseDevice(true);
|
|
}
|
|
|
|
ParametricSpace StressSpace;
|
|
ParametricFunction stressp;
|
|
|
|
ParametricSpace R;
|
|
ParametricFunction dt_est;
|
|
};
|
|
|
|
class MassPAOperator : public Operator
|
|
{
|
|
public:
|
|
MassPAOperator(ParFiniteElementSpace &pfes,
|
|
const IntegrationRule &ir,
|
|
Coefficient &Q) :
|
|
Operator(pfes.GetTrueVSize()),
|
|
comm(pfes.GetParMesh()->GetComm()),
|
|
dim(pfes.GetMesh()->Dimension()),
|
|
NE(pfes.GetMesh()->GetNE()),
|
|
vsize(pfes.GetVSize()),
|
|
pabf(&pfes),
|
|
ess_tdofs_count(0),
|
|
ess_tdofs(0)
|
|
{
|
|
if (dim > 1)
|
|
{
|
|
pabf.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
|
}
|
|
pabf.AddDomainIntegrator(new mfem::MassIntegrator(Q, &ir));
|
|
pabf.Assemble();
|
|
pabf.FormSystemMatrix(mfem::Array<int>(), mass);
|
|
}
|
|
|
|
void SetEssentialTrueDofs(Array<int> &dofs)
|
|
{
|
|
ess_tdofs_count = dofs.Size();
|
|
if (ess_tdofs.Size() == 0)
|
|
{
|
|
int ess_tdofs_sz;
|
|
MPI_Allreduce(&ess_tdofs_count,&ess_tdofs_sz, 1, MPI_INT, MPI_SUM, comm);
|
|
MFEM_ASSERT(ess_tdofs_sz > 0, "ess_tdofs_sz should be positive!");
|
|
ess_tdofs.SetSize(ess_tdofs_sz);
|
|
}
|
|
if (ess_tdofs_count == 0) { return; }
|
|
ess_tdofs = dofs;
|
|
}
|
|
|
|
void EliminateRHS(Vector &b) const
|
|
{
|
|
if (ess_tdofs_count > 0) { b.SetSubVector(ess_tdofs, 0.0); }
|
|
}
|
|
|
|
void Mult(const Vector &x, Vector &y) const override
|
|
{
|
|
mass->Mult(x, y);
|
|
if (ess_tdofs_count > 0) { y.SetSubVector(ess_tdofs, 0.0); }
|
|
}
|
|
|
|
void FullAddMult(const Vector &x, Vector &y) const
|
|
{
|
|
mass->AddMult(x, y);
|
|
}
|
|
|
|
const ParBilinearForm &GetBF() const { return pabf; }
|
|
|
|
const MPI_Comm comm;
|
|
const int dim, NE, vsize;
|
|
ParBilinearForm pabf;
|
|
int ess_tdofs_count;
|
|
Array<int> ess_tdofs;
|
|
OperatorPtr mass;
|
|
};
|
|
|
|
class LagrangianHydroOperator : public TimeDependentOperator
|
|
{
|
|
public:
|
|
|
|
class LineSearchNewtonSolver : public NewtonSolver
|
|
{
|
|
public:
|
|
LineSearchNewtonSolver(MPI_Comm comm, const LagrangianHydroOperator &hydro)
|
|
: NewtonSolver(comm),
|
|
hydro(hydro),
|
|
beta(0.5), // Backtracking reduction factor
|
|
alpha(1e-2), // Sufficient decrease constant (Armijo condition)
|
|
max_line_iter(5) {}
|
|
|
|
void SetBacktrackingFactor(real_t b) { beta = b; }
|
|
void SetArmijoConstant(real_t a) { alpha = a; }
|
|
void SetMaxLineSearchIter(int n) { max_line_iter = n; }
|
|
|
|
protected:
|
|
void ProcessNewState(const Vector &x) const override
|
|
{
|
|
if (problem == 0) { return; }
|
|
|
|
s_new.SetSize(x.Size());
|
|
s_new = x;
|
|
s_new *= hydro.residual->dt;
|
|
s_new += hydro.residual->x;
|
|
|
|
real_t min_detJ = hydro.ComputeMinDet(s_new);
|
|
if (min_detJ <= 0.0)
|
|
{
|
|
out << "ProcessNewState negative detJ = " << min_detJ << "\n";
|
|
illegal_state = true;
|
|
}
|
|
illegal_state = false;
|
|
}
|
|
|
|
// Override the scaling factor computation to implement line search
|
|
real_t ComputeScalingFactor(const Vector &x, const Vector &b) const override
|
|
{
|
|
if (problem == 0) { return 1.0; }
|
|
|
|
const bool have_b = (b.Size() == Height());
|
|
real_t lambda = 1.0; // Initial step length
|
|
x_new.SetSize(x.Size());
|
|
r_new.SetSize(x.Size());
|
|
s_new.SetSize(x.Size());
|
|
|
|
const real_t initial_norm = Norm(r); // Current residual norm
|
|
|
|
grad->Mult(c, r_new);
|
|
const real_t grad_norm = Norm(r_new); // Gradient norm
|
|
|
|
for (int i = 0; i < max_line_iter; i++)
|
|
{
|
|
// Try step: x_new = x - lambda * c
|
|
add(x, -lambda, c, x_new);
|
|
|
|
// Before calling Mult on the operator we have to check mesh
|
|
// validity by computing the minimum determinant of the mesh
|
|
// Jacobian.
|
|
s_new = x_new;
|
|
s_new *= hydro.residual->dt;
|
|
s_new += hydro.residual->x;
|
|
real_t min_detJ = hydro.ComputeMinDet(s_new);
|
|
if (min_detJ <= 0.0)
|
|
{
|
|
out << "linesearch found negative detJ = " << min_detJ << "\n";
|
|
lambda *= beta;
|
|
continue;
|
|
}
|
|
|
|
// Evaluate residual at new point
|
|
oper->Mult(x_new, r_new);
|
|
if (have_b) { subtract(r_new, b, r_new); }
|
|
|
|
const real_t new_norm = Norm(r_new);
|
|
|
|
if (new_norm <= initial_norm - alpha * lambda * grad_norm)
|
|
{
|
|
// Found acceptable step
|
|
// if (Mpi::Root())
|
|
// { out << "linesearch good lambda: " << lambda << "\n"; }
|
|
return lambda;
|
|
}
|
|
|
|
// Backtrack
|
|
lambda *= beta;
|
|
}
|
|
|
|
if (Mpi::Root()) { out << ">>> linesearch didn't converge\n"; }
|
|
return 0.0;
|
|
}
|
|
|
|
private:
|
|
real_t beta; // Backtracking factor (how much to reduce step)
|
|
real_t alpha; // Sufficient decrease parameter
|
|
int max_line_iter; // Maximum line search iterations
|
|
mutable Vector x_new, r_new, s_new;
|
|
const LagrangianHydroOperator &hydro;
|
|
};
|
|
|
|
class LagrangianHydroJacobianOperator : public Operator
|
|
{
|
|
public:
|
|
LagrangianHydroJacobianOperator(
|
|
LagrangianHydroOperator &hydro,
|
|
std::shared_ptr<DerivativeOperator> dRvdx,
|
|
std::shared_ptr<DerivativeOperator> dRvdv,
|
|
std::shared_ptr<DerivativeOperator> dRvde,
|
|
std::shared_ptr<DerivativeOperator> dRedx,
|
|
std::shared_ptr<DerivativeOperator> dRedv,
|
|
std::shared_ptr<DerivativeOperator> dRede,
|
|
std::shared_ptr<DerivativeOperator> dTaylorSourcedx,
|
|
real_t h) :
|
|
Operator(2*hydro.H1.GetTrueVSize() + hydro.L2.GetTrueVSize()),
|
|
h(h),
|
|
H1tsize(hydro.H1.GetTrueVSize()), L2tsize(hydro.L2.GetTrueVSize()),
|
|
w(height),
|
|
z(height),
|
|
hydro(hydro),
|
|
dRvdx(dRvdx), dRvdv(dRvdv), dRvde(dRvde),
|
|
dRedx(dRedx), dRedv(dRedv), dRede(dRede),
|
|
dTaylorSourcedx(dTaylorSourcedx) { }
|
|
|
|
void Mult(const Vector &u, Vector &y) const override
|
|
{
|
|
w = u;
|
|
Vector wx, wv, we;
|
|
wx.MakeRef(w, 0, H1tsize);
|
|
wv.MakeRef(w, H1tsize, H1tsize);
|
|
we.MakeRef(w, 2*H1tsize, L2tsize);
|
|
|
|
Vector zx, zv, ze;
|
|
zx.MakeRef(z, 0, H1tsize);
|
|
zv.MakeRef(z, H1tsize, H1tsize);
|
|
ze.MakeRef(z, 2*H1tsize, L2tsize);
|
|
|
|
Vector yx, yv, ye;
|
|
yx.MakeRef(y, 0, H1tsize);
|
|
yv.MakeRef(y, H1tsize, H1tsize);
|
|
ye.MakeRef(y, 2*H1tsize, L2tsize);
|
|
|
|
// position
|
|
yx = wv;
|
|
yx *= -h;
|
|
yx += wx;
|
|
|
|
// velocity
|
|
// wv.SetSubVector(hydro.ess_tdof, 0.0);
|
|
dRvdx->Mult(wx, zv);
|
|
if (zv.CheckFinite() != 0)
|
|
{
|
|
pretty_print(zv);
|
|
}
|
|
MFEM_VERIFY(zv.CheckFinite() == 0, "err");
|
|
zv *= h;
|
|
yv = zv;
|
|
dRvdv->Mult(wv, zv);
|
|
MFEM_VERIFY(zv.CheckFinite() == 0, "err");
|
|
zv *= h;
|
|
yv += zv;
|
|
// hydro.Mv.TrueAddMult(wv, yv);
|
|
Vector wvc, yvc;
|
|
for (int c = 0; c < hydro.H1.GetMesh()->Dimension(); c++)
|
|
{
|
|
wvc.MakeRef(wv, c*hydro.H1c.GetTrueVSize(), hydro.H1c.GetTrueVSize());
|
|
yvc.MakeRef(yv, c*hydro.H1c.GetTrueVSize(), hydro.H1c.GetTrueVSize());
|
|
hydro.Mv->FullAddMult(wvc, yvc);
|
|
yvc.SyncAliasMemory(yv);
|
|
}
|
|
yv.SyncAliasMemory(y);
|
|
|
|
dRvde->Mult(we, zv);
|
|
MFEM_VERIFY(zv.CheckFinite() == 0, "err");
|
|
zv *= h;
|
|
yv += zv;
|
|
yv.SetSubVector(hydro.ess_tdof, 0.0);
|
|
|
|
// for (int i = 0; i < hydro.ess_tdof.Size(); i++)
|
|
// {
|
|
// // yv(hydro.ess_tdof[i]) = uv(hydro.ess_tdof[i]);
|
|
// yv(hydro.ess_tdof[i]) = 0.0;
|
|
// }
|
|
// yv = 0.0;
|
|
|
|
// energy
|
|
// [ wx ]
|
|
// [ dRe/dx dRe/dv dRe/de ] [ wv ]
|
|
// [ we ]
|
|
//
|
|
|
|
dRedx->Mult(wx, ze);
|
|
MFEM_VERIFY(ze.CheckFinite() == 0, "err");
|
|
if (problem == 0)
|
|
{
|
|
dTaylorSourcedx->AddMult(wx, ze);
|
|
MFEM_VERIFY(ze.CheckFinite() == 0, "err");
|
|
}
|
|
ze *= -h;
|
|
ye = ze;
|
|
|
|
dRedv->Mult(wv, ze);
|
|
MFEM_VERIFY(ze.CheckFinite() == 0, "err");
|
|
ze *= -h;
|
|
ye += ze;
|
|
|
|
dRede->Mult(we, ze);
|
|
MFEM_VERIFY(ze.CheckFinite() == 0, "err");
|
|
ze *= -h;
|
|
// OLD hydro.Me.TrueAddMult(we, ze);
|
|
hydro.Me->FullAddMult(we, ze);
|
|
MFEM_VERIFY(ze.CheckFinite() == 0, "err");
|
|
|
|
ye += ze;
|
|
|
|
yx.SyncAliasMemory(y);
|
|
yv.SyncAliasMemory(y);
|
|
ye.SyncAliasMemory(y);
|
|
}
|
|
|
|
virtual MemoryClass GetMemoryClass() const override
|
|
{
|
|
return Device::GetDeviceMemoryClass();
|
|
}
|
|
|
|
real_t h;
|
|
std::function<void(const Vector &, Vector &)> jvp, assembled_jvp;
|
|
const int H1tsize, L2tsize;
|
|
mutable Vector w, z;
|
|
|
|
LagrangianHydroOperator &hydro;
|
|
std::shared_ptr<DerivativeOperator> dRvdx;
|
|
std::shared_ptr<DerivativeOperator> dRvdv;
|
|
std::shared_ptr<DerivativeOperator> dRvde;
|
|
std::shared_ptr<DerivativeOperator> dRedx;
|
|
std::shared_ptr<DerivativeOperator> dRedv;
|
|
std::shared_ptr<DerivativeOperator> dRede;
|
|
std::shared_ptr<DerivativeOperator> dTaylorSourcedx;
|
|
};
|
|
|
|
class Preconditioner : public Solver
|
|
{
|
|
public:
|
|
Preconditioner(LagrangianHydroOperator &hydro) :
|
|
Solver(hydro.Height()),
|
|
hydro(hydro)
|
|
{};
|
|
|
|
void SetRebuildFlag(bool flag)
|
|
{
|
|
rebuild = flag;
|
|
}
|
|
|
|
void SetOperator(const Operator &op) override
|
|
{
|
|
if (!rebuild)
|
|
{
|
|
return;
|
|
}
|
|
|
|
jacobian = dynamic_cast<const LagrangianHydroJacobianOperator*>(&op);
|
|
MFEM_VERIFY(jacobian != nullptr, "Preconditioner error.");
|
|
|
|
auto comm = hydro.H1.GetComm();
|
|
const real_t h = jacobian->h;
|
|
|
|
HYPRE_BigInt *tdof_offsets = hydro.H1.GetTrueDofOffsets();
|
|
|
|
// First row
|
|
// Rx = x - h * v
|
|
// yx = I * wx - h I * wv
|
|
SparseMatrix dRxdx_diag(hydro.H1.GetTrueVSize());
|
|
for (int i = 0; i < dRxdx_diag.Height(); i++)
|
|
{
|
|
dRxdx_diag.Set(i, i, 1.0);
|
|
}
|
|
dRxdx_diag.Finalize();
|
|
HypreParMatrix dRxdx_mat(comm, hydro.H1.GlobalTrueVSize(),
|
|
tdof_offsets, &dRxdx_diag);
|
|
|
|
// dRvdv = (Mv + h dF/dv)
|
|
HypreParMatrix dRvdv_mat;
|
|
jacobian->dRvdv->Assemble(dRvdv_mat);
|
|
HypreParMatrix *Mv_hdRvdv_mat = Add(1.0, hydro.Mv_mat, h, dRvdv_mat);
|
|
auto tmp2 = Mv_hdRvdv_mat->EliminateRowsCols(hydro.ess_tdof);
|
|
delete tmp2;
|
|
|
|
// dRede = Me - h * dF^T/de
|
|
HypreParMatrix dRede_mat;
|
|
jacobian->dRede->Assemble(dRede_mat);
|
|
HypreParMatrix *Me_hdRede_mat = Add(1.0, hydro.Me_mat, -h, dRede_mat);
|
|
|
|
if (hydro.preconditioner_type == PRECONDITIONER_TYPE::BLOCK_DIAGONAL_AMG)
|
|
{
|
|
// if (Mpi::Root()) { out << "building pc\n"; }
|
|
vv_mat.reset(Mv_hdRvdv_mat);
|
|
amg_v.reset(new HypreBoomerAMG(*vv_mat));
|
|
amg_v->SetPrintLevel(0);
|
|
|
|
ee_mat.reset(Me_hdRede_mat);
|
|
amg_e.reset(new HypreBoomerAMG(*ee_mat));
|
|
amg_e->SetPrintLevel(0);
|
|
}
|
|
|
|
else if (hydro.preconditioner_type == PRECONDITIONER_TYPE::SUPERLU)
|
|
{
|
|
#ifdef MFEM_USE_SUPERLU
|
|
// dRxdv = -h * I
|
|
SparseMatrix dRxdv_diag(hydro.H1.GetTrueVSize());
|
|
for (int i = 0; i < dRxdv_diag.Height(); i++)
|
|
{
|
|
dRxdv_diag.Set(i, i, -h);
|
|
}
|
|
|
|
for (int i = 0; i < hydro.ess_tdof.Size(); i++)
|
|
{
|
|
dRxdv_diag.Set(hydro.ess_tdof[i], hydro.ess_tdof[i], 0.0);
|
|
}
|
|
dRxdv_diag.Finalize();
|
|
|
|
HypreParMatrix dRxdv_mat(comm, hydro.H1.GlobalTrueVSize(),
|
|
tdof_offsets, &dRxdv_diag);
|
|
|
|
// Second row
|
|
// Rv = Mv * v + F * I
|
|
// yv = (Mv + h * dF/dv) * wv + h * dF/dx * wx + h * dF/de * we
|
|
|
|
// dRvdx = h * dF/dx
|
|
HypreParMatrix dRvdx_mat;
|
|
jacobian->dRvdx->Assemble(dRvdx_mat);
|
|
dRvdx_mat.EliminateRows(hydro.ess_tdof);
|
|
dRvdx_mat *= h;
|
|
|
|
// dRvde = h * dF/de
|
|
HypreParMatrix dRvde_mat;
|
|
jacobian->dRvde->Assemble(dRvde_mat);
|
|
dRvde_mat.EliminateRows(hydro.ess_tdof);
|
|
dRvde_mat *= h;
|
|
|
|
// Third row
|
|
// Re = Me * e - F^T
|
|
// ye = (Me - h * dF/de) * we - h * dF/dx * wx - h * dF/dv * wv
|
|
|
|
// dRedx = -h * dF^T/dx
|
|
HypreParMatrix dRedx_mat;
|
|
jacobian->dRedx->Assemble(dRedx_mat);
|
|
|
|
if (problem == 0)
|
|
{
|
|
HypreParMatrix dTaylorSourcedx_mat;
|
|
jacobian->dTaylorSourcedx->Assemble(dTaylorSourcedx_mat);
|
|
dRedx_mat.Add(1.0, dTaylorSourcedx_mat);
|
|
}
|
|
|
|
dRedx_mat *= -h;
|
|
|
|
// dRedv = -h * dF^T/dv
|
|
HypreParMatrix dRedv_mat;
|
|
jacobian->dRedv->Assemble(dRedv_mat);
|
|
auto tmp1 = dRedv_mat.EliminateCols(hydro.ess_tdof);
|
|
delete tmp1;
|
|
dRedv_mat *= -h;
|
|
|
|
Array2D<const HypreParMatrix*> blocks(3, 3);
|
|
blocks = nullptr;
|
|
blocks(0, 0) = &dRxdx_mat;
|
|
blocks(0, 1) = &dRxdv_mat;
|
|
blocks(1, 0) = &dRvdx_mat;
|
|
blocks(1, 1) = Mv_hdRvdv_mat;
|
|
blocks(1, 2) = &dRvde_mat;
|
|
blocks(2, 0) = &dRedx_mat;
|
|
blocks(2, 1) = &dRedv_mat;
|
|
blocks(2, 2) = Me_hdRede_mat;
|
|
|
|
superlu_solver.reset(new SuperLUSolver(MPI_COMM_WORLD, 1));
|
|
block_hypre.reset(HypreParMatrixFromBlocks(blocks, nullptr));
|
|
superlu_mat.reset(new SuperLURowLocMatrix(*block_hypre));
|
|
superlu_solver->SetSymmetricPattern(false);
|
|
superlu_solver->SetOperator(*superlu_mat);
|
|
superlu_solver->SetPrintStatistics(false);
|
|
#else
|
|
MFEM_ABORT("MFEM is not built with SuperLU");
|
|
#endif
|
|
}
|
|
|
|
rebuild = false;
|
|
}
|
|
|
|
void Mult(const Vector &x, Vector &y) const override
|
|
{
|
|
if (hydro.preconditioner_type == PRECONDITIONER_TYPE::BLOCK_DIAGONAL_AMG)
|
|
{
|
|
w = x;
|
|
Vector wx, wv, we;
|
|
wx.MakeRef(w, 0, hydro.H1.GetTrueVSize());
|
|
wv.MakeRef(w, hydro.H1.GetTrueVSize(), hydro.H1.GetTrueVSize());
|
|
we.MakeRef(w, 2*hydro.H1.GetTrueVSize(), hydro.L2.GetTrueVSize());
|
|
|
|
Vector yx, yv, ye;
|
|
yx.MakeRef(y, 0, hydro.H1.GetTrueVSize());
|
|
yv.MakeRef(y, hydro.H1.GetTrueVSize(), hydro.H1.GetTrueVSize());
|
|
ye.MakeRef(y, 2*hydro.H1.GetTrueVSize(), hydro.L2.GetTrueVSize());
|
|
|
|
yx = wx;
|
|
amg_v->Mult(wv, yv);
|
|
amg_e->Mult(we, ye);
|
|
}
|
|
else if (hydro.preconditioner_type == PRECONDITIONER_TYPE::SUPERLU)
|
|
{
|
|
#ifdef MFEM_USE_SUPERLU
|
|
superlu_solver->Mult(x, y);
|
|
#else
|
|
MFEM_ABORT("MFEM is not built with SuperLU");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
mutable Vector w;
|
|
LagrangianHydroOperator &hydro;
|
|
bool rebuild = true;
|
|
const LagrangianHydroJacobianOperator *jacobian = nullptr;
|
|
std::shared_ptr<HypreParMatrix> block_hypre;
|
|
std::shared_ptr<HypreParMatrix> vv_mat;
|
|
std::shared_ptr<HypreBoomerAMG> amg_v;
|
|
std::shared_ptr<HypreParMatrix> ee_mat;
|
|
std::shared_ptr<HypreBoomerAMG> amg_e;
|
|
#ifdef MFEM_USE_SUPERLU
|
|
std::shared_ptr<SuperLURowLocMatrix> superlu_mat;
|
|
std::shared_ptr<SuperLUSolver> superlu_solver;
|
|
#endif
|
|
};
|
|
|
|
class LagrangianHydroResidualOperator : public Operator
|
|
{
|
|
public:
|
|
LagrangianHydroResidualOperator(LagrangianHydroOperator &hydro,
|
|
const Vector &x, bool fd_gradient, int dump_jacobians) :
|
|
Operator(2*hydro.H1.GetTrueVSize()+hydro.L2.GetTrueVSize()),
|
|
hydro(hydro),
|
|
H1tsize(hydro.H1.GetTrueVSize()),
|
|
H1vsize(hydro.H1.GetVSize()),
|
|
L2tsize(hydro.L2.GetTrueVSize()),
|
|
L2vsize(hydro.L2.GetVSize()),
|
|
x(x),
|
|
u(x.Size()),
|
|
u_l(2*H1vsize + L2vsize),
|
|
e_source_t(hydro.L2.GetTrueVSize()),
|
|
fd_gradient(fd_gradient),
|
|
dump_jacobians(dump_jacobians) {}
|
|
|
|
void SetTimeStep(const real_t &time_step) { this->dt = time_step; }
|
|
|
|
void Mult(const Vector &k, Vector &R) const override
|
|
{
|
|
u = k;
|
|
u *= dt;
|
|
u += x;
|
|
|
|
auto kptr = const_cast<Vector*>(&k);
|
|
Vector kx, kv, ke;
|
|
kx.MakeRef(*kptr, 0, H1tsize);
|
|
kv.MakeRef(*kptr, H1tsize, H1tsize);
|
|
ke.MakeRef(*kptr, 2*H1tsize, L2tsize);
|
|
|
|
Vector ux, uv, ue;
|
|
ux.MakeRef(u, 0, H1tsize);
|
|
uv.MakeRef(u, H1tsize, H1tsize);
|
|
ue.MakeRef(u, 2*H1tsize, L2tsize);
|
|
|
|
Vector Rx, Rv, Re;
|
|
Rx.MakeRef(R, 0, H1tsize);
|
|
Rv.MakeRef(R, H1tsize, H1tsize);
|
|
Re.MakeRef(R, 2*H1tsize, L2tsize);
|
|
|
|
Vector ux_l, uv_l, ue_l;
|
|
ux_l.MakeRef(u_l, 0, H1vsize);
|
|
uv_l.MakeRef(u_l, H1vsize, H1vsize);
|
|
ue_l.MakeRef(u_l, 2*H1vsize, L2vsize);
|
|
|
|
hydro.H1.GetProlongationMatrix()->Mult(ux, ux_l);
|
|
hydro.H1.GetProlongationMatrix()->Mult(uv, uv_l);
|
|
hydro.L2.GetProlongationMatrix()->Mult(ue, ue_l);
|
|
|
|
hydro.UpdateMesh(ux_l);
|
|
hydro.mesh_nodes.SyncMemory(ux_l);
|
|
|
|
Rx = kx;
|
|
Rx -= uv;
|
|
|
|
// out << "in qupdate\n";
|
|
// pretty_print(ux_l);
|
|
hydro.qdata_is_current = false;
|
|
hydro.UpdateQuadratureData(u_l);
|
|
|
|
hydro.momentum_pa->SetParameters({&hydro.qdata->stressp});
|
|
hydro.momentum_pa->Mult(uv, Rv);
|
|
|
|
// hydro.Mv.TrueAddMult(kv, Rv);
|
|
Vector kvc, Rvc;
|
|
for (int c = 0; c < hydro.H1.GetMesh()->Dimension(); c++)
|
|
{
|
|
kvc.MakeRef(kv, c*hydro.H1c.GetTrueVSize(), hydro.H1c.GetTrueVSize());
|
|
Rvc.MakeRef(Rv, c*hydro.H1c.GetTrueVSize(), hydro.H1c.GetTrueVSize());
|
|
hydro.Mv->FullAddMult(kvc, Rvc);
|
|
Rvc.SyncAliasMemory(Rv);
|
|
}
|
|
Rv.SyncAliasMemory(R);
|
|
|
|
Rv.SetSubVector(hydro.ess_tdof, 0.0);
|
|
|
|
hydro.energy_conservation_pa->SetParameters({&uv_l, &hydro.qdata->stressp});
|
|
hydro.energy_conservation_pa->Mult(ue, Re);
|
|
|
|
Re.Neg();
|
|
|
|
if (problem == 0)
|
|
{
|
|
hydro.taylor_source_mf->SetParameters({&ux_l});
|
|
hydro.taylor_source_mf->Mult(e_source_t, e_source_t);
|
|
Re -= e_source_t;
|
|
}
|
|
|
|
// hydro.Me.TrueAddMult(ke, Re);
|
|
hydro.Me->FullAddMult(ke, Re);
|
|
|
|
Rx.SyncAliasMemory(R);
|
|
Rv.SyncAliasMemory(R);
|
|
Re.SyncAliasMemory(R);
|
|
}
|
|
|
|
Operator& GetGradient(const Vector &k) const override
|
|
{
|
|
u = k;
|
|
u *= dt;
|
|
u += x;
|
|
|
|
auto kptr = const_cast<Vector*>(&k);
|
|
Vector kx, kv, ke;
|
|
kx.MakeRef(*kptr, 0, H1tsize);
|
|
kv.MakeRef(*kptr, H1tsize, H1tsize);
|
|
ke.MakeRef(*kptr, 2*H1tsize, L2tsize);
|
|
|
|
Vector ux, uv, ue;
|
|
ux.MakeRef(u, 0, H1tsize);
|
|
uv.MakeRef(u, H1tsize, H1tsize);
|
|
ue.MakeRef(u, 2*H1tsize, L2tsize);
|
|
|
|
Vector ux_l, uv_l, ue_l;
|
|
ux_l.MakeRef(u_l, 0, H1vsize);
|
|
uv_l.MakeRef(u_l, H1vsize, H1vsize);
|
|
ue_l.MakeRef(u_l, 2*H1vsize, L2vsize);
|
|
|
|
hydro.H1.GetProlongationMatrix()->Mult(ux, ux_l);
|
|
hydro.H1.GetProlongationMatrix()->Mult(uv, uv_l);
|
|
hydro.L2.GetProlongationMatrix()->Mult(ue, ue_l);
|
|
|
|
if (fd_gradient)
|
|
{
|
|
fd_jacobian = std::make_shared<FDJacobian>(*this, k, 1e-8);
|
|
return *fd_jacobian;
|
|
}
|
|
else
|
|
{
|
|
auto dRvdx = hydro.momentum_mf->GetDerivative(COORDINATES, {&uv_l},
|
|
{&hydro.rho0, &hydro.x0, &ux_l, &hydro.material, &ue_l});
|
|
|
|
auto dRvdv = hydro.momentum_mf->GetDerivative(VELOCITY, {&uv_l},
|
|
{&hydro.rho0, &hydro.x0, &ux_l, &hydro.material, &ue_l});
|
|
|
|
auto dRvde = hydro.momentum_mf->GetDerivative(SPECIFIC_INTERNAL_ENERGY, {&uv_l},
|
|
{&hydro.rho0, &hydro.x0, &ux_l, &hydro.material, &ue_l});
|
|
|
|
auto dRedx = hydro.energy_conservation_mf->GetDerivative(COORDINATES, {&ue_l},
|
|
{&uv_l, &hydro.rho0, &hydro.x0, &ux_l, &hydro.material});
|
|
|
|
auto dRedv = hydro.energy_conservation_mf->GetDerivative(VELOCITY, {&ue_l},
|
|
{&uv_l, &hydro.rho0, &hydro.x0, &ux_l, &hydro.material});
|
|
|
|
auto dRede = hydro.energy_conservation_mf->GetDerivative(
|
|
SPECIFIC_INTERNAL_ENERGY, {&ue_l},
|
|
{&uv_l, &hydro.rho0, &hydro.x0, &ux_l, &hydro.material});
|
|
|
|
auto dTaylorSourcedx = hydro.taylor_source_mf->GetDerivative(COORDINATES, {&ue_l}, {&ux_l});
|
|
|
|
jacobian = std::make_shared<LagrangianHydroJacobianOperator>(
|
|
hydro, dRvdx, dRvdv, dRvde, dRedx, dRedv, dRede, dTaylorSourcedx, dt);
|
|
|
|
if (dump_jacobians > 0)
|
|
{
|
|
auto ess_tdof_backup(hydro.ess_tdof);
|
|
hydro.ess_tdof.SetSize(0);
|
|
|
|
out << "\ndumping jacobians\n";
|
|
std::ofstream jvpmat("jvpmat.m");
|
|
jacobian->PrintMatlab(jvpmat);
|
|
jvpmat.close();
|
|
|
|
fd_jacobian = std::make_shared<FDJacobian>(*this, k, 1e-8);
|
|
std::ofstream fdjacmat("fdjacmat.m");
|
|
fd_jacobian->PrintMatlab(fdjacmat);
|
|
fdjacmat.close();
|
|
|
|
// hydro.preconditioner->SetOperator(*jacobian);
|
|
// std::ofstream jprecmat("jprecmat.m");
|
|
// hydro.preconditioner->block_hypre->PrintMatlab(jprecmat);
|
|
// jprecmat.close();
|
|
|
|
if (dump_jacobians == 1)
|
|
{
|
|
exit(0);
|
|
}
|
|
|
|
hydro.ess_tdof = ess_tdof_backup;
|
|
}
|
|
|
|
if (hydro.use_petsc)
|
|
{
|
|
#ifdef MFEM_USE_PETSC
|
|
const real_t h = jacobian->h;
|
|
auto comm = hydro.H1.GetComm();
|
|
|
|
// Build the assembled PETSc Jacobian.
|
|
//
|
|
// Layout:
|
|
// [ x ] size H1tsize
|
|
// [ v ] size H1tsize
|
|
// [ e ] size L2tsize
|
|
|
|
const int H1tsize = hydro.H1.GetTrueVSize();
|
|
const int L2tsize = hydro.L2.GetTrueVSize();
|
|
|
|
//
|
|
// Build diagonal blocks through HypreParMatrix to guarantee a
|
|
// consistent parallel layout.
|
|
//
|
|
|
|
HYPRE_BigInt *H1_tdof_offsets = hydro.H1.GetTrueDofOffsets();
|
|
|
|
// Rxdx.
|
|
SparseMatrix dRxdx_diag(hydro.H1.GetTrueVSize());
|
|
for (int i = 0; i < dRxdx_diag.Height(); i++)
|
|
{
|
|
dRxdx_diag.Set(i, i, 1.0);
|
|
}
|
|
dRxdx_diag.Finalize();
|
|
HypreParMatrix dRxdx_mat(comm, hydro.H1.GlobalTrueVSize(),
|
|
H1_tdof_offsets, &dRxdx_diag);
|
|
PetscParMatrix dRxdx_petsc(&dRxdx_mat);
|
|
|
|
// Rxdv.
|
|
SparseMatrix dRxdv_diag(hydro.H1.GetTrueVSize());
|
|
for (int i = 0; i < dRxdv_diag.Height(); i++)
|
|
{
|
|
dRxdv_diag.Set(i, i, -h);
|
|
}
|
|
for (int i = 0; i < hydro.ess_tdof.Size(); i++)
|
|
{
|
|
dRxdv_diag.Set(hydro.ess_tdof[i], hydro.ess_tdof[i], 0.0);
|
|
}
|
|
dRxdv_diag.Finalize();
|
|
HypreParMatrix dRxdv_mat(comm, hydro.H1.GlobalTrueVSize(),
|
|
H1_tdof_offsets, &dRxdv_diag);
|
|
PetscParMatrix dRxdv_petsc(&dRxdv_mat);
|
|
|
|
// Rvdx.
|
|
HypreParMatrix dRvdx_mat;
|
|
jacobian->dRvdx->Assemble(dRvdx_mat);
|
|
dRvdx_mat.EliminateRows(hydro.ess_tdof);
|
|
PetscParMatrix dRvdx_petsc(&dRvdx_mat);
|
|
dRvdx_petsc *= h;
|
|
|
|
// Rvdv.
|
|
HypreParMatrix dRvdv_mat;
|
|
jacobian->dRvdv->Assemble(dRvdv_mat);
|
|
ParBilinearForm Mv(&hydro.H1);
|
|
Mv.AddDomainIntegrator(new VectorMassIntegrator(hydro.rho0_coeff,
|
|
&hydro.ir));
|
|
Mv.Assemble();
|
|
Mv.Finalize();
|
|
HypreParMatrix Mv_mat;
|
|
Mv.FormSystemMatrix(mfem::Array<int>(), Mv_mat);
|
|
HypreParMatrix *dRvdv_combined = Add(1.0, Mv_mat, h, dRvdv_mat);
|
|
auto tmp0 = dRvdv_combined->EliminateRowsCols(hydro.ess_tdof);
|
|
delete tmp0;
|
|
PetscParMatrix dRvdv_petsc(dRvdv_combined);
|
|
delete dRvdv_combined;
|
|
|
|
// Rvde.
|
|
HypreParMatrix dRvde_mat;
|
|
jacobian->dRvde->Assemble(dRvde_mat);
|
|
dRvde_mat.EliminateRows(hydro.ess_tdof);
|
|
PetscParMatrix dRvde_petsc(&dRvde_mat);
|
|
dRvde_petsc *= h;
|
|
|
|
// Redx.
|
|
HypreParMatrix dRedx_mat;
|
|
jacobian->dRedx->Assemble(dRedx_mat);
|
|
PetscParMatrix dRedx_petsc(&dRedx_mat);
|
|
if (problem == 0)
|
|
{
|
|
HypreParMatrix dTaylorSourcedx_mat;
|
|
jacobian->dTaylorSourcedx->Assemble(dTaylorSourcedx_mat);
|
|
PetscParMatrix dTaylorSourcedx_petsc(&dTaylorSourcedx_mat);
|
|
dRedx_petsc += dTaylorSourcedx_petsc;
|
|
}
|
|
dRedx_petsc *= -h;
|
|
|
|
// Redv.
|
|
HypreParMatrix dRedv_mat;
|
|
jacobian->dRedv->Assemble(dRedv_mat);
|
|
auto tmp1 = dRedv_mat.EliminateCols(hydro.ess_tdof);
|
|
delete tmp1;
|
|
PetscParMatrix dRedv_petsc(&dRedv_mat);
|
|
dRedv_petsc *= -h;
|
|
|
|
// Rede.
|
|
HypreParMatrix dRede_mat;
|
|
jacobian->dRede->Assemble(dRede_mat);
|
|
PetscParMatrix dRede_petsc(&dRede_mat);
|
|
ParBilinearForm Me(&hydro.L2);
|
|
Me.AddDomainIntegrator(new MassIntegrator(hydro.rho0_coeff,
|
|
&hydro.ir));
|
|
Me.Assemble();
|
|
Me.Finalize();
|
|
HypreParMatrix Me_mat;
|
|
Me.FormSystemMatrix(mfem::Array<int>(), Me_mat);
|
|
PetscParMatrix Me_petsc(&Me_mat);
|
|
PetscErrorCode ierr = MatAYPX(dRede_petsc, -h, Me_petsc,
|
|
MatStructure::DIFFERENT_NONZERO_PATTERN);
|
|
MFEM_VERIFY(ierr == PETSC_SUCCESS, "MatAYPX(dRede) failed");
|
|
|
|
Array<int> offsets(4);
|
|
offsets[0] = 0;
|
|
offsets[1] = H1tsize;
|
|
offsets[2] = H1tsize;
|
|
offsets[3] = L2tsize;
|
|
offsets.PartialSum();
|
|
|
|
BlockOperator block_op(offsets);
|
|
block_op.SetBlock(0, 0, &dRxdx_petsc);
|
|
block_op.SetBlock(0, 1, &dRxdv_petsc);
|
|
block_op.SetBlock(1, 0, &dRvdx_petsc);
|
|
block_op.SetBlock(1, 1, &dRvdv_petsc);
|
|
block_op.SetBlock(1, 2, &dRvde_petsc);
|
|
block_op.SetBlock(2, 0, &dRedx_petsc);
|
|
block_op.SetBlock(2, 1, &dRedv_petsc);
|
|
block_op.SetBlock(2, 2, &dRede_petsc);
|
|
|
|
// Recreate the PETSc matrix every time. Trying to update the
|
|
// underlying Mat in-place is error-prone because the temporary
|
|
// block/matrix objects go out of scope after this call.
|
|
petsc_jacobian.reset(
|
|
new PetscParMatrix(comm, &block_op, Operator::PETSC_MATAIJ));
|
|
return *petsc_jacobian;
|
|
#else
|
|
MFEM_ABORT("MFEM is not built with PETSc");
|
|
#endif
|
|
}
|
|
|
|
return *jacobian;
|
|
}
|
|
}
|
|
|
|
LagrangianHydroOperator &hydro;
|
|
real_t dt;
|
|
const int H1tsize;
|
|
const int H1vsize;
|
|
const int L2tsize;
|
|
const int L2vsize;
|
|
const Vector &x;
|
|
mutable Vector u, u_l, e_source_t;
|
|
mutable std::shared_ptr<FDJacobian> fd_jacobian;
|
|
mutable std::shared_ptr<LagrangianHydroJacobianOperator> jacobian;
|
|
#ifdef MFEM_USE_PETSC
|
|
mutable std::unique_ptr<PetscParMatrix> petsc_jacobian;
|
|
#endif
|
|
bool fd_gradient;
|
|
int dump_jacobians;
|
|
};
|
|
|
|
LagrangianHydroOperator(
|
|
ParFiniteElementSpace &H1,
|
|
ParFiniteElementSpace &L2,
|
|
Array<int> &ess_tdof,
|
|
const IntegrationRule &ir,
|
|
FunctionCoefficient &rho0_coeff,
|
|
ParGridFunction &x0_gf,
|
|
ParGridFunction &rho0_gf,
|
|
ParGridFunction &material_gf,
|
|
std::shared_ptr<DifferentiableOperator> update_qdata,
|
|
std::shared_ptr<DifferentiableOperator> dtest_mf,
|
|
std::shared_ptr<DifferentiableOperator> momentum_mf,
|
|
std::shared_ptr<DifferentiableOperator> momentum_pa,
|
|
std::shared_ptr<DifferentiableOperator> energy_conservation_mf,
|
|
std::shared_ptr<DifferentiableOperator> energy_conservation_pa,
|
|
std::shared_ptr<DifferentiableOperator> total_internal_energy_mf,
|
|
std::shared_ptr<DifferentiableOperator> total_kinetic_energy_mf,
|
|
std::shared_ptr<DifferentiableOperator> density_mf,
|
|
std::shared_ptr<DifferentiableOperator> taylor_source_mf,
|
|
std::shared_ptr<QuadratureData> qdata,
|
|
const bool &fd_gradient,
|
|
const int &dump_jacobians,
|
|
const int &nonlinear_maximum_iterations,
|
|
const real_t &nonlinear_relative_tolerance,
|
|
const int &krylov_maximum_iterations,
|
|
const int &preconditioner_lag,
|
|
const PRECONDITIONER_TYPE &preconditioner_type,
|
|
Vector& external_data,
|
|
const bool use_petsc) :
|
|
TimeDependentOperator(2*H1.GetVSize()+L2.GetVSize()),
|
|
H1(H1),
|
|
L2(L2),
|
|
H1c(H1.GetParMesh(), H1.FEColl(), 1),
|
|
ess_tdof(ess_tdof),
|
|
ir(ir),
|
|
x0(x0_gf),
|
|
rho0(rho0_gf),
|
|
material(material_gf),
|
|
update_qdata(update_qdata),
|
|
dtest_mf(dtest_mf),
|
|
momentum_mf(momentum_mf),
|
|
momentum_pa(momentum_pa),
|
|
energy_conservation_mf(energy_conservation_mf),
|
|
energy_conservation_pa(energy_conservation_pa),
|
|
total_internal_energy_mf(total_internal_energy_mf),
|
|
total_kinetic_energy_mf(total_kinetic_energy_mf),
|
|
density_mf(density_mf),
|
|
taylor_source_mf(taylor_source_mf),
|
|
qdata(qdata),
|
|
mesh_nodes(&H1),
|
|
rhsvc(&H1c),
|
|
dvc(&H1c),
|
|
Mv_blf(&H1),
|
|
Me_blf(&L2),
|
|
rho0_coeff(rho0_coeff),
|
|
RHSv(H1.GetTrueVSize()),
|
|
rhsv(H1.GetVSize()),
|
|
X(2*H1.GetTrueVSize()+L2.GetTrueVSize()),
|
|
Xv(H1.GetTrueVSize()),
|
|
Xvc(H1c.GetTrueVSize()),
|
|
Xe(L2.GetTrueVSize()),
|
|
K(2*H1.GetTrueVSize()+L2.GetTrueVSize()),
|
|
B(H1c.GetTrueVSize()),
|
|
RHSe(L2.GetTrueVSize()),
|
|
rhse(L2.GetVSize()),
|
|
nl2dofs(L2.GetFE(0)->GetDof()),
|
|
fd_gradient(fd_gradient),
|
|
dump_jacobians(dump_jacobians),
|
|
nonlinear_maximum_iterations(nonlinear_maximum_iterations),
|
|
nonlinear_relative_tolerance(nonlinear_relative_tolerance),
|
|
krylov_maximum_iterations(krylov_maximum_iterations),
|
|
preconditioner_lag(preconditioner_lag),
|
|
preconditioner_type(preconditioner_type),
|
|
external_data(external_data),
|
|
use_petsc(use_petsc)
|
|
{
|
|
Mv = new MassPAOperator(H1c, ir, rho0_coeff);
|
|
Array<int> empty_tdofs;
|
|
Mv_Jprec = new OperatorJacobiSmoother(Mv->GetBF(), empty_tdofs);
|
|
|
|
Me = new MassPAOperator(L2, ir, rho0_coeff);
|
|
|
|
// Inside the above constructors for mass, there is reordering of the mesh
|
|
// nodes which is performed on the host. Since the mesh nodes are a
|
|
// subvector, so we need to sync with the rest of the base vector (which
|
|
// is assumed to be in the memory space used by the mfem::Device).
|
|
H1.GetParMesh()->GetNodes()->ReadWrite();
|
|
// Attributes 1/2/3 correspond to fixed-x/y/z boundaries, i.e.,
|
|
// we must enforce v_x/y/z = 0 for the velocity components.
|
|
const int bdr_attr_max = H1.GetMesh()->bdr_attributes.Max();
|
|
Array<int> ess_bdr(bdr_attr_max);
|
|
for (int c = 0; c < H1.GetMesh()->Dimension(); c++)
|
|
{
|
|
ess_bdr = 0;
|
|
ess_bdr[c] = 1;
|
|
H1c.GetEssentialTrueDofs(ess_bdr, c_tdofs[c]);
|
|
c_tdofs[c].Read();
|
|
}
|
|
|
|
Mv_blf.AddDomainIntegrator(new VectorMassIntegrator(rho0_coeff, &ir));
|
|
Mv_blf.Assemble();
|
|
Mv_blf.FormSystemMatrix(mfem::Array<int>(), Mv_mat);
|
|
|
|
Me_blf.AddDomainIntegrator(new MassIntegrator(rho0_coeff, &ir));
|
|
Me_blf.Assemble();
|
|
Me_blf.FormSystemMatrix(mfem::Array<int>(), Me_mat);
|
|
|
|
residual.reset(new LagrangianHydroResidualOperator(*this, X, fd_gradient,
|
|
dump_jacobians));
|
|
|
|
if (use_petsc)
|
|
{
|
|
#ifdef MFEM_USE_PETSC
|
|
snes = new PetscNonlinearSolver(MPI_COMM_WORLD);
|
|
snes->SetOperator(*residual);
|
|
snes->SetRelTol(nonlinear_relative_tolerance);
|
|
snes->SetMaxIter(nonlinear_maximum_iterations);
|
|
snes->SetJacobianType(Operator::PETSC_MATAIJ);
|
|
#else
|
|
MFEM_ABORT("MFEM is not built with PETSc");
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
preconditioner.reset(new Preconditioner(*this));
|
|
|
|
auto gmres = new GMRESSolver(MPI_COMM_WORLD);
|
|
gmres->SetKDim(500);
|
|
gmres->SetMaxIter(krylov_maximum_iterations);
|
|
gmres->SetRelTol(1e-12);
|
|
gmres->SetAbsTol(0.0);
|
|
//gmres->SetPrintLevel(IterativeSolver::PrintLevel().Summary());
|
|
gmres->SetPrintLevel(IterativeSolver::PrintLevel().None());
|
|
gmres->SetPreconditioner(*preconditioner);
|
|
krylov.reset(gmres);
|
|
|
|
newton.reset(new LineSearchNewtonSolver(MPI_COMM_WORLD, *this));
|
|
newton->SetPrintLevel(IterativeSolver::PrintLevel().Summary());
|
|
//newton->SetPrintLevel(IterativeSolver::PrintLevel().None());
|
|
newton->SetOperator(*residual);
|
|
newton->SetSolver(*krylov);
|
|
newton->SetMaxIter(nonlinear_maximum_iterations);
|
|
newton->SetRelTol(nonlinear_relative_tolerance);
|
|
newton->SetAbsTol(0.0);
|
|
// newton->SetAdaptiveLinRtol();
|
|
}
|
|
}
|
|
|
|
void Mult(const Vector &S, Vector &dSdt) const override
|
|
{
|
|
UpdateMesh(S);
|
|
UpdateQuadratureData(S);
|
|
|
|
auto sptr = const_cast<Vector*>(&S);
|
|
const int H1vsize = H1.GetVSize();
|
|
|
|
ParGridFunction x, v, e;
|
|
x.MakeRef(&H1, *sptr, 0);
|
|
v.MakeRef(&H1, *sptr, H1vsize);
|
|
e.MakeRef(&L2, *sptr, 2*H1vsize);
|
|
|
|
ParGridFunction dx, dv, de;
|
|
dx.MakeRef(&H1, dSdt, 0);
|
|
dv.MakeRef(&H1, dSdt, H1vsize);
|
|
de.MakeRef(&L2, dSdt, 2*H1vsize);
|
|
|
|
// solve position
|
|
dx = v;
|
|
|
|
// out << ">>> dx\n";
|
|
// pretty_print(dx);
|
|
|
|
// solve velocity
|
|
{
|
|
dv = 0.0;
|
|
|
|
H1.GetRestrictionMatrix()->Mult(v, Xv);
|
|
// momentum_mf->SetParameters({&rho0, &x0, &x, &material, &e});
|
|
// momentum_mf->Mult(Xv, RHSv);
|
|
momentum_pa->SetParameters({&qdata->stressp});
|
|
momentum_pa->Mult(Xv, RHSv);
|
|
RHSv.Neg();
|
|
H1.GetRestrictionMatrix()->MultTranspose(RHSv, rhsv);
|
|
|
|
// pretty_print(rhsv);
|
|
|
|
// solve for each velocity component
|
|
const int size = H1c.GetVSize();
|
|
const Operator *Pconf = H1c.GetProlongationMatrix();
|
|
for (int c = 0; c < H1.GetMesh()->Dimension(); c++)
|
|
{
|
|
dvc.MakeRef(&H1c, dSdt, H1vsize + c*size);
|
|
rhsvc.MakeRef(&H1c, rhsv, c*size);
|
|
if (Pconf)
|
|
{
|
|
Pconf->MultTranspose(rhsvc, B);
|
|
}
|
|
else
|
|
{
|
|
B = rhsvc;
|
|
}
|
|
|
|
CGSolver cg(H1c.GetParMesh()->GetComm());
|
|
cg.SetPreconditioner(*Mv_Jprec);
|
|
cg.SetOperator(*Mv);
|
|
cg.SetRelTol(1e-8);
|
|
cg.SetAbsTol(0.0);
|
|
cg.SetMaxIter(300);
|
|
cg.SetPrintLevel(-1);
|
|
|
|
H1c.GetRestrictionMatrix()->Mult(dvc, Xvc);
|
|
Mv->SetEssentialTrueDofs(c_tdofs[c]);
|
|
Mv->EliminateRHS(B);
|
|
cg.Mult(B, Xvc);
|
|
if (Pconf)
|
|
{
|
|
Pconf->Mult(Xvc, dvc);
|
|
}
|
|
else
|
|
{
|
|
dvc = Xvc;
|
|
}
|
|
dvc.GetMemory().SyncAlias(dSdt.GetMemory(), dvc.Size());
|
|
}
|
|
}
|
|
// out << ">>> dv\n";
|
|
// pretty_print(dv);
|
|
|
|
// solve energy
|
|
{
|
|
de = 0.0;
|
|
|
|
L2.GetRestrictionMatrix()->Mult(e, Xe);
|
|
// energy_conservation_mf->SetParameters({&v, &rho0, &x0, &x, &material});
|
|
// energy_conservation_mf->Mult(Xe, RHSe);
|
|
energy_conservation_pa->SetParameters({&v, &qdata->stressp});
|
|
energy_conservation_pa->Mult(Xe, RHSe);
|
|
L2.GetRestrictionMatrix()->MultTranspose(RHSe, rhse);
|
|
|
|
if (problem == 0)
|
|
{
|
|
LinearForm e_source(&L2);
|
|
e_source = 0.0;
|
|
L2.GetMesh()->DeleteGeometricFactors();
|
|
|
|
FunctionCoefficient coeff(taylor_source);
|
|
DomainLFIntegrator *d = new DomainLFIntegrator(coeff, &ir);
|
|
e_source.AddDomainIntegrator(d);
|
|
e_source.UseFastAssembly(true);
|
|
e_source.Assemble();
|
|
|
|
rhse += e_source;
|
|
}
|
|
|
|
CGSolver cg(L2.GetParMesh()->GetComm());
|
|
cg.SetOperator(*Me);
|
|
cg.iterative_mode = false;
|
|
cg.SetRelTol(1e-8);
|
|
cg.SetAbsTol(0.0);
|
|
cg.SetMaxIter(300);
|
|
cg.SetPrintLevel(-1);
|
|
cg.Mult(rhse, de);
|
|
de.GetMemory().SyncAlias(dSdt.GetMemory(), de.Size());
|
|
|
|
// out << ">>> de\n";
|
|
// pretty_print(de);
|
|
}
|
|
|
|
qdata_is_current = false;
|
|
|
|
// out << ">>> dSdt\n";
|
|
// pretty_print(dSdt);
|
|
}
|
|
|
|
void ImplicitSolve(const real_t dt, const Vector &x, Vector &k) override
|
|
{
|
|
auto xptr = const_cast<Vector*>(&x);
|
|
|
|
Vector xx, xv, xe;
|
|
xx.MakeRef(*xptr, 0, H1.GetVSize());
|
|
xv.MakeRef(*xptr, H1.GetVSize(), H1.GetVSize());
|
|
xe.MakeRef(*xptr, 2*H1.GetVSize(), L2.GetVSize());
|
|
|
|
Xx.MakeRef(X, 0, H1.GetTrueVSize());
|
|
Xv.MakeRef(X, H1.GetTrueVSize(), H1.GetTrueVSize());
|
|
Xe.MakeRef(X, 2*H1.GetTrueVSize(), L2.GetTrueVSize());
|
|
|
|
H1.GetRestrictionMatrix()->Mult(xx, Xx);
|
|
H1.GetRestrictionMatrix()->Mult(xv, Xv);
|
|
L2.GetRestrictionMatrix()->Mult(xe, Xe);
|
|
|
|
Xx.SyncAliasMemory(X);
|
|
Xv.SyncAliasMemory(X);
|
|
Xe.SyncAliasMemory(X);
|
|
|
|
residual->SetTimeStep(dt);
|
|
|
|
Vector zero;
|
|
K = X;
|
|
if (use_petsc)
|
|
{
|
|
#ifdef MFEM_USE_PETSC
|
|
// PETSc's SNES may cache internal state that depends on the operator.
|
|
// Recreate the SNES object periodically (and when the timestep
|
|
// changes) to avoid stale Jacobian/preconditioner state across
|
|
// time steps/stages, especially when users request Jacobian lagging
|
|
// through `-snes_lag_jacobian`.
|
|
if (current_dt != dt || snes == nullptr || petsc_lag >= petsc_lag_steps)
|
|
{
|
|
current_dt = dt;
|
|
petsc_lag = 0;
|
|
delete snes;
|
|
snes = new PetscNonlinearSolver(MPI_COMM_WORLD);
|
|
snes->SetOperator(*residual);
|
|
snes->SetRelTol(nonlinear_relative_tolerance);
|
|
snes->SetMaxIter(nonlinear_maximum_iterations);
|
|
snes->SetJacobianType(Operator::PETSC_MATAIJ);
|
|
}
|
|
petsc_lag++;
|
|
|
|
snes->Mult(zero, K);
|
|
newton_converged = (snes->GetConverged() > 0);
|
|
#else
|
|
MFEM_ABORT("MFEM is not built with PETSc");
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if (current_dt != dt || lag >= preconditioner_lag)
|
|
{
|
|
lag = 0;
|
|
current_dt = dt;
|
|
preconditioner->SetRebuildFlag(true);
|
|
}
|
|
lag++;
|
|
|
|
newton->Mult(zero, K);
|
|
|
|
// If some SDIRK stage didn't converge, keep it at false.
|
|
// (these are reset after every time step).
|
|
if (newton_converged)
|
|
{
|
|
newton_converged = newton->GetConverged();
|
|
}
|
|
newton_max_iter = std::max(newton_max_iter, newton->GetNumIterations());
|
|
}
|
|
|
|
Kx.MakeRef(K, 0, H1.GetTrueVSize());
|
|
Kv.MakeRef(K, H1.GetTrueVSize(), H1.GetTrueVSize());
|
|
Ke.MakeRef(K, 2*H1.GetTrueVSize(), L2.GetTrueVSize());
|
|
|
|
Vector kx, kv, ke;
|
|
kx.MakeRef(k, 0, H1.GetVSize());
|
|
kv.MakeRef(k, H1.GetVSize(), H1.GetVSize());
|
|
ke.MakeRef(k, 2*H1.GetVSize(), L2.GetVSize());
|
|
|
|
H1.GetProlongationMatrix()->Mult(Kx, kx);
|
|
H1.GetProlongationMatrix()->Mult(Kv, kv);
|
|
L2.GetProlongationMatrix()->Mult(Ke, ke);
|
|
}
|
|
|
|
void UpdateMesh(const Vector &S) const
|
|
{
|
|
Vector* sptr = const_cast<Vector*>(&S);
|
|
mesh_nodes.MakeRef(&H1, *sptr, 0);
|
|
H1.GetParMesh()->NewNodes(mesh_nodes, false);
|
|
}
|
|
|
|
void ResetTimeStepEstimate()
|
|
{
|
|
external_data[EXT_DATA_IDX::DT_ESTIMATE] =
|
|
std::numeric_limits<double>::infinity();
|
|
}
|
|
|
|
void ResetQuadratureData() const
|
|
{
|
|
qdata_is_current = false;
|
|
}
|
|
|
|
void ResetNewtonInfo()
|
|
{
|
|
newton_converged = true;
|
|
newton_max_iter = -1;
|
|
}
|
|
|
|
real_t GetTimeStepEstimate(const Vector &S)
|
|
{
|
|
MFEM_VERIFY(dtest_mf, "dt estimate operator (dtest_mf) is null");
|
|
MFEM_VERIFY(qdata, "quadrature data (qdata) is null");
|
|
|
|
UpdateMesh(S);
|
|
UpdateQuadratureData(S);
|
|
|
|
auto sptr = const_cast<Vector*>(&S);
|
|
const int H1vsize = H1.GetVSize();
|
|
ParGridFunction x, v, e;
|
|
x.MakeRef(&H1, *sptr, 0);
|
|
v.MakeRef(&H1, *sptr, H1vsize);
|
|
e.MakeRef(&L2, *sptr, 2*H1vsize);
|
|
|
|
dtest_mf->SetParameters({&v, &rho0, &x0, &x, &material, &e});
|
|
dtest_mf->Mult(qdata->dt_est, qdata->dt_est);
|
|
|
|
real_t dt_est_local = qdata->dt_est.Min();
|
|
|
|
real_t dt_est_global;
|
|
MPI_Allreduce(&dt_est_local, &dt_est_global, 1, MPITypeMap<real_t>::mpi_type,
|
|
MPI_MIN,
|
|
L2.GetComm());
|
|
|
|
return dt_est_global;
|
|
}
|
|
|
|
real_t InternalEnergy(ParGridFunction &e)
|
|
{
|
|
const auto mt = Device::GetDeviceMemoryType();
|
|
Vector E(L2.GetTrueVSize(), mt), Y(L2.GetTrueVSize(), mt);
|
|
total_internal_energy_mf->SetParameters({&rho0, &x0});
|
|
L2.GetRestrictionMatrix()->Mult(e, E);
|
|
total_internal_energy_mf->Mult(E, Y);
|
|
const real_t ie_local = Y.Sum();
|
|
real_t ie_global = 0.0;
|
|
MPI_Allreduce(&ie_local, &ie_global, 1, MPI_DOUBLE, MPI_SUM,
|
|
L2.GetParMesh()->GetComm());
|
|
return ie_global;
|
|
}
|
|
|
|
real_t KineticEnergy(ParGridFunction &v)
|
|
{
|
|
const auto mt = Device::GetDeviceMemoryType();
|
|
Vector V(H1.GetTrueVSize(), mt), Y(L2.GetTrueVSize(), mt);
|
|
total_kinetic_energy_mf->SetParameters({&rho0, &x0});
|
|
H1.GetRestrictionMatrix()->Mult(v, V);
|
|
total_kinetic_energy_mf->Mult(V, Y);
|
|
const real_t ke_local = Y.Sum();
|
|
real_t ke_global = 0.0;
|
|
MPI_Allreduce(&ke_local, &ke_global, 1, MPI_DOUBLE, MPI_SUM,
|
|
H1.GetParMesh()->GetComm());
|
|
return ke_global;
|
|
}
|
|
|
|
void ComputeDensity(ParGridFunction &rho)
|
|
{
|
|
rho.SetSpace(&L2);
|
|
|
|
ParGridFunction rhs_l(&L2);
|
|
|
|
Vector rho0_t(L2.GetTrueVSize()),
|
|
rho_t(L2.GetTrueVSize()),
|
|
rhs(L2.GetTrueVSize());
|
|
|
|
const int l2dofs_cnt = L2.GetFE(0)->GetDof();
|
|
DenseMatrix Mrho(l2dofs_cnt);
|
|
DenseMatrixInverse inv(&Mrho);
|
|
Vector rhs_e(l2dofs_cnt), rho_z(l2dofs_cnt);
|
|
Array<int> dofs(l2dofs_cnt);
|
|
MassIntegrator mi(&ir);
|
|
|
|
density_mf->SetParameters({&x0});
|
|
L2.GetProlongationMatrix()->MultTranspose(rho0, rho0_t);
|
|
density_mf->Mult(rho0_t, rhs);
|
|
L2.GetProlongationMatrix()->Mult(rhs, rhs_l);
|
|
|
|
for (int e = 0; e < L2.GetParMesh()->GetNE(); e++)
|
|
{
|
|
const FiniteElement &fe = *L2.GetFE(e);
|
|
ElementTransformation &eltr = *L2.GetElementTransformation(e);
|
|
L2.GetElementDofs(e, dofs);
|
|
mi.AssembleElementMatrix(fe, eltr, Mrho);
|
|
inv.Factor();
|
|
rhs_l.GetElementDofValues(e, rhs_e);
|
|
inv.Mult(rhs_e, rho_z);
|
|
rho.SetSubVector(dofs, rho_z);
|
|
}
|
|
}
|
|
|
|
void DensityScatter()
|
|
{
|
|
Mesh m0 = H1.GetParMesh()->GetSerialMesh(0);
|
|
GridFunction r0 = rho0.GetSerialGridFunction(0, m0);
|
|
|
|
if (Mpi::WorldRank() != 0) { return; }
|
|
|
|
std::ofstream fstream_rho;
|
|
fstream_rho.open("rho.out");
|
|
fstream_rho.precision(8);
|
|
|
|
const int nqp = ir.GetNPoints();
|
|
Vector pos(2);
|
|
const int NE = m0.GetNE();
|
|
for (int e = 0; e < NE; e++)
|
|
{
|
|
ElementTransformation &Tr = *m0.GetElementTransformation(e);
|
|
|
|
for (int q = 0; q < nqp; q++)
|
|
{
|
|
const IntegrationPoint &ip = ir.IntPoint(q);
|
|
Tr.SetIntPoint(&ip);
|
|
Tr.Transform(ip, pos);
|
|
|
|
double detJ = Tr.Weight();
|
|
// rho0 * detJ0 / detJ.
|
|
double rho = r0.GetValue(Tr, ip) * (1.0 / NE) / detJ;
|
|
double r = sqrt(pos(0) * pos(0) + pos(1) * pos(1));
|
|
|
|
fstream_rho << r << " " << rho << "\n";
|
|
}
|
|
}
|
|
|
|
fstream_rho.close();
|
|
}
|
|
|
|
real_t ComputeMinDet(const Vector &S) const
|
|
{
|
|
auto sptr = const_cast<Vector*>(&S);
|
|
Vector x, x_loc(H1.GetVSize());
|
|
x.MakeRef(*sptr, 0, H1.GetTrueVSize());
|
|
H1.GetProlongationMatrix()->Mult(x, x_loc);
|
|
|
|
ParGridFunction xgf;
|
|
xgf.MakeRef(&H1, x_loc, 0);
|
|
|
|
auto g = GeometricFactors(xgf, ir, GeometricFactors::JACOBIANS |
|
|
GeometricFactors::DETERMINANTS);
|
|
auto min = g.detJ.Min();
|
|
MPI_Allreduce(MPI_IN_PLACE, &min, 1, MPITypeMap<real_t>::mpi_type,
|
|
MPI_MIN, L2.GetComm());
|
|
|
|
return min;
|
|
}
|
|
|
|
void UpdateQuadratureData(const Vector &S) const
|
|
{
|
|
if (qdata_is_current) { return; }
|
|
// out << "updating qdata\n";
|
|
qdata_is_current = true;
|
|
|
|
auto sptr = const_cast<Vector*>(&S);
|
|
const int H1vsize = H1.GetVSize();
|
|
ParGridFunction x, v, e;
|
|
x.MakeRef(&H1, *sptr, 0);
|
|
v.MakeRef(&H1, *sptr, H1vsize);
|
|
e.MakeRef(&L2, *sptr, 2*H1vsize);
|
|
update_qdata->SetParameters({&v, &rho0, &x0, &x, &material, &e});
|
|
update_qdata->Mult(qdata->stressp, qdata->stressp);
|
|
}
|
|
|
|
virtual MemoryClass GetMemoryClass() const override
|
|
{
|
|
return Device::GetDeviceMemoryClass();
|
|
}
|
|
|
|
~LagrangianHydroOperator()
|
|
{
|
|
#ifdef MFEM_USE_PETSC
|
|
delete snes;
|
|
#endif
|
|
delete Mv;
|
|
delete Mv_Jprec;
|
|
delete Me;
|
|
}
|
|
|
|
ParFiniteElementSpace &H1;
|
|
ParFiniteElementSpace &L2;
|
|
mutable ParFiniteElementSpace H1c;
|
|
Array<int> &ess_tdof;
|
|
mutable Array<int> c_tdofs[3];
|
|
const IntegrationRule &ir;
|
|
ParGridFunction &x0;
|
|
ParGridFunction &rho0;
|
|
ParGridFunction &material;
|
|
std::shared_ptr<DifferentiableOperator> update_qdata;
|
|
std::shared_ptr<DifferentiableOperator> dtest_mf;
|
|
std::shared_ptr<DifferentiableOperator> momentum_mf;
|
|
std::shared_ptr<DifferentiableOperator> momentum_pa;
|
|
std::shared_ptr<DifferentiableOperator> energy_conservation_mf;
|
|
std::shared_ptr<DifferentiableOperator> energy_conservation_pa;
|
|
std::shared_ptr<DifferentiableOperator> total_internal_energy_mf;
|
|
std::shared_ptr<DifferentiableOperator> total_kinetic_energy_mf;
|
|
std::shared_ptr<DifferentiableOperator> density_mf;
|
|
std::shared_ptr<DifferentiableOperator> taylor_source_mf;
|
|
std::shared_ptr<QuadratureData> qdata;
|
|
mutable ParGridFunction mesh_nodes, rhsvc, dvc;
|
|
mutable MassPAOperator *Mv = nullptr, *Me = nullptr;
|
|
ParBilinearForm Mv_blf, Me_blf;
|
|
HypreParMatrix Mv_mat, Me_mat;
|
|
|
|
std::shared_ptr<LagrangianHydroResidualOperator> residual;
|
|
std::shared_ptr<Preconditioner> preconditioner;
|
|
std::shared_ptr<Solver> krylov;
|
|
std::shared_ptr<NewtonSolver> newton;
|
|
#ifdef MFEM_USE_PETSC
|
|
PetscNonlinearSolver *snes = nullptr;
|
|
#endif
|
|
real_t current_dt = 0.0;
|
|
int petsc_lag = 0;
|
|
int lag = 0;
|
|
|
|
mutable FunctionCoefficient rho0_coeff;
|
|
OperatorJacobiSmoother *Mv_Jprec = nullptr;
|
|
mutable Vector RHSv, rhsv, X, Xx, Xv, Xvc, Xe, K, Kx, Kv, Ke, B, RHSe, rhse;
|
|
const int nl2dofs;
|
|
bool fd_gradient;
|
|
int dump_jacobians;
|
|
const int nonlinear_maximum_iterations;
|
|
const real_t nonlinear_relative_tolerance;
|
|
const int krylov_maximum_iterations;
|
|
const int preconditioner_lag;
|
|
const PRECONDITIONER_TYPE preconditioner_type;
|
|
bool newton_converged = true;
|
|
int newton_max_iter = -1;
|
|
Vector &external_data;
|
|
const bool use_petsc;
|
|
mutable bool qdata_is_current = false;
|
|
};
|
|
|
|
template <int DIM>
|
|
static auto CreateLagrangianHydroOperator(
|
|
ParFiniteElementSpace &H1,
|
|
ParFiniteElementSpace &L2,
|
|
Array<int> &ess_tdof,
|
|
FunctionCoefficient &rho0_coeff,
|
|
ParGridFunction &x0_gf,
|
|
ParGridFunction &rho0_gf,
|
|
ParGridFunction &material_gf,
|
|
Vector &external_data,
|
|
const IntegrationRule &ir,
|
|
const bool &fd_gradient,
|
|
const int &dump_jacobians,
|
|
const int &nonlinear_maximum_iterations,
|
|
const real_t &nonlinear_relative_tolerance,
|
|
const int &krylov_maximum_iterations,
|
|
const int &preconditioner_lag,
|
|
const PRECONDITIONER_TYPE &preconditioner_type,
|
|
const bool use_petsc)
|
|
{
|
|
ParMesh &mesh = *H1.GetParMesh();
|
|
|
|
auto qdata = std::make_shared<QuadratureData>(mesh, ir);
|
|
|
|
int ne_loc = mesh.GetNE(), ne_global = 0;
|
|
real_t vol_loc = 0.0, vol_global = 0.0;
|
|
for (int e = 0; e < mesh.GetNE(); e++)
|
|
{
|
|
vol_loc += mesh.GetElementVolume(e);
|
|
}
|
|
MPI_Allreduce(&vol_loc, &vol_global, 1, MPI_DOUBLE, MPI_SUM, mesh.GetComm());
|
|
MPI_Allreduce(&ne_loc, &ne_global, 1, MPI_INT, MPI_SUM, mesh.GetComm());
|
|
|
|
real_t &h0 = external_data[EXT_DATA_IDX::H0];
|
|
switch (mesh.GetElementBaseGeometry(0))
|
|
{
|
|
case Geometry::SEGMENT: h0 = vol_global / ne_global; break;
|
|
case Geometry::SQUARE: h0 = sqrt(vol_global / ne_global); break;
|
|
case Geometry::TRIANGLE: h0 = sqrt(2.0 * vol_global / ne_global); break;
|
|
case Geometry::CUBE: h0 = pow(vol_global / ne_global, 1./3.); break;
|
|
case Geometry::TETRAHEDRON: h0 = pow(6.0 * vol_global / ne_global,
|
|
1./3.); break;
|
|
default: MFEM_ABORT("Unknown zone type!");
|
|
}
|
|
h0 /= (double) H1.GetOrder(0);
|
|
|
|
// const real_t h0 = sqrt(vol_global / ne_global) /
|
|
// static_cast<real_t>(H1.GetOrder(0));
|
|
|
|
// qdata->order_v = order_v;
|
|
qdata->dt_est = std::numeric_limits<real_t>::infinity();
|
|
|
|
auto d_external_data = external_data.ReadWrite();
|
|
|
|
Array<int> all_domain_attr(mesh.attributes.Max());
|
|
all_domain_attr = 1;
|
|
|
|
// Contains a ParametricSpace.
|
|
std::shared_ptr<DifferentiableOperator> dt_est_mf;
|
|
{
|
|
mfem::tuple dt_est_kernel_ao =
|
|
{
|
|
Gradient<VELOCITY>{},
|
|
Value<DENSITY0>{},
|
|
Gradient<COORDINATES0>{},
|
|
Gradient<COORDINATES>{},
|
|
Value<MATERIAL>{},
|
|
Value<SPECIFIC_INTERNAL_ENERGY>{},
|
|
Weight{}
|
|
};
|
|
|
|
mfem::tuple dt_est_kernel_oo = {None<DT_EST>{}};
|
|
|
|
std::vector dt_est_solutions =
|
|
{
|
|
FieldDescriptor{DT_EST, &qdata->R}
|
|
};
|
|
|
|
std::vector dt_est_parameters =
|
|
{
|
|
FieldDescriptor{VELOCITY, &H1},
|
|
FieldDescriptor{DENSITY0, &L2},
|
|
FieldDescriptor{COORDINATES0, &H1},
|
|
FieldDescriptor{COORDINATES, &H1},
|
|
FieldDescriptor{MATERIAL, material_gf.ParFESpace()},
|
|
FieldDescriptor{SPECIFIC_INTERNAL_ENERGY, &L2},
|
|
};
|
|
|
|
dt_est_mf = std::make_shared<DifferentiableOperator>(
|
|
dt_est_solutions, dt_est_parameters, mesh);
|
|
TimeStepEstimateQFunction<DIM> dt_est_qf(d_external_data);
|
|
dt_est_mf->AddDomainIntegrator(dt_est_qf, dt_est_kernel_ao,
|
|
dt_est_kernel_oo,
|
|
ir,
|
|
all_domain_attr);
|
|
}
|
|
|
|
// Contains a ParametricSpace.
|
|
std::shared_ptr<DifferentiableOperator> update_qdata;
|
|
{
|
|
mfem::tuple update_qdata_kernel_ao =
|
|
{
|
|
Gradient<VELOCITY>{},
|
|
Value<DENSITY0>{},
|
|
Gradient<COORDINATES0>{},
|
|
Gradient<COORDINATES>{},
|
|
Value<MATERIAL>{},
|
|
Value<SPECIFIC_INTERNAL_ENERGY>{},
|
|
Weight{}
|
|
};
|
|
|
|
mfem::tuple update_qdata_kernel_oo = {None<STRESS_TENSOR>{}};
|
|
|
|
std::vector<FieldDescriptor> update_qdata_solutions =
|
|
{
|
|
{STRESS_TENSOR, &qdata->StressSpace}
|
|
};
|
|
|
|
std::vector<FieldDescriptor> update_qdata_parameters =
|
|
{
|
|
{VELOCITY, &H1},
|
|
{DENSITY0, &L2},
|
|
{COORDINATES0, &H1},
|
|
{COORDINATES, &H1},
|
|
{MATERIAL, material_gf.ParFESpace()},
|
|
{SPECIFIC_INTERNAL_ENERGY, &L2},
|
|
};
|
|
|
|
update_qdata = std::make_shared<DifferentiableOperator>(
|
|
update_qdata_solutions, update_qdata_parameters, mesh);
|
|
UpdateQuadratureDataQFunction<DIM> update_qdata_qf(d_external_data);
|
|
update_qdata->AddDomainIntegrator(update_qdata_qf, update_qdata_kernel_ao,
|
|
update_qdata_kernel_oo,
|
|
ir,
|
|
all_domain_attr);
|
|
}
|
|
|
|
// Create momentum operator
|
|
std::shared_ptr<DifferentiableOperator> momentum_mf;
|
|
{
|
|
mfem::tuple momentum_mf_kernel_ao =
|
|
{
|
|
Gradient<VELOCITY>{},
|
|
Value<DENSITY0>{},
|
|
Gradient<COORDINATES0>{},
|
|
Gradient<COORDINATES>{},
|
|
Value<MATERIAL>{},
|
|
Value<SPECIFIC_INTERNAL_ENERGY>{},
|
|
Weight{}
|
|
};
|
|
|
|
mfem::tuple momentum_mf_kernel_oo = {Gradient<VELOCITY>{}};
|
|
|
|
// <sigma, grad(w) * J^-T> * det(J) * weights
|
|
// <sigma(J^-T det(J) weights), grad(w)>
|
|
|
|
std::vector momentum_mf_solutions =
|
|
{
|
|
FieldDescriptor{VELOCITY, &H1}
|
|
};
|
|
|
|
std::vector momentum_mf_parameters =
|
|
{
|
|
FieldDescriptor{DENSITY0, &L2},
|
|
FieldDescriptor{COORDINATES0, &H1},
|
|
FieldDescriptor{COORDINATES, &H1},
|
|
FieldDescriptor{MATERIAL, material_gf.ParFESpace()},
|
|
FieldDescriptor{SPECIFIC_INTERNAL_ENERGY, &L2},
|
|
};
|
|
|
|
momentum_mf = std::make_shared<DifferentiableOperator>(
|
|
momentum_mf_solutions, momentum_mf_parameters, mesh);
|
|
|
|
MomentumQFunction<DIM> momentum_qf(d_external_data);
|
|
auto derivatives =
|
|
std::integer_sequence<size_t, VELOCITY, COORDINATES, SPECIFIC_INTERNAL_ENERGY> {};
|
|
momentum_mf->AddDomainIntegrator(momentum_qf, momentum_mf_kernel_ao,
|
|
momentum_mf_kernel_oo, ir, all_domain_attr, derivatives);
|
|
}
|
|
|
|
std::shared_ptr<DifferentiableOperator> momentum_pa;
|
|
{
|
|
mfem::tuple momentum_pa_kernel_ao = {None<STRESS_TENSOR>{}};
|
|
mfem::tuple momentum_pa_kernel_oo = {Gradient<VELOCITY>{}};
|
|
|
|
std::vector<FieldDescriptor> momentum_pa_solutions = {{VELOCITY, &H1}};
|
|
std::vector<FieldDescriptor> momentum_pa_parameters = {{STRESS_TENSOR, &qdata->StressSpace}};
|
|
|
|
momentum_pa = std::make_shared<DifferentiableOperator>(
|
|
momentum_pa_solutions, momentum_pa_parameters, mesh);
|
|
|
|
MomentumPAQFunction<DIM> momentum_pa_qf;
|
|
momentum_pa->AddDomainIntegrator(momentum_pa_qf, momentum_pa_kernel_ao,
|
|
momentum_pa_kernel_oo, ir, all_domain_attr);
|
|
}
|
|
|
|
// Create energy conservation operator
|
|
std::shared_ptr<DifferentiableOperator> energy_conservation_mf;
|
|
{
|
|
mfem::tuple energy_conservation_mf_kernel_ao =
|
|
{
|
|
Gradient<VELOCITY>{},
|
|
Value<DENSITY0>{},
|
|
Gradient<COORDINATES0>{},
|
|
Gradient<COORDINATES>{},
|
|
Value<MATERIAL>{},
|
|
Value<SPECIFIC_INTERNAL_ENERGY>{},
|
|
Weight{}
|
|
};
|
|
|
|
mfem::tuple energy_conservation_mf_kernel_oo = {Value<SPECIFIC_INTERNAL_ENERGY>{}};
|
|
|
|
// <sigma, grad(v) * inv(J) * phi> * det(J) * w
|
|
// <sigma(J^-T det(J) w), grad(v) * inv(J)>
|
|
|
|
std::vector energy_conservation_mf_solutions =
|
|
{
|
|
FieldDescriptor{SPECIFIC_INTERNAL_ENERGY, &L2}
|
|
};
|
|
|
|
std::vector energy_conservation_mf_parameters =
|
|
{
|
|
FieldDescriptor{VELOCITY, &H1},
|
|
FieldDescriptor{DENSITY0, &L2},
|
|
FieldDescriptor{COORDINATES0, &H1},
|
|
FieldDescriptor{COORDINATES, &H1},
|
|
FieldDescriptor{MATERIAL, material_gf.ParFESpace()},
|
|
};
|
|
|
|
energy_conservation_mf =
|
|
std::make_shared<DifferentiableOperator>(
|
|
energy_conservation_mf_solutions, energy_conservation_mf_parameters, mesh);
|
|
|
|
EnergyConservationQFunction<DIM> energy_conservation_qf(d_external_data);
|
|
auto derivatives =
|
|
std::integer_sequence<size_t, VELOCITY, COORDINATES, SPECIFIC_INTERNAL_ENERGY> {};
|
|
energy_conservation_mf->AddDomainIntegrator(
|
|
energy_conservation_qf, energy_conservation_mf_kernel_ao,
|
|
energy_conservation_mf_kernel_oo, ir, all_domain_attr, derivatives);
|
|
}
|
|
|
|
std::shared_ptr<DifferentiableOperator> energy_conservation_pa;
|
|
{
|
|
mfem::tuple energy_conservation_pa_kernel_ao = {Gradient<VELOCITY>{}, None<STRESS_TENSOR>{}};
|
|
mfem::tuple energy_conservation_pa_kernel_oo = {Value<SPECIFIC_INTERNAL_ENERGY>{}};
|
|
|
|
std::vector<FieldDescriptor> energy_conservation_pa_solutions =
|
|
{
|
|
{SPECIFIC_INTERNAL_ENERGY, &L2}
|
|
};
|
|
|
|
std::vector<FieldDescriptor> energy_conservation_pa_parameters =
|
|
{
|
|
{VELOCITY, &H1},
|
|
{STRESS_TENSOR, &qdata->StressSpace}
|
|
};
|
|
|
|
energy_conservation_pa =
|
|
std::make_shared<DifferentiableOperator>(
|
|
energy_conservation_pa_solutions, energy_conservation_pa_parameters, mesh);
|
|
|
|
EnergyConservationPAQFunction<DIM> energy_conservation_pa_qf;
|
|
energy_conservation_pa->AddDomainIntegrator(
|
|
energy_conservation_pa_qf, energy_conservation_pa_kernel_ao,
|
|
energy_conservation_pa_kernel_oo, ir, all_domain_attr);
|
|
}
|
|
|
|
// Create total internal energy operator
|
|
std::shared_ptr<DifferentiableOperator> total_internal_energy_mf;
|
|
{
|
|
mfem::tuple total_internal_energy_kernel_ao =
|
|
{
|
|
Value<SPECIFIC_INTERNAL_ENERGY>{},
|
|
Value<DENSITY0>{},
|
|
Gradient<COORDINATES0>{},
|
|
Weight{}
|
|
};
|
|
|
|
mfem::tuple total_internal_energy_kernel_oo = {Value<SPECIFIC_INTERNAL_ENERGY>{}};
|
|
|
|
std::vector total_internal_energy_solutions =
|
|
{
|
|
FieldDescriptor{SPECIFIC_INTERNAL_ENERGY, &L2}
|
|
};
|
|
|
|
std::vector total_internal_energy_parameters =
|
|
{
|
|
FieldDescriptor{DENSITY0, &L2},
|
|
FieldDescriptor{COORDINATES0, &H1}
|
|
};
|
|
|
|
total_internal_energy_mf =
|
|
std::make_shared<DifferentiableOperator>(
|
|
total_internal_energy_solutions,
|
|
total_internal_energy_parameters,
|
|
mesh);
|
|
|
|
TotalInternalEnergyQFunction<DIM> total_internal_energy_qf;
|
|
total_internal_energy_mf->AddDomainIntegrator(
|
|
total_internal_energy_qf, total_internal_energy_kernel_ao,
|
|
total_internal_energy_kernel_oo, ir, all_domain_attr);
|
|
}
|
|
|
|
// Create total kinetic energy operator
|
|
std::shared_ptr<DifferentiableOperator> total_kinetic_energy_mf;
|
|
{
|
|
mfem::tuple total_kinetic_energy_kernel_ao =
|
|
{
|
|
Value<VELOCITY>{},
|
|
Value<DENSITY0>{},
|
|
Gradient<COORDINATES0>{},
|
|
Weight{}
|
|
};
|
|
|
|
mfem::tuple total_kinetic_energy_kernel_oo = {Value<DENSITY0>{}};
|
|
|
|
std::vector total_kinetic_energy_solutions =
|
|
{
|
|
FieldDescriptor{VELOCITY, &H1}
|
|
};
|
|
|
|
std::vector total_kinetic_energy_parameters =
|
|
{
|
|
FieldDescriptor{DENSITY0, &L2},
|
|
FieldDescriptor{COORDINATES0, &H1}
|
|
};
|
|
|
|
total_kinetic_energy_mf =
|
|
std::make_shared<DifferentiableOperator>(
|
|
total_kinetic_energy_solutions,
|
|
total_kinetic_energy_parameters, mesh);
|
|
TotalKineticEnergyQFunction<DIM> total_kinetic_energy_qf;
|
|
total_kinetic_energy_mf->AddDomainIntegrator(
|
|
total_kinetic_energy_qf, total_kinetic_energy_kernel_ao,
|
|
total_kinetic_energy_kernel_oo, ir, all_domain_attr);
|
|
}
|
|
|
|
// Create density operator
|
|
std::shared_ptr<DifferentiableOperator> density_mf;
|
|
{
|
|
mfem::tuple density_kernel_ao =
|
|
{
|
|
Value<DENSITY0>{},
|
|
Gradient<COORDINATES0>{},
|
|
Weight{}
|
|
};
|
|
|
|
mfem::tuple density_kernel_oo = {Value<DENSITY0>{}};
|
|
|
|
std::vector density_solutions =
|
|
{
|
|
FieldDescriptor{DENSITY0, &L2}
|
|
};
|
|
|
|
std::vector density_parameters =
|
|
{
|
|
FieldDescriptor{COORDINATES0, &H1}
|
|
};
|
|
|
|
density_mf = std::make_shared<DifferentiableOperator>(
|
|
density_solutions, density_parameters, mesh);
|
|
|
|
DensityQFunction<DIM> density_qf;
|
|
density_mf->AddDomainIntegrator(density_qf, density_kernel_ao,
|
|
density_kernel_oo, ir, all_domain_attr);
|
|
}
|
|
|
|
// Create taylor source oeprator
|
|
std::shared_ptr<DifferentiableOperator> taylor_source_mf;
|
|
{
|
|
mfem::tuple taylor_source_kernel_ao =
|
|
{
|
|
Value<COORDINATES>{},
|
|
Gradient<COORDINATES>{},
|
|
Weight{}
|
|
};
|
|
|
|
mfem::tuple taylor_source_kernel_oo = {Value<SPECIFIC_INTERNAL_ENERGY>{}};
|
|
|
|
std::vector taylor_source_solutions =
|
|
{
|
|
FieldDescriptor{SPECIFIC_INTERNAL_ENERGY, &L2}
|
|
};
|
|
|
|
std::vector taylor_source_parameters =
|
|
{
|
|
FieldDescriptor{COORDINATES, &H1}
|
|
};
|
|
|
|
taylor_source_mf = std::make_shared<DifferentiableOperator>(
|
|
taylor_source_solutions, taylor_source_parameters, mesh);
|
|
|
|
TaylorSourceQFunction<DIM> taylor_source_qf;
|
|
auto derivatives = std::integer_sequence<size_t, COORDINATES> {};
|
|
taylor_source_mf->AddDomainIntegrator(taylor_source_qf, taylor_source_kernel_ao,
|
|
taylor_source_kernel_oo, ir, all_domain_attr,
|
|
derivatives);
|
|
}
|
|
|
|
return new LagrangianHydroOperator(
|
|
H1,
|
|
L2,
|
|
ess_tdof,
|
|
ir,
|
|
rho0_coeff,
|
|
x0_gf,
|
|
rho0_gf,
|
|
material_gf,
|
|
update_qdata,
|
|
dt_est_mf,
|
|
momentum_mf,
|
|
momentum_pa,
|
|
energy_conservation_mf,
|
|
energy_conservation_pa,
|
|
total_internal_energy_mf,
|
|
total_kinetic_energy_mf,
|
|
density_mf,
|
|
taylor_source_mf,
|
|
qdata,
|
|
fd_gradient,
|
|
dump_jacobians,
|
|
nonlinear_maximum_iterations,
|
|
nonlinear_relative_tolerance,
|
|
krylov_maximum_iterations,
|
|
preconditioner_lag,
|
|
preconditioner_type,
|
|
external_data,
|
|
use_petsc);
|
|
}
|
|
|
|
|
|
void VisualizeField(socketstream &sock, const char *vishost, int visport,
|
|
ParGridFunction &gf, const char *title,
|
|
int x, int y, int w, int h, bool vec = false)
|
|
{
|
|
gf.HostRead();
|
|
ParMesh &pmesh = *gf.ParFESpace()->GetParMesh();
|
|
MPI_Comm comm = pmesh.GetComm();
|
|
|
|
int num_procs, myid;
|
|
MPI_Comm_size(comm, &num_procs);
|
|
MPI_Comm_rank(comm, &myid);
|
|
|
|
bool newly_opened = false;
|
|
int connection_failed;
|
|
|
|
do
|
|
{
|
|
if (myid == 0)
|
|
{
|
|
if (!sock.is_open() || !sock)
|
|
{
|
|
sock.open(vishost, visport);
|
|
sock.precision(8);
|
|
newly_opened = true;
|
|
}
|
|
sock << "solution\n";
|
|
}
|
|
|
|
pmesh.PrintAsOne(sock);
|
|
gf.SaveAsOne(sock);
|
|
|
|
if (myid == 0 && newly_opened)
|
|
{
|
|
const char* keys = (gf.FESpace()->GetMesh()->Dimension() == 2)
|
|
? "mAcRjl" : "mmaaAcl";
|
|
|
|
sock << "window_title '" << title << "'\n"
|
|
<< "window_geometry "
|
|
<< x << " " << y << " " << w << " " << h << "\n"
|
|
<< "keys " << keys;
|
|
if ( vec ) { sock << "vvv"; }
|
|
sock << std::endl;
|
|
}
|
|
|
|
if (myid == 0)
|
|
{
|
|
connection_failed = !sock && !newly_opened;
|
|
}
|
|
MPI_Bcast(&connection_failed, 1, MPI_INT, 0, comm);
|
|
}
|
|
while (connection_failed);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
Mpi::Init();
|
|
Hypre::Init();
|
|
|
|
out << std::setprecision(15);
|
|
|
|
const char *device_config = "cpu";
|
|
|
|
const char *mesh_file = "./rectangle01_quad.mesh";
|
|
|
|
int dim = 2;
|
|
int refinements = 0;
|
|
int order_v = 2;
|
|
int order_e = 1;
|
|
int order_q = -1;
|
|
real_t t_final = 0.0;
|
|
real_t blast_position[] = {0.0, 0.0, 0.0};
|
|
int ode_solver_type = 4;
|
|
bool fd_gradient = false;
|
|
bool use_viscosity = false;
|
|
real_t cfl = 0.5;
|
|
real_t nonlinear_relative_tolerance = 1e-5;
|
|
int nonlinear_maximum_iterations = 10;
|
|
int krylov_maximum_iterations = 10;
|
|
int preconditioner_lag = 0;
|
|
int vis_steps = 1;
|
|
bool glvis = false;
|
|
bool paraview = false;
|
|
int viscosity_type = 2;
|
|
real_t viscosity_q1 = 0.5;
|
|
real_t viscosity_q2 = 1.0;
|
|
int preconditioner_type = PRECONDITIONER_TYPE::BLOCK_DIAGONAL_AMG;
|
|
int dump_jacobians = 0;
|
|
int nretry = 100;
|
|
const char *petsc_opts = "";
|
|
bool petsc_legacy_dt = false;
|
|
bool petsc_opts_provided = false;
|
|
bool mesh_provided = false;
|
|
for (int i = 1; i < argc; i++)
|
|
{
|
|
if (strcmp(argv[i], "-petsc-opts") == 0 || strcmp(argv[i], "--petsc-opts") == 0)
|
|
{
|
|
petsc_opts_provided = true;
|
|
break;
|
|
}
|
|
}
|
|
for (int i = 1; i < argc; i++)
|
|
{
|
|
if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--mesh") == 0)
|
|
{
|
|
mesh_provided = true;
|
|
break;
|
|
}
|
|
}
|
|
if (petsc_opts_provided && !mesh_provided)
|
|
{
|
|
// Match the old PETSc prototype default mesh.
|
|
mesh_file = "../../data/inline-quad.mesh";
|
|
}
|
|
|
|
OptionsParser args(argc, argv);
|
|
args.AddOption(&dim, "-dim", "--dimension", "Dimension of the problem.");
|
|
args.AddOption(&mesh_file, "-m", "--mesh",
|
|
"Mesh file to use.");
|
|
args.AddOption(&refinements, "-rs", "--ref", "");
|
|
args.AddOption(&order_v, "-ov", "--ov", "");
|
|
args.AddOption(&order_e, "-oe", "--oe", "");
|
|
args.AddOption(&order_q, "-oq", "--oq", "");
|
|
args.AddOption(&t_final, "-tf", "--tf", "");
|
|
args.AddOption(&problem, "-p", "--p", "");
|
|
args.AddOption(&cfl, "-cfl", "--cfl", "");
|
|
args.AddOption(&device_config, "-d", "--device",
|
|
"Device configuration string, see Device::Configure().");
|
|
args.AddOption(&use_viscosity, "-av", "--av", "-no-av", "--no-av", "");
|
|
args.AddOption(&fd_gradient, "-fd", "--fd", "-no-fd", "--no-fd", "");
|
|
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
|
"ODE solver: 1 - Forward Euler,\n\t"
|
|
" 2 - RK2 SSP, 3 - RK3 SSP, 4 - RK4, 6 - RK6,\n\t"
|
|
" 7 - RK2Avg."
|
|
" 11 - Backward Euler"
|
|
" 12 - Implicit Midpoint"
|
|
" 13 - SDIRK33Solver");
|
|
args.AddOption(&nonlinear_maximum_iterations, "-nmi", "--nmi",
|
|
"Maximum number of nonlinear iterations.");
|
|
args.AddOption(&nonlinear_relative_tolerance, "-nrt", "--nrt",
|
|
"Nonlinear relative tolerance.");
|
|
args.AddOption(&krylov_maximum_iterations, "-kmi", "--kmi",
|
|
"Maximum number of Krylov iterations.");
|
|
args.AddOption(&preconditioner_lag, "-pl", "--pl",
|
|
"Number of nonlinear solves to wait before updating the preconditioner.");
|
|
args.AddOption(&preconditioner_type, "-pt", "--pt",
|
|
"Preconditioner type: 0 - SuperLU_DIST, 1 - Block Diagonal AMG");
|
|
args.AddOption(&vis_steps, "-vs", "--vis-steps",
|
|
"Number of visualization steps.");
|
|
args.AddOption(&glvis, "-glvis", "--glvis", "-no-glvis", "--no-glvis", "");
|
|
args.AddOption(¶view, "-paraview", "--paraview",
|
|
"-no-paraview", "--no-paraview",
|
|
"Enable ParaView output (VTK files; can be expensive).");
|
|
args.AddOption(&viscosity_type, "-av-type", "--av-type", "");
|
|
args.AddOption(&viscosity_q1, "-av-q1", "--av-q1",
|
|
"Artificial viscosity (type 7) linear coefficient.");
|
|
args.AddOption(&viscosity_q2, "-av-q2", "--av-q2",
|
|
"Artificial viscosity (type 7) quadratic coefficient.");
|
|
args.AddOption(&dump_jacobians, "-dump-jacobians", "--dump-jacobians", "");
|
|
args.AddOption(&nretry, "-nretry", "--nretry", "");
|
|
args.AddOption(&petsc_opts, "-petsc-opts", "--petsc-opts",
|
|
"PETSc options (enables PETSc SNES path).");
|
|
args.ParseCheck();
|
|
|
|
Device device(device_config);
|
|
if (Mpi::Root()) { device.Print(); }
|
|
|
|
real_t blast_energy = (dim == 2) ? 0.25 : 0.125;
|
|
|
|
const bool use_petsc = petsc_opts_provided;
|
|
if (use_petsc)
|
|
{
|
|
#ifdef MFEM_USE_PETSC
|
|
std::vector<char*> petsc_argv;
|
|
petsc_argv.push_back(argv[0]); // Program name as first arg
|
|
|
|
// Split petsc_opts string into individual arguments.
|
|
std::string opts_str(petsc_opts);
|
|
std::istringstream iss(opts_str);
|
|
std::string arg;
|
|
|
|
while (iss >> arg)
|
|
{
|
|
char* arg_copy = new char[arg.length() + 1];
|
|
std::strcpy(arg_copy, arg.c_str());
|
|
petsc_argv.push_back(arg_copy);
|
|
}
|
|
|
|
int petsc_argc = static_cast<int>(petsc_argv.size());
|
|
char** petsc_args = petsc_argv.data();
|
|
|
|
MFEMInitializePetsc(&petsc_argc, &petsc_args);
|
|
|
|
// Keep output formatting consistent with laghos_petsc.cpp for PETSc runs.
|
|
out << std::setprecision(6);
|
|
#else
|
|
MFEM_ABORT("MFEM is not built with PETSc");
|
|
#endif
|
|
}
|
|
|
|
Mesh serial_mesh = Mesh(mesh_file, true, true);
|
|
|
|
if ((dim == 2) && (problem == 0 || problem == 1))
|
|
{
|
|
serial_mesh = Mesh(Mesh::MakeCartesian2D(1, 1, Element::QUADRILATERAL,
|
|
true));
|
|
const int NBE = serial_mesh.GetNBE();
|
|
for (int b = 0; b < NBE; b++)
|
|
{
|
|
Element *bel = serial_mesh.GetBdrElement(b);
|
|
const int attr = (b < NBE/2) ? 2 : 1;
|
|
bel->SetAttribute(attr);
|
|
}
|
|
}
|
|
if (dim == 3 && (problem == 0 || problem == 1))
|
|
{
|
|
serial_mesh = Mesh(Mesh::MakeCartesian3D(2, 2, 2, Element::HEXAHEDRON,
|
|
true));
|
|
const int NBE = serial_mesh.GetNBE();
|
|
for (int b = 0; b < NBE; b++)
|
|
{
|
|
Element *bel = serial_mesh.GetBdrElement(b);
|
|
const int attr = (b < NBE/3) ? 3 : (b < 2*NBE/3) ? 1 : 2;
|
|
bel->SetAttribute(attr);
|
|
}
|
|
}
|
|
|
|
if (problem == 2)
|
|
{
|
|
serial_mesh = Mesh(Mesh::MakeCartesian1D(1));
|
|
serial_mesh.GetBdrElement(0)->SetAttribute(1);
|
|
serial_mesh.GetBdrElement(1)->SetAttribute(1);
|
|
}
|
|
|
|
for (int i = 0; i < refinements; i++)
|
|
{
|
|
serial_mesh.UniformRefinement();
|
|
}
|
|
|
|
// serial_mesh.EnsureNCMesh();
|
|
// serial_mesh.RandomRefinement(0.1);
|
|
|
|
// The mesh dimension can change based on selected problem setup (e.g. -p 2).
|
|
dim = serial_mesh.Dimension();
|
|
|
|
ParMesh mesh = ParMesh(MPI_COMM_WORLD, serial_mesh);
|
|
|
|
// Define the parallel finite element spaces. We use:
|
|
// - H1 (Gauss-Lobatto, continuous) for position and velocity.
|
|
// - L2 (Bernstein, discontinuous) for specific internal energy.
|
|
H1_FECollection H1FEC(order_v, dim);
|
|
ParFiniteElementSpace H1FESpace(&mesh, &H1FEC, dim);
|
|
L2_FECollection L2FEC(order_e, dim, BasisType::Positive);
|
|
ParFiniteElementSpace L2FESpace(&mesh, &L2FEC);
|
|
|
|
const auto global_ne = mesh.GetGlobalNE();
|
|
const auto global_h1tsize = H1FESpace.GlobalTrueVSize();
|
|
const auto global_l2tsize = L2FESpace.GlobalTrueVSize();
|
|
|
|
if (Mpi::Root())
|
|
{
|
|
out << "num el: " << global_ne << "\n";
|
|
out << "num kinematic dofs: " << global_h1tsize << "\n";
|
|
out << "num thermodynamic dofs: " << global_l2tsize << "\n";
|
|
}
|
|
|
|
Array<int> ess_tdof, ess_vdofs;
|
|
{
|
|
Array<int> ess_bdr(mesh.bdr_attributes.Max()), dofs_marker, dofs_list;
|
|
for (int d = 0; d < mesh.Dimension(); d++)
|
|
{
|
|
// Attributes 1/2/3 correspond to fixed-x/y/z boundaries,
|
|
// i.e., we must enforce v_x/y/z = 0 for the velocity components.
|
|
ess_bdr = 0; ess_bdr[d] = 1;
|
|
H1FESpace.GetEssentialTrueDofs(ess_bdr, dofs_list, d);
|
|
ess_tdof.Append(dofs_list);
|
|
H1FESpace.GetEssentialVDofs(ess_bdr, dofs_marker, d);
|
|
FiniteElementSpace::MarkerToList(dofs_marker, dofs_list);
|
|
ess_vdofs.Append(dofs_list);
|
|
}
|
|
}
|
|
ess_tdof.Sort();
|
|
ess_tdof.Unique();
|
|
|
|
// The monolithic BlockVector stores unknown fields as:
|
|
// - 0 -> position
|
|
// - 1 -> velocity
|
|
// - 2 -> specific internal energy
|
|
const int Vsize_l2 = L2FESpace.GetVSize();
|
|
const int Vsize_h1 = H1FESpace.GetVSize();
|
|
Array<int> offset(4);
|
|
offset[0] = 0;
|
|
offset[1] = offset[0] + Vsize_h1;
|
|
offset[2] = offset[1] + Vsize_h1;
|
|
offset[3] = offset[2] + Vsize_l2;
|
|
BlockVector S(offset, Device::GetDeviceMemoryType());
|
|
|
|
ParGridFunction x_gf, v_gf, e_gf;
|
|
x_gf.MakeRef(&H1FESpace, S, offset[0]);
|
|
v_gf.MakeRef(&H1FESpace, S, offset[1]);
|
|
e_gf.MakeRef(&L2FESpace, S, offset[2]);
|
|
|
|
mesh.SetNodalGridFunction(&x_gf);
|
|
x_gf.SyncAliasMemory(S);
|
|
|
|
ParGridFunction x0_gf = x_gf;
|
|
|
|
auto v0 = [](const Vector &x, Vector &v)
|
|
{
|
|
switch (problem)
|
|
{
|
|
case 0:
|
|
v(0) = sin(M_PI*x(0)) * cos(M_PI*x(1));
|
|
v(1) = -cos(M_PI*x(0)) * sin(M_PI*x(1));
|
|
if (x.Size() == 3)
|
|
{
|
|
v(0) *= cos(M_PI*x(2));
|
|
v(1) *= cos(M_PI*x(2));
|
|
v(2) = 0.0;
|
|
}
|
|
break;
|
|
case 1: v = 0.0; break;
|
|
case 2: v = 0.0; break;
|
|
case 3: v = 0.0; break;
|
|
default: MFEM_ABORT("error");
|
|
}
|
|
};
|
|
|
|
VectorFunctionCoefficient v_coeff(dim, v0);
|
|
v_gf.ProjectCoefficient(v_coeff);
|
|
for (int i = 0; i < ess_vdofs.Size(); i++)
|
|
{
|
|
v_gf(ess_vdofs[i]) = 0.0;
|
|
}
|
|
v_gf.SyncAliasMemory(S);
|
|
|
|
auto rho0 = [&dim](const Vector &x)
|
|
{
|
|
switch (problem)
|
|
{
|
|
case 0: return 1.0;
|
|
case 1: return 1.0;
|
|
case 2: return (x(0) < 0.5) ? 1.0 : 0.1;
|
|
case 3: return (dim == 2) ? x(0) > 1.0 && x(1) > 1.5 ? 0.125 : 1.0
|
|
: x(0) > 1.0 && ((x(1) < 1.5 && x(2) < 1.5) ||
|
|
(x(1) > 1.5 && x(2) > 1.5)) ? 0.125 : 1.0;
|
|
default: MFEM_ABORT("error");
|
|
}
|
|
};
|
|
|
|
ParGridFunction rho0_gf(&L2FESpace);
|
|
FunctionCoefficient rho0_coeff(rho0);
|
|
L2_FECollection l2_fec(order_e, mesh.Dimension());
|
|
ParFiniteElementSpace l2_fes(&mesh, &l2_fec);
|
|
ParGridFunction l2_rho0_gf(&l2_fes), l2_e(&l2_fes);
|
|
l2_rho0_gf.ProjectCoefficient(rho0_coeff);
|
|
rho0_gf.ProjectGridFunction(l2_rho0_gf);
|
|
|
|
auto gamma_func = [](const Vector &x)
|
|
{
|
|
switch (problem)
|
|
{
|
|
case 0: return 5.0 / 3.0;
|
|
case 1: return 1.4;
|
|
case 2: return 1.4;
|
|
case 3: return (x(0) > 1.0 && x(1) <= 1.5) ? 1.4 : 1.5;
|
|
default: MFEM_ABORT("error");
|
|
}
|
|
};
|
|
|
|
auto e0 = [&rho0, &gamma_func](const Vector &x)
|
|
{
|
|
switch (problem)
|
|
{
|
|
case 0:
|
|
{
|
|
const real_t denom = 2.0 / 3.0; // (5/3 - 1) * density.
|
|
real_t val;
|
|
if (x.Size() == 2)
|
|
{
|
|
val = 1.0 + (cos(2*M_PI*x(0)) + cos(2*M_PI*x(1))) / 4.0;
|
|
}
|
|
else
|
|
{
|
|
val = 100.0 + ((cos(2*M_PI*x(2)) + 2) *
|
|
(cos(2*M_PI*x(0)) + cos(2*M_PI*x(1))) - 2) / 16.0;
|
|
}
|
|
return val/denom;
|
|
}
|
|
case 1: return 0.0; // This case in initialized in main().
|
|
case 2: return (x(0) < 0.5) ? 1.0 / rho0(x) / (gamma_func(x) - 1.0)
|
|
: 0.1 / rho0(x) / (gamma_func(x) - 1.0);
|
|
case 3: return (x(0) > 1.0) ? 0.1 / rho0(x) / (gamma_func(x) - 1.0)
|
|
: 1.0 / rho0(x) / (gamma_func(x) - 1.0);
|
|
default: MFEM_ABORT("error");
|
|
}
|
|
};
|
|
|
|
if (problem == 1)
|
|
{
|
|
DeltaCoefficient e_coeff(blast_position[0], blast_position[1],
|
|
blast_position[2], blast_energy);
|
|
l2_e.ProjectCoefficient(e_coeff);
|
|
}
|
|
else
|
|
{
|
|
FunctionCoefficient e_coeff(e0);
|
|
l2_e.ProjectCoefficient(e_coeff);
|
|
}
|
|
|
|
e_gf.ProjectGridFunction(l2_e);
|
|
e_gf.SyncAliasMemory(S);
|
|
|
|
L2_FECollection material_fec(0, dim);
|
|
ParFiniteElementSpace L2CFESpace(&mesh, &material_fec);
|
|
ParGridFunction material_gf(&L2CFESpace);
|
|
FunctionCoefficient material_coeff(gamma_func);
|
|
material_gf.ProjectCoefficient(material_coeff);
|
|
|
|
ParGridFunction rho_gf(&L2FESpace);
|
|
|
|
IntegrationRule ir = IntRules.Get(mesh.GetElementBaseGeometry(0),
|
|
3 * H1FESpace.GetOrder(0) + L2FESpace.GetOrder(0) - 1);
|
|
|
|
if (Mpi::Root())
|
|
{
|
|
out << "num qp: " << ir.GetNPoints() << "\n";
|
|
}
|
|
|
|
// Create external data vector.
|
|
Vector external_data(EXT_DATA_IDX::COUNT);
|
|
external_data[EXT_DATA_IDX::CFL] = cfl;
|
|
external_data[EXT_DATA_IDX::ORDER_VEL] = order_v;
|
|
external_data[EXT_DATA_IDX::VISCOSITY_FLAG] = use_viscosity;
|
|
external_data[EXT_DATA_IDX::VISCOSITY_TYPE] = viscosity_type;
|
|
external_data[EXT_DATA_IDX::VISC_Q1] = viscosity_q1;
|
|
external_data[EXT_DATA_IDX::VISC_Q2] = viscosity_q2;
|
|
external_data[EXT_DATA_IDX::DT_ESTIMATE] =
|
|
std::numeric_limits<real_t>::infinity();
|
|
|
|
LagrangianHydroOperator *hydro = nullptr;
|
|
switch (dim)
|
|
{
|
|
case 2:
|
|
{
|
|
hydro = CreateLagrangianHydroOperator<2>(H1FESpace, L2FESpace,
|
|
ess_tdof,
|
|
rho0_coeff,
|
|
x0_gf, rho0_gf, material_gf,
|
|
external_data,
|
|
ir,
|
|
fd_gradient,
|
|
dump_jacobians,
|
|
nonlinear_maximum_iterations,
|
|
nonlinear_relative_tolerance,
|
|
krylov_maximum_iterations,
|
|
preconditioner_lag,
|
|
(PRECONDITIONER_TYPE)preconditioner_type,
|
|
use_petsc);
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
hydro = CreateLagrangianHydroOperator<3>(H1FESpace, L2FESpace,
|
|
ess_tdof,
|
|
rho0_coeff,
|
|
x0_gf, rho0_gf, material_gf,
|
|
external_data,
|
|
ir,
|
|
fd_gradient,
|
|
dump_jacobians,
|
|
nonlinear_maximum_iterations,
|
|
nonlinear_relative_tolerance,
|
|
krylov_maximum_iterations,
|
|
preconditioner_lag,
|
|
(PRECONDITIONER_TYPE)preconditioner_type,
|
|
use_petsc);
|
|
break;
|
|
}
|
|
default: MFEM_ABORT("unsupported mesh dimension: " << dim);
|
|
}
|
|
|
|
ODESolver *ode_solver = NULL;
|
|
switch (ode_solver_type)
|
|
{
|
|
case 1: ode_solver = new ForwardEulerSolver; break;
|
|
case 2: ode_solver = new RK2Solver(0.5); break;
|
|
case 3: ode_solver = new RK3SSPSolver; break;
|
|
case 4: ode_solver = new RK4Solver; break;
|
|
case 6: ode_solver = new RK6Solver; break;
|
|
case 11: ode_solver = new BackwardEulerSolver; petsc_lag_steps = 1; break;
|
|
case 12: ode_solver = new ImplicitMidpointSolver; petsc_lag_steps = 2; break;
|
|
case 13: ode_solver = new SDIRK33Solver; petsc_lag_steps = 3; break;
|
|
case 14: ode_solver = new SDIRK34Solver; petsc_lag_steps = 3; break;
|
|
default:
|
|
out << "Unknown ODE solver type: " << ode_solver_type << '\n';
|
|
return -1;
|
|
}
|
|
ode_solver->Init(*hydro);
|
|
|
|
hydro->ComputeDensity(rho_gf);
|
|
const real_t energy_init = hydro->InternalEnergy(e_gf) +
|
|
hydro->KineticEnergy(v_gf);
|
|
|
|
socketstream vis_rho, vis_v, vis_e;
|
|
char vishost[] = "localhost";
|
|
int visport = 19916;
|
|
if (glvis)
|
|
{
|
|
// Make sure all MPI ranks have sent their 'v' solution before initiating
|
|
// another set of GLVis connections (one from each rank):
|
|
MPI_Barrier(mesh.GetComm());
|
|
vis_rho.precision(8);
|
|
vis_v.precision(8);
|
|
vis_e.precision(8);
|
|
int Wx = 0, Wy = 0; // window position
|
|
const int Ww = 350, Wh = 350; // window size
|
|
int offx = Ww+10; // window offsets
|
|
if (problem != 0 && problem != 4)
|
|
{
|
|
VisualizeField(vis_rho, vishost, visport, rho_gf,
|
|
"Density", Wx, Wy, Ww, Wh);
|
|
}
|
|
Wx += offx;
|
|
VisualizeField(vis_v, vishost, visport, v_gf,
|
|
"Velocity", Wx, Wy, Ww, Wh);
|
|
Wx += offx;
|
|
VisualizeField(vis_e, vishost, visport, e_gf,
|
|
"Specific Internal Energy", Wx, Wy, Ww, Wh);
|
|
}
|
|
|
|
if (Mpi::Root())
|
|
{
|
|
out << "energy initial: " << energy_init << "\n";
|
|
}
|
|
|
|
real_t t = 0.0;
|
|
hydro->ResetTimeStepEstimate();
|
|
real_t dt = hydro->GetTimeStepEstimate(S);
|
|
|
|
if (Mpi::Root())
|
|
{
|
|
out << "time step estimate: " << dt << "\n";
|
|
}
|
|
|
|
real_t t_old;
|
|
bool last_step = false;
|
|
BlockVector S_old(S);
|
|
|
|
std::unique_ptr<QuadratureSpace> qs;
|
|
std::unique_ptr<QuadratureFunction> vqf;
|
|
std::unique_ptr<QuadratureFunction> rqf;
|
|
std::unique_ptr<QuadratureFunction> eqf;
|
|
std::unique_ptr<ParaViewDataCollection> dc;
|
|
|
|
auto paraview_save = [&](const int ti, const real_t time)
|
|
{
|
|
if (!dc) { return; }
|
|
hydro->ComputeDensity(rho_gf);
|
|
vqf->ProjectGridFunction(v_gf);
|
|
rqf->ProjectGridFunction(rho_gf);
|
|
eqf->ProjectGridFunction(e_gf);
|
|
dc->SetCycle(ti);
|
|
dc->SetTime(time);
|
|
dc->Save();
|
|
};
|
|
|
|
if (paraview)
|
|
{
|
|
qs.reset(new QuadratureSpace(mesh, ir));
|
|
vqf.reset(new QuadratureFunction(qs.get(), v_gf.VectorDim()));
|
|
rqf.reset(new QuadratureFunction(qs.get(), rho_gf.VectorDim()));
|
|
eqf.reset(new QuadratureFunction(qs.get(), e_gf.VectorDim()));
|
|
|
|
dc.reset(new ParaViewDataCollection("dfem", &mesh));
|
|
dc->SetLevelsOfDetail(order_v);
|
|
dc->SetDataFormat(VTKFormat::BINARY);
|
|
dc->SetHighOrderOutput(true);
|
|
dc->RegisterField("velocity", &v_gf);
|
|
dc->RegisterField("density", &rho_gf);
|
|
dc->RegisterField("specific_internal_energy", &e_gf);
|
|
dc->RegisterField("material", &material_gf);
|
|
dc->RegisterQField("velocity_qf", vqf.get());
|
|
dc->RegisterQField("density_qf", rqf.get());
|
|
dc->RegisterQField("specific_internal_energy_qf", eqf.get());
|
|
|
|
paraview_save(0, 0.0);
|
|
}
|
|
|
|
for (int ti = 1; !last_step; ti++)
|
|
{
|
|
if (t + dt >= t_final)
|
|
{
|
|
dt = t_final - t;
|
|
last_step = true;
|
|
}
|
|
|
|
S_old = S;
|
|
t_old = t;
|
|
hydro->ResetTimeStepEstimate();
|
|
|
|
hydro->ResetNewtonInfo();
|
|
ode_solver->Step(S, t, dt);
|
|
|
|
// Adaptive time step control.
|
|
const real_t dt_est = hydro->GetTimeStepEstimate(S);
|
|
|
|
if (ode_solver_type > 10) // implicit.
|
|
{
|
|
// Don't repeat for TGreen.
|
|
if (problem != 0)
|
|
{
|
|
// Repeat.
|
|
if (hydro->newton_converged == false || hydro->newton_max_iter >= 4)
|
|
{
|
|
dt *= 0.85;
|
|
t = t_old;
|
|
S = S_old;
|
|
hydro->ResetQuadratureData();
|
|
last_step = false;
|
|
if (Mpi::Root())
|
|
{
|
|
out << "Repeating step " << ti << " with dt: " << dt << "." << std::endl;
|
|
if (!hydro->newton_converged)
|
|
{
|
|
if (use_petsc)
|
|
{
|
|
#ifdef MFEM_USE_PETSC
|
|
out << "SNES did not converge in "
|
|
<< hydro->snes->GetNumIterations() << " iterations.\n";
|
|
#else
|
|
out << "SNES did not converge.\n";
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
out << "Newton did not converge in "
|
|
<< hydro->newton_max_iter << " iterations.\n";
|
|
}
|
|
}
|
|
else { out << "Estimated dt lower than taken dt.\n"; }
|
|
}
|
|
|
|
if (!hydro->newton_converged)
|
|
{
|
|
if (nretry == 1)
|
|
{
|
|
hydro->residual->dump_jacobians = 1;
|
|
}
|
|
else if (nretry == 0)
|
|
{
|
|
if (Mpi::Root()) { out << "no retry planned, exit\n"; }
|
|
exit(1);
|
|
}
|
|
nretry--;
|
|
}
|
|
|
|
ti--; continue;
|
|
}
|
|
else if (hydro->newton_max_iter <= 1)
|
|
{
|
|
dt *= 1.05;
|
|
if (Mpi::Root()) { out << "Increasing dt: " << dt << std::endl; }
|
|
}
|
|
}
|
|
}
|
|
else if (dt_est < dt) // explicit.
|
|
{
|
|
// Repeat (solve again) with a decreased time step - decrease of the
|
|
// time estimate suggests appearance of oscillations.
|
|
dt *= 0.85;
|
|
if (dt < std::numeric_limits<real_t>::epsilon())
|
|
{ MFEM_ABORT("The time step crashed!"); }
|
|
t = t_old;
|
|
S = S_old;
|
|
hydro->ResetQuadratureData();
|
|
last_step = false;
|
|
if (Mpi::Root()) { out << "Repeating step " << ti << std::endl; }
|
|
ti--; continue;
|
|
}
|
|
else if (dt_est > 1.25 * dt) { dt *= 1.02; }
|
|
|
|
x_gf.SyncAliasMemory(S);
|
|
v_gf.SyncAliasMemory(S);
|
|
e_gf.SyncAliasMemory(S);
|
|
|
|
// Make sure that the mesh corresponds to the new solution state. This is
|
|
// needed, because some time integrators use different S-type vectors
|
|
// and the oper object might have redirected the mesh positions to those.
|
|
mesh.NewNodes(x_gf, false);
|
|
|
|
if (paraview && (ti % vis_steps == 0 || last_step))
|
|
{
|
|
paraview_save(ti, t);
|
|
}
|
|
|
|
// verr_gf.ProjectCoefficient(v_coeff);
|
|
// for (int i = 0; i < verr_gf.Size(); i++)
|
|
// {
|
|
// verr_gf(i) = abs(verr_gf(i) - v_gf(i));
|
|
// }
|
|
|
|
if (ti % vis_steps == 0 || last_step)
|
|
{
|
|
if (Mpi::Root()) { out << "dt_est: " << dt_est << "\n"; }
|
|
|
|
if (Mpi::Root())
|
|
{
|
|
out << "step " << std::setw(5) << ti
|
|
<< ",\tt = " << std::setw(5) << t
|
|
<< ",\tdt = " << std::setw(5) << dt;
|
|
out << std::endl;
|
|
}
|
|
}
|
|
|
|
if (glvis && (ti % vis_steps == 0 || last_step))
|
|
{
|
|
hydro->ComputeDensity(rho_gf);
|
|
int Wx = 0, Wy = 0; // window position
|
|
int Ww = 350, Wh = 350; // window size
|
|
int offx = Ww+10; // window offsets
|
|
if (problem != 0 && problem != 4)
|
|
{
|
|
VisualizeField(vis_rho, vishost, visport, rho_gf,
|
|
"Density", Wx, Wy, Ww, Wh);
|
|
}
|
|
Wx += offx;
|
|
VisualizeField(vis_v, vishost, visport,
|
|
v_gf, "Velocity", Wx, Wy, Ww, Wh);
|
|
Wx += offx;
|
|
VisualizeField(vis_e, vishost, visport, e_gf,
|
|
"Specific Internal Energy", Wx, Wy, Ww,Wh);
|
|
Wx += offx;
|
|
}
|
|
}
|
|
|
|
const real_t energy_final = hydro->InternalEnergy(e_gf)
|
|
+ hydro->KineticEnergy(v_gf);
|
|
|
|
if (Mpi::Root())
|
|
{
|
|
out << std::scientific << std::setprecision(2)
|
|
<< "Energy diff: " << fabs(energy_init - energy_final) << std::endl;
|
|
}
|
|
|
|
if (problem == 0)
|
|
{
|
|
const real_t v_err_max = v_gf.ComputeMaxError(v_coeff);
|
|
const real_t v_err_l1 = v_gf.ComputeL1Error(v_coeff);
|
|
const real_t v_err_l2 = v_gf.ComputeL2Error(v_coeff);
|
|
if (Mpi::Root())
|
|
{
|
|
out << "L_inf error: " << v_err_max << std::endl
|
|
<< "L_1 error: " << v_err_l1 << std::endl
|
|
<< "L_2 error: " << v_err_l2 << std::endl;
|
|
}
|
|
}
|
|
|
|
if (problem == 1)
|
|
{
|
|
hydro->DensityScatter();
|
|
}
|
|
|
|
if (problem == 0 && dim == 3)
|
|
{
|
|
mesh.SaveAsOne("tgreen.mesh");
|
|
v_gf.SaveAsOne("tgreen.gf");
|
|
}
|
|
if (problem == 1 && dim == 3)
|
|
{
|
|
mesh.SaveAsOne("sedov.mesh");
|
|
rho_gf.SaveAsOne("sedov.gf");
|
|
}
|
|
if (problem == 3)
|
|
{
|
|
mesh.SaveAsOne("3point.mesh");
|
|
rho_gf.SaveAsOne("3point.gf");
|
|
}
|
|
|
|
delete hydro;
|
|
|
|
#ifdef MFEM_USE_PETSC
|
|
if (use_petsc) { MFEMFinalizePetsc(); }
|
|
#endif
|
|
|
|
return 0;
|
|
}
|