Source code for promweaver.j_prom_bc

from typing import Optional

import numpy as np
from numpy.polynomial.legendre import leggauss

from .bc_provider import PromBcProvider
from .limb_darkening import outgoing_chromo_ray_mu
from .prom_bc import PromBc


[docs] class UniformJPromBc(PromBc): r""" Simple boundary condition that supplies :math:`J_\nu` at each incoming ray and frequency for the boundary condition. This value is uniform over each ray. Putting the boundary condition into `final_synthesis` mode by requesting a single `compute_rays` from the model, or setting `final_synthesis` to true on the `lw.Atmosphere` used in the model samples the radiation directly along the ray path, as the converged populations have been determined. Parameters ---------- projection : str filament or prominence bc_provider : PromBcProvider The provider to be used for computing the necessary intensity values. altitude_m : float The altitude of the prominence above the solar surface [m] prom_upper_lower : str, optional Whether this is the 'upper' or 'lower' z-boundary for a prominence (not used in filament cases). Nrays : int, optional The number of Gauss-Legendre rays to be used to compute J. Default: 50. """ def __init__( self, projection: str, bc_provider: PromBcProvider, altitude_m: float, prom_upper_lower: Optional[str] = None, Nrays: int = 50, ): self.provider = bc_provider self.projection = projection self.altitude = altitude_m if projection not in ["prominence", "filament"]: raise ValueError( f"Expected projection ({projection}), to be 'prominence' or 'filament'" ) self.projection = projection self.bc_type = prom_upper_lower self.Nrays = Nrays Jmuz, Jwmu = leggauss(Nrays) Jmuz = 0.5 * Jmuz + 0.5 Jwmu *= 0.5 self.Jmuz = Jmuz self.Jwmu = Jwmu self.final_synthesis = False def update_bc(self, atmos, spect): Nmu = atmos.muz.shape[0] Nwave = spect.wavelength.shape[0] # NOTE(cmo): Final output variable Iinterp = np.zeros((Nwave, Nmu, 1)) final_synth = self.final_synthesis if Nmu != 1 else True is_prom = self.projection == "prominence" lower = self.bc_type == "lower" multi_I = self.provider.specialised_multi_I if final_synth or np.any(atmos.wmu == 0.0): mu_ins = [] for mu_idx in range(Nmu): if not final_synth and atmos.wmu[mu_idx] != 0.0: mu_ins.append(None) continue if is_prom: mu_out = atmos.mux[mu_idx] else: mu_out = atmos.muz[mu_idx] if is_prom: if (lower and mu_out >= 0.0) or ((not lower) and mu_out <= 0.0): # NOTE(cmo): From corona, so zero mu_ins.append(None) continue mu_out = abs(mu_out) mu_in = outgoing_chromo_ray_mu(mu_out, self.altitude) mu_ins.append(mu_in) if not multi_I: Iinterp[:, mu_idx, 0] = self.provider.compute_I( spect.wavelength, mu_in ) if multi_I: Iinterp[:, :, 0] = self.provider.compute_multi_I( spect.wavelength, mu_ins ) if not final_synth: J = np.zeros(Nwave) if multi_I: mu_ins = [outgoing_chromo_ray_mu(mu, self.altitude) for mu in self.Jmuz] Is = self.provider.compute_multi_I(spect.wavelength, mu_ins) for idx, wmu in enumerate(self.Jwmu): J += Is[:, idx] * wmu else: for mu, wmu in zip(self.Jmuz, self.Jwmu): mu_in = outgoing_chromo_ray_mu(mu, self.altitude) J += self.provider.compute_I(spect.wavelength, mu_in) * wmu # NOTE(jmj): 0.5 multiplication to account for flipping in radial # axis i.e., we don't explicitly consider coronal and so the # chromospheric input would be copy-pasted there if is_prom: J *= 0.5 for mu_idx in range(Nmu): if atmos.wmu[mu_idx] != 0.0: Iinterp[:, mu_idx, 0] = J[:] self.Iinterp = Iinterp self.muz_computed = np.copy(self.muz) self.wavelength_computed = np.copy(spect.wavelength)
[docs] def compute_bc(self, atmos, spect): any_param_change = False try: if self.final_synthesis != atmos.final_synthesis: self.final_synthesis = atmos.final_synthesis any_param_change = True except AttributeError: if self.final_synthesis: self.final_synthesis = False any_param_change = True try: if ( self.muz.shape[0] != self.muz_computed.shape[0] or np.any(self.muz != self.muz_computed) or self.wavelength_computed.shape[0] != spect.wavelength.shape[0] or np.any(self.wavelength_computed != spect.wavelength) ): any_param_change = True except AttributeError: any_param_change = True if any_param_change: self.update_bc(atmos, spect) return self.Iinterp