Source code for schrodinger.application.matsci.spectra

"""
Copyright Schrodinger, LLC. All rights reserved.
"""

from collections import OrderedDict
from past.utils import old_div

import numpy
from scipy import sparse
from scipy import constants
from scipy.signal import savgol_filter
from scipy.sparse.linalg import spsolve
from scipy.spatial import ConvexHull

from schrodinger.application.matsci import xrd
from schrodinger.application.matsci.nano.xtal import get_pymatgen_structure
from schrodinger.infra import mm
from schrodinger.infra import table
from schrodinger.utils import fileutils

DEFAULT_BANDWIDTH = 8
DEFAULT_BANDWIDTH_MIN = 0
DEFAULT_UV_BANDWIDTH = 3000
DEFAULT_UV_BANDWIDTH_MIN = 0
DEFAULT_UVNM_BANDWIDTH = 40
DEFAULT_UVNM_BANDWIDTH_MIN = 0
DEFAULT_VCD_BANDWIDTH = 4
DEFAULT_VCD_THEO_INTENSITY = 0.02
DEFAULT_VCD_EXP_INTENSITY = 1.0e-6
DEFAULT_IRRAMAN_THEO_INTENSITY = 0.05
DEFAULT_IRRAMAN_EXP_INTENSITY = 0.05
JAG_MAEFILE_PROP = 's_j_Jaguar_mae_file'
IR_OR_RAMAN = 'IR/Raman'
VCD = 'VCD'
ECD = 'ECD'
UVVIS = 'UV/Vis'
VIB_SPECTRUM_MIN = 0
VIB_SPECTRUM_MAX = 4000
UV_SPECTRUM_MIN = 12500  # 800 nm
UV_SPECTRUM_MAX = 100000  # 100 nm
# data is min, max, step
X_RANGE_MAP = {
    IR_OR_RAMAN: (VIB_SPECTRUM_MIN, VIB_SPECTRUM_MAX, 1),
    VCD: (VIB_SPECTRUM_MIN, VIB_SPECTRUM_MAX, 1),
    ECD: (UV_SPECTRUM_MIN, UV_SPECTRUM_MAX, 10),
    UVVIS: (UV_SPECTRUM_MIN, UV_SPECTRUM_MAX, 10)
}

GAUSSIAN = 'Gaussian'
LORENTZIAN = 'Lorentzian'
HARTREE_TO_EV = constants.value('hartree-electron volt relationship')
C_ATOMIC_UNITS = constants.value('inverse fine-structure constant')
BOHR_TO_NM = constants.giga * constants.value('Bohr radius')
# E(ev) = 1239.8419739885849 / lambda(nm)
NM_TO_EV = HARTREE_TO_EV * 2 * constants.pi * C_ATOMIC_UNITS * BOHR_TO_NM
# E(ev) = 0.0001239841973988585 * v_bar(cm-1)
WAVENUMBER_TO_EV = NM_TO_EV * constants.hecto / constants.giga

PDP_DEFAULT_JOB_NAME = 'powder_diffraction_pattern'
PDP_FILE_EXT = '.pdp.spm'

POLYORDER = 'polyorder'
WINDOW_WIDTH = 'win_width'
LAMBDA = 'lambda_val'
WTFACTOR = 'wt_factor'

DEFAULT_LAMBDA = 5.0
DEFAULT_WT_FACTOR = 1e-5


