<commit_before>#!/usr/bin/env python

"""
A module to perform diffusion analyses (e.g. calculating diffusivity from
mean square displacements etc.). If you use this module,
please consider citing the following papers::

    Ong, S. P., Mo, Y., Richards, W. D., Miara, L., Lee, H. S., & Ceder, G.
    (2013). Phase stability, electrochemical stability and ionic conductivity
    of the Li10+-1MP2X12 (M = Ge, Si, Sn, Al or P, and X = O, S or Se) family
    of superionic conductors. Energy & Environmental Science, 6(1), 148.
    doi:10.1039/c2ee23355j

    Mo, Y., Ong, S. P., & Ceder, G. (2012). First Principles Study of the
    Li10GeP2S12 Lithium Super Ionic Conductor Material. Chemistry of Materials,
    24(1), 15-17. doi:10.1021/cm203303y
"""

from __future__ import division

__author__ = "Will Richards"
__version__ = "0.1"
__maintainer__ = "Will Richards"
__email__ = "wrichard@mit.edu"
__status__ = "Beta"
__date__ = "5/2/13"

import numpy as np

from pymatgen.core import Structure, smart_element_or_specie
from pymatgen.core.physical_constants import AVOGADROS_CONST, BOLTZMANN_CONST,\
    ELECTRON_CHARGE
from pymatgen.serializers.json_coders import MSONable
from pymatgen.io.vaspio.vasp_output import Vasprun

class DiffusionAnalyzer(MSONable):
    """
    Class for performing diffusion analyses.

    .. attribute: diffusivity

        Diffusivity in cm^2 / cm

    .. attribute: conductivity

        Conductivity in mS / cm

    .. attribute: diffusivity_components

        A vector with diffusivity in the a, b and c directions in cm^2 / cm

    .. attribute: conductivity components

        A vector with conductivity in the a, b and c directions in mS / cm

    .. attribute: max_framework_displacement

        The maximum (drift adjusted) distance of any framework atom from its
        starting location in A

    """

    def __init__(self, structure, displacements, specie, temperature,
                 time_step, step_skip=10, max_dt=0.7):
        """
        This constructor is meant to be used with pre-processed data.
        Other convenient constructors are provided as static methods, e.g.,
        see from_vaspruns.

        Args:
            structure:
                Initial structure.
            displacements:
                Numpy array of with shape [site, time step, axis]
            specie:
                Specie to calculate diffusivity for as a String. E.g., "Li".
            temperature:
                Temperature of the diffusion run in Kelvin.
            time_step:
                Time step between measurements.
            step_skip:
                Sampling frequency of the displacements (time_step is
                multiplied by this number to get the real time between
                measurements)
            max_dt:
                maximum fraction of the total run time to use when
                calculating MSD vs dt. Typical values are between 0.1-1.
                If 0.1, the highest time interval will have 10 uncorrelated
                samplings. If 1, there will only be one sampling at this
                maximum dt.
        """
        self.s = structure
        self.disp = displacements
        self.sp = specie
        self.temperature = temperature
        self.time_step = time_step
        self.step_skip = step_skip
        self.max_dt = max_dt
        self.indices = []
        self.framework_indices = []
        for i, site in enumerate(structure):
            if site.specie.symbol == specie:
                self.indices.append(i)
            else:
                self.framework_indices.append(i)
        if self.disp.shape[1] < 2:
            self.diffusivity = 0.
            self.conductivity = 0.
            self.diffusivity_components = np.array([0., 0., 0.])
            self.conductivity_components = np.array([0., 0., 0.])
            self.max_framework_displacement = 0
        else:
            framework_disp = self.disp[self.framework_indices]
            drift = np.average(framework_disp, axis=0)[None, :, :]
            #drift corrected position
            dc_x = self.disp[self.indices] - drift
            dc_framework = self.disp[self.framework_indices] - drift
            self.max_framework_displacement = \
                np.max(np.sum(dc_framework ** 2, axis=-1) ** 0.5)
            df_x = self.s.lattice.get_fractional_coords(dc_x)
            #limit the number of sampled timesteps to 200
            timesteps = np.arange(10, int(max_dt * dc_x.shape[1]),
                                  max(dc_x.shape[1] / 200, 1))
            x = timesteps * self.time_step * self.step_skip

            #calculate the smoothed msd values
            s_msd = np.zeros_like(x, dtype=np.double)
            s_msd_components = np.zeros(x.shape + (3,))
            lengths = np.array(self.s.lattice.abc)[None, None, :]
            for i, n in enumerate(timesteps):
                dx = dc_x[:, n:, :] - dc_x[:, :-n, :]
                s_msd[i] = 3 * np.average(dx ** 2)
                dcomponents = (df_x[:, n:, :] - df_x[:, :-n, :]) * lengths
                s_msd_components[i] = np.average(np.average(dcomponents ** 2,
                                                            axis=1), axis=0)

            #run the regression on the msd components
            m_components = np.zeros(3)
            for i in range(3):
                a = np.ones((len(x), 2))
                a[:, 0] = x
                (m, c), res, rank, s = \
                    np.linalg.lstsq(a, s_msd_components[:, i])
                m_components[i] = max(m, 1e-15)

            a = np.ones((len(x), 2))
            a[:, 0] = x
            (m, c), res, rank, s = np.linalg.lstsq(a, s_msd)
            #m shouldn't be negative
            m = max(m, 1e-20)

            #factor of 10 is to convert from A^2/fs to cm^2/s
            #factor of 6 is for dimensionality
            conv_factor = get_conversion_factor(self.s, self.sp,
                                                self.temperature)
            self.diffusivity = m / 60
            self.conductivity = self.diffusivity * conv_factor

            self.diffusivity_components = m_components / 20
            self.conductivity_components = self.diffusivity_components * \
                conv_factor

    @classmethod
    def from_vaspruns(cls, vaspruns, specie, max_dt=0.7):
        """
        Convenient constructor that takes in a list of Vasprun objects to
        perform diffusion analysis.

        Args:
            vaspruns:
                list of Vasprun objects (must be ordered in sequence of run).
                 E.g., you may have performed sequential VASP runs to obtain
                 sufficient statistics.
            specie:
                Specie to calculate diffusivity for as a String. E.g., "Li".
            max_dt:
                maximum fraction of the total run time to use when
                calculating MSD vs dt. Typical values are between 0.1-1.
                If 0.1, the highest time interval will have 10 uncorrelated
                samplings. If 1, there will only be one sampling at this
                maximum dt.
        """
        structure = vaspruns[0].initial_structure
        step_skip = vaspruns[0].ionic_step_skip

        p = []
        for vr in vaspruns:
            assert vr.ionic_step_skip == step_skip
            p.extend([np.array(s['structure'].frac_coords)[:, None, :]
                      for s in vr.ionic_steps])
        p = np.concatenate(p, axis=1)
        dp = p[:, 1:, :] - p[:, :-1, :]
        dp = np.concatenate([np.zeros_like(dp[:, (0,), :]), dp], axis=1)
        dp = dp - np.round(dp)
        f_disp = np.cumsum(dp, axis=1)
        disp = structure.lattice.get_cartesian_coords(f_disp)

        temperature = vaspruns[0].parameters['TEEND']
        time_step = vaspruns[0].parameters['POTIM']

        return cls(structure, disp, specie, temperature,
                   time_step, step_skip=step_skip, max_dt=max_dt)

    @classmethod
    def from_files(cls, filepaths, specie, step_skip=10, ncores=None):
        """
        Convenient constructor that takes in a list of vasprun.xml paths to
        perform diffusion analysis.

        Args:
            filepaths:
                List of paths to vasprun.xml files of runs. (must be
                ordered in sequence of run). For example,
                you may have done sequential VASP runs and they are in run1,
                run2, run3, etc. You should then pass in
                ["run1/vasprun.xml", "run2/vasprun.xml", ...].
            specie:
                Specie to calculate diffusivity for as a String. E.g., "Li".
            step_skip:
                Sampling frequency of the displacements (time_step is
                multiplied by this number to get the real time between
                measurements). E.g., you may not want to sample every single
                time step.
            ncores:
                Numbers of cores to use for multiprocessing. Can speed up
                vasprun parsing considerable. Defaults to None,
                which means serial.
        """
        func = map
        if ncores is not None:
            import multiprocessing
            p = multiprocessing.Pool(ncores)
            func = p.map
        vaspruns = func(_get_vasprun, [(p, step_skip) for p in filepaths])
        return cls.from_vaspruns(vaspruns, specie=specie)

    @property
    def to_dict(self):
        return {
            "@module": self.__class__.__module__,
            "@class": self.__class__.__name__,
            "structure": self.s.to_dict,
            "displacements": self.disp.tolist(),
            "specie": self.sp,
            "temperature": self.temperature,
            "time_step": self.time_step,
            "step_skip": self.step_skip,
            "max_dt": self.max_dt
        }

    @classmethod
    def from_dict(cls, d):
        structure = Structure.from_dict(d["structure"])
        return cls(structure, np.array(d["displacements"]), specie=d["specie"],
                   temperature=d["temperature"], time_step=d["time_step"],
                   step_skip=d["step_skip"], max_dt=d["max_dt"])

