feat(libmeanfield): centrifugal + pressure
This commit is contained in:
@@ -1,21 +1,55 @@
|
||||
module;
|
||||
#include "mfem.hpp"
|
||||
#include <source_location>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <format>
|
||||
#include <source_location>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <format>
|
||||
|
||||
module mean_field;
|
||||
import :mapping.coefficients;
|
||||
import :analysis.integral;
|
||||
|
||||
namespace {
|
||||
double centrifugal_potential(const mfem::Vector &phys_x, const double omega) {
|
||||
double centrifugal_potential(
|
||||
const mfem::Vector &phys_x,
|
||||
const double omega
|
||||
) {
|
||||
const double s2 = std::pow(phys_x(0), 2) + std::pow(phys_x(1), 2);
|
||||
return -0.5 * s2 * std::pow(omega, 2);
|
||||
}
|
||||
}
|
||||
|
||||
void grid_function_to_true_dofs(
|
||||
const mfem::ParFiniteElementSpace &finite_element_space,
|
||||
const mfem::GridFunction &grid_function,
|
||||
mfem::Vector &true_dofs
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
grid_function.Size() == finite_element_space.GetVSize(),
|
||||
"The grid function does not match the requested finite-element "
|
||||
"space."
|
||||
);
|
||||
|
||||
true_dofs.SetSize(finite_element_space.GetTrueVSize());
|
||||
|
||||
const mfem::Operator *restriction =
|
||||
finite_element_space.GetRestrictionMatrix();
|
||||
|
||||
if (restriction != nullptr) {
|
||||
restriction->Mult(grid_function, true_dofs);
|
||||
} else {
|
||||
MFEM_VERIFY(
|
||||
grid_function.Size() == true_dofs.Size(),
|
||||
"A finite-element space without a restriction operator must "
|
||||
"have "
|
||||
"matching local and true sizes."
|
||||
);
|
||||
|
||||
true_dofs = grid_function;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::physics {
|
||||
GravitySolution grav_potential(
|
||||
@@ -24,68 +58,106 @@ namespace mean_field::physics {
|
||||
const mfem::GridFunction &rho,
|
||||
const bool phi_warm
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr && rho.FESpace() == f.densityFes.get(),
|
||||
"Gravity solve requires rho to use the registered density space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr,
|
||||
"Gravity solve requires the registered gravity-potential space."
|
||||
);
|
||||
|
||||
mfem::Array<int> outer_bdr_marker(f.mesh->bdr_attributes.Max());
|
||||
outer_bdr_marker = 0;
|
||||
outer_bdr_marker = 0;
|
||||
outer_bdr_marker[1] = 1;
|
||||
|
||||
mfem::ParLinearForm g_rhs(f.RT_fes.get());
|
||||
mfem::ParLinearForm g_rhs(f.gravityFluxFes.get());
|
||||
|
||||
// ReSharper disable once CppTooWideScope
|
||||
std::unique_ptr<mfem::Coefficient> boundary_potential_coeff;
|
||||
|
||||
if (!f.has_mapping()) { // We only need to explicitly add a boundary integrator if a mapping is not being used. In the case where the outer domain has been compactified the φ=0 boundary condition is the natural condition and MFEM automatically handles this
|
||||
auto boundary_potential = [&f](const mfem::Vector& x_physical) {
|
||||
if (!f.has_mapping()) { // We only need to explicitly add a boundary
|
||||
// integrator if a mapping is not being used. In
|
||||
// the case where the outer domain has been
|
||||
// compactified the φ=0 boundary condition is
|
||||
// the natural condition and MFEM automatically
|
||||
// handles this
|
||||
auto boundary_potential = [&f](const mfem::Vector &x_physical) {
|
||||
return l2_multipole_potential(f, utils::MASS, x_physical);
|
||||
};
|
||||
|
||||
boundary_potential_coeff = std::make_unique<mfem::FunctionCoefficient>(boundary_potential);
|
||||
auto boundary_integrator = std::make_unique<mfem::VectorFEBoundaryFluxLFIntegrator>(*boundary_potential_coeff);
|
||||
const mfem::FiniteElement& boundary_element = *f.RT_fes->GetTypicalTraceElement();
|
||||
boundary_potential_coeff =
|
||||
std::make_unique<mfem::FunctionCoefficient>(boundary_potential);
|
||||
auto boundary_integrator =
|
||||
std::make_unique<mfem::VectorFEBoundaryFluxLFIntegrator>(
|
||||
*boundary_potential_coeff
|
||||
);
|
||||
const mfem::FiniteElement &boundary_element =
|
||||
*f.gravityFluxFes->GetTypicalTraceElement();
|
||||
|
||||
f.quadrature_factory->configure_gravity_boundary(*boundary_integrator, quadrature::QuadratureRole::discretization, boundary_element, utils::DOMAINS::VACUUM, quadrature::MappingKind::none);
|
||||
g_rhs.AddBoundaryIntegrator(boundary_integrator.release(), outer_bdr_marker);
|
||||
f.quadratureFactory->configure_gravity_boundary(
|
||||
*boundary_integrator,
|
||||
quadrature::QuadratureRole::discretization, boundary_element,
|
||||
utils::DOMAINS::VACUUM, quadrature::MappingKind::none
|
||||
);
|
||||
g_rhs.AddBoundaryIntegrator(
|
||||
boundary_integrator.release(), outer_bdr_marker
|
||||
);
|
||||
}
|
||||
|
||||
g_rhs.Assemble();
|
||||
mfem::GridFunctionCoefficient rho_coeff(&rho);
|
||||
mfem::ConstantCoefficient G4pi(4.0 * M_PI * utils::G);
|
||||
mfem::ProductCoefficient source_coeff(G4pi, rho_coeff);
|
||||
mfem::ParLinearForm f_rhs(f.L2_fes.get());
|
||||
mfem::ParLinearForm f_rhs(f.gravityPotentialFes.get());
|
||||
|
||||
std::unique_ptr<mfem::Coefficient> mapped_source_coeff;
|
||||
mfem::Coefficient* active_source_coeff = &source_coeff;
|
||||
quadrature::MappingKind source_mapping_kind = quadrature::MappingKind::none;
|
||||
mfem::Coefficient *active_source_coeff = &source_coeff;
|
||||
quadrature::MappingKind source_mapping_kind =
|
||||
quadrature::MappingKind::none;
|
||||
|
||||
if (f.has_mapping()) {
|
||||
mapped_source_coeff = std::make_unique<mapping::MappedScalarCoefficient>(*f.mapping, source_coeff);
|
||||
mapped_source_coeff =
|
||||
std::make_unique<mapping::MappedScalarCoefficient>(
|
||||
*f.mapping, source_coeff
|
||||
);
|
||||
active_source_coeff = mapped_source_coeff.get();
|
||||
source_mapping_kind = quadrature::MappingKind::general;
|
||||
}
|
||||
|
||||
auto source_integrator = std::make_unique<mfem::DomainLFIntegrator>(*active_source_coeff);
|
||||
const mfem::FiniteElement& source_test_element = *f.L2_fes->GetTypicalFE();
|
||||
const mfem::ElementTransformation& source_transformation = *f.mesh->GetElementTransformation(0);
|
||||
const int source_coefficient_order = f.L2_fes->GetMaxElementOrder();
|
||||
auto source_integrator =
|
||||
std::make_unique<mfem::DomainLFIntegrator>(*active_source_coeff);
|
||||
const mfem::FiniteElement &source_test_element =
|
||||
*f.gravityPotentialFes->GetTypicalFE();
|
||||
const mfem::ElementTransformation &source_transformation =
|
||||
*f.mesh->GetElementTransformation(0);
|
||||
const int source_coefficient_order = f.densityFes->GetMaxElementOrder();
|
||||
|
||||
f.quadrature_factory->configure_gravity_source(*source_integrator, quadrature::QuadratureRole::discretization, source_test_element, source_transformation, source_coefficient_order, utils::DOMAINS::STELLAR, source_mapping_kind);
|
||||
f_rhs.AddDomainIntegrator(source_integrator.release(), f.gravity_context.stellar_mask);
|
||||
f.quadratureFactory->configure_gravity_source(
|
||||
*source_integrator, quadrature::QuadratureRole::discretization,
|
||||
source_test_element, source_transformation,
|
||||
source_coefficient_order, utils::DOMAINS::STELLAR,
|
||||
source_mapping_kind
|
||||
);
|
||||
f_rhs.AddDomainIntegrator(
|
||||
source_integrator.release(), f.gravityContext.stellar_mask
|
||||
);
|
||||
f_rhs.Assemble();
|
||||
|
||||
mfem::BlockVector RHS(f.gravity_block_true_offsets);
|
||||
mfem::BlockVector RHS(f.gravityBlockTrueOffsets);
|
||||
RHS.GetBlock(0) = *g_rhs.ParallelAssemble();
|
||||
RHS.GetBlock(1) = *f_rhs.ParallelAssemble();
|
||||
|
||||
mfem::BlockVector X(f.gravity_block_true_offsets);
|
||||
mfem::BlockVector X(f.gravityBlockTrueOffsets);
|
||||
X = 0.0;
|
||||
f.gravity_context.minres->SetOperator(*f.gravity_context.block_A);
|
||||
f.gravity_context.minres->Mult(RHS, X);
|
||||
f.gravityContext.minres->SetOperator(*f.gravityContext.block_A);
|
||||
f.gravityContext.minres->Mult(RHS, X);
|
||||
|
||||
GravitySolution solution(f);
|
||||
solution.gradPhi.SetFromTrueDofs(X.GetBlock(0));
|
||||
solution.phi.SetFromTrueDofs(X.GetBlock(1));
|
||||
|
||||
return solution;
|
||||
|
||||
}
|
||||
|
||||
mfem::GridFunction get_potential(
|
||||
@@ -105,18 +177,21 @@ namespace mean_field::physics {
|
||||
|
||||
std::unique_ptr<mfem::Coefficient> centrifugal_coeff;
|
||||
if (fem.has_mapping()) {
|
||||
centrifugal_coeff = std::make_unique<mapping::PhysicalPositionFunctionCoefficient>(*fem.mapping, rot);
|
||||
centrifugal_coeff = std::make_unique<
|
||||
mapping::PhysicalPositionFunctionCoefficient>(
|
||||
*fem.mapping, rot
|
||||
);
|
||||
} else {
|
||||
centrifugal_coeff = std::make_unique<mfem::FunctionCoefficient>(rot);
|
||||
centrifugal_coeff =
|
||||
std::make_unique<mfem::FunctionCoefficient>(rot);
|
||||
}
|
||||
|
||||
mfem::GridFunction centrifugal_gf(fem.H1_fes.get());
|
||||
mfem::GridFunction centrifugal_gf(fem.gravityPotentialFes.get());
|
||||
centrifugal_gf.ProjectCoefficient(*centrifugal_coeff);
|
||||
|
||||
phi.phi += centrifugal_gf;
|
||||
}
|
||||
return phi.phi;
|
||||
|
||||
}
|
||||
|
||||
mfem::DenseMatrix compute_quadrupole_moment_tensor(
|
||||
@@ -128,11 +203,23 @@ namespace mean_field::physics {
|
||||
mfem::DenseMatrix local_Q(dim, dim);
|
||||
local_Q = 0.0;
|
||||
|
||||
for (int i = 0; i < fem.H1_fes->GetNE(); ++i) {
|
||||
if (fem.mesh->GetAttribute(i) == 3) continue;
|
||||
for (int i = 0; i < fem.mesh->GetNE(); ++i) {
|
||||
if (fem.mesh->GetAttribute(i) == 3)
|
||||
continue;
|
||||
|
||||
mfem::ElementTransformation *trans = fem.mesh->GetElementTransformation(i);
|
||||
const mfem::IntegrationRule &ir = *fem.int_rule;
|
||||
mfem::ElementTransformation *trans =
|
||||
fem.mesh->GetElementTransformation(i);
|
||||
using DensityField = field::Field<field::Density>;
|
||||
const quadrature::Query query =
|
||||
DensityField::make_query<field::Density::Form::Quadrupole>(
|
||||
quadrature::QuadratureRole::diagnostic, trans->OrderW(),
|
||||
std::array<int, 1>{2}, utils::DOMAINS::STELLAR,
|
||||
fem.has_mapping() ? quadrature::MappingKind::general
|
||||
: quadrature::MappingKind::none
|
||||
);
|
||||
const mfem::IntegrationRule &ir =
|
||||
*fem.quadratureFactory->get(query, trans->GetGeometryType())
|
||||
.integration_rule;
|
||||
|
||||
for (int j = 0; j < ir.GetNPoints(); ++j) {
|
||||
const mfem::IntegrationPoint &ip = ir.IntPoint(j);
|
||||
@@ -164,7 +251,8 @@ namespace mean_field::physics {
|
||||
for (int m = 0; m < dim; ++m) {
|
||||
for (int n = 0; n < dim; ++n) {
|
||||
const double delta = (m == n) ? 1.0 : 0.0;
|
||||
const double contrib = 3.0 * x_prime(m) * x_prime(n) - delta * r_sq;
|
||||
const double contrib =
|
||||
3.0 * x_prime(m) * x_prime(n) - delta * r_sq;
|
||||
local_Q(m, n) += rho_val * contrib * weight;
|
||||
}
|
||||
}
|
||||
@@ -172,7 +260,10 @@ namespace mean_field::physics {
|
||||
}
|
||||
|
||||
mfem::DenseMatrix global_Q(dim, dim);
|
||||
MPI_Allreduce(local_Q.GetData(), global_Q.GetData(), dim * dim, MPI_DOUBLE, MPI_SUM, fem.H1_fes->GetComm());
|
||||
MPI_Allreduce(
|
||||
local_Q.GetData(), global_Q.GetData(), dim * dim, MPI_DOUBLE,
|
||||
MPI_SUM, fem.mesh->GetComm()
|
||||
);
|
||||
|
||||
return global_Q;
|
||||
}
|
||||
@@ -183,7 +274,8 @@ namespace mean_field::physics {
|
||||
const mfem::Vector &phys_x
|
||||
) {
|
||||
const double r = phys_x.Norml2();
|
||||
if (r < 1e-12) return 0.0;
|
||||
if (r < 1e-12)
|
||||
return 0.0;
|
||||
|
||||
const int dim = fem.mesh->Dimension();
|
||||
|
||||
@@ -197,7 +289,8 @@ namespace mean_field::physics {
|
||||
}
|
||||
}
|
||||
|
||||
const double l2_contrib = -(utils::G / (2.0 * std::pow(r, 3))) * l2_mult_factor;
|
||||
const double l2_contrib =
|
||||
-(utils::G / (2.0 * std::pow(r, 3))) * l2_mult_factor;
|
||||
|
||||
const double l0_contrib = -utils::G * total_mass / r;
|
||||
|
||||
@@ -211,87 +304,331 @@ namespace mean_field::physics {
|
||||
// ==========================================
|
||||
// 1. Partially Assemble the High-Order Mass Block
|
||||
// ==========================================
|
||||
f.gravity_context.m_form = std::make_unique<mfem::ParBilinearForm>(f.RT_fes.get());
|
||||
f.gravity_context.m_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
|
||||
f.gravityContext.m_form =
|
||||
std::make_unique<mfem::ParBilinearForm>(f.gravityFluxFes.get());
|
||||
f.gravityContext.m_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
|
||||
|
||||
std::unique_ptr<mfem::VectorFEMassIntegrator> hdiv_mass_integrator;
|
||||
|
||||
if (f.has_mapping()) {
|
||||
f.gravity_context.mapped_hdiv_mass_coeff = std::make_unique<mapping::MappedHDivMassCoefficient>(*f.mapping, f.mesh->Dimension());
|
||||
hdiv_mass_integrator = std::make_unique<mfem::VectorFEMassIntegrator>(*f.gravity_context.mapped_hdiv_mass_coeff);
|
||||
f.gravityContext.mapped_hdiv_mass_coeff =
|
||||
std::make_unique<mapping::MappedHDivMassCoefficient>(
|
||||
*f.mapping, f.mesh->Dimension()
|
||||
);
|
||||
hdiv_mass_integrator =
|
||||
std::make_unique<mfem::VectorFEMassIntegrator>(
|
||||
*f.gravityContext.mapped_hdiv_mass_coeff
|
||||
);
|
||||
} else {
|
||||
f.gravity_context.mapped_hdiv_mass_coeff.reset();
|
||||
hdiv_mass_integrator = std::make_unique<mfem::VectorFEMassIntegrator>();
|
||||
f.gravityContext.mapped_hdiv_mass_coeff.reset();
|
||||
hdiv_mass_integrator =
|
||||
std::make_unique<mfem::VectorFEMassIntegrator>();
|
||||
}
|
||||
|
||||
const mfem::FiniteElement& hdiv_element = *f.RT_fes->GetTypicalFE();
|
||||
const mfem::ElementTransformation& hdiv_transformation = *f.mesh->GetElementTransformation(0);
|
||||
const quadrature::MappingKind mapping_kind = f.has_mapping() ? quadrature::MappingKind::general : quadrature::MappingKind::none;
|
||||
const mfem::FiniteElement &hdiv_element =
|
||||
*f.gravityFluxFes->GetTypicalFE();
|
||||
const mfem::ElementTransformation &hdiv_transformation =
|
||||
*f.mesh->GetElementTransformation(0);
|
||||
const quadrature::MappingKind mapping_kind =
|
||||
f.has_mapping() ? quadrature::MappingKind::general
|
||||
: quadrature::MappingKind::none;
|
||||
|
||||
f.quadrature_factory->configure_gravity_hdiv_mass(*hdiv_mass_integrator, quadrature::QuadratureRole::discretization, hdiv_element, hdiv_transformation, utils::DOMAINS::ALL, mapping_kind);
|
||||
f.gravity_context.m_form->AddDomainIntegrator(hdiv_mass_integrator.release());
|
||||
f.gravity_context.m_form->Assemble();
|
||||
f.quadratureFactory->configure_gravity_hdiv_mass(
|
||||
*hdiv_mass_integrator, quadrature::QuadratureRole::discretization,
|
||||
hdiv_element, hdiv_transformation, utils::DOMAINS::ALL, mapping_kind
|
||||
);
|
||||
f.gravityContext.m_form->AddDomainIntegrator(
|
||||
hdiv_mass_integrator.release()
|
||||
);
|
||||
|
||||
f.gravityContext.m_form->Assemble();
|
||||
|
||||
// ==========================================
|
||||
// 2. Partially Assemble the High-Order Divergence Block
|
||||
// ==========================================
|
||||
f.gravity_context.b_form = std::make_unique<mfem::ParMixedBilinearForm>(f.RT_fes.get(), f.L2_fes.get());
|
||||
f.gravity_context.b_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
|
||||
f.gravityContext.b_form = std::make_unique<mfem::ParMixedBilinearForm>(
|
||||
f.gravityFluxFes.get(), f.gravityPotentialFes.get()
|
||||
);
|
||||
f.gravityContext.b_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
|
||||
|
||||
auto divergence_discretization_integrator = std::make_unique<mfem::VectorFEDivergenceIntegrator>();
|
||||
const mfem::FiniteElement& divergence_discretization_test_element = *f.L2_fes->GetTypicalFE();
|
||||
auto divergence_discretization_integrator =
|
||||
std::make_unique<mfem::VectorFEDivergenceIntegrator>();
|
||||
const mfem::FiniteElement &divergence_discretization_test_element =
|
||||
*f.gravityPotentialFes->GetTypicalFE();
|
||||
|
||||
f.quadrature_factory->configure_gravity_divergence(*divergence_discretization_integrator, quadrature::QuadratureRole::discretization, hdiv_element, divergence_discretization_test_element, hdiv_transformation, utils::DOMAINS::ALL, quadrature::MappingKind::none);
|
||||
f.gravity_context.b_form->AddDomainIntegrator(divergence_discretization_integrator.release());
|
||||
f.gravity_context.b_form->Assemble();
|
||||
f.quadratureFactory->configure_gravity_divergence(
|
||||
*divergence_discretization_integrator,
|
||||
quadrature::QuadratureRole::discretization, hdiv_element,
|
||||
divergence_discretization_test_element, hdiv_transformation,
|
||||
utils::DOMAINS::ALL, quadrature::MappingKind::none
|
||||
);
|
||||
f.gravityContext.b_form->AddDomainIntegrator(
|
||||
divergence_discretization_integrator.release()
|
||||
);
|
||||
|
||||
f.gravityContext.b_form->Assemble();
|
||||
|
||||
MFEM_VERIFY(
|
||||
f.domainMapperStateless != nullptr,
|
||||
"Gravity source partial assembly requires the stateless domain "
|
||||
"mapper."
|
||||
);
|
||||
|
||||
mfem::Vector displacement_true(f.displacementFes->GetTrueVSize());
|
||||
displacement_true = 0.0;
|
||||
|
||||
const mfem::GridFunction *active_displacement =
|
||||
f.mapping->GetDisplacement();
|
||||
|
||||
if (active_displacement != nullptr) {
|
||||
grid_function_to_true_dofs(
|
||||
*f.displacementFes, *active_displacement, displacement_true
|
||||
);
|
||||
}
|
||||
|
||||
auto source_form =
|
||||
std::make_unique<operators::PreparedMappedGravitySourceOperator>(
|
||||
f, *f.domainMapperStateless
|
||||
);
|
||||
|
||||
source_form->Prepare(displacement_true);
|
||||
|
||||
f.gravityContext.source_form = std::move(source_form);
|
||||
// ==========================================
|
||||
// 3. Assemble Global Block Operator
|
||||
// ==========================================
|
||||
f.gravity_context.BT = std::make_unique<mfem::TransposeOperator>(f.gravity_context.b_form.get());
|
||||
f.gravityContext.BT = std::make_unique<mfem::TransposeOperator>(
|
||||
f.gravityContext.b_form.get()
|
||||
);
|
||||
|
||||
f.gravity_context.block_A = std::make_unique<mfem::BlockOperator>(f.gravity_block_true_offsets);
|
||||
f.gravity_context.block_A->SetBlock(0, 0, f.gravity_context.m_form.get());
|
||||
f.gravity_context.block_A->SetBlock(0, 1, f.gravity_context.BT.get());
|
||||
f.gravity_context.block_A->SetBlock(1, 0, f.gravity_context.b_form.get());
|
||||
f.gravityContext.block_A =
|
||||
std::make_unique<mfem::BlockOperator>(f.gravityBlockTrueOffsets);
|
||||
f.gravityContext.block_A->SetBlock(0, 0, f.gravityContext.m_form.get());
|
||||
f.gravityContext.block_A->SetBlock(0, 1, f.gravityContext.BT.get());
|
||||
f.gravityContext.block_A->SetBlock(1, 0, f.gravityContext.b_form.get());
|
||||
|
||||
// ==========================================
|
||||
// 4. Construct a mapped Schur preconditioner
|
||||
// ==========================================
|
||||
mfem::Vector mass_diagonal(f.RT_fes->GetTrueVSize());
|
||||
f.gravity_context.m_form->AssembleDiagonal(mass_diagonal);
|
||||
mfem::Vector mass_diagonal(f.gravityFluxFes->GetTrueVSize());
|
||||
f.gravityContext.m_form->AssembleDiagonal(mass_diagonal);
|
||||
|
||||
mfem::Vector inverse_mass_diagonal(mass_diagonal);
|
||||
|
||||
for (int i = 0; i < inverse_mass_diagonal.Size(); ++i) {
|
||||
MFEM_VERIFY(std::isfinite(inverse_mass_diagonal(i)) && inverse_mass_diagonal(i) > 0.0, "Mapped RT mass matrix has a non-positive or non-finite diagonal entry.");
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(inverse_mass_diagonal(i)) &&
|
||||
inverse_mass_diagonal(i) > 0.0,
|
||||
"Mapped RT mass matrix has a non-positive or non-finite "
|
||||
"diagonal "
|
||||
"entry."
|
||||
);
|
||||
inverse_mass_diagonal(i) = 1.0 / inverse_mass_diagonal(i);
|
||||
}
|
||||
|
||||
mfem::ParMixedBilinearForm b_preconditioner(f.RT_fes.get(), f.L2_fes.get());
|
||||
auto divergence_preconditioner_integrator = std::make_unique<mfem::VectorFEDivergenceIntegrator>();
|
||||
mfem::ParMixedBilinearForm b_preconditioner(
|
||||
f.gravityFluxFes.get(), f.gravityPotentialFes.get()
|
||||
);
|
||||
auto divergence_preconditioner_integrator =
|
||||
std::make_unique<mfem::VectorFEDivergenceIntegrator>();
|
||||
|
||||
const mfem::FiniteElement& divergence_trial_element = *f.RT_fes->GetTypicalFE();
|
||||
const mfem::FiniteElement& divergence_test_element = *f.L2_fes->GetTypicalFE();
|
||||
const mfem::ElementTransformation& divergence_transformation = *f.mesh->GetElementTransformation(0);
|
||||
const mfem::FiniteElement &divergence_trial_element =
|
||||
*f.gravityFluxFes->GetTypicalFE();
|
||||
const mfem::FiniteElement &divergence_test_element =
|
||||
*f.gravityPotentialFes->GetTypicalFE();
|
||||
const mfem::ElementTransformation &divergence_transformation =
|
||||
*f.mesh->GetElementTransformation(0);
|
||||
|
||||
f.quadrature_factory->configure_gravity_divergence(*divergence_preconditioner_integrator, quadrature::QuadratureRole::preconditioner, divergence_trial_element, divergence_test_element, divergence_transformation, utils::DOMAINS::ALL, quadrature::MappingKind::none);
|
||||
b_preconditioner.AddDomainIntegrator(divergence_preconditioner_integrator.release());
|
||||
f.quadratureFactory->configure_gravity_divergence(
|
||||
*divergence_preconditioner_integrator,
|
||||
quadrature::QuadratureRole::preconditioner,
|
||||
divergence_trial_element, divergence_test_element,
|
||||
divergence_transformation, utils::DOMAINS::ALL,
|
||||
quadrature::MappingKind::none
|
||||
);
|
||||
b_preconditioner.AddDomainIntegrator(
|
||||
divergence_preconditioner_integrator.release()
|
||||
);
|
||||
b_preconditioner.Assemble();
|
||||
b_preconditioner.Finalize();
|
||||
std::unique_ptr<mfem::HypreParMatrix> b_matrix(b_preconditioner.ParallelAssemble());
|
||||
std::unique_ptr<mfem::HypreParMatrix> inverse_mass_b_transpose(b_matrix->Transpose());
|
||||
std::unique_ptr<mfem::HypreParMatrix> b_matrix(
|
||||
b_preconditioner.ParallelAssemble()
|
||||
);
|
||||
std::unique_ptr<mfem::HypreParMatrix> inverse_mass_b_transpose(
|
||||
b_matrix->Transpose()
|
||||
);
|
||||
|
||||
inverse_mass_b_transpose->ScaleRows(inverse_mass_diagonal);
|
||||
|
||||
f.gravity_context.Schur.reset(mfem::ParMult(b_matrix.get(), inverse_mass_b_transpose.get()));
|
||||
f.gravityContext.Schur.reset(
|
||||
mfem::ParMult(b_matrix.get(), inverse_mass_b_transpose.get())
|
||||
);
|
||||
|
||||
// ==========================================
|
||||
// 5. Wire Up the preconditioners
|
||||
// ==========================================
|
||||
f.gravity_context.prec_M = std::make_unique<mfem::OperatorJacobiSmoother>(mass_diagonal, empty_tdofs);
|
||||
f.gravity_context.prec_Phi->SetOperator(*f.gravity_context.Schur);
|
||||
f.gravity_context.block_prec->SetDiagonalBlock(0, f.gravity_context.prec_M.get());
|
||||
f.gravity_context.block_prec->SetDiagonalBlock(1, f.gravity_context.prec_Phi.get());
|
||||
f.gravityContext.prec_M =
|
||||
std::make_unique<mfem::OperatorJacobiSmoother>(
|
||||
mass_diagonal, empty_tdofs
|
||||
);
|
||||
f.gravityContext.prec_Phi->SetOperator(*f.gravityContext.Schur);
|
||||
f.gravityContext.block_prec->SetDiagonalBlock(
|
||||
0, f.gravityContext.prec_M.get()
|
||||
);
|
||||
f.gravityContext.block_prec->SetDiagonalBlock(
|
||||
1, f.gravityContext.prec_Phi.get()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
GravitySolution grav_potential_new(
|
||||
fem::FEM &f,
|
||||
const utils::Args &args,
|
||||
const mfem::GridFunction &rho,
|
||||
const mfem::GridFunction &displacement
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
f.mesh != nullptr,
|
||||
"Gravity initialization requires a parallel mesh."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.densityFes != nullptr,
|
||||
"Gravity initialization requires the density finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityPotentialFes != nullptr,
|
||||
"Gravity initialization requires the gravity-potential "
|
||||
"finite-element "
|
||||
"space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityFluxFes != nullptr,
|
||||
"Gravity initialization requires the "
|
||||
"gravity-gradient finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.displacementFes != nullptr, "Gravity initialization requires the "
|
||||
"displacement finite-element space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.domainMapperStateless != nullptr,
|
||||
"Gravity initialization requires the stateless domain mapper."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityContext.b_form != nullptr,
|
||||
"Gravity initialization requires the divergence operator."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityContext.BT != nullptr,
|
||||
"Gravity initialization requires the transpose divergence operator."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
f.gravityContext.block_prec != nullptr,
|
||||
"Gravity initialization requires the gravity block preconditioner."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
rho.FESpace() == f.densityFes.get(),
|
||||
"Gravity initialization requires density to use the FEM density "
|
||||
"space."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
displacement.FESpace() == f.displacementFes.get(),
|
||||
"Gravity initialization requires displacement to use the FEM "
|
||||
"Vec_H1 "
|
||||
"space."
|
||||
);
|
||||
|
||||
using form = utils::blocks::gravity_field_form;
|
||||
|
||||
constexpr auto gravity_gradient_residual_block =
|
||||
utils::blocks::get_residual_block<form>(
|
||||
utils::blocks::gravity_field.gradient_term
|
||||
);
|
||||
|
||||
constexpr auto gravity_poisson_residual_block =
|
||||
utils::blocks::get_residual_block<form>(
|
||||
utils::blocks::gravity_field.poisson_term
|
||||
);
|
||||
|
||||
const std::array<int, form::value_block_count> value_sizes{
|
||||
f.densityFes->GetTrueVSize(), f.displacementFes->GetTrueVSize(),
|
||||
f.gravityFluxFes->GetTrueVSize(),
|
||||
f.gravityPotentialFes->GetTrueVSize()
|
||||
};
|
||||
|
||||
const std::array<int, form::residual_block_count> residual_sizes{
|
||||
f.gravityFluxFes->GetTrueVSize(),
|
||||
f.gravityPotentialFes->GetTrueVSize()
|
||||
};
|
||||
|
||||
const utils::blocks::form_layout<form> layout(
|
||||
value_sizes, residual_sizes
|
||||
);
|
||||
|
||||
mfem::Vector density_true;
|
||||
mfem::Vector displacement_true;
|
||||
|
||||
grid_function_to_true_dofs(*f.densityFes, rho, density_true);
|
||||
grid_function_to_true_dofs(
|
||||
*f.displacementFes, displacement, displacement_true
|
||||
);
|
||||
|
||||
operators::context::gravity_field::GravityFieldLinearizationContext
|
||||
linearization_context(f, *f.domainMapperStateless);
|
||||
|
||||
operators::GravityFieldJacobianOperator gravity_jacobian(
|
||||
f, *f.domainMapperStateless, linearization_context,
|
||||
layout.value_offsets(), layout.residual_offsets()
|
||||
);
|
||||
|
||||
operators::GravityFieldOperator gravity_operator(
|
||||
f, *f.domainMapperStateless, linearization_context,
|
||||
layout.value_offsets(), gravity_jacobian
|
||||
);
|
||||
|
||||
operators::context::gravity_field::GravityFieldGeometryContext
|
||||
reduced_geometry_context(f, *f.domainMapperStateless);
|
||||
|
||||
operators::ReducedGravityFieldOperator reduced_operator(
|
||||
gravity_operator, reduced_geometry_context, displacement_true
|
||||
);
|
||||
|
||||
mfem::Vector right_hand_side;
|
||||
reduced_operator.BuildRightHandSide(density_true, right_hand_side);
|
||||
|
||||
MFEM_VERIFY(
|
||||
right_hand_side.Size() == reduced_operator.Height(),
|
||||
"The reduced gravity right-hand side has the wrong size."
|
||||
);
|
||||
|
||||
mfem::BlockVector gravity_state(
|
||||
reduced_operator.GetGravityTrueOffsets()
|
||||
);
|
||||
gravity_state = 0.0;
|
||||
|
||||
mfem::MINRESSolver minres(f.mesh->GetComm());
|
||||
minres.SetOperator(reduced_operator);
|
||||
minres.SetPreconditioner(*f.gravityContext.block_prec);
|
||||
minres.SetRelTol(args.p.rtol);
|
||||
minres.SetAbsTol(args.p.atol);
|
||||
minres.SetMaxIter(args.p.max_iters);
|
||||
minres.SetPrintLevel(1);
|
||||
minres.Mult(right_hand_side, gravity_state);
|
||||
|
||||
MFEM_VERIFY(
|
||||
minres.GetConverged(),
|
||||
"The reduced gravity solve failed to converge."
|
||||
);
|
||||
|
||||
GravitySolution solution(f);
|
||||
|
||||
solution.gradPhi.SetFromTrueDofs(
|
||||
gravity_state.GetBlock(gravity_gradient_residual_block)
|
||||
);
|
||||
|
||||
solution.phi.SetFromTrueDofs(
|
||||
gravity_state.GetBlock(gravity_poisson_residual_block)
|
||||
);
|
||||
|
||||
return solution;
|
||||
}
|
||||
} // namespace mean_field::physics
|
||||
|
||||
Reference in New Issue
Block a user