Test normalized unbordered adapter and report its storage scope
This commit is contained in:
@@ -354,6 +354,8 @@ void BenchmarkPreconditioners(State &state, const std::filesystem::path &output,
|
|||||||
metric("setup_jacobian_seconds", stats.setupOperatorSeconds);
|
metric("setup_jacobian_seconds", stats.setupOperatorSeconds);
|
||||||
metric("setup_inverse_seconds", stats.setupInverseSeconds);
|
metric("setup_inverse_seconds", stats.setupInverseSeconds);
|
||||||
metric("persistent_vector_bytes", stats.persistentVectorBytes);
|
metric("persistent_vector_bytes", stats.persistentVectorBytes);
|
||||||
|
metric("unbordered_adapter_vector_bytes", sizeof(mfem::real_t) *
|
||||||
|
4 * static_cast<std::size_t>(state.normalizedOperator->Height()));
|
||||||
metric("applications", stats.applications);
|
metric("applications", stats.applications);
|
||||||
metric("internal_jacobian_applications", stats.operatorApplications);
|
metric("internal_jacobian_applications", stats.operatorApplications);
|
||||||
metric("internal_inverse_applications", stats.inverseApplications);
|
metric("internal_inverse_applications", stats.inverseApplications);
|
||||||
@@ -643,6 +645,7 @@ void BenchmarkPreconditioners(State &state, const std::filesystem::path &output,
|
|||||||
<< "The Schur partition uses every native surface parameter and generated scalar block, including a central border if present.\n"
|
<< "The Schur partition uses every native surface parameter and generated scalar block, including a central border if present.\n"
|
||||||
<< "Its field inverse restricts the unbordered structure inverse, not an exact field elimination.\n"
|
<< "Its field inverse restricts the unbordered structure inverse, not an exact field elimination.\n"
|
||||||
<< "Existing scalar-border construction is retained in setup cost but its inverse is not applied by the new wrapper.\n"
|
<< "Existing scalar-border construction is retained in setup cost but its inverse is not applied by the new wrapper.\n"
|
||||||
|
<< "The unbordered adapter is frozen at construction; rebuild it and the Schur wrapper for a new Jacobian.\n"
|
||||||
<< "setup_plus_solve_seconds includes construction, normalization binding, Krylov setup, and Krylov solve.\n"
|
<< "setup_plus_solve_seconds includes construction, normalization binding, Krylov setup, and Krylov solve.\n"
|
||||||
<< "setup_solve_and_true_check_seconds adds the separately measured original-J true-residual gate.\n"
|
<< "setup_solve_and_true_check_seconds adds the separately measured original-J true-residual gate.\n"
|
||||||
<< "measured_candidate_seconds also includes diagnostic refresh, coarse exactness checks, and inverse/Jv comparison.\n"
|
<< "measured_candidate_seconds also includes diagnostic refresh, coarse exactness checks, and inverse/Jv comparison.\n"
|
||||||
|
|||||||
@@ -45,7 +45,11 @@ namespace surface_border_schur_test {
|
|||||||
class Operation final : public mfem::Operator {
|
class Operation final : public mfem::Operator {
|
||||||
public:
|
public:
|
||||||
explicit Operation(mfem::DenseMatrix matrix)
|
explicit Operation(mfem::DenseMatrix matrix)
|
||||||
: mfem::Operator(matrix.Height()), matrix(std::move(matrix)) {}
|
: mfem::Operator(matrix.Height()), matrix(std::move(matrix)),
|
||||||
|
residualScales(Height()), stateScales(Width()) {
|
||||||
|
residualScales = 1.0;
|
||||||
|
stateScales = 1.0;
|
||||||
|
}
|
||||||
void Mult(const mfem::Vector &input, mfem::Vector &output) const override {
|
void Mult(const mfem::Vector &input, mfem::Vector &output) const override {
|
||||||
++applications;
|
++applications;
|
||||||
matrix.Mult(input, output);
|
matrix.Mult(input, output);
|
||||||
@@ -53,7 +57,16 @@ namespace surface_border_schur_test {
|
|||||||
bool IsPrepared() const { return prepared; }
|
bool IsPrepared() const { return prepared; }
|
||||||
const Operation &GetProblem() const { return *this; }
|
const Operation &GetProblem() const { return *this; }
|
||||||
std::uint64_t GetPreparationGeneration() const { return generation; }
|
std::uint64_t GetPreparationGeneration() const { return generation; }
|
||||||
|
void DenormalizeResidual(const mfem::Vector &input, mfem::Vector &output) const {
|
||||||
|
for (int i = 0; i < input.Size(); ++i)
|
||||||
|
output(i) = input(i) * residualScales(i);
|
||||||
|
}
|
||||||
|
void NormalizeState(const mfem::Vector &input, mfem::Vector &output) const {
|
||||||
|
for (int i = 0; i < input.Size(); ++i)
|
||||||
|
output(i) = input(i) / stateScales(i);
|
||||||
|
}
|
||||||
mfem::DenseMatrix matrix;
|
mfem::DenseMatrix matrix;
|
||||||
|
mfem::Vector residualScales, stateScales;
|
||||||
bool prepared{true};
|
bool prepared{true};
|
||||||
std::uint64_t generation{1};
|
std::uint64_t generation{1};
|
||||||
mutable std::uint64_t applications{0};
|
mutable std::uint64_t applications{0};
|
||||||
@@ -124,6 +137,7 @@ namespace surface_border_schur_test {
|
|||||||
|
|
||||||
namespace blocks = utils::blocks;
|
namespace blocks = utils::blocks;
|
||||||
struct StructureDescriptor {
|
struct StructureDescriptor {
|
||||||
|
using BackendType = preconditioning::backend::Diagonal;
|
||||||
using CorrectionBlocks = blocks::type_list<blocks::density::mass::value,
|
using CorrectionBlocks = blocks::type_list<blocks::density::mass::value,
|
||||||
blocks::surface_deformation::parameters::value, blocks::enthalpy::specific::value>;
|
blocks::surface_deformation::parameters::value, blocks::enthalpy::specific::value>;
|
||||||
using ResidualBlocks = blocks::type_list<blocks::enthalpy::specific::residual,
|
using ResidualBlocks = blocks::type_list<blocks::enthalpy::specific::residual,
|
||||||
@@ -147,6 +161,16 @@ namespace surface_border_schur_test {
|
|||||||
std::array<Range, 5> rows{{{6, 0, 1}, {2, 1, 3}, {0, 4, 2}, {5, 6, 1}, {7, 7, 1}}};
|
std::array<Range, 5> rows{{{6, 0, 1}, {2, 1, 3}, {0, 4, 2}, {5, 6, 1}, {7, 7, 1}}};
|
||||||
const auto &GetCorrectionRanges() const { return columns; }
|
const auto &GetCorrectionRanges() const { return columns; }
|
||||||
const auto &GetResidualRanges() const { return rows; }
|
const auto &GetResidualRanges() const { return rows; }
|
||||||
|
void PackResidual(const mfem::Vector &input, mfem::Vector &output) const {
|
||||||
|
for (const auto &range : rows)
|
||||||
|
for (int i = 0; i < range.size; ++i)
|
||||||
|
output(range.preconditionerOffset + i) = input(range.equilibriumOffset + i);
|
||||||
|
}
|
||||||
|
void UnpackCorrection(const mfem::Vector &input, mfem::Vector &output) const {
|
||||||
|
for (const auto &range : columns)
|
||||||
|
for (int i = 0; i < range.size; ++i)
|
||||||
|
output(range.equilibriumOffset + i) = input(range.preconditionerOffset + i);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
struct CoordinateBinding {
|
struct CoordinateBinding {
|
||||||
RootDescriptor root;
|
RootDescriptor root;
|
||||||
@@ -154,6 +178,73 @@ namespace surface_border_schur_test {
|
|||||||
const auto &GetBlock() const { return root; }
|
const auto &GetBlock() const { return root; }
|
||||||
const auto &GetCoordinateMap() const { return coordinates; }
|
const auto &GetCoordinateMap() const { return coordinates; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PackedStructure final : public mfem::Solver {
|
||||||
|
public:
|
||||||
|
PackedStructure() : mfem::Solver(6), matrix(6) {
|
||||||
|
matrix = 0.0;
|
||||||
|
for (int i = 0; i < 6; ++i)
|
||||||
|
matrix(i, i) = i + 2.0;
|
||||||
|
matrix(0, 5) = 0.4;
|
||||||
|
matrix(4, 1) = -0.6;
|
||||||
|
}
|
||||||
|
void SetOperator(const mfem::Operator &) override {}
|
||||||
|
void Mult(const mfem::Vector &input, mfem::Vector &output) const override {
|
||||||
|
++applications;
|
||||||
|
matrix.Mult(input, output);
|
||||||
|
}
|
||||||
|
mfem::DenseMatrix matrix;
|
||||||
|
mutable unsigned applications{0};
|
||||||
|
};
|
||||||
|
struct GroupedStructure {
|
||||||
|
PackedStructure structure;
|
||||||
|
const auto &GetStructurePreconditioner() const { return structure; }
|
||||||
|
};
|
||||||
|
class PhysicalStructureBinding final : public mfem::Solver, public CoordinateBinding {
|
||||||
|
public:
|
||||||
|
static constexpr auto applicationContract = preconditioning::ApplicationContract::stationary_linear;
|
||||||
|
explicit PhysicalStructureBinding(const Operation &operation)
|
||||||
|
: mfem::Solver(operation.Width()), operation(operation), generation(operation.generation) {}
|
||||||
|
void SetOperator(const mfem::Operator &) override {}
|
||||||
|
void Mult(const mfem::Vector &, mfem::Vector &) const override {
|
||||||
|
throw std::logic_error("The adapter must not apply the scalar-bordered inverse.");
|
||||||
|
}
|
||||||
|
const auto &GetProblem() const { return operation; }
|
||||||
|
const auto &GetGroupedPreconditioner() const { return grouped; }
|
||||||
|
bool IsCurrent() const { return generation == operation.generation; }
|
||||||
|
const Operation &operation;
|
||||||
|
std::uint64_t generation;
|
||||||
|
GroupedStructure grouped;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Surface Border Schur Structure Adapter Preserves Nonunit Normalization And Typed Packing",
|
||||||
|
"[solver][surface_border_schur][serial]") {
|
||||||
|
using namespace surface_border_schur_test;
|
||||||
|
mfem::DenseMatrix matrix(8);
|
||||||
|
matrix = 0.0;
|
||||||
|
Operation operation(std::move(matrix));
|
||||||
|
operation.residualScales = Vector({2, 3, 5, 7, 11, 13, 17, 19});
|
||||||
|
operation.stateScales = Vector({23, 29, 31, 37, 41, 43, 47, 53});
|
||||||
|
PhysicalStructureBinding physical(operation);
|
||||||
|
probe::NormalizedUnborderedStructureInverse adapter(operation, physical);
|
||||||
|
STATIC_CHECK(probe::SurfaceBorderSchurInverse<decltype(adapter)>);
|
||||||
|
auto rhs = Vector({1, 2, 3, 4, 5, 6, 7, 8});
|
||||||
|
const auto expected = Vector({42.0 / 23, 112.0 / 29, 275.0 / 31, 3.0 / 37,
|
||||||
|
0, 240.4 / 43, 45.0 / 47, 0});
|
||||||
|
mfem::Vector observed(8);
|
||||||
|
adapter.Mult(rhs, observed);
|
||||||
|
CHECK(Difference(observed, expected) < 1.e-13);
|
||||||
|
CHECK(physical.grouped.structure.applications == 1);
|
||||||
|
adapter.Mult(rhs, rhs);
|
||||||
|
CHECK(Difference(rhs, expected) < 1.e-13);
|
||||||
|
physical.grouped.structure.iterative_mode = true;
|
||||||
|
CHECK_FALSE(adapter.IsCurrent());
|
||||||
|
physical.grouped.structure.iterative_mode = false;
|
||||||
|
++operation.generation;
|
||||||
|
physical.generation = operation.generation;
|
||||||
|
CHECK_FALSE(adapter.IsCurrent());
|
||||||
|
CHECK_THROWS_AS(adapter.Mult(rhs, observed), std::logic_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Surface Border Schur Coordinates Include All Native Modes And Generated Central Border",
|
TEST_CASE("Surface Border Schur Coordinates Include All Native Modes And Generated Central Border",
|
||||||
|
|||||||
Reference in New Issue
Block a user