def get_conversion_factor(structure, species, temperature):
    """
    Conversion factor to convert between cm^2/s diffusivity measurements and
    mS/cm conductivity measurements based on number of atoms of diffusing
    species. Note that the charge is based on the oxidation state of the
    species (where available), or else the number of valence electrons
    (usually a good guess, esp for main group ions).

    Args:
        structure:
            Input structure.
        species:
            Diffusing species.
        temperature:
            Temperature of the diffusion run in Kelvin.

    Returns:
        Conversion factor.
        Conductivity (in mS/cm) = Conversion Factor * Diffusivity (in cm^2/s)
    """
    df_sp = smart_element_or_specie(species)
    if hasattr(df_sp, "oxi_state"):
        z = df_sp.oxi_state
    else:
        z = df_sp.full_electronic_structure[-1][2]

    n = structure.composition[species]

    V = structure.volume * 1e-24  # units cm^3
    F = ELECTRON_CHARGE * AVOGADROS_CONST  # sA/mol
    return 1000 * n / (V * AVOGADROS_CONST) * z ** 2 * F ** 2\
        / (BOLTZMANN_CONST * AVOGADROS_CONST * temperature)

def _get_vasprun(args):
    return Vasprun(args[0], ionic_step_skip=args[1])<commit_msg>Simplify diffusion_analyzer a little.<commit_after>#!/usr/bin/env python

"""
A module to perform diffusion analyses (e.g. calculating diffusivity from
mean square displacements etc.). If you use this module,
please consider citing the following papers::

    Ong, S. P., Mo, Y., Richards, W. D., Miara, L., Lee, H. S., & Ceder, G.
    (2013). Phase stability, electrochemical stability and ionic conductivity
    of the Li10+-1MP2X12 (M = Ge, Si, Sn, Al or P, and X = O, S or Se) family
    of superionic conductors. Energy & Environmental Science, 6(1), 148.
    doi:10.1039/c2ee23355j

    Mo, Y., Ong, S. P., & Ceder, G. (2012). First Principles Study of the
    Li10GeP2S12 Lithium Super Ionic Conductor Material. Chemistry of Materials,
    24(1), 15-17. doi:10.1021/cm203303y
"""

from __future__ import division

__author__ = "Will Richards"
__version__ = "0.1"
__maintainer__ = "Will Richards"
__email__ = "wrichard@mit.edu"
__status__ = "Beta"
__date__ = "5/2/13"

import numpy as np

from pymatgen.core import Structure, smart_element_or_specie
from pymatgen.core.physical_constants import AVOGADROS_CONST, BOLTZMANN_CONST,\
    ELECTRON_CHARGE
from pymatgen.serializers.json_coders import MSONable
from pymatgen.io.vaspio.vasp_output import Vasprun

class DiffusionAnalyzer(MSONable):
    """
    Class for performing diffusion analyses.

    .. attribute: diffusivity

        Diffusivity in cm^2 / cm

    .. attribute: conductivity

        Conductivity in mS / cm

    .. attribute: diffusivity_components

        A vector with diffusivity in the a, b and c directions in cm^2 / cm

    .. attribute: conductivity components

        A vector with conductivity in the a, b and c directions in mS / cm

    .. attribute: max_framework_displacement

        The maximum (drift adjusted) distance of any framework atom from its
        starting location in A

    """

    def __init__(self, structure, displacements, specie, temperature,
                 time_step, step_skip=10, max_dt=0.7):
        """
        This constructor is meant to be used with pre-processed data.
        Other convenient constructors are provided as static methods, e.g.,
        see from_vaspruns.

        Args:
            structure:
                Initial structure.
            displacements:
                Numpy array of with shape [site, time step, axis]
            specie:
                Specie to calculate diffusivity for as a String. E.g., "Li".
            temperature:
                Temperature of the diffusion run in Kelvin.
            time_step:
                Time step between measurements.
            step_skip:
                Sampling frequency of the displacements (time_step is
                multiplied by this number to get the real time between
                measurements)
            max_dt:
                maximum fraction of the total run time to use when
                calculating MSD vs dt. Typical values are between 0.1-1.
                If 0.1, the highest time interval will have 10 uncorrelated
                samplings. If 1, there will only be one sampling at this
                maximum dt.
        """
        self.s = structure
        self.disp = displacements
        self.sp = specie
        self.temperature = temperature
        self.time_step = time_step
        self.step_skip = step_skip
        self.max_dt = max_dt
        self.indices = []
        self.framework_indices = []
        for i, site in enumerate(structure):
            if site.specie.symbol == specie:
                self.indices.append(i)
            else:
                self.framework_indices.append(i)
        if self.disp.shape[1] < 2:
            self.diffusivity = 0.
            self.conductivity = 0.
            self.diffusivity_components = np.array([0., 0., 0.])
            self.conductivity_components = np.array([0., 0., 0.])
            self.max_framework_displacement = 0
        else:
            framework_disp = self.disp[self.framework_indices]
            drift = np.average(framework_disp, axis=0)[None, :, :]
            #drift corrected position
            dc_x = self.disp[self.indices] - drift
            dc_framework = self.disp[self.framework_indices] - drift
            self.max_framework_displacement = \
                np.max(np.sum(dc_framework ** 2, axis=-1) ** 0.5)
            df_x = self.s.lattice.get_fractional_coords(dc_x)
            #limit the number of sampled timesteps to 200
            timesteps = np.arange(10, int(max_dt * dc_x.shape[1]),
                                  max(dc_x.shape[1] / 200, 1))
            x = timesteps * self.time_step * self.step_skip

            #calculate the smoothed msd values
            s_msd = np.zeros_like(x, dtype=np.double)
            s_msd_components = np.zeros(x.shape + (3,))
            lengths = np.array(self.s.lattice.abc)[None, None, :]
            for i, n in enumerate(timesteps):
                dx = dc_x[:, n:, :] - dc_x[:, :-n, :]
                s_msd[i] = 3 * np.average(dx ** 2)
                dcomponents = (df_x[:, n:, :] - df_x[:, :-n, :]) * lengths
                s_msd_components[i] = np.average(np.average(dcomponents ** 2,
                                                            axis=1), axis=0)

            #run the regression on the msd components
            m_components = np.zeros(3)
            for i in range(3):
                a = np.ones((len(x), 2))
                a[:, 0] = x
                (m, c), res, rank, s = \
                    np.linalg.lstsq(a, s_msd_components[:, i])
                m_components[i] = max(m, 1e-15)

            a = np.ones((len(x), 2))
            a[:, 0] = x
            (m, c), res, rank, s = np.linalg.lstsq(a, s_msd)
            #m shouldn't be negative
            m = max(m, 1e-20)

            #factor of 10 is to convert from A^2/fs to cm^2/s
            #factor of 6 is for dimensionality
            conv_factor = get_conversion_factor(self.s, self.sp,
                                                self.temperature)
            self.diffusivity = m / 60
            self.conductivity = self.diffusivity * conv_factor

            self.diffusivity_components = m_components / 20
            self.conductivity_components = self.diffusivity_components * \
                conv_factor

    @classmethod
    def from_vaspruns(cls, vaspruns, specie, max_dt=0.7):
        """
        Convenient constructor that takes in a list of Vasprun objects to
        perform diffusion analysis.

        Args:
            vaspruns:
                list of Vasprun objects (must be ordered in sequence of run).
                 E.g., you may have performed sequential VASP runs to obtain
                 sufficient statistics.
            specie:
                Specie to calculate diffusivity for as a String. E.g., "Li".
            max_dt:
                maximum fraction of the total run time to use when
                calculating MSD vs dt. Typical values are between 0.1-1.
                If 0.1, the highest time interval will have 10 uncorrelated
                samplings. If 1, there will only be one sampling at this
                maximum dt.
        """
        structure = vaspruns[0].initial_structure
        step_skip = vaspruns[0].ionic_step_skip

        p = []
        for vr in vaspruns:
            assert vr.ionic_step_skip == step_skip
            p.extend([np.array(s['structure'].frac_coords)[:, None]
                      for s in vr.ionic_steps])
        p = np.concatenate(p, axis=1)
        dp = p[:, 1:] - p[:, :-1]
        dp = np.concatenate([np.zeros_like(dp[:, (0,)]), dp], axis=1)
        dp = dp - np.round(dp)
        f_disp = np.cumsum(dp, axis=1)
        disp = structure.lattice.get_cartesian_coords(f_disp)

        temperature = vaspruns[0].parameters['TEEND']
        time_step = vaspruns[0].parameters['POTIM']

        return cls(structure, disp, specie, temperature,
                   time_step, step_skip=step_skip, max_dt=max_dt)

    @classmethod
    def from_files(cls, filepaths, specie, step_skip=10, ncores=None):
        """
        Convenient constructor that takes in a list of vasprun.xml paths to
        perform diffusion analysis.

        Args:
            filepaths:
                List of paths to vasprun.xml files of runs. (must be
                ordered in sequence of run). For example,
                you may have done sequential VASP runs and they are in run1,
                run2, run3, etc. You should then pass in
                ["run1/vasprun.xml", "run2/vasprun.xml", ...].
            specie:
                Specie to calculate diffusivity for as a String. E.g., "Li".
            step_skip:
                Sampling frequency of the displacements (time_step is
                multiplied by this number to get the real time between
                measurements). E.g., you may not want to sample every single
                time step.
            ncores:
                Numbers of cores to use for multiprocessing. Can speed up
                vasprun parsing considerable. Defaults to None,
                which means serial.
        """
        func = map
        if ncores is not None:
            import multiprocessing
            p = multiprocessing.Pool(ncores)
            func = p.map
        vaspruns = func(_get_vasprun, [(p, step_skip) for p in filepaths])
        return cls.from_vaspruns(vaspruns, specie=specie)

    @property
    def to_dict(self):
        return {
            "@module": self.__class__.__module__,
            "@class": self.__class__.__name__,
            "structure": self.s.to_dict,
            "displacements": self.disp.tolist(),
            "specie": self.sp,
            "temperature": self.temperature,
            "time_step": self.time_step,
            "step_skip": self.step_skip,
            "max_dt": self.max_dt
        }

    @classmethod
    def from_dict(cls, d):
        structure = Structure.from_dict(d["structure"])
        return cls(structure, np.array(d["displacements"]), specie=d["specie"],
                   temperature=d["temperature"], time_step=d["time_step"],
                   step_skip=d["step_skip"], max_dt=d["max_dt"])

