Skip to content

Commit

Permalink
Numerical He NLTE.
Browse files Browse the repository at this point in the history
  • Loading branch information
Aoife Boyle committed Aug 28, 2015
1 parent cbcb1cf commit 7fb1a67
Show file tree
Hide file tree
Showing 10 changed files with 483 additions and 22 deletions.
318 changes: 318 additions & 0 deletions .idea/workspace.xml

Large diffs are not rendered by default.

14 changes: 10 additions & 4 deletions docs/physics/plasma/helium_nlte.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ The `helium_treatment` setting in the Tardis config. file will accept one of thr
* `recomb-nlte`: Treats helium in NLTE using the analytical approximation outlined in an upcoming paper.
* `numerical-nlte`: To be implemented. Will allow the use of a separate module (not distributed with Tardis) to perform helium NLTE calculations numerically.

`recomb-NLTE` (Paper version)
-----------------------------
Recombination He NLTE
----------------------

Paper version:

This section will summarise the equations used in the calculation of the helium state for the `recomb-NLTE` approximation in Tardis. A full physical justification for these equations will be provided in an upcoming paper. All of the level populations are given as a function of the He II ground state population (:math:`n_{2,1,0}`), and the values are then normalised using the helium number density to give the correct level number densities.

Symbols/Indexing:
Expand Down Expand Up @@ -35,11 +38,14 @@ Symbols/Indexing:
.. math::
n_{2,2,0} = \frac{n_{2,1,0}}{n_{e}}\times[W(\delta_{2,2}\times\zeta_{2,2}+W(1-\zeta_{2,2})]\left(\frac{T_{e}}{T_{r}}\right)^{1/2}\times\frac{2g_{2,2,0}}{g_{2,1,0}}\times\left(\frac{2\pi m_{e}kT_{r}}{h^{2}}\right)^{3/2}\times\exp{\left(-\frac{\chi_{2,1}}{kT_{r}}\right)}
`recomb-NLTE` (Code version)
-----------------------------
Code Version:

In the Tardis plasma, some of these equations are re-written slightly to make use of existing property methods (e.g. `PhiSahaLTE`, `PhiSahaNebular`) often using the relation:

.. math::
\frac{N_{i,j}}{Z_{i,j}} = \frac{n_{i,j,k}}{g_{i,j,k}}
Numerical He NLTE
------------------

Another `helium_treatment` option offered by Tardis is `numerical-nlte`. The use of this plasma property requires an additional code that is the property of Stephan Hachinger (see arXiv:1201.1506) and is not distributed with Tardis. Tardis also requires a specific atomic datafile to use this module. This plasma option is included so that people who have access to and permission to use the necessary module may use it. Otherwise, the `recomb-NLTE` option provides a very accurate alternative approximation.
8 changes: 7 additions & 1 deletion tardis/data/tardis_config_definition.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,15 @@ plasma:
property_type: string
mandatory: False
default: none
allowed_value: none recomb-nlte
allowed_value: none recomb-nlte numerical-nlte
help: none to treat He as the other elements. recomb-nlte to treat with NLTE approximation.

heating_rate_data_file:
property_type: string
mandatory: False
default: none
help: Path to file containing heating rate/light curve data.

model:
structure:
property_type : container-property
Expand Down
8 changes: 7 additions & 1 deletion tardis/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,20 @@ def __init__(self, tardis_config):
self.ws = (0.5 * (1 - np.sqrt(1 -
(tardis_config.structure.r_inner[0] ** 2 / tardis_config.structure.r_middle ** 2).to(1).value)))

heating_rate_data_file = getattr(tardis_config.plasma, 'heating_rate_data_file', None)

self.plasma_array = LegacyPlasmaArray(tardis_config.number_densities, tardis_config.atom_data,
tardis_config.supernova.time_explosion.to('s').value,
nlte_config=tardis_config.plasma.nlte,
delta_treatment=tardis_config.plasma.delta_treatment,
ionization_mode=tardis_config.plasma.ionization,
excitation_mode=tardis_config.plasma.excitation,
line_interaction_type=tardis_config.plasma.line_interaction_type,
link_t_rad_t_electron=0.9, helium_treatment=tardis_config.plasma.helium_treatment)
link_t_rad_t_electron=0.9,
helium_treatment=tardis_config.plasma.helium_treatment,
heating_rate_data_file=heating_rate_data_file,
v_inner=tardis_config.structure.v_inner,
v_outer=tardis_config.structure.v_outer)

