Source code for exatomic.core.tensor

# -*- coding: utf-8 -*-
# Copyright (c) 2015-2022, Exa Analytics Development Team
# Distributed under the terms of the Apache License 2.0

from exatomic.exa import DataFrame
import numpy as np
import pandas as pd
from exatomic import plotter

# changing this so that tensor refers to a base class of attributes that all
# tensor property dataframes should have
[docs]class Tensor(DataFrame): """ The tensor dataframe. +---------------+----------+-----------------------------------------+ | Column | Type | Description | +===============+==========+=========================================+ | xx | float | 0,0 position in tensor | +---------------+----------+-----------------------------------------+ | xy | float | 1,0 position in tensor | +---------------+----------+-----------------------------------------+ | xz | float | 2,0 position in tensor | +---------------+----------+-----------------------------------------+ | yx | float | 0,1 position in tensor | +---------------+----------+-----------------------------------------+ | yy | float | 1,1 position in tensor | +---------------+----------+-----------------------------------------+ | yz | float | 2,1 position in tensor | +---------------+----------+-----------------------------------------+ | zx | float | 0,3 position in tensor | +---------------+----------+-----------------------------------------+ | zy | float | 1,3 position in tensor | +---------------+----------+-----------------------------------------+ | zz | float | 2,3 position in tensor | +---------------+----------+-----------------------------------------+ | frame | category | frame value to which atach tensor | +---------------+----------+-----------------------------------------+ | atom | int | atom index of molecule to place tensor | +---------------+----------+-----------------------------------------+ | label | category | label of the type of tensor | +---------------+----------+-----------------------------------------+ """ _index = 'tensor' _columns = ['xx','xy','xz','yx','yy','yz','zx','zy','zz', 'frame','atom','label'] _categories = {'frame': np.int64, 'label': str} #@property #def _constructor(self): # return Tensor
[docs] @classmethod def from_file(cls, filename): """ A file reader that will take a tensor file and extract all necessary information. There is a specific file format in place and is as follows frame label atom xx xy xz yx yy yz zx zy zz For multiple tensors just append the same format as above without whitespace unless leaving the frame, label, atom attributes as empty. Args: filename (str): file pathname Returns: tens (:class:`~exatomic.tensor.Tensor`): Tensor table with the tensor attributes """ df = pd.read_csv(filename, delim_whitespace=True, header=None, skip_blank_lines=False) meta = df[::4] idxs = meta.index.values n = len(idxs) df = df[~df.index.isin(idxs)] df[1] = df[1].astype(np.float64) df['grp'] = [i for i in range(n) for j in range(3)] df = pd.DataFrame(df.groupby('grp').apply(lambda x: x.unstack().values[:-3]).values.tolist(), columns=['xx','xy','xz','yx','yy','yz','zx','zy','zz']) # scale = [] # for i in df.index.values: # scale.append(5./abs(df.loc[i,:]).max().astype(np.float64)) meta.reset_index(drop=True, inplace=True) meta.rename(columns={0: 'frame', 1: 'label', 2: 'atom'}, inplace=True) df = pd.concat([meta, df], axis=1) df['atom'] = df['atom'].astype(np.int64) df['frame'] = df['frame'].astype(np.int64) df['symbols'] = np.repeat('', n) # df['scale'] = scale # print(df) return cls(df)
[docs]class Polarizability(Tensor): _index = 'polarizability' _columns = ['xx', 'xy', 'xz', 'yx', 'yy', 'yz', 'zx', 'zy', 'zz', 'frame', 'label', 'type'] _categories = {'frame': np.int64, 'label': str}
[docs]class NMRShielding(Tensor): """ The NMR Shielding tensor dataframe. +---------------+----------+-----------------------------------------+ | Column | Type | Description | +===============+==========+=========================================+ | xx | float | 0,0 position in tensor | +---------------+----------+-----------------------------------------+ | xy | float | 1,0 position in tensor | +---------------+----------+-----------------------------------------+ | xz | float | 2,0 position in tensor | +---------------+----------+-----------------------------------------+ | yx | float | 0,1 position in tensor | +---------------+----------+-----------------------------------------+ | yy | float | 1,1 position in tensor | +---------------+----------+-----------------------------------------+ | yz | float | 2,1 position in tensor | +---------------+----------+-----------------------------------------+ | zx | float | 0,3 position in tensor | +---------------+----------+-----------------------------------------+ | zy | float | 1,3 position in tensor | +---------------+----------+-----------------------------------------+ | zz | float | 2,3 position in tensor | +---------------+----------+-----------------------------------------+ | frame | category | frame value to which atach tensor | +---------------+----------+-----------------------------------------+ | atom | int | atom index of molecule to place tensor | +---------------+----------+-----------------------------------------+ | label | category | label of the type of tensor | +---------------+----------+-----------------------------------------+ | symbol | category | atom symbol the tensor is attached to | +---------------+----------+-----------------------------------------+ | isotropic | float | isotropic shift value of the tensor | +---------------+----------+-----------------------------------------+ """ _index = 'nmr_shielding' _columns = ['xx','xy','xz','yx','yy','yz','zx','zy','zz', 'frame','atom','label','symbol','isotropic'] _categories = {'frame': np.int64, 'label': str, 'symbol': str}
[docs] def nmr_spectra(self, fwhm=1, ref=None, atom='H', lineshape='lorentzian', xrange=None, res=None, invert_x=False, **kwargs): ''' Generate NMR spectra with the plotter class. We can define a gaussian or lorentzian lineshape function. For the most part we pass all of the kwargs directly into the plotter.Plot class. Args: fwhm (float): Full-width at half-maximum ref (float): Isotropic shift of the reference compound atom (str): Atom that we want to display the spectra for lineshape (str): Switch beteen the different lineshape functions available xrange (list): X-bounds for the plot res (float): Resolution for the plot line invert_x (bool): Invert x-axis ''' # define the lineshape and store the function call in the line variable try: line = getattr(plotter, lineshape) except AttributeError: raise NotImplementedError("Sorry we have not yet implemented the lineshape {}.".format(lineshape)) # define a default parameter for the plot width # we did this for a full-screen jupyter notebook on a 1920x1080 monitor if not "plot_width" in kwargs: kwargs.update(plot_width=900) # define the class plot = plotter.Plot(**kwargs) # this is designed for a single frame if self['frame'].unique().shape[0] != 1: raise NotImplementedError("We have not yet expanded to include multiple frames") # grab the locations of the peaks shifts = self.groupby('symbol').get_group(atom)['isotropic'].astype(np.float64) if ref is not None: # we just try to take care of any possible types for the ref variable # its a bit of overkill but just want to make sure we can deal with them if isinstance(ref, float) or isinstance(ref, int): shifts = ref - shifts elif isinstance(ref, list) or isinstance(ref, np.ndarray): shifts = ref[0] - shifts else: raise TypeError("Could not understand type ref type {}.".format(type(ref))) # define xbounds xrange = [shifts.min()-10*fwhm, shifts.max()+10*fwhm] if xrange is None else xrange # deal with inverted bounds if xrange[0] > xrange[1]: xrange = sorted(xrange) invert_x = True res = fwhm/50 if res is None else res x_data = np.arange(*xrange, res) shifts = shifts[shifts.between(*xrange)] shifts = shifts.values # get the y data by calling the lineshape function generator y_data = line(freq=shifts, x=x_data, fwhm=fwhm) # plot the lineshape data plot.fig.line(x_data, y_data) # plot the points on the plot to show were the frequency values are # more useful when we have nearly degenerate vibrations plot.fig.scatter(shifts, line(freq=shifts, x=shifts, fwhm=fwhm)) # just to make sure the plot is oriemted correctly # typically 0 is on the left when we have a reference compound god knows why if ref is not None or invert_x: plot.set_xrange(xmin=xrange[1], xmax=xrange[0]) else: plot.set_xrange(xmin=xrange[0], xmax=xrange[1]) # display the figure with our generated method plot.show()
[docs]class JCoupling(Tensor): """ The J-Coupling tensor dataframe +---------------+----------+-----------------------------------------+ | Column | Type | Description | +===============+==========+=========================================+ | xx | float | 0,0 position in tensor | +---------------+----------+-----------------------------------------+ | xy | float | 1,0 position in tensor | +---------------+----------+-----------------------------------------+ | xz | float | 2,0 position in tensor | +---------------+----------+-----------------------------------------+ | yx | float | 0,1 position in tensor | +---------------+----------+-----------------------------------------+ | yy | float | 1,1 position in tensor | +---------------+----------+-----------------------------------------+ | yz | float | 2,1 position in tensor | +---------------+----------+-----------------------------------------+ | zx | float | 0,3 position in tensor | +---------------+----------+-----------------------------------------+ | zy | float | 1,3 position in tensor | +---------------+----------+-----------------------------------------+ | zz | float | 2,3 position in tensor | +---------------+----------+-----------------------------------------+ | isotropic | float | isotropic shift value of the tensor | +---------------+----------+-----------------------------------------+ | atom | int | atom index of molecule to place tensor | +---------------+----------+-----------------------------------------+ | symbol | category | atom symbol the tensor is attached to | +---------------+----------+-----------------------------------------+ | pt_atom | int | atom index of perturbing atom | +---------------+----------+-----------------------------------------+ | pt_symbol | category | atom symbol of perturbing atom | +---------------+----------+-----------------------------------------+ | label | category | label of the type of tensor | +---------------+----------+-----------------------------------------+ | frame | category | frame value to which atach tensor | +---------------+----------+-----------------------------------------+ """ _index = 'j_coupling' _columns = ['xx','xy','xz','yx','yy','yz','zx','zy','zz', 'isotropic', 'atom', 'symbol', 'pt_atom', 'pt_symbol', 'label', 'frame'] _categories = {'frame': np.int64, 'label': str, 'symbol': str, 'pt_symbol': str}
[docs]def add_tensor(uni, fp): """ Simple function to add a tensor object to the universe. Args: uni (universe): Universe object fp (str): file pathname """ uni.tensor = Tensor.from_file(fp)
# if fp != None: # uni.tensor = Tensor.from_file(fp) # if any('tensor' in x for x in vars(uni)): # tmp = [x for x in vars(uni) if 'tensor' in x] # uni.tensor = pd.concat([uni[i] for i in tmp], sort=False).reset_index(drop=True)