def get_conversion_factor(structure, species, temperature):
    """
    Conversion factor to convert between cm^2/s diffusivity measurements and
    mS/cm conductivity measurements based on number of atoms of diffusing
    species. Note that the charge is based on the oxidation state of the
    species (where available), or else the number of valence electrons
    (usually a good guess, esp for main group ions).

    Args:
        structure:
            Input structure.
        species:
            Diffusing species.
        temperature:
            Temperature of the diffusion run in Kelvin.

    Returns:
        Conversion factor.
        Conductivity (in mS/cm) = Conversion Factor * Diffusivity (in cm^2/s)
    """
    df_sp = smart_element_or_specie(species)
    if hasattr(df_sp, "oxi_state"):
        z = df_sp.oxi_state
    else:
        z = df_sp.full_electronic_structure[-1][2]

    n = structure.composition[species]

    V = structure.volume * 1e-24  # units cm^3
    F = ELECTRON_CHARGE * AVOGADROS_CONST  # sA/mol
    return 1000 * n / (V * AVOGADROS_CONST) * z ** 2 * F ** 2\
        / (BOLTZMANN_CONST * AVOGADROS_CONST * temperature)

def _get_vasprun(args):
    return Vasprun(args[0], ionic_step_skip=args[1])<|endoftext|>


<commit_before>from __future__ import absolute_import

import plotly
import plotly.graph_objs as go

def validate_coerce_fig_to_dict(fig, validate, clone=True):
    from plotly.basedatatypes import BaseFigure

    if isinstance(fig, BaseFigure):
        fig_dict = fig.to_dict(clone=clone)
    elif isinstance(fig, dict):
        if validate:
            # This will raise an exception if fig is not a valid plotly figure
            fig_dict = plotly.graph_objs.Figure(fig).to_plotly_json()
        else:
            fig_dict = fig
    else:
        raise ValueError(
            """
The fig parameter must be a dict or Figure.
    Received value of type {typ}: {v}""".format(
                typ=type(fig), v=fig
            )
        )
    return fig_dict

def validate_coerce_output_type(output_type):
    if output_type == "Figure" or output_type == go.Figure:
        cls = go.Figure
    elif output_type == "FigureWidget" or (
        hasattr(go, "FigureWidget") and output_type == go.FigureWidget
    ):
        cls = go.FigureWidget
    else:
        raise ValueError(
            """
Invalid output type: {output_type}
    Must be one of: 'Figure', 'FigureWidget'"""
        )
    return cls
<commit_msg>Handle Dash objects in to_json<commit_after>from __future__ import absolute_import

import plotly
import plotly.graph_objs as go

def validate_coerce_fig_to_dict(fig, validate, clone=True):
    from plotly.basedatatypes import BaseFigure

    if isinstance(fig, BaseFigure):
        fig_dict = fig.to_dict(clone=clone)
    elif isinstance(fig, dict):
        if validate:
            # This will raise an exception if fig is not a valid plotly figure
            fig_dict = plotly.graph_objs.Figure(fig).to_plotly_json()
        else:
            fig_dict = fig
    elif hasattr(fig, "to_plotly_json"):
        fig_dict = fig.to_plotly_json()
    else:
        raise ValueError(
            """
The fig parameter must be a dict or Figure.
    Received value of type {typ}: {v}""".format(
                typ=type(fig), v=fig
            )
        )
    return fig_dict

def validate_coerce_output_type(output_type):
    if output_type == "Figure" or output_type == go.Figure:
        cls = go.Figure
    elif output_type == "FigureWidget" or (
        hasattr(go, "FigureWidget") and output_type == go.FigureWidget
    ):
        cls = go.FigureWidget
    else:
        raise ValueError(
            """
Invalid output type: {output_type}
    Must be one of: 'Figure', 'FigureWidget'"""
        )
    return cls
<|endoftext|>


<commit_before>#!/usr/bin/env python

"""
A module to perform diffusion analyses (e.g. calculating diffusivity from
mean square displacements etc.). If you use this module,
please consider citing the following papers::

    Ong, S. P., Mo, Y., Richards, W. D., Miara, L., Lee, H. S., & Ceder, G.
    (2013). Phase stability, electrochemical stability and ionic conductivity
    of the Li10+-1MP2X12 (M = Ge, Si, Sn, Al or P, and X = O, S or Se) family
    of superionic conductors. Energy & Environmental Science, 6(1), 148.
    doi:10.1039/c2ee23355j

    Mo, Y., Ong, S. P., & Ceder, G. (2012). First Principles Study of the
    Li10GeP2S12 Lithium Super Ionic Conductor Material. Chemistry of Materials,
    24(1), 15-17. doi:10.1021/cm203303y
"""

from __future__ import division

__author__ = "Will Richards"
__version__ = "0.1"
__maintainer__ = "Will Richards"
__email__ = "wrichard@mit.edu"
__status__ = "Beta"
__date__ = "5/2/13"

import numpy as np

from pymatgen.core import Structure, smart_element_or_specie
from pymatgen.core.physical_constants import AVOGADROS_CONST, BOLTZMANN_CONST,\
    ELECTRON_CHARGE
from pymatgen.serializers.json_coders import MSONable

