280 lines
13 KiB
C++
280 lines
13 KiB
C++
module;
|
|
#include <mfem.hpp>
|
|
|
|
module mean_field;
|
|
import :operators.gravity_field_jacobian;
|
|
import :utils.blocks;
|
|
|
|
namespace {
|
|
template <int index>
|
|
mfem::Vector make_read_only_value_view(
|
|
const mfem::Vector &vector,
|
|
const mfem::Array<int> &offsets,
|
|
const mean_field::utils::blocks::value_block<index>
|
|
) {
|
|
const int offset = offsets[index];
|
|
const int size = offsets[index + 1] - offset;
|
|
return mfem::Vector(const_cast<mfem::real_t *>(vector.GetData()) + offset, size);
|
|
}
|
|
|
|
template <int index>
|
|
mfem::Vector make_residual_view(
|
|
mfem::Vector &vector,
|
|
const mfem::Array<int> &offsets,
|
|
const mean_field::utils::blocks::residual_block<index>
|
|
) {
|
|
const int offset = offsets[index];
|
|
const int size = offsets[index + 1] - offset;
|
|
return mfem::Vector(vector.GetData() + offset, size);
|
|
}
|
|
|
|
template <int index>
|
|
int get_block_size(
|
|
const mfem::Array<int> &offsets,
|
|
const mean_field::utils::blocks::value_block<index>
|
|
) {
|
|
return offsets[index + 1] - offsets[index];
|
|
}
|
|
|
|
template <int index>
|
|
int get_block_size(
|
|
const mfem::Array<int> &offsets,
|
|
const mean_field::utils::blocks::residual_block<index>
|
|
) {
|
|
return offsets[index + 1] - offsets[index];
|
|
}
|
|
|
|
void validate_offsets(
|
|
const mfem::Array<int> &offsets,
|
|
const int block_count,
|
|
const char *message
|
|
) {
|
|
MFEM_VERIFY(offsets.Size() == block_count + 1, message);
|
|
MFEM_VERIFY(offsets[0] == 0, "Block offsets must begin at zero.");
|
|
|
|
for (int i = 0; i < block_count; ++i)
|
|
MFEM_VERIFY(offsets[i + 1] >= offsets[i], "Block offsets must be nondecreasing.");
|
|
}
|
|
|
|
void validate_layout(
|
|
const mean_field::fem::FEM &f,
|
|
const mfem::Array<int> &state_offsets,
|
|
const mfem::Array<int> &residual_offsets
|
|
) {
|
|
using form = mean_field::utils::blocks::gravity_field_form;
|
|
|
|
constexpr auto density_block =
|
|
mean_field::utils::blocks::get_value_block<form>(mean_field::utils::blocks::density_field.mass_term);
|
|
constexpr auto displacement_block = mean_field::utils::blocks::get_value_block<form>(
|
|
mean_field::utils::blocks::displacement_field.geometry_term
|
|
);
|
|
constexpr auto gravity_gradient_block =
|
|
mean_field::utils::blocks::get_value_block<form>(mean_field::utils::blocks::gravity_field.gradient_term);
|
|
constexpr auto gravity_potential_block =
|
|
mean_field::utils::blocks::get_value_block<form>(mean_field::utils::blocks::gravity_field.poisson_term);
|
|
constexpr auto gravity_gradient_residual_block =
|
|
mean_field::utils::blocks::get_residual_block<form>(mean_field::utils::blocks::gravity_field.gradient_term);
|
|
constexpr auto gravity_poisson_residual_block =
|
|
mean_field::utils::blocks::get_residual_block<form>(mean_field::utils::blocks::gravity_field.poisson_term);
|
|
|
|
validate_offsets(
|
|
state_offsets, form::value_block_count,
|
|
"Gravity Jacobian state offsets do not match the gravity field "
|
|
"form."
|
|
);
|
|
validate_offsets(
|
|
residual_offsets, form::residual_block_count,
|
|
"Gravity Jacobian residual offsets do not match the gravity field "
|
|
"form."
|
|
);
|
|
|
|
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
|
const auto density_map =
|
|
mean_field::field::make_field_dof_map<mean_field::field::Density, DomainSchema>(*f.densityFes);
|
|
const auto displacement_map =
|
|
mean_field::field::make_field_dof_map<mean_field::field::Displacement, DomainSchema>(*f.displacementFes);
|
|
const auto flux_map =
|
|
mean_field::field::make_field_dof_map<mean_field::field::Gravity, DomainSchema>(*f.gravityFluxFes);
|
|
const auto potential_map =
|
|
mean_field::field::make_field_dof_map<mean_field::field::Gravity, DomainSchema>(*f.gravityPotentialFes);
|
|
|
|
MFEM_VERIFY(
|
|
get_block_size(state_offsets, density_block) == density_map.reduced_size(),
|
|
"The Jacobian density block has the wrong size."
|
|
);
|
|
MFEM_VERIFY(
|
|
get_block_size(state_offsets, displacement_block) == displacement_map.reduced_size(),
|
|
"The Jacobian displacement block has the wrong size."
|
|
);
|
|
MFEM_VERIFY(
|
|
get_block_size(state_offsets, gravity_gradient_block) == flux_map.reduced_size(),
|
|
"The Jacobian gravity-gradient block has the wrong size."
|
|
);
|
|
MFEM_VERIFY(
|
|
get_block_size(state_offsets, gravity_potential_block) == potential_map.reduced_size(),
|
|
"The Jacobian gravity-potential block has the wrong size."
|
|
);
|
|
MFEM_VERIFY(
|
|
get_block_size(residual_offsets, gravity_gradient_residual_block) == flux_map.reduced_size(),
|
|
"The Jacobian gradient-residual block has the wrong size."
|
|
);
|
|
MFEM_VERIFY(
|
|
get_block_size(residual_offsets, gravity_poisson_residual_block) == potential_map.reduced_size(),
|
|
"The Jacobian Poisson-residual block has the wrong size."
|
|
);
|
|
}
|
|
} // namespace
|
|
|
|
namespace mean_field::operators {
|
|
GravityFieldJacobianOperator::GravityFieldJacobianOperator(
|
|
fem::FEM &f,
|
|
const mapping::DomainMapper &domain_mapper,
|
|
const context::gravity_field::GravityFieldLinearizationContext &linearization_context,
|
|
const mfem::Array<int> &state_offsets,
|
|
const mfem::Array<int> &residual_offsets
|
|
)
|
|
: Operator(
|
|
residual_offsets.Last(),
|
|
state_offsets.Last()
|
|
),
|
|
m_fem(f),
|
|
m_domain_mapper(domain_mapper),
|
|
m_linearization_context(linearization_context),
|
|
m_state_offsets(state_offsets),
|
|
m_residual_offsets(residual_offsets) {
|
|
MFEM_VERIFY(
|
|
f.densityFes != nullptr, "GravityFieldJacobianOperator requires the density finite-element "
|
|
"space."
|
|
);
|
|
MFEM_VERIFY(
|
|
f.gravityPotentialFes != nullptr, "GravityFieldJacobianOperator requires the gravity-potential "
|
|
"finite-element space."
|
|
);
|
|
MFEM_VERIFY(
|
|
f.gravityFluxFes != nullptr, "GravityFieldJacobianOperator requires the "
|
|
"gravity-gradient finite-element space."
|
|
);
|
|
MFEM_VERIFY(
|
|
f.displacementFes != nullptr, "GravityFieldJacobianOperator requires the "
|
|
"displacement finite-element space."
|
|
);
|
|
MFEM_VERIFY(
|
|
f.quadratureFactory != nullptr, "GravityFieldJacobianOperator requires the quadrature-rule factory."
|
|
);
|
|
MFEM_VERIFY(
|
|
domain_mapper.GetDimension() == f.mesh->Dimension(),
|
|
"GravityFieldJacobianOperator received a domain mapper with the "
|
|
"wrong "
|
|
"dimension."
|
|
);
|
|
|
|
validate_layout(f, m_state_offsets, m_residual_offsets);
|
|
}
|
|
|
|
void GravityFieldJacobianOperator::Mult(
|
|
const mfem::Vector &direction,
|
|
mfem::Vector &action
|
|
) const {
|
|
MFEM_VERIFY(
|
|
m_linearization_context.IsPrepared(), "GravityFieldJacobianOperator requires a prepared linearization "
|
|
"context."
|
|
);
|
|
MFEM_VERIFY(
|
|
direction.Size() == Width(), "GravityFieldJacobianOperator received a direction with the wrong "
|
|
"size."
|
|
);
|
|
|
|
using form = utils::blocks::gravity_field_form;
|
|
|
|
constexpr auto density_block = utils::blocks::get_value_block<form>(utils::blocks::density_field.mass_term);
|
|
constexpr auto displacement_block =
|
|
utils::blocks::get_value_block<form>(utils::blocks::displacement_field.geometry_term);
|
|
constexpr auto gravity_gradient_block =
|
|
utils::blocks::get_value_block<form>(utils::blocks::gravity_field.gradient_term);
|
|
constexpr auto gravity_potential_block =
|
|
utils::blocks::get_value_block<form>(utils::blocks::gravity_field.poisson_term);
|
|
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 context::gravity_field::GravityFieldGeometryContext &geometry_context =
|
|
m_linearization_context.GetGeometryContext();
|
|
const mfem::Vector &density = m_linearization_context.GetDensityTrue();
|
|
const mfem::Vector &gravity_gradient = m_linearization_context.GetGravityGradientTrue();
|
|
|
|
const mfem::Vector density_direction = make_read_only_value_view(direction, m_state_offsets, density_block);
|
|
const mfem::Vector displacement_direction =
|
|
make_read_only_value_view(direction, m_state_offsets, displacement_block);
|
|
const mfem::Vector gravity_gradient_direction =
|
|
make_read_only_value_view(direction, m_state_offsets, gravity_gradient_block);
|
|
const mfem::Vector gravity_potential_direction =
|
|
make_read_only_value_view(direction, m_state_offsets, gravity_potential_block);
|
|
|
|
const field::FieldDofMap &displacement_map = m_linearization_context.GetDisplacementMap();
|
|
const field::FieldDofMap &flux_map = m_linearization_context.GetGravityGradientMap();
|
|
const field::FieldDofMap &potential_map = m_linearization_context.GetGravityPotentialMap();
|
|
|
|
mfem::Vector displacement_direction_true(displacement_map.full_size());
|
|
mfem::Vector gravity_gradient_direction_true(flux_map.full_size());
|
|
mfem::Vector gravity_potential_direction_true(potential_map.full_size());
|
|
displacement_map.scatter(displacement_direction, displacement_direction_true);
|
|
flux_map.scatter(gravity_gradient_direction, gravity_gradient_direction_true);
|
|
potential_map.scatter(gravity_potential_direction, gravity_potential_direction_true);
|
|
|
|
action.SetSize(Height());
|
|
action = 0.0;
|
|
|
|
mfem::Vector gravity_gradient_action =
|
|
make_residual_view(action, m_residual_offsets, gravity_gradient_residual_block);
|
|
mfem::Vector gravity_poisson_action =
|
|
make_residual_view(action, m_residual_offsets, gravity_poisson_residual_block);
|
|
|
|
mfem::Vector transpose_divergence_action_true;
|
|
mfem::Vector transpose_divergence_action(flux_map.reduced_size());
|
|
mfem::Vector divergence_action_true;
|
|
mfem::Vector source_action;
|
|
mfem::Vector mass_variation_action_true;
|
|
mfem::Vector mass_variation_action(flux_map.reduced_size());
|
|
mfem::Vector source_variation_action_true;
|
|
mfem::Vector source_variation_action(potential_map.reduced_size());
|
|
|
|
geometry_context.GetMassOperator().Mult(gravity_gradient_direction, gravity_gradient_action);
|
|
geometry_context.GetSourceOperator().Mult(density_direction, source_action);
|
|
|
|
geometry_context.GetMassOperator().MultDisplacementVariationTrue(
|
|
gravity_gradient, displacement_direction_true, mass_variation_action_true
|
|
);
|
|
flux_map.gather(mass_variation_action_true, mass_variation_action);
|
|
|
|
geometry_context.GetSourceOperator().MultDisplacementVariationTrue(
|
|
density, displacement_direction_true, source_variation_action_true
|
|
);
|
|
potential_map.gather(source_variation_action_true, source_variation_action);
|
|
|
|
transpose_divergence_action_true.SetSize(flux_map.full_size());
|
|
geometry_context.GetTransposeDivergenceOperator().Mult(
|
|
gravity_potential_direction_true, transpose_divergence_action_true
|
|
);
|
|
flux_map.gather(transpose_divergence_action_true, transpose_divergence_action);
|
|
|
|
gravity_gradient_action += transpose_divergence_action;
|
|
gravity_gradient_action += mass_variation_action;
|
|
|
|
divergence_action_true.SetSize(potential_map.full_size());
|
|
geometry_context.GetDivergenceOperator().Mult(gravity_gradient_direction_true, divergence_action_true);
|
|
potential_map.gather(divergence_action_true, gravity_poisson_action);
|
|
|
|
gravity_poisson_action -= source_action;
|
|
gravity_poisson_action -= source_variation_action;
|
|
|
|
gravity_gradient_action.SyncAliasMemory(action);
|
|
gravity_poisson_action.SyncAliasMemory(action);
|
|
}
|
|
|
|
const context::gravity_field::GravityFieldLinearizationContext &
|
|
GravityFieldJacobianOperator::GetLinearizationContext() const noexcept {
|
|
return m_linearization_context;
|
|
}
|
|
} // namespace mean_field::operators
|