# Source code for boule._triaxialellipsoid

# Copyright (c) 2019 The Boule Developers.
# Distributed under the terms of the BSD 3-Clause License.
# SPDX-License-Identifier: BSD-3-Clause
#
# This code is part of the Fatiando a Terra project (https://www.fatiando.org)
#
"""
Define a reference triaxial ellipsoid.
"""
from warnings import warn

import attr
import numpy as np

# Don't let ellipsoid parameters be changed to avoid messing up calculations
# accidentally.
[docs]@attr.s(frozen=True) class TriaxialEllipsoid: r""" A rotating triaxial ellipsoid. The ellipsoid is defined by five parameters: semimajor axis, semimedium axis, semiminor axis, geocentric gravitational constant, and angular velocity The thee semi-axis are different and the ellipsoid spins around it's largest moment of inertia. **This class is read-only:** Input parameters and attributes cannot be changed after instantiation. **Units:** All input parameters and derived attributes are in SI units. .. attention:: Gravity calculations have not been implemented yet for triaxial ellipsoids. If you're interested in this feature or would like to help implement it, please get in touch <https://www.fatiando.org/contact>__. Parameters ---------- name : str A short name for the ellipsoid, for example "WGS84". semimajor_axis : float The semimajor (largest) axis of the ellipsoid. Definition: :math:a. Units: :math:m. semimedium_axis : float The semimedium (middle) axis of the ellipsoid. Definition: :math:b. Units: :math:m. semiminor_axis : float The semiminor (smallest) axis of the ellipsoid. Definition: :math:c. Units: :math:m. geocentric_grav_const : float The geocentric gravitational constant. The product of the mass of the ellipsoid :math:M and the gravitational constant :math:G. Definition: :math:GM. Units: :math:m^3.s^{-2}. angular_velocity : float The angular velocity of the rotating ellipsoid. Definition: :math:\omega. Units: :math:\\rad.s^{-1}. long_name : str or None A long name for the ellipsoid, for example "World Geodetic System 1984" (optional). reference : str or None Citation for the ellipsoid parameter values (optional). Examples -------- We can define an ellipsoid by setting the 5 key numerical parameters: >>> ellipsoid = TriaxialEllipsoid( ... name="VESTA", ... long_name="Vesta Triaxial Ellipsoid", ... semimajor_axis=286_300, ... semimedium_axis=278_600, ... semiminor_axis=223_200, ... geocentric_grav_const=1.729094e10, ... angular_velocity=326.71050958367e-6, ... reference=( ... "Russell, C. T., Raymond, C. A., Coradini, A., McSween, " ... "H. Y., Zuber, M. T., Nathues, A., et al. (2012). Dawn at " ... "Vesta: Testing the Protoplanetary Paradigm. Science. " ... "doi:10.1126/science.1219381" ... ), ... ) >>> print(ellipsoid) # doctest: +ELLIPSIS TriaxialEllipsoid(name='VESTA', ...) >>> print(ellipsoid.long_name) Vesta Triaxial Ellipsoid The class then defines several derived attributes based on the input parameters: >>> print(f"{ellipsoid.mean_radius:.0f} m") 262700 m >>> print(f"{ellipsoid.volume * 1e-9:.0f} km³") 74573626 km³ """ name = attr.ib() semimajor_axis = attr.ib() semimedium_axis = attr.ib() semiminor_axis = attr.ib() geocentric_grav_const = attr.ib() angular_velocity = attr.ib() long_name = attr.ib(default=None) reference = attr.ib(default=None) def _raise_invalid_axis(self): "Raise a ValueError informing that the axis are invalid." raise ValueError( "Invalid triaxial ellipsoid axis: " f"major={self.semimajor_axis} " f"medium={self.semimedium_axis} " f"minor={self.semiminor_axis}. " "Must be major > medium > minor." ) @semimajor_axis.validator def _check_semimajor_axis(self, semimajor_axis, value): """ Check if semimajor_axis is positive and is the largest of the axis. """ if not value > 0: raise ValueError( f"Invalid semi-major axis '{value}'. Should be greater than zero." ) if self.semimedium_axis > value: self._raise_invalid_axis() @semimedium_axis.validator def _check_semimedium_axis(self, semimedium_axis, value): """ Check if semimedium_axis is positive and larger than semi-minor axis. """ if not value > 0: raise ValueError( f"Invalid semi-medium axis '{value}'. Should be greater than zero." ) if self.semiminor_axis > value: self._raise_invalid_axis() @semiminor_axis.validator def _check_semiminor_axis(self, semiminor_axis, value): "Check if semiminor_axis is positive." if not value > 0: raise ValueError( f"Invalid semi-minor axis '{value}'. Should be greater than zero." ) # Don't need to check here because if the two checks for major and # medium pass it means that this is the smallest. @geocentric_grav_const.validator def _check_geocentric_grav_const(self, geocentric_grav_const, value): "Warn if geocentric_grav_const is negative." if value < 0: warn(f"The geocentric gravitational constant is negative: '{value}'") @property def mean_radius(self): r""" The arithmetic mean radius of the ellipsoid. Definition: :math:R = \dfrac{a + b + c}{3}. Units: :math:m. """ return (self.semimajor_axis + self.semimedium_axis + self.semiminor_axis) / 3 @property def volume(self): r""" The volume bounded by the ellipsoid. Definition: :math:V = \dfrac{4}{3} \pi a b c. Units: :math:m^3. """ return ( (4 / 3 * np.pi) * self.semimajor_axis * self.semimedium_axis * self.semiminor_axis )