class DiffusionAnalyzer(MSONable):
    """
    Class for performing diffusion analyses.

    .. attribute: diffusivity

        Diffusivity in cm^2 / cm

    .. attribute: conductivity

        Conductivity in mS / cm

    .. attribute: diffusivity_components

        A vector with diffusivity in the a, b and c directions in cm^2 / cm

    .. attribute: conductivity components

        A vector with conductivity in the a, b and c directions in mS / cm

    .. attribute: max_framework_displacement

        The maximum (drift adjusted) distance of any framework atom from its
        starting location in A

    """

    def __init__(self, structure, displacements, specie, temperature,
                 time_step, step_skip=10):
        """
        This constructor is meant to be used with pre-processed data.
        Other convenient constructors are provded as static methods, e.g.,
        see from_vaspruns.

        Args:
            structure:
                Initial structure.
            displacements:
                Numpy array of with shape [site, time step, axis]
            specie:
                Specie to calculate diffusivity for as a String. E.g., "Li".
            temperature:
                Temperature of the diffusion run in Kelvin.
            time_step:
                Time step between measurements.
            step_skip:
                Sampling frequency of the displacements (time_step is
                multiplied by this number to get the real time between
                measurements)
        """
        self.s = structure
        self.disp = displacements
        self.sp = specie
        self.temperature = temperature
        self.time_step = time_step
        self.step_skip = step_skip
        self.indices = []
        self.framework_indices = []
        for i, site in enumerate(structure):
            if site.specie.symbol == specie:
                self.indices.append(i)
            else:
                self.framework_indices.append(i)
        if self.disp.shape[1] < 2:
            self.diffusivity = 0.
            self.conductivity = 0.
            self.diffusivity_components = np.array([0., 0., 0.])
            self.conductivity_components = np.array([0., 0., 0.])
            self.max_framework_displacement = 0
        else:
            framework_disp = self.disp[self.framework_indices]
            drift = np.average(framework_disp, axis=0)
            #drift corrected position
            dc_x = self.disp[self.indices] - drift[None, :, :]
            dc_framework = self.disp[self.framework_indices] - drift[None, :, :]
            self.max_framework_displacement = \
                np.max(np.sum(dc_framework ** 2, axis=-1) ** 0.5)
            df_x = self.s.lattice.get_fractional_coords(dc_x)
            #limit the number of sampled timesteps to 200
            timesteps = np.arange(10, dc_x.shape[1],
                                  max(dc_x.shape[1] / 200, 1))
            x = timesteps * self.time_step * self.step_skip

            #calculate the smoothed msd values
            s_msd = np.zeros_like(x, dtype=np.double)
            s_msd_components = np.zeros(x.shape + (3,))
            lengths = np.array(self.s.lattice.abc)[None, None, :]
            for i, n in enumerate(timesteps):
                dx = dc_x[:, n:, :] - dc_x[:, :-n, :]
                s_msd[i] = 3 * np.average(dx ** 2)
                dcomponents = (df_x[:, n:, :] - df_x[:, :-n, :]) * lengths
                s_msd_components[i] = np.average(np.average(dcomponents ** 2,
                                                            axis=1), axis=0)

            #run the regression on the msd components
            m_components = np.zeros(3)
            for i in range(3):
                a = np.ones((len(x), 2))
                a[:, 0] = x
                (m, c), res, rank, s = \
                    np.linalg.lstsq(a, s_msd_components[:, i])
                m_components[i] = max(m, 1e-15)

            a = np.ones((len(x), 2))
            a[:, 0] = x
            (m, c), res, rank, s = np.linalg.lstsq(a, s_msd)
            #m shouldn't be negative
            m = max(m, 1e-20)

            #factor of 10 is to convert from A^2/fs to cm^2/s
            #factor of 6 is for dimensionality
            conv_factor = get_conversion_factor(self.s, self.sp,
                                                self.temperature)
            self.diffusivity = m / 60
            self.conductivity = self.diffusivity * conv_factor

            self.diffusivity_components = m_components / 20
            self.conductivity_components = self.diffusivity_components * \
                conv_factor

    @staticmethod
    def from_vaspruns(vaspruns, specie):
        """
        Convenient constructor that takes in a list of Vasprun objects to
        perform diffusion analysis.

        Args:
            vaspruns:
                list of Vasprun objects (must be ordered in sequence of run).
                 E.g., you may have performed sequential VASP runs to obtain
                 sufficient statistics.
            specie:
                Specie to calculate diffusivity for as a String. E.g., "Li".
        """
        structure = vaspruns[0].initial_structure
        step_skip = vaspruns[0].ionic_step_skip

        p = []
        for vr in vaspruns:
            assert vr.ionic_step_skip == step_skip
            p.extend([np.array(s['structure'].frac_coords)[:, None, :]
                      for s in vr.ionic_steps])
        p = np.concatenate(p, axis=1)
        dp = p[:, 1:, :] - p[:, :-1, :]
        dp = np.concatenate([np.zeros_like(dp[:, (0,), :]), dp], axis=1)
        dp = dp - np.round(dp)
        f_disp = np.cumsum(dp, axis=1)
        disp = structure.lattice.get_cartesian_coords(f_disp)

        temperature = vaspruns[0].parameters['TEEND']
        time_step = vaspruns[0].parameters['POTIM']

        return DiffusionAnalyzer(structure, disp, specie, temperature,
                                 time_step, step_skip=step_skip)

    @property
    def to_dict(self):
        return {
            "@module": self.__class__.__module__,
            "@class": self.__class__.__name__,
            "structure": self.s.to_dict,
            "displacements": self.disp.tolist(),
            "specie": self.sp,
            "temperature": self.temperature,
            "time_step": self.time_step,
            "step_skip": self.step_skip
        }

    @classmethod
    def from_dict(cls, d):
        structure = Structure.from_dict(d["structure"])
        return cls(structure, np.array(d["displacements"]), specie=d["specie"],
                   temperature=d["temperature"], time_step=d["time_step"],
                   step_skip=d["step_skip"])

def get_conversion_factor(structure, species, temperature):
    """
    Conversion factor to convert between cm^2/s diffusivity measurements and
    mS/cm conductivity measurements based on number of atoms of diffusing
    species and .

    Args:
        structure:
            Input structure.
        species:
            Diffusing species.
        temperature:
            Temperature of the diffusion run in Kelvin.

    Returns:
        Conversion factor.
        Conductivity (in mS/cm) = Conversion Factor * Diffusivity (in cm^2/s)
    """
    df_sp = smart_element_or_specie(species)
    if df_sp.Z in [1, 3, 11, 19, 37, 55]:
        z = 1
    else:
        z = df_sp.oxi_state

    n = structure.composition[species]

    V = structure.volume * 1e-24  # units cm^3
    F = ELECTRON_CHARGE * AVOGADROS_CONST  # sA/mol
    return 1000 * n / (V * AVOGADROS_CONST) * z ** 2 * F ** 2\
        / (BOLTZMANN_CONST * AVOGADROS_CONST * temperature)
<commit_msg>Slight mod to make it simpler for drift.<commit_after>#!/usr/bin/env python

"""
A module to perform diffusion analyses (e.g. calculating diffusivity from
mean square displacements etc.). If you use this module,
please consider citing the following papers::

    Ong, S. P., Mo, Y., Richards, W. D., Miara, L., Lee, H. S., & Ceder, G.
    (2013). Phase stability, electrochemical stability and ionic conductivity
    of the Li10+-1MP2X12 (M = Ge, Si, Sn, Al or P, and X = O, S or Se) family
    of superionic conductors. Energy & Environmental Science, 6(1), 148.
    doi:10.1039/c2ee23355j

    Mo, Y., Ong, S. P., & Ceder, G. (2012). First Principles Study of the
    Li10GeP2S12 Lithium Super Ionic Conductor Material. Chemistry of Materials,
    24(1), 15-17. doi:10.1021/cm203303y
"""

from __future__ import division

__author__ = "Will Richards"
__version__ = "0.1"
__maintainer__ = "Will Richards"
__email__ = "wrichard@mit.edu"
__status__ = "Beta"
__date__ = "5/2/13"

import numpy as np

from pymatgen.core import Structure, smart_element_or_specie
from pymatgen.core.physical_constants import AVOGADROS_CONST, BOLTZMANN_CONST,\
    ELECTRON_CHARGE
from pymatgen.serializers.json_coders import MSONable

