#include #include #include #include #include #include #include import mean_field; import test_helpers; using namespace mean_field; using Catch::Matchers::WithinAbs; namespace { constexpr int dimension = 3; mfem::Vector make_vector( const double x, const double y, const double z ) { mfem::Vector vector(dimension); vector(0) = x; vector(1) = y; vector(2) = z; return vector; } mfem::DenseMatrix make_identity() { mfem::DenseMatrix matrix(dimension); matrix = 0.0; for (int i = 0; i < dimension; ++i) matrix(i, i) = 1.0; return matrix; } void check_vector( const mfem::Vector &actual, const mfem::Vector &expected, const double tolerance ) { REQUIRE(actual.Size() == expected.Size()); for (int i = 0; i < actual.Size(); ++i) CHECK_THAT(actual(i), WithinAbs(expected(i), tolerance)); } void check_matrix( const mfem::DenseMatrix &actual, const mfem::DenseMatrix &expected, const double tolerance ) { REQUIRE(actual.Height() == expected.Height()); REQUIRE(actual.Width() == expected.Width()); for (int i = 0; i < actual.Height(); ++i) { for (int j = 0; j < actual.Width(); ++j) CHECK_THAT(actual(i, j), WithinAbs(expected(i, j), tolerance)); } } struct AnalyticFactors { double computational_radius; double scale; double scale_derivative; }; AnalyticFactors compute_analytic_factors( const double r_star, const double r_inf, const double coordinate ) { const double radial_extent = r_inf - r_star; const double computational_radius = r_star + coordinate * radial_extent; const double scale = r_star / (computational_radius * (1.0 - coordinate)); const double scale_derivative = scale * (1.0 / (1.0 - coordinate) - radial_extent / computational_radius); return { .computational_radius = computational_radius, .scale = scale, .scale_derivative = scale_derivative }; } mapping::MappingStatus evaluate_affine_map( const mapping::compactification::ExteriorDomainMap &exterior_map, const mfem::Vector &reference_position, const mfem::DenseMatrix &affine_jacobian, const mfem::Vector &offset, const double compactification_coordinate, const mfem::Vector &compactification_coordinate_gradient, mapping::compactification::ExteriorMapResult &result ) { mfem::Vector displaced_position(dimension); affine_jacobian.Mult(reference_position, displaced_position); displaced_position += offset; const mapping::compactification::ExteriorMapInput input{ .reference_position = reference_position, .displaced_position = displaced_position, .displacement_jacobian = affine_jacobian, .compactification_coordinate = compactification_coordinate, .compactification_coordinate_gradient = compactification_coordinate_gradient }; return exterior_map.Evaluate(input, result); } } // namespace TEST_CASE( "Kelvin Compactification Validates Its Configuration", tags::unit &tags::mapping &tags::kelvin ) { CHECK_NOTHROW( mapping::compactification::KelvinCompactification( {.r_star_ref = 1.0, .r_inf_ref = 4.0} ) ); CHECK_THROWS_AS( mapping::compactification::KelvinCompactification( {.r_star_ref = 0.0, .r_inf_ref = 4.0} ), std::invalid_argument ); CHECK_THROWS_AS( mapping::compactification::KelvinCompactification( {.r_star_ref = -1.0, .r_inf_ref = 4.0} ), std::invalid_argument ); CHECK_THROWS_AS( mapping::compactification::KelvinCompactification( {.r_star_ref = 2.0, .r_inf_ref = 2.0} ), std::invalid_argument ); CHECK_THROWS_AS( mapping::compactification::KelvinCompactification( {.r_star_ref = 3.0, .r_inf_ref = 2.0} ), std::invalid_argument ); CHECK_THROWS_AS( mapping::compactification::KelvinCompactification( {.r_star_ref = 1.0, .r_inf_ref = std::numeric_limits::infinity()} ), std::invalid_argument ); CHECK_THROWS_AS( mapping::compactification::KelvinCompactification( {.r_star_ref = 1.0, .r_inf_ref = 4.0, .coordinate_tolerance = -1.0e-12} ), std::invalid_argument ); CHECK_THROWS_AS( mapping::compactification::KelvinCompactification( {.r_star_ref = 1.0, .r_inf_ref = 4.0, .coordinate_tolerance = 1.0} ), std::invalid_argument ); CHECK_THROWS_AS( mapping::compactification::KelvinCompactification( {.r_star_ref = 1.0, .r_inf_ref = 4.0, .coordinate_tolerance = std::numeric_limits::quiet_NaN()} ), std::invalid_argument ); } TEST_CASE( "Kelvin Compactification Reports Its Configuration", tags::unit &tags::mapping &tags::kelvin ) { constexpr double coordinate_tolerance = 3.0e-11; mapping::compactification::KelvinCompactification compactification( {.r_star_ref = 1.25, .r_inf_ref = 5.5, .coordinate_tolerance = coordinate_tolerance} ); CHECK(compactification.GetName() == "KelvinCompactification"); CHECK_THAT( compactification.GetReferenceStellarRadius(), WithinAbs(1.25, 0.0) ); CHECK_THAT( compactification.GetReferenceInfinityRadius(), WithinAbs(5.5, 0.0) ); CHECK_THAT( compactification.GetCoordinateTolerance(), WithinAbs(coordinate_tolerance, 0.0) ); } TEST_CASE( "Kelvin Compactification Uses The Exterior Coordinate Rather Than " "Euclidean Radius", tags::unit &tags::mapping &tags::kelvin ) { constexpr double tolerance = 0.0; constexpr double coordinate = 0.37; mapping::compactification::KelvinCompactification compactification( {.r_star_ref = 1.0, .r_inf_ref = 4.0} ); const mfem::DenseMatrix displacement_jacobian = make_identity(); const mfem::Vector displaced_position = make_vector(1.4, -0.2, 0.3); const mfem::Vector coordinate_gradient = make_vector(0.2, -0.1, 0.05); const mfem::Vector reference_a = make_vector(0.2, 0.1, -0.1); const mfem::Vector reference_b = make_vector(12.0, -7.0, 4.0); const mapping::compactification::ExteriorMapInput input_a{ reference_a, displaced_position, displacement_jacobian, coordinate, coordinate_gradient }; const mapping::compactification::ExteriorMapInput input_b{ reference_b, displaced_position, displacement_jacobian, coordinate, coordinate_gradient }; mapping::compactification::ExteriorMapResult result_a; mapping::compactification::ExteriorMapResult result_b; REQUIRE( compactification.Evaluate(input_a, result_a) == mapping::MappingStatus::valid ); REQUIRE( compactification.Evaluate(input_b, result_b) == mapping::MappingStatus::valid ); check_vector( result_a.physical_position, result_b.physical_position, tolerance ); check_matrix( result_a.mapping_jacobian, result_b.mapping_jacobian, tolerance ); } TEST_CASE( "Kelvin Compactification Matches Its Analytic Radial Map", tags::unit &tags::mapping &tags::kelvin ) { constexpr double r_star = 1.0; constexpr double r_inf = 4.0; constexpr double radial_extent = r_inf - r_star; constexpr double tolerance = 2.0e-12; mapping::compactification::KelvinCompactification compactification( {.r_star_ref = r_star, .r_inf_ref = r_inf} ); const mfem::DenseMatrix identity = make_identity(); const mfem::Vector coordinate_gradient = make_vector(1.0 / radial_extent, 0.0, 0.0); for (const double computational_radius : std::array{1.0, 1.25, 2.0, 3.0, 3.75}) { CAPTURE(computational_radius); const double coordinate = (computational_radius - r_star) / radial_extent; const mfem::Vector reference_position = make_vector(computational_radius, 0.0, 0.0); const mfem::Vector displaced_position(reference_position); const mapping::compactification::ExteriorMapInput input{ reference_position, displaced_position, identity, coordinate, coordinate_gradient }; mapping::compactification::ExteriorMapResult result; REQUIRE( compactification.Evaluate(input, result) == mapping::MappingStatus::valid ); const double expected_radius = r_star * radial_extent / (r_inf - computational_radius); const double expected_radial_derivative = r_star * radial_extent / std::pow(r_inf - computational_radius, 2.0); const double expected_tangential_scale = expected_radius / computational_radius; const mfem::Vector expected_position = make_vector(expected_radius, 0.0, 0.0); mfem::DenseMatrix expected_jacobian(dimension); expected_jacobian = 0.0; expected_jacobian(0, 0) = expected_radial_derivative; expected_jacobian(1, 1) = expected_tangential_scale; expected_jacobian(2, 2) = expected_tangential_scale; check_vector(result.physical_position, expected_position, tolerance); check_matrix(result.mapping_jacobian, expected_jacobian, tolerance); CHECK(result.mapping_jacobian.Det() > 0.0); } } TEST_CASE( "Kelvin Compactification Matches Its Full Cartesian Formula", tags::unit &tags::mapping &tags::kelvin ) { constexpr double r_star = 1.0; constexpr double r_inf = 4.0; constexpr double coordinate = 0.42; constexpr double tolerance = 1.0e-12; mapping::compactification::KelvinCompactification compactification( {.r_star_ref = r_star, .r_inf_ref = r_inf} ); const mfem::Vector reference_position = make_vector(0.8, 0.4, -0.2); const mfem::Vector displaced_position = make_vector(1.1, 0.5, -0.1); const mfem::Vector coordinate_gradient = make_vector(0.20, -0.10, 0.05); mfem::DenseMatrix displacement_jacobian(dimension); displacement_jacobian(0, 0) = 1.10; displacement_jacobian(0, 1) = 0.05; displacement_jacobian(0, 2) = 0.00; displacement_jacobian(1, 0) = -0.02; displacement_jacobian(1, 1) = 0.95; displacement_jacobian(1, 2) = 0.03; displacement_jacobian(2, 0) = 0.01; displacement_jacobian(2, 1) = -0.04; displacement_jacobian(2, 2) = 1.05; const mapping::compactification::ExteriorMapInput input{ reference_position, displaced_position, displacement_jacobian, coordinate, coordinate_gradient }; mapping::compactification::ExteriorMapResult result; REQUIRE( compactification.Evaluate(input, result) == mapping::MappingStatus::valid ); const AnalyticFactors factors = compute_analytic_factors(r_star, r_inf, coordinate); mfem::Vector expected_position(displaced_position); expected_position *= factors.scale; mfem::DenseMatrix expected_jacobian(displacement_jacobian); expected_jacobian *= factors.scale; for (int i = 0; i < dimension; ++i) { for (int j = 0; j < dimension; ++j) expected_jacobian(i, j) += displaced_position(i) * factors.scale_derivative * coordinate_gradient(j); } check_vector(result.physical_position, expected_position, tolerance); check_matrix(result.mapping_jacobian, expected_jacobian, tolerance); } TEST_CASE( "Kelvin Compactification Jacobian Matches Coordinate Finite Differences", tags::unit &tags::mapping &tags::kelvin ) { constexpr double base_coordinate = 0.38; constexpr double difference_step = 1.0e-6; constexpr double tolerance = 3.0e-9; mapping::compactification::KelvinCompactification compactification( {.r_star_ref = 1.0, .r_inf_ref = 4.0} ); mfem::DenseMatrix affine_jacobian(dimension); affine_jacobian(0, 0) = 1.05; affine_jacobian(0, 1) = 0.02; affine_jacobian(0, 2) = 0.00; affine_jacobian(1, 0) = -0.01; affine_jacobian(1, 1) = 0.98; affine_jacobian(1, 2) = 0.03; affine_jacobian(2, 0) = 0.02; affine_jacobian(2, 1) = 0.00; affine_jacobian(2, 2) = 1.02; const mfem::Vector offset = make_vector(0.04, -0.03, 0.02); const mfem::Vector reference_position = make_vector(1.4, 0.3, -0.2); const mfem::Vector coordinate_gradient = make_vector(0.11, -0.07, 0.05); mapping::compactification::ExteriorMapResult base_result; REQUIRE( evaluate_affine_map( compactification, reference_position, affine_jacobian, offset, base_coordinate, coordinate_gradient, base_result ) == mapping::MappingStatus::valid ); for (int coordinate = 0; coordinate < dimension; ++coordinate) { mfem::Vector reference_plus(reference_position); mfem::Vector reference_minus(reference_position); reference_plus(coordinate) += difference_step; reference_minus(coordinate) -= difference_step; const double compactification_plus = base_coordinate + difference_step * coordinate_gradient(coordinate); const double compactification_minus = base_coordinate - difference_step * coordinate_gradient(coordinate); mapping::compactification::ExteriorMapResult result_plus; mapping::compactification::ExteriorMapResult result_minus; REQUIRE( evaluate_affine_map( compactification, reference_plus, affine_jacobian, offset, compactification_plus, coordinate_gradient, result_plus ) == mapping::MappingStatus::valid ); REQUIRE( evaluate_affine_map( compactification, reference_minus, affine_jacobian, offset, compactification_minus, coordinate_gradient, result_minus ) == mapping::MappingStatus::valid ); for (int component = 0; component < dimension; ++component) { const double finite_difference = (result_plus.physical_position(component) - result_minus.physical_position(component)) / (2.0 * difference_step); CHECK_THAT( finite_difference, WithinAbs( base_result.mapping_jacobian(component, coordinate), tolerance ) ); } } } TEST_CASE( "Kelvin Compactification Variation Matches State Finite Differences", tags::unit &tags::mapping &tags::kelvin ) { constexpr double coordinate = 0.46; constexpr double difference_step = 1.0e-6; constexpr double tolerance = 2.0e-10; mapping::compactification::KelvinCompactification compactification( {.r_star_ref = 1.0, .r_inf_ref = 4.0} ); const mfem::Vector reference_position = make_vector(1.3, -0.2, 0.4); const mfem::Vector displaced_position = make_vector(1.4, -0.1, 0.35); const mfem::Vector coordinate_gradient = make_vector(0.12, -0.04, 0.08); const mfem::Vector position_direction = make_vector(0.03, -0.05, 0.02); mfem::DenseMatrix displacement_jacobian = make_identity(); displacement_jacobian(0, 1) = 0.04; displacement_jacobian(1, 2) = -0.03; displacement_jacobian(2, 0) = 0.02; mfem::DenseMatrix jacobian_direction(dimension); jacobian_direction(0, 0) = 0.02; jacobian_direction(0, 1) = -0.01; jacobian_direction(0, 2) = 0.03; jacobian_direction(1, 0) = 0.01; jacobian_direction(1, 1) = -0.02; jacobian_direction(1, 2) = 0.00; jacobian_direction(2, 0) = -0.01; jacobian_direction(2, 1) = 0.02; jacobian_direction(2, 2) = 0.01; const mapping::compactification::ExteriorMapInput input{ reference_position, displaced_position, displacement_jacobian, coordinate, coordinate_gradient }; const mapping::compactification::ExteriorMapDirection direction{ position_direction, jacobian_direction }; mapping::compactification::ExteriorMapResult base_result; mapping::compactification::ExteriorMapVariation variation; REQUIRE( compactification.Evaluate(input, base_result) == mapping::MappingStatus::valid ); REQUIRE( compactification.EvaluateVariation( input, base_result, direction, variation ) == mapping::MappingStatus::valid ); mfem::Vector displaced_plus(displaced_position); mfem::Vector displaced_minus(displaced_position); displaced_plus.Add(difference_step, position_direction); displaced_minus.Add(-difference_step, position_direction); mfem::DenseMatrix jacobian_plus(displacement_jacobian); mfem::DenseMatrix jacobian_minus(displacement_jacobian); jacobian_plus.Add(difference_step, jacobian_direction); jacobian_minus.Add(-difference_step, jacobian_direction); const mapping::compactification::ExteriorMapInput input_plus{ reference_position, displaced_plus, jacobian_plus, coordinate, coordinate_gradient }; const mapping::compactification::ExteriorMapInput input_minus{ reference_position, displaced_minus, jacobian_minus, coordinate, coordinate_gradient }; mapping::compactification::ExteriorMapResult result_plus; mapping::compactification::ExteriorMapResult result_minus; REQUIRE( compactification.Evaluate(input_plus, result_plus) == mapping::MappingStatus::valid ); REQUIRE( compactification.Evaluate(input_minus, result_minus) == mapping::MappingStatus::valid ); for (int i = 0; i < dimension; ++i) { const double position_finite_difference = (result_plus.physical_position(i) - result_minus.physical_position(i)) / (2.0 * difference_step); CHECK_THAT( position_finite_difference, WithinAbs(variation.physical_position_variation(i), tolerance) ); for (int j = 0; j < dimension; ++j) { const double jacobian_finite_difference = (result_plus.mapping_jacobian(i, j) - result_minus.mapping_jacobian(i, j)) / (2.0 * difference_step); CHECK_THAT( jacobian_finite_difference, WithinAbs(variation.mapping_jacobian_variation(i, j), tolerance) ); } } } TEST_CASE( "Kelvin Compactification Preserves Rotational Covariance", tags::unit &tags::mapping &tags::kelvin ) { constexpr double coordinate = 0.31; constexpr double tolerance = 1.0e-12; mapping::compactification::KelvinCompactification compactification( {.r_star_ref = 1.0, .r_inf_ref = 4.0} ); const mfem::Vector reference_position = make_vector(1.3, 0.4, -0.2); const mfem::Vector displaced_position = make_vector(1.4, 0.2, -0.1); const mfem::Vector coordinate_gradient = make_vector(0.16, -0.08, 0.03); mfem::DenseMatrix displacement_jacobian = make_identity(); displacement_jacobian(0, 1) = 0.05; displacement_jacobian(1, 0) = -0.02; displacement_jacobian(2, 1) = 0.03; const mapping::compactification::ExteriorMapInput input{ reference_position, displaced_position, displacement_jacobian, coordinate, coordinate_gradient }; mapping::compactification::ExteriorMapResult result; REQUIRE( compactification.Evaluate(input, result) == mapping::MappingStatus::valid ); mfem::DenseMatrix rotation(dimension); rotation = 0.0; rotation(0, 1) = -1.0; rotation(1, 0) = 1.0; rotation(2, 2) = 1.0; mfem::Vector rotated_reference(dimension); mfem::Vector rotated_displaced(dimension); mfem::Vector rotated_coordinate_gradient(dimension); rotation.Mult(reference_position, rotated_reference); rotation.Mult(displaced_position, rotated_displaced); rotation.Mult(coordinate_gradient, rotated_coordinate_gradient); mfem::DenseMatrix temporary(dimension); mfem::DenseMatrix rotated_displacement_jacobian(dimension); mfem::Mult(rotation, displacement_jacobian, temporary); mfem::MultABt(temporary, rotation, rotated_displacement_jacobian); const mapping::compactification::ExteriorMapInput rotated_input{ rotated_reference, rotated_displaced, rotated_displacement_jacobian, coordinate, rotated_coordinate_gradient }; mapping::compactification::ExteriorMapResult rotated_result; REQUIRE( compactification.Evaluate(rotated_input, rotated_result) == mapping::MappingStatus::valid ); mfem::Vector expected_position(dimension); rotation.Mult(result.physical_position, expected_position); mfem::DenseMatrix expected_jacobian(dimension); mfem::Mult(rotation, result.mapping_jacobian, temporary); mfem::MultABt(temporary, rotation, expected_jacobian); check_vector( rotated_result.physical_position, expected_position, tolerance ); check_matrix(rotated_result.mapping_jacobian, expected_jacobian, tolerance); } TEST_CASE( "Kelvin Compactification Is Continuous At The Mesh Defined Stellar Surface", tags::unit &tags::mapping &tags::kelvin ) { constexpr double tolerance = 1.0e-14; mapping::compactification::KelvinCompactification compactification( {.r_star_ref = 1.0, .r_inf_ref = 4.0} ); const mfem::Vector reference_position = make_vector( -0.5260553366425769, 0.5260553366425769, -0.6553163792879153 ); const mfem::Vector displaced_position = make_vector(-0.55, 0.51, -0.63); const mfem::Vector coordinate_gradient = make_vector(-0.18, 0.18, -0.22); const mfem::DenseMatrix displacement_jacobian = make_identity(); REQUIRE(reference_position.Norml2() < 1.0); const mapping::compactification::ExteriorMapInput input{ reference_position, displaced_position, displacement_jacobian, 0.0, coordinate_gradient }; mapping::compactification::ExteriorMapResult result; REQUIRE( compactification.Evaluate(input, result) == mapping::MappingStatus::valid ); check_vector(result.physical_position, displaced_position, tolerance); CHECK(result.mapping_jacobian.Det() > 0.0); } TEST_CASE( "Kelvin Compactification Has Correct Infinity And Coordinate Bound " "Behavior", tags::unit &tags::mapping &tags::kelvin ) { constexpr double r_star = 1.0; constexpr double r_inf = 4.0; constexpr double radial_extent = r_inf - r_star; constexpr double coordinate_tolerance = 1.0e-12; constexpr double tolerance = 2.0e-11; mapping::compactification::KelvinCompactification compactification( {.r_star_ref = r_star, .r_inf_ref = r_inf, .coordinate_tolerance = coordinate_tolerance} ); const mfem::DenseMatrix identity = make_identity(); const mfem::Vector coordinate_gradient = make_vector(1.0 / radial_extent, 0.0, 0.0); for (const double coordinate : std::array{0.0, 0.25, 0.75, 0.95, 0.99, 0.999}) { CAPTURE(coordinate); const double computational_radius = r_star + coordinate * radial_extent; const mfem::Vector reference_position = make_vector(computational_radius, 0.0, 0.0); const mapping::compactification::ExteriorMapInput input{ reference_position, reference_position, identity, coordinate, coordinate_gradient }; mapping::compactification::ExteriorMapResult result; REQUIRE( compactification.Evaluate(input, result) == mapping::MappingStatus::valid ); CHECK_THAT( result.physical_position.Norml2() * (1.0 - coordinate), WithinAbs(r_star, tolerance) ); } const mfem::Vector reference_position = make_vector(r_inf, 0.0, 0.0); mapping::compactification::ExteriorMapResult result; CHECK( compactification.Evaluate( {reference_position, reference_position, identity, 1.0, coordinate_gradient}, result ) == mapping::MappingStatus::at_compactified_infinity ); CHECK( compactification.Evaluate( {reference_position, reference_position, identity, 1.0 - 0.5 * coordinate_tolerance, coordinate_gradient}, result ) == mapping::MappingStatus::at_compactified_infinity ); CHECK( compactification.Evaluate( {reference_position, reference_position, identity, 1.0 + 0.5 * coordinate_tolerance, coordinate_gradient}, result ) == mapping::MappingStatus::at_compactified_infinity ); CHECK( compactification.Evaluate( {reference_position, reference_position, identity, 1.0 + 2.0 * coordinate_tolerance, coordinate_gradient}, result ) == mapping::MappingStatus::outside_reference_domain ); CHECK( compactification.Evaluate( {reference_position, reference_position, identity, -2.0 * coordinate_tolerance, coordinate_gradient}, result ) == mapping::MappingStatus::outside_reference_domain ); const mfem::Vector surface_position = make_vector(0.97, 0.0, 0.0); CHECK( compactification.Evaluate( {surface_position, surface_position, identity, -0.5 * coordinate_tolerance, coordinate_gradient}, result ) == mapping::MappingStatus::valid ); check_vector(result.physical_position, surface_position, tolerance); } TEST_CASE( "Kelvin Compactification Rejects Invalid Inputs And Inverted Maps", tags::unit &tags::mapping &tags::kelvin ) { mapping::compactification::KelvinCompactification compactification( {.r_star_ref = 1.0, .r_inf_ref = 4.0} ); const mfem::Vector reference_position = make_vector(2.0, 0.0, 0.0); const mfem::Vector displaced_position(reference_position); const mfem::Vector coordinate_gradient = make_vector(1.0 / 3.0, 0.0, 0.0); const mfem::Vector zero_gradient = make_vector(0.0, 0.0, 0.0); const mfem::DenseMatrix identity = make_identity(); mapping::compactification::ExteriorMapResult result; mfem::Vector wrong_dimension(2); wrong_dimension = 1.0; CHECK( compactification.Evaluate( {wrong_dimension, displaced_position, identity, 1.0 / 3.0, coordinate_gradient}, result ) == mapping::MappingStatus::invalid_dimension ); CHECK( compactification.Evaluate( {reference_position, displaced_position, identity, 1.0 / 3.0, wrong_dimension}, result ) == mapping::MappingStatus::invalid_dimension ); mfem::Vector non_finite_position(reference_position); non_finite_position(1) = std::numeric_limits::quiet_NaN(); CHECK( compactification.Evaluate( {non_finite_position, displaced_position, identity, 1.0 / 3.0, coordinate_gradient}, result ) == mapping::MappingStatus::non_finite_input ); mfem::Vector non_finite_gradient(coordinate_gradient); non_finite_gradient(2) = std::numeric_limits::infinity(); CHECK( compactification.Evaluate( {reference_position, displaced_position, identity, 1.0 / 3.0, non_finite_gradient}, result ) == mapping::MappingStatus::non_finite_input ); CHECK( compactification.Evaluate( {reference_position, displaced_position, identity, std::numeric_limits::quiet_NaN(), coordinate_gradient}, result ) == mapping::MappingStatus::non_finite_input ); mfem::DenseMatrix singular_displacement_jacobian(dimension); singular_displacement_jacobian = 0.0; CHECK( compactification.Evaluate( {reference_position, displaced_position, singular_displacement_jacobian, 1.0 / 3.0, zero_gradient}, result ) == mapping::MappingStatus::non_positive_determinant ); mfem::DenseMatrix inverted_displacement_jacobian = make_identity(); inverted_displacement_jacobian(0, 0) = -1.0; CHECK( compactification.Evaluate( {reference_position, displaced_position, inverted_displacement_jacobian, 1.0 / 3.0, zero_gradient}, result ) == mapping::MappingStatus::non_positive_determinant ); } TEST_CASE( "Kelvin Compactification Variation Rejects Invalid Inputs", tags::unit &tags::mapping &tags::kelvin ) { mapping::compactification::KelvinCompactification compactification( {.r_star_ref = 1.0, .r_inf_ref = 4.0} ); const mfem::Vector reference_position = make_vector(2.0, 0.0, 0.0); const mfem::Vector displaced_position(reference_position); const mfem::Vector coordinate_gradient = make_vector(1.0 / 3.0, 0.0, 0.0); const mfem::DenseMatrix identity = make_identity(); const mapping::compactification::ExteriorMapInput input{ reference_position, displaced_position, identity, 1.0 / 3.0, coordinate_gradient }; mapping::compactification::ExteriorMapResult result; REQUIRE( compactification.Evaluate(input, result) == mapping::MappingStatus::valid ); const mfem::Vector valid_position_direction = make_vector(0.01, -0.02, 0.03); const mfem::DenseMatrix valid_jacobian_direction = make_identity(); mapping::compactification::ExteriorMapVariation variation; mfem::Vector wrong_dimension(2); wrong_dimension = 0.0; CHECK( compactification.EvaluateVariation( input, result, {wrong_dimension, valid_jacobian_direction}, variation ) == mapping::MappingStatus::invalid_dimension ); mfem::DenseMatrix wrong_jacobian_dimension(2); wrong_jacobian_dimension = 0.0; CHECK( compactification.EvaluateVariation( input, result, {valid_position_direction, wrong_jacobian_dimension}, variation ) == mapping::MappingStatus::invalid_dimension ); mfem::Vector non_finite_direction(valid_position_direction); non_finite_direction(0) = std::numeric_limits::quiet_NaN(); CHECK( compactification.EvaluateVariation( input, result, {non_finite_direction, valid_jacobian_direction}, variation ) == mapping::MappingStatus::non_finite_input ); } TEST_CASE( "Kelvin Compactification Evaluations Are Independent", tags::unit &tags::mapping &tags::kelvin ) { constexpr double tolerance = 0.0; mapping::compactification::KelvinCompactification compactification( {.r_star_ref = 1.0, .r_inf_ref = 4.0} ); const mfem::DenseMatrix identity = make_identity(); const mfem::Vector gradient_a = make_vector(0.12, 0.03, -0.02); const mfem::Vector gradient_b = make_vector(-0.04, 0.15, 0.01); const mfem::Vector reference_a = make_vector(1.5, 0.2, 0.1); const mfem::Vector reference_b = make_vector(2.5, -0.3, 0.4); const mapping::compactification::ExteriorMapInput input_a{ reference_a, reference_a, identity, 0.25, gradient_a }; const mapping::compactification::ExteriorMapInput input_b{ reference_b, reference_b, identity, 0.70, gradient_b }; mapping::compactification::ExteriorMapResult first_a; mapping::compactification::ExteriorMapResult result_b; mapping::compactification::ExteriorMapResult second_a; REQUIRE( compactification.Evaluate(input_a, first_a) == mapping::MappingStatus::valid ); REQUIRE( compactification.Evaluate(input_b, result_b) == mapping::MappingStatus::valid ); REQUIRE( compactification.Evaluate(input_a, second_a) == mapping::MappingStatus::valid ); check_vector( first_a.physical_position, second_a.physical_position, tolerance ); check_matrix( first_a.mapping_jacobian, second_a.mapping_jacobian, tolerance ); }