self.spectrum = TARDISSpectrum(tardis_config.spectrum.frequency, tardis_config.supernova.distance)
self.spectrum_virtual = TARDISSpectrum(tardis_config.spectrum.frequency, tardis_config.supernova.distance)
Expand Down
2 changes: 1 addition & 1 deletion tardis/plasma/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def __init__(self, plasma_properties, **kwargs):
self.plasma_properties = self._init_properties(plasma_properties,
**kwargs)
self._build_graph()
self.write_to_tex('Plasma_Graph', 'Plasma_Formulae')
# self.write_to_tex('Plasma_Graph', 'Plasma_Formulae')
self.update(**kwargs)

def __getattr__(self, item):
Expand Down
3 changes: 3 additions & 0 deletions tardis/plasma/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,7 @@ class PlasmaIonizationError(PlasmaException):
pass

class PlasmaConfigContradiction(PlasmaException):
pass

class PlasmaConfigError(PlasmaException):
pass
12 changes: 7 additions & 5 deletions tardis/plasma/properties/level_population.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ class LevelNumberDensity(ProcessingPlasmaProperty):
latex_name = ('N_{i,j,k}',)
latex_formula = ('N_{i,j}\\dfrac{bf_{i,j,k}}{Z_{i,j}}',)

def calculate():
def calculate(self):
pass

def __init__(self, plasma_parent, helium_treatment='dilute-lte'):
super(LevelNumberDensity, self).__init__(plasma_parent)
if hasattr(self.plasma_parent, 'plasma_properties_dict'):
if 'HeliumNLTE' in \
self.plasma_parent.plasma_properties_dict.keys():
helium_treatment=='recomb-nlte'
helium_treatment='recomb-nlte'
if helium_treatment=='recomb-nlte':
self.calculate = self._calculate_helium_recomb_nlte
self.calculate = self._calculate_helium_nlte
elif helium_treatment=='dilute-lte':
self.calculate = self._calculate_dilute_lte
self._update_inputs()
Expand All @@ -40,9 +40,11 @@ def _calculate_dilute_lte(self, level_boltzmann_factor, ion_number_density,
level_population_fraction.index.droplevel(2)].values
return level_population_fraction * ion_number_density_broadcast

def _calculate_helium_recomb_nlte(self, level_boltzmann_factor,
def _calculate_helium_nlte(self, level_boltzmann_factor,
ion_number_density, levels, partition_function, helium_population):
level_number_density = self._calculate_dilute_lte(
level_boltzmann_factor, ion_number_density, levels,
partition_function)
level_number_density.ix[2].update(helium_population)
if helium_population is not None:
level_number_density.ix[2].update(helium_population)
return level_number_density
122 changes: 115 additions & 7 deletions tardis/plasma/properties/nlte.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import numpy as np
import pandas as pd
from scipy import interpolate
import logging
import os

from tardis.plasma.properties.base import (BasePlasmaProperty,
ProcessingPlasmaProperty)
from tardis.plasma.properties import PhiSahaNebular, PhiSahaLTE
from tardis.plasma.properties import NumberDensity

__all__ = ['PreviousElectronDensities', 'PreviousBetaSobolevs',
'HeliumNLTE']
'HeliumNLTE', 'HeliumNumericalNLTE']

logger = logging.getLogger(__name__)

class PreviousIterationProperty(BasePlasmaProperty):