class DiffusionAnalyzer(MSONable):
    """
    Class for performing diffusion analyses.

    .. attribute: diffusivity

        Diffusivity in cm^2 / cm

    .. attribute: conductivity

        Conductivity in mS / cm

    .. attribute: diffusivity_components

        A vector with diffusivity in the a, b and c directions in cm^2 / cm

    .. attribute: conductivity components

        A vector with conductivity in the a, b and c directions in mS / cm

    .. attribute: max_framework_displacement

        The maximum (drift adjusted) distance of any framework atom from its
        starting location in A

    """

    def __init__(self, structure, displacements, specie, temperature,
                 time_step, step_skip=10):
        """
        This constructor is meant to be used with pre-processed data.
        Other convenient constructors are provded as static methods, e.g.,
        see from_vaspruns.

        Args:
            structure:
                Initial structure.
            displacements:
                Numpy array of with shape [site, time step, axis]
            specie:
                Specie to calculate diffusivity for as a String. E.g., "Li".
            temperature:
                Temperature of the diffusion run in Kelvin.
            time_step:
                Time step between measurements.
            step_skip:
                Sampling frequency of the displacements (time_step is
                multiplied by this number to get the real time between
                measurements)
        """
        self.s = structure
        self.disp = displacements
        self.sp = specie
        self.temperature = temperature
        self.time_step = time_step
        self.step_skip = step_skip
        self.indices = []
        self.framework_indices = []
        for i, site in enumerate(structure):
            if site.specie.symbol == specie:
                self.indices.append(i)
            else:
                self.framework_indices.append(i)
        if self.disp.shape[1] < 2:
            self.diffusivity = 0.
            self.conductivity = 0.
            self.diffusivity_components = np.array([0., 0., 0.])
            self.conductivity_components = np.array([0., 0., 0.])
            self.max_framework_displacement = 0
        else:
            framework_disp = self.disp[self.framework_indices]
            drift = np.average(framework_disp, axis=0)[None, :, :]
            #drift corrected position
            dc_x = self.disp[self.indices] - drift
            dc_framework = self.disp[self.framework_indices] - drift
            self.max_framework_displacement = \
                np.max(np.sum(dc_framework ** 2, axis=-1) ** 0.5)
            df_x = self.s.lattice.get_fractional_coords(dc_x)
            #limit the number of sampled timesteps to 200
            timesteps = np.arange(10, dc_x.shape[1],
                                  max(dc_x.shape[1] / 200, 1))
            x = timesteps * self.time_step * self.step_skip

            #calculate the smoothed msd values
            s_msd = np.zeros_like(x, dtype=np.double)
            s_msd_components = np.zeros(x.shape + (3,))
            lengths = np.array(self.s.lattice.abc)[None, None, :]
            for i, n in enumerate(timesteps):
                dx = dc_x[:, n:, :] - dc_x[:, :-n, :]
                s_msd[i] = 3 * np.average(dx ** 2)
                dcomponents = (df_x[:, n:, :] - df_x[:, :-n, :]) * lengths
                s_msd_components[i] = np.average(np.average(dcomponents ** 2,
                                                            axis=1), axis=0)

            #run the regression on the msd components
            m_components = np.zeros(3)
            for i in range(3):
                a = np.ones((len(x), 2))
                a[:, 0] = x
                (m, c), res, rank, s = \
                    np.linalg.lstsq(a, s_msd_components[:, i])
                m_components[i] = max(m, 1e-15)

            a = np.ones((len(x), 2))
            a[:, 0] = x
            (m, c), res, rank, s = np.linalg.lstsq(a, s_msd)
            #m shouldn't be negative
            m = max(m, 1e-20)

            #factor of 10 is to convert from A^2/fs to cm^2/s
            #factor of 6 is for dimensionality
            conv_factor = get_conversion_factor(self.s, self.sp,
                                                self.temperature)
            self.diffusivity = m / 60
            self.conductivity = self.diffusivity * conv_factor

            self.diffusivity_components = m_components / 20
            self.conductivity_components = self.diffusivity_components * \
                conv_factor

    @staticmethod
    def from_vaspruns(vaspruns, specie):
        """
        Convenient constructor that takes in a list of Vasprun objects to
        perform diffusion analysis.

        Args:
            vaspruns:
                list of Vasprun objects (must be ordered in sequence of run).
                 E.g., you may have performed sequential VASP runs to obtain
                 sufficient statistics.
            specie:
                Specie to calculate diffusivity for as a String. E.g., "Li".
        """
        structure = vaspruns[0].initial_structure
        step_skip = vaspruns[0].ionic_step_skip

        p = []
        for vr in vaspruns:
            assert vr.ionic_step_skip == step_skip
            p.extend([np.array(s['structure'].frac_coords)[:, None, :]
                      for s in vr.ionic_steps])
        p = np.concatenate(p, axis=1)
        dp = p[:, 1:, :] - p[:, :-1, :]
        dp = np.concatenate([np.zeros_like(dp[:, (0,), :]), dp], axis=1)
        dp = dp - np.round(dp)
        f_disp = np.cumsum(dp, axis=1)
        disp = structure.lattice.get_cartesian_coords(f_disp)

        temperature = vaspruns[0].parameters['TEEND']
        time_step = vaspruns[0].parameters['POTIM']

        return DiffusionAnalyzer(structure, disp, specie, temperature,
                                 time_step, step_skip=step_skip)

    @property
    def to_dict(self):
        return {
            "@module": self.__class__.__module__,
            "@class": self.__class__.__name__,
            "structure": self.s.to_dict,
            "displacements": self.disp.tolist(),
            "specie": self.sp,
            "temperature": self.temperature,
            "time_step": self.time_step,
            "step_skip": self.step_skip
        }

    @classmethod
    def from_dict(cls, d):
        structure = Structure.from_dict(d["structure"])
        return cls(structure, np.array(d["displacements"]), specie=d["specie"],
                   temperature=d["temperature"], time_step=d["time_step"],
                   step_skip=d["step_skip"])

def get_conversion_factor(structure, species, temperature):
    """
    Conversion factor to convert between cm^2/s diffusivity measurements and
    mS/cm conductivity measurements based on number of atoms of diffusing
    species and .

    Args:
        structure:
            Input structure.
        species:
            Diffusing species.
        temperature:
            Temperature of the diffusion run in Kelvin.

    Returns:
        Conversion factor.
        Conductivity (in mS/cm) = Conversion Factor * Diffusivity (in cm^2/s)
    """
    df_sp = smart_element_or_specie(species)
    if df_sp.Z in [1, 3, 11, 19, 37, 55]:
        z = 1
    else:
        z = df_sp.oxi_state

    n = structure.composition[species]

    V = structure.volume * 1e-24  # units cm^3
    F = ELECTRON_CHARGE * AVOGADROS_CONST  # sA/mol
    return 1000 * n / (V * AVOGADROS_CONST) * z ** 2 * F ** 2\
        / (BOLTZMANN_CONST * AVOGADROS_CONST * temperature)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
import random
import requests
import string
from time import time
from datetime import datetime

# returns a random timestamp
def random_timestamp():
    return random.randrange(int(time()))

# returns a random date since Jan 1st 1970
# optional date format may be passed
def random_date(format='%m-%d-%Y %H:%M:%S'):
    return datetime.fromtimestamp(random_timestamp()).strftime(format)

# returns a random char from the concatenation of ascii_lowercase and ascii_uppercase
def random_letter():
    return random.choice(string.ascii_letters)

# returns a random char from: 'abcdefghijklmnopqrstuvwxyz'
def random_lowercase():
    return random.choice(string.ascii_lowercase)

# returns a random char from: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
def random_uppercase():
    return random.choice(string.ascii_uppercase)

# returns a random char from: '0123456789'
def random_digit():
    return random.choice(string.digits)

# returns a random char from: '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
def random_punctuation():
    return random.choice(string.punctuation)

# returns a random char from the concatenation of digits, ascii_letters, punctuation, and whitespace.
def random_printable():
    return random.choice(string.printable)

def random_chuck_joke():
    try:
        data = requests.get("https://api.chucknorris.io/jokes/random").json()['value']
    except Exception:
        data = 'Chuck Norris doesn’t use web standards as the web will conform to him. AND YOU! CHECK YOUR INTERNET CONNECTION'

    return data

def random_hex_color():
    alpha = '0123456789abcdef'
    hex_color = '#'
    for i in range(6):
        hex_color += random.choice(alpha)
    return hex_color

def random_rgb_color():
    rgb_color = [random.randint(0,255),random.randint(0,255),random.randint(0,255)]
    return rgb_color

def random_pokemon():
    num = str(random.randint(1,700))
    data = requests.get('https://pokeapi.co/api/v2/pokemon/'+num).json()
    my_pokemon = data['forms'][0]['name']
    return my_pokemon

def random_fake_person():
    try:
        person =requests.get('http://api.namefake.com/random').json()
    except Exception:
        person = ""
    return person

def random_football_team():
    num = str(random.randint(1,890))
    try:
        football_team = requests.get('http://api.football-data.org/v1/teams/'+num).json()
    except Exception:
        football_team = ""
    return football_team

def random_word():
    try:
        word = requests.get("http://www.setgetgo.com/randomword/get.php").text
    except Exception as e:
        word = "potatoes"
    return word

def random_wikipedia_article():
    try:
        response = requests.get("https://en.wikipedia.org/w/api.php?format=json&action=query&generator=random&grnnamespace=0&prop=revisions|extracts&exintro=&explaintext&rvprop=content").json()
        res = response['query']['pages'][response['query']['pages'].keys()[0]]

        article = {
            "title": res['title'],
            "text": res['extract']
        }

    except Exception as e:
        article = {
            "title": "Python",
            "text": "Python is a widely used high-level, general-purpose, interpreted, dynamic programming language. Its design philosophy emphasizes code readability, and its syntax allows programmers to express concepts in fewer lines of code than possible in languages such as C++ or Java. The language provides constructs intended to enable writing clear programs on both a small and large scale.\nPython supports multiple programming paradigms, including object-oriented, imperative, functional programming, and procedural styles. It features a dynamic type system and automatic memory management and has a large and comprehensive standard library.\nPython interpreters are available for many operating systems, allowing Python code to run on a wide variety of systems. CPython, the reference implementation of Python, is open source software and has a community-based development model, as do nearly all of its variant implementations. CPython is managed by the non-profit Python Software Foundation.\n\n"
        }
    return article
