Skip to content

Commit

Permalink
Add coordinate map SphericalToCartesianPfaffian
Browse files Browse the repository at this point in the history
This map will be used in spherical shell elements in order to use
a spherical harmonic basis.
  • Loading branch information
kidder committed Dec 11, 2024
1 parent 327199f commit 8faf5f8
Show file tree
Hide file tree
Showing 5 changed files with 253 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/Domain/CoordinateMaps/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ spectre_target_sources(
KerrHorizonConforming.cpp
Rotation.cpp
SpecialMobius.cpp
SphericalToCartesianPfaffian.cpp
UniformCylindricalEndcap.cpp
UniformCylindricalFlatEndcap.cpp
UniformCylindricalSide.cpp
Expand Down Expand Up @@ -72,6 +73,7 @@ spectre_target_headers(
ProductMaps.tpp
Rotation.hpp
SpecialMobius.hpp
SphericalToCartesianPfaffian.hpp
Tags.hpp
TimeDependentHelpers.hpp
UniformCylindricalEndcap.hpp
Expand Down
124 changes: 124 additions & 0 deletions src/Domain/CoordinateMaps/SphericalToCartesianPfaffian.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Distributed under the MIT License.
// See LICENSE.txt for details.

#include "Domain/CoordinateMaps/SphericalToCartesianPfaffian.hpp"

#include <cmath>
#include <cstddef>
#include <pup.h>

#include "DataStructures/DataVector.hpp"
#include "DataStructures/Tensor/Tensor.hpp"
#include "Utilities/DereferenceWrapper.hpp"
#include "Utilities/GenerateInstantiations.hpp"
#include "Utilities/MakeWithValue.hpp"

namespace domain::CoordinateMaps {
SphericalToCartesianPfaffian::SphericalToCartesianPfaffian() = default;
SphericalToCartesianPfaffian::~SphericalToCartesianPfaffian() = default;
SphericalToCartesianPfaffian::SphericalToCartesianPfaffian(
SphericalToCartesianPfaffian&&) = default;
SphericalToCartesianPfaffian::SphericalToCartesianPfaffian(
const SphericalToCartesianPfaffian&) = default;
SphericalToCartesianPfaffian& SphericalToCartesianPfaffian::operator=(
const SphericalToCartesianPfaffian&) = default;
SphericalToCartesianPfaffian& SphericalToCartesianPfaffian::operator=(
SphericalToCartesianPfaffian&&) = default;

template <typename T>
std::array<tt::remove_cvref_wrap_t<T>, 3>
SphericalToCartesianPfaffian::operator()(
const std::array<T, 3>& source_coords) const {
const auto& [r, theta, phi] = source_coords;
return {
{r * sin(theta) * cos(phi), r * sin(theta) * sin(phi), r * cos(theta)}};
}

std::optional<std::array<double, 3>> SphericalToCartesianPfaffian::inverse(

Check failure on line 37 in src/Domain/CoordinateMaps/SphericalToCartesianPfaffian.cpp

View workflow job for this annotation

GitHub Actions / Clang-tidy (Debug)

method 'inverse' can be made static

Check failure on line 37 in src/Domain/CoordinateMaps/SphericalToCartesianPfaffian.cpp

View workflow job for this annotation

GitHub Actions / Clang-tidy (Release)

method 'inverse' can be made static
const std::array<double, 3>& target_coords) const {
const auto& [x, y, z] = target_coords;
if (UNLIKELY(y == 0.0 and x == 0.0)) {
if (UNLIKELY(z == 0.0)) {
return std::array{0.0, 0.5 * M_PI, 0.0};
} else {
return std::array{std::abs(z), z > 0.0 ? 0.0 : M_PI, 0.0};
}
} else {
const double r = std::hypot(x, y, z);
return std::array{r, acos(z / r),
x > 0.0 ? atan2(y, x) : atan2(y, x) + 2.0 * M_PI};
}
}

template <typename T>
tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>
SphericalToCartesianPfaffian::jacobian(
const std::array<T, 3>& source_coords) const {
const auto& [r, theta, phi] = source_coords;
tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian_matrix{
make_with_value<tt::remove_cvref_wrap_t<T>>(dereference_wrapper(r), 0.0)};
get<0, 0>(jacobian_matrix) = sin(theta) * cos(phi);
get<1, 0>(jacobian_matrix) = sin(theta) * sin(phi);
get<2, 0>(jacobian_matrix) = cos(theta);
get<0, 1>(jacobian_matrix) = r * cos(theta) * cos(phi);
get<1, 1>(jacobian_matrix) = r * cos(theta) * sin(phi);
get<2, 1>(jacobian_matrix) = -r * sin(theta);
// Pfaffian basis means phi components are 1 / sin(theta) times those of a
// coord basis
get<0, 2>(jacobian_matrix) = -r * sin(phi);
get<1, 2>(jacobian_matrix) = r * cos(phi);
// get<2, 2>(jacobian_matrix) is zero
return jacobian_matrix;
}

template <typename T>
tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>
SphericalToCartesianPfaffian::inv_jacobian(
const std::array<T, 3>& source_coords) const {
const auto& [r, theta, phi] = source_coords;
tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian_matrix{
make_with_value<tt::remove_cvref_wrap_t<T>>(dereference_wrapper(r), 0.0)};
get<0, 0>(inv_jacobian_matrix) = sin(theta) * cos(phi);
get<0, 1>(inv_jacobian_matrix) = sin(theta) * sin(phi);
get<0, 2>(inv_jacobian_matrix) = cos(theta);
get<1, 0>(inv_jacobian_matrix) = cos(theta) * cos(phi) / r;
get<1, 1>(inv_jacobian_matrix) = cos(theta) * sin(phi) / r;
get<1, 2>(inv_jacobian_matrix) = -sin(theta) / r;
// Pfaffian basis means phi components are sin(theta) times those of a coord
// basis
get<2, 0>(inv_jacobian_matrix) = -sin(phi) / r;
get<2, 1>(inv_jacobian_matrix) = cos(phi) / r;
// get<2, 2>(inv_jacobian_matrix) is zero
return inv_jacobian_matrix;
}

void SphericalToCartesianPfaffian::pup(PUP::er& /*p*/) {}

bool operator==(const SphericalToCartesianPfaffian& /*lhs*/,
const SphericalToCartesianPfaffian& /*rhs*/) {
return true;
}

bool operator!=(const SphericalToCartesianPfaffian& lhs,
const SphericalToCartesianPfaffian& rhs) {
return not(lhs == rhs);
}

#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)

#define INSTANTIATE_DTYPE(_, data) \
template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3> \
SphericalToCartesianPfaffian::operator()( \
const std::array<DTYPE(data), 3>& source_coords) const; \
template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \
SphericalToCartesianPfaffian::jacobian( \
const std::array<DTYPE(data), 3>& source_coords) const; \
template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \
SphericalToCartesianPfaffian::inv_jacobian( \
const std::array<DTYPE(data), 3>& source_coords) const;

GENERATE_INSTANTIATIONS(INSTANTIATE_DTYPE,
(double, DataVector,
std::reference_wrapper<const double>,
std::reference_wrapper<const DataVector>))
} // namespace domain::CoordinateMaps
57 changes: 57 additions & 0 deletions src/Domain/CoordinateMaps/SphericalToCartesianPfaffian.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Distributed under the MIT License.
// See LICENSE.txt for details.

#pragma once

#include <array>
#include <cstddef>
#include <optional>

#include "DataStructures/Tensor/TypeAliases.hpp"
#include "Utilities/TypeTraits/RemoveReferenceWrapper.hpp"

/// \cond
namespace PUP {
class er;
} // namespace PUP
/// \endcond

namespace domain::CoordinateMaps {

class SphericalToCartesianPfaffian {
public:
static constexpr size_t dim = 3;
SphericalToCartesianPfaffian();
~SphericalToCartesianPfaffian();

Check failure on line 25 in src/Domain/CoordinateMaps/SphericalToCartesianPfaffian.hpp

View workflow job for this annotation

GitHub Actions / Clang-tidy (Debug)

class 'SphericalToCartesianPfaffian' can be made trivially destructible by defaulting the destructor on its first declaration

Check failure on line 25 in src/Domain/CoordinateMaps/SphericalToCartesianPfaffian.hpp

View workflow job for this annotation

GitHub Actions / Clang-tidy (Release)

class 'SphericalToCartesianPfaffian' can be made trivially destructible by defaulting the destructor on its first declaration
SphericalToCartesianPfaffian(SphericalToCartesianPfaffian&&);
SphericalToCartesianPfaffian(const SphericalToCartesianPfaffian&);
SphericalToCartesianPfaffian& operator=(const SphericalToCartesianPfaffian&);
SphericalToCartesianPfaffian& operator=(SphericalToCartesianPfaffian&&);

template <typename T>
std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(
const std::array<T, 3>& source_coords) const;

std::optional<std::array<double, 3>> inverse(
const std::array<double, 3>& target_coords) const;

template <typename T>
tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(
const std::array<T, 3>& source_coords) const;

template <typename T>
tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(
const std::array<T, 3>& source_coords) const;

// NOLINTNEXTLINE(google-runtime-references)
void pup(PUP::er& p);

static constexpr bool is_identity() { return false; }
};

bool operator==(const SphericalToCartesianPfaffian& lhs,
const SphericalToCartesianPfaffian& rhs);

bool operator!=(const SphericalToCartesianPfaffian& lhs,
const SphericalToCartesianPfaffian& rhs);
} // namespace domain::CoordinateMaps
1 change: 1 addition & 0 deletions tests/Unit/Domain/CoordinateMaps/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ set(LIBRARY_SOURCES
Test_ProductMaps.cpp
Test_Rotation.cpp
Test_SpecialMobius.cpp
Test_SphericalToCartesianPfaffian.cpp
Test_Tags.cpp
Test_TimeDependentHelpers.cpp
Test_UniformCylindricalEndcap.cpp
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Distributed under the MIT License.
// See LICENSE.txt for details.

#include "Framework/TestingFramework.hpp"

#include <array>
#include <optional>
#include <pup.h>

#include "DataStructures/DataVector.hpp"
#include "DataStructures/Tensor/Tensor.hpp"
#include "Domain/CoordinateMaps/SphericalToCartesianPfaffian.hpp"
#include "Framework/TestHelpers.hpp"
#include "Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp"
#include "NumericalAlgorithms/Spectral/LogicalCoordinates.hpp"
#include "NumericalAlgorithms/Spectral/Mesh.hpp"

namespace domain {
namespace {
void test_map_at_point(const CoordinateMaps::SphericalToCartesianPfaffian& map,
const std::array<double, 3>& source_point,
const std::array<double, 3>& target_point) {
test_inverse_map(map, source_point);
if (source_point !=
std::array{0.0, 0.5 * M_PI, 0.0}) { // inv jac singular at origin
test_coordinate_map_argument_types(map, source_point);
test_inv_jacobian(map, source_point);
}
CHECK_ITERABLE_APPROX(map(source_point), target_point);
CHECK_ITERABLE_APPROX(map.inverse(target_point).value(), source_point);
}

void test_map(const CoordinateMaps::SphericalToCartesianPfaffian& map) {
CHECK(not map.is_identity());
CHECK_FALSE(map != map);
test_serialization(map);
const std::array<double, 3> source_origin{{0.0, 0.5 * M_PI, 0.0}};
const std::array<double, 3> target_origin{{0.0, 0.0, 0.0}};
test_map_at_point(map, source_origin, target_origin);
const std::array<double, 3> source_north_pole{{0.5, 0.0, 0.0}};
const std::array<double, 3> target_north_pole{{0.0, 0.0, 0.5}};
test_map_at_point(map, source_north_pole, target_north_pole);
const std::array<double, 3> source_south_pole{{0.25, M_PI, 0.0}};
const std::array<double, 3> target_south_pole{{0.0, 0.0, -0.25}};
test_map_at_point(map, source_south_pole, target_south_pole);
const Mesh<3> mesh{
{5, 3, 5},
{Spectral::Basis::Legendre, Spectral::Basis::SphericalHarmonic,
Spectral::Basis::SphericalHarmonic},
{Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss,
Spectral::Quadrature::Equiangular}};
const auto xi = logical_coordinates(mesh);
const std::array<DataVector, 3> source_coords{xi[0] + 2.0, xi[1], xi[2]};
test_inv_jacobian(map, source_coords);
}

void test() {
CoordinateMaps::SphericalToCartesianPfaffian original_map{};

Check failure on line 58 in tests/Unit/Domain/CoordinateMaps/Test_SphericalToCartesianPfaffian.cpp

View workflow job for this annotation

GitHub Actions / Clang-tidy (Debug)

variable 'original_map' of type 'CoordinateMaps::SphericalToCartesianPfaffian' can be declared 'const'

Check failure on line 58 in tests/Unit/Domain/CoordinateMaps/Test_SphericalToCartesianPfaffian.cpp

View workflow job for this annotation

GitHub Actions / Clang-tidy (Release)

variable 'original_map' of type 'CoordinateMaps::SphericalToCartesianPfaffian' can be declared 'const'
test_map(original_map);
const auto serialized_map = serialize_and_deserialize(original_map);
test_map(serialized_map);
}
} // namespace

SPECTRE_TEST_CASE("Unit.Domain.CoordinateMaps.SphericalToCartesianPfaffian",
"[Domain][Unit]") {
test();
}
} // namespace domain

0 comments on commit 8faf5f8

Please sign in to comment.