""" Base Tides Module
"""
import numpy as np
from typing import Dict, TYPE_CHECKING, Tuple
import TidalPy
from TidalPy.exceptions import (AttributeNotSetError, ConfigPropertyChangeError, IncompatibleModelError,
IncorrectMethodToSetStateProperty, InitiatedPropertyChangeError, NotYetImplementedError,
OuterscopePropertySetError, ParameterValueError)
from TidalPy.utilities.classes.config.config import WorldConfigHolder
from ..love1d import complex_love_general, effective_rigidity_general
from ..dissipation import calc_tidal_susceptibility, calc_tidal_susceptibility_reduced
from ..modes.mode_manipulation import find_mode_manipulators
from TidalPy.logger import get_logger
log = get_logger("TidalPy")
if TYPE_CHECKING:
from TidalPy.utilities.types import ComplexArray, FloatArray
from TidalPy.structures.world_types import TidalWorldType
from ..eccentricity_funcs import EccenOutput
from ..inclination_funcs import InclinOutput
from ..modes.mode_manipulation import (DissipTermsArray, FreqSig, ResultsByFreqType, UniqueFreqType)
# TODO: Add a spin-sync version
[docs]
class TidesBase(WorldConfigHolder):
""" TidesBase
Holder for all tidal heating and tidal potential calculations
Tides class stores model parameters and methods for calculating tidal heating and tidal potential derivatives
which are general functions of (T, P, melt_frac, w, e, obliquity)
Attributes
----------
eccentricity_truncation_lvl
max_tidal_order_lvl
use_obliquity_tides
multiply_modes_by_sign
eccentricity_results
obliquity_results
tidal_susceptibility_reduced
tidal_susceptibility
unique_tidal_frequencies
tidal_terms_by_frequency
tidal_heating_global
global_negative_imk_by_orderl
global_love_by_orderl
effective_q_by_orderl
dUdM
dUdw
dUdO
fixed_q
fixed_dt
fixed_k2
thermal_set
orbit_set
"""
model = 'base'
world_config_key = 'tides'
def __init__(self, world: 'TidalWorldType', store_config_in_world: bool = True, initialize: bool = True):
""" Constructor for TidesBase class
Parameters
----------
world : TidalWorldType
The world where tides are being calculated.
store_config_in_world : bool = True
Flag that determines if the final model's configuration dictionary should be copied into the
`world.config` dictionary.
initialize : bool = True
If `True`, then an initial call to the tide's reinit method will be made at the end of construction.
"""
self.default_config = TidalPy.config['tides']['models'][self.model]
super().__init__(world, store_config_in_world=store_config_in_world)
# Initialized properties
self._thermal_set = False
self._orbit_set = False
# State properties
self._fixed_q = None
self._fixed_dt = None
self._fixed_k2 = None
self._tidal_susceptibility = None
self._tidal_susceptibility_reduced = None
self._unique_tidal_frequencies = None
self._tidal_terms_by_frequency = None
self._tidal_heating_global = None
self._dUdM = None
self._dUdw = None
self._dUdO = None
self._effective_q_by_orderl = None
self._global_negative_imk_by_orderl = None
self._global_love_by_orderl = None
self._need_to_collapse_modes = False
self._new_tidal_frequencies = False
# Model configurations that will be set in reinit
self._eccentricity_truncation_lvl = None
self._max_tidal_order_lvl = None
self._use_obliquity_tides = None
self._multiply_modes_by_sign = None
# Functions to be initialized in self.reinit
self._eccentricity_results = None
self._obliquity_results = None
self.eccentricity_func = None
self.obliquity_func = None
self.collapse_modes_func = None
self.calculate_modes_func = None
# TidalPy logging and debug info
log.debug(f'Building {self.model} tides class for {self.world.name}.')
# Call reinit for initialization
if initialize:
self.reinit(initial_init=True)
[docs]
def reinit(self, initial_init: bool = False):
""" Load configurations into the Tides class and import any config-dependent functions.
This reinit process is separate from the __init__ method because the Orbit class may need to overload some
configurations after class initialization.
Parameters
----------
initial_init : bool = False
This should be set to True the first time reinit is called.
Raises
------
NotYetImplementedError
ParameterValueError
"""
if initial_init:
log.debug(f'Initializing tides class: {self}.')
else:
log.debug(f'Reinit called for {self}.')
self.clear_state()
# Reset configurations
self._eccentricity_truncation_lvl = None
self._max_tidal_order_lvl = None
self._use_obliquity_tides = None
self._multiply_modes_by_sign = None
# Load in configurations
self._eccentricity_truncation_lvl = self.config['eccentricity_truncation_lvl']
self._max_tidal_order_lvl = self.config['max_tidal_order_l']
self._use_obliquity_tides = self.config['obliquity_tides_on']
self._multiply_modes_by_sign = self.config['multiply_modes_by_sign']
# Ensure the tidal order and orbital truncation levels make sense
if self.max_tidal_order_lvl > 7:
raise NotYetImplementedError(f'Tidal order {self.max_tidal_order_lvl} has not been implemented.')
if self.eccentricity_truncation_lvl % 2 != 0:
raise ParameterValueError('Orbital truncation level must be an even integer.')
if self.eccentricity_truncation_lvl < 2:
raise ParameterValueError('Orbital truncation level must be greater than or equal to 2.')
if self.eccentricity_truncation_lvl not in (2, 4, 6, 8, 10, 12, 14, 16, 18, 20):
if self.max_tidal_order_lvl == 2 and self.eccentricity_truncation_lvl == 22:
# This was implemented in v0.2.1
pass
else:
raise NotYetImplementedError(
f'Orbital truncation level of {self.eccentricity_truncation_lvl} has not been implemented.'
)
# Setup functions
self.calculate_modes_func, self.collapse_modes_func, self.eccentricity_func, self.obliquity_func = \
find_mode_manipulators(self.max_tidal_order_lvl, self.eccentricity_truncation_lvl, self.use_obliquity_tides)
[docs]
def post_orbit_initialize(self):
""" Initialize various tidal parameters once a tidal host is connected to the world (via an orbit class).
Raises
------
AttributeNotSetError
"""
log.debug(f'Post-orbit tidal initialization started for {self}.')
# Pull out parameters that are called multiple times in this method
tidal_host = self.tidal_host
world_radius = self.world.radius
semi_major_axis = self.semi_major_axis
if tidal_host is None:
raise AttributeNotSetError('Tidal host must be connected to target body in order to initialize tides.')
# Try to update properties that depend on the orbit and tidal host
self._tidal_susceptibility_reduced = \
calc_tidal_susceptibility_reduced(tidal_host.mass, world_radius)
if semi_major_axis is not None:
self._tidal_susceptibility = calc_tidal_susceptibility(
tidal_host.mass, world_radius, semi_major_axis
)
[docs]
def orbit_spin_changed(
self, eccentricity_change: bool = True, obliquity_change: bool = True,
orbital_freq_changed: bool = True, spin_freq_changed: bool = True,
force_obliquity_update: bool = False,
call_world_frequency_changed: bool = True,
call_collapse_modes: bool = True
) -> Tuple[ 'UniqueFreqType', 'ResultsByFreqType']:
""" Calculate tidal heating and potential derivative terms based on the current orbital state.
This will also calculate new unique tidal frequencies which must then be digested by the rheological model
at each planetary layer.
Parameters
----------
eccentricity_change : bool = True
If there was no change in eccentricity (or if the orbit set the eccentricity) set this to False for a
performance boost. If False, eccentricity functions won't be called.
obliquity_change : bool = True
If there was no change in obliquity set this to False for a performance boost.
If False, obliquity functions won't be called.
orbital_freq_changed : bool = True
If there was no change in the orbital frequency set this to False for a performance boost.
spin_freq_changed : bool = True
If there was no change in the spin frequency set this to False.
force_obliquity_update : bool = False
If True, then this method will call the obliquity update function even if it would otherwise skip it
due to obliquity dependence being turned off.
call_world_frequency_changed : bool = True
If True, then the method will call the world's complex compliance calculator.
This flag is set to False for the CPL method.
call_collapse_modes : bool = True
If True, then this method will call collapse modes (if needed).
Returns
-------
unique_tidal_frequencies : UniqueFreqType
Each unique frequency stored as a signature (orbital motion and spin-rate coeffs),
and the calculated frequency (combo of orbital motion and spin-rate) [rad s-1]
tidal_terms_by_frequency : ResultsByFreqType
Results for tidal heating, dU/dM, dU/dw, dU/dO are stored in a tuple for each tidal harmonic l and
unique frequency.
See Also
--------
TidalPy.tides.dissipation.mode_collapse
"""
log.debug(f'Method orbit_spin_changed called for {self}.')
self._need_to_collapse_modes = False
self._new_tidal_frequencies = False
# Pull out parameters that are called multiple times in the method.
eccentricity = self.eccentricity
obliquity = self.obliquity
world_radius = self.world.radius
semi_major_axis = self.semi_major_axis
spin_frequency = self.spin_frequency
orbital_frequency = self.orbital_frequency
# Make updates
if orbital_freq_changed:
# Orbital changes may have changed the tidal susceptibility
self._tidal_susceptibility = calc_tidal_susceptibility(
self.tidal_host.mass, world_radius,
semi_major_axis
)
# Check if we need to calculate new eccentricity results
if eccentricity is not None:
if eccentricity_change or self.eccentricity_results is None:
self._eccentricity_results = self.eccentricity_func(eccentricity)
self._need_to_collapse_modes = True
# Check if we need to update obliquity results
if self.use_obliquity_tides:
if obliquity is not None:
if obliquity_change or self.obliquity_results is None:
self._obliquity_results = self.obliquity_func(obliquity)
self._need_to_collapse_modes = True
else:
# We assume that the obliquity has the same shape of eccentricity even when it is not being used.
# OPT: Check to see if we can universally get away with having this just be a scalar rather than a
# potentially large array.
if eccentricity is not None:
zero_obliquity = np.zeros_like(eccentricity)
# Since obliquity tides are not used they will never change. So do not call the function unless forced
# to do so.
if self.obliquity_results is None or force_obliquity_update:
self._obliquity_results = self.obliquity_func(zero_obliquity)
self._need_to_collapse_modes = True
obliquity_results = self.obliquity_results
eccentricity_results = self.eccentricity_results
# Check the shape of the eccentricity and obliquity results
if obliquity_results is not None and eccentricity_results is not None:
# Check that obliquity and eccentricity results have the same size (same max_l used).
if len(obliquity_results) != len(eccentricity_results):
raise IncompatibleModelError(
'Obliquity and Eccentricity results do not have the same size.'
'max_tidal_l may not be equal for both functions.'
)
# Determine if new tidal frequencies need to be calculated
if spin_freq_changed or orbital_freq_changed:
if eccentricity_results is not None and obliquity_results is not None and \
spin_frequency is not None and orbital_frequency is not None:
# Update the tidal frequencies and terms using the new orbital frequency
self._unique_tidal_frequencies, self._tidal_terms_by_frequency = \
self.calculate_modes_func(
spin_frequency, orbital_frequency, semi_major_axis,
world_radius, eccentricity_results, obliquity_results,
self.multiply_modes_by_sign
)
# Now that there are new frequencies, tell the world so that new complex compliances can be calculated.
self._new_tidal_frequencies = True
self._need_to_collapse_modes = True
# Tell the planet to make any updates due to a potential frequency change.
if self._new_tidal_frequencies and call_world_frequency_changed:
# Updating the tidal frequencies will automatically call the tide's collapse_modes method. The
# collapse_tidal_modes flag prevents that method from being called more than once.
self.world.tidal_frequencies_changed(collapse_tidal_modes=False)
# Collapse tidal modes
if self._need_to_collapse_modes and call_collapse_modes:
self.collapse_modes()
# Return frequencies and tidal terms
return self.unique_tidal_frequencies, self.tidal_terms_by_frequency
[docs]
def fixed_q_dt_changed(self):
""" The fixed tidal dissipation parameters (fixed-q or fixed-dt) have changed. Make any necessary updates. """
log.debug(f'Method fixed_q_dt_changed called for {self}.')
# Updates done by child classes
[docs]
def clear_state(self):
""" Clear the tidal model's state properties """
super().clear_state()
self._eccentricity_results = None
self._obliquity_results = None
self._tidal_susceptibility = None
self._tidal_susceptibility_reduced = None
self._unique_tidal_frequencies = None
self._tidal_terms_by_frequency = None
self._tidal_heating_global = None
self._global_negative_imk_by_orderl = None
self._dUdM = None
self._dUdw = None
self._dUdO = None
self._need_to_collapse_modes = False
self._new_tidal_frequencies = False
[docs]
def set_fixed_q(self, fixed_q: float, run_updates: bool = True):
""" Set a new global tidal dissipation efficiency or 'fixed-Q' for the world
This is used in calculating tidal dissipation assuming a CPL model.
Notes
-----
.. There is some discussion that, mathematically, q should never be below 1. TidalPy does not check for this
but it is something to keep in mind.
Parameters
----------
fixed_q : float
New dissipation efficiency.
run_updates : bool = True
If `True`, this method will call the update tides method.
"""
log.debug(f'Method set_fixed_q called for {self}.')
self._fixed_q = fixed_q
if run_updates:
self.fixed_q_dt_changed()
[docs]
def set_fixed_dt(self, fixed_dt: float, run_updates: bool = True):
""" Set a new global tidal dissipation efficiency frequency scale 'dt' for the world
This is used in calculating tidal dissipation assuming a CTL model.
Parameters
----------
fixed_dt : float
New dissipation efficiency frequency scale [s].
run_updates : bool = True
If `True`, this method will call the update tides method.
"""
log.debug(f'Method set_fixed_dt called for {self}.')
self._fixed_dt = fixed_dt
if run_updates:
self.fixed_q_dt_changed()
[docs]
def set_state(self, fixed_q: float = None, fixed_dt: float = None, run_updates: bool = True):
""" Change the state of the Tides class
This is more efficient than calling the individual setters.
Parameters
----------
fixed_q : float (optional)
New dissipation efficiency.
fixed_dt : float (optional)
New dissipation efficiency frequency scale [s].
run_updates : bool = True
If `True`, this method will call the update tides method.
"""
log.debug(f'Method set_state called for {self}.')
if fixed_q is not None:
self.set_fixed_q(fixed_q, run_updates=False)
if fixed_dt is not None:
self.set_fixed_dt(fixed_dt, run_updates=False)
if run_updates:
self.fixed_q_dt_changed()
[docs]
def collapse_modes(self):
""" Calculate Global Love number based on current thermal state.
Requires a prior orbit_spin_changed() call as unique frequencies are used to calculate the complex compliances
used to calculate the Love numbers.
Returns
-------
tidal_heating : np.ndarray
Tidal heating [W]
This could potentially restricted to a layer or for an entire planet.
dUdM : np.ndarray
Tidal potential derivative with respect to the mean anomaly [J kg-1 radians-1]
This could potentially restricted to a layer or for an entire planet.
dUdw : np.ndarray
Tidal potential derivative with respect to the pericentre [J kg-1 radians-1]
This could potentially restricted to a layer or for an entire planet.
dUdO : np.ndarray
Tidal potential derivative with respect to the planet's node [J kg-1 radians-1]
This could potentially restricted to a layer or for an entire planet.
See Also
--------
TidalPy.tides.Tides.orbit_spin_changed
"""
log.debug(f'Method collapse_modes called for {self}.')
# Most of mode_collapse is implemented by a child class of TidesBase
# # Initialized properties
@property
def thermal_set(self) -> bool:
""" Flag to check if a Thermal Class has been initialized in the tide class host world. """
return self._thermal_set
@thermal_set.setter
def thermal_set(self, value):
raise InitiatedPropertyChangeError
@property
def orbit_set(self) -> bool:
""" Flag to check if an Orbit Class has been initialized in the tide class host world. """
return self._orbit_set
@orbit_set.setter
def orbit_set(self, value):
raise InitiatedPropertyChangeError
# # Configuration properties
@property
def eccentricity_truncation_lvl(self) -> int:
""" Maximum eccentricity truncation level to include in tidal calculations """
return self._eccentricity_truncation_lvl
@eccentricity_truncation_lvl.setter
def eccentricity_truncation_lvl(self, value):
# TODO: Think about if you want the user to update these. These setters could make a call to self.reinit()
# which the user could make on their own. So it may make sense to allow the setter.
raise ConfigPropertyChangeError
@property
def max_tidal_order_lvl(self) -> int:
""" Maximum tidal order to include in tidal calculations """
return self._max_tidal_order_lvl
@max_tidal_order_lvl.setter
def max_tidal_order_lvl(self, value):
raise ConfigPropertyChangeError
@property
def use_obliquity_tides(self) -> bool:
""" Flag for if obliquity tides should be calculated
Notes
-----
- Setting this to False leads to more efficient calculations than if the obliquity of a world is simply set to
zero.
"""
return self._use_obliquity_tides
@use_obliquity_tides.setter
def use_obliquity_tides(self, value):
raise ConfigPropertyChangeError
@property
def multiply_modes_by_sign(self):
""" Flag for if the tidal dissipation results should be multiplied by the mode's sign.
This should always be `True` unless doing specific tests.
"""
return self._multiply_modes_by_sign
@multiply_modes_by_sign.setter
def multiply_modes_by_sign(self, value):
raise ConfigPropertyChangeError
# # State properties
@property
def eccentricity_results(self) -> Dict[int, 'EccenOutput']:
""" Eccentricity function results (squared) stored by order_l """
return self._eccentricity_results
@eccentricity_results.setter
def eccentricity_results(self, value):
raise IncorrectMethodToSetStateProperty
@property
def obliquity_results(self) -> Dict[Tuple[int, int], 'InclinOutput']:
""" Obliquity/Inclination function results (squared) stored by integers (m, p) """
return self._obliquity_results
@obliquity_results.setter
def obliquity_results(self, value):
raise IncorrectMethodToSetStateProperty
@property
def tidal_susceptibility_reduced(self) -> 'FloatArray':
""" Tidal susceptibility (reduced, no semi-major axis dependence) [N m7] """
return self._tidal_susceptibility_reduced
@tidal_susceptibility_reduced.setter
def tidal_susceptibility_reduced(self, value):
raise IncorrectMethodToSetStateProperty
@property
def tidal_susceptibility(self) -> 'FloatArray':
""" Tidal susceptibility [N m] """
return self._tidal_susceptibility
@tidal_susceptibility.setter
def tidal_susceptibility(self, value):
raise IncorrectMethodToSetStateProperty
@property
def unique_tidal_frequencies(self) -> Dict['FreqSig', 'FloatArray']:
""" Unique tidal frequencies (abs(tidal modes)) stored by frequency signature """
return self._unique_tidal_frequencies
@unique_tidal_frequencies.setter
def unique_tidal_frequencies(self, value):
raise IncorrectMethodToSetStateProperty
@property
def tidal_terms_by_frequency(self) -> Dict['FreqSig', Dict[int, 'DissipTermsArray']]:
""" Tidal terms stored by frequency signature """
return self._tidal_terms_by_frequency
@tidal_terms_by_frequency.setter
def tidal_terms_by_frequency(self, value):
raise IncorrectMethodToSetStateProperty
@property
def tidal_heating_global(self) -> 'FloatArray':
""" Global tidal heating rate [W] """
return self._tidal_heating_global
@tidal_heating_global.setter
def tidal_heating_global(self, value):
raise IncorrectMethodToSetStateProperty
@property
def global_negative_imk_by_orderl(self) -> Dict[int, 'FloatArray']:
""" Global negative of the imaginary portion of the Love number, -Im[k_l] """
return self._global_negative_imk_by_orderl
@global_negative_imk_by_orderl.setter
def global_negative_imk_by_orderl(self, value):
raise IncorrectMethodToSetStateProperty
@property
def global_love_by_orderl(self) -> Dict[int, 'FloatArray']:
""" Global complex Love number, k_l """
return self._global_love_by_orderl
@global_love_by_orderl.setter
def global_love_by_orderl(self, value):
raise IncorrectMethodToSetStateProperty
@property
def effective_q_by_orderl(self) -> Dict[int, 'FloatArray']:
""" World's effective tidal dissipation factor (for each tidal order level) """
return self._effective_q_by_orderl
@effective_q_by_orderl.setter
def effective_q_by_orderl(self, value):
raise IncorrectMethodToSetStateProperty
@property
def dUdM(self) -> 'FloatArray':
""" Global partial derivative of the tidal potential with respect to the mean anomaly """
return self._dUdM
@dUdM.setter
def dUdM(self, value):
raise IncorrectMethodToSetStateProperty
@property
def dUdw(self) -> 'FloatArray':
""" Global partial derivative of the tidal potential with respect to the argument of pericentre """
return self._dUdw
@dUdw.setter
def dUdw(self, value):
raise IncorrectMethodToSetStateProperty
@property
def dUdO(self) -> 'FloatArray':
""" Global partial derivative of the tidal potential with respect to the argument of node """
return self._dUdO
@dUdO.setter
def dUdO(self, value):
raise IncorrectMethodToSetStateProperty
@property
def fixed_q(self) -> float:
""" Fixed tidal dissipation factor (used in CPL tidal calculation method) """
return self._fixed_q
@fixed_q.setter
def fixed_q(self, new_fixed_q: float):
self.set_fixed_q(new_fixed_q)
@property
def fixed_dt(self) -> float:
""" Fixed tidal dissipation frequency dependency (used in CTL tidal calculation method) """
return self._fixed_dt
@fixed_dt.setter
def fixed_dt(self, new_fixed_dt: float):
self.set_fixed_dt(new_fixed_dt)
@property
def fixed_k2(self) -> float:
""" Fixed static k2 for the world """
return self._fixed_k2
@fixed_k2.setter
def fixed_k2(self, value):
raise OuterscopePropertySetError
# # Outer-scope Properties
# World properties
@property
def semi_major_axis(self):
""" Outer-scope wrapper for world.semi_major_axis """
return self.world.semi_major_axis
@semi_major_axis.setter
def semi_major_axis(self, value):
raise OuterscopePropertySetError
@property
def orbital_frequency(self):
""" Outer-scope wrapper for world.orbital_frequency """
return self.world.orbital_frequency
@orbital_frequency.setter
def orbital_frequency(self, value):
raise OuterscopePropertySetError
@property
def spin_frequency(self):
""" Outer-scope wrapper for world.spin_frequency """
return self.world.spin_frequency
@spin_frequency.setter
def spin_frequency(self, value):
raise OuterscopePropertySetError
@property
def eccentricity(self):
""" Outer-scope wrapper for world.eccentricity """
return self.world.eccentricity
@eccentricity.setter
def eccentricity(self, value):
raise OuterscopePropertySetError
@property
def obliquity(self):
""" Outer-scope wrapper for world.obliquity """
return self.world.obliquity
@obliquity.setter
def obliquity(self, value):
raise OuterscopePropertySetError
@property
def tidal_host(self):
""" Outer-scope wrapper for world.orbit.tidal_host """
return self.world.tidal_host
@tidal_host.setter
def tidal_host(self, value):
raise OuterscopePropertySetError
@property
def radius(self):
""" Outer-scope wrapper for world.radius """
return self.world.radius
@radius.setter
def radius(self, value):
raise OuterscopePropertySetError
@property
def moi(self):
""" Outer-scope wrapper for world.moi """
return self.world.moi
@moi.setter
def moi(self, value):
raise OuterscopePropertySetError
# # Dunder methods
def __str__(self):
str_ = f'{self.__class__.__name__}'
if self.world is not None:
str_ += f' ({self.world})'
return str_
# # Static Methods
[docs]
@staticmethod
def calculate_tidal_susceptibility(
host_mass: float, target_radius: float,
semi_major_axis: 'FloatArray'
) -> 'FloatArray':
""" Calculate the tidal susceptibility for a target object orbiting
Wrapper for TidalPy.tides.dissipation.calc_tidal_susceptibility
Parameters
----------
host_mass : float
Tidal host's mass [kg]
target_radius : float
Target body's mean radius [m]
semi_major_axis : FloatArray
Orbital separation between the target and host [m]
Returns
-------
tidal_susceptibility : FloatArray
Tidal Susceptibility [N m]
"""
tidal_susceptibility = calc_tidal_susceptibility(host_mass, target_radius, semi_major_axis)
return tidal_susceptibility
[docs]
@staticmethod
def calculate_effective_rigidity(
shear_modulus: 'FloatArray', gravity: float, radius: float, bulk_density: float,
tidal_order_l: int = 2
) -> 'FloatArray':
""" Calculate the effective rigidity of a layer or planet
Wrapper for TidalPy.tides.love1d.effective_rigidity_general
Parameters
----------
shear_modulus : FloatArray
Shear modulus of a layer/planet [Pa]
gravity : float
Acceleration of gravity at the surface of a layer/planet [m s-2]
radius : float
Radius at the top of a layer/planet [m]
bulk_density : float
Bulk density of a layer/planet [kg m-3]
tidal_order_l : int = 2
Tidal harmonic order integer
Returns
-------
effective_rigidity : FloatArray
Effective rigidity of the layer/planet [Pa Pa-1]
"""
effective_rigidity = effective_rigidity_general(
shear_modulus, gravity, radius, bulk_density,
order_l=tidal_order_l
)
return effective_rigidity
[docs]
@staticmethod
def calculate_complex_love_number(
shear_modulus: 'FloatArray', complex_compliance: 'ComplexArray',
effective_rigidity: 'FloatArray', tidal_order_l: int = 2
) -> 'ComplexArray':
""" Calculate the complex Love number of a layer or planet
Wrapper for TidalPy.tides.love1d.complex_love_general
Parameters
----------
shear_modulus : FloatArray
Shear modulus of a layer/planet [Pa]
complex_compliance : ComplexArray
Complex compliance of a layer/planet [Pa -1]
effective_rigidity : FloatArray
Effective rigidity of a layer/planet [Pa Pa-1]
See: Tides.calculate_effective_rigidity
tidal_order_l : int = 2
Tidal harmonic order integer
Returns
-------
complex_love_number : ComplexArray
Complex Love number for the layer/planet
"""
complex_love_number = complex_love_general(
complex_compliance, shear_modulus, effective_rigidity,
order_l=tidal_order_l
)
return complex_love_number