module; #include #include #include module mean_field; namespace { bool vector_is_finite(const mfem::Vector &vector) { for (int i = 0; i < vector.Size(); ++i) { if (!std::isfinite(vector(i))) return false; } return true; } bool matrix_is_finite(const mfem::DenseMatrix &matrix) { for (int i = 0; i < matrix.Height(); ++i) { for (int j = 0; j < matrix.Width(); ++j) { if (!std::isfinite(matrix(i, j))) return false; } } return true; } } // namespace namespace mean_field::mapping::compactification { KelvinCompactification::KelvinCompactification(options::KelvinCompactificationOptions options) : m_options(options) { if (!std::isfinite(m_options.r_star_ref) || !std::isfinite(m_options.r_inf_ref)) { throw std::invalid_argument("Kelvin compactification radii must be finite."); } if (m_options.r_star_ref <= 0.0 || m_options.r_inf_ref <= m_options.r_star_ref) { throw std::invalid_argument("Kelvin compactification requires 0 < r_star_ref < r_inf_ref."); } if (!std::isfinite(m_options.coordinate_tolerance) || m_options.coordinate_tolerance < 0.0 || m_options.coordinate_tolerance >= 1.0) { throw std::invalid_argument( "Kelvin compactification coordinate tolerance must be finite " "and lie " "in [0, 1)." ); } } MappingStatus KelvinCompactification::ComputeRadialFactors( const double compactification_coordinate, RadialFactors &factors ) const { if (!std::isfinite(compactification_coordinate)) return MappingStatus::non_finite_input; const double tolerance = m_options.coordinate_tolerance; if (compactification_coordinate < -tolerance || compactification_coordinate > 1.0 + tolerance) { return MappingStatus::outside_reference_domain; } double coordinate = compactification_coordinate; if (coordinate < 0.0) coordinate = 0.0; if (coordinate >= 1.0 - tolerance) { return MappingStatus::at_compactified_infinity; } const double radial_extent = m_options.r_inf_ref - m_options.r_star_ref; const double computational_radius = m_options.r_star_ref + coordinate * radial_extent; if (!std::isfinite(computational_radius) || computational_radius <= 0.0) { return MappingStatus::invalid_reference_radius; } const double one_minus_coordinate = 1.0 - coordinate; const double denominator = computational_radius * one_minus_coordinate; if (!std::isfinite(denominator) || denominator <= 0.0) { return MappingStatus::non_finite_result; } const double scale = m_options.r_star_ref / denominator; const double scale_derivative = scale * (1.0 / one_minus_coordinate - radial_extent / computational_radius); if (!std::isfinite(scale) || !std::isfinite(scale_derivative)) { return MappingStatus::non_finite_result; } factors.coordinate = coordinate; factors.computational_radius = computational_radius; factors.scale = scale; factors.scale_derivative = scale_derivative; return MappingStatus::valid; } MappingStatus KelvinCompactification::Evaluate( const ExteriorMapInput &input, ExteriorMapResult &result ) const { const int dimension = input.reference_position.Size(); if (dimension <= 0 || input.displaced_position.Size() != dimension || input.compactification_coordinate_gradient.Size() != dimension) { return MappingStatus::invalid_dimension; } if (input.displacement_jacobian.Height() != dimension || input.displacement_jacobian.Width() != dimension) { return MappingStatus::invalid_dimension; } if (!vector_is_finite(input.reference_position) || !vector_is_finite(input.displaced_position) || !vector_is_finite(input.compactification_coordinate_gradient) || !matrix_is_finite(input.displacement_jacobian)) { return MappingStatus::non_finite_input; } RadialFactors factors; const MappingStatus factor_status = ComputeRadialFactors(input.compactification_coordinate, factors); if (factor_status != MappingStatus::valid) return factor_status; result.physical_position.SetSize(dimension); result.mapping_jacobian.SetSize(dimension, dimension); for (int i = 0; i < dimension; ++i) { result.physical_position(i) = factors.scale * input.displaced_position(i); for (int j = 0; j < dimension; ++j) { const double scale_gradient = factors.scale_derivative * input.compactification_coordinate_gradient(j); result.mapping_jacobian(i, j) = factors.scale * input.displacement_jacobian(i, j) + input.displaced_position(i) * scale_gradient; } } if (!vector_is_finite(result.physical_position) || !matrix_is_finite(result.mapping_jacobian)) { return MappingStatus::non_finite_result; } const double mapping_determinant = result.mapping_jacobian.Det(); if (!std::isfinite(mapping_determinant)) return MappingStatus::non_finite_result; if (mapping_determinant <= 0.0) return MappingStatus::non_positive_determinant; return MappingStatus::valid; } MappingStatus KelvinCompactification::EvaluateVariation( const ExteriorMapInput &input, const ExteriorMapResult &result, const ExteriorMapDirection &direction, ExteriorMapVariation &variation ) const { const int dimension = input.reference_position.Size(); if (dimension <= 0 || input.displaced_position.Size() != dimension || input.compactification_coordinate_gradient.Size() != dimension) { return MappingStatus::invalid_dimension; } if (input.displacement_jacobian.Height() != dimension || input.displacement_jacobian.Width() != dimension) { return MappingStatus::invalid_dimension; } if (result.physical_position.Size() != dimension || result.mapping_jacobian.Height() != dimension || result.mapping_jacobian.Width() != dimension) { return MappingStatus::invalid_dimension; } if (direction.displaced_position_variation.Size() != dimension || direction.displacement_jacobian_variation.Height() != dimension || direction.displacement_jacobian_variation.Width() != dimension) { return MappingStatus::invalid_dimension; } if (!vector_is_finite(input.reference_position) || !vector_is_finite(input.displaced_position) || !vector_is_finite(input.compactification_coordinate_gradient) || !matrix_is_finite(input.displacement_jacobian)) { return MappingStatus::non_finite_input; } if (!vector_is_finite(result.physical_position) || !matrix_is_finite(result.mapping_jacobian) || !vector_is_finite(direction.displaced_position_variation) || !matrix_is_finite(direction.displacement_jacobian_variation)) { return MappingStatus::non_finite_input; } RadialFactors factors; const MappingStatus factor_status = ComputeRadialFactors(input.compactification_coordinate, factors); if (factor_status != MappingStatus::valid) return factor_status; variation.physical_position_variation.SetSize(dimension); variation.mapping_jacobian_variation.SetSize(dimension, dimension); for (int i = 0; i < dimension; ++i) { variation.physical_position_variation(i) = factors.scale * direction.displaced_position_variation(i); for (int j = 0; j < dimension; ++j) { const double scale_gradient = factors.scale_derivative * input.compactification_coordinate_gradient(j); variation.mapping_jacobian_variation(i, j) = factors.scale * direction.displacement_jacobian_variation(i, j) + direction.displaced_position_variation(i) * scale_gradient; } } if (!vector_is_finite(variation.physical_position_variation) || !matrix_is_finite(variation.mapping_jacobian_variation)) { return MappingStatus::non_finite_result; } return MappingStatus::valid; } std::string_view KelvinCompactification::GetName() const noexcept { return "KelvinCompactification"; } double KelvinCompactification::GetReferenceStellarRadius() const noexcept { return m_options.r_star_ref; } double KelvinCompactification::GetReferenceInfinityRadius() const noexcept { return m_options.r_inf_ref; } double KelvinCompactification::GetCoordinateTolerance() const noexcept { return m_options.coordinate_tolerance; } } // namespace mean_field::mapping::compactification