<commit_msg>added randomize_list<commit_after># -*- coding: utf-8 -*-
import random
import requests
import string
from time import time
from datetime import datetime

# returns a randomly reordered version of the provided list
def randomize_list(list=[]):
    random.shuffle(list)
    return list

# returns a random timestamp
def random_timestamp():
    return random.randrange(int(time()))

# returns a random date since Jan 1st 1970
# optional date format may be passed
def random_date(format='%m-%d-%Y %H:%M:%S'):
    return datetime.fromtimestamp(random_timestamp()).strftime(format)

# returns a random char from the concatenation of ascii_lowercase and ascii_uppercase
def random_letter():
    return random.choice(string.ascii_letters)

# returns a random char from: 'abcdefghijklmnopqrstuvwxyz'
def random_lowercase():
    return random.choice(string.ascii_lowercase)

# returns a random char from: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
def random_uppercase():
    return random.choice(string.ascii_uppercase)

# returns a random char from: '0123456789'
def random_digit():
    return random.choice(string.digits)

# returns a random char from: '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
def random_punctuation():
    return random.choice(string.punctuation)

# returns a random char from the concatenation of digits, ascii_letters, punctuation, and whitespace.
def random_printable():
    return random.choice(string.printable)

def random_chuck_joke():
    try:
        data = requests.get("https://api.chucknorris.io/jokes/random").json()['value']
    except Exception:
        data = 'Chuck Norris doesn’t use web standards as the web will conform to him. AND YOU! CHECK YOUR INTERNET CONNECTION'

    return data

def random_hex_color():
    alpha = '0123456789abcdef'
    hex_color = '#'
    for i in range(6):
        hex_color += random.choice(alpha)
    return hex_color

def random_rgb_color():
    rgb_color = [random.randint(0,255),random.randint(0,255),random.randint(0,255)]
    return rgb_color

def random_pokemon():
    num = str(random.randint(1,700))
    data = requests.get('https://pokeapi.co/api/v2/pokemon/'+num).json()
    my_pokemon = data['forms'][0]['name']
    return my_pokemon

def random_fake_person():
    try:
        person =requests.get('http://api.namefake.com/random').json()
    except Exception:
        person = ""
    return person

def random_football_team():
    num = str(random.randint(1,890))
    try:
        football_team = requests.get('http://api.football-data.org/v1/teams/'+num).json()
    except Exception:
        football_team = ""
    return football_team

def random_word():
    try:
        word = requests.get("http://www.setgetgo.com/randomword/get.php").text
    except Exception as e:
        word = "potatoes"
    return word

def random_wikipedia_article():
    try:
        response = requests.get("https://en.wikipedia.org/w/api.php?format=json&action=query&generator=random&grnnamespace=0&prop=revisions|extracts&exintro=&explaintext&rvprop=content").json()
        res = response['query']['pages'][response['query']['pages'].keys()[0]]

        article = {
            "title": res['title'],
            "text": res['extract']
        }

    except Exception as e:
        article = {
            "title": "Python",
            "text": "Python is a widely used high-level, general-purpose, interpreted, dynamic programming language. Its design philosophy emphasizes code readability, and its syntax allows programmers to express concepts in fewer lines of code than possible in languages such as C++ or Java. The language provides constructs intended to enable writing clear programs on both a small and large scale.\nPython supports multiple programming paradigms, including object-oriented, imperative, functional programming, and procedural styles. It features a dynamic type system and automatic memory management and has a large and comprehensive standard library.\nPython interpreters are available for many operating systems, allowing Python code to run on a wide variety of systems. CPython, the reference implementation of Python, is open source software and has a community-based development model, as do nearly all of its variant implementations. CPython is managed by the non-profit Python Software Foundation.\n\n"
        }
    return article
<|endoftext|>


<commit_before># coding: utf-8
# Copyright (c) Pymatgen Development Team.
# Distributed under the terms of the MIT License.

from __future__ import division, print_function, unicode_literals
from __future__ import absolute_import

"""
This module provides a class used to describe the elastic tensor,
including methods used to fit the elastic tensor from linear response
stress-strain data
"""

from pymatgen.analysis.elasticity import voigt_map, reverse_voigt_map
from pymatgen.analysis.elasticity.tensors import TensorBase
from pymatgen.analysis.elasticity.stress import Stress
from pymatgen.analysis.elasticity.strain import Strain
import numpy as np
import warnings
import itertools
from six.moves import range

__author__ = "Maarten de Jong"
__copyright__ = "Copyright 2012, The Materials Project"
__credits__ = "Joseph Montoya, Shyam Dwaraknath, Mark Asta, Anubhav Jain"
__version__ = "1.0"
__maintainer__ = "Joseph Montoya"
__email__ = "montoyjh@lbl.gov"
__status__ = "Development"
__date__ = "March 22, 2012"

