Source code for TidalPy.toolbox.quick_tides

"""quick_tides.py module.

Purpose
-------
The primary purpose of this module is to quickly and easily provide the user with tidal dissipation calculation
    functions (heating and orbit-spin derivatives) for homogeneous worlds without requiring the user to manually
    pull together several different TidalPy functions.

Limitations
-----------
These functions may have an impact to performance and certainly to flexibility. For example, there are various
    checks performed in each that a user may want to skip. It is recommended to only use this function as a starting
    point and then develop a custom function or script to suite your specific needs.

These are intended to be used for homogeneous worlds with one layer. Dissipation for multi-layer worlds can be done
    with TidalPy's OOP approach or a custom script.

"""

import copy
from typing import Dict, TYPE_CHECKING, Tuple, Union

import numpy as np

from TidalPy.exceptions import ArgumentException, IncorrectArgumentType, MissingArgumentError, MissingAttributeError
from TidalPy.utilities.conversions import days2rads, orbital_motion2semi_a
from TidalPy.dynamics import semia_eccen_derivatives, semia_eccen_derivatives_dual, spin_rate_derivative
from TidalPy.rheology.complex_compliance import known_models as known_compliance_models
from TidalPy.rheology.complex_compliance.complex_compliance import compliance_dict_helper
from TidalPy.tides.ctl_funcs import linear_dt
from TidalPy.tides.dissipation import calc_tidal_susceptibility
from TidalPy.tides.methods.global_approx import cpl_neg_imk_helper_func, ctl_neg_imk_helper_func
from TidalPy.tides.modes.mode_manipulation import find_mode_manipulators

if TYPE_CHECKING:
    from TidalPy.utilities.types import ComplexArray, FloatArray, NoneType
    from TidalPy.structures.world_types import all_tidal_world_types

NoneFloatArray = Union['NoneType', 'FloatArray']
NoneFloat = Union['NoneType', float]
NoneInt = Union['NoneType', int]
SingleBodyResultType = Dict[str, Union['FloatArray', Dict[int, Union['ComplexArray', 'FloatArray']]]]


