From 7e877b8838b8180fdf1448034de996c570a03079 Mon Sep 17 00:00:00 2001 From: pablogila Date: Sun, 5 Jan 2025 22:11:21 +0100 Subject: [PATCH] first integration of the spectra module --- README.md | 14 +- aton/__init__.py | 1 + aton/_version.py | 2 +- aton/interface/__init__.py | 17 + aton/interface/qe.py | 2 +- aton/spectra/__init__.py | 23 + aton/spectra/classes.py | 260 +- aton/spectra/{deuteration.py => deuterium.py} | 69 +- aton/spectra/fit.py | 97 +- aton/spectra/normalize.py | 88 +- aton/spectra/plot.py | 57 +- aton/spectra/sample.py | 76 - aton/spectra/samples.py | 70 + docs/aton.html | 24 +- docs/aton/_version.html | 4 +- docs/aton/alias.html | 2 +- docs/aton/atoms.html | 2 +- docs/aton/call.html | 2 +- docs/aton/elements.html | 2 +- docs/aton/file.html | 2 +- docs/aton/interface.html | 48 +- docs/aton/interface/castep.html | 2 +- docs/aton/interface/phonopy.html | 2 +- docs/aton/interface/qe.html | 8 +- docs/aton/spectra.html | 328 ++ docs/aton/spectra/classes.html | 2764 +++++++++++++++++ docs/aton/spectra/deuterium.html | 909 ++++++ docs/aton/spectra/fit.html | 737 +++++ docs/aton/spectra/normalize.html | 558 ++++ docs/aton/spectra/plot.html | 586 ++++ docs/aton/spectra/samples.html | 467 +++ docs/aton/text.html | 2 +- docs/aton/text/edit.html | 2 +- docs/aton/text/extract.html | 2 +- docs/aton/text/find.html | 2 +- docs/aton/units.html | 2 +- docs/search.js | 2 +- makedocs.py | 2 +- 38 files changed, 6847 insertions(+), 390 deletions(-) create mode 100644 aton/interface/__init__.py create mode 100644 aton/spectra/__init__.py rename aton/spectra/{deuteration.py => deuterium.py} (87%) delete mode 100644 aton/spectra/sample.py create mode 100644 aton/spectra/samples.py create mode 100644 docs/aton/spectra.html create mode 100644 docs/aton/spectra/classes.html create mode 100644 docs/aton/spectra/deuterium.html create mode 100644 docs/aton/spectra/fit.html create mode 100644 docs/aton/spectra/normalize.html create mode 100644 docs/aton/spectra/plot.html create mode 100644 docs/aton/spectra/samples.html diff --git a/README.md b/README.md index 28cfa7b..6f92a0f 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@

Welcome to the **A**b-ini**T**i**O** and **N**eutron research toolbox, or [Aton](https://en.wikipedia.org/wiki/Aten). -Just like its ancient Egyptian deity counterpart, this is an all-in-one Python package with powerful and comprehensive tools for cutting-edge materials research. +Inspired by its ancient Egyptian deity counterpart, this all-in-one Python package provides powerful and comprehensive tools for cutting-edge materials research, focused on (but not limited to) neutron science. -Aton allows you to easily create, edit and analyse all kinds of text files, with a special focus on *ab-initio* calculations. -In particular, it contains interfaces for [Quantum ESPRESSO](https://www.quantum-espresso.org/), [Phonopy](https://phonopy.github.io/phonopy/) and [CASTEP](https://castep-docs.github.io/castep-docs/). +Aton provides a range of spectral analysis tools, from spectra normalisation to deuteration estimation using the DINS impulse approximation. +A set of physico-chemical constants and definitions is also included. -This is combined with a range of spectral analysis tools, focused on (but not limited to) neutron science. -A set of physico-chemical constants and definitions is also included. +Aton also allows you to easily create, edit and analyse all kinds of text files, with a special focus on *ab-initio* calculations. +In particular, it contains interfaces for [Quantum ESPRESSO](https://www.quantum-espresso.org/), [Phonopy](https://phonopy.github.io/phonopy/) and [CASTEP](https://castep-docs.github.io/castep-docs/). Check the [full documentation online](https://pablogila.github.io/Aton/). @@ -46,8 +46,8 @@ pip install . # Documentation -Check the [full Aton documentation online](https://pablogila.github.io/Aton/). -An offline version of the documentation is available in `docs/aton.html`. +The full Aton documentation is available [online](https://pablogila.github.io/Aton/). +An offline version of the documentation is found at `docs/aton.html`. Code examples are included in the `examples/` folder. ## Submodules diff --git a/aton/__init__.py b/aton/__init__.py index 656aa9d..4592242 100644 --- a/aton/__init__.py +++ b/aton/__init__.py @@ -12,4 +12,5 @@ from . import file from . import text from . import interface +from . import spectra diff --git a/aton/_version.py b/aton/_version.py index 016e470..bf13db5 100644 --- a/aton/_version.py +++ b/aton/_version.py @@ -10,5 +10,5 @@ """ -__version__ = 'v0.0.1a3' +__version__ = 'v0.0.1b1' diff --git a/aton/interface/__init__.py b/aton/interface/__init__.py new file mode 100644 index 0000000..230878c --- /dev/null +++ b/aton/interface/__init__.py @@ -0,0 +1,17 @@ +""" +# Description + +This module contains interfaces for *ab-initio* and related calculation sofware. + +# Index + +- `aton.interface.qe` +- `aton.interface.phonopy` +- `aton.interface.castep` + +""" + +from . import qe +from . import phonopy +from . import castep + diff --git a/aton/interface/qe.py b/aton/interface/qe.py index cbad4a6..0a93d61 100644 --- a/aton/interface/qe.py +++ b/aton/interface/qe.py @@ -373,7 +373,7 @@ def set_value( ''' Replace the `value` of a `key` parameter in an input `filepath`. If `value=''`, the parameter gets deleted.\n - Remember to include the upper commas `'` on values that use them.\n + Remember to include the single quotes `'` on values that use them.\n Updating 'ATOMIC_POSITIONS' updates 'nat' automatically, and updating 'ATOMIC_SPECIES' updates 'ntyp'. ''' diff --git a/aton/spectra/__init__.py b/aton/spectra/__init__.py new file mode 100644 index 0000000..dc5d0ee --- /dev/null +++ b/aton/spectra/__init__.py @@ -0,0 +1,23 @@ +""" +# Description + +This module contains spectral analysis tools. + +# Index + +- `aton.spectra.classes` +- `aton.spectra.fit` +- `aton.spectra.normalize` +- `aton.spectra.deuterium` +- `aton.spectra.samples` +- `aton.spectra.plot` + +""" + +from .classes import Spectra, Plotting, Scaling, Material +from . import fit +from . import normalize +from . import deuterium +from . import samples +from .plot import plot + diff --git a/aton/spectra/classes.py b/aton/spectra/classes.py index 1a50985..fd6c180 100644 --- a/aton/spectra/classes.py +++ b/aton/spectra/classes.py @@ -1,11 +1,13 @@ -''' +""" # Description -This module contains common classes and their functions, used to load and manipulate data. -Any class can be instantiated directly: for example, to create a new -`Spectra` class for your data, you just need to call `maatpy.Spectra(options)` as described below: + +This module contains common classes used to load and manipulate spectral data. +Any class can be instantiated directly from the `aton.spectra` module; +for example, to create a new `Spectra` class for your data, +you just need to call `aton.spectra.Spectra(options)` as described below: ```python -import maatpy as mt -ins = mt.Spectra( +import aton +ins = aton.spectra.Spectra( # Options here ) ``` @@ -13,27 +15,27 @@ # Index - `Spectra`. Used to load and process spectral data. - `Plotting`. Stores plotting options. Used inside `Spectra.plotting`. -- `ScaleRange`. Handles data normalization inside the specified range of values. Used inside `Spectra.scale_range`. +- `Scaling`. Handles data normalization inside the specified range of values. Used inside `Spectra.scaling`. - `Material`. Used to store and calculate material parameters, such as molar masses and cross sections. --- -''' +""" -from . import alias -from .constants import * -from .elements import atom -from . import atoms import numpy as np import pandas as pd from copy import deepcopy import os +import aton.alias as alias +from aton.units import * +import aton.atoms +import aton.elements class Plotting: ''' Stores plotting options. - Read by `maatpy.plot.spectra(Spectra)`. + Read by `aton.spectra.plot`. ''' def __init__( self, @@ -54,6 +56,7 @@ def __init__( legend_title:str=None, legend_size='medium', legend_loc='best', + save_as:str=None, ): '''Default values can be overwritten when initializing the Plotting object.''' self.title = title @@ -105,8 +108,10 @@ def __init__( legend = [legend] self.legend = legend ''' - If `None`, the filenames will be used as legend. Can be a bool to show or hide the plot legend. - It can also be an array containing the strings to display; in that case, elements set to `False` will not be displayed. + If `None`, the files will be used as legend. + Can be a bool to show or hide the plot legend. + It can also be an array containing the strings to display; + in that case, elements set to `False` will not be displayed. ''' self.legend_title = legend_title '''Title of the legend. Defaults to `None`.''' @@ -114,6 +119,8 @@ def __init__( '''Size of the legend, as in matplotlib. Defaults to `'medium'`.''' self.legend_loc = legend_loc '''Location of the legend, as in matplotlib. Defaults to `'best'`.''' + self.save_as = save_as + '''Filename to save the plot. None by default.''' def _set_limits(self, limits) -> list: '''Set the x and y limits of the plot.''' @@ -136,23 +143,33 @@ def _set_limits(self, limits) -> list: raise ValueError(f"Unknown plotting limits: Must be specified as a list of two elements, as [low_limit, high_limit]. Got: {limits}") -class ScaleRange: +class Scaling: ''' - The ScaleRange object is used to handle the normalization of the data inside the specified x-range, - to the same heigth as in the specified `index` dataset (the first one by default). - - Custom heights can be normalized with `ymin` and `ymax`, overriding the x-values. - For example, you may want to normalize with respect to the height of a given peak that overlaps with another. - Those peaks may have ymin values of 2 and 3, and ymax values of 50 and 60 respectively. In that case: + The Scaling object is used to handle the normalization + of the data inside the specified x-range, + to the same heigth as in the specified `index` dataset + (the first one by default). + + Custom heights can be normalized with `ymin` and `ymax`, + overriding the x-values. + For example, you may want to normalize two spectra datasets + with respect to the height of a given peak that overlaps with another. + Those peaks may have ymin values of 2 and 3, and ymax values + of 50 and 60 respectively. In that case: ```python - spectra.scale_range = ScaleRange(index=0, ymin=[2, 3], ymax=[50, 60]) + spectra.scaling = Scaling(index=0, ymin=[2, 3], ymax=[50, 60]) ``` - To normalize when plotting with `maatpy.plot.spectra(Spectra)`, remember to set `Plotting.normalize=True`. - When normalizing the plot, all datasets are fitted inside the plotting window, scaling over the entire data range into view. - To override this behaviour and expand over the given range to fill the plot window, you can set `ScaleRange.zoom=True`. - This zoom setting can also be enabled without normalizing the plot, resulting in a zoom over the given range - so that the `index` dataset fits the full plotting window, scaling the rest of the set accordingly. + To normalize when plotting with `aton.spectra.plot(Spectra)`, + remember to set `Plotting.normalize=True`. + + When normalizing the plot, all datasets are fitted inside the + plotting window, scaling over the entire data range into view. + To override this behaviour and expand over the given range + to fill the plot window, you can set `Scaling.zoom=True`. + This zoom setting can also be enabled without normalizing the plot, + resulting in a zoom over the given range so that the `index` dataset + fits the full plotting window, scaling the rest of the set accordingly. ''' def __init__( self, @@ -163,7 +180,7 @@ def __init__( ymax:list=None, zoom:bool=False, ): - '''All values can be set when initializing the ScaleRange object.''' + '''All values can be set when initializing the Scaling object.''' self.index: int = index '''Index of the dataframe to use as reference.''' self.xmin: float = xmin @@ -200,9 +217,9 @@ def set_y(self, ymin:list=None, ymax:list=None): class Spectra: - ''' - Spectra object. Used to load and process spectral data. - Most functions present in MaatPy receive this object as input. + """Spectra object. Used to load and process spectral data. + + Most functions present in the `atom.spectra` module receive this object as input. **Use example:** to load two INS spectra CSV files from MANTID with cm$^{-1}$ as input units, and plot them in meV units, normalizing their heights over the range from 20 to 50 meV: @@ -210,14 +227,14 @@ class Spectra: import maatpy as mt ins = mt.Spectra( type='INS', - filename=['example_1.csv', 'example_2.csv'], + files=['example_1.csv', 'example_2.csv'], units_in='cm-1', units='meV', plotting=mt.Plotting( title='Calculated INS', normalize=True, ), - scale_range=mt.ScaleRange( + scaling=mt.Scaling( xmin=20, xmax=50, ), @@ -228,93 +245,90 @@ class Spectra: Check more use examples in the `/examples/` folder. Below is a list of the available parameters for the Spectra object, along with their descriptions. - ''' + """ def __init__( self, type:str=None, comment:str=None, - save_as:str=None, - filename=None, - dataframe=None, + files=None, + dfs=None, units=None, units_in=None, plotting:Plotting=Plotting(), - scale_range:ScaleRange=ScaleRange(), + scaling:Scaling=Scaling(), ): '''All values can be set when initializing the Spectra object.''' self.type = None '''Type of the spectra: `'INS'`, `'ATR'`, or `'RAMAN'`.''' self.comment = comment '''Custom comment. If `Plotting.title` is None, it will be the title of the plot.''' - self.save_as = save_as - '''Filename to save the plot. None by default.''' - self.filename = None + self.files = None ''' - List containing the filenames with the spectral data. + List containing the files with the spectral data. Loaded automatically with Pandas at initialization. In order for Pandas to read the files properly, note that the column lines must start by `#`. Any additional line that is not data must be removed or commented with `#`. CSV files must be formatted with the first column as the energy or energy transfer, and the second column with the intensity or absorbance, depending on the case. An additional third `'Error'` column can be used. ''' - self.dataframe = None + self.dfs = None ''' List containing the pandas dataframes with the spectral data. - Loaded automatically from the filenames at initialization. + Loaded automatically from the files at initialization. ''' self.units = None - '''Target units of the spectral data. Can be `'meV'` or `'cm-1'`, written as any of the variants listed in `maatpy.alias.unit[unit]`.''' + '''Target units of the spectral data. Can be `'meV'` or `'cm-1'`, written as any of the variants listed in `aton.alias.units[unit]`.''' self.units_in = None ''' - Input units of the spectral data, used in the input CSV files. Can be `'meV'` or `'cm-1'`, written as any of the variants listed in `maatpy.alias.unit[unit]`. + Input units of the spectral data, used in the input CSV files. Can be `'meV'` or `'cm-1'`, written as any of the variants listed in `aton.alias.units[unit]`. If the input CSV files have different units, it can also be set as a list of the same length of the number of input files, eg. `['meV', 'cm-1', 'cm-1']`. ''' self.plotting = plotting '''`Plotting` object, used to set the plotting options.''' - self.scale_range = scale_range - '''`ScaleRange` object, used to set the normalization parameters.''' + self.scaling = scaling + '''`Scaling` object, used to set the normalization parameters.''' self = self._set_type(type) - self = self._set_dataframe(filename, dataframe) + self = self._set_dataframes(files, dfs) self = self.set_units(units, units_in) def _set_type(self, type): '''Set and normalize the type of the spectra: `INS`, `ATR`, or `RAMAN`.''' - if type in alias.experiment['INS']: + if type in alias.experiments['INS']: self.type = 'INS' - elif type in alias.experiment['ATR']: + elif type in alias.experiments['ATR']: self.type = 'ATR' - elif type in alias.experiment['RAMAN']: + elif type in alias.experiments['RAMAN']: self.type = 'RAMAN' else: self.type = type return self - def _set_dataframe(self, filename, dataframe): - '''Set the dataframes, from the given files or dataframes.''' - if isinstance(filename, list): - self.filename = filename - elif isinstance(filename, str): - self.filename = [filename] + def _set_dataframes(self, files, dfs): + '''Set the dfs list of dataframes, from the given files or dfs.''' + if isinstance(files, list): + self.files = files + elif isinstance(files, str): + self.files = [files] else: - self.filename = [] + self.files = [] - if isinstance(dataframe, pd.DataFrame): - self.dataframe = [dataframe] - elif isinstance(dataframe, list) and isinstance(dataframe[0], pd.DataFrame): - self.dataframe = dataframe + if isinstance(dfs, pd.DataFrame): + self.dfs = [dfs] + elif isinstance(dfs, list) and isinstance(dfs[0], pd.DataFrame): + self.dfs = dfs else: - self.dataframe = [self._read_dataframe(file) for file in self.filename] + self.dfs = [self._read_dataframe(filename) for filename in self.files] return self def _read_dataframe(self, filename): - '''Read the dataframes from the files.''' + '''Read a dataframe from a file.''' root = os.getcwd() - file = os.path.join(root, filename) - df = pd.read_csv(file, comment='#') + file_path = os.path.join(root, filename) + df = pd.read_csv(file_path, comment='#') df = df.sort_values(by=df.columns[0]) # Sort the data by energy - print(f'\nNew dataframe from {file}') + print(f'\nNew dataframe from {filename}') print(df.head(),'\n') return df @@ -336,8 +350,8 @@ def set_units( mev = 'meV' cm = 'cm-1' unit_format={ - mev: alias.unit['meV'], - cm: alias.unit['cm-1'] + alias.unit['cm'], + mev: alias.units['meV'], + cm: alias.units['cm-1'] + alias.units['cm'], } if self.units is not None: units_in = deepcopy(self.units) @@ -358,15 +372,15 @@ def set_units( units_in[i] = key break if len(units_in) == 1: - units_in = units_in * len(self.filename) - elif len(units_in) != len(self.filename): - raise ValueError("units_in must be a list of the same length as filenames.") + units_in = units_in * len(self.files) + elif len(units_in) != len(self.files): + raise ValueError("units_in must be a list of the same length as files.") if isinstance(units_in, str): for key, value in unit_format.items(): if units_in in value: units_in = key break - units_in = [units_in] * len(self.filename) + units_in = [units_in] * len(self.files) if isinstance(self.units, list): for i, unit in enumerate(self.units): for key, value in unit_format.items(): @@ -374,32 +388,32 @@ def set_units( self.units[i] = key break if len(self.units) == 1: - self.units = self.units * len(self.filename) - elif len(self.units) != len(self.filename): - raise ValueError("units_in must be a list of the same length as filenames.") + self.units = self.units * len(self.files) + elif len(self.units) != len(self.files): + raise ValueError("units_in must be a list of the same length as files.") if isinstance(self.units, str): for key, value in unit_format.items(): if self.units in value: self.units = key break - self.units = [self.units] * len(self.filename) + self.units = [self.units] * len(self.files) if units_in is None: return self - # Otherwise, convert the dataframes + # Otherwise, convert the dfs if len(self.units) != len(units_in): raise ValueError("Units len mismatching.") for i, unit in enumerate(self.units): if unit == units_in[i]: continue if unit == mev and units_in[i] == cm: - self.dataframe[i][self.dataframe[i].columns[0]] = self.dataframe[i][self.dataframe[i].columns[0]] * cm_to_meV + self.dfs[i][self.dfs[i].columns[0]] = self.dfs[i][self.dfs[i].columns[0]] * cm_to_meV elif unit == cm and units_in[i] == mev: - self.dataframe[i][self.dataframe[i].columns[0]] = self.dataframe[i][self.dataframe[i].columns[0]] * meV_to_cm + self.dfs[i][self.dfs[i].columns[0]] = self.dfs[i][self.dfs[i].columns[0]] * meV_to_cm else: raise ValueError(f"Unit conversion error between '{unit}' and '{units_in[i]}'") # Rename dataframe columns E_units = None - for i, df in enumerate(self.dataframe): + for i, df in enumerate(self.dfs): if self.units[i] == mev: E_units = 'meV' elif self.units[i] == cm: @@ -407,26 +421,26 @@ def set_units( else: E_units = self.units[i] if self.type == 'INS': - if self.dataframe[i].shape[1] == 3: - self.dataframe[i].columns = [f'Energy transfer / {E_units}', 'S(Q,E)', 'Error'] + if self.dfs[i].shape[1] == 3: + self.dfs[i].columns = [f'Energy transfer / {E_units}', 'S(Q,E)', 'Error'] else: - self.dataframe[i].columns = [f'Energy transfer / {E_units}', 'S(Q,E)'] + self.dfs[i].columns = [f'Energy transfer / {E_units}', 'S(Q,E)'] elif self.type == 'ATR': - self.dataframe[i].columns = [f'Wavenumber / {E_units}', 'Absorbance'] + self.dfs[i].columns = [f'Wavenumber / {E_units}', 'Absorbance'] elif self.type == 'RAMAN': - self.dataframe[i].columns = [f'Raman shift / {E_units}', 'Counts'] + self.dfs[i].columns = [f'Raman shift / {E_units}', 'Counts'] return self class Material: - ''' - Material class. + '''Material class. Used to calculate molar masses and cross sections, - and to pass data to different analysis functions such as `maatpy.deuteration.impulse_approx().` + and to pass data to different analysis functions + such as `aton.spectra.deuterium.impulse_approx().` ''' def __init__( self, - atoms:dict, + elements:dict, name:str=None, grams:float=None, grams_error:float=None, @@ -434,12 +448,14 @@ def __init__( mols_error:float=None, molar_mass:float=None, cross_section:float=None, + peaks:dict=None, ): ''' - All values can be set when initializing the Material object. However, it is recommended - to only set the atoms and the grams, and optionally the name, and calculate the rest with `Material.set()`. + All values can be set when initializing the Material object. + However, it is recommended to only set the elements and the grams, + and optionally the name, and calculate the rest with `Material.set()`. ''' - self.atoms = atoms + self.elements = elements ''' Dict of atoms in the material, as in `{'H': 6, 'C':1, 'N':1}`. Isotopes can be expressed as 'H2', 'He4', etc. with the atom symbol + isotope mass number. @@ -449,15 +465,27 @@ def __init__( self.grams = grams '''Mass, in grams.''' self.grams_error = grams_error - '''Error of the measured mass in grams. Set automatically with `Material.set()`.''' + '''Error of the measured mass in grams. + Set automatically with `Material.set()`. + ''' self.mols = mols - '''Number of moles. Set automatically with `Material.set()`.''' + '''Number of moles. + Set automatically with `Material.set()`. + ''' self.mols_error = mols_error - '''Error of the number of moles. Set automatically with `Material.set()`.''' + '''Error of the number of moles. + Set automatically with `Material.set()`. + ''' self.molar_mass = molar_mass - '''Molar mass of the material, in mol/g. Calculated automatically with `Material.set()`.''' + '''Molar mass of the material, in mol/g. + Calculated automatically with `Material.set()`. + ''' self.cross_section = cross_section - '''Cross section of the material, in barns. Calculated automatically with `Material.set()`.''' + '''Neutron total bound scattering cross section, in barns. + Calculated automatically with `Material.set()`. + ''' + self.peaks = peaks + '''Dict with interesting peaks that you might want to store for later use.''' def _set_grams_error(self): '''Set the error in grams, based on the number of decimal places.''' @@ -468,18 +496,18 @@ def _set_grams_error(self): self.grams_error = 10**(-decimal_accuracy) def _set_mass(self): - ''' - Set the molar mass of the material. - If `Material.grams` is not `None`, the number of moles will be calculated and overwritten. - If an isotope is used, eg. `'He4'`, it splits the name with `maatpy.constants.atoms.get_isotope_index`. + '''Set the molar mass of the material. + If `Material.grams` is provided, the number of moles will be + calculated and overwritten. Isotopes can be used as 'element + A', + eg. `'He4'`. This gets splitted with `aton.elements.split_isotope`. ''' material_grams_per_mol = 0.0 - for key in self.atoms: + for key in self.elements: try: - material_grams_per_mol += self.atoms[key] * atom[key].mass + material_grams_per_mol += self.elements[key] * aton.atoms[key].mass except KeyError: # Split the atomic flag as H2, etc - element, isotope = atoms.split_isotope(key) - material_grams_per_mol += self.atoms[key] * atom[element].isotope[isotope].mass + element, isotope = aton.elements.split_isotope(key) + material_grams_per_mol += self.elements[key] * aton.atoms[element].isotope[isotope].mass self.molar_mass = material_grams_per_mol if self.grams is not None: self._set_grams_error() @@ -488,16 +516,16 @@ def _set_mass(self): def _set_cross_section(self): ''' - Set the cross section of the material, based on the atoms dict. - If an isotope is used, eg. `'He4'`, it splits the name with `maatpy.constants.atoms.get_isotope_index`. + Set the cross section of the material, based on the self.elements dict. + If an isotope is used, eg. `'He4'`, it splits the name with `aton.elements.split_isotope`. ''' total_cross_section = 0.0 - for key in self.atoms: + for key in self.elements: try: - total_cross_section += self.atoms[key] * atom[key].cross_section + total_cross_section += self.elements[key] * aton.atoms[key].cross_section except KeyError: # Split the atomic flag as H2, etc - element, isotope_index = atoms.split_isotope(key) - total_cross_section += self.atoms[key] * atom[element].isotope[isotope_index].cross_section + element, isotope_index = aton.elements.split_isotope(key) + total_cross_section += self.elements[key] * aton.atoms[element].isotope[isotope_index].cross_section self.cross_section = total_cross_section def set(self): @@ -522,7 +550,7 @@ def print(self): print(f'Molar mass: {self.molar_mass} g/mol') if self.cross_section is not None: print(f'Cross section: {self.cross_section} barns') - if self.atoms is not None: - print(f'Atoms: {self.atoms}') + if self.elements is not None: + print(f'Elements: {self.elements}') print('') diff --git a/aton/spectra/deuteration.py b/aton/spectra/deuterium.py similarity index 87% rename from aton/spectra/deuteration.py rename to aton/spectra/deuterium.py index a96d598..7b6136d 100644 --- a/aton/spectra/deuteration.py +++ b/aton/spectra/deuterium.py @@ -1,20 +1,20 @@ -''' +""" # Description -This module contains different methods to calculate deuteration levels from spectra. +This module contains methods to calculate deuteration levels from different spectra. # Index - `impulse_approx()` - `peaks_mapi()` --- -''' +""" -from . import alias -from .constants import * -from .classes import * -from .fit import area_under_peak, ratio_areas, plateau from copy import deepcopy +import aton.alias as alias +from aton.units import * +from .classes import Spectra, Material +from aton.spectra.fit import area_under_peak, ratio_areas, plateau def impulse_approx( @@ -23,17 +23,22 @@ def impulse_approx( material_D: Material, threshold: float=600, H_df_index: int=0, - D_df_index: int=1 + D_df_index: int=1, ) -> tuple: - ''' - Calculate the deuteration levels from INS spectra with the *Impulse Approximation*, - see https://www.tandfonline.com/doi/full/10.1080/00018732.2017.1317963. - - Protonated and deuterated materials must be specified as `maatpy.classes.Material` objects. - The threshold controls the start of the plateau (in meV) to consider Deep Inelastic Neutron Scattering (DINS). - The protonated and deuterated dataframe indexes are specified by `H_df_index` and `D_df_index`, respectively. - - In this approximation, the ideal ratio between the cross-sections and the experimental ratio between the pleteaus at high energies should be the same: + """Calculate the deuteration levels from INS spectra + with the *Impulse Approximation*, see + https://www.tandfonline.com/doi/full/10.1080/00018732.2017.1317963. + + Protonated and deuterated materials must be specified + as `aton.spectra.Material` objects. + The threshold controls the start of the plateau (in meV) + to start considering Deep Inelastic Neutron Scattering (DINS). + The protonated and deuterated dataframe indexes are specified + by `H_df_index` and `D_df_index`, respectively. + + In this approximation, the ideal ratio between + the cross-sections and the experimental ratio between + the pleteaus at high energies should be the same: $$ \\frac{\\text{plateau_D}}{\\text{plateau_H}} \\approx \\frac{\\text{cross_section_D}}{\\text{cross_section_H}} $$ @@ -43,8 +48,8 @@ def impulse_approx( $$ > [!WARNING] - > This approximation is very sensitive to the mass sample, specified by `maatpy.classes.Material.grams`. - ''' + > This approximation is very sensitive to the mass sample, specified by `aton.spectra.Material.grams`. + """ ins = deepcopy(ins) material_H = deepcopy(material_H) material_D = deepcopy(material_D) @@ -64,8 +69,8 @@ def impulse_approx( ins.set_units('meV', units_in) # Divide the y values of the dataframes by the mols of the material. - ins.dataframe[H_df_index][ins.dataframe[H_df_index].columns[1]] = ins.dataframe[H_df_index][ins.dataframe[H_df_index].columns[1]] - ins.dataframe[D_df_index][ins.dataframe[D_df_index].columns[1]] = ins.dataframe[D_df_index][ins.dataframe[D_df_index].columns[1]] + ins.dfs[H_df_index][ins.dfs[H_df_index].columns[1]] = ins.dfs[H_df_index][ins.dfs[H_df_index].columns[1]] + ins.dfs[D_df_index][ins.dfs[D_df_index].columns[1]] = ins.dfs[D_df_index][ins.dfs[D_df_index].columns[1]] plateau_H, plateau_H_error = plateau(ins, [threshold, None], H_df_index) plateau_D, plateau_D_error = plateau(ins, [threshold, None], D_df_index) @@ -99,9 +104,11 @@ def peaks_mapi( df_index:int=0, ) -> str: ''' - Calculate the deuteration of your CH$_3$NH$_3$PbI$_3$ samples by integrating the INS disrotatory peaks, + Calculate the deuteration of your CH$_3$NH$_3$PbI$_3$ samples + by integrating the INS disrotatory peaks, which appear at around 38 meV for the fully protonated sample. - Note that `peaks` must be a dictionary with the peak limits and the baseline, as in the example below: + Note that `peaks` must be a dictionary with the peak limits + and the baseline, as in the example below: ```python peaks = { 'baseline' : None, @@ -115,19 +122,22 @@ def peaks_mapi( 'h0d6' : [26.5, 28.0], } ``` - Peak keywords required for partial deuteration: `h6d0`, `h5d1`, `h4d2`, `h3d3`. - Additional peak keywords required for total deuteration: `h2d4`, `h1d5`, `h0d6`. - If some peak is not present in your sample, just set the limits to a small baseline plateau. + Peak keywords required for selective deuteration (only C or only N): + `h6d0`, `h5d1`, `h4d2`, `h3d3`. + Additional peak keywords required for total deuteration: + `h2d4`, `h1d5`, `h0d6`. + If some peak is not present in your sample, + just set the limits to a small baseline plateau. ''' peak_data = deepcopy(ins) baseline = 0.0 baseline_error = 0.0 - if 'baseline' in peaks: + if 'baseline' in peaks.keys(): if peaks['baseline'] is not None: baseline = peaks['baseline'] - if 'baseline_error' in peaks: + if 'baseline_error' in peaks.keys(): if peaks['baseline_error'] is not None: baseline_error = peaks['baseline_error'] @@ -227,12 +237,11 @@ def peaks_mapi( protonation_CDND_amine = 1 * h3d3_ratio_CDND + (2/3) * h2d4_ratio_CDND + (1/3) * h1d5_ratio_CDND + 0 * h0d6_ratio_CDND protonation_CDND_amine_error = np.sqrt((1 * h3d3_error_CDND)**2 + (2/3 * h2d4_error_CDND)**2 + (1/3 * h1d5_error_CDND)**2) - print() if hasattr(ins, "plotting") and ins.plotting.legend != None: print(f'Sample: {ins.plotting.legend[df_index]}') else: - print(f'Sample: {ins.filename[df_index]}') + print(f'Sample: {ins.files[df_index]}') print(f'Corrected baseline: {round(baseline,2)} +- {round(baseline_error,2)}') if not run_total: print(f"HHH {h6d0_limits}: {round(h6d0_ratio,2)} +- {round(h6d0_error,2)}") diff --git a/aton/spectra/fit.py b/aton/spectra/fit.py index c52fcab..10938a9 100644 --- a/aton/spectra/fit.py +++ b/aton/spectra/fit.py @@ -1,54 +1,23 @@ -''' +""" # Description -This module contains functions for fitting and analyzing data. +This module contains functions for fitting and analyzing spectral data. # Index -- `mean()` - `plateau()` - `area_under_peak()` - `ratio_areas()` +- `mean()` --- -''' +""" import math -from .constants import * -from .classes import * import scipy import numpy as np from copy import deepcopy - - -def mean( - array:list, - rounded:bool=True, - degrees_of_freedom=0 - ) -> tuple: - ''' - Takes an `array` of numerical values - and returns a tuple with the mean and standard deviation, - calculated with numpy as:\n - $\\sigma_{x}=\\sqrt{\\frac{\\sum{(x_{i}-{\\overline{x}})^2}}{N-\\text{ddof}}}$\n - where ddof are the delta `degrees_of_freedom`, zero by default. - Set it to `1` for a corrected sample standard deviation (low N cases), - see more details [here](https://en.wikipedia.org/wiki/Standard_deviation#Corrected_sample_standard_deviation).\n - The mean is rounded up to the order of the error by default. To override this behaviour, set `rounded=False`. - ''' - if not all(isinstance(x, (int, float, np.ndarray)) for x in array): - raise ValueError("mean_std(list) requires numerical values (int, float, or numpy.ndarray).") - data = np.asarray(array) - mean = float(data.mean()) - error = float(data.std(ddof=degrees_of_freedom)) - if not rounded or error == 0: - return mean, error - exponent = int(math.floor(math.log10(abs(error)))) - first_three_digits = int(100*abs(error) / 10**exponent) - if 104 < first_three_digits < 195: - exponent -= 1 - rounded_mean = round(mean, -exponent) - rounded_error = round(error, -exponent) - return rounded_mean, rounded_error +from aton.units import * +from .classes import Spectra def plateau( @@ -56,15 +25,15 @@ def plateau( cuts=None, df_index:int=0 ) -> tuple: - ''' - Fit the mean value and the error of a plateau in a `maatpy.classes.Spectra` object. - If `maatpy.classes.Spectra.dataframe[df_index]` has an 'Error' column, those errors are also taken into account + """Fit the mean value and the error of a plateau in a `aton.spectra.Spectra` object. + + If `aton.spectra.Spectra.dfs[df_index]` has an 'Error' column, those errors are also taken into account along with the standard deviation of the mean, else only the standard deviation is considered. The 'Error' column title can be any string in `maatpy.alias.file['Error']`.\n Use as `maatpy.fit.plateau(spectra, cuts=[low_cut, high_cut], df_index=0)`. Note that `cuts`, `low_cut` and/or `top_cut` can be set to None. - ''' - df = deepcopy(spectra.dataframe[df_index]) + """ + df = deepcopy(spectra.dfs[df_index]) if isinstance(cuts, list): low_cut = cuts[0] if len(cuts) > 1: @@ -85,7 +54,7 @@ def plateau( df = df[df[df.columns[0]] <= top_cut] mean = df[df.columns[1]].mean() std_mean = df[df.columns[1]].std() - error_column = next((col for col in alias.file['Error'] if col in df.columns), None) # Get the error column title + error_column = next((col for col in alias.file['error'] if col in df.columns), None) # Get the error column title if error_column: errors = df[error_column].to_numpy() std_data = np.sqrt(np.sum(errors**2)) / len(errors) @@ -102,7 +71,7 @@ def area_under_peak( errors_as_in_baseline:bool=True, min_as_baseline:bool=False ) -> tuple: - ''' + """ Calculate the area under a given peak. Peaks must be defined as `peak:list=[xmin, xmax, baseline=0, baseline_error=0]`. @@ -111,7 +80,7 @@ def area_under_peak( If `min_as_baseline=True` and `baseline=0`, the baseline is assumed to be the minimum value. Also, if `min_as_baseline=True` and there are negative areas even after applying the baseline, the baseline will be corrected to the minimum value. - ''' + """ if len(peak) < 2: raise ValueError("area_under_peak: peak must have at least two values: [xmin, xmax]") xmin = peak[0] @@ -119,7 +88,7 @@ def area_under_peak( baseline = peak[2] if len(peak) >= 3 else 0.0 baseline_error = peak[3] if len(peak) >= 4 else 0.0 - df = deepcopy(spectra.dataframe[df_index]) + df = deepcopy(spectra.dfs[df_index]) df_range = df[(df[df.columns[0]] >= xmin) & (df[df.columns[0]] <= xmax)] x = df_range[df.columns[0]].to_numpy() y = df_range[df.columns[1]].to_numpy() @@ -153,12 +122,12 @@ def ratio_areas( area_total_error:float=0.0, inverse_ratio:bool=False ) -> tuple: - ''' - Check the ratio between two areas, e.g. to estimate deuteration levels from ATR data. + """Check the ratio between two areas, e.g. to estimate deuteration levels from ATR data. + The ratio is calculated as `area / area_total`. This behavior is modified if `inverse_ratio = True`, so that the ratio is calculated as `(area_total - area) / area_total`. Note that changing the ratio calculation also affects the error propagation. - ''' + """ if inverse_ratio: ratio = (area_total - area) / area_total if ratio != 0.0: @@ -174,3 +143,33 @@ def ratio_areas( return ratio, ratio_error + +def mean( + array:list, + rounded:bool=True, + degrees_of_freedom=0 + ) -> tuple: + """Takes an `array` of numerical values and returns the mean and standard deviation. + + It is calculated with numpy as:\n + $\\sigma_{x}=\\sqrt{\\frac{\\sum{(x_{i}-{\\overline{x}})^2}}{N-\\text{ddof}}}$\n + where ddof are the delta `degrees_of_freedom`, zero by default. + Set it to `1` for a corrected sample standard deviation (low N cases), + see more details [here](https://en.wikipedia.org/wiki/Standard_deviation#Corrected_sample_standard_deviation).\n + The mean is rounded up to the order of the error by default. To override this behaviour, set `rounded=False`. + """ + if not all(isinstance(x, (int, float, np.ndarray)) for x in array): + raise ValueError("mean_std(list) requires numerical values (int, float, or numpy.ndarray).") + data = np.asarray(array) + mean = float(data.mean()) + error = float(data.std(ddof=degrees_of_freedom)) + if not rounded or error == 0: + return mean, error + exponent = int(math.floor(math.log10(abs(error)))) + first_three_digits = int(100*abs(error) / 10**exponent) + if 104 < first_three_digits < 195: + exponent -= 1 + rounded_mean = round(mean, -exponent) + rounded_error = round(error, -exponent) + return rounded_mean, rounded_error + diff --git a/aton/spectra/normalize.py b/aton/spectra/normalize.py index b52c06b..c381417 100644 --- a/aton/spectra/normalize.py +++ b/aton/spectra/normalize.py @@ -11,10 +11,10 @@ ''' -from . import alias +import aton.alias as alias +from aton.units import * from .classes import * from .fit import * -from .constants import * def unit_str(unit:str): @@ -27,92 +27,92 @@ def unit_str(unit:str): def spectra(spectra:Spectra): - '''Normalize the given spectra by height, with optional `maatpy.classes.ScaleRange` attributes.''' + '''Normalize the given spectra by height, with optional `maatpy.classes.Scaling` attributes.''' sdata = deepcopy(spectra) - if hasattr(sdata, 'scale_range') and sdata.scale_range is not None: - scale_range = sdata.scale_range - if scale_range.ymax: + if hasattr(sdata, 'scaling') and sdata.scaling is not None: + scaling = sdata.scaling + if scaling.ymax: return _spectra_y(sdata) else: - scale_range = ScaleRange() + scaling = Scaling() - df_index = scale_range.index if scale_range.index else 0 - df0 = sdata.dataframe[df_index] + df_index = scaling.index if scaling.index else 0 + df0 = sdata.dfs[df_index] - if scale_range.xmin is None: - scale_range.xmin = min(df0[df0.columns[0]]) - if scale_range.xmax is None: - scale_range.xmax = max(df0[df0.columns[0]]) + if scaling.xmin is None: + scaling.xmin = min(df0[df0.columns[0]]) + if scaling.xmax is None: + scaling.xmax = max(df0[df0.columns[0]]) - sdata.scale_range = scale_range + sdata.scaling = scaling - xmin = scale_range.xmin - xmax = scale_range.xmax + xmin = scaling.xmin + xmax = scaling.xmax df0 = df0[(df0[df0.columns[0]] >= xmin) & (df0[df0.columns[0]] <= xmax)] ymax_on_range = df0[df0.columns[1]].max() normalized_dataframes = [] - for df in sdata.dataframe: + for df in sdata.dfs: df_range = df[(df[df.columns[0]] >= xmin) & (df[df.columns[0]] <= xmax)] i_ymax_on_range = df_range[df_range.columns[1]].max() df[df.columns[1]] = df[df.columns[1]] * ymax_on_range / i_ymax_on_range normalized_dataframes.append(df) - sdata.dataframe = normalized_dataframes + sdata.dfs = normalized_dataframes return sdata def _spectra_y(sdata:Spectra): - if not len(sdata.scale_range.ymax) == len(sdata.dataframe): + if not len(sdata.scaling.ymax) == len(sdata.dfs): raise ValueError("normalize: len(ymax) does not match len(dataframe)") - scale_range = sdata.scale_range - ymax = scale_range.ymax - ymin = scale_range.ymin if scale_range.ymin else [0.0] + scaling = sdata.scaling + ymax = scaling.ymax + ymin = scaling.ymin if scaling.ymin else [0.0] if len(ymin) == 1: - ymin = ymin * len(sdata.dataframe) - index = scale_range.index if scale_range.index else 0 + ymin = ymin * len(sdata.dfs) + index = scaling.index if scaling.index else 0 reference_height = ymax[index] - ymin[index] normalized_dataframes = [] - for i, df in enumerate(sdata.dataframe): + for i, df in enumerate(sdata.dfs): height = ymax[i] - ymin[i] df[df.columns[1]] = df[df.columns[1]] * reference_height / height normalized_dataframes.append(df) - sdata.dataframe = normalized_dataframes + sdata.dfs = normalized_dataframes return sdata def area(spectra:Spectra): ''' - Normalize the given spectra by the area under the datasets, with optional `maatpy.classes.ScaleRange` attributes. + Normalize the given spectra by the area under the datasets, with optional `maatpy.classes.Scaling` attributes. ''' sdata = deepcopy(spectra) - if hasattr(sdata, 'scale_range') and sdata.scale_range is not None: - scale_range = sdata.scale_range - if scale_range.ymax: + if hasattr(sdata, 'scaling') and sdata.scaling is not None: + scaling = sdata.scaling + if scaling.ymax: return _normalize_y(sdata) else: - scale_range = ScaleRange() + scaling = Scaling() - df_index = scale_range.index if scale_range.index else 0 - df0 = sdata.dataframe[df_index] + df_index = scaling.index if scaling.index else 0 + df0 = sdata.dfs[df_index] - if scale_range.xmin is None: - scale_range.xmin = min(df0[df0.columns[0]]) - if scale_range.xmax is None: - scale_range.xmax = max(df0[df0.columns[0]]) + if scaling.xmin is None: + scaling.xmin = min(df0[df0.columns[0]]) + if scaling.xmax is None: + scaling.xmax = max(df0[df0.columns[0]]) - sdata.scale_range = scale_range + sdata.scaling = scaling - xmin = scale_range.xmin - xmax = scale_range.xmax + xmin = scaling.xmin + xmax = scaling.xmax df0 = df0[(df0[df0.columns[0]] >= xmin) & (df0[df0.columns[0]] <= xmax)] area_df0, _ = area_under_peak(sdata, peak=[xmin,xmax], df_index=df_index, min_as_baseline=True) normalized_dataframes = [] - for df_i, df in enumerate(sdata.dataframe): + for df_i, df in enumerate(sdata.dfs): area_df, _ = area_under_peak(sdata, peak=[xmin,xmax], df_index=df_i, min_as_baseline=True) - scaling = area_df0 / area_df - df[df.columns[1]] = df[df.columns[1]] * scaling + scaling_factor = area_df0 / area_df + df[df.columns[1]] = df[df.columns[1]] * scaling_factor normalized_dataframes.append(df) - sdata.dataframe = normalized_dataframes + sdata.dfs = normalized_dataframes return sdata diff --git a/aton/spectra/plot.py b/aton/spectra/plot.py index ac62cd1..349c51a 100644 --- a/aton/spectra/plot.py +++ b/aton/spectra/plot.py @@ -1,23 +1,20 @@ ''' # Description -This module manages the plotting of data. - -# Index -- `spectra()` +This module loads the `plot()` function, used to plot `aton.spectra.SpectraData` data. --- ''' +import matplotlib.pyplot as plt from .classes import * from . import normalize -from . import alias -import matplotlib.pyplot as plt +import aton.alias as alias -def spectra(spectrum:Spectra): +def plot(spectrum:Spectra): ''' - Plot the given spectra, with optional `maatpy.classes.Plotting` and `maatpy.classes.ScaleRange` attributes. + Plot the given spectra, with optional `maatpy.classes.Plotting` and `maatpy.classes.Scaling` attributes. ''' strings_to_delete_from_name = ['.csv', '.dat', '.txt', '_INS', '_ATR', '_FTIR', '_temp', '_RAMAN', '_Raman', '/data/', 'data/', '/csv/', 'csv/', '/INS/', 'INS/', '/FTIR/', 'FTIR/', '/ATR/', 'ATR/', '_smooth', '_smoothed', '_subtracted', '_cellsubtracted'] @@ -48,20 +45,20 @@ def spectra(spectrum:Spectra): title = sdata.plotting.title low_xlim = sdata.plotting.xlim[0] top_xlim = sdata.plotting.xlim[1] - xlabel = sdata.plotting.xlabel if sdata.plotting.xlabel is not None else sdata.dataframe[0].columns[0] - ylabel = sdata.plotting.ylabel if sdata.plotting.ylabel is not None else sdata.dataframe[0].columns[1] + xlabel = sdata.plotting.xlabel if sdata.plotting.xlabel is not None else sdata.dfs[0].columns[0] + ylabel = sdata.plotting.ylabel if sdata.plotting.ylabel is not None else sdata.dfs[0].columns[1] else: title = sdata.comment - number_of_plots = len(sdata.dataframe) + number_of_plots = len(sdata.dfs) height = top_ylim - low_ylim if hasattr(sdata, 'plotting') and sdata.plotting.offset is True: - for i, df in enumerate(sdata.dataframe): + for i, df in enumerate(sdata.dfs): reverse_i = (number_of_plots - 1) - i df[df.columns[1]] = df[df.columns[1]] + (reverse_i * height) elif hasattr(sdata, 'plotting') and (isinstance(sdata.plotting.offset, float) or isinstance(sdata.plotting.offset, int)): offset = sdata.plotting.offset - for i, df in enumerate(sdata.dataframe): + for i, df in enumerate(sdata.dfs): reverse_i = (number_of_plots - 1) - i df[df.columns[1]] = df[df.columns[1]] + (reverse_i * offset) _, calculated_top_ylim = _get_ylimits(sdata) @@ -69,21 +66,21 @@ def spectra(spectrum:Spectra): if hasattr(sdata, 'plotting') and hasattr(sdata.plotting, 'legend'): if sdata.plotting.legend == False: - for df in sdata.dataframe: + for df in sdata.dfs: df.plot(x=df.columns[0], y=df.columns[1], ax=ax) elif sdata.plotting.legend != None: - if len(sdata.plotting.legend) == len(sdata.dataframe): - for i, df in enumerate(sdata.dataframe): + if len(sdata.plotting.legend) == len(sdata.dfs): + for i, df in enumerate(sdata.dfs): if sdata.plotting.legend[i] == False: continue # Skip plots with False in the legend clean_name = sdata.plotting.legend[i] df.plot(x=df.columns[0], y=df.columns[1], label=clean_name, ax=ax) elif len(sdata.plotting.legend) == 1: clean_name = sdata.plotting.legend[0] - for i, df in enumerate(sdata.dataframe): + for i, df in enumerate(sdata.dfs): df.plot(x=df.columns[0], y=df.columns[1], label=clean_name, ax=ax) - elif sdata.plotting.legend == None and len(sdata.filename) == len(sdata.dataframe): - for df, name in zip(sdata.dataframe, sdata.filename): + elif sdata.plotting.legend == None and len(sdata.files) == len(sdata.dfs): + for df, name in zip(sdata.dfs, sdata.files): clean_name = name for string in strings_to_delete_from_name: clean_name = clean_name.replace(string, '') @@ -125,9 +122,9 @@ def spectra(spectrum:Spectra): for vline in sdata.plotting.vline: ax.axvline(x=vline, color='gray', alpha=0.5, linestyle='--') - if sdata.save_as: + if hasattr(sdata, 'plotting') and sdata.plotting.save_as: root = os.getcwd() - save_name = os.path.join(root, sdata.save_as) + save_name = os.path.join(root, sdata.plotting.save_as) plt.savefig(save_name) plt.show() @@ -135,7 +132,7 @@ def spectra(spectrum:Spectra): def _get_ylimits(spectrum:Spectra) -> tuple[float, float]: all_y_values = [] - for df in spectrum.dataframe: + for df in spectrum.dfs: df_trim = df if hasattr(spectrum, 'plotting') and spectrum.plotting.xlim[0] is not None: df_trim = df_trim[(df_trim[df_trim.columns[0]] >= spectrum.plotting.xlim[0])] @@ -146,15 +143,15 @@ def _get_ylimits(spectrum:Spectra) -> tuple[float, float]: calculated_top_ylim = max(all_y_values) ymax_on_range = None - if hasattr(spectrum, 'scale_range') and spectrum.scale_range is not None: - df_index = spectrum.scale_range.index if spectrum.scale_range.index else 0 - df0 = spectrum.dataframe[df_index] - if spectrum.scale_range.xmin: - df0 = df0[(df0[df0.columns[0]] >= spectrum.scale_range.xmin)] - if spectrum.scale_range.xmax: - df0 = df0[(df0[df0.columns[0]] <= spectrum.scale_range.xmax)] + if hasattr(spectrum, 'scaling') and spectrum.scaling is not None: + df_index = spectrum.scaling.index if spectrum.scaling.index else 0 + df0 = spectrum.dfs[df_index] + if spectrum.scaling.xmin: + df0 = df0[(df0[df0.columns[0]] >= spectrum.scaling.xmin)] + if spectrum.scaling.xmax: + df0 = df0[(df0[df0.columns[0]] <= spectrum.scaling.xmax)] ymax_on_range = df0[df0.columns[1]].max() - if spectrum.scale_range.zoom and ymax_on_range is not None: + if spectrum.scaling.zoom and ymax_on_range is not None: calculated_top_ylim = ymax_on_range return calculated_low_ylim, calculated_top_ylim diff --git a/aton/spectra/sample.py b/aton/spectra/sample.py deleted file mode 100644 index 4a32db5..0000000 --- a/aton/spectra/sample.py +++ /dev/null @@ -1,76 +0,0 @@ -''' -# Description -This module contains premade examples of material compositions and other experimental values. -The `maatpy.classes.Material.grams` is yet to be provided, -before setting the material with `maatpy.classes.Material.set()`. - ---- -''' - - -from .constants import * -from .classes import * - - -############################# -## MATERIAL COMPOSITIONS ## -############################# -MAPI = Material( - atoms={'Pb': 1, 'I': 3, 'C': 1, 'N': 1, 'H': 6}, - name='MAPbI3' - ) -'''CH$_3$NH$_3$PbI$_3$''' -#MAPI.set() - -MAPI_CDND = Material( - atoms={'Pb': 1, 'I': 3, 'C': 1, 'N': 1, 'H2': 6}, - name='CD3ND3PbI3' - ) -'''CD$_3$ND$_3$PbI$_3$''' -#MAPI_CDND.set() - -MAPI_ND = Material( - atoms={'Pb': 1, 'I': 3, 'C': 1, 'N': 1, 'H': 3, 'H2': 3}, - name='CH3ND3PbI3' - ) -'''CH$_3$ND$_3$PbI$_3$''' -#MAPI_ND.set() - -MAPI_CD = Material( - atoms={'Pb': 1, 'I': 3, 'C': 1, 'N': 1, 'H': 3, 'H2': 3}, - name='CD3NH3PbI3' - ) -'''CD$_3$NH$_3$PbI$_3$''' -#MAPI_CD.set() - -CH3NH3I = Material( - atoms={'C' : 1, 'N': 1, 'H': 6}, - name='CH3NH3' - ) -'''CH$_3$NH$_3$I''' -#CH3NH3I.set() - -CH3ND3I = Material( - atoms={'C' : 1, 'N': 1, 'H': 3, 'H2': 3}, - name='CH3ND3' - ) -'''CH$_3$ND$_3$I''' -#CH3ND3I.set() - - -########################### -## EXPERIMENTAL VALUES ## -########################### -MAPI_peaks = { - 'baseline' : None, - 'baseline_error' : None, - 'h6d0' : [36.0, 39.0], - 'h5d1' : [33.0, 35.0], - 'h4d2' : [30.7, 33.0], - 'h3d3' : [28.8, 30.7], -} -''' -Experimental values of the partially-deuterated amine peaks for the disrotatory mode of MAPbI3's methylammonium. -Measured at TOSCA, ISIS RAL, UK, May 2024. -''' - diff --git a/aton/spectra/samples.py b/aton/spectra/samples.py new file mode 100644 index 0000000..89be990 --- /dev/null +++ b/aton/spectra/samples.py @@ -0,0 +1,70 @@ +''' +# Description +This module contains premade examples of material compositions. +The `aton.spectra.Material.grams` is yet to be provided, +before setting the material with `aton.spectra.Material.set()`. + +--- +''' + + +from .classes import Material + + +MAPbI3 = Material( + elements={'Pb': 1, 'I': 3, 'C': 1, 'N': 1, 'H': 6}, + name='MAPbI3' + ) +'''CH$_3$NH$_3$PbI$_3$''' + + +CD3ND3PbI3 = Material( + elements={'Pb': 1, 'I': 3, 'C': 1, 'N': 1, 'H2': 6}, + name='CD3ND3PbI3', + peaks = { + 'baseline' : None, + 'baseline_error' : None, + 'h6d0' : [36.0, 39.0], + 'h5d1' : [33.0, 35.0], + 'h4d2' : [30.7, 33.0], + 'h3d3' : [28.8, 30.7], + } +) +'''CD$_3$ND$_3$PbI$_3$. +With experimental values of the partially-deuterated amine peaks +for the disrotatory mode of MAPbI3's methylammonium. +Measured at TOSCA, ISIS RAL, UK, May 2024. +''' + + +CH3ND3PbI3 = Material( + elements={'Pb': 1, 'I': 3, 'C': 1, 'N': 1, 'H': 3, 'H2': 3}, + name='CH3ND3PbI3' +) +'''CH$_3$ND$_3$PbI$_3$''' +#MAPI_ND.set() + + +CD3NH3PbI3 = Material( + elements={'Pb': 1, 'I': 3, 'C': 1, 'N': 1, 'H': 3, 'H2': 3}, + name='CD3NH3PbI3' +) +'''CD$_3$NH$_3$PbI$_3$''' +#MAPI_CD.set() + + +CH3NH3I = Material( + elements={'C' : 1, 'N': 1, 'H': 6}, + name='CH3NH3' +) +'''CH$_3$NH$_3$I''' +#CH3NH3I.set() + + +CH3ND3I = Material( + elements={'C' : 1, 'N': 1, 'H': 3, 'H2': 3}, + name='CH3ND3' +) +'''CH$_3$ND$_3$I''' +#CH3ND3I.set() + diff --git a/docs/aton.html b/docs/aton.html index 773ac05..78a81c9 100644 --- a/docs/aton.html +++ b/docs/aton.html @@ -90,13 +90,14 @@

Submodules

  • call
  • elements
  • file
  • +
  • interface
  • +
  • spectra
  • text
  • units
  • -
  • interface
  • - + built with pdoc aton

    Welcome to the Ab-iniTiO and Neutron research toolbox, or Aton. -Just like its ancient Egyptian deity counterpart, this is an all-in-one Python package with powerful and comprehensive tools for cutting-edge materials research.

    +Inspired by its ancient Egyptian deity counterpart, this all-in-one Python package provides powerful and comprehensive tools for cutting-edge materials research, focused on (but not limited to) neutron science.

    -

    Aton allows you to easily create, edit and analyse all kinds of text files, with a special focus on ab-initio calculations. -In particular, it contains interfaces for Quantum ESPRESSO, Phonopy and CASTEP.

    +

    Aton provides a range of spectral analysis tools, from spectra normalisation to deuteration estimation using the DINS impulse approximation.
    +A set of physico-chemical constants and definitions is also included.

    -

    This is combined with a range of spectral analysis tools, focused on (but not limited to) neutron science. -A set of physico-chemical constants and definitions is also included.

    +

    Aton also allows you to easily create, edit and analyse all kinds of text files, with a special focus on ab-initio calculations. +In particular, it contains interfaces for Quantum ESPRESSO, Phonopy and CASTEP.


    @@ -162,8 +163,8 @@

    From source

    Documentation

    -

    Check the full Aton documentation online.
    -An offline version of the documentation is available in docs/aton.html.
    +

    The full Aton documentation is available online.
    +An offline version of the documentation is found at docs/aton.html.
    Code examples are included in the examples/ folder.

    Submodules

    @@ -173,7 +174,7 @@

    Submodules

    diff --git a/docs/aton/_version.html b/docs/aton/_version.html index 8249f6f..f0ca580 100644 --- a/docs/aton/_version.html +++ b/docs/aton/_version.html @@ -76,7 +76,7 @@

    API Documentation

    - + built with pdoc 10 11""" 12 -13__version__ = 'v0.0.1a3' +13__version__ = 'v0.0.1b1' diff --git a/docs/aton/alias.html b/docs/aton/alias.html index c155331..ff44e14 100644 --- a/docs/aton/alias.html +++ b/docs/aton/alias.html @@ -92,7 +92,7 @@

    API Documentation

    - + built with pdocAPI Documentation -
    Aton v0.0.1a3 documentation
    +
    Aton v0.0.1b1 documentation
    built with pdocAPI Documentation -
    Aton v0.0.1a3 documentation
    +
    Aton v0.0.1b1 documentation
    built with pdocAPI Documentation -
    Aton v0.0.1a3 documentation
    +
    Aton v0.0.1b1 documentation
    built with pdocAPI Documentation -
    Aton v0.0.1a3 documentation
    +
    Aton v0.0.1b1 documentation
    built with pdoc +

    Contents

    +
    +

    Submodules

    - + built with pdocSubmodules

    aton.interface

    - - - - +

    Description

    + +

    This module contains interfaces for ab-initio and related calculation sofware.

    + +

    Index

    + + +
    + + + + + +
     1"""
    + 2# Description
    + 3
    + 4This module contains interfaces for *ab-initio* and related calculation sofware.
    + 5
    + 6# Index
    + 7
    + 8- `aton.interface.qe`
    + 9- `aton.interface.phonopy`
    +10- `aton.interface.castep`
    +11
    +12"""
    +13
    +14from . import qe
    +15from . import phonopy
    +16from . import castep
    +
    + + + + + + + + +
    +
    +

    +aton.spectra

    + +

    Description

    + +

    This module contains spectral analysis tools.

    + +

    Index

    + + +
    + + + + + +
     1"""
    + 2# Description
    + 3
    + 4This module contains spectral analysis tools.
    + 5
    + 6# Index
    + 7
    + 8- `aton.spectra.classes`
    + 9- `aton.spectra.fit`
    +10- `aton.spectra.normalize`
    +11- `aton.spectra.deuterium`
    +12- `aton.spectra.samples`
    +13- `aton.spectra.plot`
    +14
    +15"""
    +16
    +17from .classes import Spectra, Plotting, Scaling, Material
    +18from . import fit
    +19from . import normalize
    +20from . import deuterium
    +21from . import samples
    +22from .plot import plot
    +
    + + +
    +
    + + \ No newline at end of file diff --git a/docs/aton/spectra/classes.html b/docs/aton/spectra/classes.html new file mode 100644 index 0000000..622611f --- /dev/null +++ b/docs/aton/spectra/classes.html @@ -0,0 +1,2764 @@ + + + + + + + aton.spectra.classes API documentation + + + + + + + + + + + + + +
    +
    +

    +aton.spectra.classes

    + +

    Description

    + +

    This module contains common classes used to load and manipulate spectral data. +Any class can be instantiated directly from the aton.spectra module; +for example, to create a new Spectra class for your data, +you just need to call aton.spectra.Spectra(options) as described below:

    + +
    +
    import aton
    +ins = aton.spectra.Spectra(
    +    # Options here
    +    )
    +
    +
    + +

    Index

    + +
      +
    • Spectra. Used to load and process spectral data.
    • +
    • Plotting. Stores plotting options. Used inside Spectra.plotting.
    • +
    • Scaling. Handles data normalization inside the specified range of values. Used inside Spectra.scaling.
    • +
    • Material. Used to store and calculate material parameters, such as molar masses and cross sections.
    • +
    + +
    +
    + + + + + +
      1"""
    +  2# Description
    +  3
    +  4This module contains common classes used to load and manipulate spectral data.
    +  5Any class can be instantiated directly from the `aton.spectra` module;
    +  6for example, to create a new `Spectra` class for your data,
    +  7you just need to call `aton.spectra.Spectra(options)` as described below:
    +  8```python
    +  9import aton
    + 10ins = aton.spectra.Spectra(
    + 11    # Options here
    + 12    )
    + 13```
    + 14
    + 15# Index
    + 16- `Spectra`. Used to load and process spectral data.
    + 17- `Plotting`. Stores plotting options. Used inside `Spectra.plotting`.
    + 18- `Scaling`. Handles data normalization inside the specified range of values. Used inside `Spectra.scaling`.
    + 19- `Material`. Used to store and calculate material parameters, such as molar masses and cross sections.
    + 20
    + 21---
    + 22"""
    + 23
    + 24
    + 25import numpy as np
    + 26import pandas as pd
    + 27from copy import deepcopy
    + 28import os
    + 29import aton.alias as alias
    + 30from aton.units import *
    + 31import aton.atoms
    + 32import aton.elements
    + 33
    + 34
    + 35class Plotting:
    + 36    '''
    + 37    Stores plotting options.
    + 38    Read by `aton.spectra.plot`.
    + 39    '''
    + 40    def __init__(
    + 41            self,
    + 42            title:str=None,
    + 43            xlim=None,
    + 44            ylim=None,
    + 45            margins=None,
    + 46            offset=True,
    + 47            normalize:bool=False,
    + 48            vline:list=None,
    + 49            vline_error:list=None,
    + 50            figsize:tuple=None,
    + 51            log_xscale:bool=False,
    + 52            show_yticks:bool=False,
    + 53            xlabel:str=None,
    + 54            ylabel:str=None,
    + 55            legend=None,
    + 56            legend_title:str=None,
    + 57            legend_size='medium',
    + 58            legend_loc='best',
    + 59            save_as:str=None,
    + 60        ):
    + 61        '''Default values can be overwritten when initializing the Plotting object.'''
    + 62        self.title = title
    + 63        '''Title of the plot. Set it to an empty string to remove the title.'''
    + 64        self.xlim = self._set_limits(xlim)
    + 65        '''List with the x-limits of the plot, as in `[xlim_low, xlim_top]`.'''
    + 66        self.ylim = self._set_limits(ylim)
    + 67        '''List with the y-limits of the plot, as in `[ylim_low, ylim_top]`.'''
    + 68        self.margins = self._set_limits(margins)
    + 69        '''List with additional margins at the bottom and top of the plot, as in `[low_margin, top_margin]`.'''
    + 70        self.offset = offset
    + 71        '''
    + 72        If `True`, the plots will be separated automatically.
    + 73        It can be set to a float, to equally offset the plots by a given value.
    + 74        '''
    + 75        self.normalize = normalize
    + 76        '''
    + 77        Normalize or not the plotted spectra.
    + 78        `True` or `'y'` or `'Y'` to normalize the heights, `'area'` or `'a'` or `'A'` to normalize the areas.
    + 79        '''
    + 80        if vline is not None and not isinstance(vline, list):
    + 81            vline = [vline]
    + 82        self.vline = vline
    + 83        '''Vertical line/s to plot. Can be an int or float with the x-position, or a list with several ones.'''
    + 84        if vline_error is not None and not isinstance(vline_error, list):
    + 85            vline_error = [vline_error]
    + 86        self.vline_error = vline_error
    + 87        '''
    + 88        If not `None`, it will plot a shaded area of the specified width around the vertical lines specified at `vline`.
    + 89        It can be an array of the same length as `vline`, or a single value to be applied to all.
    + 90        '''
    + 91        self.figsize = figsize
    + 92        '''Tuple with the figure size, as in matplotlib.'''
    + 93        self.log_xscale = log_xscale
    + 94        '''If true, plot the x-axis in logarithmic scale.'''
    + 95        self.show_yticks = show_yticks
    + 96        '''Show or not the yticks on the plot.'''
    + 97        self.xlabel = xlabel
    + 98        '''
    + 99        Custom label of the x-axis. If `None`, the default label will be used.
    +100        Set to `''` to remove the label of the horizontal axis.
    +101        '''
    +102        self.ylabel = ylabel
    +103        '''
    +104        Label of the y-axis. If `None`, the default label will be used.
    +105        Set to `''` to remove the label of the vertical axis.
    +106        '''
    +107        if not isinstance(legend, list) and legend is not None and legend != False:
    +108            legend = [legend]
    +109        self.legend = legend
    +110        '''
    +111        If `None`, the files will be used as legend.
    +112        Can be a bool to show or hide the plot legend.
    +113        It can also be an array containing the strings to display;
    +114        in that case, elements set to `False` will not be displayed.
    +115        '''
    +116        self.legend_title = legend_title
    +117        '''Title of the legend. Defaults to `None`.'''
    +118        self.legend_size = legend_size
    +119        '''Size of the legend, as in matplotlib. Defaults to `'medium'`.'''
    +120        self.legend_loc = legend_loc
    +121        '''Location of the legend, as in matplotlib. Defaults to `'best'`.'''
    +122        self.save_as = save_as
    +123        '''Filename to save the plot. None by default.'''
    +124
    +125    def _set_limits(self, limits) -> list:
    +126        '''Set the x and y limits of the plot.'''
    +127        if limits is None:
    +128            return [None, None]
    +129        if isinstance(limits, tuple):
    +130            limits = list(limits)
    +131        if isinstance(limits, list):
    +132            if len(limits) == 0:
    +133                return [None, None]
    +134            if len(limits) == 1:
    +135                return [None, limits[0]]
    +136            if len(limits) == 2:
    +137                return limits
    +138            else:
    +139                return limits[:2]
    +140        if isinstance(limits, int) or isinstance(limits, float):
    +141            return [None, limits]
    +142        else:
    +143            raise ValueError(f"Unknown plotting limits: Must be specified as a list of two elements, as [low_limit, high_limit]. Got: {limits}")
    +144
    +145
    +146class Scaling:
    +147    '''
    +148    The Scaling object is used to handle the normalization
    +149    of the data inside the specified x-range,
    +150    to the same heigth as in the specified `index` dataset
    +151    (the first one by default).
    +152
    +153    Custom heights can be normalized with `ymin` and `ymax`,
    +154    overriding the x-values.
    +155    For example, you may want to normalize two spectra datasets
    +156    with respect to the height of a given peak that overlaps with another.
    +157    Those peaks may have ymin values of 2 and 3, and ymax values
    +158    of 50 and 60 respectively. In that case:
    +159    ```python
    +160    spectra.scaling = Scaling(index=0, ymin=[2, 3], ymax=[50, 60])
    +161    ```
    +162
    +163    To normalize when plotting with `aton.spectra.plot(Spectra)`,
    +164    remember to set `Plotting.normalize=True`.
    +165
    +166    When normalizing the plot, all datasets are fitted inside the
    +167    plotting window, scaling over the entire data range into view.
    +168    To override this behaviour and expand over the given range
    +169    to fill the plot window, you can set `Scaling.zoom=True`.
    +170    This zoom setting can also be enabled without normalizing the plot,
    +171    resulting in a zoom over the given range so that the `index` dataset
    +172    fits the full plotting window, scaling the rest of the set accordingly.
    +173    '''
    +174    def __init__(
    +175            self,
    +176            index:int=0,
    +177            xmin:float=None,
    +178            xmax:float=None,
    +179            ymin:list=None,
    +180            ymax:list=None,
    +181            zoom:bool=False,
    +182        ):
    +183        '''All values can be set when initializing the Scaling object.'''
    +184        self.index: int = index
    +185        '''Index of the dataframe to use as reference.'''
    +186        self.xmin: float = xmin
    +187        '''Minimum x-value to start normalizing the plots.'''
    +188        self.xmax: float = xmax
    +189        '''Maximum x-value to normalize the plots.'''
    +190        self.ymin: list = ymin
    +191        '''List with minimum y-values to normalize the plots.'''
    +192        self.ymax: list = ymax
    +193        '''List with minimum y-values to normalize the plots.
    +194        If `Plotting.normalize=True`, the plots are normalized according to the y-values provided.
    +195        '''
    +196        self.zoom: bool = zoom
    +197        '''
    +198        Used when plotting with `maatpy.plot.spectra()`.
    +199        If true, the data inside the range is scaled up to fit the entire plotting window.
    +200        '''
    +201
    +202    def set_x(self, xmin:float=None, xmax:float=None):
    +203        '''Override with an horizontal range.'''
    +204        self.xmin = xmin
    +205        self.xmax = xmax
    +206        self.ymin = None
    +207        self.ymax = None
    +208        return self
    +209
    +210    def set_y(self, ymin:list=None, ymax:list=None):
    +211        '''Override with a vertical range.'''
    +212        self.xmin = None
    +213        self.xmax = None
    +214        self.ymin = ymin
    +215        self.ymax = ymax
    +216        return self
    +217
    +218
    +219class Spectra:
    +220    """Spectra object. Used to load and process spectral data.
    +221
    +222    Most functions present in the `atom.spectra` module receive this object as input.
    +223
    +224    **Use example:** to load two INS spectra CSV files from MANTID with cm$^{-1}$ as input units,
    +225    and plot them in meV units, normalizing their heights over the range from 20 to 50 meV:
    +226    ```python
    +227    import maatpy as mt
    +228    ins = mt.Spectra(
    +229        type='INS',
    +230        files=['example_1.csv', 'example_2.csv'],
    +231        units_in='cm-1',
    +232        units='meV',
    +233        plotting=mt.Plotting(
    +234            title='Calculated INS',
    +235            normalize=True,
    +236            ),
    +237        scaling=mt.Scaling(
    +238            xmin=20,
    +239            xmax=50,
    +240            ),
    +241        )
    +242    mt.plot.spectra(ins)
    +243    ```
    +244
    +245    Check more use examples in the `/examples/` folder.
    +246
    +247    Below is a list of the available parameters for the Spectra object, along with their descriptions.
    +248    """
    +249    def __init__(
    +250            self,
    +251            type:str=None,
    +252            comment:str=None,
    +253            files=None,
    +254            dfs=None,
    +255            units=None,
    +256            units_in=None,
    +257            plotting:Plotting=Plotting(),
    +258            scaling:Scaling=Scaling(),
    +259        ):
    +260        '''All values can be set when initializing the Spectra object.'''
    +261        self.type = None
    +262        '''Type of the spectra: `'INS'`, `'ATR'`, or `'RAMAN'`.'''
    +263        self.comment = comment
    +264        '''Custom comment. If `Plotting.title` is None,  it will be the title of the plot.'''
    +265        self.files = None
    +266        '''
    +267        List containing the files with the spectral data.
    +268        Loaded automatically with Pandas at initialization.
    +269        In order for Pandas to read the files properly, note that the column lines must start by `#`.
    +270        Any additional line that is not data must be removed or commented with `#`.
    +271        CSV files must be formatted with the first column as the energy or energy transfer,
    +272        and the second column with the intensity or absorbance, depending on the case. An additional third `'Error'` column can be used.
    +273        '''
    +274        self.dfs = None
    +275        '''
    +276        List containing the pandas dataframes with the spectral data.
    +277        Loaded automatically from the files at initialization.
    +278        '''
    +279        self.units = None
    +280        '''Target units of the spectral data. Can be `'meV'` or `'cm-1'`, written as any of the variants listed in `aton.alias.units[unit]`.'''
    +281        self.units_in = None
    +282        '''
    +283        Input units of the spectral data, used in the input CSV files. Can be `'meV'` or `'cm-1'`, written as any of the variants listed in `aton.alias.units[unit]`.
    +284        If the input CSV files have different units, it can also be set as a list of the same length of the number of input files, eg. `['meV', 'cm-1', 'cm-1']`.
    +285        '''
    +286        self.plotting = plotting
    +287        '''`Plotting` object, used to set the plotting options.'''
    +288        self.scaling = scaling
    +289        '''`Scaling` object, used to set the normalization parameters.'''
    +290
    +291        self = self._set_type(type)
    +292        self = self._set_dataframes(files, dfs)
    +293        self = self.set_units(units, units_in)
    +294
    +295    def _set_type(self, type):
    +296        '''Set and normalize the type of the spectra: `INS`, `ATR`, or `RAMAN`.'''
    +297        if type in alias.experiments['INS']:
    +298            self.type = 'INS'
    +299        elif type in alias.experiments['ATR']:
    +300            self.type = 'ATR'
    +301        elif type in alias.experiments['RAMAN']:
    +302            self.type = 'RAMAN'
    +303        else:
    +304            self.type = type
    +305        return self
    +306
    +307    def _set_dataframes(self, files, dfs):
    +308        '''Set the dfs list of dataframes, from the given files or dfs.'''
    +309        if isinstance(files, list):
    +310            self.files = files
    +311        elif isinstance(files, str):
    +312            self.files = [files]
    +313        else:
    +314            self.files = []
    +315
    +316        if isinstance(dfs, pd.DataFrame):
    +317            self.dfs = [dfs]
    +318        elif isinstance(dfs, list) and isinstance(dfs[0], pd.DataFrame):
    +319            self.dfs = dfs
    +320        else:
    +321            self.dfs = [self._read_dataframe(filename) for filename in self.files]
    +322        return self
    +323
    +324    def _read_dataframe(self, filename):
    +325        '''Read a dataframe from a file.'''
    +326        root = os.getcwd()
    +327        file_path = os.path.join(root, filename)
    +328        df = pd.read_csv(file_path, comment='#')
    +329        df = df.sort_values(by=df.columns[0]) # Sort the data by energy
    +330
    +331        print(f'\nNew dataframe from {filename}')
    +332        print(df.head(),'\n')
    +333        return df
    +334
    +335    def set_units(
    +336            self,
    +337            units,
    +338            units_in=None,
    +339            default_unit='cm-1',
    +340            ):
    +341        '''
    +342        Method to change between spectral units. ALWAYS use this method to do that.
    +343
    +344        For example, to change from cm-1 to meV:
    +345        ```python
    +346        # Spectra.set_units(desired_units, units_input)
    +347        Spectra.set_units('meV', 'cm-1')
    +348        ```
    +349        '''
    +350        mev = 'meV'
    +351        cm = 'cm-1'
    +352        unit_format={
    +353                mev: alias.units['meV'],
    +354                cm: alias.units['cm-1'] + alias.units['cm'],
    +355            }
    +356        if self.units is not None:
    +357            units_in = deepcopy(self.units)
    +358            self.units = units
    +359        elif units is not None:
    +360            units_in = units_in
    +361            self.units = deepcopy(units)
    +362        elif units is None and units_in is None:
    +363            units_in = None
    +364            self.units = default_unit
    +365        elif units is None and units_in is not None:
    +366            units_in = None
    +367            self.units = deepcopy(units_in)
    +368        if isinstance(units_in, list):
    +369            for i, unit_in in enumerate(units_in):
    +370                for key, value in unit_format.items():
    +371                    if unit_in in value:
    +372                        units_in[i] = key
    +373                        break
    +374            if len(units_in) == 1:
    +375                units_in = units_in * len(self.files)
    +376            elif len(units_in) != len(self.files):
    +377                raise ValueError("units_in must be a list of the same length as files.")
    +378        if isinstance(units_in, str):
    +379            for key, value in unit_format.items():
    +380                if units_in in value:
    +381                    units_in = key
    +382                    break
    +383            units_in = [units_in] * len(self.files)
    +384        if isinstance(self.units, list):
    +385            for i, unit in enumerate(self.units):
    +386                for key, value in unit_format.items():
    +387                    if unit in value:
    +388                        self.units[i] = key
    +389                        break
    +390            if len(self.units) == 1:
    +391                self.units = self.units * len(self.files)
    +392            elif len(self.units) != len(self.files):
    +393                raise ValueError("units_in must be a list of the same length as files.")
    +394        if isinstance(self.units, str):
    +395            for key, value in unit_format.items():
    +396                if self.units in value:
    +397                    self.units = key
    +398                    break
    +399            self.units = [self.units] * len(self.files)
    +400        if units_in is None:
    +401            return self
    +402        # Otherwise, convert the dfs
    +403        if len(self.units) != len(units_in):
    +404            raise ValueError("Units len mismatching.")
    +405        for i, unit in enumerate(self.units):
    +406            if unit == units_in[i]:
    +407                continue
    +408            if unit == mev and units_in[i] == cm:
    +409                self.dfs[i][self.dfs[i].columns[0]] = self.dfs[i][self.dfs[i].columns[0]] * cm_to_meV
    +410            elif unit == cm and units_in[i] == mev:
    +411                self.dfs[i][self.dfs[i].columns[0]] = self.dfs[i][self.dfs[i].columns[0]] * meV_to_cm
    +412            else:
    +413                raise ValueError(f"Unit conversion error between '{unit}' and '{units_in[i]}'")
    +414        # Rename dataframe columns
    +415        E_units = None
    +416        for i, df in enumerate(self.dfs):
    +417            if self.units[i] == mev:
    +418                E_units = 'meV'
    +419            elif self.units[i] == cm:
    +420                E_units = 'cm-1'
    +421            else:
    +422                E_units = self.units[i]
    +423            if self.type == 'INS':
    +424                if self.dfs[i].shape[1] == 3:
    +425                    self.dfs[i].columns = [f'Energy transfer / {E_units}', 'S(Q,E)', 'Error']
    +426                else:
    +427                    self.dfs[i].columns = [f'Energy transfer / {E_units}', 'S(Q,E)']
    +428            elif self.type == 'ATR':
    +429                self.dfs[i].columns = [f'Wavenumber / {E_units}', 'Absorbance']
    +430            elif self.type == 'RAMAN':
    +431                self.dfs[i].columns = [f'Raman shift / {E_units}', 'Counts']
    +432        return self
    +433
    +434
    +435class Material:
    +436    '''Material class.
    +437    Used to calculate molar masses and cross sections,
    +438    and to pass data to different analysis functions
    +439    such as `aton.spectra.deuterium.impulse_approx().`
    +440    '''
    +441    def __init__(
    +442            self,
    +443            elements:dict,
    +444            name:str=None,
    +445            grams:float=None,
    +446            grams_error:float=None,
    +447            mols:float=None,
    +448            mols_error:float=None,
    +449            molar_mass:float=None,
    +450            cross_section:float=None,
    +451            peaks:dict=None,
    +452        ):
    +453        '''
    +454        All values can be set when initializing the Material object.
    +455        However, it is recommended to only set the elements and the grams,
    +456        and optionally the name, and calculate the rest with `Material.set()`.
    +457        '''
    +458        self.elements = elements
    +459        '''
    +460        Dict of atoms in the material, as in `{'H': 6, 'C':1, 'N':1}`.
    +461        Isotopes can be expressed as 'H2', 'He4', etc. with the atom symbol + isotope mass number.
    +462        '''
    +463        self.name = name
    +464        '''String with the name of the material.'''
    +465        self.grams = grams
    +466        '''Mass, in grams.'''
    +467        self.grams_error = grams_error
    +468        '''Error of the measured mass in grams.
    +469        Set automatically with `Material.set()`.
    +470        '''
    +471        self.mols = mols
    +472        '''Number of moles.
    +473        Set automatically with `Material.set()`.
    +474        '''
    +475        self.mols_error = mols_error
    +476        '''Error of the number of moles.
    +477        Set automatically with `Material.set()`.
    +478        '''
    +479        self.molar_mass = molar_mass
    +480        '''Molar mass of the material, in mol/g.
    +481        Calculated automatically with `Material.set()`.
    +482        '''
    +483        self.cross_section = cross_section
    +484        '''Neutron total bound scattering cross section, in barns.
    +485        Calculated automatically with `Material.set()`.
    +486        '''
    +487        self.peaks = peaks
    +488        '''Dict with interesting peaks that you might want to store for later use.'''
    +489
    +490    def _set_grams_error(self):
    +491        '''Set the error in grams, based on the number of decimal places.'''
    +492        if self.grams is None:
    +493            return
    +494        decimal_accuracy = len(str(self.grams).split('.')[1])
    +495        # Calculate the error in grams
    +496        self.grams_error = 10**(-decimal_accuracy)
    +497
    +498    def _set_mass(self):
    +499        '''Set the molar mass of the material.
    +500        If `Material.grams` is provided, the number of moles will be
    +501        calculated and overwritten. Isotopes can be used as 'element + A',
    +502        eg. `'He4'`. This gets splitted with `aton.elements.split_isotope`.
    +503        '''
    +504        material_grams_per_mol = 0.0
    +505        for key in self.elements:
    +506            try:
    +507                material_grams_per_mol += self.elements[key] * aton.atoms[key].mass
    +508            except KeyError: # Split the atomic flag as H2, etc
    +509                element, isotope = aton.elements.split_isotope(key)
    +510                material_grams_per_mol += self.elements[key] * aton.atoms[element].isotope[isotope].mass
    +511        self.molar_mass = material_grams_per_mol
    +512        if self.grams is not None:
    +513            self._set_grams_error()
    +514            self.mols = self.grams / material_grams_per_mol
    +515            self.mols_error = self.mols * np.sqrt((self.grams_error / self.grams)**2)
    +516    
    +517    def _set_cross_section(self):
    +518        '''
    +519        Set the cross section of the material, based on the self.elements dict.
    +520        If an isotope is used, eg. `'He4'`, it splits the name with `aton.elements.split_isotope`.
    +521        '''
    +522        total_cross_section = 0.0
    +523        for key in self.elements:
    +524            try:
    +525                total_cross_section += self.elements[key] * aton.atoms[key].cross_section
    +526            except KeyError: # Split the atomic flag as H2, etc
    +527                element, isotope_index = aton.elements.split_isotope(key)
    +528                total_cross_section += self.elements[key] * aton.atoms[element].isotope[isotope_index].cross_section
    +529        self.cross_section = total_cross_section
    +530
    +531    def set(self):
    +532        '''Set the molar mass, cross section and errors of the material.'''
    +533        self._set_mass()
    +534        self._set_cross_section()
    +535
    +536    def print(self):
    +537        '''Print a summary with the material information.'''
    +538        print('\nMATERIAL')
    +539        if self.name is not None:
    +540            print(f'Name: {self.name}')
    +541        if self.grams is not None and self.grams_error is not None:
    +542            print(f'Grams: {self.grams} +- {self.grams_error} g')
    +543        elif self.grams is not None:
    +544            print(f'Grams: {self.grams} g')
    +545        if self.mols is not None and self.mols_error is not None:
    +546            print(f'Moles: {self.mols} +- {self.mols_error} mol')
    +547        elif self.mols is not None:
    +548            print(f'Moles: {self.mols} mol')
    +549        if self.molar_mass is not None:
    +550            print(f'Molar mass: {self.molar_mass} g/mol')
    +551        if self.cross_section is not None:
    +552            print(f'Cross section: {self.cross_section} barns')
    +553        if self.elements is not None:
    +554            print(f'Elements: {self.elements}')
    +555        print('')
    +
    + + +
    +
    + +
    + + class + Plotting: + + + +
    + +
     36class Plotting:
    + 37    '''
    + 38    Stores plotting options.
    + 39    Read by `aton.spectra.plot`.
    + 40    '''
    + 41    def __init__(
    + 42            self,
    + 43            title:str=None,
    + 44            xlim=None,
    + 45            ylim=None,
    + 46            margins=None,
    + 47            offset=True,
    + 48            normalize:bool=False,
    + 49            vline:list=None,
    + 50            vline_error:list=None,
    + 51            figsize:tuple=None,
    + 52            log_xscale:bool=False,
    + 53            show_yticks:bool=False,
    + 54            xlabel:str=None,
    + 55            ylabel:str=None,
    + 56            legend=None,
    + 57            legend_title:str=None,
    + 58            legend_size='medium',
    + 59            legend_loc='best',
    + 60            save_as:str=None,
    + 61        ):
    + 62        '''Default values can be overwritten when initializing the Plotting object.'''
    + 63        self.title = title
    + 64        '''Title of the plot. Set it to an empty string to remove the title.'''
    + 65        self.xlim = self._set_limits(xlim)
    + 66        '''List with the x-limits of the plot, as in `[xlim_low, xlim_top]`.'''
    + 67        self.ylim = self._set_limits(ylim)
    + 68        '''List with the y-limits of the plot, as in `[ylim_low, ylim_top]`.'''
    + 69        self.margins = self._set_limits(margins)
    + 70        '''List with additional margins at the bottom and top of the plot, as in `[low_margin, top_margin]`.'''
    + 71        self.offset = offset
    + 72        '''
    + 73        If `True`, the plots will be separated automatically.
    + 74        It can be set to a float, to equally offset the plots by a given value.
    + 75        '''
    + 76        self.normalize = normalize
    + 77        '''
    + 78        Normalize or not the plotted spectra.
    + 79        `True` or `'y'` or `'Y'` to normalize the heights, `'area'` or `'a'` or `'A'` to normalize the areas.
    + 80        '''
    + 81        if vline is not None and not isinstance(vline, list):
    + 82            vline = [vline]
    + 83        self.vline = vline
    + 84        '''Vertical line/s to plot. Can be an int or float with the x-position, or a list with several ones.'''
    + 85        if vline_error is not None and not isinstance(vline_error, list):
    + 86            vline_error = [vline_error]
    + 87        self.vline_error = vline_error
    + 88        '''
    + 89        If not `None`, it will plot a shaded area of the specified width around the vertical lines specified at `vline`.
    + 90        It can be an array of the same length as `vline`, or a single value to be applied to all.
    + 91        '''
    + 92        self.figsize = figsize
    + 93        '''Tuple with the figure size, as in matplotlib.'''
    + 94        self.log_xscale = log_xscale
    + 95        '''If true, plot the x-axis in logarithmic scale.'''
    + 96        self.show_yticks = show_yticks
    + 97        '''Show or not the yticks on the plot.'''
    + 98        self.xlabel = xlabel
    + 99        '''
    +100        Custom label of the x-axis. If `None`, the default label will be used.
    +101        Set to `''` to remove the label of the horizontal axis.
    +102        '''
    +103        self.ylabel = ylabel
    +104        '''
    +105        Label of the y-axis. If `None`, the default label will be used.
    +106        Set to `''` to remove the label of the vertical axis.
    +107        '''
    +108        if not isinstance(legend, list) and legend is not None and legend != False:
    +109            legend = [legend]
    +110        self.legend = legend
    +111        '''
    +112        If `None`, the files will be used as legend.
    +113        Can be a bool to show or hide the plot legend.
    +114        It can also be an array containing the strings to display;
    +115        in that case, elements set to `False` will not be displayed.
    +116        '''
    +117        self.legend_title = legend_title
    +118        '''Title of the legend. Defaults to `None`.'''
    +119        self.legend_size = legend_size
    +120        '''Size of the legend, as in matplotlib. Defaults to `'medium'`.'''
    +121        self.legend_loc = legend_loc
    +122        '''Location of the legend, as in matplotlib. Defaults to `'best'`.'''
    +123        self.save_as = save_as
    +124        '''Filename to save the plot. None by default.'''
    +125
    +126    def _set_limits(self, limits) -> list:
    +127        '''Set the x and y limits of the plot.'''
    +128        if limits is None:
    +129            return [None, None]
    +130        if isinstance(limits, tuple):
    +131            limits = list(limits)
    +132        if isinstance(limits, list):
    +133            if len(limits) == 0:
    +134                return [None, None]
    +135            if len(limits) == 1:
    +136                return [None, limits[0]]
    +137            if len(limits) == 2:
    +138                return limits
    +139            else:
    +140                return limits[:2]
    +141        if isinstance(limits, int) or isinstance(limits, float):
    +142            return [None, limits]
    +143        else:
    +144            raise ValueError(f"Unknown plotting limits: Must be specified as a list of two elements, as [low_limit, high_limit]. Got: {limits}")
    +
    + + +

    Stores plotting options. +Read by aton.spectra.plot.

    +
    + + +
    + +
    + + Plotting( title: str = None, xlim=None, ylim=None, margins=None, offset=True, normalize: bool = False, vline: list = None, vline_error: list = None, figsize: tuple = None, log_xscale: bool = False, show_yticks: bool = False, xlabel: str = None, ylabel: str = None, legend=None, legend_title: str = None, legend_size='medium', legend_loc='best', save_as: str = None) + + + +
    + +
     41    def __init__(
    + 42            self,
    + 43            title:str=None,
    + 44            xlim=None,
    + 45            ylim=None,
    + 46            margins=None,
    + 47            offset=True,
    + 48            normalize:bool=False,
    + 49            vline:list=None,
    + 50            vline_error:list=None,
    + 51            figsize:tuple=None,
    + 52            log_xscale:bool=False,
    + 53            show_yticks:bool=False,
    + 54            xlabel:str=None,
    + 55            ylabel:str=None,
    + 56            legend=None,
    + 57            legend_title:str=None,
    + 58            legend_size='medium',
    + 59            legend_loc='best',
    + 60            save_as:str=None,
    + 61        ):
    + 62        '''Default values can be overwritten when initializing the Plotting object.'''
    + 63        self.title = title
    + 64        '''Title of the plot. Set it to an empty string to remove the title.'''
    + 65        self.xlim = self._set_limits(xlim)
    + 66        '''List with the x-limits of the plot, as in `[xlim_low, xlim_top]`.'''
    + 67        self.ylim = self._set_limits(ylim)
    + 68        '''List with the y-limits of the plot, as in `[ylim_low, ylim_top]`.'''
    + 69        self.margins = self._set_limits(margins)
    + 70        '''List with additional margins at the bottom and top of the plot, as in `[low_margin, top_margin]`.'''
    + 71        self.offset = offset
    + 72        '''
    + 73        If `True`, the plots will be separated automatically.
    + 74        It can be set to a float, to equally offset the plots by a given value.
    + 75        '''
    + 76        self.normalize = normalize
    + 77        '''
    + 78        Normalize or not the plotted spectra.
    + 79        `True` or `'y'` or `'Y'` to normalize the heights, `'area'` or `'a'` or `'A'` to normalize the areas.
    + 80        '''
    + 81        if vline is not None and not isinstance(vline, list):
    + 82            vline = [vline]
    + 83        self.vline = vline
    + 84        '''Vertical line/s to plot. Can be an int or float with the x-position, or a list with several ones.'''
    + 85        if vline_error is not None and not isinstance(vline_error, list):
    + 86            vline_error = [vline_error]
    + 87        self.vline_error = vline_error
    + 88        '''
    + 89        If not `None`, it will plot a shaded area of the specified width around the vertical lines specified at `vline`.
    + 90        It can be an array of the same length as `vline`, or a single value to be applied to all.
    + 91        '''
    + 92        self.figsize = figsize
    + 93        '''Tuple with the figure size, as in matplotlib.'''
    + 94        self.log_xscale = log_xscale
    + 95        '''If true, plot the x-axis in logarithmic scale.'''
    + 96        self.show_yticks = show_yticks
    + 97        '''Show or not the yticks on the plot.'''
    + 98        self.xlabel = xlabel
    + 99        '''
    +100        Custom label of the x-axis. If `None`, the default label will be used.
    +101        Set to `''` to remove the label of the horizontal axis.
    +102        '''
    +103        self.ylabel = ylabel
    +104        '''
    +105        Label of the y-axis. If `None`, the default label will be used.
    +106        Set to `''` to remove the label of the vertical axis.
    +107        '''
    +108        if not isinstance(legend, list) and legend is not None and legend != False:
    +109            legend = [legend]
    +110        self.legend = legend
    +111        '''
    +112        If `None`, the files will be used as legend.
    +113        Can be a bool to show or hide the plot legend.
    +114        It can also be an array containing the strings to display;
    +115        in that case, elements set to `False` will not be displayed.
    +116        '''
    +117        self.legend_title = legend_title
    +118        '''Title of the legend. Defaults to `None`.'''
    +119        self.legend_size = legend_size
    +120        '''Size of the legend, as in matplotlib. Defaults to `'medium'`.'''
    +121        self.legend_loc = legend_loc
    +122        '''Location of the legend, as in matplotlib. Defaults to `'best'`.'''
    +123        self.save_as = save_as
    +124        '''Filename to save the plot. None by default.'''
    +
    + + +

    Default values can be overwritten when initializing the Plotting object.

    +
    + + +
    +
    +
    + title + + +
    + + +

    Title of the plot. Set it to an empty string to remove the title.

    +
    + + +
    +
    +
    + xlim + + +
    + + +

    List with the x-limits of the plot, as in [xlim_low, xlim_top].

    +
    + + +
    +
    +
    + ylim + + +
    + + +

    List with the y-limits of the plot, as in [ylim_low, ylim_top].

    +
    + + +
    +
    +
    + margins + + +
    + + +

    List with additional margins at the bottom and top of the plot, as in [low_margin, top_margin].

    +
    + + +
    +
    +
    + offset + + +
    + + +

    If True, the plots will be separated automatically. +It can be set to a float, to equally offset the plots by a given value.

    +
    + + +
    +
    +
    + normalize + + +
    + + +

    Normalize or not the plotted spectra. +True or 'y' or 'Y' to normalize the heights, 'area' or 'a' or 'A' to normalize the areas.

    +
    + + +
    +
    +
    + vline + + +
    + + +

    Vertical line/s to plot. Can be an int or float with the x-position, or a list with several ones.

    +
    + + +
    +
    +
    + vline_error + + +
    + + +

    If not None, it will plot a shaded area of the specified width around the vertical lines specified at vline. +It can be an array of the same length as vline, or a single value to be applied to all.

    +
    + + +
    +
    +
    + figsize + + +
    + + +

    Tuple with the figure size, as in matplotlib.

    +
    + + +
    +
    +
    + log_xscale + + +
    + + +

    If true, plot the x-axis in logarithmic scale.

    +
    + + +
    +
    +
    + show_yticks + + +
    + + +

    Show or not the yticks on the plot.

    +
    + + +
    +
    +
    + xlabel + + +
    + + +

    Custom label of the x-axis. If None, the default label will be used. +Set to '' to remove the label of the horizontal axis.

    +
    + + +
    +
    +
    + ylabel + + +
    + + +

    Label of the y-axis. If None, the default label will be used. +Set to '' to remove the label of the vertical axis.

    +
    + + +
    +
    +
    + legend + + +
    + + +

    If None, the files will be used as legend. +Can be a bool to show or hide the plot legend. +It can also be an array containing the strings to display; +in that case, elements set to False will not be displayed.

    +
    + + +
    +
    +
    + legend_title + + +
    + + +

    Title of the legend. Defaults to None.

    +
    + + +
    +
    +
    + legend_size + + +
    + + +

    Size of the legend, as in matplotlib. Defaults to 'medium'.

    +
    + + +
    +
    +
    + legend_loc + + +
    + + +

    Location of the legend, as in matplotlib. Defaults to 'best'.

    +
    + + +
    +
    +
    + save_as + + +
    + + +

    Filename to save the plot. None by default.

    +
    + + +
    +
    +
    + +
    + + class + Scaling: + + + +
    + +
    147class Scaling:
    +148    '''
    +149    The Scaling object is used to handle the normalization
    +150    of the data inside the specified x-range,
    +151    to the same heigth as in the specified `index` dataset
    +152    (the first one by default).
    +153
    +154    Custom heights can be normalized with `ymin` and `ymax`,
    +155    overriding the x-values.
    +156    For example, you may want to normalize two spectra datasets
    +157    with respect to the height of a given peak that overlaps with another.
    +158    Those peaks may have ymin values of 2 and 3, and ymax values
    +159    of 50 and 60 respectively. In that case:
    +160    ```python
    +161    spectra.scaling = Scaling(index=0, ymin=[2, 3], ymax=[50, 60])
    +162    ```
    +163
    +164    To normalize when plotting with `aton.spectra.plot(Spectra)`,
    +165    remember to set `Plotting.normalize=True`.
    +166
    +167    When normalizing the plot, all datasets are fitted inside the
    +168    plotting window, scaling over the entire data range into view.
    +169    To override this behaviour and expand over the given range
    +170    to fill the plot window, you can set `Scaling.zoom=True`.
    +171    This zoom setting can also be enabled without normalizing the plot,
    +172    resulting in a zoom over the given range so that the `index` dataset
    +173    fits the full plotting window, scaling the rest of the set accordingly.
    +174    '''
    +175    def __init__(
    +176            self,
    +177            index:int=0,
    +178            xmin:float=None,
    +179            xmax:float=None,
    +180            ymin:list=None,
    +181            ymax:list=None,
    +182            zoom:bool=False,
    +183        ):
    +184        '''All values can be set when initializing the Scaling object.'''
    +185        self.index: int = index
    +186        '''Index of the dataframe to use as reference.'''
    +187        self.xmin: float = xmin
    +188        '''Minimum x-value to start normalizing the plots.'''
    +189        self.xmax: float = xmax
    +190        '''Maximum x-value to normalize the plots.'''
    +191        self.ymin: list = ymin
    +192        '''List with minimum y-values to normalize the plots.'''
    +193        self.ymax: list = ymax
    +194        '''List with minimum y-values to normalize the plots.
    +195        If `Plotting.normalize=True`, the plots are normalized according to the y-values provided.
    +196        '''
    +197        self.zoom: bool = zoom
    +198        '''
    +199        Used when plotting with `maatpy.plot.spectra()`.
    +200        If true, the data inside the range is scaled up to fit the entire plotting window.
    +201        '''
    +202
    +203    def set_x(self, xmin:float=None, xmax:float=None):
    +204        '''Override with an horizontal range.'''
    +205        self.xmin = xmin
    +206        self.xmax = xmax
    +207        self.ymin = None
    +208        self.ymax = None
    +209        return self
    +210
    +211    def set_y(self, ymin:list=None, ymax:list=None):
    +212        '''Override with a vertical range.'''
    +213        self.xmin = None
    +214        self.xmax = None
    +215        self.ymin = ymin
    +216        self.ymax = ymax
    +217        return self
    +
    + + +

    The Scaling object is used to handle the normalization +of the data inside the specified x-range, +to the same heigth as in the specified index dataset +(the first one by default).

    + +

    Custom heights can be normalized with ymin and ymax, +overriding the x-values. +For example, you may want to normalize two spectra datasets +with respect to the height of a given peak that overlaps with another. +Those peaks may have ymin values of 2 and 3, and ymax values +of 50 and 60 respectively. In that case:

    + +
    +
    spectra.scaling = Scaling(index=0, ymin=[2, 3], ymax=[50, 60])
    +
    +
    + +

    To normalize when plotting with aton.spectra.plot(Spectra), +remember to set Plotting.normalize=True.

    + +

    When normalizing the plot, all datasets are fitted inside the +plotting window, scaling over the entire data range into view. +To override this behaviour and expand over the given range +to fill the plot window, you can set Scaling.zoom=True. +This zoom setting can also be enabled without normalizing the plot, +resulting in a zoom over the given range so that the index dataset +fits the full plotting window, scaling the rest of the set accordingly.

    +
    + + +
    + +
    + + Scaling( index: int = 0, xmin: float = None, xmax: float = None, ymin: list = None, ymax: list = None, zoom: bool = False) + + + +
    + +
    175    def __init__(
    +176            self,
    +177            index:int=0,
    +178            xmin:float=None,
    +179            xmax:float=None,
    +180            ymin:list=None,
    +181            ymax:list=None,
    +182            zoom:bool=False,
    +183        ):
    +184        '''All values can be set when initializing the Scaling object.'''
    +185        self.index: int = index
    +186        '''Index of the dataframe to use as reference.'''
    +187        self.xmin: float = xmin
    +188        '''Minimum x-value to start normalizing the plots.'''
    +189        self.xmax: float = xmax
    +190        '''Maximum x-value to normalize the plots.'''
    +191        self.ymin: list = ymin
    +192        '''List with minimum y-values to normalize the plots.'''
    +193        self.ymax: list = ymax
    +194        '''List with minimum y-values to normalize the plots.
    +195        If `Plotting.normalize=True`, the plots are normalized according to the y-values provided.
    +196        '''
    +197        self.zoom: bool = zoom
    +198        '''
    +199        Used when plotting with `maatpy.plot.spectra()`.
    +200        If true, the data inside the range is scaled up to fit the entire plotting window.
    +201        '''
    +
    + + +

    All values can be set when initializing the Scaling object.

    +
    + + +
    +
    +
    + index: int + + +
    + + +

    Index of the dataframe to use as reference.

    +
    + + +
    +
    +
    + xmin: float + + +
    + + +

    Minimum x-value to start normalizing the plots.

    +
    + + +
    +
    +
    + xmax: float + + +
    + + +

    Maximum x-value to normalize the plots.

    +
    + + +
    +
    +
    + ymin: list + + +
    + + +

    List with minimum y-values to normalize the plots.

    +
    + + +
    +
    +
    + ymax: list + + +
    + + +

    List with minimum y-values to normalize the plots. +If Plotting.normalize=True, the plots are normalized according to the y-values provided.

    +
    + + +
    +
    +
    + zoom: bool + + +
    + + +

    Used when plotting with maatpy.plot.spectra(). +If true, the data inside the range is scaled up to fit the entire plotting window.

    +
    + + +
    +
    + +
    + + def + set_x(self, xmin: float = None, xmax: float = None): + + + +
    + +
    203    def set_x(self, xmin:float=None, xmax:float=None):
    +204        '''Override with an horizontal range.'''
    +205        self.xmin = xmin
    +206        self.xmax = xmax
    +207        self.ymin = None
    +208        self.ymax = None
    +209        return self
    +
    + + +

    Override with an horizontal range.

    +
    + + +
    +
    + +
    + + def + set_y(self, ymin: list = None, ymax: list = None): + + + +
    + +
    211    def set_y(self, ymin:list=None, ymax:list=None):
    +212        '''Override with a vertical range.'''
    +213        self.xmin = None
    +214        self.xmax = None
    +215        self.ymin = ymin
    +216        self.ymax = ymax
    +217        return self
    +
    + + +

    Override with a vertical range.

    +
    + + +
    +
    +
    + +
    + + class + Spectra: + + + +
    + +
    220class Spectra:
    +221    """Spectra object. Used to load and process spectral data.
    +222
    +223    Most functions present in the `atom.spectra` module receive this object as input.
    +224
    +225    **Use example:** to load two INS spectra CSV files from MANTID with cm$^{-1}$ as input units,
    +226    and plot them in meV units, normalizing their heights over the range from 20 to 50 meV:
    +227    ```python
    +228    import maatpy as mt
    +229    ins = mt.Spectra(
    +230        type='INS',
    +231        files=['example_1.csv', 'example_2.csv'],
    +232        units_in='cm-1',
    +233        units='meV',
    +234        plotting=mt.Plotting(
    +235            title='Calculated INS',
    +236            normalize=True,
    +237            ),
    +238        scaling=mt.Scaling(
    +239            xmin=20,
    +240            xmax=50,
    +241            ),
    +242        )
    +243    mt.plot.spectra(ins)
    +244    ```
    +245
    +246    Check more use examples in the `/examples/` folder.
    +247
    +248    Below is a list of the available parameters for the Spectra object, along with their descriptions.
    +249    """
    +250    def __init__(
    +251            self,
    +252            type:str=None,
    +253            comment:str=None,
    +254            files=None,
    +255            dfs=None,
    +256            units=None,
    +257            units_in=None,
    +258            plotting:Plotting=Plotting(),
    +259            scaling:Scaling=Scaling(),
    +260        ):
    +261        '''All values can be set when initializing the Spectra object.'''
    +262        self.type = None
    +263        '''Type of the spectra: `'INS'`, `'ATR'`, or `'RAMAN'`.'''
    +264        self.comment = comment
    +265        '''Custom comment. If `Plotting.title` is None,  it will be the title of the plot.'''
    +266        self.files = None
    +267        '''
    +268        List containing the files with the spectral data.
    +269        Loaded automatically with Pandas at initialization.
    +270        In order for Pandas to read the files properly, note that the column lines must start by `#`.
    +271        Any additional line that is not data must be removed or commented with `#`.
    +272        CSV files must be formatted with the first column as the energy or energy transfer,
    +273        and the second column with the intensity or absorbance, depending on the case. An additional third `'Error'` column can be used.
    +274        '''
    +275        self.dfs = None
    +276        '''
    +277        List containing the pandas dataframes with the spectral data.
    +278        Loaded automatically from the files at initialization.
    +279        '''
    +280        self.units = None
    +281        '''Target units of the spectral data. Can be `'meV'` or `'cm-1'`, written as any of the variants listed in `aton.alias.units[unit]`.'''
    +282        self.units_in = None
    +283        '''
    +284        Input units of the spectral data, used in the input CSV files. Can be `'meV'` or `'cm-1'`, written as any of the variants listed in `aton.alias.units[unit]`.
    +285        If the input CSV files have different units, it can also be set as a list of the same length of the number of input files, eg. `['meV', 'cm-1', 'cm-1']`.
    +286        '''
    +287        self.plotting = plotting
    +288        '''`Plotting` object, used to set the plotting options.'''
    +289        self.scaling = scaling
    +290        '''`Scaling` object, used to set the normalization parameters.'''
    +291
    +292        self = self._set_type(type)
    +293        self = self._set_dataframes(files, dfs)
    +294        self = self.set_units(units, units_in)
    +295
    +296    def _set_type(self, type):
    +297        '''Set and normalize the type of the spectra: `INS`, `ATR`, or `RAMAN`.'''
    +298        if type in alias.experiments['INS']:
    +299            self.type = 'INS'
    +300        elif type in alias.experiments['ATR']:
    +301            self.type = 'ATR'
    +302        elif type in alias.experiments['RAMAN']:
    +303            self.type = 'RAMAN'
    +304        else:
    +305            self.type = type
    +306        return self
    +307
    +308    def _set_dataframes(self, files, dfs):
    +309        '''Set the dfs list of dataframes, from the given files or dfs.'''
    +310        if isinstance(files, list):
    +311            self.files = files
    +312        elif isinstance(files, str):
    +313            self.files = [files]
    +314        else:
    +315            self.files = []
    +316
    +317        if isinstance(dfs, pd.DataFrame):
    +318            self.dfs = [dfs]
    +319        elif isinstance(dfs, list) and isinstance(dfs[0], pd.DataFrame):
    +320            self.dfs = dfs
    +321        else:
    +322            self.dfs = [self._read_dataframe(filename) for filename in self.files]
    +323        return self
    +324
    +325    def _read_dataframe(self, filename):
    +326        '''Read a dataframe from a file.'''
    +327        root = os.getcwd()
    +328        file_path = os.path.join(root, filename)
    +329        df = pd.read_csv(file_path, comment='#')
    +330        df = df.sort_values(by=df.columns[0]) # Sort the data by energy
    +331
    +332        print(f'\nNew dataframe from {filename}')
    +333        print(df.head(),'\n')
    +334        return df
    +335
    +336    def set_units(
    +337            self,
    +338            units,
    +339            units_in=None,
    +340            default_unit='cm-1',
    +341            ):
    +342        '''
    +343        Method to change between spectral units. ALWAYS use this method to do that.
    +344
    +345        For example, to change from cm-1 to meV:
    +346        ```python
    +347        # Spectra.set_units(desired_units, units_input)
    +348        Spectra.set_units('meV', 'cm-1')
    +349        ```
    +350        '''
    +351        mev = 'meV'
    +352        cm = 'cm-1'
    +353        unit_format={
    +354                mev: alias.units['meV'],
    +355                cm: alias.units['cm-1'] + alias.units['cm'],
    +356            }
    +357        if self.units is not None:
    +358            units_in = deepcopy(self.units)
    +359            self.units = units
    +360        elif units is not None:
    +361            units_in = units_in
    +362            self.units = deepcopy(units)
    +363        elif units is None and units_in is None:
    +364            units_in = None
    +365            self.units = default_unit
    +366        elif units is None and units_in is not None:
    +367            units_in = None
    +368            self.units = deepcopy(units_in)
    +369        if isinstance(units_in, list):
    +370            for i, unit_in in enumerate(units_in):
    +371                for key, value in unit_format.items():
    +372                    if unit_in in value:
    +373                        units_in[i] = key
    +374                        break
    +375            if len(units_in) == 1:
    +376                units_in = units_in * len(self.files)
    +377            elif len(units_in) != len(self.files):
    +378                raise ValueError("units_in must be a list of the same length as files.")
    +379        if isinstance(units_in, str):
    +380            for key, value in unit_format.items():
    +381                if units_in in value:
    +382                    units_in = key
    +383                    break
    +384            units_in = [units_in] * len(self.files)
    +385        if isinstance(self.units, list):
    +386            for i, unit in enumerate(self.units):
    +387                for key, value in unit_format.items():
    +388                    if unit in value:
    +389                        self.units[i] = key
    +390                        break
    +391            if len(self.units) == 1:
    +392                self.units = self.units * len(self.files)
    +393            elif len(self.units) != len(self.files):
    +394                raise ValueError("units_in must be a list of the same length as files.")
    +395        if isinstance(self.units, str):
    +396            for key, value in unit_format.items():
    +397                if self.units in value:
    +398                    self.units = key
    +399                    break
    +400            self.units = [self.units] * len(self.files)
    +401        if units_in is None:
    +402            return self
    +403        # Otherwise, convert the dfs
    +404        if len(self.units) != len(units_in):
    +405            raise ValueError("Units len mismatching.")
    +406        for i, unit in enumerate(self.units):
    +407            if unit == units_in[i]:
    +408                continue
    +409            if unit == mev and units_in[i] == cm:
    +410                self.dfs[i][self.dfs[i].columns[0]] = self.dfs[i][self.dfs[i].columns[0]] * cm_to_meV
    +411            elif unit == cm and units_in[i] == mev:
    +412                self.dfs[i][self.dfs[i].columns[0]] = self.dfs[i][self.dfs[i].columns[0]] * meV_to_cm
    +413            else:
    +414                raise ValueError(f"Unit conversion error between '{unit}' and '{units_in[i]}'")
    +415        # Rename dataframe columns
    +416        E_units = None
    +417        for i, df in enumerate(self.dfs):
    +418            if self.units[i] == mev:
    +419                E_units = 'meV'
    +420            elif self.units[i] == cm:
    +421                E_units = 'cm-1'
    +422            else:
    +423                E_units = self.units[i]
    +424            if self.type == 'INS':
    +425                if self.dfs[i].shape[1] == 3:
    +426                    self.dfs[i].columns = [f'Energy transfer / {E_units}', 'S(Q,E)', 'Error']
    +427                else:
    +428                    self.dfs[i].columns = [f'Energy transfer / {E_units}', 'S(Q,E)']
    +429            elif self.type == 'ATR':
    +430                self.dfs[i].columns = [f'Wavenumber / {E_units}', 'Absorbance']
    +431            elif self.type == 'RAMAN':
    +432                self.dfs[i].columns = [f'Raman shift / {E_units}', 'Counts']
    +433        return self
    +
    + + +

    Spectra object. Used to load and process spectral data.

    + +

    Most functions present in the atom.spectra module receive this object as input.

    + +

    Use example: to load two INS spectra CSV files from MANTID with cm$^{-1}$ as input units, +and plot them in meV units, normalizing their heights over the range from 20 to 50 meV:

    + +
    +
    import maatpy as mt
    +ins = mt.Spectra(
    +    type='INS',
    +    files=['example_1.csv', 'example_2.csv'],
    +    units_in='cm-1',
    +    units='meV',
    +    plotting=mt.Plotting(
    +        title='Calculated INS',
    +        normalize=True,
    +        ),
    +    scaling=mt.Scaling(
    +        xmin=20,
    +        xmax=50,
    +        ),
    +    )
    +mt.plot.spectra(ins)
    +
    +
    + +

    Check more use examples in the /examples/ folder.

    + +

    Below is a list of the available parameters for the Spectra object, along with their descriptions.

    +
    + + +
    + +
    + + Spectra( type: str = None, comment: str = None, files=None, dfs=None, units=None, units_in=None, plotting: Plotting = <Plotting object>, scaling: Scaling = <Scaling object>) + + + +
    + +
    250    def __init__(
    +251            self,
    +252            type:str=None,
    +253            comment:str=None,
    +254            files=None,
    +255            dfs=None,
    +256            units=None,
    +257            units_in=None,
    +258            plotting:Plotting=Plotting(),
    +259            scaling:Scaling=Scaling(),
    +260        ):
    +261        '''All values can be set when initializing the Spectra object.'''
    +262        self.type = None
    +263        '''Type of the spectra: `'INS'`, `'ATR'`, or `'RAMAN'`.'''
    +264        self.comment = comment
    +265        '''Custom comment. If `Plotting.title` is None,  it will be the title of the plot.'''
    +266        self.files = None
    +267        '''
    +268        List containing the files with the spectral data.
    +269        Loaded automatically with Pandas at initialization.
    +270        In order for Pandas to read the files properly, note that the column lines must start by `#`.
    +271        Any additional line that is not data must be removed or commented with `#`.
    +272        CSV files must be formatted with the first column as the energy or energy transfer,
    +273        and the second column with the intensity or absorbance, depending on the case. An additional third `'Error'` column can be used.
    +274        '''
    +275        self.dfs = None
    +276        '''
    +277        List containing the pandas dataframes with the spectral data.
    +278        Loaded automatically from the files at initialization.
    +279        '''
    +280        self.units = None
    +281        '''Target units of the spectral data. Can be `'meV'` or `'cm-1'`, written as any of the variants listed in `aton.alias.units[unit]`.'''
    +282        self.units_in = None
    +283        '''
    +284        Input units of the spectral data, used in the input CSV files. Can be `'meV'` or `'cm-1'`, written as any of the variants listed in `aton.alias.units[unit]`.
    +285        If the input CSV files have different units, it can also be set as a list of the same length of the number of input files, eg. `['meV', 'cm-1', 'cm-1']`.
    +286        '''
    +287        self.plotting = plotting
    +288        '''`Plotting` object, used to set the plotting options.'''
    +289        self.scaling = scaling
    +290        '''`Scaling` object, used to set the normalization parameters.'''
    +291
    +292        self = self._set_type(type)
    +293        self = self._set_dataframes(files, dfs)
    +294        self = self.set_units(units, units_in)
    +
    + + +

    All values can be set when initializing the Spectra object.

    +
    + + +
    +
    +
    + type + + +
    + + +

    Type of the spectra: 'INS', 'ATR', or 'RAMAN'.

    +
    + + +
    +
    +
    + comment + + +
    + + +

    Custom comment. If Plotting.title is None, it will be the title of the plot.

    +
    + + +
    +
    +
    + files + + +
    + + +

    List containing the files with the spectral data. +Loaded automatically with Pandas at initialization. +In order for Pandas to read the files properly, note that the column lines must start by #. +Any additional line that is not data must be removed or commented with #. +CSV files must be formatted with the first column as the energy or energy transfer, +and the second column with the intensity or absorbance, depending on the case. An additional third 'Error' column can be used.

    +
    + + +
    +
    +
    + dfs + + +
    + + +

    List containing the pandas dataframes with the spectral data. +Loaded automatically from the files at initialization.

    +
    + + +
    +
    +
    + units + + +
    + + +

    Target units of the spectral data. Can be 'meV' or 'cm-1', written as any of the variants listed in aton.alias.units[unit].

    +
    + + +
    +
    +
    + units_in + + +
    + + +

    Input units of the spectral data, used in the input CSV files. Can be 'meV' or 'cm-1', written as any of the variants listed in aton.alias.units[unit]. +If the input CSV files have different units, it can also be set as a list of the same length of the number of input files, eg. ['meV', 'cm-1', 'cm-1'].

    +
    + + +
    +
    +
    + plotting + + +
    + + +

    Plotting object, used to set the plotting options.

    +
    + + +
    +
    +
    + scaling + + +
    + + +

    Scaling object, used to set the normalization parameters.

    +
    + + +
    +
    + +
    + + def + set_units(self, units, units_in=None, default_unit='cm-1'): + + + +
    + +
    336    def set_units(
    +337            self,
    +338            units,
    +339            units_in=None,
    +340            default_unit='cm-1',
    +341            ):
    +342        '''
    +343        Method to change between spectral units. ALWAYS use this method to do that.
    +344
    +345        For example, to change from cm-1 to meV:
    +346        ```python
    +347        # Spectra.set_units(desired_units, units_input)
    +348        Spectra.set_units('meV', 'cm-1')
    +349        ```
    +350        '''
    +351        mev = 'meV'
    +352        cm = 'cm-1'
    +353        unit_format={
    +354                mev: alias.units['meV'],
    +355                cm: alias.units['cm-1'] + alias.units['cm'],
    +356            }
    +357        if self.units is not None:
    +358            units_in = deepcopy(self.units)
    +359            self.units = units
    +360        elif units is not None:
    +361            units_in = units_in
    +362            self.units = deepcopy(units)
    +363        elif units is None and units_in is None:
    +364            units_in = None
    +365            self.units = default_unit
    +366        elif units is None and units_in is not None:
    +367            units_in = None
    +368            self.units = deepcopy(units_in)
    +369        if isinstance(units_in, list):
    +370            for i, unit_in in enumerate(units_in):
    +371                for key, value in unit_format.items():
    +372                    if unit_in in value:
    +373                        units_in[i] = key
    +374                        break
    +375            if len(units_in) == 1:
    +376                units_in = units_in * len(self.files)
    +377            elif len(units_in) != len(self.files):
    +378                raise ValueError("units_in must be a list of the same length as files.")
    +379        if isinstance(units_in, str):
    +380            for key, value in unit_format.items():
    +381                if units_in in value:
    +382                    units_in = key
    +383                    break
    +384            units_in = [units_in] * len(self.files)
    +385        if isinstance(self.units, list):
    +386            for i, unit in enumerate(self.units):
    +387                for key, value in unit_format.items():
    +388                    if unit in value:
    +389                        self.units[i] = key
    +390                        break
    +391            if len(self.units) == 1:
    +392                self.units = self.units * len(self.files)
    +393            elif len(self.units) != len(self.files):
    +394                raise ValueError("units_in must be a list of the same length as files.")
    +395        if isinstance(self.units, str):
    +396            for key, value in unit_format.items():
    +397                if self.units in value:
    +398                    self.units = key
    +399                    break
    +400            self.units = [self.units] * len(self.files)
    +401        if units_in is None:
    +402            return self
    +403        # Otherwise, convert the dfs
    +404        if len(self.units) != len(units_in):
    +405            raise ValueError("Units len mismatching.")
    +406        for i, unit in enumerate(self.units):
    +407            if unit == units_in[i]:
    +408                continue
    +409            if unit == mev and units_in[i] == cm:
    +410                self.dfs[i][self.dfs[i].columns[0]] = self.dfs[i][self.dfs[i].columns[0]] * cm_to_meV
    +411            elif unit == cm and units_in[i] == mev:
    +412                self.dfs[i][self.dfs[i].columns[0]] = self.dfs[i][self.dfs[i].columns[0]] * meV_to_cm
    +413            else:
    +414                raise ValueError(f"Unit conversion error between '{unit}' and '{units_in[i]}'")
    +415        # Rename dataframe columns
    +416        E_units = None
    +417        for i, df in enumerate(self.dfs):
    +418            if self.units[i] == mev:
    +419                E_units = 'meV'
    +420            elif self.units[i] == cm:
    +421                E_units = 'cm-1'
    +422            else:
    +423                E_units = self.units[i]
    +424            if self.type == 'INS':
    +425                if self.dfs[i].shape[1] == 3:
    +426                    self.dfs[i].columns = [f'Energy transfer / {E_units}', 'S(Q,E)', 'Error']
    +427                else:
    +428                    self.dfs[i].columns = [f'Energy transfer / {E_units}', 'S(Q,E)']
    +429            elif self.type == 'ATR':
    +430                self.dfs[i].columns = [f'Wavenumber / {E_units}', 'Absorbance']
    +431            elif self.type == 'RAMAN':
    +432                self.dfs[i].columns = [f'Raman shift / {E_units}', 'Counts']
    +433        return self
    +
    + + +

    Method to change between spectral units. ALWAYS use this method to do that.

    + +

    For example, to change from cm-1 to meV:

    + +
    +
    # Spectra.set_units(desired_units, units_input)
    +Spectra.set_units('meV', 'cm-1')
    +
    +
    +
    + + +
    +
    +
    + +
    + + class + Material: + + + +
    + +
    436class Material:
    +437    '''Material class.
    +438    Used to calculate molar masses and cross sections,
    +439    and to pass data to different analysis functions
    +440    such as `aton.spectra.deuterium.impulse_approx().`
    +441    '''
    +442    def __init__(
    +443            self,
    +444            elements:dict,
    +445            name:str=None,
    +446            grams:float=None,
    +447            grams_error:float=None,
    +448            mols:float=None,
    +449            mols_error:float=None,
    +450            molar_mass:float=None,
    +451            cross_section:float=None,
    +452            peaks:dict=None,
    +453        ):
    +454        '''
    +455        All values can be set when initializing the Material object.
    +456        However, it is recommended to only set the elements and the grams,
    +457        and optionally the name, and calculate the rest with `Material.set()`.
    +458        '''
    +459        self.elements = elements
    +460        '''
    +461        Dict of atoms in the material, as in `{'H': 6, 'C':1, 'N':1}`.
    +462        Isotopes can be expressed as 'H2', 'He4', etc. with the atom symbol + isotope mass number.
    +463        '''
    +464        self.name = name
    +465        '''String with the name of the material.'''
    +466        self.grams = grams
    +467        '''Mass, in grams.'''
    +468        self.grams_error = grams_error
    +469        '''Error of the measured mass in grams.
    +470        Set automatically with `Material.set()`.
    +471        '''
    +472        self.mols = mols
    +473        '''Number of moles.
    +474        Set automatically with `Material.set()`.
    +475        '''
    +476        self.mols_error = mols_error
    +477        '''Error of the number of moles.
    +478        Set automatically with `Material.set()`.
    +479        '''
    +480        self.molar_mass = molar_mass
    +481        '''Molar mass of the material, in mol/g.
    +482        Calculated automatically with `Material.set()`.
    +483        '''
    +484        self.cross_section = cross_section
    +485        '''Neutron total bound scattering cross section, in barns.
    +486        Calculated automatically with `Material.set()`.
    +487        '''
    +488        self.peaks = peaks
    +489        '''Dict with interesting peaks that you might want to store for later use.'''
    +490
    +491    def _set_grams_error(self):
    +492        '''Set the error in grams, based on the number of decimal places.'''
    +493        if self.grams is None:
    +494            return
    +495        decimal_accuracy = len(str(self.grams).split('.')[1])
    +496        # Calculate the error in grams
    +497        self.grams_error = 10**(-decimal_accuracy)
    +498
    +499    def _set_mass(self):
    +500        '''Set the molar mass of the material.
    +501        If `Material.grams` is provided, the number of moles will be
    +502        calculated and overwritten. Isotopes can be used as 'element + A',
    +503        eg. `'He4'`. This gets splitted with `aton.elements.split_isotope`.
    +504        '''
    +505        material_grams_per_mol = 0.0
    +506        for key in self.elements:
    +507            try:
    +508                material_grams_per_mol += self.elements[key] * aton.atoms[key].mass
    +509            except KeyError: # Split the atomic flag as H2, etc
    +510                element, isotope = aton.elements.split_isotope(key)
    +511                material_grams_per_mol += self.elements[key] * aton.atoms[element].isotope[isotope].mass
    +512        self.molar_mass = material_grams_per_mol
    +513        if self.grams is not None:
    +514            self._set_grams_error()
    +515            self.mols = self.grams / material_grams_per_mol
    +516            self.mols_error = self.mols * np.sqrt((self.grams_error / self.grams)**2)
    +517    
    +518    def _set_cross_section(self):
    +519        '''
    +520        Set the cross section of the material, based on the self.elements dict.
    +521        If an isotope is used, eg. `'He4'`, it splits the name with `aton.elements.split_isotope`.
    +522        '''
    +523        total_cross_section = 0.0
    +524        for key in self.elements:
    +525            try:
    +526                total_cross_section += self.elements[key] * aton.atoms[key].cross_section
    +527            except KeyError: # Split the atomic flag as H2, etc
    +528                element, isotope_index = aton.elements.split_isotope(key)
    +529                total_cross_section += self.elements[key] * aton.atoms[element].isotope[isotope_index].cross_section
    +530        self.cross_section = total_cross_section
    +531
    +532    def set(self):
    +533        '''Set the molar mass, cross section and errors of the material.'''
    +534        self._set_mass()
    +535        self._set_cross_section()
    +536
    +537    def print(self):
    +538        '''Print a summary with the material information.'''
    +539        print('\nMATERIAL')
    +540        if self.name is not None:
    +541            print(f'Name: {self.name}')
    +542        if self.grams is not None and self.grams_error is not None:
    +543            print(f'Grams: {self.grams} +- {self.grams_error} g')
    +544        elif self.grams is not None:
    +545            print(f'Grams: {self.grams} g')
    +546        if self.mols is not None and self.mols_error is not None:
    +547            print(f'Moles: {self.mols} +- {self.mols_error} mol')
    +548        elif self.mols is not None:
    +549            print(f'Moles: {self.mols} mol')
    +550        if self.molar_mass is not None:
    +551            print(f'Molar mass: {self.molar_mass} g/mol')
    +552        if self.cross_section is not None:
    +553            print(f'Cross section: {self.cross_section} barns')
    +554        if self.elements is not None:
    +555            print(f'Elements: {self.elements}')
    +556        print('')
    +
    + + +

    Material class. +Used to calculate molar masses and cross sections, +and to pass data to different analysis functions +such as aton.spectra.deuterium.impulse_approx().

    +
    + + +
    + +
    + + Material( elements: dict, name: str = None, grams: float = None, grams_error: float = None, mols: float = None, mols_error: float = None, molar_mass: float = None, cross_section: float = None, peaks: dict = None) + + + +
    + +
    442    def __init__(
    +443            self,
    +444            elements:dict,
    +445            name:str=None,
    +446            grams:float=None,
    +447            grams_error:float=None,
    +448            mols:float=None,
    +449            mols_error:float=None,
    +450            molar_mass:float=None,
    +451            cross_section:float=None,
    +452            peaks:dict=None,
    +453        ):
    +454        '''
    +455        All values can be set when initializing the Material object.
    +456        However, it is recommended to only set the elements and the grams,
    +457        and optionally the name, and calculate the rest with `Material.set()`.
    +458        '''
    +459        self.elements = elements
    +460        '''
    +461        Dict of atoms in the material, as in `{'H': 6, 'C':1, 'N':1}`.
    +462        Isotopes can be expressed as 'H2', 'He4', etc. with the atom symbol + isotope mass number.
    +463        '''
    +464        self.name = name
    +465        '''String with the name of the material.'''
    +466        self.grams = grams
    +467        '''Mass, in grams.'''
    +468        self.grams_error = grams_error
    +469        '''Error of the measured mass in grams.
    +470        Set automatically with `Material.set()`.
    +471        '''
    +472        self.mols = mols
    +473        '''Number of moles.
    +474        Set automatically with `Material.set()`.
    +475        '''
    +476        self.mols_error = mols_error
    +477        '''Error of the number of moles.
    +478        Set automatically with `Material.set()`.
    +479        '''
    +480        self.molar_mass = molar_mass
    +481        '''Molar mass of the material, in mol/g.
    +482        Calculated automatically with `Material.set()`.
    +483        '''
    +484        self.cross_section = cross_section
    +485        '''Neutron total bound scattering cross section, in barns.
    +486        Calculated automatically with `Material.set()`.
    +487        '''
    +488        self.peaks = peaks
    +489        '''Dict with interesting peaks that you might want to store for later use.'''
    +
    + + +

    All values can be set when initializing the Material object. +However, it is recommended to only set the elements and the grams, +and optionally the name, and calculate the rest with Material.set().

    +
    + + +
    +
    +
    + elements + + +
    + + +

    Dict of atoms in the material, as in {'H': 6, 'C':1, 'N':1}. +Isotopes can be expressed as 'H2', 'He4', etc. with the atom symbol + isotope mass number.

    +
    + + +
    +
    +
    + name + + +
    + + +

    String with the name of the material.

    +
    + + +
    +
    +
    + grams + + +
    + + +

    Mass, in grams.

    +
    + + +
    +
    +
    + grams_error + + +
    + + +

    Error of the measured mass in grams. +Set automatically with Material.set().

    +
    + + +
    +
    +
    + mols + + +
    + + +

    Number of moles. +Set automatically with Material.set().

    +
    + + +
    +
    +
    + mols_error + + +
    + + +

    Error of the number of moles. +Set automatically with Material.set().

    +
    + + +
    +
    +
    + molar_mass + + +
    + + +

    Molar mass of the material, in mol/g. +Calculated automatically with Material.set().

    +
    + + +
    +
    +
    + cross_section + + +
    + + +

    Neutron total bound scattering cross section, in barns. +Calculated automatically with Material.set().

    +
    + + +
    +
    +
    + peaks + + +
    + + +

    Dict with interesting peaks that you might want to store for later use.

    +
    + + +
    +
    + +
    + + def + set(self): + + + +
    + +
    532    def set(self):
    +533        '''Set the molar mass, cross section and errors of the material.'''
    +534        self._set_mass()
    +535        self._set_cross_section()
    +
    + + +

    Set the molar mass, cross section and errors of the material.

    +
    + + +
    +
    + +
    + + def + print(self): + + + +
    + +
    537    def print(self):
    +538        '''Print a summary with the material information.'''
    +539        print('\nMATERIAL')
    +540        if self.name is not None:
    +541            print(f'Name: {self.name}')
    +542        if self.grams is not None and self.grams_error is not None:
    +543            print(f'Grams: {self.grams} +- {self.grams_error} g')
    +544        elif self.grams is not None:
    +545            print(f'Grams: {self.grams} g')
    +546        if self.mols is not None and self.mols_error is not None:
    +547            print(f'Moles: {self.mols} +- {self.mols_error} mol')
    +548        elif self.mols is not None:
    +549            print(f'Moles: {self.mols} mol')
    +550        if self.molar_mass is not None:
    +551            print(f'Molar mass: {self.molar_mass} g/mol')
    +552        if self.cross_section is not None:
    +553            print(f'Cross section: {self.cross_section} barns')
    +554        if self.elements is not None:
    +555            print(f'Elements: {self.elements}')
    +556        print('')
    +
    + + +

    Print a summary with the material information.

    +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/docs/aton/spectra/deuterium.html b/docs/aton/spectra/deuterium.html new file mode 100644 index 0000000..0840b61 --- /dev/null +++ b/docs/aton/spectra/deuterium.html @@ -0,0 +1,909 @@ + + + + + + + aton.spectra.deuterium API documentation + + + + + + + + + + + + + +
    +
    +

    +aton.spectra.deuterium

    + +

    Description

    + +

    This module contains methods to calculate deuteration levels from different spectra.

    + +

    Index

    + + + +
    +
    + + + + + +
      1"""
    +  2# Description
    +  3This module contains methods to calculate deuteration levels from different spectra.
    +  4
    +  5# Index
    +  6- `impulse_approx()`
    +  7- `peaks_mapi()`
    +  8
    +  9---
    + 10"""
    + 11
    + 12
    + 13from copy import deepcopy
    + 14import aton.alias as alias
    + 15from aton.units import *
    + 16from .classes import Spectra, Material
    + 17from aton.spectra.fit import area_under_peak, ratio_areas, plateau
    + 18
    + 19
    + 20def impulse_approx(
    + 21        ins: Spectra,
    + 22        material_H: Material,
    + 23        material_D: Material,
    + 24        threshold: float=600,
    + 25        H_df_index: int=0,
    + 26        D_df_index: int=1,
    + 27    ) -> tuple:
    + 28    """Calculate the deuteration levels from INS spectra
    + 29    with the *Impulse Approximation*, see
    + 30    https://www.tandfonline.com/doi/full/10.1080/00018732.2017.1317963.
    + 31
    + 32    Protonated and deuterated materials must be specified
    + 33    as `aton.spectra.Material` objects.
    + 34    The threshold controls the start of the plateau (in meV)
    + 35    to start considering Deep Inelastic Neutron Scattering (DINS).
    + 36    The protonated and deuterated dataframe indexes are specified
    + 37    by `H_df_index` and `D_df_index`, respectively.
    + 38
    + 39    In this approximation, the ideal ratio between
    + 40    the cross-sections and the experimental ratio between
    + 41    the pleteaus at high energies should be the same:
    + 42    $$
    + 43    \\frac{\\text{plateau_D}}{\\text{plateau_H}} \\approx \\frac{\\text{cross_section_D}}{\\text{cross_section_H}}
    + 44    $$
    + 45    Taking this into account, the deuteration is estimated as:
    + 46    $$
    + 47    \\text{Deuteration} = \\frac{1-\\text{real_ratio}}{1-\\text{ideal_ratio}}
    + 48    $$
    + 49
    + 50    > [!WARNING]
    + 51    > This approximation is very sensitive to the mass sample, specified by `aton.spectra.Material.grams`.
    + 52    """
    + 53    ins = deepcopy(ins)
    + 54    material_H = deepcopy(material_H)
    + 55    material_D = deepcopy(material_D)
    + 56    material_H_grams = 1.0 if material_H.grams is None else material_H.grams
    + 57    material_H.grams = material_H_grams
    + 58    material_H.set()
    + 59    material_D_grams = 1.0 if material_D.grams is None else material_D.grams
    + 60    material_D.grams = material_D_grams
    + 61    material_D.set()
    + 62
    + 63    material_H.print()
    + 64    material_D.print()
    + 65
    + 66    # Make sure units are in meV
    + 67    units_in = ins.units
    + 68    if units_in not in alias.unit['meV']:
    + 69        ins.set_units('meV', units_in)
    + 70
    + 71    # Divide the y values of the dataframes by the mols of the material.
    + 72    ins.dfs[H_df_index][ins.dfs[H_df_index].columns[1]] = ins.dfs[H_df_index][ins.dfs[H_df_index].columns[1]]
    + 73    ins.dfs[D_df_index][ins.dfs[D_df_index].columns[1]] = ins.dfs[D_df_index][ins.dfs[D_df_index].columns[1]]
    + 74
    + 75    plateau_H, plateau_H_error = plateau(ins, [threshold, None], H_df_index)
    + 76    plateau_D, plateau_D_error = plateau(ins, [threshold, None], D_df_index)
    + 77
    + 78    plateau_H_normalized = plateau_H / material_H.mols
    + 79    plateau_H_normalized_error = plateau_H_normalized * np.sqrt((plateau_H_error / plateau_H)**2 + (material_H.mols_error / material_H.mols)**2)
    + 80    plateau_D_normalized = plateau_D / material_D.mols
    + 81    plateau_D_normalized_error = plateau_D_normalized * np.sqrt((plateau_D_error / plateau_D)**2 + (material_D.mols_error / material_D.mols)**2)
    + 82
    + 83    # ratio if fully protonated = 1.0
    + 84    ratio = plateau_D_normalized / plateau_H_normalized  # ratio_ideal < ratio < 1.0
    + 85    ratio_ideal = material_D.cross_section / material_H.cross_section  # 0.0 < ratio_ideal < 1.0
    + 86
    + 87    ratio_error = abs(ratio * np.sqrt((plateau_H_normalized_error / plateau_H_normalized)**2 + (plateau_D_normalized_error / plateau_D_normalized)**2))
    + 88
    + 89    deuteration = (1 - ratio) / (1 - ratio_ideal)
    + 90    deuteration_error = abs(deuteration * np.sqrt((ratio_error / ratio)**2))
    + 91
    + 92    print(f'Normalized plateau H:      {plateau_H_normalized} +- {plateau_H_normalized_error}')
    + 93    print(f'Normalized plateau D:      {plateau_D_normalized} +- {plateau_D_normalized_error}')
    + 94    print(f'Ratio D/H plateaus:        {ratio} +- {ratio_error}')
    + 95    print(f'Ratio D/H cross sections:  {ratio_ideal}')
    + 96
    + 97    print(f"\nDeuteration: {deuteration:.2f} +- {deuteration_error:.2f}\n")
    + 98    return round(deuteration,2), round(deuteration_error,2)
    + 99
    +100
    +101def peaks_mapi(
    +102        ins:Spectra,
    +103        peaks:dict,
    +104        df_index:int=0,
    +105    ) -> str:
    +106    '''
    +107    Calculate the deuteration of your CH$_3$NH$_3$PbI$_3$ samples
    +108    by integrating the INS disrotatory peaks,
    +109    which appear at around 38 meV for the fully protonated sample.
    +110    Note that `peaks` must be a dictionary with the peak limits
    +111    and the baseline, as in the example below:
    +112    ```python
    +113    peaks = {
    +114        'baseline' : None,
    +115        'baseline_error' : None,
    +116        'h6d0' : [41, 43],
    +117        'h5d1' : [41, 43],
    +118        'h4d2' : [41, 43],
    +119        'h3d3' : [34.7, 37.3],
    +120        'h2d4' : [31.0, 33.0],
    +121        'h1d5' : [28.0, 30.5],
    +122        'h0d6' : [26.5, 28.0],
    +123        }
    +124    ```
    +125    Peak keywords required for selective deuteration (only C or only N):
    +126    `h6d0`, `h5d1`, `h4d2`, `h3d3`.
    +127    Additional peak keywords required for total deuteration:
    +128    `h2d4`, `h1d5`, `h0d6`.
    +129    If some peak is not present in your sample,
    +130    just set the limits to a small baseline plateau.
    +131    '''
    +132
    +133    peak_data = deepcopy(ins)
    +134
    +135    baseline = 0.0
    +136    baseline_error = 0.0
    +137    if 'baseline' in peaks.keys():
    +138        if peaks['baseline'] is not None:
    +139            baseline = peaks['baseline']
    +140    if 'baseline_error' in peaks.keys():
    +141        if peaks['baseline_error'] is not None:
    +142            baseline_error = peaks['baseline_error']
    +143
    +144    run_partial = True
    +145    run_total = True
    +146
    +147    h6d0_limits = None
    +148    h5d1_limits = None
    +149    h4d2_limits = None
    +150    h3d3_limits = None
    +151    h2d4_limits = None
    +152    h1d5_limits = None
    +153    h0d6_limits = None
    +154
    +155    if 'h6d0' in peaks:
    +156        h6d0_limits = peaks['h6d0']
    +157    if 'h5d1' in peaks:
    +158        h5d1_limits = peaks['h5d1']
    +159    if 'h4d2' in peaks:
    +160        h4d2_limits = peaks['h4d2']
    +161    if 'h3d3' in peaks:
    +162        h3d3_limits = peaks['h3d3']
    +163    if 'h2d4' in peaks:
    +164        h2d4_limits = peaks['h2d4']
    +165    if 'h1d5' in peaks:
    +166        h1d5_limits = peaks['h1d5']
    +167    if 'h0d6' in peaks:
    +168        h0d6_limits = peaks['h0d6']
    +169
    +170    if h0d6_limits is None or h1d5_limits is None or h2d4_limits is None or h3d3_limits is None:
    +171        run_total = False
    +172    if h6d0_limits is None or h5d1_limits is None or h4d2_limits is None or h3d3_limits is None:
    +173        run_partial = False
    +174
    +175    if not run_partial:
    +176        raise ValueError('No peaks to integrate. Remember to assign peak limits as a dictionary with the keys: h6d0, h5d1, h4d2, h3d3, h2d4, h1d5, h0d6.')
    +177
    +178    h6d0_area, h6d0_area_error = area_under_peak(peak_data, [h6d0_limits[0], h6d0_limits[1], baseline, baseline_error], df_index, True)
    +179    h5d1_area, h5d1_area_error = area_under_peak(peak_data, [h5d1_limits[0], h5d1_limits[1], baseline, baseline_error], df_index, True)
    +180    h4d2_area, h4d2_area_error = area_under_peak(peak_data, [h4d2_limits[0], h4d2_limits[1], baseline, baseline_error], df_index, True)
    +181    h3d3_area, h3d3_area_error = area_under_peak(peak_data, [h3d3_limits[0], h3d3_limits[1], baseline, baseline_error], df_index, True)
    +182    h6d0_area /= 6
    +183    h5d1_area /= 5
    +184    h4d2_area /= 4
    +185    h3d3_area /= 3
    +186    h6d0_area_error /= 6
    +187    h5d1_area_error /= 5
    +188    h4d2_area_error /= 4
    +189    h3d3_area_error /= 3
    +190    
    +191    if not run_total:
    +192        total_area = h6d0_area + h5d1_area + h4d2_area + h3d3_area
    +193        total_area_error = np.sqrt(h6d0_area_error**2 + h5d1_area_error**2 + h4d2_area_error**2 + h3d3_area_error**2)
    +194
    +195        h6d0_ratio, h6d0_error = ratio_areas(h6d0_area, total_area, h6d0_area_error, total_area_error)
    +196        h5d1_ratio, h5d1_error = ratio_areas(h5d1_area, total_area, h5d1_area_error, total_area_error)
    +197        h4d2_ratio, h4d2_error = ratio_areas(h4d2_area, total_area, h4d2_area_error, total_area_error)
    +198        h3d3_ratio, h3d3_error = ratio_areas(h3d3_area, total_area, h3d3_area_error, total_area_error)
    +199
    +200        deuteration = 0 * h6d0_ratio + (1/3) * h5d1_ratio + (2/3) * h4d2_ratio + 1 * h3d3_ratio
    +201        protonation = 1 * h6d0_ratio + (2/3) * h5d1_ratio + (1/3) * h4d2_ratio + 0 * h3d3_ratio
    +202
    +203        deuteration_error = np.sqrt((1/3 * h5d1_error)**2 + (2/3 * h4d2_error)**2 + (1 * h3d3_error)**2)
    +204        protonation_error = np.sqrt((1 * h6d0_error)**2 + (2/3 * h5d1_error)**2 + (1/3 * h4d2_error)**2)
    +205
    +206    if run_total:
    +207        h2d4_area, h2d4_area_error = area_under_peak(peak_data, [h2d4_limits[0], h2d4_limits[1], baseline, baseline_error], df_index, True)
    +208        h1d5_area, h1d5_area_error = area_under_peak(peak_data, [h1d5_limits[0], h1d5_limits[1], baseline, baseline_error], df_index, True)
    +209        h0d6_area, h0d6_area_error = area_under_peak(peak_data, [h0d6_limits[0], h0d6_limits[1], baseline, baseline_error], df_index, True)
    +210        h2d4_area /= 2
    +211        h1d5_area /= 1
    +212        h0d6_area /= 1
    +213        h2d4_area_error /= 2
    +214        h1d5_area_error /= 1
    +215        h0d6_area_error /= 1
    +216
    +217        total_area_CDND = h6d0_area + h5d1_area + h4d2_area + h3d3_area + h2d4_area + h1d5_area + h0d6_area
    +218        total_area_error_CDND = np.sqrt(h6d0_area_error**2 + h5d1_area_error**2 + h4d2_area_error**2 + h3d3_area_error**2 + h2d4_area_error**2 + h1d5_area_error**2 + h0d6_area_error**2)
    +219
    +220        h6d0_ratio_CDND, h6d0_error_CDND = ratio_areas(h6d0_area, total_area_CDND, h6d0_area_error, total_area_error_CDND)
    +221        h5d1_ratio_CDND, h5d1_error_CDND = ratio_areas(h5d1_area, total_area_CDND, h5d1_area_error, total_area_error_CDND)
    +222        h4d2_ratio_CDND, h4d2_error_CDND = ratio_areas(h4d2_area, total_area_CDND, h4d2_area_error, total_area_error_CDND)
    +223        h3d3_ratio_CDND, h3d3_error_CDND = ratio_areas(h3d3_area, total_area_CDND, h3d3_area_error, total_area_error_CDND)
    +224        h2d4_ratio_CDND, h2d4_error_CDND = ratio_areas(h2d4_area, total_area_CDND, h2d4_area_error, total_area_error_CDND)
    +225        h1d5_ratio_CDND, h1d5_error_CDND = ratio_areas(h1d5_area, total_area_CDND, h1d5_area_error, total_area_error_CDND)
    +226        h0d6_ratio_CDND, h0d6_error_CDND = ratio_areas(h0d6_area, total_area_CDND, h0d6_area_error, total_area_error_CDND)
    +227
    +228        deuteration_CDND = 0 * h6d0_ratio_CDND + (1/6) * h5d1_ratio_CDND + (2/6) * h4d2_ratio_CDND + (3/6) * h3d3_ratio_CDND + (4/6) * h2d4_ratio_CDND + (5/6) * h1d5_ratio_CDND + 1 * h0d6_ratio_CDND
    +229        deuteration_CDND_error = np.sqrt((1/6 * h5d1_error_CDND)**2 + (2/6 * h4d2_error_CDND)**2 + (3/6 * h3d3_error_CDND)**2 + (4/6 * h2d4_error_CDND)**2 + (5/6 * h1d5_error_CDND)**2 + (1 * h0d6_error_CDND)**2)
    +230
    +231        protonation_CDND = 1 * h6d0_ratio_CDND + (5/6) * h5d1_ratio_CDND + (4/6) * h4d2_ratio_CDND + (3/6) * h3d3_ratio_CDND + (2/6) * h2d4_ratio_CDND + (1/6) * h1d5_ratio_CDND + 0 * h0d6_ratio_CDND
    +232        protonation_CDND_error = np.sqrt((1 * h6d0_error_CDND)**2 + (5/6 * h5d1_error_CDND)**2 + (4/6 * h4d2_error_CDND)**2 + (3/6 * h3d3_error_CDND)**2 + (2/6 * h2d4_error_CDND)**2 + (1/6 * h1d5_error_CDND)**2)
    +233
    +234        deuteration_CDND_amine = 0 * h3d3_ratio_CDND + (1/3) * h2d4_ratio_CDND + (2/3) * h1d5_ratio_CDND + 1 * h0d6_ratio_CDND
    +235        deuteration_CDND_amine_error = np.sqrt((1/3 * h2d4_error_CDND)**2 + (2/3 * h1d5_error_CDND)**2 + (1 * h0d6_error_CDND)**2)
    +236
    +237        protonation_CDND_amine = 1 * h3d3_ratio_CDND + (2/3) * h2d4_ratio_CDND + (1/3) * h1d5_ratio_CDND + 0 * h0d6_ratio_CDND
    +238        protonation_CDND_amine_error = np.sqrt((1 * h3d3_error_CDND)**2 + (2/3 * h2d4_error_CDND)**2 + (1/3 * h1d5_error_CDND)**2)
    +239
    +240    print()
    +241    if hasattr(ins, "plotting") and ins.plotting.legend != None:
    +242        print(f'Sample:  {ins.plotting.legend[df_index]}')
    +243    else:
    +244        print(f'Sample:  {ins.files[df_index]}')
    +245    print(f'Corrected baseline: {round(baseline,2)} +- {round(baseline_error,2)}')
    +246    if not run_total:
    +247        print(f"HHH {h6d0_limits}:  {round(h6d0_ratio,2)}  +-  {round(h6d0_error,2)}")
    +248        print(f"DHH {h5d1_limits}:  {round(h5d1_ratio,2)}  +-  {round(h5d1_error,2)}")
    +249        print(f"DDH {h4d2_limits}:  {round(h4d2_ratio,2)}  +-  {round(h4d2_error,2)}")
    +250        print(f"DDD {h3d3_limits}:  {round(h3d3_ratio,2)}  +-  {round(h3d3_error,2)}")
    +251        print(f"Amine deuteration:  {round(deuteration,2)}  +-  {round(deuteration_error,2)}")
    +252        print(f"Amine protonation:  {round(protonation,2)}  +-  {round(protonation_error,2)}")
    +253        print()
    +254        return f"{deuteration:.2f} +- {deuteration_error:.2f}"
    +255    else:
    +256        print(f"HHH-HHH {h6d0_limits}:  {round(h6d0_ratio_CDND,2)}  +-  {round(h6d0_error_CDND,2)}")
    +257        print(f"DHH-HHH {h5d1_limits}:  {round(h5d1_ratio_CDND,2)}  +-  {round(h5d1_error_CDND,2)}")
    +258        print(f"DDH-HHH {h4d2_limits}:  {round(h4d2_ratio_CDND,2)}  +-  {round(h4d2_error_CDND,2)}")
    +259        print(f"DDD-HHH {h3d3_limits}:  {round(h3d3_ratio_CDND,2)}  +-  {round(h3d3_error_CDND,2)}")
    +260        print(f"DDD-DHH {h2d4_limits}:  {round(h2d4_ratio_CDND,2)}  +-  {round(h2d4_error_CDND,2)}")
    +261        print(f"DDD-DDH {h1d5_limits}:  {round(h1d5_ratio_CDND,2)}  +-  {round(h1d5_error_CDND,2)}")
    +262        print(f"DDD-DDD {h0d6_limits}:  {round(h0d6_ratio_CDND,2)}  +-  {round(h0d6_error_CDND,2)}")
    +263        print(f"Total deuteration:  {round(deuteration_CDND,2)}  +-  {round(deuteration_CDND_error,2)}")
    +264        print(f"Total protonation:  {round(protonation_CDND,2)}  +-  {round(protonation_CDND_error,2)}")
    +265        print(f"Amine deuteration:  {round(deuteration_CDND_amine,2)}  +-  {round(deuteration_CDND_amine_error,2)}")
    +266        print(f"Amine protonation:  {round(protonation_CDND_amine,2)}  +-  {round(protonation_CDND_amine_error,2)}")
    +267        print()
    +268        return f"{deuteration_CDND_amine:.2f} +- {deuteration_CDND_amine_error:.2f} / {deuteration_CDND:.2f} +- {deuteration_CDND_error:.2f}"
    +
    + + +
    +
    + +
    + + def + impulse_approx( ins: aton.spectra.classes.Spectra, material_H: aton.spectra.classes.Material, material_D: aton.spectra.classes.Material, threshold: float = 600, H_df_index: int = 0, D_df_index: int = 1) -> tuple: + + + +
    + +
    21def impulse_approx(
    +22        ins: Spectra,
    +23        material_H: Material,
    +24        material_D: Material,
    +25        threshold: float=600,
    +26        H_df_index: int=0,
    +27        D_df_index: int=1,
    +28    ) -> tuple:
    +29    """Calculate the deuteration levels from INS spectra
    +30    with the *Impulse Approximation*, see
    +31    https://www.tandfonline.com/doi/full/10.1080/00018732.2017.1317963.
    +32
    +33    Protonated and deuterated materials must be specified
    +34    as `aton.spectra.Material` objects.
    +35    The threshold controls the start of the plateau (in meV)
    +36    to start considering Deep Inelastic Neutron Scattering (DINS).
    +37    The protonated and deuterated dataframe indexes are specified
    +38    by `H_df_index` and `D_df_index`, respectively.
    +39
    +40    In this approximation, the ideal ratio between
    +41    the cross-sections and the experimental ratio between
    +42    the pleteaus at high energies should be the same:
    +43    $$
    +44    \\frac{\\text{plateau_D}}{\\text{plateau_H}} \\approx \\frac{\\text{cross_section_D}}{\\text{cross_section_H}}
    +45    $$
    +46    Taking this into account, the deuteration is estimated as:
    +47    $$
    +48    \\text{Deuteration} = \\frac{1-\\text{real_ratio}}{1-\\text{ideal_ratio}}
    +49    $$
    +50
    +51    > [!WARNING]
    +52    > This approximation is very sensitive to the mass sample, specified by `aton.spectra.Material.grams`.
    +53    """
    +54    ins = deepcopy(ins)
    +55    material_H = deepcopy(material_H)
    +56    material_D = deepcopy(material_D)
    +57    material_H_grams = 1.0 if material_H.grams is None else material_H.grams
    +58    material_H.grams = material_H_grams
    +59    material_H.set()
    +60    material_D_grams = 1.0 if material_D.grams is None else material_D.grams
    +61    material_D.grams = material_D_grams
    +62    material_D.set()
    +63
    +64    material_H.print()
    +65    material_D.print()
    +66
    +67    # Make sure units are in meV
    +68    units_in = ins.units
    +69    if units_in not in alias.unit['meV']:
    +70        ins.set_units('meV', units_in)
    +71
    +72    # Divide the y values of the dataframes by the mols of the material.
    +73    ins.dfs[H_df_index][ins.dfs[H_df_index].columns[1]] = ins.dfs[H_df_index][ins.dfs[H_df_index].columns[1]]
    +74    ins.dfs[D_df_index][ins.dfs[D_df_index].columns[1]] = ins.dfs[D_df_index][ins.dfs[D_df_index].columns[1]]
    +75
    +76    plateau_H, plateau_H_error = plateau(ins, [threshold, None], H_df_index)
    +77    plateau_D, plateau_D_error = plateau(ins, [threshold, None], D_df_index)
    +78
    +79    plateau_H_normalized = plateau_H / material_H.mols
    +80    plateau_H_normalized_error = plateau_H_normalized * np.sqrt((plateau_H_error / plateau_H)**2 + (material_H.mols_error / material_H.mols)**2)
    +81    plateau_D_normalized = plateau_D / material_D.mols
    +82    plateau_D_normalized_error = plateau_D_normalized * np.sqrt((plateau_D_error / plateau_D)**2 + (material_D.mols_error / material_D.mols)**2)
    +83
    +84    # ratio if fully protonated = 1.0
    +85    ratio = plateau_D_normalized / plateau_H_normalized  # ratio_ideal < ratio < 1.0
    +86    ratio_ideal = material_D.cross_section / material_H.cross_section  # 0.0 < ratio_ideal < 1.0
    +87
    +88    ratio_error = abs(ratio * np.sqrt((plateau_H_normalized_error / plateau_H_normalized)**2 + (plateau_D_normalized_error / plateau_D_normalized)**2))
    +89
    +90    deuteration = (1 - ratio) / (1 - ratio_ideal)
    +91    deuteration_error = abs(deuteration * np.sqrt((ratio_error / ratio)**2))
    +92
    +93    print(f'Normalized plateau H:      {plateau_H_normalized} +- {plateau_H_normalized_error}')
    +94    print(f'Normalized plateau D:      {plateau_D_normalized} +- {plateau_D_normalized_error}')
    +95    print(f'Ratio D/H plateaus:        {ratio} +- {ratio_error}')
    +96    print(f'Ratio D/H cross sections:  {ratio_ideal}')
    +97
    +98    print(f"\nDeuteration: {deuteration:.2f} +- {deuteration_error:.2f}\n")
    +99    return round(deuteration,2), round(deuteration_error,2)
    +
    + + +

    Calculate the deuteration levels from INS spectra +with the Impulse Approximation, see +https://www.tandfonline.com/doi/full/10.1080/00018732.2017.1317963.

    + +

    Protonated and deuterated materials must be specified +as aton.spectra.Material objects. +The threshold controls the start of the plateau (in meV) +to start considering Deep Inelastic Neutron Scattering (DINS). +The protonated and deuterated dataframe indexes are specified +by H_df_index and D_df_index, respectively.

    + +

    In this approximation, the ideal ratio between +the cross-sections and the experimental ratio between +the pleteaus at high energies should be the same: +$$ +\frac{\text{plateau_D}}{\text{plateau_H}} \approx \frac{\text{cross_section_D}}{\text{cross_section_H}} +$$ +Taking this into account, the deuteration is estimated as: +$$ +\text{Deuteration} = \frac{1-\text{real_ratio}}{1-\text{ideal_ratio}} +$$

    + +
    +Warning +

    This approximation is very sensitive to the mass sample, specified by aton.spectra.Material.grams.

    +
    +
    + + +
    +
    + +
    + + def + peaks_mapi(ins: aton.spectra.classes.Spectra, peaks: dict, df_index: int = 0) -> str: + + + +
    + +
    102def peaks_mapi(
    +103        ins:Spectra,
    +104        peaks:dict,
    +105        df_index:int=0,
    +106    ) -> str:
    +107    '''
    +108    Calculate the deuteration of your CH$_3$NH$_3$PbI$_3$ samples
    +109    by integrating the INS disrotatory peaks,
    +110    which appear at around 38 meV for the fully protonated sample.
    +111    Note that `peaks` must be a dictionary with the peak limits
    +112    and the baseline, as in the example below:
    +113    ```python
    +114    peaks = {
    +115        'baseline' : None,
    +116        'baseline_error' : None,
    +117        'h6d0' : [41, 43],
    +118        'h5d1' : [41, 43],
    +119        'h4d2' : [41, 43],
    +120        'h3d3' : [34.7, 37.3],
    +121        'h2d4' : [31.0, 33.0],
    +122        'h1d5' : [28.0, 30.5],
    +123        'h0d6' : [26.5, 28.0],
    +124        }
    +125    ```
    +126    Peak keywords required for selective deuteration (only C or only N):
    +127    `h6d0`, `h5d1`, `h4d2`, `h3d3`.
    +128    Additional peak keywords required for total deuteration:
    +129    `h2d4`, `h1d5`, `h0d6`.
    +130    If some peak is not present in your sample,
    +131    just set the limits to a small baseline plateau.
    +132    '''
    +133
    +134    peak_data = deepcopy(ins)
    +135
    +136    baseline = 0.0
    +137    baseline_error = 0.0
    +138    if 'baseline' in peaks.keys():
    +139        if peaks['baseline'] is not None:
    +140            baseline = peaks['baseline']
    +141    if 'baseline_error' in peaks.keys():
    +142        if peaks['baseline_error'] is not None:
    +143            baseline_error = peaks['baseline_error']
    +144
    +145    run_partial = True
    +146    run_total = True
    +147
    +148    h6d0_limits = None
    +149    h5d1_limits = None
    +150    h4d2_limits = None
    +151    h3d3_limits = None
    +152    h2d4_limits = None
    +153    h1d5_limits = None
    +154    h0d6_limits = None
    +155
    +156    if 'h6d0' in peaks:
    +157        h6d0_limits = peaks['h6d0']
    +158    if 'h5d1' in peaks:
    +159        h5d1_limits = peaks['h5d1']
    +160    if 'h4d2' in peaks:
    +161        h4d2_limits = peaks['h4d2']
    +162    if 'h3d3' in peaks:
    +163        h3d3_limits = peaks['h3d3']
    +164    if 'h2d4' in peaks:
    +165        h2d4_limits = peaks['h2d4']
    +166    if 'h1d5' in peaks:
    +167        h1d5_limits = peaks['h1d5']
    +168    if 'h0d6' in peaks:
    +169        h0d6_limits = peaks['h0d6']
    +170
    +171    if h0d6_limits is None or h1d5_limits is None or h2d4_limits is None or h3d3_limits is None:
    +172        run_total = False
    +173    if h6d0_limits is None or h5d1_limits is None or h4d2_limits is None or h3d3_limits is None:
    +174        run_partial = False
    +175
    +176    if not run_partial:
    +177        raise ValueError('No peaks to integrate. Remember to assign peak limits as a dictionary with the keys: h6d0, h5d1, h4d2, h3d3, h2d4, h1d5, h0d6.')
    +178
    +179    h6d0_area, h6d0_area_error = area_under_peak(peak_data, [h6d0_limits[0], h6d0_limits[1], baseline, baseline_error], df_index, True)
    +180    h5d1_area, h5d1_area_error = area_under_peak(peak_data, [h5d1_limits[0], h5d1_limits[1], baseline, baseline_error], df_index, True)
    +181    h4d2_area, h4d2_area_error = area_under_peak(peak_data, [h4d2_limits[0], h4d2_limits[1], baseline, baseline_error], df_index, True)
    +182    h3d3_area, h3d3_area_error = area_under_peak(peak_data, [h3d3_limits[0], h3d3_limits[1], baseline, baseline_error], df_index, True)
    +183    h6d0_area /= 6
    +184    h5d1_area /= 5
    +185    h4d2_area /= 4
    +186    h3d3_area /= 3
    +187    h6d0_area_error /= 6
    +188    h5d1_area_error /= 5
    +189    h4d2_area_error /= 4
    +190    h3d3_area_error /= 3
    +191    
    +192    if not run_total:
    +193        total_area = h6d0_area + h5d1_area + h4d2_area + h3d3_area
    +194        total_area_error = np.sqrt(h6d0_area_error**2 + h5d1_area_error**2 + h4d2_area_error**2 + h3d3_area_error**2)
    +195
    +196        h6d0_ratio, h6d0_error = ratio_areas(h6d0_area, total_area, h6d0_area_error, total_area_error)
    +197        h5d1_ratio, h5d1_error = ratio_areas(h5d1_area, total_area, h5d1_area_error, total_area_error)
    +198        h4d2_ratio, h4d2_error = ratio_areas(h4d2_area, total_area, h4d2_area_error, total_area_error)
    +199        h3d3_ratio, h3d3_error = ratio_areas(h3d3_area, total_area, h3d3_area_error, total_area_error)
    +200
    +201        deuteration = 0 * h6d0_ratio + (1/3) * h5d1_ratio + (2/3) * h4d2_ratio + 1 * h3d3_ratio
    +202        protonation = 1 * h6d0_ratio + (2/3) * h5d1_ratio + (1/3) * h4d2_ratio + 0 * h3d3_ratio
    +203
    +204        deuteration_error = np.sqrt((1/3 * h5d1_error)**2 + (2/3 * h4d2_error)**2 + (1 * h3d3_error)**2)
    +205        protonation_error = np.sqrt((1 * h6d0_error)**2 + (2/3 * h5d1_error)**2 + (1/3 * h4d2_error)**2)
    +206
    +207    if run_total:
    +208        h2d4_area, h2d4_area_error = area_under_peak(peak_data, [h2d4_limits[0], h2d4_limits[1], baseline, baseline_error], df_index, True)
    +209        h1d5_area, h1d5_area_error = area_under_peak(peak_data, [h1d5_limits[0], h1d5_limits[1], baseline, baseline_error], df_index, True)
    +210        h0d6_area, h0d6_area_error = area_under_peak(peak_data, [h0d6_limits[0], h0d6_limits[1], baseline, baseline_error], df_index, True)
    +211        h2d4_area /= 2
    +212        h1d5_area /= 1
    +213        h0d6_area /= 1
    +214        h2d4_area_error /= 2
    +215        h1d5_area_error /= 1
    +216        h0d6_area_error /= 1
    +217
    +218        total_area_CDND = h6d0_area + h5d1_area + h4d2_area + h3d3_area + h2d4_area + h1d5_area + h0d6_area
    +219        total_area_error_CDND = np.sqrt(h6d0_area_error**2 + h5d1_area_error**2 + h4d2_area_error**2 + h3d3_area_error**2 + h2d4_area_error**2 + h1d5_area_error**2 + h0d6_area_error**2)
    +220
    +221        h6d0_ratio_CDND, h6d0_error_CDND = ratio_areas(h6d0_area, total_area_CDND, h6d0_area_error, total_area_error_CDND)
    +222        h5d1_ratio_CDND, h5d1_error_CDND = ratio_areas(h5d1_area, total_area_CDND, h5d1_area_error, total_area_error_CDND)
    +223        h4d2_ratio_CDND, h4d2_error_CDND = ratio_areas(h4d2_area, total_area_CDND, h4d2_area_error, total_area_error_CDND)
    +224        h3d3_ratio_CDND, h3d3_error_CDND = ratio_areas(h3d3_area, total_area_CDND, h3d3_area_error, total_area_error_CDND)
    +225        h2d4_ratio_CDND, h2d4_error_CDND = ratio_areas(h2d4_area, total_area_CDND, h2d4_area_error, total_area_error_CDND)
    +226        h1d5_ratio_CDND, h1d5_error_CDND = ratio_areas(h1d5_area, total_area_CDND, h1d5_area_error, total_area_error_CDND)
    +227        h0d6_ratio_CDND, h0d6_error_CDND = ratio_areas(h0d6_area, total_area_CDND, h0d6_area_error, total_area_error_CDND)
    +228
    +229        deuteration_CDND = 0 * h6d0_ratio_CDND + (1/6) * h5d1_ratio_CDND + (2/6) * h4d2_ratio_CDND + (3/6) * h3d3_ratio_CDND + (4/6) * h2d4_ratio_CDND + (5/6) * h1d5_ratio_CDND + 1 * h0d6_ratio_CDND
    +230        deuteration_CDND_error = np.sqrt((1/6 * h5d1_error_CDND)**2 + (2/6 * h4d2_error_CDND)**2 + (3/6 * h3d3_error_CDND)**2 + (4/6 * h2d4_error_CDND)**2 + (5/6 * h1d5_error_CDND)**2 + (1 * h0d6_error_CDND)**2)
    +231
    +232        protonation_CDND = 1 * h6d0_ratio_CDND + (5/6) * h5d1_ratio_CDND + (4/6) * h4d2_ratio_CDND + (3/6) * h3d3_ratio_CDND + (2/6) * h2d4_ratio_CDND + (1/6) * h1d5_ratio_CDND + 0 * h0d6_ratio_CDND
    +233        protonation_CDND_error = np.sqrt((1 * h6d0_error_CDND)**2 + (5/6 * h5d1_error_CDND)**2 + (4/6 * h4d2_error_CDND)**2 + (3/6 * h3d3_error_CDND)**2 + (2/6 * h2d4_error_CDND)**2 + (1/6 * h1d5_error_CDND)**2)
    +234
    +235        deuteration_CDND_amine = 0 * h3d3_ratio_CDND + (1/3) * h2d4_ratio_CDND + (2/3) * h1d5_ratio_CDND + 1 * h0d6_ratio_CDND
    +236        deuteration_CDND_amine_error = np.sqrt((1/3 * h2d4_error_CDND)**2 + (2/3 * h1d5_error_CDND)**2 + (1 * h0d6_error_CDND)**2)
    +237
    +238        protonation_CDND_amine = 1 * h3d3_ratio_CDND + (2/3) * h2d4_ratio_CDND + (1/3) * h1d5_ratio_CDND + 0 * h0d6_ratio_CDND
    +239        protonation_CDND_amine_error = np.sqrt((1 * h3d3_error_CDND)**2 + (2/3 * h2d4_error_CDND)**2 + (1/3 * h1d5_error_CDND)**2)
    +240
    +241    print()
    +242    if hasattr(ins, "plotting") and ins.plotting.legend != None:
    +243        print(f'Sample:  {ins.plotting.legend[df_index]}')
    +244    else:
    +245        print(f'Sample:  {ins.files[df_index]}')
    +246    print(f'Corrected baseline: {round(baseline,2)} +- {round(baseline_error,2)}')
    +247    if not run_total:
    +248        print(f"HHH {h6d0_limits}:  {round(h6d0_ratio,2)}  +-  {round(h6d0_error,2)}")
    +249        print(f"DHH {h5d1_limits}:  {round(h5d1_ratio,2)}  +-  {round(h5d1_error,2)}")
    +250        print(f"DDH {h4d2_limits}:  {round(h4d2_ratio,2)}  +-  {round(h4d2_error,2)}")
    +251        print(f"DDD {h3d3_limits}:  {round(h3d3_ratio,2)}  +-  {round(h3d3_error,2)}")
    +252        print(f"Amine deuteration:  {round(deuteration,2)}  +-  {round(deuteration_error,2)}")
    +253        print(f"Amine protonation:  {round(protonation,2)}  +-  {round(protonation_error,2)}")
    +254        print()
    +255        return f"{deuteration:.2f} +- {deuteration_error:.2f}"
    +256    else:
    +257        print(f"HHH-HHH {h6d0_limits}:  {round(h6d0_ratio_CDND,2)}  +-  {round(h6d0_error_CDND,2)}")
    +258        print(f"DHH-HHH {h5d1_limits}:  {round(h5d1_ratio_CDND,2)}  +-  {round(h5d1_error_CDND,2)}")
    +259        print(f"DDH-HHH {h4d2_limits}:  {round(h4d2_ratio_CDND,2)}  +-  {round(h4d2_error_CDND,2)}")
    +260        print(f"DDD-HHH {h3d3_limits}:  {round(h3d3_ratio_CDND,2)}  +-  {round(h3d3_error_CDND,2)}")
    +261        print(f"DDD-DHH {h2d4_limits}:  {round(h2d4_ratio_CDND,2)}  +-  {round(h2d4_error_CDND,2)}")
    +262        print(f"DDD-DDH {h1d5_limits}:  {round(h1d5_ratio_CDND,2)}  +-  {round(h1d5_error_CDND,2)}")
    +263        print(f"DDD-DDD {h0d6_limits}:  {round(h0d6_ratio_CDND,2)}  +-  {round(h0d6_error_CDND,2)}")
    +264        print(f"Total deuteration:  {round(deuteration_CDND,2)}  +-  {round(deuteration_CDND_error,2)}")
    +265        print(f"Total protonation:  {round(protonation_CDND,2)}  +-  {round(protonation_CDND_error,2)}")
    +266        print(f"Amine deuteration:  {round(deuteration_CDND_amine,2)}  +-  {round(deuteration_CDND_amine_error,2)}")
    +267        print(f"Amine protonation:  {round(protonation_CDND_amine,2)}  +-  {round(protonation_CDND_amine_error,2)}")
    +268        print()
    +269        return f"{deuteration_CDND_amine:.2f} +- {deuteration_CDND_amine_error:.2f} / {deuteration_CDND:.2f} +- {deuteration_CDND_error:.2f}"
    +
    + + +

    Calculate the deuteration of your CH$_3$NH$_3$PbI$_3$ samples +by integrating the INS disrotatory peaks, +which appear at around 38 meV for the fully protonated sample. +Note that peaks must be a dictionary with the peak limits +and the baseline, as in the example below:

    + +
    +
    peaks = {
    +    'baseline' : None,
    +    'baseline_error' : None,
    +    'h6d0' : [41, 43],
    +    'h5d1' : [41, 43],
    +    'h4d2' : [41, 43],
    +    'h3d3' : [34.7, 37.3],
    +    'h2d4' : [31.0, 33.0],
    +    'h1d5' : [28.0, 30.5],
    +    'h0d6' : [26.5, 28.0],
    +    }
    +
    +
    + +

    Peak keywords required for selective deuteration (only C or only N): +h6d0, h5d1, h4d2, h3d3. +Additional peak keywords required for total deuteration: +h2d4, h1d5, h0d6. +If some peak is not present in your sample, +just set the limits to a small baseline plateau.

    +
    + + +
    +
    + + \ No newline at end of file diff --git a/docs/aton/spectra/fit.html b/docs/aton/spectra/fit.html new file mode 100644 index 0000000..0074e16 --- /dev/null +++ b/docs/aton/spectra/fit.html @@ -0,0 +1,737 @@ + + + + + + + aton.spectra.fit API documentation + + + + + + + + + + + + + +
    +
    +

    +aton.spectra.fit

    + +

    Description

    + +

    This module contains functions for fitting and analyzing spectral data.

    + +

    Index

    + + + +
    +
    + + + + + +
      1"""
    +  2# Description
    +  3This module contains functions for fitting and analyzing spectral data.
    +  4
    +  5# Index
    +  6- `plateau()`
    +  7- `area_under_peak()`
    +  8- `ratio_areas()`
    +  9- `mean()`
    + 10
    + 11---
    + 12"""
    + 13
    + 14
    + 15import math
    + 16import scipy
    + 17import numpy as np
    + 18from copy import deepcopy
    + 19from aton.units import *
    + 20from .classes import Spectra
    + 21
    + 22
    + 23def plateau(
    + 24        spectra:Spectra,
    + 25        cuts=None,
    + 26        df_index:int=0
    + 27    ) -> tuple:
    + 28    """Fit the mean value and the error of a plateau in a `aton.spectra.Spectra` object.
    + 29    
    + 30    If `aton.spectra.Spectra.dfs[df_index]` has an 'Error' column, those errors are also taken into account
    + 31    along with the standard deviation of the mean, else only the standard deviation is considered.
    + 32    The 'Error' column title can be any string in `maatpy.alias.file['Error']`.\n
    + 33    Use as `maatpy.fit.plateau(spectra, cuts=[low_cut, high_cut], df_index=0)`.
    + 34    Note that `cuts`, `low_cut` and/or `top_cut` can be set to None.
    + 35    """
    + 36    df = deepcopy(spectra.dfs[df_index])
    + 37    if isinstance(cuts, list):
    + 38        low_cut = cuts[0]
    + 39        if len(cuts) > 1:
    + 40            top_cut = cuts[1]
    + 41        else:
    + 42            top_cut = None
    + 43    elif isinstance(cuts, float):  # If cuts is a float, it is considered as low_cut
    + 44        low_cut = cuts
    + 45        top_cut = None
    + 46    elif cuts is None:
    + 47        low_cut = None
    + 48        top_cut = None
    + 49    else:
    + 50        raise ValueError("plateau: cuts must be a float for the low_cut, or a list")
    + 51    if low_cut is not None:
    + 52        df = df[df[df.columns[0]] >= low_cut]
    + 53    if top_cut is not None:
    + 54        df = df[df[df.columns[0]] <= top_cut]
    + 55    mean = df[df.columns[1]].mean()
    + 56    std_mean = df[df.columns[1]].std()
    + 57    error_column = next((col for col in alias.file['error'] if col in df.columns), None)  # Get the error column title
    + 58    if error_column:
    + 59        errors = df[error_column].to_numpy()
    + 60        std_data = np.sqrt(np.sum(errors**2)) / len(errors)
    + 61        std = np.sqrt(std_data**2 + std_mean**2)
    + 62    else:
    + 63        std = std_mean
    + 64    return mean, std
    + 65
    + 66
    + 67def area_under_peak(
    + 68        spectra:Spectra,
    + 69        peak:list,
    + 70        df_index:int=0,
    + 71        errors_as_in_baseline:bool=True,
    + 72        min_as_baseline:bool=False
    + 73    ) -> tuple:
    + 74    """
    + 75    Calculate the area under a given peak.
    + 76
    + 77    Peaks must be defined as `peak:list=[xmin, xmax, baseline=0, baseline_error=0]`.
    + 78    If the dataset has no `Error` column, the error for each point is assumed to be the same
    + 79    as the baseline error if `errors_as_in_baseline=True`, otherwise it is assumed to be zero.
    + 80    If `min_as_baseline=True` and `baseline=0`, the baseline is assumed to be the minimum value.
    + 81    Also, if `min_as_baseline=True` and there are negative areas even after applying the baseline,
    + 82    the baseline will be corrected to the minimum value.
    + 83    """
    + 84    if len(peak) < 2:
    + 85        raise ValueError("area_under_peak: peak must have at least two values: [xmin, xmax]")
    + 86    xmin = peak[0]
    + 87    xmax = peak[1]
    + 88    baseline = peak[2] if len(peak) >= 3 else 0.0
    + 89    baseline_error = peak[3] if len(peak) >= 4 else 0.0
    + 90
    + 91    df = deepcopy(spectra.dfs[df_index])
    + 92    df_range = df[(df[df.columns[0]] >= xmin) & (df[df.columns[0]] <= xmax)]
    + 93    x = df_range[df.columns[0]].to_numpy()
    + 94    y = df_range[df.columns[1]].to_numpy()
    + 95    
    + 96    min_y = y.min()
    + 97    if min_as_baseline and (baseline == 0 or baseline > min_y):
    + 98        baseline = min_y
    + 99
    +100    y = y - baseline
    +101
    +102    area = scipy.integrate.simpson(y, x=x)
    +103
    +104    error_column = next((col for col in alias.file['Error'] if col in df_range.columns), None)  # Get the error column title
    +105    if error_column:
    +106        point_errors = df_range[error_column].to_numpy()
    +107    else: # Assume the error in each point is the same as the baseline error
    +108        if errors_as_in_baseline == True:
    +109            point_errors = np.full_like(y, baseline_error)
    +110        else:
    +111            point_errors = np.zeros_like(y)
    +112    total_errors = np.sqrt(point_errors**2 + baseline_error**2)
    +113    area_error = np.sqrt(scipy.integrate.simpson(total_errors**2, x=x))
    +114
    +115    return area, area_error
    +116
    +117
    +118def ratio_areas(
    +119        area:float,
    +120        area_total:float,
    +121        area_error:float=0.0,
    +122        area_total_error:float=0.0,
    +123        inverse_ratio:bool=False
    +124    ) -> tuple:
    +125    """Check the ratio between two areas, e.g. to estimate deuteration levels from ATR data.
    +126    
    +127    The ratio is calculated as `area / area_total`. This behavior is modified if `inverse_ratio = True`,
    +128    so that the ratio is calculated as `(area_total - area) / area_total`.
    +129    Note that changing the ratio calculation also affects the error propagation.
    +130    """
    +131    if inverse_ratio:
    +132        ratio = (area_total - area) / area_total
    +133        if ratio != 0.0:
    +134            ratio_error = abs(ratio) * np.sqrt((np.sqrt(area_total_error**2 + area_error**2) / (area_total - area))**2 + (area_total_error / area_total)**2)
    +135        else:
    +136            ratio_error = None
    +137    else:
    +138        ratio = area / area_total
    +139        if ratio != 0.0:
    +140            ratio_error = abs(ratio) * np.sqrt((area_error / area)**2 + (area_total_error / area_total)**2)
    +141        else:
    +142            ratio_error = None
    +143    
    +144    return ratio, ratio_error
    +145
    +146
    +147def mean(
    +148        array:list,
    +149        rounded:bool=True,
    +150        degrees_of_freedom=0
    +151    ) -> tuple:
    +152    """Takes an `array` of numerical values and returns the mean and standard deviation.
    +153
    +154    It is calculated with numpy as:\n
    +155    $\\sigma_{x}=\\sqrt{\\frac{\\sum{(x_{i}-{\\overline{x}})^2}}{N-\\text{ddof}}}$\n
    +156    where ddof are the delta `degrees_of_freedom`, zero by default.
    +157    Set it to `1` for a corrected sample standard deviation (low N cases),
    +158    see more details [here](https://en.wikipedia.org/wiki/Standard_deviation#Corrected_sample_standard_deviation).\n
    +159    The mean is rounded up to the order of the error by default. To override this behaviour, set `rounded=False`.
    +160    """
    +161    if not all(isinstance(x, (int, float, np.ndarray)) for x in array):
    +162        raise ValueError("mean_std(list) requires numerical values (int, float, or numpy.ndarray).")
    +163    data = np.asarray(array)
    +164    mean = float(data.mean())
    +165    error = float(data.std(ddof=degrees_of_freedom))
    +166    if not rounded or error == 0:
    +167        return mean, error
    +168    exponent = int(math.floor(math.log10(abs(error))))
    +169    first_three_digits = int(100*abs(error) / 10**exponent)
    +170    if 104 < first_three_digits < 195:
    +171        exponent -= 1
    +172    rounded_mean = round(mean, -exponent)
    +173    rounded_error = round(error, -exponent)
    +174    return rounded_mean, rounded_error
    +
    + + +
    +
    + +
    + + def + plateau( spectra: aton.spectra.classes.Spectra, cuts=None, df_index: int = 0) -> tuple: + + + +
    + +
    24def plateau(
    +25        spectra:Spectra,
    +26        cuts=None,
    +27        df_index:int=0
    +28    ) -> tuple:
    +29    """Fit the mean value and the error of a plateau in a `aton.spectra.Spectra` object.
    +30    
    +31    If `aton.spectra.Spectra.dfs[df_index]` has an 'Error' column, those errors are also taken into account
    +32    along with the standard deviation of the mean, else only the standard deviation is considered.
    +33    The 'Error' column title can be any string in `maatpy.alias.file['Error']`.\n
    +34    Use as `maatpy.fit.plateau(spectra, cuts=[low_cut, high_cut], df_index=0)`.
    +35    Note that `cuts`, `low_cut` and/or `top_cut` can be set to None.
    +36    """
    +37    df = deepcopy(spectra.dfs[df_index])
    +38    if isinstance(cuts, list):
    +39        low_cut = cuts[0]
    +40        if len(cuts) > 1:
    +41            top_cut = cuts[1]
    +42        else:
    +43            top_cut = None
    +44    elif isinstance(cuts, float):  # If cuts is a float, it is considered as low_cut
    +45        low_cut = cuts
    +46        top_cut = None
    +47    elif cuts is None:
    +48        low_cut = None
    +49        top_cut = None
    +50    else:
    +51        raise ValueError("plateau: cuts must be a float for the low_cut, or a list")
    +52    if low_cut is not None:
    +53        df = df[df[df.columns[0]] >= low_cut]
    +54    if top_cut is not None:
    +55        df = df[df[df.columns[0]] <= top_cut]
    +56    mean = df[df.columns[1]].mean()
    +57    std_mean = df[df.columns[1]].std()
    +58    error_column = next((col for col in alias.file['error'] if col in df.columns), None)  # Get the error column title
    +59    if error_column:
    +60        errors = df[error_column].to_numpy()
    +61        std_data = np.sqrt(np.sum(errors**2)) / len(errors)
    +62        std = np.sqrt(std_data**2 + std_mean**2)
    +63    else:
    +64        std = std_mean
    +65    return mean, std
    +
    + + +

    Fit the mean value and the error of a plateau in a aton.spectra.Spectra object.

    + +

    If aton.spectra.Spectra.dfs[df_index] has an 'Error' column, those errors are also taken into account +along with the standard deviation of the mean, else only the standard deviation is considered. +The 'Error' column title can be any string in maatpy.alias.file['Error'].

    + +

    Use as maatpy.fit.plateau(spectra, cuts=[low_cut, high_cut], df_index=0). +Note that cuts, low_cut and/or top_cut can be set to None.

    +
    + + +
    +
    + +
    + + def + area_under_peak( spectra: aton.spectra.classes.Spectra, peak: list, df_index: int = 0, errors_as_in_baseline: bool = True, min_as_baseline: bool = False) -> tuple: + + + +
    + +
     68def area_under_peak(
    + 69        spectra:Spectra,
    + 70        peak:list,
    + 71        df_index:int=0,
    + 72        errors_as_in_baseline:bool=True,
    + 73        min_as_baseline:bool=False
    + 74    ) -> tuple:
    + 75    """
    + 76    Calculate the area under a given peak.
    + 77
    + 78    Peaks must be defined as `peak:list=[xmin, xmax, baseline=0, baseline_error=0]`.
    + 79    If the dataset has no `Error` column, the error for each point is assumed to be the same
    + 80    as the baseline error if `errors_as_in_baseline=True`, otherwise it is assumed to be zero.
    + 81    If `min_as_baseline=True` and `baseline=0`, the baseline is assumed to be the minimum value.
    + 82    Also, if `min_as_baseline=True` and there are negative areas even after applying the baseline,
    + 83    the baseline will be corrected to the minimum value.
    + 84    """
    + 85    if len(peak) < 2:
    + 86        raise ValueError("area_under_peak: peak must have at least two values: [xmin, xmax]")
    + 87    xmin = peak[0]
    + 88    xmax = peak[1]
    + 89    baseline = peak[2] if len(peak) >= 3 else 0.0
    + 90    baseline_error = peak[3] if len(peak) >= 4 else 0.0
    + 91
    + 92    df = deepcopy(spectra.dfs[df_index])
    + 93    df_range = df[(df[df.columns[0]] >= xmin) & (df[df.columns[0]] <= xmax)]
    + 94    x = df_range[df.columns[0]].to_numpy()
    + 95    y = df_range[df.columns[1]].to_numpy()
    + 96    
    + 97    min_y = y.min()
    + 98    if min_as_baseline and (baseline == 0 or baseline > min_y):
    + 99        baseline = min_y
    +100
    +101    y = y - baseline
    +102
    +103    area = scipy.integrate.simpson(y, x=x)
    +104
    +105    error_column = next((col for col in alias.file['Error'] if col in df_range.columns), None)  # Get the error column title
    +106    if error_column:
    +107        point_errors = df_range[error_column].to_numpy()
    +108    else: # Assume the error in each point is the same as the baseline error
    +109        if errors_as_in_baseline == True:
    +110            point_errors = np.full_like(y, baseline_error)
    +111        else:
    +112            point_errors = np.zeros_like(y)
    +113    total_errors = np.sqrt(point_errors**2 + baseline_error**2)
    +114    area_error = np.sqrt(scipy.integrate.simpson(total_errors**2, x=x))
    +115
    +116    return area, area_error
    +
    + + +

    Calculate the area under a given peak.

    + +

    Peaks must be defined as peak:list=[xmin, xmax, baseline=0, baseline_error=0]. +If the dataset has no Error column, the error for each point is assumed to be the same +as the baseline error if errors_as_in_baseline=True, otherwise it is assumed to be zero. +If min_as_baseline=True and baseline=0, the baseline is assumed to be the minimum value. +Also, if min_as_baseline=True and there are negative areas even after applying the baseline, +the baseline will be corrected to the minimum value.

    +
    + + +
    +
    + +
    + + def + ratio_areas( area: float, area_total: float, area_error: float = 0.0, area_total_error: float = 0.0, inverse_ratio: bool = False) -> tuple: + + + +
    + +
    119def ratio_areas(
    +120        area:float,
    +121        area_total:float,
    +122        area_error:float=0.0,
    +123        area_total_error:float=0.0,
    +124        inverse_ratio:bool=False
    +125    ) -> tuple:
    +126    """Check the ratio between two areas, e.g. to estimate deuteration levels from ATR data.
    +127    
    +128    The ratio is calculated as `area / area_total`. This behavior is modified if `inverse_ratio = True`,
    +129    so that the ratio is calculated as `(area_total - area) / area_total`.
    +130    Note that changing the ratio calculation also affects the error propagation.
    +131    """
    +132    if inverse_ratio:
    +133        ratio = (area_total - area) / area_total
    +134        if ratio != 0.0:
    +135            ratio_error = abs(ratio) * np.sqrt((np.sqrt(area_total_error**2 + area_error**2) / (area_total - area))**2 + (area_total_error / area_total)**2)
    +136        else:
    +137            ratio_error = None
    +138    else:
    +139        ratio = area / area_total
    +140        if ratio != 0.0:
    +141            ratio_error = abs(ratio) * np.sqrt((area_error / area)**2 + (area_total_error / area_total)**2)
    +142        else:
    +143            ratio_error = None
    +144    
    +145    return ratio, ratio_error
    +
    + + +

    Check the ratio between two areas, e.g. to estimate deuteration levels from ATR data.

    + +

    The ratio is calculated as area / area_total. This behavior is modified if inverse_ratio = True, +so that the ratio is calculated as (area_total - area) / area_total. +Note that changing the ratio calculation also affects the error propagation.

    +
    + + +
    +
    + +
    + + def + mean(array: list, rounded: bool = True, degrees_of_freedom=0) -> tuple: + + + +
    + +
    148def mean(
    +149        array:list,
    +150        rounded:bool=True,
    +151        degrees_of_freedom=0
    +152    ) -> tuple:
    +153    """Takes an `array` of numerical values and returns the mean and standard deviation.
    +154
    +155    It is calculated with numpy as:\n
    +156    $\\sigma_{x}=\\sqrt{\\frac{\\sum{(x_{i}-{\\overline{x}})^2}}{N-\\text{ddof}}}$\n
    +157    where ddof are the delta `degrees_of_freedom`, zero by default.
    +158    Set it to `1` for a corrected sample standard deviation (low N cases),
    +159    see more details [here](https://en.wikipedia.org/wiki/Standard_deviation#Corrected_sample_standard_deviation).\n
    +160    The mean is rounded up to the order of the error by default. To override this behaviour, set `rounded=False`.
    +161    """
    +162    if not all(isinstance(x, (int, float, np.ndarray)) for x in array):
    +163        raise ValueError("mean_std(list) requires numerical values (int, float, or numpy.ndarray).")
    +164    data = np.asarray(array)
    +165    mean = float(data.mean())
    +166    error = float(data.std(ddof=degrees_of_freedom))
    +167    if not rounded or error == 0:
    +168        return mean, error
    +169    exponent = int(math.floor(math.log10(abs(error))))
    +170    first_three_digits = int(100*abs(error) / 10**exponent)
    +171    if 104 < first_three_digits < 195:
    +172        exponent -= 1
    +173    rounded_mean = round(mean, -exponent)
    +174    rounded_error = round(error, -exponent)
    +175    return rounded_mean, rounded_error
    +
    + + +

    Takes an array of numerical values and returns the mean and standard deviation.

    + +

    It is calculated with numpy as:

    + +

    $\sigma_{x}=\sqrt{\frac{\sum{(x_{i}-{\overline{x}})^2}}{N-\text{ddof}}}$

    + +

    where ddof are the delta degrees_of_freedom, zero by default. +Set it to 1 for a corrected sample standard deviation (low N cases), +see more details here.

    + +

    The mean is rounded up to the order of the error by default. To override this behaviour, set rounded=False.

    +
    + + +
    +
    + + \ No newline at end of file diff --git a/docs/aton/spectra/normalize.html b/docs/aton/spectra/normalize.html new file mode 100644 index 0000000..5cdebc7 --- /dev/null +++ b/docs/aton/spectra/normalize.html @@ -0,0 +1,558 @@ + + + + + + + aton.spectra.normalize API documentation + + + + + + + + + + + + + +
    +
    +

    +aton.spectra.normalize

    + +

    Description

    + +

    This module contains functions to normalize data and other variables.

    + +

    Index

    + + + +
    +
    + + + + + +
      1'''
    +  2# Description
    +  3This module contains functions to normalize data and other variables.
    +  4
    +  5# Index
    +  6- `unit_str()`
    +  7- `spectra()`
    +  8- `area()`
    +  9
    + 10---
    + 11'''
    + 12
    + 13
    + 14import aton.alias as alias
    + 15from aton.units import *
    + 16from .classes import *
    + 17from .fit import *
    + 18
    + 19
    + 20def unit_str(unit:str):
    + 21    '''Normalize a given unit string to a standarized unit string, following `maatpy.alias.unit`.'''
    + 22    for key, value in alias.unit.items():
    + 23        if unit in value:
    + 24            return key
    + 25    print(f"WARNING: Unknown unit '{unit}'")
    + 26    return unit
    + 27
    + 28
    + 29def spectra(spectra:Spectra):
    + 30    '''Normalize the given spectra by height, with optional `maatpy.classes.Scaling` attributes.'''
    + 31    sdata = deepcopy(spectra)
    + 32    if hasattr(sdata, 'scaling') and sdata.scaling is not None:
    + 33        scaling = sdata.scaling
    + 34        if scaling.ymax:
    + 35            return _spectra_y(sdata)
    + 36    else:
    + 37        scaling = Scaling()
    + 38
    + 39    df_index = scaling.index if scaling.index else 0
    + 40    df0 = sdata.dfs[df_index]
    + 41
    + 42    if scaling.xmin is None:
    + 43        scaling.xmin = min(df0[df0.columns[0]])
    + 44    if scaling.xmax is None:
    + 45        scaling.xmax = max(df0[df0.columns[0]])
    + 46
    + 47    sdata.scaling = scaling
    + 48
    + 49    xmin = scaling.xmin
    + 50    xmax = scaling.xmax
    + 51
    + 52    df0 = df0[(df0[df0.columns[0]] >= xmin) & (df0[df0.columns[0]] <= xmax)]
    + 53    ymax_on_range = df0[df0.columns[1]].max()
    + 54    normalized_dataframes = []
    + 55    for df in sdata.dfs:
    + 56        df_range = df[(df[df.columns[0]] >= xmin) & (df[df.columns[0]] <= xmax)]
    + 57        i_ymax_on_range = df_range[df_range.columns[1]].max()
    + 58        df[df.columns[1]] =  df[df.columns[1]] * ymax_on_range / i_ymax_on_range
    + 59        normalized_dataframes.append(df)
    + 60    sdata.dfs = normalized_dataframes
    + 61    return sdata
    + 62
    + 63
    + 64def _spectra_y(sdata:Spectra):
    + 65    if not len(sdata.scaling.ymax) == len(sdata.dfs):
    + 66        raise ValueError("normalize: len(ymax) does not match len(dataframe)")
    + 67    scaling = sdata.scaling
    + 68    ymax = scaling.ymax
    + 69    ymin = scaling.ymin if scaling.ymin else [0.0]
    + 70    if len(ymin) == 1:
    + 71        ymin = ymin * len(sdata.dfs)
    + 72    index = scaling.index if scaling.index else 0
    + 73    reference_height = ymax[index] - ymin[index]
    + 74    normalized_dataframes = []
    + 75    for i, df in enumerate(sdata.dfs):
    + 76        height = ymax[i] - ymin[i]
    + 77        df[df.columns[1]] =  df[df.columns[1]] * reference_height / height
    + 78        normalized_dataframes.append(df)
    + 79    sdata.dfs = normalized_dataframes
    + 80    return sdata
    + 81
    + 82
    + 83def area(spectra:Spectra):
    + 84    '''
    + 85    Normalize the given spectra by the area under the datasets, with optional `maatpy.classes.Scaling` attributes.
    + 86    '''
    + 87    sdata = deepcopy(spectra)
    + 88    if hasattr(sdata, 'scaling') and sdata.scaling is not None:
    + 89        scaling = sdata.scaling
    + 90        if scaling.ymax:
    + 91            return _normalize_y(sdata)
    + 92    else:
    + 93        scaling = Scaling()
    + 94
    + 95    df_index = scaling.index if scaling.index else 0
    + 96    df0 = sdata.dfs[df_index]
    + 97
    + 98    if scaling.xmin is None:
    + 99        scaling.xmin = min(df0[df0.columns[0]])
    +100    if scaling.xmax is None:
    +101        scaling.xmax = max(df0[df0.columns[0]])
    +102
    +103    sdata.scaling = scaling
    +104
    +105    xmin = scaling.xmin
    +106    xmax = scaling.xmax
    +107
    +108    df0 = df0[(df0[df0.columns[0]] >= xmin) & (df0[df0.columns[0]] <= xmax)]
    +109    area_df0, _ = area_under_peak(sdata, peak=[xmin,xmax], df_index=df_index, min_as_baseline=True)
    +110    normalized_dataframes = []
    +111    for df_i, df in enumerate(sdata.dfs):
    +112        area_df, _ = area_under_peak(sdata, peak=[xmin,xmax], df_index=df_i, min_as_baseline=True)
    +113        scaling_factor = area_df0 / area_df
    +114        df[df.columns[1]] =  df[df.columns[1]] * scaling_factor
    +115        normalized_dataframes.append(df)
    +116    sdata.dfs = normalized_dataframes
    +117    return sdata
    +
    + + +
    +
    + +
    + + def + unit_str(unit: str): + + + +
    + +
    21def unit_str(unit:str):
    +22    '''Normalize a given unit string to a standarized unit string, following `maatpy.alias.unit`.'''
    +23    for key, value in alias.unit.items():
    +24        if unit in value:
    +25            return key
    +26    print(f"WARNING: Unknown unit '{unit}'")
    +27    return unit
    +
    + + +

    Normalize a given unit string to a standarized unit string, following maatpy.alias.unit.

    +
    + + +
    +
    + +
    + + def + spectra(spectra: aton.spectra.classes.Spectra): + + + +
    + +
    30def spectra(spectra:Spectra):
    +31    '''Normalize the given spectra by height, with optional `maatpy.classes.Scaling` attributes.'''
    +32    sdata = deepcopy(spectra)
    +33    if hasattr(sdata, 'scaling') and sdata.scaling is not None:
    +34        scaling = sdata.scaling
    +35        if scaling.ymax:
    +36            return _spectra_y(sdata)
    +37    else:
    +38        scaling = Scaling()
    +39
    +40    df_index = scaling.index if scaling.index else 0
    +41    df0 = sdata.dfs[df_index]
    +42
    +43    if scaling.xmin is None:
    +44        scaling.xmin = min(df0[df0.columns[0]])
    +45    if scaling.xmax is None:
    +46        scaling.xmax = max(df0[df0.columns[0]])
    +47
    +48    sdata.scaling = scaling
    +49
    +50    xmin = scaling.xmin
    +51    xmax = scaling.xmax
    +52
    +53    df0 = df0[(df0[df0.columns[0]] >= xmin) & (df0[df0.columns[0]] <= xmax)]
    +54    ymax_on_range = df0[df0.columns[1]].max()
    +55    normalized_dataframes = []
    +56    for df in sdata.dfs:
    +57        df_range = df[(df[df.columns[0]] >= xmin) & (df[df.columns[0]] <= xmax)]
    +58        i_ymax_on_range = df_range[df_range.columns[1]].max()
    +59        df[df.columns[1]] =  df[df.columns[1]] * ymax_on_range / i_ymax_on_range
    +60        normalized_dataframes.append(df)
    +61    sdata.dfs = normalized_dataframes
    +62    return sdata
    +
    + + +

    Normalize the given spectra by height, with optional maatpy.classes.Scaling attributes.

    +
    + + +
    +
    + +
    + + def + area(spectra: aton.spectra.classes.Spectra): + + + +
    + +
     84def area(spectra:Spectra):
    + 85    '''
    + 86    Normalize the given spectra by the area under the datasets, with optional `maatpy.classes.Scaling` attributes.
    + 87    '''
    + 88    sdata = deepcopy(spectra)
    + 89    if hasattr(sdata, 'scaling') and sdata.scaling is not None:
    + 90        scaling = sdata.scaling
    + 91        if scaling.ymax:
    + 92            return _normalize_y(sdata)
    + 93    else:
    + 94        scaling = Scaling()
    + 95
    + 96    df_index = scaling.index if scaling.index else 0
    + 97    df0 = sdata.dfs[df_index]
    + 98
    + 99    if scaling.xmin is None:
    +100        scaling.xmin = min(df0[df0.columns[0]])
    +101    if scaling.xmax is None:
    +102        scaling.xmax = max(df0[df0.columns[0]])
    +103
    +104    sdata.scaling = scaling
    +105
    +106    xmin = scaling.xmin
    +107    xmax = scaling.xmax
    +108
    +109    df0 = df0[(df0[df0.columns[0]] >= xmin) & (df0[df0.columns[0]] <= xmax)]
    +110    area_df0, _ = area_under_peak(sdata, peak=[xmin,xmax], df_index=df_index, min_as_baseline=True)
    +111    normalized_dataframes = []
    +112    for df_i, df in enumerate(sdata.dfs):
    +113        area_df, _ = area_under_peak(sdata, peak=[xmin,xmax], df_index=df_i, min_as_baseline=True)
    +114        scaling_factor = area_df0 / area_df
    +115        df[df.columns[1]] =  df[df.columns[1]] * scaling_factor
    +116        normalized_dataframes.append(df)
    +117    sdata.dfs = normalized_dataframes
    +118    return sdata
    +
    + + +

    Normalize the given spectra by the area under the datasets, with optional maatpy.classes.Scaling attributes.

    +
    + + +
    +
    + + \ No newline at end of file diff --git a/docs/aton/spectra/plot.html b/docs/aton/spectra/plot.html new file mode 100644 index 0000000..c32509e --- /dev/null +++ b/docs/aton/spectra/plot.html @@ -0,0 +1,586 @@ + + + + + + + aton.spectra.plot API documentation + + + + + + + + + + + + + +
    +
    +

    +aton.spectra.plot

    + +

    Description

    + +

    This module loads the plot() function, used to plot aton.spectra.SpectraData data.

    + +
    +
    + + + + + +
      1'''
    +  2# Description
    +  3This module loads the `plot()` function, used to plot `aton.spectra.SpectraData` data.
    +  4
    +  5--- 
    +  6'''
    +  7
    +  8
    +  9import matplotlib.pyplot as plt
    + 10from .classes import *
    + 11from . import normalize
    + 12import aton.alias as alias
    + 13
    + 14
    + 15def plot(spectrum:Spectra):
    + 16    '''
    + 17    Plot the given spectra, with optional `maatpy.classes.Plotting` and `maatpy.classes.Scaling` attributes.
    + 18    '''
    + 19
    + 20    strings_to_delete_from_name = ['.csv', '.dat', '.txt', '_INS', '_ATR', '_FTIR', '_temp', '_RAMAN', '_Raman', '/data/', 'data/', '/csv/', 'csv/', '/INS/', 'INS/', '/FTIR/', 'FTIR/', '/ATR/', 'ATR/', '_smooth', '_smoothed', '_subtracted', '_cellsubtracted']
    + 21    normalize_area_keys = alias.parameters['area']
    + 22    normalize_height_keys = alias.parameters['height']
    + 23    normalize_height_keys.extend(alias.boolean[True])
    + 24
    + 25    sdata = deepcopy(spectrum)
    + 26
    + 27    if hasattr(sdata, 'plotting') and sdata.plotting.figsize:
    + 28        fig, ax = plt.subplots(figsize=sdata.plotting.figsize)
    + 29    else:
    + 30        fig, ax = plt.subplots()
    + 31
    + 32    if sdata.plotting.normalize in normalize_height_keys:
    + 33        sdata = normalize.spectra(sdata)
    + 34    elif sdata.plotting.normalize in normalize_area_keys:
    + 35        sdata = normalize.area(sdata)
    + 36
    + 37    calculated_low_ylim, calculated_top_ylim = _get_ylimits(sdata)
    + 38
    + 39    low_ylim = calculated_low_ylim if not hasattr(sdata, 'plotting') or sdata.plotting.ylim[0] is None else sdata.plotting.ylim[0]
    + 40    top_ylim = calculated_top_ylim if not hasattr(sdata, 'plotting') or sdata.plotting.ylim[1] is None else sdata.plotting.ylim[1]
    + 41    
    + 42    low_xlim = None
    + 43    top_xlim = None
    + 44    if getattr(sdata, 'plotting', None) is not None:
    + 45        title = sdata.plotting.title
    + 46        low_xlim = sdata.plotting.xlim[0]
    + 47        top_xlim = sdata.plotting.xlim[1]
    + 48        xlabel = sdata.plotting.xlabel if sdata.plotting.xlabel is not None else sdata.dfs[0].columns[0]
    + 49        ylabel = sdata.plotting.ylabel if sdata.plotting.ylabel is not None else sdata.dfs[0].columns[1]
    + 50    else:
    + 51        title = sdata.comment
    + 52
    + 53    number_of_plots = len(sdata.dfs)
    + 54    height = top_ylim - low_ylim
    + 55    if hasattr(sdata, 'plotting') and sdata.plotting.offset is True:
    + 56        for i, df in enumerate(sdata.dfs):
    + 57            reverse_i = (number_of_plots - 1) - i
    + 58            df[df.columns[1]] = df[df.columns[1]] + (reverse_i * height)
    + 59    elif hasattr(sdata, 'plotting') and (isinstance(sdata.plotting.offset, float) or isinstance(sdata.plotting.offset, int)):
    + 60        offset = sdata.plotting.offset
    + 61        for i, df in enumerate(sdata.dfs):
    + 62            reverse_i = (number_of_plots - 1) - i
    + 63            df[df.columns[1]] = df[df.columns[1]] + (reverse_i * offset)
    + 64    _, calculated_top_ylim = _get_ylimits(sdata)
    + 65    top_ylim = calculated_top_ylim if not hasattr(sdata, 'plotting') or sdata.plotting.ylim[1] is None else sdata.plotting.ylim[1]
    + 66
    + 67    if hasattr(sdata, 'plotting') and hasattr(sdata.plotting, 'legend'):
    + 68        if sdata.plotting.legend == False:
    + 69            for df in sdata.dfs:
    + 70                df.plot(x=df.columns[0], y=df.columns[1], ax=ax)
    + 71        elif sdata.plotting.legend != None:
    + 72            if len(sdata.plotting.legend) == len(sdata.dfs):
    + 73                for i, df in enumerate(sdata.dfs):
    + 74                    if sdata.plotting.legend[i] == False:
    + 75                        continue  # Skip plots with False in the legend
    + 76                    clean_name = sdata.plotting.legend[i]
    + 77                    df.plot(x=df.columns[0], y=df.columns[1], label=clean_name, ax=ax)
    + 78            elif len(sdata.plotting.legend) == 1:
    + 79                clean_name = sdata.plotting.legend[0]
    + 80                for i, df in enumerate(sdata.dfs):
    + 81                    df.plot(x=df.columns[0], y=df.columns[1], label=clean_name, ax=ax)
    + 82        elif sdata.plotting.legend == None and len(sdata.files) == len(sdata.dfs):
    + 83            for df, name in zip(sdata.dfs, sdata.files):
    + 84                clean_name = name
    + 85                for string in strings_to_delete_from_name:
    + 86                    clean_name = clean_name.replace(string, '')
    + 87                clean_name = clean_name.replace('_', ' ')
    + 88                df.plot(x=df.columns[0], y=df.columns[1], label=clean_name, ax=ax)
    + 89
    + 90    plt.title(title)
    + 91    plt.xlabel(xlabel)
    + 92    plt.ylabel(ylabel)
    + 93
    + 94    add_top = 0
    + 95    add_low = 0
    + 96    if hasattr(sdata, 'plotting'):
    + 97        add_low = sdata.plotting.margins[0]
    + 98        add_top = sdata.plotting.margins[1]
    + 99        if sdata.plotting.log_xscale:
    +100            ax.set_xscale('log')
    +101        if not sdata.plotting.show_yticks:
    +102            ax.set_yticks([])
    +103        if sdata.plotting.legend != False:
    +104            ax.legend(title=sdata.plotting.legend_title, fontsize=sdata.plotting.legend_size, loc=sdata.plotting.legend_loc)
    +105        else:
    +106            ax.legend().set_visible(False)
    +107    
    +108    low_ylim = low_ylim - add_low
    +109    top_ylim = top_ylim + add_top
    +110
    +111    ax.set_ylim(bottom=low_ylim)
    +112    ax.set_ylim(top=top_ylim)
    +113    ax.set_xlim(left=low_xlim)
    +114    ax.set_xlim(right=top_xlim)
    +115
    +116    if hasattr(sdata, 'plotting') and sdata.plotting.vline is not None and sdata.plotting.vline_error is not None:
    +117        for vline, vline_error in zip(sdata.plotting.vline, sdata.plotting.vline_error):
    +118            lower_bound = vline - vline_error
    +119            upper_bound = vline + vline_error
    +120            ax.fill_between([lower_bound, upper_bound], low_ylim, top_ylim, color='gray', alpha=0.1)
    +121    elif hasattr(sdata, 'plotting') and sdata.plotting.vline is not None:
    +122        for vline in sdata.plotting.vline:
    +123            ax.axvline(x=vline, color='gray', alpha=0.5, linestyle='--')
    +124
    +125    if hasattr(sdata, 'plotting') and sdata.plotting.save_as:
    +126        root = os.getcwd()
    +127        save_name = os.path.join(root, sdata.plotting.save_as)
    +128        plt.savefig(save_name)
    +129    
    +130    plt.show()
    +131
    +132
    +133def _get_ylimits(spectrum:Spectra) -> tuple[float, float]:
    +134    all_y_values = []
    +135    for df in spectrum.dfs:
    +136        df_trim = df
    +137        if hasattr(spectrum, 'plotting') and spectrum.plotting.xlim[0] is not None:
    +138            df_trim = df_trim[(df_trim[df_trim.columns[0]] >= spectrum.plotting.xlim[0])]
    +139        if hasattr(spectrum, 'plotting') and spectrum.plotting.xlim[1] is not None:
    +140            df_trim = df_trim[(df_trim[df_trim.columns[0]] <= spectrum.plotting.xlim[1])]
    +141        all_y_values.extend(df_trim[df_trim.columns[1]].tolist())
    +142    calculated_low_ylim = min(all_y_values)
    +143    calculated_top_ylim = max(all_y_values)
    +144
    +145    ymax_on_range = None
    +146    if hasattr(spectrum, 'scaling') and spectrum.scaling is not None:
    +147        df_index = spectrum.scaling.index if spectrum.scaling.index else 0
    +148        df0 = spectrum.dfs[df_index]
    +149        if spectrum.scaling.xmin:
    +150            df0 = df0[(df0[df0.columns[0]] >= spectrum.scaling.xmin)]
    +151        if spectrum.scaling.xmax:
    +152            df0 = df0[(df0[df0.columns[0]] <= spectrum.scaling.xmax)]
    +153        ymax_on_range = df0[df0.columns[1]].max()
    +154        if spectrum.scaling.zoom and ymax_on_range is not None:
    +155            calculated_top_ylim = ymax_on_range
    +156
    +157    return calculated_low_ylim, calculated_top_ylim
    +
    + + +
    +
    + +
    + + def + plot(spectrum: aton.spectra.classes.Spectra): + + + +
    + +
     16def plot(spectrum:Spectra):
    + 17    '''
    + 18    Plot the given spectra, with optional `maatpy.classes.Plotting` and `maatpy.classes.Scaling` attributes.
    + 19    '''
    + 20
    + 21    strings_to_delete_from_name = ['.csv', '.dat', '.txt', '_INS', '_ATR', '_FTIR', '_temp', '_RAMAN', '_Raman', '/data/', 'data/', '/csv/', 'csv/', '/INS/', 'INS/', '/FTIR/', 'FTIR/', '/ATR/', 'ATR/', '_smooth', '_smoothed', '_subtracted', '_cellsubtracted']
    + 22    normalize_area_keys = alias.parameters['area']
    + 23    normalize_height_keys = alias.parameters['height']
    + 24    normalize_height_keys.extend(alias.boolean[True])
    + 25
    + 26    sdata = deepcopy(spectrum)
    + 27
    + 28    if hasattr(sdata, 'plotting') and sdata.plotting.figsize:
    + 29        fig, ax = plt.subplots(figsize=sdata.plotting.figsize)
    + 30    else:
    + 31        fig, ax = plt.subplots()
    + 32
    + 33    if sdata.plotting.normalize in normalize_height_keys:
    + 34        sdata = normalize.spectra(sdata)
    + 35    elif sdata.plotting.normalize in normalize_area_keys:
    + 36        sdata = normalize.area(sdata)
    + 37
    + 38    calculated_low_ylim, calculated_top_ylim = _get_ylimits(sdata)
    + 39
    + 40    low_ylim = calculated_low_ylim if not hasattr(sdata, 'plotting') or sdata.plotting.ylim[0] is None else sdata.plotting.ylim[0]
    + 41    top_ylim = calculated_top_ylim if not hasattr(sdata, 'plotting') or sdata.plotting.ylim[1] is None else sdata.plotting.ylim[1]
    + 42    
    + 43    low_xlim = None
    + 44    top_xlim = None
    + 45    if getattr(sdata, 'plotting', None) is not None:
    + 46        title = sdata.plotting.title
    + 47        low_xlim = sdata.plotting.xlim[0]
    + 48        top_xlim = sdata.plotting.xlim[1]
    + 49        xlabel = sdata.plotting.xlabel if sdata.plotting.xlabel is not None else sdata.dfs[0].columns[0]
    + 50        ylabel = sdata.plotting.ylabel if sdata.plotting.ylabel is not None else sdata.dfs[0].columns[1]
    + 51    else:
    + 52        title = sdata.comment
    + 53
    + 54    number_of_plots = len(sdata.dfs)
    + 55    height = top_ylim - low_ylim
    + 56    if hasattr(sdata, 'plotting') and sdata.plotting.offset is True:
    + 57        for i, df in enumerate(sdata.dfs):
    + 58            reverse_i = (number_of_plots - 1) - i
    + 59            df[df.columns[1]] = df[df.columns[1]] + (reverse_i * height)
    + 60    elif hasattr(sdata, 'plotting') and (isinstance(sdata.plotting.offset, float) or isinstance(sdata.plotting.offset, int)):
    + 61        offset = sdata.plotting.offset
    + 62        for i, df in enumerate(sdata.dfs):
    + 63            reverse_i = (number_of_plots - 1) - i
    + 64            df[df.columns[1]] = df[df.columns[1]] + (reverse_i * offset)
    + 65    _, calculated_top_ylim = _get_ylimits(sdata)
    + 66    top_ylim = calculated_top_ylim if not hasattr(sdata, 'plotting') or sdata.plotting.ylim[1] is None else sdata.plotting.ylim[1]
    + 67
    + 68    if hasattr(sdata, 'plotting') and hasattr(sdata.plotting, 'legend'):
    + 69        if sdata.plotting.legend == False:
    + 70            for df in sdata.dfs:
    + 71                df.plot(x=df.columns[0], y=df.columns[1], ax=ax)
    + 72        elif sdata.plotting.legend != None:
    + 73            if len(sdata.plotting.legend) == len(sdata.dfs):
    + 74                for i, df in enumerate(sdata.dfs):
    + 75                    if sdata.plotting.legend[i] == False:
    + 76                        continue  # Skip plots with False in the legend
    + 77                    clean_name = sdata.plotting.legend[i]
    + 78                    df.plot(x=df.columns[0], y=df.columns[1], label=clean_name, ax=ax)
    + 79            elif len(sdata.plotting.legend) == 1:
    + 80                clean_name = sdata.plotting.legend[0]
    + 81                for i, df in enumerate(sdata.dfs):
    + 82                    df.plot(x=df.columns[0], y=df.columns[1], label=clean_name, ax=ax)
    + 83        elif sdata.plotting.legend == None and len(sdata.files) == len(sdata.dfs):
    + 84            for df, name in zip(sdata.dfs, sdata.files):
    + 85                clean_name = name
    + 86                for string in strings_to_delete_from_name:
    + 87                    clean_name = clean_name.replace(string, '')
    + 88                clean_name = clean_name.replace('_', ' ')
    + 89                df.plot(x=df.columns[0], y=df.columns[1], label=clean_name, ax=ax)
    + 90
    + 91    plt.title(title)
    + 92    plt.xlabel(xlabel)
    + 93    plt.ylabel(ylabel)
    + 94
    + 95    add_top = 0
    + 96    add_low = 0
    + 97    if hasattr(sdata, 'plotting'):
    + 98        add_low = sdata.plotting.margins[0]
    + 99        add_top = sdata.plotting.margins[1]
    +100        if sdata.plotting.log_xscale:
    +101            ax.set_xscale('log')
    +102        if not sdata.plotting.show_yticks:
    +103            ax.set_yticks([])
    +104        if sdata.plotting.legend != False:
    +105            ax.legend(title=sdata.plotting.legend_title, fontsize=sdata.plotting.legend_size, loc=sdata.plotting.legend_loc)
    +106        else:
    +107            ax.legend().set_visible(False)
    +108    
    +109    low_ylim = low_ylim - add_low
    +110    top_ylim = top_ylim + add_top
    +111
    +112    ax.set_ylim(bottom=low_ylim)
    +113    ax.set_ylim(top=top_ylim)
    +114    ax.set_xlim(left=low_xlim)
    +115    ax.set_xlim(right=top_xlim)
    +116
    +117    if hasattr(sdata, 'plotting') and sdata.plotting.vline is not None and sdata.plotting.vline_error is not None:
    +118        for vline, vline_error in zip(sdata.plotting.vline, sdata.plotting.vline_error):
    +119            lower_bound = vline - vline_error
    +120            upper_bound = vline + vline_error
    +121            ax.fill_between([lower_bound, upper_bound], low_ylim, top_ylim, color='gray', alpha=0.1)
    +122    elif hasattr(sdata, 'plotting') and sdata.plotting.vline is not None:
    +123        for vline in sdata.plotting.vline:
    +124            ax.axvline(x=vline, color='gray', alpha=0.5, linestyle='--')
    +125
    +126    if hasattr(sdata, 'plotting') and sdata.plotting.save_as:
    +127        root = os.getcwd()
    +128        save_name = os.path.join(root, sdata.plotting.save_as)
    +129        plt.savefig(save_name)
    +130    
    +131    plt.show()
    +
    + + +

    Plot the given spectra, with optional maatpy.classes.Plotting and maatpy.classes.Scaling attributes.

    +
    + + +
    +
    + + \ No newline at end of file diff --git a/docs/aton/spectra/samples.html b/docs/aton/spectra/samples.html new file mode 100644 index 0000000..693167b --- /dev/null +++ b/docs/aton/spectra/samples.html @@ -0,0 +1,467 @@ + + + + + + + aton.spectra.samples API documentation + + + + + + + + + + + + + +
    +
    +

    +aton.spectra.samples

    + +

    Description

    + +

    This module contains premade examples of material compositions. +The aton.spectra.Material.grams is yet to be provided, +before setting the material with aton.spectra.Material.set().

    + +
    +
    + + + + + +
     1'''
    + 2# Description
    + 3This module contains premade examples of material compositions.
    + 4The `aton.spectra.Material.grams` is yet to be provided,
    + 5before setting the material with `aton.spectra.Material.set()`.
    + 6
    + 7---
    + 8'''
    + 9
    +10
    +11from .classes import Material
    +12
    +13
    +14MAPbI3 = Material(
    +15    elements={'Pb': 1, 'I': 3, 'C': 1, 'N': 1, 'H': 6},
    +16    name='MAPbI3'
    +17    )
    +18'''CH$_3$NH$_3$PbI$_3$'''
    +19
    +20
    +21CD3ND3PbI3 = Material(
    +22    elements={'Pb': 1, 'I': 3, 'C': 1, 'N': 1, 'H2': 6},
    +23    name='CD3ND3PbI3',
    +24    peaks = {
    +25        'baseline'       : None,
    +26        'baseline_error' : None,
    +27        'h6d0'           : [36.0, 39.0],
    +28        'h5d1'           : [33.0, 35.0],
    +29        'h4d2'           : [30.7, 33.0],
    +30        'h3d3'           : [28.8, 30.7],
    +31    }
    +32)
    +33'''CD$_3$ND$_3$PbI$_3$.
    +34With experimental values of the partially-deuterated amine peaks
    +35for the disrotatory mode of MAPbI3's methylammonium.
    +36Measured at TOSCA, ISIS RAL, UK, May 2024.
    +37'''
    +38
    +39
    +40CH3ND3PbI3 = Material(
    +41    elements={'Pb': 1, 'I': 3, 'C': 1, 'N': 1, 'H': 3, 'H2': 3},
    +42    name='CH3ND3PbI3'
    +43)
    +44'''CH$_3$ND$_3$PbI$_3$'''
    +45#MAPI_ND.set()
    +46
    +47
    +48CD3NH3PbI3 = Material(
    +49    elements={'Pb': 1, 'I': 3, 'C': 1, 'N': 1, 'H': 3, 'H2': 3},
    +50    name='CD3NH3PbI3'
    +51)
    +52'''CD$_3$NH$_3$PbI$_3$'''
    +53#MAPI_CD.set()
    +54
    +55
    +56CH3NH3I = Material(
    +57    elements={'C' : 1, 'N': 1, 'H': 6},
    +58    name='CH3NH3'
    +59)
    +60'''CH$_3$NH$_3$I'''
    +61#CH3NH3I.set()
    +62
    +63
    +64CH3ND3I = Material(
    +65    elements={'C' : 1, 'N': 1, 'H': 3, 'H2': 3},
    +66    name='CH3ND3'
    +67)
    +68'''CH$_3$ND$_3$I'''
    +69#CH3ND3I.set()
    +
    + + +
    +
    +
    + MAPbI3 = +<aton.spectra.classes.Material object> + + +
    + + +

    CH$_3$NH$_3$PbI$_3$

    +
    + + +
    +
    +
    + CD3ND3PbI3 = +<aton.spectra.classes.Material object> + + +
    + + +

    CD$_3$ND$_3$PbI$_3$. +With experimental values of the partially-deuterated amine peaks +for the disrotatory mode of MAPbI3's methylammonium. +Measured at TOSCA, ISIS RAL, UK, May 2024.

    +
    + + +
    +
    +
    + CH3ND3PbI3 = +<aton.spectra.classes.Material object> + + +
    + + +

    CH$_3$ND$_3$PbI$_3$

    +
    + + +
    +
    +
    + CD3NH3PbI3 = +<aton.spectra.classes.Material object> + + +
    + + +

    CD$_3$NH$_3$PbI$_3$

    +
    + + +
    +
    +
    + CH3NH3I = +<aton.spectra.classes.Material object> + + +
    + + +

    CH$_3$NH$_3$I

    +
    + + +
    +
    +
    + CH3ND3I = +<aton.spectra.classes.Material object> + + +
    + + +

    CH$_3$ND$_3$I

    +
    + + +
    +
    + + \ No newline at end of file diff --git a/docs/aton/text.html b/docs/aton/text.html index 2774e7a..4608150 100644 --- a/docs/aton/text.html +++ b/docs/aton/text.html @@ -79,7 +79,7 @@

    Submodules

    - + built with pdocAPI Documentation -
    Aton v0.0.1a3 documentation
    +
    Aton v0.0.1b1 documentation
    built with pdocAPI Documentation -
    Aton v0.0.1a3 documentation
    +
    Aton v0.0.1b1 documentation
    built with pdocAPI Documentation -
    Aton v0.0.1a3 documentation
    +
    Aton v0.0.1b1 documentation
    built with pdocAPI Documentation -
    Aton v0.0.1a3 documentation
    +
    Aton v0.0.1b1 documentation
    built with pdoco;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;u0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();oWelcome to the Ab-iniTiO and Neutron research toolbox, or Aton.\nJust like its ancient Egyptian deity counterpart, this is an all-in-one Python package with powerful and comprehensive tools for cutting-edge materials research.

    \n\n

    Aton allows you to easily create, edit and analyse all kinds of text files, with a special focus on ab-initio calculations.\nIn particular, it contains interfaces for Quantum ESPRESSO, Phonopy and CASTEP.

    \n\n

    This is combined with a range of spectral analysis tools, focused on (but not limited to) neutron science.\nA set of physico-chemical constants and definitions is also included.

    \n\n
    \n\n

    Installation

    \n\n

    As always, it is recommended to install your packages in a virtual environment:

    \n\n
    \n
    python3 -m venv .venv\nsource .venv/bin/activate\n
    \n
    \n\n

    With pip

    \n\n

    The fastest way to install Aton is through pip:

    \n\n
    \n
    pip install aton\n
    \n
    \n\n

    From source

    \n\n

    Optionally, you can install Aton from the GitHub repository.

    \n\n

    First install the dependencies:

    \n\n
    \n
    pip install pandas numpy scipy\n
    \n
    \n\n

    Then clone the repository or download the latest stable release as a ZIP, unzip it, and run inside the Aton/ directory:

    \n\n
    \n
    pip install .\n
    \n
    \n\n
    \n\n

    Documentation

    \n\n

    Check the full Aton documentation online.
    \nAn offline version of the documentation is available in docs/aton.html.
    \nCode examples are included in the examples/ folder.

    \n\n

    Submodules

    \n\n

    Aton contains the following modules:

    \n\n\n\n

    General text edition

    \n\n

    The aton.text module includes the following general text-related submodules:

    \n\n\n\n

    Interfaces for ab-initio codes

    \n\n

    The aton.interface module contains interfaces for several ab-initio codes. These are powered by the aton.text module and can be easily extended. The following interfaces are included:

    \n\n\n\n

    Spectral analysis tools

    \n\n

    The aton.spectra module IS YET TO BE IMPLEMENTED.

    \n\n\n\n
    \n\n

    Contributing

    \n\n

    If you are interested in opening an issue or a pull request, please feel free to do so on GitHub.
    \nFor major changes, please get in touch first to discuss the details.

    \n\n

    Code style

    \n\n

    Please try to follow some general guidelines:

    \n\n\n\n

    Testing with PyTest

    \n\n

    If you are modifying the source code, you should run the automated tests of the tests/ folder to check that everything works as intended.\nTo do so, first install PyTest in your environment,

    \n\n
    \n
    pip install pytest\n
    \n
    \n\n

    And then run PyTest inside the Aton/ directory,

    \n\n
    \n
    pytest -vv\n
    \n
    \n\n

    Compiling the documentation

    \n\n

    The documentation can be compiled automatically to docs/aton.html with pdoc and Aton itself, by running:

    \n\n
    \n
    python3 makedocs.py\n
    \n
    \n\n
    \n\n

    License

    \n\n

    Copyright (C) 2024 Pablo Gila-Herranz
    \nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU Affero General Public License as published\nby the Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.
    \nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    \nSee the attached GNU Affero General Public License for more details.

    \n"}, {"fullname": "aton.alias", "modulename": "aton.alias", "kind": "module", "doc": "

    Description

    \n\n

    This module contains common dictionaries to normalise and correct user inputs.\nAll values are in lowercase to allow comparison with the string.lower() method.

    \n\n

    Use example:

    \n\n
    \n
    unit = 'Electronvolts'\nif unit.lower() in aton.alias.units['eV']:\n    ... do stuff ...\n
    \n
    \n\n

    Index

    \n\n\n\n
    \n"}, {"fullname": "aton.alias.units", "modulename": "aton.alias", "qualname": "units", "kind": "variable", "doc": "

    Dict with unit names.

    \n", "annotation": ": dict", "default_value": "{'mol': ['mol', 'mols', 'mole', 'moles'], 'g': ['g', 'gram', 'grams'], 'kg': ['kg', 'kilogram', 'kilograms'], 'amu': ['amu', 'atomicmassunit', 'atomicmassunits'], 'eV': ['ev', 'electronvolt', 'electronvolts'], 'meV': ['mev', 'millielectronvolt', 'millielectronvolts'], 'J': ['j', 'joule', 'joules'], 'cal': ['cal', 'calorie', 'calories'], 'kcal': ['kcal', 'kilocalorie', 'kilocalories'], 'Ry': ['ry', 'rydberg', 'rydbergs'], 'cm-1': ['cm^{-1}', 'cm1', 'cm-1', 'cm^-1'], 'cm': ['cm', 'centimeter', 'centimeters'], 'A': ['a', 'aa', 'angstrom', 'angstroms', 'armstrong', 'armstrongs'], 'bohr': ['bohr', 'bohrs', 'bohrradii'], 'm': ['m', 'meter', 'meters'], 'deg': ['deg', 'degree', 'degrees'], 'rad': ['rad', 'radian', 'radians'], 'bar': ['bar', 'bars'], 'kbar': ['kbar', 'kilobar', 'kilobars'], 'Pa': ['pa', 'pascal', 'pascals'], 'GPa': ['gpa', 'gigapascal', 'gigapascals'], 's': ['s', 'second', 'seconds'], 'H': ['h', 'hour', 'hours']}"}, {"fullname": "aton.alias.parameters", "modulename": "aton.alias", "qualname": "parameters", "kind": "variable", "doc": "

    Dict with different parameters.

    \n", "default_value": "{'height': ['height', 'h'], 'area': ['area', 'a']}"}, {"fullname": "aton.alias.experiments", "modulename": "aton.alias", "qualname": "experiments", "kind": "variable", "doc": "

    Dictionary with the available experiment types.

    \n", "annotation": ": dict", "default_value": "{'ins': ['ins', 'inelasticneutronscattering', 'inelastic neutron scattering'], 'atr': ['atr', 'ftir', 'attenuatedtotalreflection', 'attenuated total reflection'], 'raman': ['raman'], 'qens': ['qens', 'quasielasticneutronscattering', 'quasielastic neutron scattering', 'quasi elastic neutron scattering']}"}, {"fullname": "aton.alias.files", "modulename": "aton.alias", "qualname": "files", "kind": "variable", "doc": "

    Strings related to files.

    \n", "default_value": "{'file': ['file', 'files', 'f', 'filepath', 'file path', 'filename', 'file name'], 'dir': ['dir', 'directory', 'd', 'folder'], 'error': ['Error', 'error', 'ERROR', 'Errors', 'errors', 'ERRORS']}"}, {"fullname": "aton.alias.boolean", "modulename": "aton.alias", "qualname": "boolean", "kind": "variable", "doc": "

    Strings with booleans such as 'yes' / 'no'.

    \n", "default_value": "{True: ['yes', 'YES', 'Yes', 'Y', 'y', 'T', 'True', 'TRUE', 't', 'true', True, 'Si', 'SI', 'si', 'S', 's'], False: ['no', 'NO', 'No', 'N', 'n', 'F', 'False', 'FALSE', 'f', 'false', False]}"}, {"fullname": "aton.atoms", "modulename": "aton.atoms", "kind": "module", "doc": "

    Description

    \n\n

    This module contains the atoms megadictionary,\nwhich contains the properties of all elements.\nIt is managed and updated automatically with aton.elements,\nwhich also contains the literature references for this data.

    \n\n

    The atoms dictionary can be loaded directly as aton.atoms.\nUse example:

    \n\n
    \n
    aluminium_cross_section = aton.atoms['Al'].cross_section  # 1.503\nHe4_mass = aton.atoms['H'].isotope[4].mass  # 4.0026032497\n
    \n
    \n\n
    \n"}, {"fullname": "aton.atoms.atoms", "modulename": "aton.atoms", "qualname": "atoms", "kind": "variable", "doc": "

    \n", "default_value": "{'H': <aton.elements.Element object>, 'He': <aton.elements.Element object>, 'Li': <aton.elements.Element object>, 'Be': <aton.elements.Element object>, 'B': <aton.elements.Element object>, 'C': <aton.elements.Element object>, 'N': <aton.elements.Element object>, 'O': <aton.elements.Element object>, 'F': <aton.elements.Element object>, 'Ne': <aton.elements.Element object>, 'Na': <aton.elements.Element object>, 'Mg': <aton.elements.Element object>, 'Al': <aton.elements.Element object>, 'Si': <aton.elements.Element object>, 'P': <aton.elements.Element object>, 'S': <aton.elements.Element object>, 'Cl': <aton.elements.Element object>, 'Ar': <aton.elements.Element object>, 'K': <aton.elements.Element object>, 'Ca': <aton.elements.Element object>, 'Sc': <aton.elements.Element object>, 'Ti': <aton.elements.Element object>, 'V': <aton.elements.Element object>, 'Cr': <aton.elements.Element object>, 'Mn': <aton.elements.Element object>, 'Fe': <aton.elements.Element object>, 'Co': <aton.elements.Element object>, 'Ni': <aton.elements.Element object>, 'Cu': <aton.elements.Element object>, 'Zn': <aton.elements.Element object>, 'Ga': <aton.elements.Element object>, 'Ge': <aton.elements.Element object>, 'As': <aton.elements.Element object>, 'Se': <aton.elements.Element object>, 'Br': <aton.elements.Element object>, 'Kr': <aton.elements.Element object>, 'Rb': <aton.elements.Element object>, 'Sr': <aton.elements.Element object>, 'Y': <aton.elements.Element object>, 'Zr': <aton.elements.Element object>, 'Nb': <aton.elements.Element object>, 'Mo': <aton.elements.Element object>, 'Tc': <aton.elements.Element object>, 'Ru': <aton.elements.Element object>, 'Rh': <aton.elements.Element object>, 'Pd': <aton.elements.Element object>, 'Ag': <aton.elements.Element object>, 'Cd': <aton.elements.Element object>, 'In': <aton.elements.Element object>, 'Sn': <aton.elements.Element object>, 'Sb': <aton.elements.Element object>, 'Te': <aton.elements.Element object>, 'I': <aton.elements.Element object>, 'Xe': <aton.elements.Element object>, 'Cs': <aton.elements.Element object>, 'Ba': <aton.elements.Element object>, 'La': <aton.elements.Element object>, 'Ce': <aton.elements.Element object>, 'Pr': <aton.elements.Element object>, 'Nd': <aton.elements.Element object>, 'Pm': <aton.elements.Element object>, 'Sm': <aton.elements.Element object>, 'Eu': <aton.elements.Element object>, 'Gd': <aton.elements.Element object>, 'Tb': <aton.elements.Element object>, 'Dy': <aton.elements.Element object>, 'Ho': <aton.elements.Element object>, 'Er': <aton.elements.Element object>, 'Tm': <aton.elements.Element object>, 'Yb': <aton.elements.Element object>, 'Lu': <aton.elements.Element object>, 'Hf': <aton.elements.Element object>, 'Ta': <aton.elements.Element object>, 'W': <aton.elements.Element object>, 'Re': <aton.elements.Element object>, 'Os': <aton.elements.Element object>, 'Ir': <aton.elements.Element object>, 'Pt': <aton.elements.Element object>, 'Au': <aton.elements.Element object>, 'Hg': <aton.elements.Element object>, 'Tl': <aton.elements.Element object>, 'Pb': <aton.elements.Element object>, 'Bi': <aton.elements.Element object>, 'Po': <aton.elements.Element object>, 'At': <aton.elements.Element object>, 'Rn': <aton.elements.Element object>, 'Fr': <aton.elements.Element object>, 'Ra': <aton.elements.Element object>, 'Ac': <aton.elements.Element object>, 'Th': <aton.elements.Element object>, 'Pa': <aton.elements.Element object>, 'U': <aton.elements.Element object>, 'Np': <aton.elements.Element object>, 'Pu': <aton.elements.Element object>, 'Am': <aton.elements.Element object>, 'Cm': <aton.elements.Element object>, 'Bk': <aton.elements.Element object>, 'Cf': <aton.elements.Element object>, 'Es': <aton.elements.Element object>, 'Fm': <aton.elements.Element object>, 'Md': <aton.elements.Element object>, 'No': <aton.elements.Element object>, 'Lr': <aton.elements.Element object>, 'Rf': <aton.elements.Element object>, 'Db': <aton.elements.Element object>, 'Sg': <aton.elements.Element object>, 'Bh': <aton.elements.Element object>, 'Hs': <aton.elements.Element object>, 'Mt': <aton.elements.Element object>, 'Ds': <aton.elements.Element object>, 'Rg': <aton.elements.Element object>, 'Cn': <aton.elements.Element object>, 'Uut': <aton.elements.Element object>, 'Uuq': <aton.elements.Element object>, 'Uup': <aton.elements.Element object>, 'Uuh': <aton.elements.Element object>, 'Uus': <aton.elements.Element object>, 'Uuo': <aton.elements.Element object>}"}, {"fullname": "aton.call", "modulename": "aton.call", "kind": "module", "doc": "

    Description

    \n\n

    Functions to handle bash calls and related operations on Linux systems.

    \n\n

    Index

    \n\n\n\n
    \n"}, {"fullname": "aton.call.bash", "modulename": "aton.call", "qualname": "bash", "kind": "function", "doc": "

    Run a bash shell command, inside an optional cwd directory.\nIf empty, the current working directory will be used.\nPrints the running command and outputs by default, override this with verbose=False.\nReturns the result of the command used, except for when\nerrors are raised automatically; set return_anyway=True to override this.

    \n", "signature": "(\tcommand: str,\tcwd=None,\tverbose: bool = True,\treturn_anyway: bool = False):", "funcdef": "def"}, {"fullname": "aton.call.git", "modulename": "aton.call", "qualname": "git", "kind": "function", "doc": "

    Automatically update a Git repository.

    \n", "signature": "(path=None, verbose=True, message=None, tag=None) -> None:", "funcdef": "def"}, {"fullname": "aton.call.here", "modulename": "aton.call", "qualname": "here", "kind": "function", "doc": "

    Runs the rest of the script inside the specified folder.\nIf none is provided, it runs from the same directory where the current script lies.\nThis is really useful to run scripts from the VSCode terminal, etc.\nReturns the path of the used folder, or the path of the script if folder is not provided.

    \n\n

    Note that this changes not only the working directory of your script,\nbut also of other scripts that import and run your script.

    \n", "signature": "(folder=None) -> str:", "funcdef": "def"}, {"fullname": "aton.elements", "modulename": "aton.elements", "kind": "module", "doc": "

    Description

    \n\n

    This module contains functions to sort and analyse element data\nfrom the aton.atoms megadictionary, which contains the properties of all elements.\nIt also contains the tools needed to automatically update said megadictionary.

    \n\n

    Index

    \n\n\n\n

    References

    \n\n

    Atomic mass are in atomic mass units (amu), and come from:\nPure Appl. Chem., Vol. 78, No. 11, pp. 2051-2066, 2006.\nThe following masses are obtained from Wikipedia:\nAc: 227, Np: 237, Pm: 145, Tc: 98

    \n\n

    Isotope mass, mass_number and abundance come from:\nJ. R. de Laeter, J. K. B\u00f6hlke, P. De Bi\u00e8vre, H. Hidaka, H. S. Peiser, K. J. R. Rosman\nand P. D. P. Taylor (2003). 'Atomic weights of the elements. Review 2000 (IUPAC Technical Report)'

    \n\n

    Total bound scattering cross_section $\\sigma_s$ are in barns (1 b = 100 fm$^2$).\nFrom Felix Fernandez-Alonso's book 'Neutron Scattering Fundamentals' (2013).

    \n\n
    \n"}, {"fullname": "aton.elements.Element", "modulename": "aton.elements", "qualname": "Element", "kind": "class", "doc": "

    Used in the aton.atoms megadictionary to store element data.

    \n"}, {"fullname": "aton.elements.Element.__init__", "modulename": "aton.elements", "qualname": "Element.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tZ: int = None,\tsymbol: str = None,\tname: str = None,\tmass: float = None,\tcross_section: float = None,\tisotope: dict = None)"}, {"fullname": "aton.elements.Element.Z", "modulename": "aton.elements", "qualname": "Element.Z", "kind": "variable", "doc": "

    Atomic number (Z). Corresponds to the number of protons / electrons.

    \n", "annotation": ": int"}, {"fullname": "aton.elements.Element.symbol", "modulename": "aton.elements", "qualname": "Element.symbol", "kind": "variable", "doc": "

    Standard symbol of the element.

    \n", "annotation": ": str"}, {"fullname": "aton.elements.Element.name", "modulename": "aton.elements", "qualname": "Element.name", "kind": "variable", "doc": "

    Full name.

    \n", "annotation": ": str"}, {"fullname": "aton.elements.Element.mass", "modulename": "aton.elements", "qualname": "Element.mass", "kind": "variable", "doc": "

    Atomic mass, in atomic mass units (amu).

    \n", "annotation": ": float"}, {"fullname": "aton.elements.Element.cross_section", "modulename": "aton.elements", "qualname": "Element.cross_section", "kind": "variable", "doc": "

    Total bound scattering cross section.

    \n", "annotation": ": float"}, {"fullname": "aton.elements.Element.isotope", "modulename": "aton.elements", "qualname": "Element.isotope", "kind": "variable", "doc": "

    Dictionary containing the different Isotope of the element. The keys are the mass number (A).

    \n", "annotation": ": dict"}, {"fullname": "aton.elements.Isotope", "modulename": "aton.elements", "qualname": "Isotope", "kind": "class", "doc": "

    Used in the aton.atoms megadictionary to store isotope data.

    \n"}, {"fullname": "aton.elements.Isotope.__init__", "modulename": "aton.elements", "qualname": "Isotope.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tA: int = None,\tmass: float = None,\tabundance: float = None,\tcross_section: float = None)"}, {"fullname": "aton.elements.Isotope.A", "modulename": "aton.elements", "qualname": "Isotope.A", "kind": "variable", "doc": "

    Mass number (A) of the isotope. Corresponds to the total number of protons + neutrons in the core.

    \n", "annotation": ": int"}, {"fullname": "aton.elements.Isotope.mass", "modulename": "aton.elements", "qualname": "Isotope.mass", "kind": "variable", "doc": "

    Atomic mass of the isotope, in atomic mass units (amu).

    \n", "annotation": ": float"}, {"fullname": "aton.elements.Isotope.abundance", "modulename": "aton.elements", "qualname": "Isotope.abundance", "kind": "variable", "doc": "

    Relative abundance of the isotope.

    \n", "annotation": ": float"}, {"fullname": "aton.elements.Isotope.cross_section", "modulename": "aton.elements", "qualname": "Isotope.cross_section", "kind": "variable", "doc": "

    Total bound scattering cross section of the isotope.

    \n", "annotation": ": float"}, {"fullname": "aton.elements.export_atoms", "modulename": "aton.elements", "qualname": "export_atoms", "kind": "function", "doc": "

    Export a dictionary of chemical elements to a python file.

    \n\n

    This is used to build and update the aton.atoms megadictionary, that contains\nall the element data, such as masses, cross-sections, etc.

    \n", "signature": "(atoms: dict, filename='exported_atoms.py') -> None:", "funcdef": "def"}, {"fullname": "aton.elements.split_isotope", "modulename": "aton.elements", "qualname": "split_isotope", "kind": "function", "doc": "

    Split the name of an isotope into the element and the mass number, eg. He4 -> He, 4.

    \n\n

    If the isotope is not found in the aton.atoms megadictionary it raises an error,\ninforming of the allowed mass numbers (A) values for the given element.

    \n", "signature": "(name: str) -> tuple:", "funcdef": "def"}, {"fullname": "aton.elements.allowed_isotopes", "modulename": "aton.elements", "qualname": "allowed_isotopes", "kind": "function", "doc": "

    Return a list with the allowed mass numbers (A) of a given element.

    \n\n

    These mass numbers are used as isotope keys in the aton.atoms megadictionary.

    \n", "signature": "(element) -> list:", "funcdef": "def"}, {"fullname": "aton.file", "modulename": "aton.file", "kind": "module", "doc": "

    Description

    \n\n

    Functions to move files around.

    \n\n

    Index

    \n\n\n\n
    \n"}, {"fullname": "aton.file.get", "modulename": "aton.file", "qualname": "get", "kind": "function", "doc": "

    Check if the given filepath exists in the currrent working directory\nor in the full path, and returns its full path as a string.

    \n\n

    Raises an error if the file is not found, unless return_anyway=True,\nin which case it returns None. This can be used to personalize errors.

    \n\n

    If the provided string is a directory, it checks the files inside it.\nif there is only one file inside, it returns said file;\nif there are more files, it tries to filter them with the filters keyword(s) to return a single file.\nIf this fails, try using more strict filers to return a single file.

    \n", "signature": "(filepath, filters=None, return_anyway: bool = False) -> str:", "funcdef": "def"}, {"fullname": "aton.file.get_list", "modulename": "aton.file", "qualname": "get_list", "kind": "function", "doc": "

    Takes a folder, filters the content with the filters keyword(s) if provided, and returns a list with the matches.\nThe full paths are returned by default; to get only the base names, set abspath=False.

    \n", "signature": "(folder: str, filters=None, abspath: bool = True) -> list:", "funcdef": "def"}, {"fullname": "aton.file.copy", "modulename": "aton.file", "qualname": "copy", "kind": "function", "doc": "

    Copies the content of old file to new file with shutil,\nafter making sure that the file exists with thotpy.file.get().

    \n", "signature": "(old: str, new: str) -> None:", "funcdef": "def"}, {"fullname": "aton.file.move", "modulename": "aton.file", "qualname": "move", "kind": "function", "doc": "

    Moves old file to new file.

    \n", "signature": "(old: str, new: str) -> None:", "funcdef": "def"}, {"fullname": "aton.file.remove", "modulename": "aton.file", "qualname": "remove", "kind": "function", "doc": "

    Removes the given file or folder at filepath.

    \n\n
    \n

    WARNING: Removing stuff is always dangerous, be careful!

    \n
    \n", "signature": "(filepath: str) -> None:", "funcdef": "def"}, {"fullname": "aton.file.rename_on_folder", "modulename": "aton.file", "qualname": "rename_on_folder", "kind": "function", "doc": "

    Batch renames files in the given folder, replacing old string by new string.\nIf no folder is provided, the current working directory is used.

    \n", "signature": "(old: str, new: str, folder=None) -> None:", "funcdef": "def"}, {"fullname": "aton.file.rename_on_folders", "modulename": "aton.file", "qualname": "rename_on_folders", "kind": "function", "doc": "

    Renames the files inside the subfolders in the parent folder,\nfrom an old string to the new string.\nIf no folder is provided, the current working directory is used.

    \n", "signature": "(old: str, new: str, folder=None) -> None:", "funcdef": "def"}, {"fullname": "aton.file.copy_to_folders", "modulename": "aton.file", "qualname": "copy_to_folders", "kind": "function", "doc": "

    Copies the files from the parent folder with the given extension to individual subfolders.\nThe subfolders are named as the original files,\nremoving the strings from the strings_to_delete list.\nIf no folder is provided, it runs in the current working directory.

    \n", "signature": "(extension: str = None, strings_to_delete: list = [], folder=None) -> None:", "funcdef": "def"}, {"fullname": "aton.text", "modulename": "aton.text", "kind": "module", "doc": "

    Description

    \n\n

    This module contains tools for general text operations.

    \n\n

    Index

    \n\n\n"}, {"fullname": "aton.text.edit", "modulename": "aton.text.edit", "kind": "module", "doc": "

    Description

    \n\n

    Functions to manipulate the content of text files.

    \n\n

    Index

    \n\n\n\n
    \n"}, {"fullname": "aton.text.edit.insert_at", "modulename": "aton.text.edit", "qualname": "insert_at", "kind": "function", "doc": "

    Inserts a text in the line with position index of a given filepath.\nIf position is negative, starts from the end of the file.

    \n", "signature": "(filepath, text: str, position: int) -> None:", "funcdef": "def"}, {"fullname": "aton.text.edit.insert_under", "modulename": "aton.text.edit", "qualname": "insert_under", "kind": "function", "doc": "

    Inserts the given text string under the line(s) containing\nthe key in the given filepath.\nThe keyword can be at any position within the line.\nBy default all matches are inserted with insertions=0,\nbut it can insert only a specific number of matches\nwith positive numbers (1, 2...), or starting from the bottom with negative numbers.\nThe text can be introduced after a specific number of lines after the match,\nchanging the value skips. Negative integers introduce the text in the previous lines.\nRegular expressions can be used by setting regex=True.

    \n", "signature": "(\tfilepath,\tkey: str,\ttext: str,\tinsertions: int = 0,\tskips: int = 0,\tregex: bool = False) -> None:", "funcdef": "def"}, {"fullname": "aton.text.edit.replace", "modulename": "aton.text.edit", "qualname": "replace", "kind": "function", "doc": "

    Replaces the key string with the text string in the specified filepath.\nTo search with regular expressions, set regex=True.

    \n\n

    It can also be used to delete the keyword with text=''.

    \n\n

    The value replacements specifies the number of replacements to perform:\n1 to replace only the first keyword found, 2, 3...\nUse negative values to replace from the end of the file,\neg. to replace the last found key, use replacements=-1.\nTo replace all values, set replacements = 0, which is the value by default.

    \n\n
    line... key ...line -> line... text ...line\n
    \n", "signature": "(\tfilepath: str,\tkey: str,\ttext: str,\treplacements: int = 0,\tregex: bool = False) -> None:", "funcdef": "def"}, {"fullname": "aton.text.edit.replace_line", "modulename": "aton.text.edit", "qualname": "replace_line", "kind": "function", "doc": "

    Replaces the entire line(s) containing the key string with the text string in the specified filepath.\nRegular expressions can be used with regex=True.

    \n\n

    It can be used to delete line(s) by setting text=''.

    \n\n

    The value replacements specifies the number of lines to replace:\n1 to replace only the first line with the keyword, 2, 3...\nUse negative values to replace from the end of the file,\ne.g., to replace only the last line containing the keyword, use replacements = -1.\nTo replace all lines, set replacements = 0, which is the value by default.

    \n\n

    The default line to replace is the matching line,\nbut it can be any other specific line after or before the matching line;\nthis is indicated with skips as a positive or negative integer.

    \n\n

    More lines can be replaced with additional lines (int).\nNote that the matched line plus the additional lines will be replaced, this is, additional lines +1.

    \n", "signature": "(\tfilepath: str,\tkey: str,\ttext: str,\treplacements: int = 0,\tskips: int = 0,\tadditional: int = 0,\tregex: bool = False) -> None:", "funcdef": "def"}, {"fullname": "aton.text.edit.replace_between", "modulename": "aton.text.edit", "qualname": "replace_between", "kind": "function", "doc": "

    Replace lines with a given text, between the keywords key1 and key2 in a specified filepath.\nRegular expressions can be used by setting regex=True.

    \n\n

    It can be used to delete the text between the keys by setting text=''.

    \n\n

    Key lines are also deleted if delete_keys=True.

    \n\n

    Only the first matches of the keywords are used by default;\nyou can use the last ones with from_end = True.

    \n\n
    lines...\nkey1\ntext\nkey2\nlines...\n
    \n", "signature": "(\tfilepath: str,\tkey1: str,\tkey2: str,\ttext: str,\tdelete_keys: bool = False,\tfrom_end: bool = False,\tregex: bool = False) -> None:", "funcdef": "def"}, {"fullname": "aton.text.edit.delete_under", "modulename": "aton.text.edit", "qualname": "delete_under", "kind": "function", "doc": "

    Deletes all the content under the line containing the key in the specified filepath.\nThe keyword can be at any position within the line.\nRegular expressions can be used by setting regex=True.

    \n\n

    By default the first matches is used; it can be any positive integer (0 is treated as 1!),\nincluding negative integers to select a match starting from the end of the file.

    \n\n

    The content can be deleted after a specific number of lines after the match,\nchanging the value skips, that skips the specified number of lines.\nNegative integers start deleting the content from the previous lines.

    \n", "signature": "(\tfilepath,\tkey: str,\tmatches: int = 1,\tskips: int = 0,\tregex: bool = False) -> None:", "funcdef": "def"}, {"fullname": "aton.text.edit.correct_with_dict", "modulename": "aton.text.edit", "qualname": "correct_with_dict", "kind": "function", "doc": "

    Corrects the given text file filepath using a correct dictionary.

    \n", "signature": "(filepath: str, correct: dict) -> None:", "funcdef": "def"}, {"fullname": "aton.text.edit.from_template", "modulename": "aton.text.edit", "qualname": "from_template", "kind": "function", "doc": "

    Copies an old text file to a new file,\ncorrecting the output file with a correct dictionary.\nAdditionally, it can add a comment at the beginning of the new file.

    \n", "signature": "(old: str, new: str, correct: dict = None, comment: str = None) -> None:", "funcdef": "def"}, {"fullname": "aton.text.extract", "modulename": "aton.text.extract", "kind": "module", "doc": "

    Description

    \n\n

    Functions to extract data from raw text strings.

    \n\n

    Index

    \n\n\n\n
    \n"}, {"fullname": "aton.text.extract.number", "modulename": "aton.text.extract", "qualname": "number", "kind": "function", "doc": "

    Extracts the float value of a given name variable from a raw text.

    \n\n

    Example:

    \n\n
    \n
    >>> text = 'energy =   500.0 Ry'\n>>> thotpy.extract.number(text, 'energy')\n500.0  # float output\n
    \n
    \n", "signature": "(text: str, name: str = '') -> float:", "funcdef": "def"}, {"fullname": "aton.text.extract.string", "modulename": "aton.text.extract", "qualname": "string", "kind": "function", "doc": "

    Extracts the text value of a given name variable from a raw string.\nStops before an optional stop string.\nRemoves leading and trailing commas by default, change this with strip=False.

    \n\n

    Example:

    \n\n
    \n
    >>> text = 'energy =   500.0 Ry were calculated'\n>>> thotpy.extract.string(text, 'energy', 'were')\n'500.0 Ry'  # String output\n
    \n
    \n", "signature": "(text: str, name: str = '', stop: str = '', strip: bool = True) -> str:", "funcdef": "def"}, {"fullname": "aton.text.extract.column", "modulename": "aton.text.extract", "qualname": "column", "kind": "function", "doc": "

    Extracts the desired float column index of a given string (0 by default).

    \n", "signature": "(text: str, column: int = 0) -> float:", "funcdef": "def"}, {"fullname": "aton.text.extract.coords", "modulename": "aton.text.extract", "qualname": "coords", "kind": "function", "doc": "

    Returns a list with the float coordinates expressed in a given text string.

    \n", "signature": "(text: str) -> list:", "funcdef": "def"}, {"fullname": "aton.text.extract.element", "modulename": "aton.text.extract", "qualname": "element", "kind": "function", "doc": "

    Extract a chemical element from a raw text string.

    \n\n

    If there are several elements, you can return a specific index match (positive, 0 by default).\nAllows for standard elements (H, He, Na...) and isotopes (H2, He4...).

    \n", "signature": "(text: str, index: int = 0) -> str:", "funcdef": "def"}, {"fullname": "aton.text.find", "modulename": "aton.text.find", "kind": "module", "doc": "

    Description

    \n\n

    Functions to search for specific content inside text files.

    \n\n

    Index

    \n\n

    Functions to find and return specific text strings:

    \n\n\n\n

    Functions to find the position in the file of specific text strings:

    \n\n\n\n
    \n"}, {"fullname": "aton.text.find.lines", "modulename": "aton.text.find", "qualname": "lines", "kind": "function", "doc": "

    Finds the line(s) containing the key string in the given filepath,\nreturning a list with the matches.

    \n\n

    The value matches specifies the max number of matches to be returned.\nDefaults to 0 to return all possible matches. Set it to 1 to return only one match,\nor to negative integers to start the search from the end of the file upwards.

    \n\n

    The value additional specifies the number of additional lines\nbelow the target line that are also returned;\n2 to return the found line plus two additional lines below, etc.\nNegative values return the specified number of lines before the target line.\nThe original ordering from the file is preserved.\nDefaults to additional=0, only returning the target line.\nBy default, the additional lines are returned in the same list item as the match separated by a \\n,\nunless split=True, in which case these additional lines\nare splitted and added as additional items in the list.\nThis way, split=False allows to differentiate between matches.

    \n\n

    To use regular expressions in the search, set regex=True.\nBy default regex search is deactivated, using the faster mmap.find and rfind methods instead.

    \n", "signature": "(\tfilepath: str,\tkey: str,\tmatches: int = 0,\tadditional: int = 0,\tsplit: bool = False,\tregex: bool = False) -> list:", "funcdef": "def"}, {"fullname": "aton.text.find.between", "modulename": "aton.text.find", "qualname": "between", "kind": "function", "doc": "

    Returns the content between the lines with key1 and key2 in the given filepath.\nKeywords can be at any position within the line.\nRegular expressions can be used by setting regex=True.

    \n\n

    Key lines are omited by default, but can be returned with include_keys=True.

    \n\n

    If there is more than one match, only the first one is considered by default;\nset match (int) to specify a particular match (1, 2... 0 is considered as 1!).\nUse negative numbers to start from the end of the file.

    \n", "signature": "(\tfilepath: str,\tkey1: str,\tkey2: str,\tinclude_keys: bool = True,\tmatch: int = 1,\tregex: bool = False) -> list:", "funcdef": "def"}, {"fullname": "aton.text.find.pos", "modulename": "aton.text.find", "qualname": "pos", "kind": "function", "doc": "

    Returns a list of the positions of a key in a given filepath (whether file or memory mapped file).

    \n\n

    The value matches specifies the max number of matches to return.\nDefaults to 0 to return all possible matches. Set it to 1 to return only one match,\n2 to get the first two matches, etc.\nYou can also set it to negative integers to start searching from the end of the file upwards.

    \n\n

    This method is faster than pos_regex(), but does not search for regular expressions.

    \n", "signature": "(filepath, key: str, matches: int = 0) -> list:", "funcdef": "def"}, {"fullname": "aton.text.find.pos_regex", "modulename": "aton.text.find", "qualname": "pos_regex", "kind": "function", "doc": "

    Returns a list of the positions of a key in a given filepath (actual file, not mmapped!).

    \n\n

    The value matches specifies the max number of matches to return.\nDefaults to 0 to return all possible matches. Set it to 1 to return only one match,\nor to negative integers to start searching from the end of the file upwards.

    \n\n

    This method is slower than pos(), but it can search for regular expressions.

    \n", "signature": "(filepath, key: str, matches: int = 0) -> list:", "funcdef": "def"}, {"fullname": "aton.text.find.next_pos", "modulename": "aton.text.find", "qualname": "next_pos", "kind": "function", "doc": "

    Returns the next position of the key string in the given filepath (file or mmapped file),\nstarting from an initial position tuple.\nThe match number specifies the nonzero index of the next match to return (1, 2... 0 is considered as 1!).\nIt can be negative to search backwards from the initial position.\nThe last known positions will be returned if no more matches are found.

    \n\n

    This method is specific for normal strings.\nTo use regular expressions, check next_pos_regex().

    \n", "signature": "(filepath, position: tuple, key: str, match: int = 1) -> tuple:", "funcdef": "def"}, {"fullname": "aton.text.find.next_pos_regex", "modulename": "aton.text.find", "qualname": "next_pos_regex", "kind": "function", "doc": "

    Returns the next position of the key string in the given filepath\n(actual file, not mmapped!), starting from an initial position tuple.\nThe match number specifies the next match to return (1, 2... 0 is considered as 1!).\nIt can be negative to search backwards from the initial position.\nThis method is specific for regular expressions.

    \n\n

    For normal strings, check the faster next_pos() method.

    \n", "signature": "(filepath, position: tuple, key: str, match: int = 0) -> tuple:", "funcdef": "def"}, {"fullname": "aton.text.find.line_pos", "modulename": "aton.text.find", "qualname": "line_pos", "kind": "function", "doc": "

    Returns the position of the full line containing the position tuple,\nin the given filepath (whether file or memory mapped file).\nA specific line below can be returned with skips being a natural int,\nor previous lines with negative values.

    \n", "signature": "(filepath, position: tuple, skips: int = 0) -> tuple:", "funcdef": "def"}, {"fullname": "aton.text.find.between_pos", "modulename": "aton.text.find", "qualname": "between_pos", "kind": "function", "doc": "

    Returns the positions of the content between the lines containing\nkey1 and key2 in the given filepath.\nKeywords can be at any position within the line.\nRegular expressions can be used by setting regex=True.

    \n\n

    Key lines are omited by default, but can be returned with include_keys=True.

    \n\n

    If there is more than one match, only the first one is considered by default;\nset match number to specify a particular match (1, 2... 0 is considered as 1!).\nUse negative numbers to start from the end of the file.

    \n", "signature": "(\tfilepath,\tkey1: str,\tkey2: str,\tinclude_keys: bool = True,\tmatch: int = 1,\tregex: bool = False) -> tuple:", "funcdef": "def"}, {"fullname": "aton.units", "modulename": "aton.units", "kind": "module", "doc": "

    Description

    \n\n

    This module contains useful constants and conversion factors.

    \n\n

    Index

    \n\n\n\n

    References

    \n\n

    These values come from the 2022 CODATA Internationally\nrecommended 2022 values of the Fundamental Physical Constants.

    \n\n
    \n\n

    Energy conversion factors

    \n\n

    Note that cm refers to cm$^{-1}$.

    \n"}, {"fullname": "aton.units.eV_to_meV", "modulename": "aton.units", "qualname": "eV_to_meV", "kind": "variable", "doc": "

    \n", "default_value": "1000.0"}, {"fullname": "aton.units.meV_to_eV", "modulename": "aton.units", "qualname": "meV_to_eV", "kind": "variable", "doc": "

    \n", "default_value": "0.001"}, {"fullname": "aton.units.meV_to_cm", "modulename": "aton.units", "qualname": "meV_to_cm", "kind": "variable", "doc": "

    \n", "default_value": "8.0655"}, {"fullname": "aton.units.cm_to_meV", "modulename": "aton.units", "qualname": "cm_to_meV", "kind": "variable", "doc": "

    \n", "default_value": "0.12398487384539086"}, {"fullname": "aton.units.eV_to_J", "modulename": "aton.units", "qualname": "eV_to_J", "kind": "variable", "doc": "

    \n", "default_value": "1.602176634e-19"}, {"fullname": "aton.units.J_to_eV", "modulename": "aton.units", "qualname": "J_to_eV", "kind": "variable", "doc": "

    \n", "default_value": "6.241509074460763e+18"}, {"fullname": "aton.units.meV_to_J", "modulename": "aton.units", "qualname": "meV_to_J", "kind": "variable", "doc": "

    \n", "default_value": "1.6021766339999998e-22"}, {"fullname": "aton.units.J_to_meV", "modulename": "aton.units", "qualname": "J_to_meV", "kind": "variable", "doc": "

    \n", "default_value": "6.241509074460763e+21"}, {"fullname": "aton.units.Ry_to_eV", "modulename": "aton.units", "qualname": "Ry_to_eV", "kind": "variable", "doc": "

    \n", "default_value": "13.60569312299"}, {"fullname": "aton.units.eV_to_Ry", "modulename": "aton.units", "qualname": "eV_to_Ry", "kind": "variable", "doc": "

    \n", "default_value": "0.07349864435133158"}, {"fullname": "aton.units.Ry_to_J", "modulename": "aton.units", "qualname": "Ry_to_J", "kind": "variable", "doc": "

    \n", "default_value": "2.179872361103e-18"}, {"fullname": "aton.units.J_to_Ry", "modulename": "aton.units", "qualname": "J_to_Ry", "kind": "variable", "doc": "

    \n", "default_value": "4.5874245567938074e+17"}, {"fullname": "aton.units.cal_to_J", "modulename": "aton.units", "qualname": "cal_to_J", "kind": "variable", "doc": "

    \n", "default_value": "4.184"}, {"fullname": "aton.units.J_to_cal", "modulename": "aton.units", "qualname": "J_to_cal", "kind": "variable", "doc": "

    \n", "default_value": "0.2390057361376673"}, {"fullname": "aton.units.kcal_to_J", "modulename": "aton.units", "qualname": "kcal_to_J", "kind": "variable", "doc": "

    \n", "default_value": "4184.0"}, {"fullname": "aton.units.J_to_kcal", "modulename": "aton.units", "qualname": "J_to_kcal", "kind": "variable", "doc": "
    \n\n

    Distance conversion factors

    \n\n

    Note that A refers to Angstroms.

    \n", "default_value": "0.0002390057361376673"}, {"fullname": "aton.units.A_to_m", "modulename": "aton.units", "qualname": "A_to_m", "kind": "variable", "doc": "

    \n", "default_value": "1e-10"}, {"fullname": "aton.units.m_to_A", "modulename": "aton.units", "qualname": "m_to_A", "kind": "variable", "doc": "

    \n", "default_value": "10000000000.0"}, {"fullname": "aton.units.bohr_to_m", "modulename": "aton.units", "qualname": "bohr_to_m", "kind": "variable", "doc": "

    \n", "default_value": "5.29177210544e-11"}, {"fullname": "aton.units.m_to_bohr", "modulename": "aton.units", "qualname": "m_to_bohr", "kind": "variable", "doc": "

    \n", "default_value": "18897261259.077824"}, {"fullname": "aton.units.A_to_bohr", "modulename": "aton.units", "qualname": "A_to_bohr", "kind": "variable", "doc": "

    \n", "default_value": "1.8897261259077824"}, {"fullname": "aton.units.bohr_to_A", "modulename": "aton.units", "qualname": "bohr_to_A", "kind": "variable", "doc": "
    \n\n

    Mass conversion factors

    \n", "default_value": "0.529177210544"}, {"fullname": "aton.units.amu_to_kg", "modulename": "aton.units", "qualname": "amu_to_kg", "kind": "variable", "doc": "

    \n", "default_value": "1.6605390666e-27"}, {"fullname": "aton.units.kg_to_amu", "modulename": "aton.units", "qualname": "kg_to_amu", "kind": "variable", "doc": "

    \n", "default_value": "6.022140762081123e+26"}, {"fullname": "aton.units.kg_to_g", "modulename": "aton.units", "qualname": "kg_to_g", "kind": "variable", "doc": "

    \n", "default_value": "1000.0"}, {"fullname": "aton.units.g_to_kg", "modulename": "aton.units", "qualname": "g_to_kg", "kind": "variable", "doc": "
    \n\n

    Pressure conversion factors

    \n", "default_value": "0.001"}, {"fullname": "aton.units.GPa_to_Pa", "modulename": "aton.units", "qualname": "GPa_to_Pa", "kind": "variable", "doc": "

    \n", "default_value": "1000000000.0"}, {"fullname": "aton.units.Pa_to_GPa", "modulename": "aton.units", "qualname": "Pa_to_GPa", "kind": "variable", "doc": "

    \n", "default_value": "1e-09"}, {"fullname": "aton.units.kbar_to_bar", "modulename": "aton.units", "qualname": "kbar_to_bar", "kind": "variable", "doc": "

    \n", "default_value": "1000.0"}, {"fullname": "aton.units.bar_to_kbar", "modulename": "aton.units", "qualname": "bar_to_kbar", "kind": "variable", "doc": "

    \n", "default_value": "0.001"}, {"fullname": "aton.units.Pa_to_bar", "modulename": "aton.units", "qualname": "Pa_to_bar", "kind": "variable", "doc": "

    \n", "default_value": "1e-05"}, {"fullname": "aton.units.bar_to_Pa", "modulename": "aton.units", "qualname": "bar_to_Pa", "kind": "variable", "doc": "

    \n", "default_value": "99999.99999999999"}, {"fullname": "aton.units.GPa_to_kbar", "modulename": "aton.units", "qualname": "GPa_to_kbar", "kind": "variable", "doc": "

    \n", "default_value": "10.0"}, {"fullname": "aton.units.kbar_to_GPa", "modulename": "aton.units", "qualname": "kbar_to_GPa", "kind": "variable", "doc": "
    \n\n

    Time conversion factors

    \n\n

    Note that H refers to hours.

    \n", "default_value": "0.1"}, {"fullname": "aton.units.H_to_s", "modulename": "aton.units", "qualname": "H_to_s", "kind": "variable", "doc": "

    \n", "default_value": "3600.0"}, {"fullname": "aton.units.s_to_H", "modulename": "aton.units", "qualname": "s_to_H", "kind": "variable", "doc": "
    \n\n

    Universal constants

    \n\n

    Given in SI units unless stated otherwise.

    \n", "default_value": "0.0002777777777777778"}, {"fullname": "aton.units.h", "modulename": "aton.units", "qualname": "h", "kind": "variable", "doc": "

    Planck constant, in J\u00b7s.

    \n", "default_value": "6.62607015e-34"}, {"fullname": "aton.units.h_eV", "modulename": "aton.units", "qualname": "h_eV", "kind": "variable", "doc": "

    Planck constant, in eV\u00b7s.

    \n", "default_value": "4.135667696923859e-15"}, {"fullname": "aton.units.hbar", "modulename": "aton.units", "qualname": "hbar", "kind": "variable", "doc": "

    Reduced Planck constant, in J\u00b7s.

    \n", "default_value": "1.0545718176461565e-34"}, {"fullname": "aton.units.hbar_eV", "modulename": "aton.units", "qualname": "hbar_eV", "kind": "variable", "doc": "

    Reduced Planck constant, in eV\u00b7s.

    \n", "default_value": "6.582119569509066e-16"}, {"fullname": "aton.interface", "modulename": "aton.interface", "kind": "module", "doc": "

    \n"}, {"fullname": "aton.interface.castep", "modulename": "aton.interface.castep", "kind": "module", "doc": "

    Description

    \n\n

    Functions to work with CASTEP calculation files.

    \n\n

    Index

    \n\n\n\n
    \n"}, {"fullname": "aton.interface.castep.read_castep", "modulename": "aton.interface.castep", "qualname": "read_castep", "kind": "function", "doc": "

    Reads a CASTEP output file, specified in filename.\nReturns a dictionary with the following keys:\n'Enthalpy' (LBFGS: Final Enthalpy, in kJ/mol),\n'Energy' (Total energy corrected for finite basis set, in eV),\n'Space group', 'Volume' (Angstrom^3), 'Density' (amu/Angstrom^3), 'Density_g' (g/cm^3),\n'A', 'B', 'C' (Angstroms), 'Alpha', 'Beta', 'Gamma' (Degrees).

    \n\n

    Note that these output keys start with a Capital letter.

    \n", "signature": "(filename) -> dict:", "funcdef": "def"}, {"fullname": "aton.interface.phonopy", "modulename": "aton.interface.phonopy", "kind": "module", "doc": "

    Description

    \n\n

    Functions to work with Phonopy calculations,\nalong with Quantum ESPRESSO.

    \n\n

    Index

    \n\n

    The two main functions that you might want to use to run phonon calculations are:

    \n\n\n\n

    The following functions are available for whoever seeks more control:

    \n\n\n\n
    \n"}, {"fullname": "aton.interface.phonopy.make", "modulename": "aton.interface.phonopy", "qualname": "make", "kind": "function", "doc": "

    Starting on a given folder (CWD if none) from the relax_in and relax_out (default ones),\ncreates the supercells of a dimension (2 2 2 by default)\nneeded for the Phonopy calculations with Quantum ESPRESSO.\nIt runs sequentially thotpy.qe.scf_from_relax(), supercells_from_scf() and scf_header_to_supercells().\nFinally, it checks the slurm_template with check_slurm_template().

    \n", "signature": "(\tdimension: str = '2 2 2',\tfolder: str = None,\trelax_in: str = 'relax.in',\trelax_out: str = 'relax.out',\tslurm_template: str = 'scf.slurm') -> None:", "funcdef": "def"}, {"fullname": "aton.interface.phonopy.sbatch", "modulename": "aton.interface.phonopy", "qualname": "sbatch", "kind": "function", "doc": "

    Launch all your supercell calculations to a cluster using a SLURM manager.\nRuns from a folder (CWD if empty), using a slurm_template (scf.slurm by default).

    \n\n

    If testing=True it skips the final sbatching, just printing the commands on the screen.

    \n\n

    The slurm template must contain the keywords\nINPUT_FILE, OUTPUT_FILE, and JOB_NAME in the following lines:

    \n\n
    #SBATCH --job-name=JOB_NAME\nmpirun pw.x -inp INPUT_FILE > OUTPUT_FILE\n
    \n", "signature": "(\tfolder=None,\tslurm_template: str = 'scf.slurm',\ttesting: bool = False) -> None:", "funcdef": "def"}, {"fullname": "aton.interface.phonopy.supercells_from_scf", "modulename": "aton.interface.phonopy", "qualname": "supercells_from_scf", "kind": "function", "doc": "

    Creates supercells of a given dimension (2 2 2 by default) inside a folder,\nfrom a Quantum ESPRESSO scf input (scf.in by default).

    \n", "signature": "(\tdimension: str = '2 2 2',\tfolder: str = None,\tscf: str = 'scf.in') -> None:", "funcdef": "def"}, {"fullname": "aton.interface.phonopy.scf_header_to_supercells", "modulename": "aton.interface.phonopy", "qualname": "scf_header_to_supercells", "kind": "function", "doc": "

    Paste the header from the scf file in folder to the supercells created by Phonopy.

    \n", "signature": "(folder: str = None, scf: str = 'scf.in') -> None:", "funcdef": "def"}, {"fullname": "aton.interface.phonopy.check_slurm_template", "modulename": "aton.interface.phonopy", "qualname": "check_slurm_template", "kind": "function", "doc": "

    Check a slurm_template inside folder.\nThe current working directory is used if folder is not provided.\nIf the file does not exist or is invalid, creates a scf_EXAMPLE.slurm file for reference.

    \n", "signature": "(folder=None, slurm_template: str = 'scf.slurm') -> str:", "funcdef": "def"}, {"fullname": "aton.interface.qe", "modulename": "aton.interface.qe", "kind": "module", "doc": "

    Description

    \n\n

    Functions to work with Quantum ESPRESSO calculation files.

    \n\n

    Index

    \n\n\n\n
    \n"}, {"fullname": "aton.interface.qe.pw_description", "modulename": "aton.interface.qe", "qualname": "pw_description", "kind": "variable", "doc": "

    Dictionary with every possible namelist as keys, and the corresponding variables as values.

    \n", "default_value": "{'&CONTROL': ['calculation', 'title', 'verbosity', 'restart_mode', 'wf_collect', 'nstep', 'iprint', 'tstress', 'tprnfor', 'dt', 'outdir', 'wfcdir', 'prefix', 'lkpoint_dir', 'max_seconds', 'etot_conv_thr', 'forc_conv_thr', 'disk_io', 'pseudo_dir', 'tefield', 'dipfield', 'lelfield', 'nberrycyc', 'lorbm', 'lberry', 'gdir', 'nppstr', 'gate', 'twochem', 'lfcp', 'trism'], '&SYSTEM': ['ibrav', 'celldm(1)', 'celldm(2)', 'celldm(3)', 'celldm(4)', 'celldm(5)', 'celldm(6)', 'A', 'B', 'C', 'cosAB', 'cosAC', 'cosBC', 'nat', 'ntyp', 'nbnd', 'nbnd_cond', 'tot_charge', 'starting_charge', 'tot_magnetization', 'starting_magnetization', 'ecutwfc', 'ecutrho', 'ecutfock', 'nr1', 'nr2', 'nr3', 'nr1s', 'nr2s', 'nr3s', 'nosym', 'nosym_evc', 'noinv', 'no_t_rev', 'force_symmorphic', 'use_all_frac', 'occupations', 'one_atom_occupations', 'starting_spin_angle', 'degauss_cond', 'nelec_cond', 'degauss', 'smearing', 'nspin', 'sic_gamma', 'pol_type', 'sic_energy', 'sci_vb', 'sci_cb', 'noncolin', 'ecfixed', 'qcutz', 'q2sigma', 'input_dft', 'ace', 'exx_fraction', 'screening_parameter', 'exxdiv_treatment', 'x_gamma_extrapolation', 'ecutvcutnqx1', 'nqx2', 'nqx3', 'localization_thr', 'Hubbard_occ', 'Hubbard_alpha', 'Hubbard_beta', 'starting_ns_eigenvalue', 'dmft', 'dmft_prefix', 'ensemble_energies', 'edir', 'emaxpos', 'eopreg', 'eamp', 'angle1', 'angle2', 'lforcet', 'constrained_magnetization', 'fixed_magnetization', 'lambda', 'report', 'lspinorb', 'assume_isolated', 'esm_bc', 'esm_w', 'esm_efield', 'esm_nfit', 'lgcscf', 'gcscf_mu', 'gcscf_conv_thr', 'gcscf_beta', 'vdw_corr', 'london', 'london_s6', 'london_c6', 'london_rvdw', 'london_rcut', 'dftd3_version', 'dftd3_threebody', 'ts_vdw_econv_thr', 'ts_vdw_isolated', 'xdm', 'xdm_a1', 'xdm_a2', 'space_group', 'uniqueb', 'origin_choice', 'rhombohedral', 'zgate', 'relaxz', 'block', 'block_1', 'block_2', 'block_height', 'nextffield'], '&ELECTRONS': ['electron_maxstep', 'exx_maxstep', 'scf_must_converge', 'conv_thr', 'adaptive_thr', 'conv_thr_init', 'conv_thr_multi', 'mixing_mode', 'mixing_beta', 'mixing_ndim', 'mixing_fixed_ns', 'diagonalization', 'diago_thr_init', 'diago_cg_maxiter', 'diago_ppcg_maxiter', 'diago_david_ndim', 'diago_rmm_ndim', 'diago_rmm_conv', 'diago_gs_nblock', 'diago_full_acc', 'efield', 'efield_cart', 'efield_phase', 'startingpot', 'startingwfc', 'tqr', 'real_space'], '&IONS': ['ion_positions', 'ion_velocities', 'ion_dynamics', 'pot_extrapolation', 'wfc_extrapolation', 'remove_rigid_rot', 'ion_temperature', 'tempw', 'tolp', 'delta_t', 'nraise', 'refold_pos', 'upscale', 'bfgs_ndim', 'trust_radius_max', 'trust_radius_min', 'trust_radius_ini', 'w_1', 'w_2', 'fire_alpha_init', 'fire_falpha', 'fire_nmin', 'fire_f_inc', 'fire_f_dec', 'fire_dtmax'], '&CELL': ['cell_dynamics', 'press', 'wmass', 'cell_factor', 'press_conv_thrcell_dofree'], '&FCP': ['fcp_mu', 'fcp_dynamics', 'fcp_conv_thr', 'fcp_ndiis', 'fcp_mass', 'fcp_velocity', 'fcp_temperature', 'fcp_tempw', 'fcp_tolp ', 'fcp_delta_t', 'fcp_nraise', 'freeze_all_atoms'], '&RISM': ['nsolv', 'closure', 'tempv', 'ecutsolv', 'solute_lj', 'solute_epsilon', 'solute_sigma', 'starting1d', 'starting3d', 'smear1d', 'smear3d', 'rism1d_maxstep', 'rism3d_maxstep', 'rism1d_conv_thr', 'rism3d_conv_thr', 'mdiis1d_size', 'mdiis3d_size', 'mdiis1d_step', 'mdiis3d_step', 'rism1d_bond_width', 'rism1d_dielectric', 'rism1d_molesize', 'rism1d_nproc', 'rism3d_conv_level', 'rism3d_planar_average', 'laue_nfit', 'laue_expand_right', 'laue_expand_left', 'laue_starting_right', 'laue_starting_left', 'laue_buffer_right', 'laue_buffer_left', 'laue_both_hands', 'laue_wall', 'laue_wall_z', 'laue_wall_rho', 'laue_wall_epsilon', 'laue_wall_sigma', 'laue_wall_lj6'], 'ATOMIC_SPECIES': ['X', 'Mass_X', 'PseudoPot_X'], 'ATOMIC_POSITIONS': ['X', 'x', 'y', 'z', 'if_pos(1)', 'if_pos(2)', 'if_pos(3)'], 'K_POINTS': ['nks', 'xk_x', 'xk_y', 'xk_z', 'wk', 'nk1', 'nk2', 'nk3', 'sk1', 'sk2', 'sk3'], 'ADDITIONAL_K_POINTS': ['nks_add', 'k_x', 'k_y', 'k_z', 'wk_'], 'CELL_PARAMETERS': ['v1', 'v2', 'v3'], 'CONSTRAINTS': ['nconstr', 'constr_tol', 'constr_type', 'constr(1)', 'constr(2)', 'constr(3)', 'constr(4)', 'constr_target'], 'OCCUPATIONS': ['f_inp1', 'f_inp2'], 'ATOMIC_VELOCITIES': ['V', 'vx', 'vy', 'vz'], 'ATOMIC_FORCES': ['X', 'fx', 'fy', 'fz'], 'SOLVENTS': ['X', 'Density', 'Molecule', 'X', 'Density_Left', 'Density_Right', 'Molecule'], 'HUBBARD': ['label(1)-manifold(1)', 'u_val(1)', 'label(1)-manifold(1)', 'j0_val(1)', 'paramType(1)', 'label(1)-manifold(1)', 'paramValue(1)', 'label(I)-manifold(I)', 'u_val(I)', 'label(I)-manifold(I)', 'j0_val(I)', 'label(I)-manifold(I)', 'label(J)-manifold(J)', 'I', 'J', 'v_val(I,J)']}"}, {"fullname": "aton.interface.qe.read_in", "modulename": "aton.interface.qe", "qualname": "read_in", "kind": "function", "doc": "

    Reads an input filepath from Quantum ESPRESSO,\nreturning a dictionary with the input values used.\nThe keys are named after the name of the corresponding variable.

    \n", "signature": "(filepath) -> dict:", "funcdef": "def"}, {"fullname": "aton.interface.qe.read_out", "modulename": "aton.interface.qe", "qualname": "read_out", "kind": "function", "doc": "

    Reads an output filepath from Quantum ESPRESSO,\nreturning a dict with the following keys:

    \n\n

    'Energy' (Ry), 'Total force' (float), 'Total SCF correction' (float),\n'Runtime' (str), 'JOB DONE' (bool), 'BFGS converged' (bool), 'BFGS failed' (bool),\n'Maxiter reached' (bool), 'Error' (str), 'Success' (bool), 'CELL_PARAMETERS_out' (list of str), 'ATOMIC_POSITIONS_out' (list of str), 'Alat' (bohr), 'Volume' (a.u.^3), 'Density' (g/cm^3).

    \n\n

    Note that these output keys start with a Capital letter.

    \n", "signature": "(filepath) -> dict:", "funcdef": "def"}, {"fullname": "aton.interface.qe.read_dir", "modulename": "aton.interface.qe", "qualname": "read_dir", "kind": "function", "doc": "

    Takes a folder containing a Quantum ESPRESSO calculation,\nand returns a dictionary containing the input parameters and output results.\nInput and output files are determined automatically,\nbut must be specified with in_str and out_str if more than one file ends with .in or .out.

    \n", "signature": "(folder, in_str: str = '.in', out_str: str = '.out') -> dict:", "funcdef": "def"}, {"fullname": "aton.interface.qe.read_dirs", "modulename": "aton.interface.qe", "qualname": "read_dirs", "kind": "function", "doc": "

    Calls recursively read_dir(), reading Quantum ESPRESSO calculations\nfrom all the subfolders inside the given directory.\nThe results are saved to CSV files inside the current directory.\nInput and output files are determined automatically, but must be specified with\nin_str and out_str if more than one file ends with .in or .out.

    \n\n

    To properly group the calculations per type, saving separated CSVs for each calculation type,\nyou can modify calc_splitter ('_' by default), calc_type_index (0) and calc_id_index (1).\nWith these default values, a subfolder named './CalculationType_CalculationID_AdditionalText/'\nwill be interpreted as follows:

    \n\n\n\n

    If everything fails, the subfolder name will be used.

    \n", "signature": "(\tdirectory,\tin_str: str = '.in',\tout_str: str = '.out',\tcalc_splitter='_',\tcalc_type_index=0,\tcalc_id_index=1) -> None:", "funcdef": "def"}, {"fullname": "aton.interface.qe.set_value", "modulename": "aton.interface.qe", "qualname": "set_value", "kind": "function", "doc": "

    Replace the value of a key parameter in an input filepath.\nIf value='', the parameter gets deleted.

    \n\n

    Remember to include the upper commas ' on values that use them.

    \n\n

    Updating 'ATOMIC_POSITIONS' updates 'nat' automatically,\nand updating 'ATOMIC_SPECIES' updates 'ntyp'.

    \n", "signature": "(filepath, key: str, value) -> None:", "funcdef": "def"}, {"fullname": "aton.interface.qe.add_atom", "modulename": "aton.interface.qe", "qualname": "add_atom", "kind": "function", "doc": "

    Adds an atom in a given filepath at a specified position.\nPosition must be a string or a list, as follows:

    \n\n

    \"specie:str float float float\" or [specie:str, float, float, float]

    \n\n

    This method updates automatically 'ntyp' and 'nat'.

    \n", "signature": "(filepath, position) -> None:", "funcdef": "def"}, {"fullname": "aton.interface.qe.normalize_cell_parameters", "modulename": "aton.interface.qe", "qualname": "normalize_cell_parameters", "kind": "function", "doc": "

    Takes a params string or a list of strings with the cell parameters\nand possibly some additional rogue lines, and returns a list os size 4,\nwith the \"CELL_PARAMETERS {alat|bohr|angstrom}\" on list[0],\nfollowed by the three coordinates.

    \n", "signature": "(params) -> list:", "funcdef": "def"}, {"fullname": "aton.interface.qe.normalize_atomic_positions", "modulename": "aton.interface.qe", "qualname": "normalize_atomic_positions", "kind": "function", "doc": "

    Takes a positions string or a list of strings with the atomic positions\nand possibly some additional rogue lines, and returns a list with the atomic positions,\nwith the \"ATOMIC_POSITIONS {alat|bohr|angstrom|crystal|crystal_sg}\" on list[0],\nfollowed by the coordinates.

    \n", "signature": "(positions) -> list:", "funcdef": "def"}, {"fullname": "aton.interface.qe.normalize_atomic_species", "modulename": "aton.interface.qe", "qualname": "normalize_atomic_species", "kind": "function", "doc": "

    Takes a species string or a list of strings with the atomic species\nand possibly some additional rogue lines, and returns a list with the atomic species\n(without the ATOMIC_SPECIES header!).

    \n", "signature": "(species) -> list:", "funcdef": "def"}, {"fullname": "aton.interface.qe.scf_from_relax", "modulename": "aton.interface.qe", "qualname": "scf_from_relax", "kind": "function", "doc": "

    Create a Quantum ESPRESSO scf.in file from a previous relax calculation.\nIf no folder is provided, the current working directory is used.\nThe relax_in and relax_out files by default are relax.in and relax.out,\nupdate the names if necessary.

    \n", "signature": "(\tfolder: str = None,\trelax_in: str = 'relax.in',\trelax_out: str = 'relax.out') -> None:", "funcdef": "def"}]; + /** pdoc search index */const docs = [{"fullname": "aton", "modulename": "aton", "kind": "module", "doc": "

    Welcome to the Ab-iniTiO and Neutron research toolbox, or Aton.\nInspired by its ancient Egyptian deity counterpart, this all-in-one Python package provides powerful and comprehensive tools for cutting-edge materials research, focused on (but not limited to) neutron science.

    \n\n

    Aton provides a range of spectral analysis tools, from spectra normalisation to deuteration estimation using the DINS impulse approximation.
    \nA set of physico-chemical constants and definitions is also included.

    \n\n

    Aton also allows you to easily create, edit and analyse all kinds of text files, with a special focus on ab-initio calculations.\nIn particular, it contains interfaces for Quantum ESPRESSO, Phonopy and CASTEP.

    \n\n
    \n\n

    Installation

    \n\n

    As always, it is recommended to install your packages in a virtual environment:

    \n\n
    \n
    python3 -m venv .venv\nsource .venv/bin/activate\n
    \n
    \n\n

    With pip

    \n\n

    The fastest way to install Aton is through pip:

    \n\n
    \n
    pip install aton\n
    \n
    \n\n

    From source

    \n\n

    Optionally, you can install Aton from the GitHub repository.

    \n\n

    First install the dependencies:

    \n\n
    \n
    pip install pandas numpy scipy\n
    \n
    \n\n

    Then clone the repository or download the latest stable release as a ZIP, unzip it, and run inside the Aton/ directory:

    \n\n
    \n
    pip install .\n
    \n
    \n\n
    \n\n

    Documentation

    \n\n

    The full Aton documentation is available online.
    \nAn offline version of the documentation is found at docs/aton.html.
    \nCode examples are included in the examples/ folder.

    \n\n

    Submodules

    \n\n

    Aton contains the following modules:

    \n\n\n\n

    General text edition

    \n\n

    The aton.text module includes the following general text-related submodules:

    \n\n\n\n

    Interfaces for ab-initio codes

    \n\n

    The aton.interface module contains interfaces for several ab-initio codes. These are powered by the aton.text module and can be easily extended. The following interfaces are included:

    \n\n\n\n

    Spectral analysis tools

    \n\n

    The aton.spectra module IS YET TO BE IMPLEMENTED.

    \n\n\n\n
    \n\n

    Contributing

    \n\n

    If you are interested in opening an issue or a pull request, please feel free to do so on GitHub.
    \nFor major changes, please get in touch first to discuss the details.

    \n\n

    Code style

    \n\n

    Please try to follow some general guidelines:

    \n\n\n\n

    Testing with PyTest

    \n\n

    If you are modifying the source code, you should run the automated tests of the tests/ folder to check that everything works as intended.\nTo do so, first install PyTest in your environment,

    \n\n
    \n
    pip install pytest\n
    \n
    \n\n

    And then run PyTest inside the Aton/ directory,

    \n\n
    \n
    pytest -vv\n
    \n
    \n\n

    Compiling the documentation

    \n\n

    The documentation can be compiled automatically to docs/aton.html with pdoc and Aton itself, by running:

    \n\n
    \n
    python3 makedocs.py\n
    \n
    \n\n
    \n\n

    License

    \n\n

    Copyright (C) 2024 Pablo Gila-Herranz
    \nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU Affero General Public License as published\nby the Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.
    \nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    \nSee the attached GNU Affero General Public License for more details.

    \n"}, {"fullname": "aton.alias", "modulename": "aton.alias", "kind": "module", "doc": "

    Description

    \n\n

    This module contains common dictionaries to normalise and correct user inputs.\nAll values are in lowercase to allow comparison with the string.lower() method.

    \n\n

    Use example:

    \n\n
    \n
    unit = 'Electronvolts'\nif unit.lower() in aton.alias.units['eV']:\n    ... do stuff ...\n
    \n
    \n\n

    Index

    \n\n\n\n
    \n"}, {"fullname": "aton.alias.units", "modulename": "aton.alias", "qualname": "units", "kind": "variable", "doc": "

    Dict with unit names.

    \n", "annotation": ": dict", "default_value": "{'mol': ['mol', 'mols', 'mole', 'moles'], 'g': ['g', 'gram', 'grams'], 'kg': ['kg', 'kilogram', 'kilograms'], 'amu': ['amu', 'atomicmassunit', 'atomicmassunits'], 'eV': ['ev', 'electronvolt', 'electronvolts'], 'meV': ['mev', 'millielectronvolt', 'millielectronvolts'], 'J': ['j', 'joule', 'joules'], 'cal': ['cal', 'calorie', 'calories'], 'kcal': ['kcal', 'kilocalorie', 'kilocalories'], 'Ry': ['ry', 'rydberg', 'rydbergs'], 'cm-1': ['cm^{-1}', 'cm1', 'cm-1', 'cm^-1'], 'cm': ['cm', 'centimeter', 'centimeters'], 'A': ['a', 'aa', 'angstrom', 'angstroms', 'armstrong', 'armstrongs'], 'bohr': ['bohr', 'bohrs', 'bohrradii'], 'm': ['m', 'meter', 'meters'], 'deg': ['deg', 'degree', 'degrees'], 'rad': ['rad', 'radian', 'radians'], 'bar': ['bar', 'bars'], 'kbar': ['kbar', 'kilobar', 'kilobars'], 'Pa': ['pa', 'pascal', 'pascals'], 'GPa': ['gpa', 'gigapascal', 'gigapascals'], 's': ['s', 'second', 'seconds'], 'H': ['h', 'hour', 'hours']}"}, {"fullname": "aton.alias.parameters", "modulename": "aton.alias", "qualname": "parameters", "kind": "variable", "doc": "

    Dict with different parameters.

    \n", "default_value": "{'height': ['height', 'h'], 'area': ['area', 'a']}"}, {"fullname": "aton.alias.experiments", "modulename": "aton.alias", "qualname": "experiments", "kind": "variable", "doc": "

    Dictionary with the available experiment types.

    \n", "annotation": ": dict", "default_value": "{'ins': ['ins', 'inelasticneutronscattering', 'inelastic neutron scattering'], 'atr': ['atr', 'ftir', 'attenuatedtotalreflection', 'attenuated total reflection'], 'raman': ['raman'], 'qens': ['qens', 'quasielasticneutronscattering', 'quasielastic neutron scattering', 'quasi elastic neutron scattering']}"}, {"fullname": "aton.alias.files", "modulename": "aton.alias", "qualname": "files", "kind": "variable", "doc": "

    Strings related to files.

    \n", "default_value": "{'file': ['file', 'files', 'f', 'filepath', 'file path', 'filename', 'file name'], 'dir': ['dir', 'directory', 'd', 'folder'], 'error': ['Error', 'error', 'ERROR', 'Errors', 'errors', 'ERRORS']}"}, {"fullname": "aton.alias.boolean", "modulename": "aton.alias", "qualname": "boolean", "kind": "variable", "doc": "

    Strings with booleans such as 'yes' / 'no'.

    \n", "default_value": "{True: ['yes', 'YES', 'Yes', 'Y', 'y', 'T', 'True', 'TRUE', 't', 'true', True, 'Si', 'SI', 'si', 'S', 's'], False: ['no', 'NO', 'No', 'N', 'n', 'F', 'False', 'FALSE', 'f', 'false', False]}"}, {"fullname": "aton.atoms", "modulename": "aton.atoms", "kind": "module", "doc": "

    Description

    \n\n

    This module contains the atoms megadictionary,\nwhich contains the properties of all elements.\nIt is managed and updated automatically with aton.elements,\nwhich also contains the literature references for this data.

    \n\n

    The atoms dictionary can be loaded directly as aton.atoms.\nUse example:

    \n\n
    \n
    aluminium_cross_section = aton.atoms['Al'].cross_section  # 1.503\nHe4_mass = aton.atoms['H'].isotope[4].mass  # 4.0026032497\n
    \n
    \n\n
    \n"}, {"fullname": "aton.atoms.atoms", "modulename": "aton.atoms", "qualname": "atoms", "kind": "variable", "doc": "

    \n", "default_value": "{'H': <aton.elements.Element object>, 'He': <aton.elements.Element object>, 'Li': <aton.elements.Element object>, 'Be': <aton.elements.Element object>, 'B': <aton.elements.Element object>, 'C': <aton.elements.Element object>, 'N': <aton.elements.Element object>, 'O': <aton.elements.Element object>, 'F': <aton.elements.Element object>, 'Ne': <aton.elements.Element object>, 'Na': <aton.elements.Element object>, 'Mg': <aton.elements.Element object>, 'Al': <aton.elements.Element object>, 'Si': <aton.elements.Element object>, 'P': <aton.elements.Element object>, 'S': <aton.elements.Element object>, 'Cl': <aton.elements.Element object>, 'Ar': <aton.elements.Element object>, 'K': <aton.elements.Element object>, 'Ca': <aton.elements.Element object>, 'Sc': <aton.elements.Element object>, 'Ti': <aton.elements.Element object>, 'V': <aton.elements.Element object>, 'Cr': <aton.elements.Element object>, 'Mn': <aton.elements.Element object>, 'Fe': <aton.elements.Element object>, 'Co': <aton.elements.Element object>, 'Ni': <aton.elements.Element object>, 'Cu': <aton.elements.Element object>, 'Zn': <aton.elements.Element object>, 'Ga': <aton.elements.Element object>, 'Ge': <aton.elements.Element object>, 'As': <aton.elements.Element object>, 'Se': <aton.elements.Element object>, 'Br': <aton.elements.Element object>, 'Kr': <aton.elements.Element object>, 'Rb': <aton.elements.Element object>, 'Sr': <aton.elements.Element object>, 'Y': <aton.elements.Element object>, 'Zr': <aton.elements.Element object>, 'Nb': <aton.elements.Element object>, 'Mo': <aton.elements.Element object>, 'Tc': <aton.elements.Element object>, 'Ru': <aton.elements.Element object>, 'Rh': <aton.elements.Element object>, 'Pd': <aton.elements.Element object>, 'Ag': <aton.elements.Element object>, 'Cd': <aton.elements.Element object>, 'In': <aton.elements.Element object>, 'Sn': <aton.elements.Element object>, 'Sb': <aton.elements.Element object>, 'Te': <aton.elements.Element object>, 'I': <aton.elements.Element object>, 'Xe': <aton.elements.Element object>, 'Cs': <aton.elements.Element object>, 'Ba': <aton.elements.Element object>, 'La': <aton.elements.Element object>, 'Ce': <aton.elements.Element object>, 'Pr': <aton.elements.Element object>, 'Nd': <aton.elements.Element object>, 'Pm': <aton.elements.Element object>, 'Sm': <aton.elements.Element object>, 'Eu': <aton.elements.Element object>, 'Gd': <aton.elements.Element object>, 'Tb': <aton.elements.Element object>, 'Dy': <aton.elements.Element object>, 'Ho': <aton.elements.Element object>, 'Er': <aton.elements.Element object>, 'Tm': <aton.elements.Element object>, 'Yb': <aton.elements.Element object>, 'Lu': <aton.elements.Element object>, 'Hf': <aton.elements.Element object>, 'Ta': <aton.elements.Element object>, 'W': <aton.elements.Element object>, 'Re': <aton.elements.Element object>, 'Os': <aton.elements.Element object>, 'Ir': <aton.elements.Element object>, 'Pt': <aton.elements.Element object>, 'Au': <aton.elements.Element object>, 'Hg': <aton.elements.Element object>, 'Tl': <aton.elements.Element object>, 'Pb': <aton.elements.Element object>, 'Bi': <aton.elements.Element object>, 'Po': <aton.elements.Element object>, 'At': <aton.elements.Element object>, 'Rn': <aton.elements.Element object>, 'Fr': <aton.elements.Element object>, 'Ra': <aton.elements.Element object>, 'Ac': <aton.elements.Element object>, 'Th': <aton.elements.Element object>, 'Pa': <aton.elements.Element object>, 'U': <aton.elements.Element object>, 'Np': <aton.elements.Element object>, 'Pu': <aton.elements.Element object>, 'Am': <aton.elements.Element object>, 'Cm': <aton.elements.Element object>, 'Bk': <aton.elements.Element object>, 'Cf': <aton.elements.Element object>, 'Es': <aton.elements.Element object>, 'Fm': <aton.elements.Element object>, 'Md': <aton.elements.Element object>, 'No': <aton.elements.Element object>, 'Lr': <aton.elements.Element object>, 'Rf': <aton.elements.Element object>, 'Db': <aton.elements.Element object>, 'Sg': <aton.elements.Element object>, 'Bh': <aton.elements.Element object>, 'Hs': <aton.elements.Element object>, 'Mt': <aton.elements.Element object>, 'Ds': <aton.elements.Element object>, 'Rg': <aton.elements.Element object>, 'Cn': <aton.elements.Element object>, 'Uut': <aton.elements.Element object>, 'Uuq': <aton.elements.Element object>, 'Uup': <aton.elements.Element object>, 'Uuh': <aton.elements.Element object>, 'Uus': <aton.elements.Element object>, 'Uuo': <aton.elements.Element object>}"}, {"fullname": "aton.call", "modulename": "aton.call", "kind": "module", "doc": "

    Description

    \n\n

    Functions to handle bash calls and related operations on Linux systems.

    \n\n

    Index

    \n\n\n\n
    \n"}, {"fullname": "aton.call.bash", "modulename": "aton.call", "qualname": "bash", "kind": "function", "doc": "

    Run a bash shell command, inside an optional cwd directory.\nIf empty, the current working directory will be used.\nPrints the running command and outputs by default, override this with verbose=False.\nReturns the result of the command used, except for when\nerrors are raised automatically; set return_anyway=True to override this.

    \n", "signature": "(\tcommand: str,\tcwd=None,\tverbose: bool = True,\treturn_anyway: bool = False):", "funcdef": "def"}, {"fullname": "aton.call.git", "modulename": "aton.call", "qualname": "git", "kind": "function", "doc": "

    Automatically update a Git repository.

    \n", "signature": "(path=None, verbose=True, message=None, tag=None) -> None:", "funcdef": "def"}, {"fullname": "aton.call.here", "modulename": "aton.call", "qualname": "here", "kind": "function", "doc": "

    Runs the rest of the script inside the specified folder.\nIf none is provided, it runs from the same directory where the current script lies.\nThis is really useful to run scripts from the VSCode terminal, etc.\nReturns the path of the used folder, or the path of the script if folder is not provided.

    \n\n

    Note that this changes not only the working directory of your script,\nbut also of other scripts that import and run your script.

    \n", "signature": "(folder=None) -> str:", "funcdef": "def"}, {"fullname": "aton.elements", "modulename": "aton.elements", "kind": "module", "doc": "

    Description

    \n\n

    This module contains functions to sort and analyse element data\nfrom the aton.atoms megadictionary, which contains the properties of all elements.\nIt also contains the tools needed to automatically update said megadictionary.

    \n\n

    Index

    \n\n\n\n

    References

    \n\n

    Atomic mass are in atomic mass units (amu), and come from:\nPure Appl. Chem., Vol. 78, No. 11, pp. 2051-2066, 2006.\nThe following masses are obtained from Wikipedia:\nAc: 227, Np: 237, Pm: 145, Tc: 98

    \n\n

    Isotope mass, mass_number and abundance come from:\nJ. R. de Laeter, J. K. B\u00f6hlke, P. De Bi\u00e8vre, H. Hidaka, H. S. Peiser, K. J. R. Rosman\nand P. D. P. Taylor (2003). 'Atomic weights of the elements. Review 2000 (IUPAC Technical Report)'

    \n\n

    Total bound scattering cross_section $\\sigma_s$ are in barns (1 b = 100 fm$^2$).\nFrom Felix Fernandez-Alonso's book 'Neutron Scattering Fundamentals' (2013).

    \n\n
    \n"}, {"fullname": "aton.elements.Element", "modulename": "aton.elements", "qualname": "Element", "kind": "class", "doc": "

    Used in the aton.atoms megadictionary to store element data.

    \n"}, {"fullname": "aton.elements.Element.__init__", "modulename": "aton.elements", "qualname": "Element.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tZ: int = None,\tsymbol: str = None,\tname: str = None,\tmass: float = None,\tcross_section: float = None,\tisotope: dict = None)"}, {"fullname": "aton.elements.Element.Z", "modulename": "aton.elements", "qualname": "Element.Z", "kind": "variable", "doc": "

    Atomic number (Z). Corresponds to the number of protons / electrons.

    \n", "annotation": ": int"}, {"fullname": "aton.elements.Element.symbol", "modulename": "aton.elements", "qualname": "Element.symbol", "kind": "variable", "doc": "

    Standard symbol of the element.

    \n", "annotation": ": str"}, {"fullname": "aton.elements.Element.name", "modulename": "aton.elements", "qualname": "Element.name", "kind": "variable", "doc": "

    Full name.

    \n", "annotation": ": str"}, {"fullname": "aton.elements.Element.mass", "modulename": "aton.elements", "qualname": "Element.mass", "kind": "variable", "doc": "

    Atomic mass, in atomic mass units (amu).

    \n", "annotation": ": float"}, {"fullname": "aton.elements.Element.cross_section", "modulename": "aton.elements", "qualname": "Element.cross_section", "kind": "variable", "doc": "

    Total bound scattering cross section.

    \n", "annotation": ": float"}, {"fullname": "aton.elements.Element.isotope", "modulename": "aton.elements", "qualname": "Element.isotope", "kind": "variable", "doc": "

    Dictionary containing the different Isotope of the element. The keys are the mass number (A).

    \n", "annotation": ": dict"}, {"fullname": "aton.elements.Isotope", "modulename": "aton.elements", "qualname": "Isotope", "kind": "class", "doc": "

    Used in the aton.atoms megadictionary to store isotope data.

    \n"}, {"fullname": "aton.elements.Isotope.__init__", "modulename": "aton.elements", "qualname": "Isotope.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tA: int = None,\tmass: float = None,\tabundance: float = None,\tcross_section: float = None)"}, {"fullname": "aton.elements.Isotope.A", "modulename": "aton.elements", "qualname": "Isotope.A", "kind": "variable", "doc": "

    Mass number (A) of the isotope. Corresponds to the total number of protons + neutrons in the core.

    \n", "annotation": ": int"}, {"fullname": "aton.elements.Isotope.mass", "modulename": "aton.elements", "qualname": "Isotope.mass", "kind": "variable", "doc": "

    Atomic mass of the isotope, in atomic mass units (amu).

    \n", "annotation": ": float"}, {"fullname": "aton.elements.Isotope.abundance", "modulename": "aton.elements", "qualname": "Isotope.abundance", "kind": "variable", "doc": "

    Relative abundance of the isotope.

    \n", "annotation": ": float"}, {"fullname": "aton.elements.Isotope.cross_section", "modulename": "aton.elements", "qualname": "Isotope.cross_section", "kind": "variable", "doc": "

    Total bound scattering cross section of the isotope.

    \n", "annotation": ": float"}, {"fullname": "aton.elements.export_atoms", "modulename": "aton.elements", "qualname": "export_atoms", "kind": "function", "doc": "

    Export a dictionary of chemical elements to a python file.

    \n\n

    This is used to build and update the aton.atoms megadictionary, that contains\nall the element data, such as masses, cross-sections, etc.

    \n", "signature": "(atoms: dict, filename='exported_atoms.py') -> None:", "funcdef": "def"}, {"fullname": "aton.elements.split_isotope", "modulename": "aton.elements", "qualname": "split_isotope", "kind": "function", "doc": "

    Split the name of an isotope into the element and the mass number, eg. He4 -> He, 4.

    \n\n

    If the isotope is not found in the aton.atoms megadictionary it raises an error,\ninforming of the allowed mass numbers (A) values for the given element.

    \n", "signature": "(name: str) -> tuple:", "funcdef": "def"}, {"fullname": "aton.elements.allowed_isotopes", "modulename": "aton.elements", "qualname": "allowed_isotopes", "kind": "function", "doc": "

    Return a list with the allowed mass numbers (A) of a given element.

    \n\n

    These mass numbers are used as isotope keys in the aton.atoms megadictionary.

    \n", "signature": "(element) -> list:", "funcdef": "def"}, {"fullname": "aton.file", "modulename": "aton.file", "kind": "module", "doc": "

    Description

    \n\n

    Functions to move files around.

    \n\n

    Index

    \n\n\n\n
    \n"}, {"fullname": "aton.file.get", "modulename": "aton.file", "qualname": "get", "kind": "function", "doc": "

    Check if the given filepath exists in the currrent working directory\nor in the full path, and returns its full path as a string.

    \n\n

    Raises an error if the file is not found, unless return_anyway=True,\nin which case it returns None. This can be used to personalize errors.

    \n\n

    If the provided string is a directory, it checks the files inside it.\nif there is only one file inside, it returns said file;\nif there are more files, it tries to filter them with the filters keyword(s) to return a single file.\nIf this fails, try using more strict filers to return a single file.

    \n", "signature": "(filepath, filters=None, return_anyway: bool = False) -> str:", "funcdef": "def"}, {"fullname": "aton.file.get_list", "modulename": "aton.file", "qualname": "get_list", "kind": "function", "doc": "

    Takes a folder, filters the content with the filters keyword(s) if provided, and returns a list with the matches.\nThe full paths are returned by default; to get only the base names, set abspath=False.

    \n", "signature": "(folder: str, filters=None, abspath: bool = True) -> list:", "funcdef": "def"}, {"fullname": "aton.file.copy", "modulename": "aton.file", "qualname": "copy", "kind": "function", "doc": "

    Copies the content of old file to new file with shutil,\nafter making sure that the file exists with thotpy.file.get().

    \n", "signature": "(old: str, new: str) -> None:", "funcdef": "def"}, {"fullname": "aton.file.move", "modulename": "aton.file", "qualname": "move", "kind": "function", "doc": "

    Moves old file to new file.

    \n", "signature": "(old: str, new: str) -> None:", "funcdef": "def"}, {"fullname": "aton.file.remove", "modulename": "aton.file", "qualname": "remove", "kind": "function", "doc": "

    Removes the given file or folder at filepath.

    \n\n
    \n

    WARNING: Removing stuff is always dangerous, be careful!

    \n
    \n", "signature": "(filepath: str) -> None:", "funcdef": "def"}, {"fullname": "aton.file.rename_on_folder", "modulename": "aton.file", "qualname": "rename_on_folder", "kind": "function", "doc": "

    Batch renames files in the given folder, replacing old string by new string.\nIf no folder is provided, the current working directory is used.

    \n", "signature": "(old: str, new: str, folder=None) -> None:", "funcdef": "def"}, {"fullname": "aton.file.rename_on_folders", "modulename": "aton.file", "qualname": "rename_on_folders", "kind": "function", "doc": "

    Renames the files inside the subfolders in the parent folder,\nfrom an old string to the new string.\nIf no folder is provided, the current working directory is used.

    \n", "signature": "(old: str, new: str, folder=None) -> None:", "funcdef": "def"}, {"fullname": "aton.file.copy_to_folders", "modulename": "aton.file", "qualname": "copy_to_folders", "kind": "function", "doc": "

    Copies the files from the parent folder with the given extension to individual subfolders.\nThe subfolders are named as the original files,\nremoving the strings from the strings_to_delete list.\nIf no folder is provided, it runs in the current working directory.

    \n", "signature": "(extension: str = None, strings_to_delete: list = [], folder=None) -> None:", "funcdef": "def"}, {"fullname": "aton.interface", "modulename": "aton.interface", "kind": "module", "doc": "

    Description

    \n\n

    This module contains interfaces for ab-initio and related calculation sofware.

    \n\n

    Index

    \n\n\n"}, {"fullname": "aton.interface.castep", "modulename": "aton.interface.castep", "kind": "module", "doc": "

    Description

    \n\n

    Functions to work with CASTEP calculation files.

    \n\n

    Index

    \n\n\n\n
    \n"}, {"fullname": "aton.interface.castep.read_castep", "modulename": "aton.interface.castep", "qualname": "read_castep", "kind": "function", "doc": "

    Reads a CASTEP output file, specified in filename.\nReturns a dictionary with the following keys:\n'Enthalpy' (LBFGS: Final Enthalpy, in kJ/mol),\n'Energy' (Total energy corrected for finite basis set, in eV),\n'Space group', 'Volume' (Angstrom^3), 'Density' (amu/Angstrom^3), 'Density_g' (g/cm^3),\n'A', 'B', 'C' (Angstroms), 'Alpha', 'Beta', 'Gamma' (Degrees).

    \n\n

    Note that these output keys start with a Capital letter.

    \n", "signature": "(filename) -> dict:", "funcdef": "def"}, {"fullname": "aton.interface.phonopy", "modulename": "aton.interface.phonopy", "kind": "module", "doc": "

    Description

    \n\n

    Functions to work with Phonopy calculations,\nalong with Quantum ESPRESSO.

    \n\n

    Index

    \n\n

    The two main functions that you might want to use to run phonon calculations are:

    \n\n\n\n

    The following functions are available for whoever seeks more control:

    \n\n\n\n
    \n"}, {"fullname": "aton.interface.phonopy.make", "modulename": "aton.interface.phonopy", "qualname": "make", "kind": "function", "doc": "

    Starting on a given folder (CWD if none) from the relax_in and relax_out (default ones),\ncreates the supercells of a dimension (2 2 2 by default)\nneeded for the Phonopy calculations with Quantum ESPRESSO.\nIt runs sequentially thotpy.qe.scf_from_relax(), supercells_from_scf() and scf_header_to_supercells().\nFinally, it checks the slurm_template with check_slurm_template().

    \n", "signature": "(\tdimension: str = '2 2 2',\tfolder: str = None,\trelax_in: str = 'relax.in',\trelax_out: str = 'relax.out',\tslurm_template: str = 'scf.slurm') -> None:", "funcdef": "def"}, {"fullname": "aton.interface.phonopy.sbatch", "modulename": "aton.interface.phonopy", "qualname": "sbatch", "kind": "function", "doc": "

    Launch all your supercell calculations to a cluster using a SLURM manager.\nRuns from a folder (CWD if empty), using a slurm_template (scf.slurm by default).

    \n\n

    If testing=True it skips the final sbatching, just printing the commands on the screen.

    \n\n

    The slurm template must contain the keywords\nINPUT_FILE, OUTPUT_FILE, and JOB_NAME in the following lines:

    \n\n
    #SBATCH --job-name=JOB_NAME\nmpirun pw.x -inp INPUT_FILE > OUTPUT_FILE\n
    \n", "signature": "(\tfolder=None,\tslurm_template: str = 'scf.slurm',\ttesting: bool = False) -> None:", "funcdef": "def"}, {"fullname": "aton.interface.phonopy.supercells_from_scf", "modulename": "aton.interface.phonopy", "qualname": "supercells_from_scf", "kind": "function", "doc": "

    Creates supercells of a given dimension (2 2 2 by default) inside a folder,\nfrom a Quantum ESPRESSO scf input (scf.in by default).

    \n", "signature": "(\tdimension: str = '2 2 2',\tfolder: str = None,\tscf: str = 'scf.in') -> None:", "funcdef": "def"}, {"fullname": "aton.interface.phonopy.scf_header_to_supercells", "modulename": "aton.interface.phonopy", "qualname": "scf_header_to_supercells", "kind": "function", "doc": "

    Paste the header from the scf file in folder to the supercells created by Phonopy.

    \n", "signature": "(folder: str = None, scf: str = 'scf.in') -> None:", "funcdef": "def"}, {"fullname": "aton.interface.phonopy.check_slurm_template", "modulename": "aton.interface.phonopy", "qualname": "check_slurm_template", "kind": "function", "doc": "

    Check a slurm_template inside folder.\nThe current working directory is used if folder is not provided.\nIf the file does not exist or is invalid, creates a scf_EXAMPLE.slurm file for reference.

    \n", "signature": "(folder=None, slurm_template: str = 'scf.slurm') -> str:", "funcdef": "def"}, {"fullname": "aton.interface.qe", "modulename": "aton.interface.qe", "kind": "module", "doc": "

    Description

    \n\n

    Functions to work with Quantum ESPRESSO calculation files.

    \n\n

    Index

    \n\n\n\n
    \n"}, {"fullname": "aton.interface.qe.pw_description", "modulename": "aton.interface.qe", "qualname": "pw_description", "kind": "variable", "doc": "

    Dictionary with every possible namelist as keys, and the corresponding variables as values.

    \n", "default_value": "{'&CONTROL': ['calculation', 'title', 'verbosity', 'restart_mode', 'wf_collect', 'nstep', 'iprint', 'tstress', 'tprnfor', 'dt', 'outdir', 'wfcdir', 'prefix', 'lkpoint_dir', 'max_seconds', 'etot_conv_thr', 'forc_conv_thr', 'disk_io', 'pseudo_dir', 'tefield', 'dipfield', 'lelfield', 'nberrycyc', 'lorbm', 'lberry', 'gdir', 'nppstr', 'gate', 'twochem', 'lfcp', 'trism'], '&SYSTEM': ['ibrav', 'celldm(1)', 'celldm(2)', 'celldm(3)', 'celldm(4)', 'celldm(5)', 'celldm(6)', 'A', 'B', 'C', 'cosAB', 'cosAC', 'cosBC', 'nat', 'ntyp', 'nbnd', 'nbnd_cond', 'tot_charge', 'starting_charge', 'tot_magnetization', 'starting_magnetization', 'ecutwfc', 'ecutrho', 'ecutfock', 'nr1', 'nr2', 'nr3', 'nr1s', 'nr2s', 'nr3s', 'nosym', 'nosym_evc', 'noinv', 'no_t_rev', 'force_symmorphic', 'use_all_frac', 'occupations', 'one_atom_occupations', 'starting_spin_angle', 'degauss_cond', 'nelec_cond', 'degauss', 'smearing', 'nspin', 'sic_gamma', 'pol_type', 'sic_energy', 'sci_vb', 'sci_cb', 'noncolin', 'ecfixed', 'qcutz', 'q2sigma', 'input_dft', 'ace', 'exx_fraction', 'screening_parameter', 'exxdiv_treatment', 'x_gamma_extrapolation', 'ecutvcutnqx1', 'nqx2', 'nqx3', 'localization_thr', 'Hubbard_occ', 'Hubbard_alpha', 'Hubbard_beta', 'starting_ns_eigenvalue', 'dmft', 'dmft_prefix', 'ensemble_energies', 'edir', 'emaxpos', 'eopreg', 'eamp', 'angle1', 'angle2', 'lforcet', 'constrained_magnetization', 'fixed_magnetization', 'lambda', 'report', 'lspinorb', 'assume_isolated', 'esm_bc', 'esm_w', 'esm_efield', 'esm_nfit', 'lgcscf', 'gcscf_mu', 'gcscf_conv_thr', 'gcscf_beta', 'vdw_corr', 'london', 'london_s6', 'london_c6', 'london_rvdw', 'london_rcut', 'dftd3_version', 'dftd3_threebody', 'ts_vdw_econv_thr', 'ts_vdw_isolated', 'xdm', 'xdm_a1', 'xdm_a2', 'space_group', 'uniqueb', 'origin_choice', 'rhombohedral', 'zgate', 'relaxz', 'block', 'block_1', 'block_2', 'block_height', 'nextffield'], '&ELECTRONS': ['electron_maxstep', 'exx_maxstep', 'scf_must_converge', 'conv_thr', 'adaptive_thr', 'conv_thr_init', 'conv_thr_multi', 'mixing_mode', 'mixing_beta', 'mixing_ndim', 'mixing_fixed_ns', 'diagonalization', 'diago_thr_init', 'diago_cg_maxiter', 'diago_ppcg_maxiter', 'diago_david_ndim', 'diago_rmm_ndim', 'diago_rmm_conv', 'diago_gs_nblock', 'diago_full_acc', 'efield', 'efield_cart', 'efield_phase', 'startingpot', 'startingwfc', 'tqr', 'real_space'], '&IONS': ['ion_positions', 'ion_velocities', 'ion_dynamics', 'pot_extrapolation', 'wfc_extrapolation', 'remove_rigid_rot', 'ion_temperature', 'tempw', 'tolp', 'delta_t', 'nraise', 'refold_pos', 'upscale', 'bfgs_ndim', 'trust_radius_max', 'trust_radius_min', 'trust_radius_ini', 'w_1', 'w_2', 'fire_alpha_init', 'fire_falpha', 'fire_nmin', 'fire_f_inc', 'fire_f_dec', 'fire_dtmax'], '&CELL': ['cell_dynamics', 'press', 'wmass', 'cell_factor', 'press_conv_thrcell_dofree'], '&FCP': ['fcp_mu', 'fcp_dynamics', 'fcp_conv_thr', 'fcp_ndiis', 'fcp_mass', 'fcp_velocity', 'fcp_temperature', 'fcp_tempw', 'fcp_tolp ', 'fcp_delta_t', 'fcp_nraise', 'freeze_all_atoms'], '&RISM': ['nsolv', 'closure', 'tempv', 'ecutsolv', 'solute_lj', 'solute_epsilon', 'solute_sigma', 'starting1d', 'starting3d', 'smear1d', 'smear3d', 'rism1d_maxstep', 'rism3d_maxstep', 'rism1d_conv_thr', 'rism3d_conv_thr', 'mdiis1d_size', 'mdiis3d_size', 'mdiis1d_step', 'mdiis3d_step', 'rism1d_bond_width', 'rism1d_dielectric', 'rism1d_molesize', 'rism1d_nproc', 'rism3d_conv_level', 'rism3d_planar_average', 'laue_nfit', 'laue_expand_right', 'laue_expand_left', 'laue_starting_right', 'laue_starting_left', 'laue_buffer_right', 'laue_buffer_left', 'laue_both_hands', 'laue_wall', 'laue_wall_z', 'laue_wall_rho', 'laue_wall_epsilon', 'laue_wall_sigma', 'laue_wall_lj6'], 'ATOMIC_SPECIES': ['X', 'Mass_X', 'PseudoPot_X'], 'ATOMIC_POSITIONS': ['X', 'x', 'y', 'z', 'if_pos(1)', 'if_pos(2)', 'if_pos(3)'], 'K_POINTS': ['nks', 'xk_x', 'xk_y', 'xk_z', 'wk', 'nk1', 'nk2', 'nk3', 'sk1', 'sk2', 'sk3'], 'ADDITIONAL_K_POINTS': ['nks_add', 'k_x', 'k_y', 'k_z', 'wk_'], 'CELL_PARAMETERS': ['v1', 'v2', 'v3'], 'CONSTRAINTS': ['nconstr', 'constr_tol', 'constr_type', 'constr(1)', 'constr(2)', 'constr(3)', 'constr(4)', 'constr_target'], 'OCCUPATIONS': ['f_inp1', 'f_inp2'], 'ATOMIC_VELOCITIES': ['V', 'vx', 'vy', 'vz'], 'ATOMIC_FORCES': ['X', 'fx', 'fy', 'fz'], 'SOLVENTS': ['X', 'Density', 'Molecule', 'X', 'Density_Left', 'Density_Right', 'Molecule'], 'HUBBARD': ['label(1)-manifold(1)', 'u_val(1)', 'label(1)-manifold(1)', 'j0_val(1)', 'paramType(1)', 'label(1)-manifold(1)', 'paramValue(1)', 'label(I)-manifold(I)', 'u_val(I)', 'label(I)-manifold(I)', 'j0_val(I)', 'label(I)-manifold(I)', 'label(J)-manifold(J)', 'I', 'J', 'v_val(I,J)']}"}, {"fullname": "aton.interface.qe.read_in", "modulename": "aton.interface.qe", "qualname": "read_in", "kind": "function", "doc": "

    Reads an input filepath from Quantum ESPRESSO,\nreturning a dictionary with the input values used.\nThe keys are named after the name of the corresponding variable.

    \n", "signature": "(filepath) -> dict:", "funcdef": "def"}, {"fullname": "aton.interface.qe.read_out", "modulename": "aton.interface.qe", "qualname": "read_out", "kind": "function", "doc": "

    Reads an output filepath from Quantum ESPRESSO,\nreturning a dict with the following keys:

    \n\n

    'Energy' (Ry), 'Total force' (float), 'Total SCF correction' (float),\n'Runtime' (str), 'JOB DONE' (bool), 'BFGS converged' (bool), 'BFGS failed' (bool),\n'Maxiter reached' (bool), 'Error' (str), 'Success' (bool), 'CELL_PARAMETERS_out' (list of str), 'ATOMIC_POSITIONS_out' (list of str), 'Alat' (bohr), 'Volume' (a.u.^3), 'Density' (g/cm^3).

    \n\n

    Note that these output keys start with a Capital letter.

    \n", "signature": "(filepath) -> dict:", "funcdef": "def"}, {"fullname": "aton.interface.qe.read_dir", "modulename": "aton.interface.qe", "qualname": "read_dir", "kind": "function", "doc": "

    Takes a folder containing a Quantum ESPRESSO calculation,\nand returns a dictionary containing the input parameters and output results.\nInput and output files are determined automatically,\nbut must be specified with in_str and out_str if more than one file ends with .in or .out.

    \n", "signature": "(folder, in_str: str = '.in', out_str: str = '.out') -> dict:", "funcdef": "def"}, {"fullname": "aton.interface.qe.read_dirs", "modulename": "aton.interface.qe", "qualname": "read_dirs", "kind": "function", "doc": "

    Calls recursively read_dir(), reading Quantum ESPRESSO calculations\nfrom all the subfolders inside the given directory.\nThe results are saved to CSV files inside the current directory.\nInput and output files are determined automatically, but must be specified with\nin_str and out_str if more than one file ends with .in or .out.

    \n\n

    To properly group the calculations per type, saving separated CSVs for each calculation type,\nyou can modify calc_splitter ('_' by default), calc_type_index (0) and calc_id_index (1).\nWith these default values, a subfolder named './CalculationType_CalculationID_AdditionalText/'\nwill be interpreted as follows:

    \n\n\n\n

    If everything fails, the subfolder name will be used.

    \n", "signature": "(\tdirectory,\tin_str: str = '.in',\tout_str: str = '.out',\tcalc_splitter='_',\tcalc_type_index=0,\tcalc_id_index=1) -> None:", "funcdef": "def"}, {"fullname": "aton.interface.qe.set_value", "modulename": "aton.interface.qe", "qualname": "set_value", "kind": "function", "doc": "

    Replace the value of a key parameter in an input filepath.\nIf value='', the parameter gets deleted.

    \n\n

    Remember to include the single quotes ' on values that use them.

    \n\n

    Updating 'ATOMIC_POSITIONS' updates 'nat' automatically,\nand updating 'ATOMIC_SPECIES' updates 'ntyp'.

    \n", "signature": "(filepath, key: str, value) -> None:", "funcdef": "def"}, {"fullname": "aton.interface.qe.add_atom", "modulename": "aton.interface.qe", "qualname": "add_atom", "kind": "function", "doc": "

    Adds an atom in a given filepath at a specified position.\nPosition must be a string or a list, as follows:

    \n\n

    \"specie:str float float float\" or [specie:str, float, float, float]

    \n\n

    This method updates automatically 'ntyp' and 'nat'.

    \n", "signature": "(filepath, position) -> None:", "funcdef": "def"}, {"fullname": "aton.interface.qe.normalize_cell_parameters", "modulename": "aton.interface.qe", "qualname": "normalize_cell_parameters", "kind": "function", "doc": "

    Takes a params string or a list of strings with the cell parameters\nand possibly some additional rogue lines, and returns a list os size 4,\nwith the \"CELL_PARAMETERS {alat|bohr|angstrom}\" on list[0],\nfollowed by the three coordinates.

    \n", "signature": "(params) -> list:", "funcdef": "def"}, {"fullname": "aton.interface.qe.normalize_atomic_positions", "modulename": "aton.interface.qe", "qualname": "normalize_atomic_positions", "kind": "function", "doc": "

    Takes a positions string or a list of strings with the atomic positions\nand possibly some additional rogue lines, and returns a list with the atomic positions,\nwith the \"ATOMIC_POSITIONS {alat|bohr|angstrom|crystal|crystal_sg}\" on list[0],\nfollowed by the coordinates.

    \n", "signature": "(positions) -> list:", "funcdef": "def"}, {"fullname": "aton.interface.qe.normalize_atomic_species", "modulename": "aton.interface.qe", "qualname": "normalize_atomic_species", "kind": "function", "doc": "

    Takes a species string or a list of strings with the atomic species\nand possibly some additional rogue lines, and returns a list with the atomic species\n(without the ATOMIC_SPECIES header!).

    \n", "signature": "(species) -> list:", "funcdef": "def"}, {"fullname": "aton.interface.qe.scf_from_relax", "modulename": "aton.interface.qe", "qualname": "scf_from_relax", "kind": "function", "doc": "

    Create a Quantum ESPRESSO scf.in file from a previous relax calculation.\nIf no folder is provided, the current working directory is used.\nThe relax_in and relax_out files by default are relax.in and relax.out,\nupdate the names if necessary.

    \n", "signature": "(\tfolder: str = None,\trelax_in: str = 'relax.in',\trelax_out: str = 'relax.out') -> None:", "funcdef": "def"}, {"fullname": "aton.spectra", "modulename": "aton.spectra", "kind": "module", "doc": "

    Description

    \n\n

    This module contains spectral analysis tools.

    \n\n

    Index

    \n\n\n"}, {"fullname": "aton.spectra.classes", "modulename": "aton.spectra.classes", "kind": "module", "doc": "

    Description

    \n\n

    This module contains common classes used to load and manipulate spectral data.\nAny class can be instantiated directly from the aton.spectra module;\nfor example, to create a new Spectra class for your data,\nyou just need to call aton.spectra.Spectra(options) as described below:

    \n\n
    \n
    import aton\nins = aton.spectra.Spectra(\n    # Options here\n    )\n
    \n
    \n\n

    Index

    \n\n\n\n
    \n"}, {"fullname": "aton.spectra.classes.Plotting", "modulename": "aton.spectra.classes", "qualname": "Plotting", "kind": "class", "doc": "

    Stores plotting options.\nRead by aton.spectra.plot.

    \n"}, {"fullname": "aton.spectra.classes.Plotting.__init__", "modulename": "aton.spectra.classes", "qualname": "Plotting.__init__", "kind": "function", "doc": "

    Default values can be overwritten when initializing the Plotting object.

    \n", "signature": "(\ttitle: str = None,\txlim=None,\tylim=None,\tmargins=None,\toffset=True,\tnormalize: bool = False,\tvline: list = None,\tvline_error: list = None,\tfigsize: tuple = None,\tlog_xscale: bool = False,\tshow_yticks: bool = False,\txlabel: str = None,\tylabel: str = None,\tlegend=None,\tlegend_title: str = None,\tlegend_size='medium',\tlegend_loc='best',\tsave_as: str = None)"}, {"fullname": "aton.spectra.classes.Plotting.title", "modulename": "aton.spectra.classes", "qualname": "Plotting.title", "kind": "variable", "doc": "

    Title of the plot. Set it to an empty string to remove the title.

    \n"}, {"fullname": "aton.spectra.classes.Plotting.xlim", "modulename": "aton.spectra.classes", "qualname": "Plotting.xlim", "kind": "variable", "doc": "

    List with the x-limits of the plot, as in [xlim_low, xlim_top].

    \n"}, {"fullname": "aton.spectra.classes.Plotting.ylim", "modulename": "aton.spectra.classes", "qualname": "Plotting.ylim", "kind": "variable", "doc": "

    List with the y-limits of the plot, as in [ylim_low, ylim_top].

    \n"}, {"fullname": "aton.spectra.classes.Plotting.margins", "modulename": "aton.spectra.classes", "qualname": "Plotting.margins", "kind": "variable", "doc": "

    List with additional margins at the bottom and top of the plot, as in [low_margin, top_margin].

    \n"}, {"fullname": "aton.spectra.classes.Plotting.offset", "modulename": "aton.spectra.classes", "qualname": "Plotting.offset", "kind": "variable", "doc": "

    If True, the plots will be separated automatically.\nIt can be set to a float, to equally offset the plots by a given value.

    \n"}, {"fullname": "aton.spectra.classes.Plotting.normalize", "modulename": "aton.spectra.classes", "qualname": "Plotting.normalize", "kind": "variable", "doc": "

    Normalize or not the plotted spectra.\nTrue or 'y' or 'Y' to normalize the heights, 'area' or 'a' or 'A' to normalize the areas.

    \n"}, {"fullname": "aton.spectra.classes.Plotting.vline", "modulename": "aton.spectra.classes", "qualname": "Plotting.vline", "kind": "variable", "doc": "

    Vertical line/s to plot. Can be an int or float with the x-position, or a list with several ones.

    \n"}, {"fullname": "aton.spectra.classes.Plotting.vline_error", "modulename": "aton.spectra.classes", "qualname": "Plotting.vline_error", "kind": "variable", "doc": "

    If not None, it will plot a shaded area of the specified width around the vertical lines specified at vline.\nIt can be an array of the same length as vline, or a single value to be applied to all.

    \n"}, {"fullname": "aton.spectra.classes.Plotting.figsize", "modulename": "aton.spectra.classes", "qualname": "Plotting.figsize", "kind": "variable", "doc": "

    Tuple with the figure size, as in matplotlib.

    \n"}, {"fullname": "aton.spectra.classes.Plotting.log_xscale", "modulename": "aton.spectra.classes", "qualname": "Plotting.log_xscale", "kind": "variable", "doc": "

    If true, plot the x-axis in logarithmic scale.

    \n"}, {"fullname": "aton.spectra.classes.Plotting.show_yticks", "modulename": "aton.spectra.classes", "qualname": "Plotting.show_yticks", "kind": "variable", "doc": "

    Show or not the yticks on the plot.

    \n"}, {"fullname": "aton.spectra.classes.Plotting.xlabel", "modulename": "aton.spectra.classes", "qualname": "Plotting.xlabel", "kind": "variable", "doc": "

    Custom label of the x-axis. If None, the default label will be used.\nSet to '' to remove the label of the horizontal axis.

    \n"}, {"fullname": "aton.spectra.classes.Plotting.ylabel", "modulename": "aton.spectra.classes", "qualname": "Plotting.ylabel", "kind": "variable", "doc": "

    Label of the y-axis. If None, the default label will be used.\nSet to '' to remove the label of the vertical axis.

    \n"}, {"fullname": "aton.spectra.classes.Plotting.legend", "modulename": "aton.spectra.classes", "qualname": "Plotting.legend", "kind": "variable", "doc": "

    If None, the files will be used as legend.\nCan be a bool to show or hide the plot legend.\nIt can also be an array containing the strings to display;\nin that case, elements set to False will not be displayed.

    \n"}, {"fullname": "aton.spectra.classes.Plotting.legend_title", "modulename": "aton.spectra.classes", "qualname": "Plotting.legend_title", "kind": "variable", "doc": "

    Title of the legend. Defaults to None.

    \n"}, {"fullname": "aton.spectra.classes.Plotting.legend_size", "modulename": "aton.spectra.classes", "qualname": "Plotting.legend_size", "kind": "variable", "doc": "

    Size of the legend, as in matplotlib. Defaults to 'medium'.

    \n"}, {"fullname": "aton.spectra.classes.Plotting.legend_loc", "modulename": "aton.spectra.classes", "qualname": "Plotting.legend_loc", "kind": "variable", "doc": "

    Location of the legend, as in matplotlib. Defaults to 'best'.

    \n"}, {"fullname": "aton.spectra.classes.Plotting.save_as", "modulename": "aton.spectra.classes", "qualname": "Plotting.save_as", "kind": "variable", "doc": "

    Filename to save the plot. None by default.

    \n"}, {"fullname": "aton.spectra.classes.Scaling", "modulename": "aton.spectra.classes", "qualname": "Scaling", "kind": "class", "doc": "

    The Scaling object is used to handle the normalization\nof the data inside the specified x-range,\nto the same heigth as in the specified index dataset\n(the first one by default).

    \n\n

    Custom heights can be normalized with ymin and ymax,\noverriding the x-values.\nFor example, you may want to normalize two spectra datasets\nwith respect to the height of a given peak that overlaps with another.\nThose peaks may have ymin values of 2 and 3, and ymax values\nof 50 and 60 respectively. In that case:

    \n\n
    \n
    spectra.scaling = Scaling(index=0, ymin=[2, 3], ymax=[50, 60])\n
    \n
    \n\n

    To normalize when plotting with aton.spectra.plot(Spectra),\nremember to set Plotting.normalize=True.

    \n\n

    When normalizing the plot, all datasets are fitted inside the\nplotting window, scaling over the entire data range into view.\nTo override this behaviour and expand over the given range\nto fill the plot window, you can set Scaling.zoom=True.\nThis zoom setting can also be enabled without normalizing the plot,\nresulting in a zoom over the given range so that the index dataset\nfits the full plotting window, scaling the rest of the set accordingly.

    \n"}, {"fullname": "aton.spectra.classes.Scaling.__init__", "modulename": "aton.spectra.classes", "qualname": "Scaling.__init__", "kind": "function", "doc": "

    All values can be set when initializing the Scaling object.

    \n", "signature": "(\tindex: int = 0,\txmin: float = None,\txmax: float = None,\tymin: list = None,\tymax: list = None,\tzoom: bool = False)"}, {"fullname": "aton.spectra.classes.Scaling.index", "modulename": "aton.spectra.classes", "qualname": "Scaling.index", "kind": "variable", "doc": "

    Index of the dataframe to use as reference.

    \n", "annotation": ": int"}, {"fullname": "aton.spectra.classes.Scaling.xmin", "modulename": "aton.spectra.classes", "qualname": "Scaling.xmin", "kind": "variable", "doc": "

    Minimum x-value to start normalizing the plots.

    \n", "annotation": ": float"}, {"fullname": "aton.spectra.classes.Scaling.xmax", "modulename": "aton.spectra.classes", "qualname": "Scaling.xmax", "kind": "variable", "doc": "

    Maximum x-value to normalize the plots.

    \n", "annotation": ": float"}, {"fullname": "aton.spectra.classes.Scaling.ymin", "modulename": "aton.spectra.classes", "qualname": "Scaling.ymin", "kind": "variable", "doc": "

    List with minimum y-values to normalize the plots.

    \n", "annotation": ": list"}, {"fullname": "aton.spectra.classes.Scaling.ymax", "modulename": "aton.spectra.classes", "qualname": "Scaling.ymax", "kind": "variable", "doc": "

    List with minimum y-values to normalize the plots.\nIf Plotting.normalize=True, the plots are normalized according to the y-values provided.

    \n", "annotation": ": list"}, {"fullname": "aton.spectra.classes.Scaling.zoom", "modulename": "aton.spectra.classes", "qualname": "Scaling.zoom", "kind": "variable", "doc": "

    Used when plotting with maatpy.plot.spectra().\nIf true, the data inside the range is scaled up to fit the entire plotting window.

    \n", "annotation": ": bool"}, {"fullname": "aton.spectra.classes.Scaling.set_x", "modulename": "aton.spectra.classes", "qualname": "Scaling.set_x", "kind": "function", "doc": "

    Override with an horizontal range.

    \n", "signature": "(self, xmin: float = None, xmax: float = None):", "funcdef": "def"}, {"fullname": "aton.spectra.classes.Scaling.set_y", "modulename": "aton.spectra.classes", "qualname": "Scaling.set_y", "kind": "function", "doc": "

    Override with a vertical range.

    \n", "signature": "(self, ymin: list = None, ymax: list = None):", "funcdef": "def"}, {"fullname": "aton.spectra.classes.Spectra", "modulename": "aton.spectra.classes", "qualname": "Spectra", "kind": "class", "doc": "

    Spectra object. Used to load and process spectral data.

    \n\n

    Most functions present in the atom.spectra module receive this object as input.

    \n\n

    Use example: to load two INS spectra CSV files from MANTID with cm$^{-1}$ as input units,\nand plot them in meV units, normalizing their heights over the range from 20 to 50 meV:

    \n\n
    \n
    import maatpy as mt\nins = mt.Spectra(\n    type='INS',\n    files=['example_1.csv', 'example_2.csv'],\n    units_in='cm-1',\n    units='meV',\n    plotting=mt.Plotting(\n        title='Calculated INS',\n        normalize=True,\n        ),\n    scaling=mt.Scaling(\n        xmin=20,\n        xmax=50,\n        ),\n    )\nmt.plot.spectra(ins)\n
    \n
    \n\n

    Check more use examples in the /examples/ folder.

    \n\n

    Below is a list of the available parameters for the Spectra object, along with their descriptions.

    \n"}, {"fullname": "aton.spectra.classes.Spectra.__init__", "modulename": "aton.spectra.classes", "qualname": "Spectra.__init__", "kind": "function", "doc": "

    All values can be set when initializing the Spectra object.

    \n", "signature": "(\ttype: str = None,\tcomment: str = None,\tfiles=None,\tdfs=None,\tunits=None,\tunits_in=None,\tplotting: aton.spectra.classes.Plotting = <aton.spectra.classes.Plotting object>,\tscaling: aton.spectra.classes.Scaling = <aton.spectra.classes.Scaling object>)"}, {"fullname": "aton.spectra.classes.Spectra.type", "modulename": "aton.spectra.classes", "qualname": "Spectra.type", "kind": "variable", "doc": "

    Type of the spectra: 'INS', 'ATR', or 'RAMAN'.

    \n"}, {"fullname": "aton.spectra.classes.Spectra.comment", "modulename": "aton.spectra.classes", "qualname": "Spectra.comment", "kind": "variable", "doc": "

    Custom comment. If Plotting.title is None, it will be the title of the plot.

    \n"}, {"fullname": "aton.spectra.classes.Spectra.files", "modulename": "aton.spectra.classes", "qualname": "Spectra.files", "kind": "variable", "doc": "

    List containing the files with the spectral data.\nLoaded automatically with Pandas at initialization.\nIn order for Pandas to read the files properly, note that the column lines must start by #.\nAny additional line that is not data must be removed or commented with #.\nCSV files must be formatted with the first column as the energy or energy transfer,\nand the second column with the intensity or absorbance, depending on the case. An additional third 'Error' column can be used.

    \n"}, {"fullname": "aton.spectra.classes.Spectra.dfs", "modulename": "aton.spectra.classes", "qualname": "Spectra.dfs", "kind": "variable", "doc": "

    List containing the pandas dataframes with the spectral data.\nLoaded automatically from the files at initialization.

    \n"}, {"fullname": "aton.spectra.classes.Spectra.units", "modulename": "aton.spectra.classes", "qualname": "Spectra.units", "kind": "variable", "doc": "

    Target units of the spectral data. Can be 'meV' or 'cm-1', written as any of the variants listed in aton.alias.units[unit].

    \n"}, {"fullname": "aton.spectra.classes.Spectra.units_in", "modulename": "aton.spectra.classes", "qualname": "Spectra.units_in", "kind": "variable", "doc": "

    Input units of the spectral data, used in the input CSV files. Can be 'meV' or 'cm-1', written as any of the variants listed in aton.alias.units[unit].\nIf the input CSV files have different units, it can also be set as a list of the same length of the number of input files, eg. ['meV', 'cm-1', 'cm-1'].

    \n"}, {"fullname": "aton.spectra.classes.Spectra.plotting", "modulename": "aton.spectra.classes", "qualname": "Spectra.plotting", "kind": "variable", "doc": "

    Plotting object, used to set the plotting options.

    \n"}, {"fullname": "aton.spectra.classes.Spectra.scaling", "modulename": "aton.spectra.classes", "qualname": "Spectra.scaling", "kind": "variable", "doc": "

    Scaling object, used to set the normalization parameters.

    \n"}, {"fullname": "aton.spectra.classes.Spectra.set_units", "modulename": "aton.spectra.classes", "qualname": "Spectra.set_units", "kind": "function", "doc": "

    Method to change between spectral units. ALWAYS use this method to do that.

    \n\n

    For example, to change from cm-1 to meV:

    \n\n
    \n
    # Spectra.set_units(desired_units, units_input)\nSpectra.set_units('meV', 'cm-1')\n
    \n
    \n", "signature": "(self, units, units_in=None, default_unit='cm-1'):", "funcdef": "def"}, {"fullname": "aton.spectra.classes.Material", "modulename": "aton.spectra.classes", "qualname": "Material", "kind": "class", "doc": "

    Material class.\nUsed to calculate molar masses and cross sections,\nand to pass data to different analysis functions\nsuch as aton.spectra.deuterium.impulse_approx().

    \n"}, {"fullname": "aton.spectra.classes.Material.__init__", "modulename": "aton.spectra.classes", "qualname": "Material.__init__", "kind": "function", "doc": "

    All values can be set when initializing the Material object.\nHowever, it is recommended to only set the elements and the grams,\nand optionally the name, and calculate the rest with Material.set().

    \n", "signature": "(\telements: dict,\tname: str = None,\tgrams: float = None,\tgrams_error: float = None,\tmols: float = None,\tmols_error: float = None,\tmolar_mass: float = None,\tcross_section: float = None,\tpeaks: dict = None)"}, {"fullname": "aton.spectra.classes.Material.elements", "modulename": "aton.spectra.classes", "qualname": "Material.elements", "kind": "variable", "doc": "

    Dict of atoms in the material, as in {'H': 6, 'C':1, 'N':1}.\nIsotopes can be expressed as 'H2', 'He4', etc. with the atom symbol + isotope mass number.

    \n"}, {"fullname": "aton.spectra.classes.Material.name", "modulename": "aton.spectra.classes", "qualname": "Material.name", "kind": "variable", "doc": "

    String with the name of the material.

    \n"}, {"fullname": "aton.spectra.classes.Material.grams", "modulename": "aton.spectra.classes", "qualname": "Material.grams", "kind": "variable", "doc": "

    Mass, in grams.

    \n"}, {"fullname": "aton.spectra.classes.Material.grams_error", "modulename": "aton.spectra.classes", "qualname": "Material.grams_error", "kind": "variable", "doc": "

    Error of the measured mass in grams.\nSet automatically with Material.set().

    \n"}, {"fullname": "aton.spectra.classes.Material.mols", "modulename": "aton.spectra.classes", "qualname": "Material.mols", "kind": "variable", "doc": "

    Number of moles.\nSet automatically with Material.set().

    \n"}, {"fullname": "aton.spectra.classes.Material.mols_error", "modulename": "aton.spectra.classes", "qualname": "Material.mols_error", "kind": "variable", "doc": "

    Error of the number of moles.\nSet automatically with Material.set().

    \n"}, {"fullname": "aton.spectra.classes.Material.molar_mass", "modulename": "aton.spectra.classes", "qualname": "Material.molar_mass", "kind": "variable", "doc": "

    Molar mass of the material, in mol/g.\nCalculated automatically with Material.set().

    \n"}, {"fullname": "aton.spectra.classes.Material.cross_section", "modulename": "aton.spectra.classes", "qualname": "Material.cross_section", "kind": "variable", "doc": "

    Neutron total bound scattering cross section, in barns.\nCalculated automatically with Material.set().

    \n"}, {"fullname": "aton.spectra.classes.Material.peaks", "modulename": "aton.spectra.classes", "qualname": "Material.peaks", "kind": "variable", "doc": "

    Dict with interesting peaks that you might want to store for later use.

    \n"}, {"fullname": "aton.spectra.classes.Material.set", "modulename": "aton.spectra.classes", "qualname": "Material.set", "kind": "function", "doc": "

    Set the molar mass, cross section and errors of the material.

    \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "aton.spectra.classes.Material.print", "modulename": "aton.spectra.classes", "qualname": "Material.print", "kind": "function", "doc": "

    Print a summary with the material information.

    \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "aton.spectra.deuterium", "modulename": "aton.spectra.deuterium", "kind": "module", "doc": "

    Description

    \n\n

    This module contains methods to calculate deuteration levels from different spectra.

    \n\n

    Index

    \n\n\n\n
    \n"}, {"fullname": "aton.spectra.deuterium.impulse_approx", "modulename": "aton.spectra.deuterium", "qualname": "impulse_approx", "kind": "function", "doc": "

    Calculate the deuteration levels from INS spectra\nwith the Impulse Approximation, see\nhttps://www.tandfonline.com/doi/full/10.1080/00018732.2017.1317963.

    \n\n

    Protonated and deuterated materials must be specified\nas aton.spectra.Material objects.\nThe threshold controls the start of the plateau (in meV)\nto start considering Deep Inelastic Neutron Scattering (DINS).\nThe protonated and deuterated dataframe indexes are specified\nby H_df_index and D_df_index, respectively.

    \n\n

    In this approximation, the ideal ratio between\nthe cross-sections and the experimental ratio between\nthe pleteaus at high energies should be the same:\n$$\n\\frac{\\text{plateau_D}}{\\text{plateau_H}} \\approx \\frac{\\text{cross_section_D}}{\\text{cross_section_H}}\n$$\nTaking this into account, the deuteration is estimated as:\n$$\n\\text{Deuteration} = \\frac{1-\\text{real_ratio}}{1-\\text{ideal_ratio}}\n$$

    \n\n
    \nWarning\n

    This approximation is very sensitive to the mass sample, specified by aton.spectra.Material.grams.

    \n
    \n", "signature": "(\tins: aton.spectra.classes.Spectra,\tmaterial_H: aton.spectra.classes.Material,\tmaterial_D: aton.spectra.classes.Material,\tthreshold: float = 600,\tH_df_index: int = 0,\tD_df_index: int = 1) -> tuple:", "funcdef": "def"}, {"fullname": "aton.spectra.deuterium.peaks_mapi", "modulename": "aton.spectra.deuterium", "qualname": "peaks_mapi", "kind": "function", "doc": "

    Calculate the deuteration of your CH$_3$NH$_3$PbI$_3$ samples\nby integrating the INS disrotatory peaks,\nwhich appear at around 38 meV for the fully protonated sample.\nNote that peaks must be a dictionary with the peak limits\nand the baseline, as in the example below:

    \n\n
    \n
    peaks = {\n    'baseline' : None,\n    'baseline_error' : None,\n    'h6d0' : [41, 43],\n    'h5d1' : [41, 43],\n    'h4d2' : [41, 43],\n    'h3d3' : [34.7, 37.3],\n    'h2d4' : [31.0, 33.0],\n    'h1d5' : [28.0, 30.5],\n    'h0d6' : [26.5, 28.0],\n    }\n
    \n
    \n\n

    Peak keywords required for selective deuteration (only C or only N):\nh6d0, h5d1, h4d2, h3d3.\nAdditional peak keywords required for total deuteration:\nh2d4, h1d5, h0d6.\nIf some peak is not present in your sample,\njust set the limits to a small baseline plateau.

    \n", "signature": "(ins: aton.spectra.classes.Spectra, peaks: dict, df_index: int = 0) -> str:", "funcdef": "def"}, {"fullname": "aton.spectra.fit", "modulename": "aton.spectra.fit", "kind": "module", "doc": "

    Description

    \n\n

    This module contains functions for fitting and analyzing spectral data.

    \n\n

    Index

    \n\n\n\n
    \n"}, {"fullname": "aton.spectra.fit.plateau", "modulename": "aton.spectra.fit", "qualname": "plateau", "kind": "function", "doc": "

    Fit the mean value and the error of a plateau in a aton.spectra.Spectra object.

    \n\n

    If aton.spectra.Spectra.dfs[df_index] has an 'Error' column, those errors are also taken into account\nalong with the standard deviation of the mean, else only the standard deviation is considered.\nThe 'Error' column title can be any string in maatpy.alias.file['Error'].

    \n\n

    Use as maatpy.fit.plateau(spectra, cuts=[low_cut, high_cut], df_index=0).\nNote that cuts, low_cut and/or top_cut can be set to None.

    \n", "signature": "(\tspectra: aton.spectra.classes.Spectra,\tcuts=None,\tdf_index: int = 0) -> tuple:", "funcdef": "def"}, {"fullname": "aton.spectra.fit.area_under_peak", "modulename": "aton.spectra.fit", "qualname": "area_under_peak", "kind": "function", "doc": "

    Calculate the area under a given peak.

    \n\n

    Peaks must be defined as peak:list=[xmin, xmax, baseline=0, baseline_error=0].\nIf the dataset has no Error column, the error for each point is assumed to be the same\nas the baseline error if errors_as_in_baseline=True, otherwise it is assumed to be zero.\nIf min_as_baseline=True and baseline=0, the baseline is assumed to be the minimum value.\nAlso, if min_as_baseline=True and there are negative areas even after applying the baseline,\nthe baseline will be corrected to the minimum value.

    \n", "signature": "(\tspectra: aton.spectra.classes.Spectra,\tpeak: list,\tdf_index: int = 0,\terrors_as_in_baseline: bool = True,\tmin_as_baseline: bool = False) -> tuple:", "funcdef": "def"}, {"fullname": "aton.spectra.fit.ratio_areas", "modulename": "aton.spectra.fit", "qualname": "ratio_areas", "kind": "function", "doc": "

    Check the ratio between two areas, e.g. to estimate deuteration levels from ATR data.

    \n\n

    The ratio is calculated as area / area_total. This behavior is modified if inverse_ratio = True,\nso that the ratio is calculated as (area_total - area) / area_total.\nNote that changing the ratio calculation also affects the error propagation.

    \n", "signature": "(\tarea: float,\tarea_total: float,\tarea_error: float = 0.0,\tarea_total_error: float = 0.0,\tinverse_ratio: bool = False) -> tuple:", "funcdef": "def"}, {"fullname": "aton.spectra.fit.mean", "modulename": "aton.spectra.fit", "qualname": "mean", "kind": "function", "doc": "

    Takes an array of numerical values and returns the mean and standard deviation.

    \n\n

    It is calculated with numpy as:

    \n\n

    $\\sigma_{x}=\\sqrt{\\frac{\\sum{(x_{i}-{\\overline{x}})^2}}{N-\\text{ddof}}}$

    \n\n

    where ddof are the delta degrees_of_freedom, zero by default.\nSet it to 1 for a corrected sample standard deviation (low N cases),\nsee more details here.

    \n\n

    The mean is rounded up to the order of the error by default. To override this behaviour, set rounded=False.

    \n", "signature": "(array: list, rounded: bool = True, degrees_of_freedom=0) -> tuple:", "funcdef": "def"}, {"fullname": "aton.spectra.normalize", "modulename": "aton.spectra.normalize", "kind": "module", "doc": "

    Description

    \n\n

    This module contains functions to normalize data and other variables.

    \n\n

    Index

    \n\n\n\n
    \n"}, {"fullname": "aton.spectra.normalize.unit_str", "modulename": "aton.spectra.normalize", "qualname": "unit_str", "kind": "function", "doc": "

    Normalize a given unit string to a standarized unit string, following maatpy.alias.unit.

    \n", "signature": "(unit: str):", "funcdef": "def"}, {"fullname": "aton.spectra.normalize.spectra", "modulename": "aton.spectra.normalize", "qualname": "spectra", "kind": "function", "doc": "

    Normalize the given spectra by height, with optional maatpy.classes.Scaling attributes.

    \n", "signature": "(spectra: aton.spectra.classes.Spectra):", "funcdef": "def"}, {"fullname": "aton.spectra.normalize.area", "modulename": "aton.spectra.normalize", "qualname": "area", "kind": "function", "doc": "

    Normalize the given spectra by the area under the datasets, with optional maatpy.classes.Scaling attributes.

    \n", "signature": "(spectra: aton.spectra.classes.Spectra):", "funcdef": "def"}, {"fullname": "aton.spectra.plot", "modulename": "aton.spectra.plot", "kind": "module", "doc": "

    Description

    \n\n

    This module loads the plot() function, used to plot aton.spectra.SpectraData data.

    \n\n
    \n"}, {"fullname": "aton.spectra.plot.plot", "modulename": "aton.spectra.plot", "qualname": "plot", "kind": "function", "doc": "

    Plot the given spectra, with optional maatpy.classes.Plotting and maatpy.classes.Scaling attributes.

    \n", "signature": "(spectrum: aton.spectra.classes.Spectra):", "funcdef": "def"}, {"fullname": "aton.spectra.samples", "modulename": "aton.spectra.samples", "kind": "module", "doc": "

    Description

    \n\n

    This module contains premade examples of material compositions.\nThe aton.spectra.Material.grams is yet to be provided,\nbefore setting the material with aton.spectra.Material.set().

    \n\n
    \n"}, {"fullname": "aton.spectra.samples.MAPbI3", "modulename": "aton.spectra.samples", "qualname": "MAPbI3", "kind": "variable", "doc": "

    CH$_3$NH$_3$PbI$_3$

    \n", "default_value": "<aton.spectra.classes.Material object>"}, {"fullname": "aton.spectra.samples.CD3ND3PbI3", "modulename": "aton.spectra.samples", "qualname": "CD3ND3PbI3", "kind": "variable", "doc": "

    CD$_3$ND$_3$PbI$_3$.\nWith experimental values of the partially-deuterated amine peaks\nfor the disrotatory mode of MAPbI3's methylammonium.\nMeasured at TOSCA, ISIS RAL, UK, May 2024.

    \n", "default_value": "<aton.spectra.classes.Material object>"}, {"fullname": "aton.spectra.samples.CH3ND3PbI3", "modulename": "aton.spectra.samples", "qualname": "CH3ND3PbI3", "kind": "variable", "doc": "

    CH$_3$ND$_3$PbI$_3$

    \n", "default_value": "<aton.spectra.classes.Material object>"}, {"fullname": "aton.spectra.samples.CD3NH3PbI3", "modulename": "aton.spectra.samples", "qualname": "CD3NH3PbI3", "kind": "variable", "doc": "

    CD$_3$NH$_3$PbI$_3$

    \n", "default_value": "<aton.spectra.classes.Material object>"}, {"fullname": "aton.spectra.samples.CH3NH3I", "modulename": "aton.spectra.samples", "qualname": "CH3NH3I", "kind": "variable", "doc": "

    CH$_3$NH$_3$I

    \n", "default_value": "<aton.spectra.classes.Material object>"}, {"fullname": "aton.spectra.samples.CH3ND3I", "modulename": "aton.spectra.samples", "qualname": "CH3ND3I", "kind": "variable", "doc": "

    CH$_3$ND$_3$I

    \n", "default_value": "<aton.spectra.classes.Material object>"}, {"fullname": "aton.text", "modulename": "aton.text", "kind": "module", "doc": "

    Description

    \n\n

    This module contains tools for general text operations.

    \n\n

    Index

    \n\n\n"}, {"fullname": "aton.text.edit", "modulename": "aton.text.edit", "kind": "module", "doc": "

    Description

    \n\n

    Functions to manipulate the content of text files.

    \n\n

    Index

    \n\n\n\n
    \n"}, {"fullname": "aton.text.edit.insert_at", "modulename": "aton.text.edit", "qualname": "insert_at", "kind": "function", "doc": "

    Inserts a text in the line with position index of a given filepath.\nIf position is negative, starts from the end of the file.

    \n", "signature": "(filepath, text: str, position: int) -> None:", "funcdef": "def"}, {"fullname": "aton.text.edit.insert_under", "modulename": "aton.text.edit", "qualname": "insert_under", "kind": "function", "doc": "

    Inserts the given text string under the line(s) containing\nthe key in the given filepath.\nThe keyword can be at any position within the line.\nBy default all matches are inserted with insertions=0,\nbut it can insert only a specific number of matches\nwith positive numbers (1, 2...), or starting from the bottom with negative numbers.\nThe text can be introduced after a specific number of lines after the match,\nchanging the value skips. Negative integers introduce the text in the previous lines.\nRegular expressions can be used by setting regex=True.

    \n", "signature": "(\tfilepath,\tkey: str,\ttext: str,\tinsertions: int = 0,\tskips: int = 0,\tregex: bool = False) -> None:", "funcdef": "def"}, {"fullname": "aton.text.edit.replace", "modulename": "aton.text.edit", "qualname": "replace", "kind": "function", "doc": "

    Replaces the key string with the text string in the specified filepath.\nTo search with regular expressions, set regex=True.

    \n\n

    It can also be used to delete the keyword with text=''.

    \n\n

    The value replacements specifies the number of replacements to perform:\n1 to replace only the first keyword found, 2, 3...\nUse negative values to replace from the end of the file,\neg. to replace the last found key, use replacements=-1.\nTo replace all values, set replacements = 0, which is the value by default.

    \n\n
    line... key ...line -> line... text ...line\n
    \n", "signature": "(\tfilepath: str,\tkey: str,\ttext: str,\treplacements: int = 0,\tregex: bool = False) -> None:", "funcdef": "def"}, {"fullname": "aton.text.edit.replace_line", "modulename": "aton.text.edit", "qualname": "replace_line", "kind": "function", "doc": "

    Replaces the entire line(s) containing the key string with the text string in the specified filepath.\nRegular expressions can be used with regex=True.

    \n\n

    It can be used to delete line(s) by setting text=''.

    \n\n

    The value replacements specifies the number of lines to replace:\n1 to replace only the first line with the keyword, 2, 3...\nUse negative values to replace from the end of the file,\ne.g., to replace only the last line containing the keyword, use replacements = -1.\nTo replace all lines, set replacements = 0, which is the value by default.

    \n\n

    The default line to replace is the matching line,\nbut it can be any other specific line after or before the matching line;\nthis is indicated with skips as a positive or negative integer.

    \n\n

    More lines can be replaced with additional lines (int).\nNote that the matched line plus the additional lines will be replaced, this is, additional lines +1.

    \n", "signature": "(\tfilepath: str,\tkey: str,\ttext: str,\treplacements: int = 0,\tskips: int = 0,\tadditional: int = 0,\tregex: bool = False) -> None:", "funcdef": "def"}, {"fullname": "aton.text.edit.replace_between", "modulename": "aton.text.edit", "qualname": "replace_between", "kind": "function", "doc": "

    Replace lines with a given text, between the keywords key1 and key2 in a specified filepath.\nRegular expressions can be used by setting regex=True.

    \n\n

    It can be used to delete the text between the keys by setting text=''.

    \n\n

    Key lines are also deleted if delete_keys=True.

    \n\n

    Only the first matches of the keywords are used by default;\nyou can use the last ones with from_end = True.

    \n\n
    lines...\nkey1\ntext\nkey2\nlines...\n
    \n", "signature": "(\tfilepath: str,\tkey1: str,\tkey2: str,\ttext: str,\tdelete_keys: bool = False,\tfrom_end: bool = False,\tregex: bool = False) -> None:", "funcdef": "def"}, {"fullname": "aton.text.edit.delete_under", "modulename": "aton.text.edit", "qualname": "delete_under", "kind": "function", "doc": "

    Deletes all the content under the line containing the key in the specified filepath.\nThe keyword can be at any position within the line.\nRegular expressions can be used by setting regex=True.

    \n\n

    By default the first matches is used; it can be any positive integer (0 is treated as 1!),\nincluding negative integers to select a match starting from the end of the file.

    \n\n

    The content can be deleted after a specific number of lines after the match,\nchanging the value skips, that skips the specified number of lines.\nNegative integers start deleting the content from the previous lines.

    \n", "signature": "(\tfilepath,\tkey: str,\tmatches: int = 1,\tskips: int = 0,\tregex: bool = False) -> None:", "funcdef": "def"}, {"fullname": "aton.text.edit.correct_with_dict", "modulename": "aton.text.edit", "qualname": "correct_with_dict", "kind": "function", "doc": "

    Corrects the given text file filepath using a correct dictionary.

    \n", "signature": "(filepath: str, correct: dict) -> None:", "funcdef": "def"}, {"fullname": "aton.text.edit.from_template", "modulename": "aton.text.edit", "qualname": "from_template", "kind": "function", "doc": "

    Copies an old text file to a new file,\ncorrecting the output file with a correct dictionary.\nAdditionally, it can add a comment at the beginning of the new file.

    \n", "signature": "(old: str, new: str, correct: dict = None, comment: str = None) -> None:", "funcdef": "def"}, {"fullname": "aton.text.extract", "modulename": "aton.text.extract", "kind": "module", "doc": "

    Description

    \n\n

    Functions to extract data from raw text strings.

    \n\n

    Index

    \n\n\n\n
    \n"}, {"fullname": "aton.text.extract.number", "modulename": "aton.text.extract", "qualname": "number", "kind": "function", "doc": "

    Extracts the float value of a given name variable from a raw text.

    \n\n

    Example:

    \n\n
    \n
    >>> text = 'energy =   500.0 Ry'\n>>> thotpy.extract.number(text, 'energy')\n500.0  # float output\n
    \n
    \n", "signature": "(text: str, name: str = '') -> float:", "funcdef": "def"}, {"fullname": "aton.text.extract.string", "modulename": "aton.text.extract", "qualname": "string", "kind": "function", "doc": "

    Extracts the text value of a given name variable from a raw string.\nStops before an optional stop string.\nRemoves leading and trailing commas by default, change this with strip=False.

    \n\n

    Example:

    \n\n
    \n
    >>> text = 'energy =   500.0 Ry were calculated'\n>>> thotpy.extract.string(text, 'energy', 'were')\n'500.0 Ry'  # String output\n
    \n
    \n", "signature": "(text: str, name: str = '', stop: str = '', strip: bool = True) -> str:", "funcdef": "def"}, {"fullname": "aton.text.extract.column", "modulename": "aton.text.extract", "qualname": "column", "kind": "function", "doc": "

    Extracts the desired float column index of a given string (0 by default).

    \n", "signature": "(text: str, column: int = 0) -> float:", "funcdef": "def"}, {"fullname": "aton.text.extract.coords", "modulename": "aton.text.extract", "qualname": "coords", "kind": "function", "doc": "

    Returns a list with the float coordinates expressed in a given text string.

    \n", "signature": "(text: str) -> list:", "funcdef": "def"}, {"fullname": "aton.text.extract.element", "modulename": "aton.text.extract", "qualname": "element", "kind": "function", "doc": "

    Extract a chemical element from a raw text string.

    \n\n

    If there are several elements, you can return a specific index match (positive, 0 by default).\nAllows for standard elements (H, He, Na...) and isotopes (H2, He4...).

    \n", "signature": "(text: str, index: int = 0) -> str:", "funcdef": "def"}, {"fullname": "aton.text.find", "modulename": "aton.text.find", "kind": "module", "doc": "

    Description

    \n\n

    Functions to search for specific content inside text files.

    \n\n

    Index

    \n\n

    Functions to find and return specific text strings:

    \n\n\n\n

    Functions to find the position in the file of specific text strings:

    \n\n\n\n
    \n"}, {"fullname": "aton.text.find.lines", "modulename": "aton.text.find", "qualname": "lines", "kind": "function", "doc": "

    Finds the line(s) containing the key string in the given filepath,\nreturning a list with the matches.

    \n\n

    The value matches specifies the max number of matches to be returned.\nDefaults to 0 to return all possible matches. Set it to 1 to return only one match,\nor to negative integers to start the search from the end of the file upwards.

    \n\n

    The value additional specifies the number of additional lines\nbelow the target line that are also returned;\n2 to return the found line plus two additional lines below, etc.\nNegative values return the specified number of lines before the target line.\nThe original ordering from the file is preserved.\nDefaults to additional=0, only returning the target line.\nBy default, the additional lines are returned in the same list item as the match separated by a \\n,\nunless split=True, in which case these additional lines\nare splitted and added as additional items in the list.\nThis way, split=False allows to differentiate between matches.

    \n\n

    To use regular expressions in the search, set regex=True.\nBy default regex search is deactivated, using the faster mmap.find and rfind methods instead.

    \n", "signature": "(\tfilepath: str,\tkey: str,\tmatches: int = 0,\tadditional: int = 0,\tsplit: bool = False,\tregex: bool = False) -> list:", "funcdef": "def"}, {"fullname": "aton.text.find.between", "modulename": "aton.text.find", "qualname": "between", "kind": "function", "doc": "

    Returns the content between the lines with key1 and key2 in the given filepath.\nKeywords can be at any position within the line.\nRegular expressions can be used by setting regex=True.

    \n\n

    Key lines are omited by default, but can be returned with include_keys=True.

    \n\n

    If there is more than one match, only the first one is considered by default;\nset match (int) to specify a particular match (1, 2... 0 is considered as 1!).\nUse negative numbers to start from the end of the file.

    \n", "signature": "(\tfilepath: str,\tkey1: str,\tkey2: str,\tinclude_keys: bool = True,\tmatch: int = 1,\tregex: bool = False) -> list:", "funcdef": "def"}, {"fullname": "aton.text.find.pos", "modulename": "aton.text.find", "qualname": "pos", "kind": "function", "doc": "

    Returns a list of the positions of a key in a given filepath (whether file or memory mapped file).

    \n\n

    The value matches specifies the max number of matches to return.\nDefaults to 0 to return all possible matches. Set it to 1 to return only one match,\n2 to get the first two matches, etc.\nYou can also set it to negative integers to start searching from the end of the file upwards.

    \n\n

    This method is faster than pos_regex(), but does not search for regular expressions.

    \n", "signature": "(filepath, key: str, matches: int = 0) -> list:", "funcdef": "def"}, {"fullname": "aton.text.find.pos_regex", "modulename": "aton.text.find", "qualname": "pos_regex", "kind": "function", "doc": "

    Returns a list of the positions of a key in a given filepath (actual file, not mmapped!).

    \n\n

    The value matches specifies the max number of matches to return.\nDefaults to 0 to return all possible matches. Set it to 1 to return only one match,\nor to negative integers to start searching from the end of the file upwards.

    \n\n

    This method is slower than pos(), but it can search for regular expressions.

    \n", "signature": "(filepath, key: str, matches: int = 0) -> list:", "funcdef": "def"}, {"fullname": "aton.text.find.next_pos", "modulename": "aton.text.find", "qualname": "next_pos", "kind": "function", "doc": "

    Returns the next position of the key string in the given filepath (file or mmapped file),\nstarting from an initial position tuple.\nThe match number specifies the nonzero index of the next match to return (1, 2... 0 is considered as 1!).\nIt can be negative to search backwards from the initial position.\nThe last known positions will be returned if no more matches are found.

    \n\n

    This method is specific for normal strings.\nTo use regular expressions, check next_pos_regex().

    \n", "signature": "(filepath, position: tuple, key: str, match: int = 1) -> tuple:", "funcdef": "def"}, {"fullname": "aton.text.find.next_pos_regex", "modulename": "aton.text.find", "qualname": "next_pos_regex", "kind": "function", "doc": "

    Returns the next position of the key string in the given filepath\n(actual file, not mmapped!), starting from an initial position tuple.\nThe match number specifies the next match to return (1, 2... 0 is considered as 1!).\nIt can be negative to search backwards from the initial position.\nThis method is specific for regular expressions.

    \n\n

    For normal strings, check the faster next_pos() method.

    \n", "signature": "(filepath, position: tuple, key: str, match: int = 0) -> tuple:", "funcdef": "def"}, {"fullname": "aton.text.find.line_pos", "modulename": "aton.text.find", "qualname": "line_pos", "kind": "function", "doc": "

    Returns the position of the full line containing the position tuple,\nin the given filepath (whether file or memory mapped file).\nA specific line below can be returned with skips being a natural int,\nor previous lines with negative values.

    \n", "signature": "(filepath, position: tuple, skips: int = 0) -> tuple:", "funcdef": "def"}, {"fullname": "aton.text.find.between_pos", "modulename": "aton.text.find", "qualname": "between_pos", "kind": "function", "doc": "

    Returns the positions of the content between the lines containing\nkey1 and key2 in the given filepath.\nKeywords can be at any position within the line.\nRegular expressions can be used by setting regex=True.

    \n\n

    Key lines are omited by default, but can be returned with include_keys=True.

    \n\n

    If there is more than one match, only the first one is considered by default;\nset match number to specify a particular match (1, 2... 0 is considered as 1!).\nUse negative numbers to start from the end of the file.

    \n", "signature": "(\tfilepath,\tkey1: str,\tkey2: str,\tinclude_keys: bool = True,\tmatch: int = 1,\tregex: bool = False) -> tuple:", "funcdef": "def"}, {"fullname": "aton.units", "modulename": "aton.units", "kind": "module", "doc": "

    Description

    \n\n

    This module contains useful constants and conversion factors.

    \n\n

    Index

    \n\n\n\n

    References

    \n\n

    These values come from the 2022 CODATA Internationally\nrecommended 2022 values of the Fundamental Physical Constants.

    \n\n
    \n\n

    Energy conversion factors

    \n\n

    Note that cm refers to cm$^{-1}$.

    \n"}, {"fullname": "aton.units.eV_to_meV", "modulename": "aton.units", "qualname": "eV_to_meV", "kind": "variable", "doc": "

    \n", "default_value": "1000.0"}, {"fullname": "aton.units.meV_to_eV", "modulename": "aton.units", "qualname": "meV_to_eV", "kind": "variable", "doc": "

    \n", "default_value": "0.001"}, {"fullname": "aton.units.meV_to_cm", "modulename": "aton.units", "qualname": "meV_to_cm", "kind": "variable", "doc": "

    \n", "default_value": "8.0655"}, {"fullname": "aton.units.cm_to_meV", "modulename": "aton.units", "qualname": "cm_to_meV", "kind": "variable", "doc": "

    \n", "default_value": "0.12398487384539086"}, {"fullname": "aton.units.eV_to_J", "modulename": "aton.units", "qualname": "eV_to_J", "kind": "variable", "doc": "

    \n", "default_value": "1.602176634e-19"}, {"fullname": "aton.units.J_to_eV", "modulename": "aton.units", "qualname": "J_to_eV", "kind": "variable", "doc": "

    \n", "default_value": "6.241509074460763e+18"}, {"fullname": "aton.units.meV_to_J", "modulename": "aton.units", "qualname": "meV_to_J", "kind": "variable", "doc": "

    \n", "default_value": "1.6021766339999998e-22"}, {"fullname": "aton.units.J_to_meV", "modulename": "aton.units", "qualname": "J_to_meV", "kind": "variable", "doc": "

    \n", "default_value": "6.241509074460763e+21"}, {"fullname": "aton.units.Ry_to_eV", "modulename": "aton.units", "qualname": "Ry_to_eV", "kind": "variable", "doc": "

    \n", "default_value": "13.60569312299"}, {"fullname": "aton.units.eV_to_Ry", "modulename": "aton.units", "qualname": "eV_to_Ry", "kind": "variable", "doc": "

    \n", "default_value": "0.07349864435133158"}, {"fullname": "aton.units.Ry_to_J", "modulename": "aton.units", "qualname": "Ry_to_J", "kind": "variable", "doc": "

    \n", "default_value": "2.179872361103e-18"}, {"fullname": "aton.units.J_to_Ry", "modulename": "aton.units", "qualname": "J_to_Ry", "kind": "variable", "doc": "

    \n", "default_value": "4.5874245567938074e+17"}, {"fullname": "aton.units.cal_to_J", "modulename": "aton.units", "qualname": "cal_to_J", "kind": "variable", "doc": "

    \n", "default_value": "4.184"}, {"fullname": "aton.units.J_to_cal", "modulename": "aton.units", "qualname": "J_to_cal", "kind": "variable", "doc": "

    \n", "default_value": "0.2390057361376673"}, {"fullname": "aton.units.kcal_to_J", "modulename": "aton.units", "qualname": "kcal_to_J", "kind": "variable", "doc": "

    \n", "default_value": "4184.0"}, {"fullname": "aton.units.J_to_kcal", "modulename": "aton.units", "qualname": "J_to_kcal", "kind": "variable", "doc": "
    \n\n

    Distance conversion factors

    \n\n

    Note that A refers to Angstroms.

    \n", "default_value": "0.0002390057361376673"}, {"fullname": "aton.units.A_to_m", "modulename": "aton.units", "qualname": "A_to_m", "kind": "variable", "doc": "

    \n", "default_value": "1e-10"}, {"fullname": "aton.units.m_to_A", "modulename": "aton.units", "qualname": "m_to_A", "kind": "variable", "doc": "

    \n", "default_value": "10000000000.0"}, {"fullname": "aton.units.bohr_to_m", "modulename": "aton.units", "qualname": "bohr_to_m", "kind": "variable", "doc": "

    \n", "default_value": "5.29177210544e-11"}, {"fullname": "aton.units.m_to_bohr", "modulename": "aton.units", "qualname": "m_to_bohr", "kind": "variable", "doc": "

    \n", "default_value": "18897261259.077824"}, {"fullname": "aton.units.A_to_bohr", "modulename": "aton.units", "qualname": "A_to_bohr", "kind": "variable", "doc": "

    \n", "default_value": "1.8897261259077824"}, {"fullname": "aton.units.bohr_to_A", "modulename": "aton.units", "qualname": "bohr_to_A", "kind": "variable", "doc": "
    \n\n

    Mass conversion factors

    \n", "default_value": "0.529177210544"}, {"fullname": "aton.units.amu_to_kg", "modulename": "aton.units", "qualname": "amu_to_kg", "kind": "variable", "doc": "

    \n", "default_value": "1.6605390666e-27"}, {"fullname": "aton.units.kg_to_amu", "modulename": "aton.units", "qualname": "kg_to_amu", "kind": "variable", "doc": "

    \n", "default_value": "6.022140762081123e+26"}, {"fullname": "aton.units.kg_to_g", "modulename": "aton.units", "qualname": "kg_to_g", "kind": "variable", "doc": "

    \n", "default_value": "1000.0"}, {"fullname": "aton.units.g_to_kg", "modulename": "aton.units", "qualname": "g_to_kg", "kind": "variable", "doc": "
    \n\n

    Pressure conversion factors

    \n", "default_value": "0.001"}, {"fullname": "aton.units.GPa_to_Pa", "modulename": "aton.units", "qualname": "GPa_to_Pa", "kind": "variable", "doc": "

    \n", "default_value": "1000000000.0"}, {"fullname": "aton.units.Pa_to_GPa", "modulename": "aton.units", "qualname": "Pa_to_GPa", "kind": "variable", "doc": "

    \n", "default_value": "1e-09"}, {"fullname": "aton.units.kbar_to_bar", "modulename": "aton.units", "qualname": "kbar_to_bar", "kind": "variable", "doc": "

    \n", "default_value": "1000.0"}, {"fullname": "aton.units.bar_to_kbar", "modulename": "aton.units", "qualname": "bar_to_kbar", "kind": "variable", "doc": "

    \n", "default_value": "0.001"}, {"fullname": "aton.units.Pa_to_bar", "modulename": "aton.units", "qualname": "Pa_to_bar", "kind": "variable", "doc": "

    \n", "default_value": "1e-05"}, {"fullname": "aton.units.bar_to_Pa", "modulename": "aton.units", "qualname": "bar_to_Pa", "kind": "variable", "doc": "

    \n", "default_value": "99999.99999999999"}, {"fullname": "aton.units.GPa_to_kbar", "modulename": "aton.units", "qualname": "GPa_to_kbar", "kind": "variable", "doc": "

    \n", "default_value": "10.0"}, {"fullname": "aton.units.kbar_to_GPa", "modulename": "aton.units", "qualname": "kbar_to_GPa", "kind": "variable", "doc": "
    \n\n

    Time conversion factors

    \n\n

    Note that H refers to hours.

    \n", "default_value": "0.1"}, {"fullname": "aton.units.H_to_s", "modulename": "aton.units", "qualname": "H_to_s", "kind": "variable", "doc": "

    \n", "default_value": "3600.0"}, {"fullname": "aton.units.s_to_H", "modulename": "aton.units", "qualname": "s_to_H", "kind": "variable", "doc": "
    \n\n

    Universal constants

    \n\n

    Given in SI units unless stated otherwise.

    \n", "default_value": "0.0002777777777777778"}, {"fullname": "aton.units.h", "modulename": "aton.units", "qualname": "h", "kind": "variable", "doc": "

    Planck constant, in J\u00b7s.

    \n", "default_value": "6.62607015e-34"}, {"fullname": "aton.units.h_eV", "modulename": "aton.units", "qualname": "h_eV", "kind": "variable", "doc": "

    Planck constant, in eV\u00b7s.

    \n", "default_value": "4.135667696923859e-15"}, {"fullname": "aton.units.hbar", "modulename": "aton.units", "qualname": "hbar", "kind": "variable", "doc": "

    Reduced Planck constant, in J\u00b7s.

    \n", "default_value": "1.0545718176461565e-34"}, {"fullname": "aton.units.hbar_eV", "modulename": "aton.units", "qualname": "hbar_eV", "kind": "variable", "doc": "

    Reduced Planck constant, in eV\u00b7s.

    \n", "default_value": "6.582119569509066e-16"}]; // mirrored in build-search-index.js (part 1) // Also split on html tags. this is a cheap heuristic, but good enough. diff --git a/makedocs.py b/makedocs.py index dafafc3..54b3ff7 100644 --- a/makedocs.py +++ b/makedocs.py @@ -31,7 +31,7 @@ '[interface.castep](https://pablogila.github.io/Aton/aton/interface/castep.html)' : '`aton.interface.castep`', '[spectra](https://pablogila.github.io/Aton/aton/spectra.html)' : '`aton.spectra`', 'Check the [full documentation online](https://pablogila.github.io/Aton/).' : '', - '

    ' : '', + '

    ' : '', } aton.text.edit.from_template(readme, temp_readme, fix_dict)