class ElasticTensor(TensorBase):
    """
    This class extends TensorBase to describe the 3x3x3x3
    elastic tensor, C_{ij}, in Voigt-notation
    """

    def __new__(cls, input_array, tol=1e-3):
        """
        Create an ElasticTensor object.  The constructor throws an error if
        the shape of the input_matrix argument is not 3x3x3x3, i. e. in true
        tensor notation.  Issues a warning if the input_matrix argument does
        not satisfy standard symmetries.  Note that the constructor uses
        __new__ rather than __init__ according to the standard method of
        subclassing numpy ndarrays.

        Args:
            input_array (3x3x3x3 array-like): the 3x3x3x3 array-like
                representing the elastic tensor

            tol (float): tolerance for initial symmetry test of tensor
        """

        obj = TensorBase(input_array).view(cls)
        if not ((obj - np.transpose(obj, (1, 0, 2, 3)) < tol).all() and
                (obj - np.transpose(obj, (0, 1, 3, 2)) < tol).all() and
                (obj - np.transpose(obj, (1, 0, 3, 2)) < tol).all() and
                (obj - np.transpose(obj, (3, 2, 0, 1)) < tol).all()):

            warnings.warn("Input elasticity tensor does "
                          "not satisfy standard symmetries")

        if obj.shape != (3, 3, 3, 3):
            raise ValueError("Default elastic tensor constructor requires "
                             "input to be the true 3x3x3x3 representation. "
                             "To construct from an elastic tensor from "
                             "6x6 Voigt array, use ElasticTensor.from_voigt")
        return obj

    @classmethod
    def from_voigt(cls, voigt_matrix, tol=1e-2):
        """
        Constructor based on the voigt notation tensor

        Args:
            voigt_matrix: (6x6 array-like): the Voigt notation 6x6 array-like
                representing the elastic tensor
        """
        voigt_matrix = np.array(voigt_matrix)
        if voigt_matrix.shape != (6, 6):
            raise ValueError("From_voigt takes a 6x6 array corresponding to "
                             "the elastic tensor in voigt notation as input.")

        c = np.zeros((3, 3, 3, 3))
        for ind in itertools.product(*[range(3)]*4):
            v_ind = (reverse_voigt_map[ind[:2]],
                     reverse_voigt_map[ind[2:]])
            c[ind] = voigt_matrix[v_ind]
        return cls(c)

    @property
    def voigt(self):
        """
        Returns the voigt notation 6x6 array corresponding to the
        elastic tensor
        """
        c_pq = np.zeros((6, 6))
        for p in range(6):
            for q in range(6):
                i, j = voigt_map[p]
                k, l = voigt_map[q]
                c_pq[p, q] = self[i, j, k, l]
        return c_pq

    @property
    def compliance_tensor(self):
        """
        returns the compliance tensor, which is the matrix inverse of the
        Voigt-notation elastic tensor
        """
        return np.linalg.inv(self.voigt)

    @property
    def k_voigt(self):
        """
        returns the K_v bulk modulus
        """
        return self.voigt[:3, :3].mean()

    @property
    def g_voigt(self):
        """
        returns the G_v shear modulus
        """
        return (2. * self.voigt[:3, :3].trace() -
                np.triu(self.voigt[:3, :3]).sum() +
                3 * self.voigt[3:, 3:].trace()) / 15.

    @property
    def k_reuss(self):
        """
        returns the K_r bulk modulus
        """
        return 1. / self.compliance_tensor[:3, :3].sum()

    @property
    def g_reuss(self):
        """
        returns the G_r shear modulus
        """
        return 15. / (8. * self.compliance_tensor[:3, :3].trace() -
                      4. * np.triu(self.compliance_tensor[:3, :3]).sum() +
                      3. * self.compliance_tensor[3:, 3:].trace())

    @property
    def k_vrh(self):
        """
        returns the K_vrh (Voigt-Reuss-Hill) average bulk modulus
        """
        return 0.5 * (self.k_voigt + self.k_reuss)

    @property
    def g_vrh(self):
        """
        returns the G_vrh (Voigt-Reuss-Hill) average shear modulus
        """
        return 0.5 * (self.g_voigt + self.g_reuss)

    @property
    def kg_average(self):
        """
        returns a list of Voigt, Reuss, and Voigt-Reuss-Hill averages of bulk
        and shear moduli similar to legacy behavior
        """
        return [self.k_voigt, self.g_voigt, self.k_reuss, self.g_reuss,
                self.k_vrh, self.g_vrh]

    @property
    def y_mod(self):
        """
        Calculates Young's modulus (in SI units) using the Voigt-Reuss-Hill averages of bulk
            and shear moduli
        """
        return 9e9 * self.k_vrh * self.g_vrh / \
                    (3. * self.k_vrh * self.g_vrh)

    @property
    def trans_v(self, structure):
        """
        Calculates transverse sound velocity (in SI units) using the Voigt-Reuss-Hill average bulk modulus
            and

        Args:
            structure: pymatgen structure object

        Returns: transverse sound velocity (in SI units)

        """
        nsites = structure.num_sites
        volume = structure.volume
        natoms = structure.composition.num_atoms
        weight = structure.composition.weight
        mass_density = 1.6605e3 * nsites * volume * weight / \
                           (natoms * volume)
        return 1e9 * self.k_vrh / mass_density**0.5

    @property
    def long_v(self, structure):
        """
        Calculates longitudinal sound velocity (in SI units) using the Voigt-Reuss-Hill average bulk modulus
            and

        Args:
            structure: pymatgen structure object

        Returns: longitudinal sound velocity (in SI units)

        """
        nsites = structure.num_sites
        volume = structure.volume
        natoms = structure.composition.num_atoms
        weight = structure.composition.weight
        mass_density = 1.6605e3 * nsites * volume * weight / \
                           (natoms * volume)
        return 1e9 * self.k_vrh + \
                     4./3. * self.g_vrh / mass_density**0.5

    @property
    def universal_anisotropy(self):
        """
        returns the universal anisotropy value
        """
        return 5. * self.g_voigt / self.g_reuss + \
            self.k_voigt / self.k_reuss - 6.

    @property
    def homogeneous_poisson(self):
        """
        returns the homogeneous poisson ratio
        """
        return (1. - 2. / 3. * self.g_vrh / self.k_vrh) / \
               (2. + 2. / 3. * self.g_vrh / self.k_vrh)

    def energy_density(self, strain):
        """
        Calculates the elastic energy density due to a strain
        """
        # Conversion factor for GPa to eV/Angstrom^3
        GPA_EV = 0.000624151

        with warnings.catch_warnings(record=True):
            e_density = np.dot(np.transpose(Strain(strain).voigt),
                np.dot(self.voigt, Strain(strain).voigt))/2 * GPA_EV

        return e_density

    @classmethod
    def from_strain_stress_list(cls, strains, stresses):
        """
        Class method to fit an elastic tensor from stress/strain data.  Method
        uses Moore-Penrose pseudoinverse to invert the s = C*e equation with
        elastic tensor, stress, and strain in voigt notation

        Args:
            stresses (Nx3x3 array-like): list or array of stresses
            strains (Nx3x3 array-like): list or array of strains
        """
        # convert the stress/strain to Nx6 arrays of voigt-notation
        warnings.warn("Linear fitting of Strain/Stress lists may yield "
                      "questionable results from vasp data, use with caution.")
        stresses = np.array([Stress(stress).voigt for stress in stresses])
        with warnings.catch_warnings(record=True):
            strains = np.array([Strain(strain).voigt for strain in strains])

        voigt_fit = np.transpose(np.dot(np.linalg.pinv(strains), stresses))
        return cls.from_voigt(voigt_fit)

    @classmethod
    def from_stress_dict(cls, stress_dict, tol=0.1, vasp=True, symmetry=False):
        """
        Constructs the elastic tensor from IndependentStrain-Stress dictionary
        corresponding to legacy behavior of elasticity package.

        Args:
            stress_dict (dict): dictionary of stresses indexed by corresponding
                IndependentStrain objects.
            tol (float): tolerance for zeroing small values of the tensor
            vasp (boolean): flag for whether the stress tensor should be
                converted based on vasp units/convention for stress
            symmetry (boolean): flag for whether or not the elastic tensor
                should fit from data based on symmetry
        """
        inds = [(0, 0), (1, 1), (2, 2), (1, 2), (0, 2), (0, 1)]
        c_ij = np.array([[np.polyfit([strain[ind1] for strain in list(stress_dict.keys())
                                      if (strain.i, strain.j) == ind1],
                                     [stress_dict[strain][ind2] for strain
                                      in list(stress_dict.keys())
                                      if (strain.i, strain.j) == ind1], 1)[0]
                          for ind1 in inds] for ind2 in inds])
        if vasp:
            c_ij *= -0.1  # Convert units/sign convention of vasp stress tensor
        c_ij[0:, 3:] = 0.5 * c_ij[0:, 3:]  # account for voigt doubling of e4,e5,e6
        c = cls.from_voigt(c_ij)
        c = c.zeroed()
        return c

    @property
    def voigt_symmetrized(self):
        """
        Reconstructs the elastic tensor by symmetrizing the voigt
        notation tensor, to allow for legacy behavior
        """

        v = self.voigt
        new_v = 0.5 * (np.transpose(v) + v)
        return ElasticTensor.from_voigt(new_v)
<commit_msg>add Snyder's acoustic sound velocity<commit_after># coding: utf-8
# Copyright (c) Pymatgen Development Team.
# Distributed under the terms of the MIT License.

from __future__ import division, print_function, unicode_literals
from __future__ import absolute_import

"""
This module provides a class used to describe the elastic tensor,
including methods used to fit the elastic tensor from linear response
stress-strain data
"""

from pymatgen.analysis.elasticity import voigt_map, reverse_voigt_map
from pymatgen.analysis.elasticity.tensors import TensorBase
from pymatgen.analysis.elasticity.stress import Stress
from pymatgen.analysis.elasticity.strain import Strain
import numpy as np
import warnings
import itertools
from six.moves import range

__author__ = "Maarten de Jong"
__copyright__ = "Copyright 2012, The Materials Project"
__credits__ = "Joseph Montoya, Shyam Dwaraknath, Mark Asta, Anubhav Jain"
__version__ = "1.0"
__maintainer__ = "Joseph Montoya"
__email__ = "montoyjh@lbl.gov"
__status__ = "Development"
__date__ = "March 22, 2012"