Expand Down Expand Up @@ -60,7 +63,7 @@ def calculate(self, level_boltzmann_factor, electron_densities,
helium_population.ix[0].ix[0] = 0.0
#He II excited states
he_two_population = level_boltzmann_factor.ix[2,1].mul(
(g.ix[2,1].ix[0]**(-1)) * (t_electrons / t_rad)**0.5)
(g.ix[2,1].ix[0]**(-1)))
helium_population.ix[1].update(he_two_population)
#He II ground state
helium_population.ix[1].ix[0] = 1.0
Expand All @@ -73,23 +76,25 @@ def calculate(self, level_boltzmann_factor, electron_densities,
helium_population.update(normalised)
return helium_population

def calculate_helium_one(self, g_electron, beta_rad, partition_function,
@staticmethod
def calculate_helium_one(g_electron, beta_rad, partition_function,
ionization_data, level_boltzmann_factor, electron_densities, g,
w, t_rad, t_electron):
(partition_function_index, ionization_data_index, partition_function,
ionization_data) = self.filter_with_helium_index(2, 1,
ionization_data) = HeliumNLTE.filter_with_helium_index(2, 1,
partition_function, ionization_data)
phis = (1 / PhiSahaLTE.calculate(g_electron, beta_rad,
partition_function, ionization_data)) * electron_densities * \
(1.0/g.ix[2,1,0]) * (1/w) * (t_rad/t_electron)**(0.5)
return level_boltzmann_factor.ix[2].ix[0].mul(
pd.DataFrame(phis.ix[2].ix[1].values)[0].transpose())

def calculate_helium_three(self, t_rad, w, zeta_data, t_electrons, delta,
@staticmethod
def calculate_helium_three(t_rad, w, zeta_data, t_electrons, delta,
g_electron, beta_rad, partition_function, ionization_data,
electron_densities):
(partition_function_index, ionization_data_index, partition_function,
ionization_data) = self.filter_with_helium_index(2, 2,
ionization_data) = HeliumNLTE.filter_with_helium_index(2, 2,
partition_function, ionization_data)
zeta_data = pd.DataFrame(zeta_data.ix[2].ix[2].values,
columns=ionization_data_index, index=zeta_data.columns).transpose()
Expand Down Expand Up @@ -121,3 +126,106 @@ def filter_with_helium_index(atomic_number, ion_number, partition_function,
columns=['ionization_energy'])
return partition_function_index, ionization_data_index,\
partition_function, ionization_data

class HeliumNumericalNLTE(ProcessingPlasmaProperty):
outputs = ('helium_population',)
'''
IMPORTANT: This particular property requires a specific numerical NLTE
solver and a specific atomic dataset (neither of which are distributed
with Tardis) to work.
'''
def calculate(self, ion_number_density, electron_densities, t_electrons, w,
lines, j_blues, levels, level_boltzmann_factor, t_rad,
zeta_data, g_electron, delta, partition_function, ionization_data,
beta_rad, g):
logger.info('Performing numerical NLTE He calculations.')
if len(j_blues)==0:
return None
heating_rate_data = np.loadtxt(
self.plasma_parent.heating_rate_data_file, unpack=True)
#Outputting data required by SH module
for zone, _ in enumerate(electron_densities):
with open('He_NLTE_Files/shellconditions_{}.txt'.format(zone),
'w') as output_file:
output_file.write(ion_number_density.ix[2].sum()[zone])
output_file.write(electron_densities[zone])
output_file.write(t_electrons[zone])
output_file.write(heating_rate_data[zone])
output_file.write(w[zone])
output_file.write(self.plasma_parent.time_explosion)
output_file.write(t_rad[zone])
output_file.write(self.plasma_parent.v_inner[zone])
output_file.write(self.plasma_parent.v_outer[zone])

for zone, _ in enumerate(electron_densities):
with open('He_NLTE_Files/abundances_{}.txt'.format(zone), 'w') as \
output_file:
for element in range(1,31):
try:
number_density = ion_number_density[zone].ix[
element].sum()
except:
number_density = 0.0
output_file.write(number_density)

helium_lines = lines[lines['atomic_number']==2]
helium_lines = helium_lines[helium_lines['ion_number']==0]
for zone, _ in enumerate(electron_densities):
with open('He_NLTE_Files/discradfield_{}.txt'.format(zone), 'w') \
as output_file:
j_blues = pd.DataFrame(j_blues, index=lines.index)
helium_j_blues = j_blues[zone].ix[helium_lines.index]
for value in helium_lines.index:
if (helium_lines.level_number_lower.ix[value]<35):
output_file.write(
int(helium_lines.level_number_lower.ix[value]+1),
int(helium_lines.level_number_upper.ix[value]+1),
j_blues[zone].ix[value])
#Running numerical simulations
for zone, _ in enumerate(electron_densities):
os.rename('He_NLTE_Files/abundances{}.txt'.format(zone),
'He_NLTE_Files/abundances_current.txt')
os.rename('He_NLTE_Files/shellconditions{}.txt'.format(zone),
'He_NLTE_Files/shellconditions_current.txt')
os.rename('He_NLTE_Files/discradfield{}.txt'.format(zone),
'He_NLTE_Files/discradfield_current.txt')
os.system("nlte-solver-module/bin/nlte_solvertest >/dev/null")
os.rename('He_NLTE_Files/abundances_current.txt',
'He_NLTE_Files/abundances{}.txt'.format(zone))
os.rename('He_NLTE_Files/shellconditions_current.txt',
'He_NLTE_Files/shellconditions{}.txt'.format(zone))
os.rename('He_NLTE_Files/discradfield_current.txt',
'He_NLTE_Files/discradfield{}.txt'.format(zone))
os.rename('debug_occs.dat', 'He_NLTE_Files/occs{}.txt'.format(zone))
#Reading in populations from files
helium_population = level_boltzmann_factor.ix[2].copy()
for zone, _ in enumerate(electron_densities):
with open('He_NLTE_Files/discradfield{}.txt'.format(zone), 'r') as \
read_file:
for level in range(0, 35):
level_population = read_file.readline()
level_population = float(level_population)
helium_population[zone].ix[0][level] = level_population
helium_population[zone].ix[1].ix[0] = float(
read_file.readline())
#Performing He LTE level populations (upper two energy levels,
#He II excited states, He III)
he_one_population = HeliumNLTE.calculate_helium_one(g_electron,
beta_rad, partition_function, ionization_data,
level_boltzmann_factor, electron_densities, g, w, t_rad,
t_electrons)
helium_population.ix[0].ix[35].update(he_one_population.ix[35])
helium_population.ix[0].ix[36].update(he_one_population.ix[36])

he_two_population = level_boltzmann_factor.ix[2].ix[1].ix[1:].mul(
(g.ix[2,1,0]**(-1)) * helium_population.ix[s1,0])
helium_population.ix[1].ix[1:].update(he_two_population)

helium_population.ix[2].ix[0] = HeliumNLTE.calculate_helium_three(
t_rad, w, zeta_data, t_electrons, delta, g_electron, beta_rad,
partition_function, ionization_data, electron_densities)
unnormalised = helium_population.sum()
normalised = helium_population.mul(ion_number_density.ix[2].sum()
/ unnormalised)
helium_population.update(normalised)
return helium_population
2 changes: 2 additions & 0 deletions tardis/plasma/properties/property_collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,5 @@ class PlasmaPropertyCollection(list):
helium_nlte_properties = PlasmaPropertyCollection([HeliumNLTE,
RadiationFieldCorrection, ZetaData,
BetaElectron, Chi0])
helium_numerical_nlte_properties = PlasmaPropertyCollection([
HeliumNumericalNLTE])
16 changes: 13 additions & 3 deletions tardis/plasma/standard_plasmas.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
basic_properties, lte_excitation_properties, lte_ionization_properties,
macro_atom_properties, dilute_lte_excitation_properties,
nebular_ionization_properties, non_nlte_properties,
nlte_properties, helium_nlte_properties)
from tardis.io.util import parse_abundance_dict_to_dataframe
nlte_properties, helium_nlte_properties, helium_numerical_nlte_properties)
from tardis.plasma.exceptions import PlasmaConfigError

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -55,7 +55,8 @@ def __init__(self, number_densities, atomic_data, time_explosion,
t_rad=None, delta_treatment=None, nlte_config=None,
ionization_mode='lte', excitation_mode='lte',
line_interaction_type='scatter', link_t_rad_t_electron=0.9,
helium_treatment='lte'):
helium_treatment='none', heating_rate_data_file=None,
v_inner=None, v_outer=None):

plasma_modules = basic_inputs + basic_properties

Expand Down Expand Up @@ -107,6 +108,15 @@ def __init__(self, number_densities, atomic_data, time_explosion,
if helium_treatment=='recomb-nlte':
plasma_modules += helium_nlte_properties

if helium_treatment=='numerical-nlte':
plasma_modules += helium_numerical_nlte_properties
if heating_rate_data_file is None:
raise PlasmaConfigError('Heating rate data file not specified')
else:
self.heating_rate_data_file = heating_rate_data_file
self.v_inner = v_inner
self.v_outer = v_outer

super(LegacyPlasmaArray, self).__init__(plasma_properties=plasma_modules,
t_rad=t_rad, abundance=abundance, density=density,
atomic_data=atomic_data, time_explosion=time_explosion,
Expand Down

0 comments on commit 7fb1a67

Please sign in to comment.