""":module PlasmaXRTSCalculatorParameters: Module that holds the PlasmaXRTSCalculatorParameters class. """
##########################################################################
# #
# Copyright (C) 2016-2017 Carsten Fortmann-Grote #
# Contact: Carsten Fortmann-Grote <carsten.grote@xfel.eu> #
# #
# This file is part of simex_platform. #
# simex_platform is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# simex_platform is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
##########################################################################
from scipy.constants import Avogadro
from scipy.constants import physical_constants
import periodictable
import copy
import math
import numpy
import os
import tempfile
from SimEx.Parameters.AbstractCalculatorParameters import AbstractCalculatorParameters
from SimEx.Utilities.Utilities import ALL_ELEMENTS
from SimEx.Utilities.EntityChecks import checkAndSetInstance
from SimEx.Utilities.EntityChecks import checkAndSetInteger
from SimEx.Utilities.EntityChecks import checkAndSetPositiveInteger
[docs]class PlasmaXRTSCalculatorParameters(AbstractCalculatorParameters):
"""
:class PlasmaXRTSCalculatorParameters: Encapsulates parameters for the plasma x-ray Thomson scattering calculator.
"""
def __init__(self,
elements=None,
photon_energy=None,
scattering_angle=None,
electron_temperature=None,
electron_density=None,
ion_temperature=None,
ion_charge=None,
mass_density=None,
debye_temperature=None,
band_gap=None,
energy_range=None,
model_Sii=None,
model_See=None,
model_Sbf=None,
model_IPL=None,
model_Mix=None,
lfc=None,
Sbf_norm=None,
source_spectrum=None,
source_spectrum_fwhm=None,
**kwargs
):
"""
:param elements: The chemical elements in the scattering target.
:type elements: list [[element symbol, stochiometric number, charge], ...], default None
:example elements: [['B', 1, 2], ['N', 1, 2]] for Boron-Nitride with both B and N two fold ionized (ion average).
:example elements: [['C', 1, 4], ['H', 1, -1]] for Plastic with both four-fold ionized C and ionization of H calculated so that the given average ion charge comes out correct.
:param photon_energy: The central energy of incoming x-ray photons.
:type photon_energy: float
:param scattering_angle: The scattering angle.
:type scattering_angle: float
:param electron_temperature: The temperature of the electron subsystems (units of eV).
:type electron_temperature: float
:param electron_density: The electron number density (units of 1/cm^3)
:type electron_density: float
:param ion_temperature: The temperature of the ion subsystem (units of eV).
:type ion_temperature: float
:param ion_charge: The average ion charge (units of elementary charge e).
:type ion_charge: float
:param mass_density: The mass density of the target (units of g/cm^3).
:type mass_density: float
:param debye_temperature: The Debye temperature (units of eV).
:type debye_temperature: float
:param band_gap: The band gap of the target (units of eV).
:type band_gap: float, default 0
:param energy_range: The energy range over which to calculate the scattering spectrum.
:type energy_range: dict, default 0
:example energy_range: energy_range={'min' -100.0, 'max' 100, 'step' 0.5} to go from -100 eV to 100 eV in steps of 0.5 eV.
:param model_Sii: The model to use for the ion-ion structure factor.
:type model_Sii: str ('DH' || 'OCP' || 'SOCP' || 'SOCPN') || float, default 'DH'
:example model_Sii: Sii=1.5 to use a fixed value of Sii=1.5
:param model_See: The model of the dynamic (high frequency) part of the electron-electron structure factor.
:type model_See: str ('RPA' || 'BMA' || 'BMA+sLFC'), default 'RPA'
:param model_Sbf: The model for the bound-free structure factor.
:type model_Sbf: str ('IA' || 'FA'), default 'IA'
:param model_IPL: Model for ionization potential lowering.
:type model_IPL: str ('SP' || 'EK') || float, default 'SP'
:example model_IPL: model_IPL=100.0 # Set the ionization potential difference (lowering) to 100 eV.
:param model_Mix: The model to use for mixing (of species).
:type model_Mix: str, default None
:param lfc: The local field correction to use.
:type lfc: float, default 0.0
:param Sbf_norm: How to normalize the bound-free structure factor.
:type Sbf_norm: str || float, default None
:param source_spectrum: Path to a file holding the x-ray probe energy spectrum.
:type source_spectrum: str, default None
:param source_spectrum_fwhm: The x-ray probe energy spectrum fwhm.
:type source_spectrum_fwhm: float
"""
# Check and set all parameters.
self.__elements = checkAndSetElements(elements)
self.__photon_energy = checkAndSetPhotonEnergy(photon_energy)
self.__scattering_angle = checkAndSetScatteringAngle(scattering_angle)
self.__electron_temperature = checkAndSetElectronTemperature(electron_temperature)
# Set electron density, charge, and mass density depending on which input was given.
self.__electron_density, self.__ion_charge, self.__mass_density = checkAndSetDensitiesAndCharge(electron_density, ion_charge, mass_density, elements)
self.__ion_temperature = checkAndSetIonTemperature(ion_temperature, self.electron_temperature)
self.__debye_temperature = checkAndSetDebyeTemperature(debye_temperature)
self.__band_gap = checkAndSetBandGap(band_gap)
self.__energy_range = checkAndSetEnergyRange(energy_range, self.electron_density)
self.__model_Sii = checkAndSetModelSii(model_Sii)
self.__model_See = checkAndSetModelSee(model_See)
self.__model_Sbf = checkAndSetModelSbf(model_Sbf)
self.__model_IPL = checkAndSetModelIPL(model_IPL)
self.__model_Mix = checkAndSetModelMix(model_Mix)
self.__lfc = checkAndSetLFC(lfc)
self.__Sbf_norm = checkAndSetSbfNorm(Sbf_norm)
self.__source_spectrum = checkAndSetSourceSpectrum(source_spectrum)
self.__source_spectrum_fwhm=checkAndSetSourceSpectrumFWHM(source_spectrum_fwhm)
# HACK
self._setSeeFlags()
self._setSiiFlags()
self._setSbfNormFlags()
self._setDebyeTemperatureFlags()
self._setBandGapFlags()
self._setIPLFlags()
self._setSourceSpectrumFlags()
# Set state to not-initialized (e.g. input deck is not written).
self.__is_initialized = False
def _setDefaults(self):
""" """
""" Set the inherited parameters defaults that depend on the special calculator. """
self._AbstractCalculatorParameters__cpus_per_task_default = 1
def _setSeeFlags(self):
""" Set the See parameters as used in the input deck generator. """
self.__use_rpa = int(self.model_See == "RPA")
self.__use_bma = int(self.model_See == "BMA")
self.__use_bma_slfc = int(self.model_See == 'BMA+sLFC')
self.__write_bma = int(self.model_See == 'BMA+sLFC' or self.model_See == 'BMA')
self.__use_lindhard = int(self.model_See == 'Lindhard')
self.__use_landen = int(self.model_See == 'Landen')
self.__use_static_lfc = int(self.model_See == 'sLFC')
self.__use_dynamic_lfc = int(self.model_See == 'dLFC')
self.__use_mff = int(self.model_See == 'MFF')
self.__write_core = 1
self.__write_total = 1
def _setSiiFlags(self):
""" Set the internal Sii parameters as used in the input deck generator."""
# By default, switch off usage of user given Sii value.
self.__Sii_value = 0.0
self.__use_Sii_value = 0
# Only if Sii model input parameter is float, use it as Sii(k).
if isinstance( self.__model_Sii, float):
self.__use_Sii_value = 1
# Copy value.
self.__Sii_value = copy.deepcopy(self.__model_Sii)
# Reset model parameter but short-cutting the setter.
self.__model_Sii = 'DH'
def _setSbfNormFlags(self):
""" Set the internal Sbf norm flags used in the input deck generator. """
# By default, switch off usage of user given SbfNorm value.
self.__Sbf_norm_value = 0.0
# Only if SbfNorm model input parameter is float, use it as SbfNorm(k).
if isinstance( self.Sbf_norm, float):
# Copy value.
self.__Sbf_norm_value = copy.deepcopy(self.Sbf_norm)
# Reset model parameter.
self.__Sbf_norm = 'USR'
def _setDebyeTemperatureFlags(self):
""" Set the internal Debye temperature flags used in the input deck generator. """
# By default, switch off usage of user given Debye temperature.
self.__use_debye_temperature = 0
self.__debye_temperature_value = 0.0
# Only if Debye Temperature is non-zero use it.
if self.debye_temperature is not None:
self.__debye_temperature_value = self.__debye_temperature
self.__use_debye_temperature = 1
def _setBandGapFlags(self):
""" Set the internal bandgap flags used in the input deck generator. """
# By default, switch off usage of band gap.
self.__use_band_gap = 0
self.__band_gap_value = 0.0
# Only if bandgap is non-zero use it.
if self.band_gap is not None:
self.__use_band_gap = 1
self.__band_gap_value = self.__band_gap
def _setIPLFlags(self):
""" Set the internal ionization potential lowering flags used in the input deck generator. """
# By default, switch off usage of band gap.
self.__ipl_value = 0.0
# Only if ipl is non-zero use it.
if isinstance( self.__model_IPL, float ):
self.__ipl_value = copy.deepcopy(self.__model_IPL)
self.__model_IPL = 'USR'
def _setSourceSpectrumFlags(self):
""" Set the internal source spectrum flags used in the input deck generation."""
# Default.
self.__use_source_spectrum_file = 0
self.__source_spectrum_identifier = "GAUSSIAN"
if self.__source_spectrum == "LORENTZ":
self.__source_spectrum_identifier = "LORENTZIAN"
elif self.__source_spectrum == "PROP":
self.__use_source_spectrum_file = 1
def _serialize(self):
""" Write the input deck for the xrts backengine. """
# Make a temporary directory.
self._tmp_dir = tempfile.mkdtemp(prefix='xrs_')
# Write the input file.
input_deck_path = os.path.join( self._tmp_dir, 'input.dat' )
with open(input_deck_path, 'w') as input_deck:
input_deck.write('--XRTS---input_file-----------------------------------\n')
input_deck.write('--\n')
input_deck.write('--fit_parameters------------------------------flag----\n')
input_deck.write('DO_FIT 0\n')
input_deck.write('PHOTON_ENERGY %4.3f\n' % (self.photon_energy))
input_deck.write('SCATTERING_ANGLE %4.3f\n' % (self.scattering_angle) )
input_deck.write('ELECTRON_TEMP %4.3f 0\n' % (self.electron_temperature) )
input_deck.write('ELECTRON_DENSITY %4.3e 0\n' % (self.electron_density*1e6) )
input_deck.write('AMPLITUDE 1.0 0\n')
input_deck.write('BASELINE 0.0 0\n')
input_deck.write('Z_FREE %4.3f 0\n' % (self.ion_charge) )
input_deck.write('OUT(1=XSEC,2=PWR) 1\n')
input_deck.write('--model_for_total_spec---------use-flag--------------\n')
input_deck.write('USE_RPA %d\n' % (self.__use_rpa) )
input_deck.write('USE_LINDHARD %d\n' % (self.__use_lindhard) )
input_deck.write('USE_TSYTOVICH 0\n' )
input_deck.write('USE_STATIC_LFC %d\n' % (self.__use_static_lfc) )
input_deck.write('USE_DYNAMIC_LFC %d\n' % (self.__use_dynamic_lfc) )
input_deck.write('USE_MFF %d\n' % (self.__use_mff) )
input_deck.write('USE_BMA %d\n' % (self.__use_bma) )
input_deck.write('USE_BMA+sLFC %d\n' % (self.__use_bma_slfc) )
input_deck.write('USE_CORE 1\n')
input_deck.write('--gradients------------------------------------------\n')
input_deck.write('GRAD 0\n')
input_deck.write('L_GRADIENT 0.0e-0 \n')
input_deck.write('T_GRADIENT 0.0 \n')
input_deck.write('DSTEP 0.0 \n')
input_deck.write('--ion_parameters----------------------------use_flag-\n')
input_deck.write('ION_TEMP %4.3f 1\n' % (self.ion_temperature) )
input_deck.write('S_ION_FEATURE %4.3f %d\n' % (self.__Sii_value, self.__use_Sii_value) )
input_deck.write('DEBYE_TEMP %4.3f %d\n' % (self.__debye_temperature_value, self.__use_debye_temperature) )
input_deck.write('BAND_GAP %4.3f %d\n' % (self.__band_gap_value, self.__use_band_gap) )
input_deck.write('--integration----------------------------------------\n')
input_deck.write('N_DAWSON 32\n')
input_deck.write('N_DISTRIBUTION 32\n')
input_deck.write('N_PVI 32\n')
input_deck.write('N_LANDEN 512\n')
input_deck.write('N_RELAXATION 1024\n')
input_deck.write('N_FFT 1024\n')
input_deck.write('EPS 1.0E-4\n')
input_deck.write('--See(k,w)------------------------------use/norm-----\n')
input_deck.write('STATIC_MODEL(DH,OCP,SOCP,SOCPN) %s\n' % (self.model_Sii) )
input_deck.write('USE_ADV_Mix %d\n' % (self.model_Mix) )
input_deck.write('USE_IRS_MODEL 0\n')
input_deck.write('HARD_SPHERE_DIAM 1E-10 0\n')
input_deck.write('POLARIZABILITY 0.0 0.0\n')
input_deck.write('BOUND-FREE_MODEL(IA,IBA,FFA) %s\n' % (self.model_Sbf) )
input_deck.write('BOUND-FREE_NORM(FK,NO,USR) %s %4.3f\n' % (self.Sbf_norm, self.__Sbf_norm_value) )
input_deck.write('BOUND-FREE_MEFF 1.0\n')
input_deck.write('USE_BOUND-FREE_DOPPLER 0\n')
input_deck.write('CONT-LOWR_MODEL(SP,EK,USR) %s %4.3f\n' % (self.model_IPL, self.__ipl_value) )
input_deck.write('GK %4.3f 0\n' % self.__lfc)
input_deck.write('RPA %d 0\n' % (self.__use_rpa) )
input_deck.write('LINDHARD %d 0\n' % (self.__use_lindhard) )
input_deck.write('SALPETER 0 0\n')
input_deck.write('LANDEN %d 0\n' % (self.__use_landen) )
input_deck.write('RPA_TSYTOVICH 0 0\n')
input_deck.write('STATIC_LFC %d 0\n' % (self.__use_static_lfc) )
input_deck.write('DYNAMIC_LFC %d 0\n' % (self.__use_dynamic_lfc) )
input_deck.write('MFF %d 0\n' % (self.__use_mff) )
input_deck.write('BMA(+sLFC) %d 0\n' % (self.__write_bma))
input_deck.write('CORE %d 0\n' % (self.__write_core))
input_deck.write('TOTAL %d 0\n' % (self.__write_total))
input_deck.write('E_MIN %8.7f \n' % (self.energy_range['min']))
input_deck.write('E_MAX %8.7f \n' % (self.energy_range['max']))
input_deck.write('E_STEP %8.7f \n' % (self.energy_range['step']))
input_deck.write('--target_spec--------------------------chem----Zfree--\n')
input_deck.write('NUMBER_OF_SPECIES %d\n' % (len(self.elements)) )
for i,element in enumerate(self.elements):
input_deck.write('TARGET_%d %s %d %d\n' % (i+1, element[0], element[1], element[2] ) )
input_deck.write('MASS_DENSITY %4.3f\n' % (self.mass_density))
input_deck.write('NE_ZF_LOCK 1\n')
input_deck.write('DATA_FILE data.txt\n')
input_deck.write('NUMBER_POINTS 1024\n')
input_deck.write('OPACITY_FILE nofile 0\n')
input_deck.write('--instrument_function---------------------------------\n')
input_deck.write('USE_FILE %d\n' % (self.__use_source_spectrum_file) )
input_deck.write('FILE_NAME source_spectrum.txt\n')
input_deck.write('INST_MODEL %s\n' % (self.__source_spectrum_identifier) )
input_deck.write('INST_FWHM %4.3f\n' % (self.__source_spectrum_fwhm) )
input_deck.write('BIN_PER_PIXEL 1.0\n')
input_deck.write('INST_INDEX 2.0\n')
input_deck.write('--additional_parameters-------------------------------\n')
input_deck.write('MAX_ITERATIONS 0\n')
input_deck.write('LEVENBERG_MARQUARDT 0\n')
input_deck.write('SIGMA_LM 1.0\n')
input_deck.write('SAVE_FILE xrts_out.txt\n')
@property
def elements(self):
""" Query for the field data. """
return self.__elements
@elements.setter
def elements(self, value):
""" Set the elements to value. """
self.__elements = checkAndSetElements(value)
@property
def photon_energy(self):
""" Query for the photon energy. """
return self.__photon_energy
@photon_energy.setter
def photon_energy(self, value):
""" Set the photon energy to value. """
self.__photon_energy = checkAndSetPhotonEnergy(value)
@property
def scattering_angle(self):
""" Query for the scattering angle. """
return self.__scattering_angle
@scattering_angle.setter
def scattering_angle(self, value):
""" Set the scattering angle to value. """
self.__scattering_angle = checkAndSetScatteringAngle(value)
@property
def electron_temperature(self):
""" Query for the electron temperature. """
return self.__electron_temperature
@electron_temperature.setter
def electron_temperature(self, value):
""" Set the electron temperature to value. """
self.__electron_temperature = checkAndSetElectronTemperature(value)
@property
def electron_density(self):
""" Query for the electron density. """
return self.__electron_density
@electron_density.setter
def electron_density(self, value):
""" Set the electron density to value. """
self.__electron_density = value
print("WARNING: Electron density might be inconsistent with mass density and charge.")
@property
def ion_temperature(self):
""" Query for the ion temperature. """
return self.__ion_temperature
@ion_temperature.setter
def ion_temperature(self, value):
""" Set the ion temperature to value. """
self.__ion_temperature = checkAndSetIonTemperature(value)
@property
def ion_charge(self):
""" Query for the ion charge. """
return self.__ion_charge
@ion_charge.setter
def ion_charge(self, value):
""" Set the ion charge to value. """
self.__ion_charge = value
print("WARNING: Ion charge might be inconsistent with electron density and mass density.")
@property
def mass_density(self):
""" Query for the mass density. """
return self.__mass_density
@mass_density.setter
def mass_density(self, value):
""" Set the mass density to value. """
self.__mass_density = value
print("WARNING: Mass density might be inconsistent with electron density and charge.")
@property
def debye_temperature(self):
""" Query for the Debye temperature. """
return self.__debye_temperature
@debye_temperature.setter
def debye_temperature(self, value):
"""
@brief Set the Debye temperature to value.
@param value1 The value to set the Debye temperature to.
<br/><b>type</b> float
"""
self.__debye_temperature = checkAndSetDebyeTemperature(value)
self._setDebyeTemperatureFlags()
@property
def band_gap(self):
""" Query for the band gap. """
return self.__band_gap
@band_gap.setter
def band_gap(self, value):
""" Set the band gap to value. """
self.__band_gap = checkAndSetBandGap(value)
self._setBandGapFlags()
@property
def energy_range(self):
""" Query for the energy range. """
return self.__energy_range
@energy_range.setter
def energy_range(self, value):
""" Set the energy range to value. """
self.__energy_range = checkAndSetEnergyRange(value)
@property
def model_Sii(self):
""" Query for the ion-ion structure factor model. """
return self.__model_Sii
@model_Sii.setter
def model_Sii(self, value):
""" Set the ion-ion structure factor model to value. """
self.__model_Sii = checkAndSetModelSii(value)
self._setSiiFlags()
@property
def model_See(self):
""" Query for the electron-electron (high-frequency) structure factor model. """
return self.__model_See
@model_See.setter
def model_See(self, value):
""" Set the electron-electron (high-frequency) structure factor model to value. """
self.__model_See = checkAndSetModelSee(value)
self._setSeeFlags()
@property
def model_Sbf(self):
""" Query for the bound-free structure factor model. """
return self.__model_Sbf
@model_Sbf.setter
def model_Sbf(self, value):
""" Set the bound-free structure factor model to value. """
self.__model_Sbf = checkAndSetModelSbf(value)
@property
def model_IPL(self):
""" Query for the ionization potential lowering model. """
return self.__model_IPL
@model_IPL.setter
def model_IPL(self, value):
""" Set the ionization potential lowering model to value. """
self.__model_IPL = checkAndSetModelIPL(value)
self._setIPLFlags()
@property
def model_Mix(self):
""" Query for the mixing model. """
return self.__model_Mix
@model_Mix.setter
def model_Mix(self, value):
""" Set the mixing model to value. """
self.__model_Mix = checkAndSetModelMix(value)
@property
def lfc(self):
""" Query for the local field factor. """
return self.__lfc
@lfc.setter
def lfc(self, value):
""" Set the local field factor to value. """
self.__lfc = checkAndSetLFC(value)
@property
def Sbf_norm(self):
""" Query for the norm of the bound-free structure factor. """
return self.__Sbf_norm
@Sbf_norm.setter
def Sbf_norm(self, value):
""" Set the norm of the bound-free structure factor to value. """
self.__Sbf_norm = checkAndSetSbfNorm(value)
self._setSbfNormFlags()
@property
def source_spectrum(self):
""" Query for the source spectrum identifier. """
return self.__source_spectrum
@source_spectrum.setter
def source_spectrum(self, value):
""" Set the source_spectrum to value."""
self.__source_spectrum = checkAndSetSourceSpectrum(value)
self._setSourceSpectrumFlags()
@property
def source_spectrum_fwhm(self):
""" Query for the source spectrum fwhm identifier. """
return self.__source_spectrum
@source_spectrum_fwhm.setter
def source_spectrum_fwhm(self, value):
""" Set the source_spectrum fwhm to value."""
self.__source_spectrum_fwhm = checkAndSetSourceSpectrumFWHM(value)
#@property
#def (self):
#""" Query for the <++>. """
#return self.__<++>
#@<++>.setter
#def <++>(self, value):
#""" Set the <++> to value."""
#self.__<++> = checkAndSet<++>(value)
#<++>
#<++>
###########################
# Check and set functions #
###########################
[docs]def checkAndSetScatteringAngle(angle):
"""
Utility to check if the scattering angle is in the correct range.
@param angle : The angle to check.
@return The checked angle.
@raise ValueError if not 0 <= angle <= 180
"""
# Set default.
if angle is None:
raise RuntimeError( "Scattering angle not specified.")
angle = checkAndSetInstance( float, angle, None)
# Check if in range.
if angle <= 0.0 or angle > 180.0:
raise ValueError
# Return.
return angle
[docs]def checkAndSetPhotonEnergy(energy):
"""
Utility to check if the photon energy is correct.
@param energy : The energy to check.
@return The checked energy.
"""
# Set default.
if energy is None:
raise RuntimeError( "Photon energy not specified.")
energy = checkAndSetInstance( float, energy, None)
# Check if in range.
if energy <= 0.0:
raise ValueError
# Return.
return energy
[docs]def checkAndSetElements(elements):
""" Utility to check if input is a valid list of elements.
@param elements: The elements to check.
<br/><b>type</b> elements: list
@return: The checked list of elements.
"""
if elements is None:
raise RuntimeError( "No element(s) specified. Give at least one chemical element.")
elements = checkAndSetInstance( list, elements, None )
# Check each element.
for element in elements:
symbol, stoch, chrg = checkAndSetInstance( list, element, None )
if symbol not in ALL_ELEMENTS:
raise ValueError( '%s is not a valid chemical element symbol.' % (symbol) )
stoch = checkAndSetPositiveInteger(stoch)
chrg = checkAndSetInteger(chrg)
if chrg < -1:
raise ValueError( "Charge must be >= -1.")
element = [symbol, stoch, chrg]
return elements
[docs]def checkAndSetElectronTemperature(electron_temperature):
""" Utility to check if input is a valid electron temperature.
@param electron_temperature : The electron temperature to check.
<br/><b>type</b> : double
@return : The checked electron temperature.
"""
if electron_temperature is None:
raise RuntimeError( "Electron temperature not specified.")
electron_temperature = checkAndSetInstance( float, electron_temperature, None)
if electron_temperature <= 0.0:
raise ValueError( "Electron temperature must be positive.")
return electron_temperature
[docs]def checkAndSetDensitiesAndCharge(electron_density, ion_charge, mass_density, elements):
""" Utility to check input and return a set of consistent electron density, average ion charge, and mass density, if two are given as input.
"""
# Find number of Nones in input.
number_of_nones = (sum(x is None for x in [electron_density, ion_charge, mass_density]))
# raise if not enough input.
if number_of_nones > 1:
raise RuntimeError( "At least two of Electron_density, ion_charge, and mass_density must be given.")
# Get molar weight needed to convert electron density to mass density.
element_symbols=[e[0] for e in elements]
element_abundances = numpy.array([e[1] for e in elements])
element_charges = numpy.array([e[2] for e in elements])
element_instances = [getattr(periodictable, es) for es in element_symbols]
molar_weights = [ei.mass for ei in element_instances]
molar_weight = sum(element_abundances * molar_weights) / sum( element_abundances )
if electron_density is None:
electron_density = mass_density * ion_charge * Avogadro / molar_weight
print("Setting electron density to %5.4e/cm**3." % (electron_density))
if ion_charge is None:
ion_charge = electron_density / (mass_density * Avogadro / molar_weight)
print("Setting average ion charge to %5.4f." % (ion_charge))
if mass_density is None:
mass_density = electron_density / (ion_charge * Avogadro / molar_weight)
print("Setting mass density to %5.4f g/cm**3." % (mass_density))
# Adjust
#negative_charge_element_index = numpy.where(element_charges == -1)
#positive_charges = element_charges[numpy.where(element_charges >= 0.0)]
#sum_s_Zini= sum(element_abundances[numpy.where(element_charges >= 0.0)] * positive_charges)
#sum_s_ni = sum(element_abundances[numpy.where(element_charges >= 0.0)])
#element_charges[negative_charge_element_index] = sum_s_Zini / ion_charge - sum_s_ni
if abs( electron_density / (mass_density * ion_charge * Avogadro / molar_weight) - 1. ) > 1e-4:
raise ValueError( "Electron density, mass_density, and ion charge are not internally consistent: ne = %5.4e/cm**3, rho*Zf*NA/u= %5.4e/cm**3." % (electron_density, mass_density * ion_charge * Avogadro/molar_weight) )
return electron_density, ion_charge, mass_density
[docs]def checkAndSetIonTemperature(ion_temperature, electron_temperature=None):
""" Utility to check if input is a valid ion temperature.
@param ion_temperature : The ion temperature to check.
<br/><b>type</b> : double
<br/><b>default</b> : Electron temperature.
@return : The checked ion temperature.
"""
if electron_temperature is None:
if ion_temperature is None:
raise RuntimeError(" Could not fix ion temperature because electron temperature not given.")
ion_temperature = checkAndSetInstance( float, ion_temperature, electron_temperature)
if ion_temperature <= 0.0:
raise ValueError( "Ion temperature must be positive.")
return ion_temperature
[docs]def checkAndSetDebyeTemperature(debye_temperature):
""" Utility to check if input is a valid Debye temperature.
@param debye_temperature : The Debye temperature to check.
<br/><b>type</b> : double
<br/><b>default</b> : 0.0
@return : The checked Debye temperature.
"""
if debye_temperature is None:
return None
debye_temperature = checkAndSetInstance( float, debye_temperature, 0.0)
if debye_temperature < 0.0:
raise ValueError( "Debye temperature must be non-negative.")
return debye_temperature
[docs]def checkAndSetBandGap(band_gap):
""" Utility to check if input is a valid bandgap.
@param band_gap: The bandgap to check.
<br/><b>type</b> : double
<br/><b>default</b> 0.0.
@return : The checked bandgap.
"""
if band_gap is None:
return None
band_gap = checkAndSetInstance( float, band_gap, 0.0)
if band_gap < 0.0:
raise ValueError( "Debye temperature must be positive.")
return band_gap
[docs]def checkAndSetModelMix(model_Mix):
""" Utility to check if input is a valid mixing model.
@param model_Mix : The mixing model to check.
<br/><b>type</b> : string
@return : The checked mixing model.
"""
if model_Mix is None:
return 0
if not isinstance( model_Mix, str):
raise TypeError('The mixing model must be a string or None.')
model_Mix = model_Mix.lower()
if not model_Mix == 'adv':
raise ValueError('The mixing model has to be "ADV" (advanced) or None.')
return {'adv' : 1, None: 0}[model_Mix]
[docs]def checkAndSetLFC(lfc):
""" Utility to check if input is a valid local field correction factor.
@param lfc : The lfc to check.
<br/><b>type</b> : double
@return : The checked lfc.
"""
lfc = checkAndSetInstance(float, lfc, 0.0)
return lfc
[docs]def checkAndSetSbfNorm(Sbf_norm):
""" Utility to check if input is a valid norm of the bound-free structure factor.
@param Sbf_norm : The norm to check.
<br/><b>type</b> : string or double.
@return : The checked norm.
"""
if Sbf_norm not in ['FK', 'NO', None] and not isinstance( Sbf_norm, float ):
raise ValueError('The bound-free norm parameter has to be "FK", "NO", None, or a numerical value.')
if Sbf_norm is None:
Sbf_norm = 'FK'
return Sbf_norm
[docs]def checkAndSetEnergyRange(energy_range, electron_density=None):
"""
Utility to check if the photon energy range is ok.
@param energy_range : The range to check.
<br/><b>type</b> dict
@return The checked photon energy range.
@raise ValueError if not of correct shape.
"""
# Raise if both arguments None.
if energy_range is None and electron_density is None:
raise RuntimeError( "At least one argument (electron_density or energy_range) must be given.")
# Some constants.
bohr_radius_m = physical_constants['Bohr radius'][0]
rydberg_energy_eV = physical_constants['Rydberg constant times hc in eV'][0]
energy_range_default = None
if electron_density is not None:
# Get plasma frequency.
plasma_frequency_eV = 4. * math.sqrt( electron_density * (bohr_radius_m)**3 * math.pi) * rydberg_energy_eV
# Set to +/- 10*wpl if no range given.
energy_range_default = {'min' : -10.0*plasma_frequency_eV,
'max' : 10.0*plasma_frequency_eV,
'step': 0.1*plasma_frequency_eV}
energy_range = checkAndSetInstance( dict, energy_range, energy_range_default)
# Check keys.
if 'min' not in list(energy_range.keys()):
raise ValueError( "'min' missing in energy range (keys).")
if 'max' not in list(energy_range.keys()):
raise ValueError( "'max' missing in energy range (keys).")
if 'step' not in list(energy_range.keys()):
raise ValueError( "'step' missing in energy range (keys).")
# Check values.
for key in list(energy_range.keys()):
if not isinstance( energy_range[key], float):
raise TypeError( "All values in energy_range must be floats.")
if energy_range['min'] > energy_range['max']:
raise ValueError( "energy_range['min'] must be smaller than energy_range['max'].")
if energy_range['max'] - energy_range['min'] < energy_range['step']:
raise ValueError( "energy_range['max'] - energy_range['min'] must be larger than the stepsize energy_range['step'].")
# Return
return energy_range
[docs]def checkAndSetModelSii( model ):
"""
Utility to check if the model is a valid model for the Rayleigh (quasistatic) scattering feature.
@param model : The model to check.
<br/><b>type</b> : str
@return : The checked model
@raise ValueError if not a string or not a valid Sii model ('RPA', 'DH',
"""
if model is None:
model = 'SOCP'
valid_models = [ 'DH', 'OCP', 'SOCP', 'SOCPN' ]
if not (isinstance( model, str) or isinstance( model, float )):
raise TypeError( "The Sii model must be a valid model specifier (string) or a float giving the value of Sii(k).")
if not (model in valid_models or isinstance( model, float )):
raise ValueError( "The Sii model must be a valid Sii model or a numerical value. Valid Sii models are %s." % (str(valid_models)) )
return model
[docs]def checkAndSetModelSee( model ):
"""
Utility to check if the model is a valid model for the high frequency (dynamic) feature.
@param model : The model to check.
<br/><b>type</b> : str
@return : The checked model
@raise ValueError if not a string or not a valid See0 model ('RPA', 'BMA', 'BMA+sLFC', 'BMA+dLFC', 'LFC', 'Landen')
"""
# Default handling.
model = checkAndSetInstance( str, model, 'RPA')
# Valid models.
valid_models = ['RPA',
'Lindhard',
'Landen',
'sLFC',
'dLFC',
'BMA',
'BMA+sLFC',
'BMA+dLFC',
]
if model not in valid_models:
raise ValueError( "The See model must be a valid See0 model. Valid See0 models are %s." % (str(valid_models)) )
# Return
return model
[docs]def checkAndSetModelSbf( model ):
"""
Utility to check if the model is a valid model for the bound-free (Compton) scattering feature.
@param model : The model to check.
<br/><b>type</b> : str
@return : The checked model
@raise ValueError if not a string or not a valid Sbf model ('IA', 'HWF')
"""
# Handle default.
model = checkAndSetInstance( str, model, 'IA')
valid_models = ['IA',
'IBA',
'FFA'
]
if model not in valid_models:
raise ValueError( "The Sbf model must be a valid Sbf model. Valid Sbf models are %s." % (str(valid_models)) )
# Return
return model
[docs]def checkAndSetModelIPL( model ):
"""
Utility to check if the model is a valid model for ionization potential lowering.
@param model : The model to check.
<br/><b>type</b> : str or float
@return : The checked model
@raise ValueError if not a valid IPL model.
"""
# Handle default.
if model is None:
model = 'SP'
valid_models = ['SP',
'EK',
]
if not ( isinstance( model, str ) or isinstance( model, float ) ):
raise TypeError("The IPL model must be a string or a float.")
if not (model in valid_models or isinstance( model, float )):
raise ValueError( "The Sbf model must be a valid Sbf model or a numerical value. Valid Sbf models are %s." % (str(valid_models)) )
# Return
return model
[docs]def checkAndSetSourceSpectrum( source_spectrum ):
"""
Utility to check sanity of given input for the source spectrum identifier.
@param source_spectrum : The source spectrum identifier to check.
<br/><b>type</b> : str
@return : The checked identifier.
@raise : TypeError or ValueError if input is not valid.
"""
# Check type.
source_spectrum = checkAndSetInstance( str, source_spectrum, "GAUSS" )
# Convert to all upper case.
source_spectrum = source_spectrum.upper()
# Check input is valid.
if source_spectrum not in ["GAUSS", "LORENTZ", "PROP"]:
raise ValueError( "Parameter 'source_spectrum' must be one of 'GAUSS', 'LORENTZ', or 'PROP'.")
# All checked, return.
return source_spectrum
[docs]def checkAndSetSourceSpectrumFWHM( fwhm ):
"""
Utility to check sanity of given input for the source spectrum full width at half maximum (fwhm).
@param source_spectrum : The value to check.
<br/><b>type</b> : float
@return : The checked value.
@raise : TypeError or ValueError if input is not valid.
"""
# Check type.
fwhm = checkAndSetInstance( float, fwhm, 5.0 )
# Check positivity.
if fwhm <= 0.0:
raise ValueError( "The parameter 'source_spectrum_fwhm' must be positive.")
# All checked, return.
return fwhm