class ElasticTensor(TensorBase):
    """
    This class extends TensorBase to describe the 3x3x3x3
    elastic tensor, C_{ij}, in Voigt-notation
    """

    def __new__(cls, input_array, tol=1e-3):
        """
        Create an ElasticTensor object.  The constructor throws an error if
        the shape of the input_matrix argument is not 3x3x3x3, i. e. in true
        tensor notation.  Issues a warning if the input_matrix argument does
        not satisfy standard symmetries.  Note that the constructor uses
        __new__ rather than __init__ according to the standard method of
        subclassing numpy ndarrays.

        Args:
            input_array (3x3x3x3 array-like): the 3x3x3x3 array-like
                representing the elastic tensor

            tol (float): tolerance for initial symmetry test of tensor
        """

        obj = TensorBase(input_array).view(cls)
        if not ((obj - np.transpose(obj, (1, 0, 2, 3)) < tol).all() and
                (obj - np.transpose(obj, (0, 1, 3, 2)) < tol).all() and
                (obj - np.transpose(obj, (1, 0, 3, 2)) < tol).all() and
                (obj - np.transpose(obj, (3, 2, 0, 1)) < tol).all()):

            warnings.warn("Input elasticity tensor does "
                          "not satisfy standard symmetries")

        if obj.shape != (3, 3, 3, 3):
            raise ValueError("Default elastic tensor constructor requires "
                             "input to be the true 3x3x3x3 representation. "
                             "To construct from an elastic tensor from "
                             "6x6 Voigt array, use ElasticTensor.from_voigt")
        return obj

    @classmethod
    def from_voigt(cls, voigt_matrix, tol=1e-2):
        """
        Constructor based on the voigt notation tensor

        Args:
            voigt_matrix: (6x6 array-like): the Voigt notation 6x6 array-like
                representing the elastic tensor
        """
        voigt_matrix = np.array(voigt_matrix)
        if voigt_matrix.shape != (6, 6):
            raise ValueError("From_voigt takes a 6x6 array corresponding to "
                             "the elastic tensor in voigt notation as input.")

        c = np.zeros((3, 3, 3, 3))
        for ind in itertools.product(*[range(3)]*4):
            v_ind = (reverse_voigt_map[ind[:2]],
                     reverse_voigt_map[ind[2:]])
            c[ind] = voigt_matrix[v_ind]
        return cls(c)

    @property
    def voigt(self):
        """
        Returns the voigt notation 6x6 array corresponding to the
        elastic tensor
        """
        c_pq = np.zeros((6, 6))
        for p in range(6):
            for q in range(6):
                i, j = voigt_map[p]
                k, l = voigt_map[q]
                c_pq[p, q] = self[i, j, k, l]
        return c_pq

    @property
    def compliance_tensor(self):
        """
        returns the compliance tensor, which is the matrix inverse of the
        Voigt-notation elastic tensor
        """
        return np.linalg.inv(self.voigt)

    @property
    def k_voigt(self):
        """
        returns the K_v bulk modulus
        """
        return self.voigt[:3, :3].mean()

    @property
    def g_voigt(self):
        """
        returns the G_v shear modulus
        """
        return (2. * self.voigt[:3, :3].trace() -
                np.triu(self.voigt[:3, :3]).sum() +
                3 * self.voigt[3:, 3:].trace()) / 15.

    @property
    def k_reuss(self):
        """
        returns the K_r bulk modulus
        """
        return 1. / self.compliance_tensor[:3, :3].sum()

    @property
    def g_reuss(self):
        """
        returns the G_r shear modulus
        """
        return 15. / (8. * self.compliance_tensor[:3, :3].trace() -
                      4. * np.triu(self.compliance_tensor[:3, :3]).sum() +
                      3. * self.compliance_tensor[3:, 3:].trace())

    @property
    def k_vrh(self):
        """
        returns the K_vrh (Voigt-Reuss-Hill) average bulk modulus
        """
        return 0.5 * (self.k_voigt + self.k_reuss)

    @property
    def g_vrh(self):
        """
        returns the G_vrh (Voigt-Reuss-Hill) average shear modulus
        """
        return 0.5 * (self.g_voigt + self.g_reuss)

    @property
    def kg_average(self):
        """
        returns a list of Voigt, Reuss, and Voigt-Reuss-Hill averages of bulk
        and shear moduli similar to legacy behavior
        """
        return [self.k_voigt, self.g_voigt, self.k_reuss, self.g_reuss,
                self.k_vrh, self.g_vrh]

    @property
    def y_mod(self):
        """
        Calculates Young's modulus (in SI units) using the Voigt-Reuss-Hill averages of bulk
            and shear moduli
        """
        return 9e9 * self.k_vrh * self.g_vrh / \
                    (3. * self.k_vrh * self.g_vrh)

    @property
    def trans_v(self, structure):
        """
        Calculates transverse sound velocity (in SI units) using the Voigt-Reuss-Hill average bulk modulus
            and

        Args:
            structure: pymatgen structure object

        Returns: transverse sound velocity (in SI units)

        """
        nsites = structure.num_sites
        volume = structure.volume
        natoms = structure.composition.num_atoms
        weight = structure.composition.weight
        mass_density = 1.6605e3 * nsites * volume * weight / \
                           (natoms * volume)
        return 1e9 * self.k_vrh / mass_density**0.5

    @property
    def long_v(self, structure):
        """
        Calculates longitudinal sound velocity (in SI units) using the Voigt-Reuss-Hill average bulk modulus
            and

        Args:
            structure: pymatgen structure object

        Returns: longitudinal sound velocity (in SI units)

        """
        nsites = structure.num_sites
        volume = structure.volume
        natoms = structure.composition.num_atoms
        weight = structure.composition.weight
        mass_density = 1.6605e3 * nsites * volume * weight / \
                           (natoms * volume)
        return 1e9 * self.k_vrh + \
                     4./3. * self.g_vrh / mass_density**0.5

    @property
    def snyder_ac(self, structure):
        """
        Calculates Snyder's acoustic sound velocity (in SI units)

        Args:
            structure: pymatgen structure object

        Returns: Snyder's acoustic sound velocity (in SI units)

        """
        nsites = structure.num_sites
        volume = structure.volume
        natoms = structure.composition.num_atoms
        num_density = 1e30 * nsites / volume
        tot_mass = sum([e.atomic_mass for e in structure.species])
        avg_mass = 1.6605e-27 * tot_mass / natoms
        return 0.38483 * avg_mass * \
                        (self.long_v + 2./3.*self.trans_v)**3. / \
                        (300. * num_density**(-2./3.) * nsites**(1./3.))

    @property
    def universal_anisotropy(self):
        """
        returns the universal anisotropy value
        """
        return 5. * self.g_voigt / self.g_reuss + \
            self.k_voigt / self.k_reuss - 6.

    @property
    def homogeneous_poisson(self):
        """
        returns the homogeneous poisson ratio
        """
        return (1. - 2. / 3. * self.g_vrh / self.k_vrh) / \
               (2. + 2. / 3. * self.g_vrh / self.k_vrh)

    def energy_density(self, strain):
        """
        Calculates the elastic energy density due to a strain
        """
        # Conversion factor for GPa to eV/Angstrom^3
        GPA_EV = 0.000624151

        with warnings.catch_warnings(record=True):
            e_density = np.dot(np.transpose(Strain(strain).voigt),
                np.dot(self.voigt, Strain(strain).voigt))/2 * GPA_EV

        return e_density

    @classmethod
    def from_strain_stress_list(cls, strains, stresses):
        """
        Class method to fit an elastic tensor from stress/strain data.  Method
        uses Moore-Penrose pseudoinverse to invert the s = C*e equation with
        elastic tensor, stress, and strain in voigt notation

        Args:
            stresses (Nx3x3 array-like): list or array of stresses
            strains (Nx3x3 array-like): list or array of strains
        """
        # convert the stress/strain to Nx6 arrays of voigt-notation
        warnings.warn("Linear fitting of Strain/Stress lists may yield "
                      "questionable results from vasp data, use with caution.")
        stresses = np.array([Stress(stress).voigt for stress in stresses])
        with warnings.catch_warnings(record=True):
            strains = np.array([Strain(strain).voigt for strain in strains])

        voigt_fit = np.transpose(np.dot(np.linalg.pinv(strains), stresses))
        return cls.from_voigt(voigt_fit)

    @classmethod
    def from_stress_dict(cls, stress_dict, tol=0.1, vasp=True, symmetry=False):
        """
        Constructs the elastic tensor from IndependentStrain-Stress dictionary
        corresponding to legacy behavior of elasticity package.

        Args:
            stress_dict (dict): dictionary of stresses indexed by corresponding
                IndependentStrain objects.
            tol (float): tolerance for zeroing small values of the tensor
            vasp (boolean): flag for whether the stress tensor should be
                converted based on vasp units/convention for stress
            symmetry (boolean): flag for whether or not the elastic tensor
                should fit from data based on symmetry
        """
        inds = [(0, 0), (1, 1), (2, 2), (1, 2), (0, 2), (0, 1)]
        c_ij = np.array([[np.polyfit([strain[ind1] for strain in list(stress_dict.keys())
                                      if (strain.i, strain.j) == ind1],
                                     [stress_dict[strain][ind2] for strain
                                      in list(stress_dict.keys())
                                      if (strain.i, strain.j) == ind1], 1)[0]
                          for ind1 in inds] for ind2 in inds])
        if vasp:
            c_ij *= -0.1  # Convert units/sign convention of vasp stress tensor
        c_ij[0:, 3:] = 0.5 * c_ij[0:, 3:]  # account for voigt doubling of e4,e5,e6
        c = cls.from_voigt(c_ij)
        c = c.zeroed()
        return c

    @property
    def voigt_symmetrized(self):
        """
        Reconstructs the elastic tensor by symmetrizing the voigt
        notation tensor, to allow for legacy behavior
        """

        v = self.voigt
        new_v = 0.5 * (np.transpose(v) + v)
        return ElasticTensor.from_voigt(new_v)
<|endoftext|>