Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor spectrum #729

Merged
merged 10 commits into from
Jun 7, 2017
Merged
87 changes: 52 additions & 35 deletions tardis/montecarlo/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
from tardis.io.util import to_hdf

import numpy as np
import pandas as pd

logger = logging.getLogger(__name__)


class MontecarloRunner(object):
"""
This class is designed as an interface between the Python part and the
Expand All @@ -31,7 +31,7 @@ class MontecarloRunner(object):

def __init__(self, seed, spectrum_frequency, virtual_spectrum_range,
sigma_thomson, enable_reflective_inner_boundary,
inner_boundary_albedo, line_interaction_type, distance=None):
inner_boundary_albedo, line_interaction_type):

self.seed = seed
self.packet_source = packet_source.BlackBodySimpleSource(seed)
Expand All @@ -41,10 +41,6 @@ def __init__(self, seed, spectrum_frequency, virtual_spectrum_range,
self.enable_reflective_inner_boundary = enable_reflective_inner_boundary
self.inner_boundary_albedo = inner_boundary_albedo
self.line_interaction_type = line_interaction_type
self.spectrum = TARDISSpectrum(spectrum_frequency, distance)
self.spectrum_virtual = TARDISSpectrum(spectrum_frequency, distance)
self.spectrum_reabsorbed = TARDISSpectrum(spectrum_frequency, distance)


def _initialize_estimator_arrays(self, no_of_shells, tau_sobolev_shape):
"""
Expand All @@ -56,13 +52,12 @@ def _initialize_estimator_arrays(self, no_of_shells, tau_sobolev_shape):
model: ~Radial1DModel
"""

#Estimators
# Estimators
self.j_estimator = np.zeros(no_of_shells, dtype=np.float64)
self.nu_bar_estimator = np.zeros(no_of_shells, dtype=np.float64)
self.j_blue_estimator = np.zeros(tau_sobolev_shape)
self.Edotlu_estimator = np.zeros(tau_sobolev_shape)


def _initialize_geometry_arrays(self, model):
"""
Generate the cgs like geometry arrays for the montecarlo part
Expand Down Expand Up @@ -94,30 +89,33 @@ def _initialize_packets(self, T, no_of_packets, no_of_virtual_packets=None):
self.last_interaction_type = -1 * np.ones(no_of_packets, dtype=np.int64)
self.last_interaction_in_nu = np.zeros(no_of_packets, dtype=np.float64)

self._montecarlo_virtual_luminosity = u.Quantity(
np.zeros_like(self.spectrum_frequency.value),
'erg / s'
)

self.legacy_montecarlo_virtual_luminosity = np.zeros_like(
self.spectrum_frequency.value)

def legacy_update_spectrum(self, no_of_virtual_packets):
montecarlo_reabsorbed_luminosity = np.histogram(
self.reabsorbed_packet_nu,
weights=self.reabsorbed_packet_luminosity,
bins=self.spectrum_frequency.value)[0] * u.erg / u.s

montecarlo_emitted_luminosity = np.histogram(
self.emitted_packet_nu,
weights=self.emitted_packet_luminosity,
bins=self.spectrum_frequency.value)[0] * u.erg / u.s
@property
def spectrum(self):
return TARDISSpectrum(
self.spectrum_frequency,
self.montecarlo_emitted_luminosity)

self.spectrum.update_luminosity(montecarlo_emitted_luminosity)
self.spectrum_reabsorbed.update_luminosity(
montecarlo_reabsorbed_luminosity)
@property
def spectrum_reabsorbed(self):
return TARDISSpectrum(
self.spectrum_frequency,
self.montecarlo_reabsorbed_luminosity)