[docs]def rubberband_method(x_data, y_data, parameters): """ Apply a rubberband to the data :type x_data: list :param x_data: list of 2 theta values :type y_data: list :param y_data: list of intensity values :type parameters: SimpleNamespace :param parameters: all the tunable parameters for the method :rtype: list :return: list of intensity values """ # Find rough base line curve vert = ConvexHull(numpy.array(list(zip(x_data, y_data)))).vertices # Rotate the base line to make it flat vert = numpy.roll(vert, -vert.argmin()) vert = vert[:vert.argmax()] # Interpret the y values for the rotate curve for the original x data return numpy.interp(x_data, x_data[vert], y_data[vert])
[docs]def least_square_method(x_data, y_data, parameters): """ Smooth data using least sqaure method :type x_data: list :param x_data: list of 2 theta values :type y_data: list :param y_data: list of intensity values :type parameters: SimpleNamespace :param parameters: all the tunable parameters for the method :rtype: list :return: list of intensity values """ lamda_val = (10**getattr(parameters, LAMBDA)) wt_factor = getattr(parameters, WTFACTOR) data_len = len(y_data) diag_mat = sparse.diags([1, -2, 1], [0, -1, -2], shape=(data_len, data_len - 2)) wts = numpy.ones(data_len) # Iterate multiple times to smooth data for index in range(10): spdiags_mat = sparse.spdiags(wts, 0, data_len, data_len) back_mat = spdiags_mat + lamda_val * diag_mat.dot(diag_mat.transpose()) back_sub = spsolve(back_mat, wts * y_data) wts = wt_factor * (y_data > back_sub) + (1 - wt_factor) * (y_data < back_sub) return back_sub
[docs]def savgol_method(x_data, y_data, parameters): """ Apply a Savitzky-Golay filter to the data :type x_data: list :param x_data: list of 2 theta values :type y_data: list :param y_data: list of intensity values :type parameters: SimpleNamespace :param parameters: all the tunable parameters for the method :rtype: list :return: list of intensity values """ window_size = getattr(parameters, WINDOW_WIDTH) polyorder = getattr(parameters, POLYORDER) return savgol_filter(y_data, window_size, polyorder, mode='mirror')
[docs]def convert_nm_and_wavenumber(value): """ Convert value in wavenumbers to nanometers or vice versa, the conversion is the same. :type value: int or float :param value: Number to convert :rtype: float :return: value converted from wavenumbers to nanometers or vice versa """ return old_div(10000000.0, float(value))
[docs]def get_file_data(filename): """ Read the data from the file :type name: str :param name: The name for the spectrum. If none, it will be derived from the file name. :rtype: tuple of (str, str, schrodinger.infra.table.Table) :return: First two members of the tuple are the s_j_x_lable and s_j_y_label properties. The third member of the tuple is a Table object from the schrodinger.infra.table module. This object holds the actual data. """ # Grab the X and Y axis labels mm.m2io_initialize(mm.error_handler) file_handle = mm.m2io_open_file(filename, mm.M2IO_READ_FORWARD) mm.m2io_goto_next_block(file_handle, 'f_m_table') xaxis_prop = mm.m2io_get_string(file_handle, ['s_j_x_label'])[0] intensity_prop = mm.m2io_get_string(file_handle, ['s_j_y_label'])[0] mm.m2io_close_file(file_handle) mytable = table.Table(filename) return xaxis_prop, intensity_prop, mytable
[docs]def gen_xvals(smin, smax, stride, stride_nm): """ Generates a set of x-values (in wavenumbers) for use in generating a curve from stick spectra. Can specify constant intervals in either wavenumber or nm space via stride_nm arg. :type smin: int :param smin: The minimum x-value in wavenumbers. :type smax: int :param smax: The maximum x-value in wavenumbers. :type stride: float :param stride: Compute the intensity every stride values of x. Can be in wavenumbers or nm. See stride_nm arg. :type stride_nm: bool :param stride_nm: if True, "stride" is given in nm. Default is wavenumbers. Results in a series of x-values that have non-constant intervals in wavenumber space, but constant in nm space. :rtype xvals: Numpy ndarray, type float :return xvals: Specifies wavenumbers at which to calculate y-values """ if stride_nm: # Note that smin/smax are reversed order in nm space since in nm, lower is higher energy (opposite of wavenumber space). smin_nm = convert_nm_and_wavenumber(smin) #convert to nm smin_closest_int_nm = round(smin_nm) # Create xvals in nm space smax_nm = convert_nm_and_wavenumber(smax) smax_closest_int_nm = round(smax_nm) xvals_nm = numpy.arange(smax_closest_int_nm, smin_closest_int_nm, stride) # Note smax first due to being in nm # Convert xvals to wavenumber space and reverse order so is from low to high energy xvals_tmp = [convert_nm_and_wavenumber(x) for x in xvals_nm] xvals_tmp.reverse() xvals = numpy.asarray(xvals_tmp) else: xvals = numpy.arange(smin, smax, stride) return xvals
[docs]def find_closest_xval(pos, xvals, stride, stride_nm): """ Finds index of xvals array corresponding to the x-value closest to peak position. :type pos: float :param pos: Position of peak in wavenumbers :type xvals: Numpy ndarray, type float :param xvals: Array of x-value in units of wavenumbers :type stride: float :param stride: Distance between each x-value in xvals. Given in either wavenumber or nm, see stride_nm arg. :type stride_nm: bool :param stride_nm: if True, "stride" is given in nm. Default is wavenumbers. Results in a series of x-values that have non-constant intervals in wavenumber space, but constant in nm space. :rtype spot: int :return spot: index of xvals array corresponding to the x-value closest to peak position """ if stride_nm: # Find closest xval - brute force since with stride_nm, the # intervals in xvals array (which is in wavenumber space) are non-constant if pos < xvals[0] or pos > xvals[-1]: spot = -1 else: spot = 0 min_diff = abs(pos - xvals[0]) for cnt, x in enumerate(xvals): diff = abs(pos - x) if diff <= min_diff: min_diff = diff spot = cnt else: # To avoid going through the entire xvals break else: # The formula below finds the index of the closest x value # assuming constant intervals in the xvals array spot = int(round((pos - xvals[0]) / stride)) return spot
[docs]def generate_curve(mytable, line_width, xprop, intensity, x_scale=1.0, uvvis=False, smin=None, smax=None, stride=None, stride_nm=False, function=LORENTZIAN, line_width_nm=False, offsets=None): """ Generate a full curve for a series of frequency/intensity lines. The curve is generated by broadening the lines with Gaussian or Lorentzian curves centered on each line, and summing the curves together. Broadening is done in wavenumber space and so returns xvalues in wavenumber space. Assumes input data's x-values are in wavenumbers UNLESS uvvis=True. If uvvis=True, assumes input x-values are in eV and converts them to wavenumbers for curve generation. :type mytable: table.Table object :param mytable: table of raw data :type line_width: float :param line_width: the half-bandwidth to use when generating spectrum :type x_scale: float :param x_scale: the x-axis scale factor :type intensity: str :param intensity: The label of the intensity property in mytable :type xprop: str :param xprop: The label of the x-axis property in mytable :type uvvis: bool :param uvvis: True if this is a uv/vis spectrum, False if not (default is False, vibrational spectrum) :type smin: int :param smin: The minimum x-value in wavenumbers. :type smax: int :param smax: The maximum x-value in wavenumbers. :type stride: float :param stride: Compute the intensity every stride values of x. :type stride_nm: bool :param stride_nm: if True, "stride" is given in nm. Default is wavenumbers. Results in a series of x-values that have non-constant intervals in wavenumber space, but constant in nm space. :type function: str :param function: The function used to broaden singular intensity values to a full spectrum curve. Default is Lorentzian, other option is Gaussian. Use the LORENTZIAN or GAUSSIAN module constants. :type line_width_nm: bool :param line_width_nm: Linewidth is given in nm. Default is wavenumbers. Not used for non-UV/Vis spectra. :type offsets: list :param offsets: A list of floats whose length is equal to the number of rows in the table. These floats will be used to shift the values of x-positions of the curve. Defaults to `None`, where no offsets are used. :rtype: tuple of 1-D numpy arrays :return: (xvalues, yvalues) with xvalues running from smin to smax with Xn = X(n-1) + stride. Note that the X unit will be wavenumbers for all spectra. """ # Assign default argument values if uvvis: if not stride: stride = 10 if not smin: smin = UV_SPECTRUM_MIN if not smax: smax = UV_SPECTRUM_MAX else: if not stride: stride = 5 if not smin: smin = VIB_SPECTRUM_MIN if not smax: smax = VIB_SPECTRUM_MAX xvals = gen_xvals(smin, smax, stride, stride_nm) yvals = numpy.zeros(len(xvals)) if offsets is None: offsets = [0.0] * len(mytable) half_original_linewidth = line_width * 0.5 # Simulate the spectrum: is_gaussian = function == GAUSSIAN for irow in range(1, len(mytable) + 1): value = mytable[irow][xprop] pos = (value + offsets[irow - 1]) if uvvis: pos = old_div(pos, WAVENUMBER_TO_EV) if line_width_nm: # We compute the spectrum in wavenumber space, but a # bandwidth in NM is not linear (or constant) in this space # Figure out what the bandwidth should be by converting the # peak to nanometers, compute the long and short sides of # the bandwidth, then convert those back to wavenumbers and # take half the difference. nm_pos = convert_nm_and_wavenumber(pos) nm_short = nm_pos - half_original_linewidth nm_long = nm_pos + half_original_linewidth wave_short = convert_nm_and_wavenumber(nm_short) wave_long = convert_nm_and_wavenumber(nm_long) line_width = old_div((wave_short - wave_long), 2.0) pos /= x_scale else: pos *= x_scale try: height = mytable[irow][intensity] except KeyError: return None, None if line_width == 0: # No line-broadening, find index of closest x value spot = find_closest_xval(pos, xvals, stride, stride_nm) if spot >= 0: try: yvals[spot] = height except IndexError: pass elif is_gaussian: yvals += height * numpy.exp( old_div(-(xvals - pos)**2, (2 * line_width**2))) else: # Lorentzian: yvals += old_div(height, (1 + (old_div( (xvals - pos), line_width))**2)) return xvals, yvals
[docs]class PowderDiffractionException(Exception): pass
[docs]def get_powder_diffraction_pattern(st, wave_length=None, debye_waller_factors=None, two_theta_range=(0, 90), compute_intensities=True): """ Get a pymatgen powder diffraction pattern. :type st: `schrodinger.structure.Structure` :param st: the structure :type wave_length: float :param wave_length: the wave length in Ang. :type debye_waller_factors: dict :param debye_waller_factors: the temperature dependent Debye-Waller factors, keys are elemental symbols, values are factors in Ang.^2 :type two_theta_range: tuple or None :param two_theta_range: (min, max) pair tuple specifying the x-axis two theta range in degrees over which to calculate the powder diffraction pattern or None if it is to be calculated at all diffracted beams within the limiting sphere of 2 * radius / wave_length :type compute_intensities: bool :param compute_intensities: If True, compute peaks intensities (requires atoms in the structure), otherwise only peak locations :raise: PowderDiffractionException if there is an issue :rtype: pymatgen.analysis.diffraction.core.DiffractionPattern :return: the pymatgen powder diffraction pattern """ if compute_intensities: assert st.atom_total pymatgen_st = get_pymatgen_structure(st) if wave_length is None: wave_length = xrd.WAVELENGTHS['CuKa'] obj = xrd.XRDCalculator(wavelength=wave_length, symprec=0, debye_waller_factors=debye_waller_factors) # see MATSCI-7468 where the input parameters result in zero peaks try: pattern = obj.get_pattern(pymatgen_st, scaled=False, two_theta_range=two_theta_range, compute_intensities=compute_intensities) except ValueError as err: raise PowderDiffractionException(str(err)) return pattern
[docs]class SpectrumFile(object): """ Manage a spectrum file. """ # the following need to be defined in subclasses HEADERS = OrderedDict() COLUMNS = OrderedDict()
[docs] def __init__(self, data_file_name, spm_file_name, spectrum_data=None): """ Create an instance. :type data_file_name: str :param data_file_name: the text file containing the data :type spm_file_name: str :param spm_file_name: the name of the `*spm` file to create :type spectrum_data: list of lists :param spectrum_data: Spectrum data. If provided, used to fill table/write file. Otherwise, data obtained from data_file_name """ self.data_file_name = data_file_name self.spm_file_name = spm_file_name self.table_obj = None self.spectrum_data = spectrum_data
[docs] @staticmethod def getData(data_file_name, separator=None, types=None): """ Return the data from the given data file. :type data_file_name: str :param data_file_name: the text file containing the data :type separator: str :param separator: the data separator :type types: list :param types: a list of types used to type cast the data :rtype: list :return: contains data tuples """ with open(data_file_name, 'r') as afile: lines = afile.readlines() data = [] for aline in lines: values = [] for idx, value in enumerate(aline.split(separator)): if types: value = types[idx](value) else: value = float(value) values.append(value) data.append(tuple(values)) return data
def _setUp(self): """ Set up. """ mm.mmtable_initialize(mm.error_handler) mm.mmerr_level(mm.error_handler, mm.MMERR_OFF) handle = mm.mmtable_new() self.table_obj = table.Table(table_handle=handle) def _buildTable(self): """ Build the table. If spectrum_data provided in init(), fill table with that data. Otherwise, read data from self.data_file_name. """ # emulate the *spm files obtained by running Jaguar for key, value in self.HEADERS.items(): mm.mmtable_set_string(self.table_obj, key, value) for acol, aname in self.COLUMNS.items(): self.table_obj.insertColumn(mm.MMTABLE_END, acol) idx = self.table_obj.getColumnIndex(acol) self.table_obj.columnSetName(idx, aname) #grab data for filling table if self.spectrum_data: data = self.spectrum_data else: data = self.getData(self.data_file_name) # table columns and rows are indexed from 1 for idx, values in enumerate(data, 1): self.table_obj.insertRow(mm.MMTABLE_END, 0) for jdx, value in enumerate(values, 1): self.table_obj[idx][jdx] = value def _createFile(self): """ Create the `*spm` file. """ mm.mmtable_m2io_write(self.spm_file_name, self.table_obj, True) def _tearDown(self): """ Tear down. """ mm.mmerr_level(mm.error_handler, mm.MMERR_WARNING) mm.mmtable_terminate()
[docs] def write(self): """ Write the `*spm` file. """ self._setUp() self._buildTable() self._createFile() self._tearDown()
[docs]class PowderDiffractionFile(SpectrumFile): """ Manage a powder diffraction pattern file. """ TWO_THETA_KEY = 'r_matsci_Two_Theta_(degrees)' TWO_THETA_TITLE = '2*Theta/deg.' INTENSITY_KEY = 'r_matsci_Intensity' INTENSITY_TITLE = 'Intensity' HKLS_KEY = 's_matsci_HKLs' HKLS_TITLE = 'HKLs' MULTIPLICITIES_KEY = 's_matsci_HKL_Multiplicities' MULTIPLICITIES_TITLE = 'Mults' INTERPLANAR_SPACING_KEY = 'r_matsci_Interplanar_Spacing_(Ang.)' INTERPLANAR_SPACING_TITLE = 'd_HKL/Ang.' # use _j_ properties so that spectrum plot automatically # recognizes the file SPECTRUM_KEY = 's_j_spectrum_type' SPECTRUM_TITLE = 'Powder Diffraction Pattern' X_KEY = 's_j_x_label' X_ALIAS = TWO_THETA_KEY Y_KEY = 's_j_y_label' Y_ALIAS = INTENSITY_KEY HEADERS = OrderedDict([ (SPECTRUM_KEY, SPECTRUM_TITLE), (X_KEY, X_ALIAS), (Y_KEY, Y_ALIAS) ]) # yapf: disable COLUMNS = OrderedDict([ (TWO_THETA_KEY, TWO_THETA_TITLE), (INTENSITY_KEY, INTENSITY_TITLE), (HKLS_KEY, HKLS_TITLE), (MULTIPLICITIES_KEY, MULTIPLICITIES_TITLE), (INTERPLANAR_SPACING_KEY, INTERPLANAR_SPACING_TITLE) ]) # yapf: disable SEPARATOR = '; ' # column types TYPES = [float, float, str, str, float]
[docs] @staticmethod def getData(data_file_name, separator=None, types=None): """ Return the data from the given data file. :type data_file_name: str :param data_file_name: the text file containing the data :type separator: str :param separator: the data separator :type types: list :param types: a list of types used to type cast the data :rtype: list :return: contains data tuples """ separator = PowderDiffractionFile.SEPARATOR types = PowderDiffractionFile.TYPES return SpectrumFile.getData(data_file_name, separator=separator, types=types)
[docs]def write_powder_diffraction_pattern(st, wave_length=None, debye_waller_factors=None, two_theta_range=(0, 90), file_name=None): """ Write a powder diffraction pattern file. :type st: `schrodinger.structure.Structure` :param st: the structure :type wave_length: float :param wave_length: the wave length in Ang. :type debye_waller_factors: dict :param debye_waller_factors: the temperature dependent Debye-Waller factors, keys are elemental symbols, values are factors in Ang.^2 :type two_theta_range: tuple or None :param two_theta_range: (min, max) pair tuple specifying the x-axis two theta range in degrees over which to calculate the powder diffraction pattern or None if it is to be calculated at all diffracted beams within the limiting sphere of 2 * radius / wave_length :type file_name: str :param file_name: the file name to which the pattern will be written :rtype: str :return: the file name to which the pattern was written """ pdp_obj = get_powder_diffraction_pattern( st, wave_length=wave_length, debye_waller_factors=debye_waller_factors, two_theta_range=two_theta_range) data = zip(pdp_obj.x, pdp_obj.y, pdp_obj.hkls, pdp_obj.d_hkls) if not file_name: file_name = PDP_DEFAULT_JOB_NAME + PDP_FILE_EXT line = ['{two_theta}', '{intensity}', '{hkls}', '{mults}', '{d_hkl}\n'] line = PowderDiffractionFile.SEPARATOR.join(line) with fileutils.tempfilename(prefix='data', suffix='.txt') as tmp_path: with open(tmp_path, 'w') as afile: for two_theta, intensity, hkl_dicts, d_hkl in data: hkls, mults = [], [] for hkl_dict in hkl_dicts: # the following 'hkl' and 'multiplicity' keys are # from pymatgen hkls.append(str(hkl_dict['hkl'])) mults.append(str(hkl_dict['multiplicity'])) hkls, mults = ', '.join(hkls), ', '.join(mults) afile.write( line.format(two_theta=two_theta, intensity=intensity, hkls=hkls, mults=mults, d_hkl=d_hkl)) obj = PowderDiffractionFile(tmp_path, file_name) obj.write() return file_name
[docs]class VCD_Spectrum(SpectrumFile): """ Manage a VCD (Vibrational Circular Dichroism) file. Note that this class expects its data to be provided upon initialization via SpectrumFile's init() fxn's spectrum_data argument, as opposed to filled after initialization via reading a file. In the future, if we want to initialize via reading a file, we'll have to implement a getData() as in PowderDiffractionFile. """ FREQ_KEY = 'r_j_Frequency_(cm-1)' FREQ_TITLE = 'Frequency (cm-1)' ROT_STR_KEY = 'r_j_Rotational_Strength_(10**-40_esu**2_cm**2)' ROT_STR_TITLE = 'Rotational Strength (10**-40 esu**2 cm**2)' # use _j_ properties so that spectrum plot automatically # recognizes the file SPECTRUM_KEY = 's_j_spectrum_type' SPECTRUM_TITLE = 'Vibrational Circular Dichroism' X_KEY = 's_j_x_label' X_ALIAS = FREQ_KEY Y_KEY = 's_j_y_label' Y_ALIAS = ROT_STR_KEY HEADERS = OrderedDict([ (SPECTRUM_KEY, SPECTRUM_TITLE), (X_KEY, X_ALIAS), (Y_KEY, Y_ALIAS) ]) # yapf: disable COLUMNS = OrderedDict([ (FREQ_KEY, FREQ_TITLE), (ROT_STR_KEY, ROT_STR_TITLE), ('s_j_Symmetry', 'Symmetry'), ('r_j_edtm_x', 'edtm x'), ('r_j_edtm_y', 'edtm y'), ('r_j_edtm_z', 'edtm z'), ('r_j_mdtm_x', 'mdtm x'), ('r_j_mdtm_y', 'mdtm y'), ('r_j_mdtm_z', 'mdtm z'), ]) # yapf: disable # column types TYPES = [float, float, str, float, float, float, float, float, float]
[docs]class ECD_Spectrum(SpectrumFile): """ Manage a ECD (Electronic Circular Dichroism) file. Note that this class expects its data to be provided upon initialization via SpectrumFile's init() fxn's spectrum_data argument, as opposed to filled after initialization via reading a file. In the future, if we want to initialize via reading a file, we'll have to implement a getData() as in PowderDiffractionFile. """ ENERGY_KEY = 'r_j_Electronic_Circular_Dichroism_Energy_(eV)' ENERGY_TITLE = 'ECD Energy (eV)' INTENSITY_KEY = 'r_j_Molar_Circular_Dichroism_(L_mol-1_cm-1)' INTENSITY_TITLE = 'Molar Circular Dichroism (L mol-1 cm-1)' # use _j_ properties so that spectrum plot automatically # recognizes the file SPECTRUM_KEY = 's_j_spectrum_type' SPECTRUM_TITLE = 'Electronic Circular Dichroism' X_KEY = 's_j_x_label' X_ALIAS = ENERGY_KEY Y_KEY = 's_j_y_label' Y_ALIAS = INTENSITY_KEY HEADERS = OrderedDict([ (SPECTRUM_KEY, SPECTRUM_TITLE), (X_KEY, X_ALIAS), (Y_KEY, Y_ALIAS) ]) # yapf: disable COLUMNS = OrderedDict([ (ENERGY_KEY, ENERGY_TITLE), (INTENSITY_KEY, INTENSITY_TITLE), ('s_j_Symmetry', 'Symmetry'), ]) # yapf: disable # column types TYPES = [float, float, str]
[docs]class IR_Spectrum(SpectrumFile): """ Manage an IR (Infrared/Vibrational) file. Note that this class expects its data to be provided upon initialization via SpectrumFile's init() fxn's spectrum_data argument, as opposed to filled after initialization via reading a file. In the future, if we want to initialize via reading a file, we'll have to implement a getData() as in PowderDiffractionFile. """ FREQ_KEY = 'r_j_Frequency_(cm-1)' FREQ_TITLE = 'Frequency (cm-1)' INTENSITY_KEY = 'r_j_Intensity_(km/mol)' INTENSITY_TITLE = 'Intensity (km/mol)' # use _j_ properties so that spectrum plot automatically # recognizes the file SPECTRUM_KEY = 's_j_spectrum_type' SPECTRUM_TITLE = 'Infrared Vibrational Frequencies' X_KEY = 's_j_x_label' X_ALIAS = FREQ_KEY Y_KEY = 's_j_y_label' Y_ALIAS = INTENSITY_KEY HEADERS = OrderedDict([ (SPECTRUM_KEY, SPECTRUM_TITLE), (X_KEY, X_ALIAS), (Y_KEY, Y_ALIAS) ]) # yapf: disable COLUMNS = OrderedDict([ (FREQ_KEY, FREQ_TITLE), (INTENSITY_KEY, INTENSITY_TITLE), ('s_j_Symmetry', 'Symmetry'), ]) # yapf: disable # column types TYPES = [float, float, str]
[docs]class RamanSpectrum(IR_Spectrum): """ Manage a Raman vibrational spectrum file. Note the inheritance from IR_Spectrum class. Some of the variables referenced below are defined there. Note that this class expects its data to be provided upon initialization via SpectrumFile's init() fxn's spectrum_data argument, as opposed to filled after initialization via reading a file. In the future, if we want to initialize via reading a file, we'll have to implement a getData() as in PowderDiffractionFile. """ INTENSITY_KEY = 'r_j_Intensity_(Angstrom**4)' INTENSITY_TITLE = 'Intensity (Angstrom**4)' SPECTRUM_TITLE = 'Raman Vibrational Frequencies' Y_ALIAS = INTENSITY_KEY HEADERS = OrderedDict([ (IR_Spectrum.SPECTRUM_KEY, SPECTRUM_TITLE), (IR_Spectrum.X_KEY, IR_Spectrum.X_ALIAS), (IR_Spectrum.Y_KEY, Y_ALIAS) ]) # yapf: disable COLUMNS = OrderedDict([ (IR_Spectrum.FREQ_KEY, IR_Spectrum.FREQ_TITLE), (INTENSITY_KEY, INTENSITY_TITLE), ('s_j_Symmetry', 'Symmetry'), ]) # yapf: disable
## column types inherited from IR_Spectrum