[docs] def quick_tidal_dissipation( host_mass: float, target_radius: float, target_mass: float, target_gravity: float, target_density: float, target_moi: float, viscosity: 'FloatArray' = None, shear_modulus: 'FloatArray' = None, rheology: str = 'Maxwell', complex_compliance_inputs: Tuple[float, ...] = None, eccentricity: 'FloatArray' = None, obliquity: 'FloatArray' = None, orbital_frequency: 'FloatArray' = None, orbital_period: 'FloatArray' = None, spin_frequency: 'FloatArray' = None, spin_period: 'FloatArray' = None, max_tidal_order_l: Union[int, 'NoneType'] = 2, eccentricity_truncation_lvl: Union[int, 'NoneType'] = 2, use_obliquity: Union[bool, 'NoneType'] = True, tidal_scale: float = 1., fixed_k2: float = 0.3, fixed_q: float = 100., fixed_dt: float = None, dspin_dt_scale: float = 1., de_dt_scale: float = 1., da_dt_scale: float = 1., precalculated_mode_results: dict = None, calculate_orbit_spin_derivatives: bool = False ) -> SingleBodyResultType: """ Calculate the single body tidal dissipation for a target world orbiting its tidal host. Assumes the host's dissipation is zero (no contribution to the orbital evolution derivatives). This function pulls together several TidalPy packages to offer an easy to use interface to calculate tidal dissipation. However it may come with an impact to performance and flexibility. For example, there are various checks performed that may want to skip. It is recommended to only use this function as a starting point and then develop a custom function or script to suite your specific needs. Parameters ---------- host_mass : float target_radius : float target_mass : float target_gravity : float target_density : float target_moi : float viscosity : FloatArray shear_modulus : FloatArray rheology : str complex_compliance_inputs : Tuple[float, ...] eccentricity : FloatArray obliquity : FloatArray orbital_frequency : FloatArray orbital_period : FloatArray spin_frequency : FloatArray spin_period : FloatArray max_tidal_order_l : int = 2 eccentricity_truncation_lvl : int = 2, use_obliquity : bool = True tidal_scale : float = 1. fixed_k2 : float = 0.3 fixed_q : float = 100. fixed_dt : float = None If `None`, a fixed_dt will be calculated as 1 / (Q * n) dspin_dt_scale : float = 1. de_dt_scale : float = 1. da_dt_scale : float = 1. precalculated_mode_results : dict = None Several things can be pre-calculated to save on performance if doing, for example, dual body analysis calculate_orbit_spin_derivatives: bool = False If `True`, then the function will calculate the spin and orbital derivatives assuming the host is non-dissipative. Returns ------- dissipation_results : dict Dissipation results for the target world 'tidal_heating': FloatArray, 'dUdM': FloatArray, 'dUdw': FloatArray, 'dUdO': FloatArray, 'love_number_by_orderl': List[ComplexArray], 'negative_imk_by_orderl': List[FloatArray], 'effective_q_by_orderl': List[FloatArray] """ # Flag that tracks if array version of TidalPy's functions should be used or not. use_array = False # Get orbital frequency if orbital_frequency is None: if orbital_period is None: raise MissingArgumentError('Orbital period or frequency is required.') else: orbital_frequency = days2rads(orbital_period) semi_major_axis = orbital_motion2semi_a(orbital_frequency, host_mass, target_mass) if type(orbital_frequency) is np.ndarray: use_array = True # Get spin frequency if spin_frequency is None: if spin_period is None: # Assume spin-locked spin_frequency = orbital_frequency else: spin_frequency = days2rads(spin_period) if type(spin_frequency) is np.ndarray: use_array = True if type(orbital_frequency) is not np.ndarray: orbital_frequency = orbital_frequency * np.ones_like(spin_frequency) semi_major_axis = semi_major_axis * np.ones_like(spin_frequency) elif type(orbital_frequency) is np.ndarray: spin_frequency = spin_frequency * np.ones_like(orbital_frequency) # Determine eccentricity and obliquity types if obliquity is None: obliquity = 0. use_obliquity = False if eccentricity is not None: if type(eccentricity) is np.ndarray: use_array = True if obliquity is not None: if type(obliquity) is not np.ndarray: obliquity = obliquity * np.ones_like(eccentricity) else: obliquity = np.zeros_like(eccentricity) else: eccentricity = 0. if obliquity is not None: if type(obliquity) is np.ndarray: use_array = True eccentricity = np.zeros_like(obliquity) # Get rheological model functions use_cpl_ctl = False fixed_inputs = tuple() if rheology.lower() in ['cpl', 'fixed_q']: use_cpl_ctl = True shear_modulus = 1. rheo_func = cpl_neg_imk_helper_func fixed_inputs = (fixed_k2, fixed_q) elif rheology.lower() in ['ctl']: use_cpl_ctl = True shear_modulus = 1. rheo_func = ctl_neg_imk_helper_func if fixed_dt is None: fixed_dt = (1. / fixed_q) * (1. / orbital_frequency) fixed_inputs = (fixed_k2, linear_dt, (fixed_dt,)) else: # Determine strength types if viscosity is None or shear_modulus is None: raise MissingArgumentError('Viscosity and shear modulus are required for non-CPL/CTL calculations.') if type(viscosity) is np.ndarray: use_array = True if type(shear_modulus) is not np.ndarray: shear_modulus = shear_modulus * np.ones_like(viscosity) if type(shear_modulus) is np.ndarray: use_array = True if type(viscosity) is not np.ndarray: viscosity = viscosity * np.ones_like(viscosity) # Find complex compliance functions rheo_lookup_name = rheology.lower() rheo_func = known_compliance_models[rheo_lookup_name] # Get mode functions if precalculated_mode_results is None: tidal_term_freq_calculator, collapse_modes_func, eccentricity_func, inclination_func = \ find_mode_manipulators( max_order_l=max_tidal_order_l, eccentricity_truncation_lvl=eccentricity_truncation_lvl, use_obliquity=use_obliquity ) else: tidal_term_freq_calculator = precalculated_mode_results['tidal_term_freq_calculator'] collapse_modes_func = precalculated_mode_results['collapse_modes_func'] eccentricity_func = precalculated_mode_results['eccentricity_func'] inclination_func = precalculated_mode_results['inclination_func'] # Calculate tidal susceptibility tidal_susceptibility = calc_tidal_susceptibility(host_mass, target_radius, semi_major_axis) # Calculate eccentricity and obliquity results if precalculated_mode_results is not None: # To increase performance, you can calculate eccentricity functions outside this function eccentricity_results_byorderl = precalculated_mode_results['eccentricity_results'] else: eccentricity_results_byorderl = eccentricity_func(eccentricity) obliquity_results_byorderl = inclination_func(obliquity) # Calculate tidal frequencies unique_frequencies, tidal_results_by_frequency = \ tidal_term_freq_calculator( spin_frequency, orbital_frequency, semi_major_axis, target_radius, eccentricity_results_byorderl, obliquity_results_byorderl, multiply_modes_by_sign=True ) # Calculate complex compliances if use_cpl_ctl: complex_compliance_by_freq = rheo_func(unique_frequencies, *fixed_inputs) else: if complex_compliance_inputs is None: complex_compliance_inputs = tuple() complex_compliance_by_freq = compliance_dict_helper( unique_frequencies, rheo_func, (shear_modulus**(-1), viscosity), complex_compliance_inputs ) # Collapse tidal modes tidal_heating, dUdM, dUdw, dUdO, love_number_by_orderl, negative_imk_by_orderl, effective_q_by_orderl = \ collapse_modes_func( target_gravity, target_radius, target_density, shear_modulus, tidal_scale, host_mass, tidal_susceptibility, complex_compliance_by_freq, tidal_results_by_frequency, max_order_l=max_tidal_order_l, cpl_ctl_method=use_cpl_ctl ) # Store results dissipation_results = { 'tidal_heating' : tidal_heating, 'tidal_torque' : host_mass * dUdO, 'dUdM' : dUdM, 'dUdw' : dUdw, 'dUdO' : dUdO, 'love_number_by_orderl' : love_number_by_orderl, 'negative_imk_by_orderl': negative_imk_by_orderl, 'effective_q_by_orderl' : effective_q_by_orderl, 'use_array' : use_array, 'semi_major_axis' : semi_major_axis, 'orbital_frequency' : orbital_frequency } if calculate_orbit_spin_derivatives: # Calculate orbit-spin derivatives dspin_dt = spin_rate_derivative(dUdO, target_moi, host_mass) da_dt, de_dt = semia_eccen_derivatives( semi_major_axis, orbital_frequency, eccentricity, target_mass, dUdM, dUdw, host_mass ) # Store results dissipation_results['spin_rate_derivative'] = dspin_dt * dspin_dt_scale dissipation_results['eccentricity_derivative'] = de_dt * de_dt_scale dissipation_results['semi_major_axis_derivative'] = da_dt * da_dt_scale return dissipation_results
[docs] def quick_dual_body_tidal_dissipation( radii: Tuple[float, float], masses: Tuple[float, float], gravities: Tuple[float, float], densities: Tuple[float, float], mois: Tuple[float, float], viscosities: Union['NoneType', Tuple[NoneFloatArray, NoneFloatArray]] = None, shear_moduli: Union['NoneType', Tuple[NoneFloatArray, NoneFloatArray]] = None, rheologies: Union[str, Tuple[str, str]] = 'Maxwell', complex_compliance_inputs: Union['NoneType', Tuple[Tuple[float, ...], Tuple[float, ...]]] = None, obliquities: Union['NoneType', Tuple[NoneFloatArray, NoneFloatArray]] = None, spin_frequencies: Union['NoneType', Tuple[NoneFloatArray, NoneFloatArray]] = None, spin_periods: Union['NoneType', Tuple[NoneFloatArray, NoneFloatArray]] = None, tidal_scales: Tuple[float, float] = (1., 1.), fixed_k2s: Tuple[float, float] = (0.3, 0.3), fixed_qs: Tuple[float, float] = (100., 100.), fixed_dts: Tuple[NoneFloat, NoneFloat] = (None, None), eccentricity: NoneFloatArray = None, orbital_frequency: NoneFloatArray = None, orbital_period: NoneFloatArray = None, max_tidal_order_l: NoneInt = 2, eccentricity_truncation_lvl: NoneInt = 2, use_obliquity: Union[bool, 'NoneType'] = True, da_dt_scale: float = 1., de_dt_scale: float = 1., dspin_dt_scale: float = 1. ) -> Dict[str, Union['FloatArray', SingleBodyResultType]]: """ Calculate the dual-body tidal dissipation for a target world orbiting its tidal host. This function pulls together several TidalPy packages to offer an easy to use interface to calculate tidal dissipation. However it may come with an impact to performance and flexibility. For example, there are various checks performed that may want to skip. It is recommended to only use this function as a starting point and then develop a custom function or script to suite your specific needs. Parameters ---------- radii : Tuple[float, float] masses : Tuple[float, float] gravities : Tuple[float, float] densities : Tuple[float, float] mois : Tuple[float, float] viscosities : Union[NoneType, Tuple[NoneFloatArray, NoneFloatArray]] = None shear_moduli : Union[NoneType, Tuple[NoneFloatArray, NoneFloatArray]] = None rheologies : Union[str, Tuple[str, str]] = 'Maxwell' complex_compliance_inputs : Union[NoneType, Tuple[Tuple[float, ...], Tuple[float, ...]]] = None obliquities : Union[NoneType, Tuple[NoneFloatArray, NoneFloatArray]] = None spin_frequencies : Union[NoneType, Tuple[NoneFloatArray, NoneFloatArray]] = None spin_periods : Union[NoneType, Tuple[NoneFloatArray, NoneFloatArray]] = None tidal_scales : Tuple[float, float] = (1., 1.) fixed_k2s : Tuple[float, float] = (.3, .3) fixed_qs : Tuple[float, float] = (100., 100.) fixed_dts : Tuple[NoneFloat, NoneFloat] = (None, None) If any are `None`, a fixed_dt will be calculated as 1 / (Q * n) eccentricity: NoneFloatArray = None orbital_frequency : NoneFloatArray = None orbital_period : NoneFloatArray = None max_tidal_order_l : NoneInt = 2 eccentricity_truncation_lvl : NoneInt = 2 use_obliquity : Union[bool, NoneType] = True da_dt_scale : float = 1. de_dt_scale : float = 1. dspin_dt_scale : float = 1. Returns ------- dissipation_results: Dict[str, Union[FloatArray, SingleBodyResultType]] Tidal dissipation results stored for both the `host` and `secondary`. """ # Check for issues if type(radii) not in [list, tuple]: raise IncorrectArgumentType('Dual-body quick tidal dissipation calculation requires inputs in list format.') elif len(radii) != 2: raise ArgumentException('Unexpected number of list items.') # Flag if array functions should be used or not. use_array = False # Clean up inputs if eccentricity is None: eccentricity = 0. elif type(eccentricity) is np.ndarray: use_array = True # Since we use the eccentricity now, outside the world calculators, we need to make sure that the obliquity and # eccentricity arrays are the correct shape. The user provided use_obliquity overrides this functions best # guess. use_obliquity_flags = list() obliquities_cleaned = list() for world_i in range(2): if obliquities is None: obliq_present_flag = False obliquity = 0. elif obliquities[world_i] is None: obliq_present_flag = False obliquity = 0. else: obliq_present_flag = True obliquity = obliquities[world_i] if use_array: obliquity = obliquity * np.ones_like(eccentricity) obliquities_cleaned.append(obliquity) use_obliquity_flags.append(obliq_present_flag and use_obliquity) del obliquities use_obliquity_flags = tuple(use_obliquity_flags) obliquities_cleaned = tuple(obliquities_cleaned) # Clean up any arguments that the user did not provide if viscosities is None: viscosities = (None, None) if shear_moduli is None: shear_moduli = (None, None) if rheologies is None: rheologies = ('Maxwell', 'Maxwell') elif type(rheologies) is str: # User may have only provided one string indicating they want the same rheology for both planets. rheologies = (rheologies, rheologies) if complex_compliance_inputs is None: complex_compliance_inputs = (tuple(), tuple()) elif type(complex_compliance_inputs) is tuple: if type(complex_compliance_inputs[0]) is tuple or type(complex_compliance_inputs[1]) is tuple: if complex_compliance_inputs[0] is None: complex_compliance_inputs = (tuple(), complex_compliance_inputs[1]) elif complex_compliance_inputs[1] is None: complex_compliance_inputs = (complex_compliance_inputs[0], tuple()) elif complex_compliance_inputs[0] is None and complex_compliance_inputs[1] is None: complex_compliance_inputs = (None, None) else: # User may have only provided one rheology input if they intend for the same rheology for both planets. complex_compliance_inputs = (complex_compliance_inputs, complex_compliance_inputs) if spin_frequencies is None: spin_frequencies = (None, None) if spin_periods is None: spin_periods = (None, None) if tidal_scales is None: tidal_scales = (1., 1.) if fixed_k2s is None: fixed_k2s = (.3, .3) if fixed_qs is None: fixed_qs = (100., 100.) if fixed_dts is None: fixed_dts = (None, None) # Get orbital frequency if orbital_frequency is None: if orbital_period is None: raise MissingArgumentError('Orbital period or frequency is required.') else: orbital_frequency = days2rads(orbital_period) semi_major_axis = orbital_motion2semi_a(orbital_frequency, masses[0], masses[1]) if type(orbital_frequency) is np.ndarray: use_array = True # Determine what calculators to use (these will be the same for both the Host & Target worlds). precalculated_mode_results = dict() tidal_term_freq_calculator, collapse_modes_func, eccentricity_func, inclination_func = \ find_mode_manipulators( max_order_l=max_tidal_order_l, eccentricity_truncation_lvl=eccentricity_truncation_lvl, use_obliquity=any(use_obliquity_flags) ) precalculated_mode_results['tidal_term_freq_calculator'] = tidal_term_freq_calculator precalculated_mode_results['collapse_modes_func'] = collapse_modes_func precalculated_mode_results['eccentricity_func'] = eccentricity_func precalculated_mode_results['inclination_func'] = inclination_func # Calculate the eccentricity function results as they will be the same for both the host and target worlds precalculated_mode_results['eccentricity_results'] = eccentricity_func(eccentricity) # Store dissipation results in a dict dissipation_results = dict() for world_i in range(2): # Assume the first world is the tidal host for naming purposes. Both world's mass will be used as the "host" # mass for dissipation calculations. if world_i == 0: world_name = 'host' host_mass = masses[1] else: world_name = 'secondary' host_mass = masses[0] # Calculate dissipation for the world dissipation_results_for_world = \ quick_tidal_dissipation( host_mass, target_radius=radii[world_i], target_mass=masses[world_i], target_gravity=gravities[world_i], target_density=densities[world_i], target_moi=mois[world_i], viscosity=viscosities[world_i], shear_modulus=shear_moduli[world_i], rheology=rheologies[world_i], complex_compliance_inputs=complex_compliance_inputs[world_i], eccentricity=eccentricity, obliquity=obliquities_cleaned[world_i], orbital_frequency=orbital_frequency, orbital_period=orbital_period, spin_frequency=spin_frequencies[world_i], spin_period=spin_periods[world_i], max_tidal_order_l=max_tidal_order_l, eccentricity_truncation_lvl=eccentricity_truncation_lvl, use_obliquity=use_obliquity_flags[world_i], tidal_scale=tidal_scales[world_i], fixed_k2=fixed_k2s[world_i], fixed_q=fixed_qs[world_i], fixed_dt=fixed_dts[world_i], precalculated_mode_results=precalculated_mode_results, calculate_orbit_spin_derivatives=False ) # Calculate spin-rate derivatives dissipation_results_for_world['spin_rate_derivative'] = \ spin_rate_derivative(dissipation_results_for_world['dUdO'], mois[world_i], host_mass) * dspin_dt_scale # Store Results dissipation_results[world_name] = dissipation_results_for_world # Confirm if arrays should be used or not if type(dissipation_results_for_world['dUdM']) is np.ndarray: use_array = True # Now that both world's dissipation is calculated we can find the dual-body evolution derivatives da_dt, de_dt = \ semia_eccen_derivatives_dual( semi_major_axis, orbital_frequency, eccentricity, masses[0], dissipation_results['host']['dUdM'], dissipation_results['host']['dUdw'], masses[1], dissipation_results['secondary']['dUdM'], dissipation_results['secondary']['dUdw'] ) # Store results dissipation_results['eccentricity_derivative'] = de_dt * de_dt_scale dissipation_results['semi_major_axis_derivative'] = da_dt * da_dt_scale return dissipation_results
[docs] def single_dissipation_from_dict_or_world_instance( host: Union[dict, 'all_tidal_world_types'], secondary: Union[dict, 'all_tidal_world_types'], viscosity: NoneFloatArray = None, shear_modulus: NoneFloatArray = None, rheology: str = 'Maxwell', complex_compliance_inputs: Union['NoneType', Tuple[float, ...]] = None, eccentricity: NoneFloatArray = None, obliquity: NoneFloatArray = None, orbital_frequency: NoneFloatArray = None, orbital_period: NoneFloatArray = None, spin_frequency: NoneFloatArray = None, spin_period: NoneFloatArray = None, max_tidal_order_l: NoneInt = 2, eccentricity_truncation_lvl: NoneInt = 2, use_obliquity: Union[bool, 'NoneType'] = True, tidal_scale: float = 1., fixed_k2: float = 0.3, fixed_q: float = 100., fixed_dt: float = None, da_dt_scale: float = 1., de_dt_scale: float = 1., dspin_dt_scale: float = 1. ) -> SingleBodyResultType: """ By providing a dictionaries or TidalPy world objects, this function will pull out the necessary planetary parameters and calculate the single body tidal dissipation. It is assumed that the host world does not participate in the tidal dissipation. Parameters ---------- host : Union[dict, 'all_tidal_world_types'] secondary : Union[dict, 'all_tidal_world_types'] viscosity : NoneFloatArray = None shear_modulus : NoneFloatArray = None rheology: str = 'Maxwell' complex_compliance_inputs : Union[NoneType, Tuple[float, ...]] = None eccentricity : NoneFloatArray = None obliquity : NoneFloatArray = None orbital_frequency : NoneFloatArray = None orbital_period : NoneFloatArray = None spin_frequency : NoneFloatArray = None spin_period : NoneFloatArray = None max_tidal_order_l : NoneInt = 2 eccentricity_truncation_lvl : NoneInt = 2 use_obliquity : Union[bool, NoneType] = True tidal_scale : float = 1. fixed_k2 : float = 0.3 fixed_q : float = 100. fixed_dt : float = None da_dt_scale : float = 1. de_dt_scale : float = 1. dspin_dt_scale : float = 1. Returns ------- dissipation_results : SingleBodyResultType Tidal dissipation results for single body dissipation. """ # If the user provided a world instance, convert it to a dict. if type(host) is dict: host_dict = host else: host_dict = copy.deepcopy(host.config) # Pull out items that may have been calculated in the planet instance for param_name in ['mass']: if param_name in dir(host): host_dict[param_name] = getattr(host, param_name) else: raise MissingAttributeError host_mass = host_dict['mass'] if type(secondary) is dict: secondary_dict = secondary else: secondary_dict = copy.deepcopy(secondary.config) # Pull out items that may have been calculated in the planet instance for param_name in ['radius', 'mass', 'gravity_surface', 'density_bulk', 'moi']: if param_name in dir(secondary): secondary_dict[param_name] = getattr(secondary, param_name) else: raise MissingAttributeError # Determine eccentricity and obliquity types if obliquity is None: obliquity = 0. use_obliquity = False if eccentricity is not None: if type(eccentricity) is np.ndarray: if obliquity is not None: if type(obliquity) is not np.ndarray: obliquity = obliquity * np.ones_like(eccentricity) else: obliquity = np.zeros_like(eccentricity) else: eccentricity = 0. if obliquity is not None: if type(obliquity) is np.ndarray: eccentricity = np.zeros_like(obliquity) # Calculate single body response single_body_response = quick_tidal_dissipation( host_mass=host_mass, target_radius=secondary_dict['radius'], target_mass=secondary_dict['mass'], target_gravity=secondary_dict['gravity_surface'], target_density=secondary_dict['density_bulk'], target_moi=secondary_dict['moi'], viscosity=viscosity, shear_modulus=shear_modulus, rheology=rheology, complex_compliance_inputs=complex_compliance_inputs, eccentricity=eccentricity, obliquity=obliquity, orbital_frequency=orbital_frequency, orbital_period=orbital_period, spin_frequency=spin_frequency, spin_period=spin_period, max_tidal_order_l=max_tidal_order_l, eccentricity_truncation_lvl=eccentricity_truncation_lvl, use_obliquity=use_obliquity, tidal_scale=tidal_scale, fixed_k2=fixed_k2, fixed_q=fixed_q, fixed_dt=fixed_dt, dspin_dt_scale=dspin_dt_scale, da_dt_scale=da_dt_scale, de_dt_scale=de_dt_scale, calculate_orbit_spin_derivatives=True ) return single_body_response
[docs] def dual_dissipation_from_dict_or_world_instance( host: Union[dict, 'all_tidal_world_types'], secondary: Union[dict, 'all_tidal_world_types'], viscosities: Union['NoneType', Tuple[NoneFloatArray, NoneFloatArray]] = None, shear_moduli: Union['NoneType', Tuple[NoneFloatArray, NoneFloatArray]] = None, rheologies: Union[str, Tuple[str, str]] = 'Maxwell', complex_compliance_inputs: Union['NoneType', Tuple[Tuple[float, ...], Tuple[float, ...]]] = None, obliquities: Union['NoneType', Tuple[NoneFloatArray, NoneFloatArray]] = None, spin_frequencies: Union['NoneType', Tuple[NoneFloatArray, NoneFloatArray]] = None, spin_periods: Union['NoneType', Tuple[NoneFloatArray, NoneFloatArray]] = None, tidal_scales: Tuple[float, float] = (1., 1.), fixed_k2s: Tuple[float, float] = (0.3, 0.3), fixed_qs: Tuple[float, float] = (100., 100.), fixed_dts: Tuple[NoneFloat, NoneFloat] = (None, None), eccentricity: NoneFloatArray = None, orbital_frequency: NoneFloatArray = None, orbital_period: NoneFloatArray = None, max_tidal_order_l: NoneInt = 2, eccentricity_truncation_lvl: NoneInt = 2, use_obliquity: Union[bool, 'NoneType'] = True, da_dt_scale: float = 1., de_dt_scale: float = 1., dspin_dt_scale: float = 1. ) -> Dict[str, Union['FloatArray', SingleBodyResultType]]: """ By providing a dictionaries or TidalPy world objects, this function will pull out the necessary planetary parameters and calculate the single body tidal dissipation. It is assumed that both the host and the secondary participate in tidal dissipation. Parameters ---------- host : Union[dict, 'all_tidal_world_types'] secondary : Union[dict, 'all_tidal_world_types'] viscosities : Union[NoneType, Tuple[NoneFloatArray, NoneFloatArray]] = None shear_moduli : Union[NoneType, Tuple[NoneFloatArray, NoneFloatArray]] = None rheologies : Union[str, Tuple[str, str]] = 'Maxwell' complex_compliance_inputs : Union[NoneType, Tuple[Tuple[float, ...], Tuple[float, ...]]] = None obliquities : Union[NoneType, Tuple[NoneFloatArray, NoneFloatArray]] = None spin_frequencies : Union[NoneType, Tuple[NoneFloatArray, NoneFloatArray]] = None spin_periods : Union[NoneType, Tuple[NoneFloatArray, NoneFloatArray]] = None tidal_scales : Tuple[float, float] = (1., 1.) fixed_k2s : Tuple[float, float] = (0.3, 0.3) fixed_qs : Tuple[float, float] = (100., 100.) fixed_dts : Tuple[NoneFloat, NoneFloat] = (None, None) eccentricity : NoneFloatArray = None orbital_frequency : NoneFloatArray = None orbital_period : NoneFloatArray = None max_tidal_order_l : NoneInt = 2 eccentricity_truncation_lvl : NoneInt = 2 use_obliquity : Union[bool, NoneType] = True da_dt_scale : float = 1. de_dt_scale : float = 1. dspin_dt_scale : float = 1. Returns ------- dissipation_results : Dict[str, Union[FloatArray, SingleBodyResultType]] A dictionary of the dissipation results for both the `host` and `secondary` tidal world. """ # If the user provided a world instance, convert it to a dict. if type(host) is dict: host_dict = host else: host_dict = copy.deepcopy(host.config) # Pull out items that may have been calculated in the planet instance for param_name in ['radius', 'mass', 'gravity_surface', 'density_bulk', 'moi']: if param_name in dir(host): host_dict[param_name] = getattr(host, param_name) else: raise MissingAttributeError if type(secondary) is dict: secondary_dict = secondary else: secondary_dict = copy.deepcopy(secondary.config) # Pull out items that may have been calculated in the planet instance for param_name in ['radius', 'mass', 'gravity_surface', 'density_bulk', 'moi']: if param_name in dir(secondary): secondary_dict[param_name] = getattr(secondary, param_name) else: raise MissingAttributeError # Call the quick dual body tidal dissipation calculator dissipation_results = \ quick_dual_body_tidal_dissipation( radii=(host_dict['radius'], secondary_dict['radius']), masses=(host_dict['mass'], secondary_dict['mass']), gravities=(host_dict['gravity_surface'], secondary_dict['gravity_surface']), densities=(host_dict['density_bulk'], secondary_dict['density_bulk']), mois=(host_dict['moi'], secondary_dict['moi']), viscosities=viscosities, shear_moduli=shear_moduli, rheologies=rheologies, complex_compliance_inputs=complex_compliance_inputs, obliquities=obliquities, spin_frequencies=spin_frequencies, spin_periods=spin_periods, tidal_scales=tidal_scales, fixed_k2s=fixed_k2s, fixed_qs=fixed_qs, fixed_dts=fixed_dts, eccentricity=eccentricity, orbital_frequency=orbital_frequency, orbital_period=orbital_period, max_tidal_order_l=max_tidal_order_l, eccentricity_truncation_lvl=eccentricity_truncation_lvl, use_obliquity=use_obliquity, da_dt_scale=da_dt_scale, de_dt_scale=de_dt_scale, dspin_dt_scale=dspin_dt_scale ) return dissipation_results