if no_of_virtual_packets > 0:
self.montecarlo_virtual_luminosity = (
self.legacy_montecarlo_virtual_luminosity *
1 * u.erg / self.time_of_simulation)[:-1]
self.spectrum_virtual.update_luminosity(
@property
def spectrum_virtual(self):
if np.all(self.montecarlo_virtual_luminosity == 0):
warnings.warn(
"MontecarloRunner.spectrum_virtual"
"is zero. Please run the montecarlo simulation with"
"no_of_virtual_packets > 0", UserWarning)

return TARDISSpectrum(
self.spectrum_frequency,
self.montecarlo_virtual_luminosity)

def run(self, model, plasma, no_of_packets, no_of_virtual_packets=0, nthreads=1,last_run=False):
Expand Down Expand Up @@ -229,18 +227,37 @@ def emitted_packet_nu(self):
def reabsorbed_packet_nu(self):
return self.output_nu[~self.emitted_packet_mask]

@property
def emitted_packet_luminosity(self):
return self.packet_luminosity[self.emitted_packet_mask]

@property
def reabsorbed_packet_luminosity(self):
return -self.packet_luminosity[~self.emitted_packet_mask]

@property
def montecarlo_reabsorbed_luminosity(self):
return u.Quantity(
np.histogram(
self.reabsorbed_packet_nu,
weights=self.reabsorbed_packet_luminosity,
bins=self.spectrum_frequency.value)[0],
'erg / s'
)

@property
def emitted_packet_luminosity(self):
return self.packet_luminosity[self.emitted_packet_mask]
def montecarlo_emitted_luminosity(self):
return u.Quantity(
np.histogram(
self.emitted_packet_nu,
weights=self.emitted_packet_luminosity,
bins=self.spectrum_frequency.value)[0],
'erg / s'
)

@property
def reabsorbed_packet_luminosity(self):
return -self.packet_luminosity[~self.emitted_packet_mask]
def montecarlo_virtual_luminosity(self):
return self._montecarlo_virtual_luminosity[:-1] / self.time_of_simulation.value

def calculate_emitted_luminosity(self, luminosity_nu_start,
luminosity_nu_end):
Expand Down Expand Up @@ -374,5 +391,5 @@ def from_config(cls, config):
sigma_thomson=sigma_thomson,
enable_reflective_inner_boundary=config.montecarlo.enable_reflective_inner_boundary,
inner_boundary_albedo=config.montecarlo.inner_boundary_albedo,
line_interaction_type=config.plasma.line_interaction_type,
distance=config.supernova.get('distance', None))
line_interaction_type=config.plasma.line_interaction_type
)
2 changes: 1 addition & 1 deletion tardis/montecarlo/montecarlo.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ cdef initialize_storage_model(model, plasma, runner, storage_model_t *storage):
storage.spectrum_delta_nu = runner.spectrum_frequency.to('Hz').value[1] - runner.spectrum_frequency.to('Hz').value[0]

storage.spectrum_virt_nu = <double*> PyArray_DATA(
runner.legacy_montecarlo_virtual_luminosity)
runner._montecarlo_virtual_luminosity.value)

storage.sigma_thomson = runner.sigma_thomson.cgs.value
storage.inverse_sigma_thomson = 1.0 / storage.sigma_thomson
Expand Down
175 changes: 102 additions & 73 deletions tardis/montecarlo/spectrum.py
Original file line number Diff line number Diff line change
@@ -1,99 +1,128 @@
import os
import warnings
import numpy as np
from astropy import constants, units as u

from tardis.io.util import to_hdf
from astropy import units as u


class TARDISSpectrum(object):
"""
TARDIS Spectrum object
"""

def __init__(self, frequency, distance=None):
self._frequency = frequency
self.wavelength = self.frequency.to('angstrom', u.spectral())

self.distance = distance
TARDISSpectrum(_frequency, luminosity)

_frequency: astropy.units.Quantity with unit 'Hz' or a length
These are bin edges of frequency or wavelenght bins for the spectrum.

luminosity: astropy.units.Quantity with unit Energy per second
The luminosity in each bin of the spectrum.

self.delta_frequency = frequency[1] - frequency[0]

self._flux_nu = np.zeros_like(frequency.value) * u.Unit('erg / (s Hz cm^2)')
self._flux_lambda = np.zeros_like(frequency.value) * u.Unit('erg / (s Angstrom cm^2)')

