Source code for astromugs.modeling.Envelope

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
File name: Envelope
Last update: May 2021
Language: Python 3.8

Short description:
    Geometrical model of a Class 0/I envelope.
    Every input and output is in AU, except densities which are
    returned in g cm^-3.
"""

import numpy as np
from scipy.optimize import brenth
from scipy.integrate import trapezoid

from dataclasses import dataclass, fields, is_dataclass
from typing import Optional, Literal, Any

from astromugs.utils.struct import EnvelopeParams
from astromugs.constants.constants import (
    mu,
    autocm,
    amu,
    Ggram,
    kb,
    M_sun,
)


# ------------------------------------------------------------------------------
#   _________   ___    __  ___        ___
#  |   ______| |   \  |  | \  \      /  /
#  |  |______  |    \ |  |  \  \    /  /
#  |   ______| |  |\ \|  |   \  \  /  /
#  |  |______  |  | \    |    \  \/  /
#  |_________| |__|  \___|     \____/
# ------------------------------------------------------------------------------
[docs] class Envelope:
[docs] def __init__( self, params: EnvelopeParams, dust: Optional[object] = None ): self.params = params self.dust = dust
# # copy dataclass attributes to instance variables (optional) #for field in params.__dataclass_fields__: #setattr(self, field, getattr(params, field)) """ The following methods give the physical properties related to the gas and dust in the envelope. The gas number density is relative to hydrogen nuclei. """ def density(self, x1: np.ndarray, x2: np.ndarray, x3: Optional[np.ndarray] = None) -> np.ndarray: """ A) Return dust density rho_d(r, z, a) or rho_d(r, theta, phi, a). Returns ------- rho : np.ndarray 3D array with shape (len(r), len(nb_sizes), len(z)), units: g cm^-3 """ if self.params.coordsystem == "spherical": rt, tt, pp = np.meshgrid(x1 * autocm, x2, x3, indexing="ij") mu_val = np.cos(tt) RR = rt * np.sin(tt) zz = rt * np.cos(tt) mu0 = np.zeros_like(mu_val) # Find mu0 via root finding for ir in range(rt.shape[0]): for it in range(rt.shape[1]): mu0[ir, it, 0] = brenth( self.solution, -1.0, 1.0, args=(rt[ir, it, 0], mu_val[ir, it, 0]), ) rho0 = 1.0 rho = ( rho0 * (rt / self.params.r_centri) ** -1.5 * (1 + mu_val / mu0) ** -0.5 * (mu_val / mu0 + 2 * mu0**2 * self.params.r_centri / rt) ** -1 ) mid1 = (np.abs(mu_val) < 1e-10) & (rt < self.params.r_centri) rho[mid1] = ( rho0 * (rt[mid1] / self.params.r_centri) ** -0.5 * (1 - rt[mid1] / self.params.r_centri) ** -1 / 2 ) mid2 = (np.abs(mu_val) < 1e-10) & (rt > self.params.r_centri) rho[mid2] = ( rho0 * (2 * rt[mid2] / self.params.r_centri - 1) ** -0.5 * (rt[mid2] / self.params.r_centri - 1) ** -1 ) rho[(rt > self.params.rmax * autocm) ^ (rt < self.params.rmin * autocm)] = 0.0 # Mass normalization if x2.max() > np.pi / 2: mdot = ( (self.params.dust_mass / self.params.dtogas) * M_sun ) / ( 2 * np.pi * trapezoid( trapezoid(rho * rt**2 * np.sin(tt), tt, axis=1), rt[:, 0, :], axis=0, ) )[0] else: mdot = ( (self.params.dust_mass / self.params.dtogas) * M_sun ) / ( 4 * np.pi * trapezoid( trapezoid(rho * rt**2 * np.sin(tt), tt, axis=1), rt[:, 0, :], axis=0, ) )[0] rho *= mdot # Outflow cavity cavity_mask = ( np.abs(zz) / autocm - self.params.cavz0 / autocm - (RR / autocm) ** self.params.cavpl > 0.0 ) rho[cavity_mask] *= self.params.cav_fact return rho def numberdensity(self, x1, x2, x3=None): """ B) Return gas number density in cm^-3. """ ng = self.density(x1, x2, x3) / (mu * amu) return ng def density_d(self, x1, x2, x3=None): """ C) Return dust density rho_d(r, z, a). Notes ----- Returns 3D array (len(nb_sizes), len(r), len(z)). Units: g cm^-3 """ rhog = self.density(x1, x2, x3) fraction = self.dust.massfraction() rhod = np.ones( (len(fraction), len(x1), len(x2), len(x3)) ) for i in range(len(fraction)): rhod[i] = fraction[i] * self.params.dtogas * rhog return rhod def numberdensity_d(self, x1, x2, x3=None): """ D) Return dust number density n_d(r, z, a). Units: cm^-3 """ mass = self.dust.grainmass() dens = self.density_d(x1, x2, x3) for i in range(len(mass)): dens[i] /= mass[i] return dens def solution(self, mu0, r, mu_val): """ E) Return the solution function used in the root finding. """ return ( mu0**3 - mu0 * (1 - r / self.params.r_centri) - mu_val * (r / self.params.r_centri) )