self.luminosity_density_nu = np.zeros_like(self.frequency) * u.Unit('erg / (s Hz)')
self.luminosity_density_lambda = np.zeros_like(self.frequency) * u.Unit('erg / (s Angstrom)')
After manually adding a distance attribute, the properties 'flux_nu' and
'flux_lambda' become available
"""

@property
def frequency(self):
return self._frequency[:-1]
def __init__(self, _frequency, luminosity):

# Check for correct inputs
if not _frequency.shape[0] == luminosity.shape[0] + 1:
raise ValueError(
"shape of '_frequency' and 'luminosity' are not compatible"
": '{}' and '{}'".format(
_frequency.shape[0],
luminosity.shape[0])
)
self._frequency = _frequency.to('Hz', u.spectral())
self.luminosity = luminosity.to('erg / s')

self.frequency = self._frequency[:-1]
self.delta_frequency = self._frequency[1] - self._frequency[0]
self.wavelength = self.frequency.to('angstrom', u.spectral())

self.luminosity_density_nu = (
self.luminosity / self.delta_frequency).to('erg / (s Hz)')
self.luminosity_density_lambda = self.f_nu_to_f_lambda(
self.luminosity_density_nu,
)

@property
def flux_nu(self):
if self.distance is None:
raise AttributeError('supernova distance not supplied - flux calculation impossible')
else:
return self._flux_nu
warnings.simplefilter('always', DeprecationWarning)
warnings.warn(
"TARDISSpectrum.flux_nu is deprecated, "
"please use TARDISSpectrum.luminosity_to_flux() in the "
"future.",
category=DeprecationWarning, stacklevel=2)
warnings.simplefilter('default', DeprecationWarning)
try:
return self.luminosity_to_flux(
self.luminosity_density_nu,
self.distance)
except AttributeError:
raise AttributeError(
'distance is required as attribute of'
'{} to calculate "{}"'.format(
self.__class__.__name__,
'flux_nu'
)
)

@property
def flux_lambda(self):
if self.distance is None:
raise AttributeError('supernova distance not supplied - flux calculation impossible')
return self._flux_lambda

def update_luminosity(self, spectrum_luminosity):
self.luminosity_density_nu = (spectrum_luminosity / self.delta_frequency).to('erg / (s Hz)')
self.luminosity_density_lambda = self.f_nu_to_f_lambda(self.luminosity_density_nu.value) \
* u.Unit('erg / (s Angstrom)')

if self.distance is not None:
self._flux_nu = (self.luminosity_density_nu / (4 * np.pi * self.distance.to('cm')**2))

self._flux_lambda = self.f_nu_to_f_lambda(self.flux_nu.value) * u.Unit('erg / (s Angstrom cm^2)')
warnings.simplefilter('always', DeprecationWarning)
warnings.warn(
"TARDISSpectrum.flux_lambda is deprecated, "
"please use TARDISSpectrum.luminosity_to_flux() in the "
"future.",
category=DeprecationWarning, stacklevel=2)
warnings.simplefilter('default', DeprecationWarning)
try:
return self.luminosity_to_flux(
self.luminosity_density_lambda,
self.distance
)
except AttributeError:
raise AttributeError(
'distance is required as attribute of'
'{} to calculate "{}"'.format(
self.__class__.__name__,
'flux_lambda'
)
)

@staticmethod
def luminosity_to_flux(luminosity, distance):
return luminosity / (4 * np.pi * distance.to('cm')**2)

def f_nu_to_f_lambda(self, f_nu):
return f_nu * self.frequency.value**2 / constants.c.cgs.value / 1e8

return f_nu * self.frequency / self.wavelength

def plot(self, ax, mode='wavelength'):
if mode == 'wavelength':
ax.plot(self.wavelength.value, self.flux_lambda.value)
ax.set_xlabel('Wavelength [%s]' % self.wavelength.unit._repr_latex_())
ax.set_ylabel('Flux [%s]' % self.flux_lambda.unit._repr_latex_())
ax.plot(
self.wavelength.value,
self.luminosity_density_lambda.value)
ax.set_xlabel('Wavelength [{}]'.format(
self.wavelength.unit._repr_latex_())
)
ax.set_ylabel(
'Flux [{:s}]'.format(
self.luminosity_density_lambda.unit._repr_latex_())
)

def to_ascii(self, fname, mode='luminosity_density'):
if mode == 'luminosity_density':
np.savetxt(fname, zip(self.wavelength.value, self.luminosity_density_lambda.value))
np.savetxt(
fname, zip(
self.wavelength.value,
self.luminosity_density_lambda.value))
elif mode == 'flux':
np.savetxt(fname, zip(self.wavelength.value, self.flux_lambda.value))
np.savetxt(
fname,
zip(self.wavelength.value, self.flux_lambda.value))
else:
raise NotImplementedError('only mode "luminosity_density" and "flux" are implemented')

def to_hdf(self, path_or_buf, path='', name=''):
"""
Store the spectrum to an HDF structure.

Parameters
----------
path_or_buf
Path or buffer to the HDF store
path : str
Path inside the HDF store to store the spectrum
name : str, optional
A different name than 'spectrum', if needed.

Returns
-------
None

"""
if not name:
name = 'spectrum'
spectrum_path = os.path.join(path, name)
properties = ['luminosity_density_nu', 'delta_frequency', 'wavelength',
'luminosity_density_lambda']
to_hdf(path_or_buf, spectrum_path, {name: getattr(self, name) for name
in properties})
raise NotImplementedError(
'only mode "luminosity_density"'
'and "flux" are implemented')

def to_hdf(self, path_or_buf, path):
pass

@classmethod
def from_hdf(cls, path_or_buf, path):
pass
Loading