コード例 #1
0
 def test_iron(self):
     recip = reciprocal_lattice([[-2.708355, 2.708355, 2.708355],
                                 [2.708355, -2.708355, 2.708355],
                                 [2.708355, 2.708355, -2.708355]])
     expected_recip = [[0., 1.15996339, 1.15996339],
                       [1.15996339, 0., 1.15996339],
                       [1.15996339, 1.15996339, 0.]]
     npt.assert_allclose(recip, expected_recip)
コード例 #2
0
 def test_identity(self):
     recip = reciprocal_lattice([[1., 0., 0.],
                                 [0., 1., 0.],
                                 [0., 0., 1.]])
     expected_recip = [[2*math.pi, 0., 0.],
                       [0., 2*math.pi, 0.],
                       [0., 0., 2*math.pi]]
     npt.assert_allclose(recip, expected_recip)
コード例 #3
0
    def test_graphite(self):
        recip = reciprocal_lattice([[4.025915, -2.324363, 0.000000],
                                    [-0.000000, 4.648726, 0.000000],
                                    [0.000000, 0.000000, 12.850138]])
        expected_recip = [[1.56068503860106, 0., 0.],
                          [0.780342519300529, 1.3515929541082, 0.],
                          [0., 0., 0.488958586061845]]

        npt.assert_allclose(recip, expected_recip)
コード例 #4
0
def plot_dispersion(data,
                    title='',
                    btol=10.0,
                    up=True,
                    down=True,
                    **line_kwargs):
    """
    Creates a Matplotlib figure of the band structure

    Parameters
    ----------
    data: Data object
        Data object containing the frequencies and other data required for
        plotting (qpts, n_ions, cell_vecs)
    title : string, optional
        The figure title. Default: ''
    btol : float, optional
        Determines the limit for plotting sections of reciprocal space on
        different subplots, as a fraction of the median distance between
        q-points. Default: 10.0
    up : boolean, optional
        Whether to plot spin up frequencies (if applicable). Default: True
    down : boolean, optional
        Whether to plot spin down frequencies (if applicable). Default: True
    **line_kwargs : Line2D properties, optional
        Used in the axes.plot command to specify properties like linewidth,
        linestyle
    Returns
    -------
    fig : matplotlib.figure.Figure or None
        If matplotlib.pyplot can be imported, returns a Figure containing
        subplot(s) for the plotted band structure, otherwise returns None.
        If there is a large gap between some q-points there can be multiple
        subplots.
    """
    try:
        import matplotlib.pyplot as plt
    except ImportError:
        warnings.warn(('Cannot import Matplotlib to plot dispersion (maybe '
                       'Matplotlib is not installed?). To install Euphonic\'s'
                       ' optional Matplotlib dependency, try:\n\npip install'
                       ' euphonic[matplotlib]\n'))
        raise

    cell_vec = (data.cell_vec.to('angstrom').magnitude)
    recip_latt = reciprocal_lattice(cell_vec)

    abscissa = calc_abscissa(data.qpts, recip_latt)
    # Determine reciprocal space coordinates that are far enough apart to be
    # in separate subplots, and determine index limits
    diff = np.diff(abscissa)
    median = np.median(diff)
    breakpoints = np.where(diff / median > btol)[0]
    imin = np.concatenate(([0], breakpoints + 1))
    imax = np.concatenate((breakpoints, [len(abscissa) - 1]))

    # Calculate width ratios so that the x-scale is the same for each subplot
    subplot_widths = [
        abscissa[imax[i]] - abscissa[imin[i]] for i in range(len(imax))
    ]
    gridspec = dict(
        width_ratios=[w / subplot_widths[0] for w in subplot_widths])
    # Create figure with correct number of subplots
    n_subplots = len(breakpoints) + 1
    fig, subplots = plt.subplots(1,
                                 n_subplots,
                                 sharey=True,
                                 gridspec_kw=gridspec)
    if n_subplots == 1:
        # Ensure subplots is always an array
        subplots = np.array([subplots])

    # Y-axis formatting, only need to format y-axis for first subplot as they
    # share the y-axis
    # Replace 1/cm with cm^-1
    units_str = data._e_units
    inverse_unit_index = units_str.find('/')
    if inverse_unit_index > -1:
        units_str = units_str[inverse_unit_index + 1:]
        subplots[0].set_ylabel('Energy (' + units_str + r'$^{-1}$)')
    else:
        subplots[0].set_ylabel('Energy (' + units_str + ')')
    subplots[0].ticklabel_format(style='sci', scilimits=(-2, 2), axis='y')

    # Configure each subplot
    # Calculate x-axis (recip space) ticks and labels
    xlabels, qpts_with_labels = recip_space_labels(data)
    for i, label in enumerate(xlabels):
        if label == 'GAMMA':
            xlabels[i] = r'$\Gamma$'
    if np.all(xlabels == ''):
        xlabels = np.around(data.qpts[qpts_with_labels, :], decimals=2)
    xticks = abscissa[qpts_with_labels]
    for i, ax in enumerate(subplots):
        # X-axis formatting
        # Set high symmetry point x-axis ticks/labels
        ax.set_xticks(xticks)
        ax.xaxis.grid(True, which='major')
        # Convert xlabels to list from Numpy array to avoid elementwise
        # comparison FutureWarning when calling set_xticklabels
        if not isinstance(xlabels, list):
            xlabels = xlabels.tolist()
        # Rotate long tick labels
        if len(max(xlabels, key=len)) >= 11:
            ax.set_xticklabels(xlabels, rotation=90)
        else:
            ax.set_xticklabels(xlabels)
        ax.set_xlim(left=abscissa[imin[i]], right=abscissa[imax[i]])

        # Plot frequencies and Fermi energy
        if up:
            if hasattr(data, 'split_i') and data.split_i.size > 0:
                split_i = data.split_i
            else:
                split_i = None
            # If there is LO-TO splitting, plot in sections
            if split_i is not None:
                section_i = np.where(
                    np.logical_and(split_i > imin[i], split_i < imax[i]))[0]
                n_sections = section_i.size + 1
            else:
                n_sections = 1

            # Put frequencies in order if reorder_freqs() has been called
            if hasattr(data, '_mode_map'):
                freqs = data.freqs.magnitude[
                    np.arange(len(data.qpts))[:, np.newaxis], data._mode_map]
                if split_i is not None:
                    split_freqs = data.split_freqs.magnitude[
                        np.arange(len(split_i))[:, np.newaxis],
                        data._mode_map[split_i]]
            else:
                freqs = data.freqs.magnitude
                if split_i is not None:
                    split_freqs = data.split_freqs.magnitude

            if n_sections > 1:
                section_edges = np.concatenate(
                    ([imin[i]], split_i[section_i], [imax[i]]))
                for n in range(n_sections):
                    plot_freqs = np.copy(
                        freqs[section_edges[n]:section_edges[n + 1] + 1])
                    if n == 0:
                        if (imin[i] in split_i):
                            # First point in this subplot is split gamma point
                            # Replace freqs with split freqs at gamma point
                            split_idx = np.where(split_i == imin[i])[0][0]
                            plot_freqs[0] = split_freqs[split_idx]
                    else:
                        # Replace freqs with split freqs at gamma point
                        plot_freqs[0] = split_freqs[section_i[n - 1]]
                    ax.plot(abscissa[section_edges[n]:section_edges[n + 1] +
                                     1],
                            plot_freqs,
                            lw=1.0,
                            **line_kwargs)
            else:
                ax.plot(abscissa[imin[i]:imax[i] + 1],
                        freqs[imin[i]:imax[i] + 1],
                        lw=1.0,
                        **line_kwargs)
        if down and hasattr(data, 'freq_down') and len(data.freq_down) > 0:
            freq_down = data.freq_down.magnitude
            ax.plot(abscissa[imin[i]:imax[i] + 1],
                    freq_down[imin[i]:imax[i] + 1],
                    lw=1.0,
                    **line_kwargs)
        if hasattr(data, 'fermi'):
            for i, ef in enumerate(data.fermi.magnitude):
                if i == 0:
                    ax.axhline(y=ef, ls='dashed', c='k', label=r'$\epsilon_F$')
                else:
                    ax.axhline(y=ef, ls='dashed', c='k')

    # Only set legend for last subplot, they all have the same legend labels
    if hasattr(data, 'fermi'):
        subplots[-1].legend()

    # Make sure axis/figure titles aren't cut off. Rect is used to leave some
    # space at the top of the figure for suptitle
    fig.suptitle(title)
    plt.tight_layout(rect=[0, 0.03, 1, 0.95])

    return fig
コード例 #5
0
def output_grace(data, seedname='out', up=True, down=True):
    """
    Creates a .agr Grace file of the band structure

    Parameters
    ----------
    data: Data object
        Data object containing the frequencies and other data required for
        plotting (qpts, n_ions, cell_vecs)
    seedname : string, optional
        Determines the figure title and output file name, seedname.agr.
        Default: 'out'
    up : boolean, optional
        Whether to plot spin up frequencies (if applicable). Default: True
    down : boolean, optional
        Whether to plot spin down frequencies (if applicable). Default: True
    """
    try:
        from PyGrace.grace import Grace
    except ImportError:
        warnings.warn(('PyGrace is not installed, attempting to write .agr '
                       'Grace file anyway. If using Python 2, you can install'
                       ' PyGrace from https://github.com/pygrace/pygrace'),
                      stacklevel=2)

    # Do calculations required for axis, tick labels etc.
    # Calculate distance along x axis
    cell_vec = (data.cell_vec.to('angstrom').magnitude)
    recip_latt = reciprocal_lattice(cell_vec)
    abscissa = calc_abscissa(data.qpts, recip_latt)
    # Calculate x-axis (recip space) ticks and labels
    xlabels, qpts_with_labels = recip_space_labels(data)

    units_str = data._e_units
    inverse_unit_index = units_str.find('/')
    if inverse_unit_index > -1:
        units_str = units_str[inverse_unit_index + 1:]
        yaxis_label = '\\f{{Symbol}}e\\f{{}} ({0}\S-1\\N)'.format(units_str)
    else:
        yaxis_label = '\\f{{Symbol}}e\\f{{}} ({0})'.format(units_str)
    # Format tick labels
    for i, label in enumerate(xlabels):
        if label == 'GAMMA':  # Format gamma symbol
            label = '\\f{Symbol}G\\f{}'

    if 'PyGrace' in sys.modules:
        grace = Grace()
        grace.background_fill = 'on'
        graph = grace.add_graph()
        graph.yaxis.label.text = yaxis_label

        graph.xaxis.tick.set_spec_ticks(abscissa[qpts_with_labels].tolist(),
                                        [], xlabels.tolist())
        graph.xaxis.tick.major_grid = 'on'
        if len(max(xlabels, key=len)) >= 11:
            graph.xaxis.ticklabel.configure(angle=315, char_size=1.0)

        for i in range(data.n_branches):
            if up:
                ds = graph.add_dataset(
                    zip(abscissa, data.freqs[:, i].magnitude))
                ds.line.configure(linewidth=2.0, color=i % 16)
                ds.symbol.shape = 0
            if down and hasattr(data, 'freq_down') and len(data.freq_down) > 0:
                ds = graph.add_dataset(
                    zip(abscissa, data.freq_down[:, i].magnitude))
                ds.line.configure(linewidth=2.0, color=i % 16)
                ds.symbol.shape = 0

        if hasattr(data, 'fermi'):
            for i, ef in enumerate(data.fermi.magnitude):
                ds = graph.add_dataset(zip([0, abscissa[-1]], [ef, ef]))
                ds.line.configure(linewidth=2.0, color=1, linestyle=3)

        graph.set_world_to_limits()
        grace.write_file(seedname + '.agr')

    else:
        with open(seedname + '.agr', 'w') as f:
            f.write('@with g0\n')
            f.write('@title "{0}"\n'.format(seedname))
            f.write('@view 0.150000, 0.250000, 0.700000, 0.850000\n')
            f.write('@world xmin 0\n')
            f.write('@world xmax {0:.3f}\n'.format(abscissa[-1] + 0.002))
            f.write('@world ymin 0\n')
            f.write('@default linewidth 2.0\n')
            f.write('@default char size 1.5\n')
            f.write('@autoscale onread yaxes\n')

            f.write('@yaxis  bar linewidth 2.0\n')
            f.write('@yaxis label "{0}"\n'.format(yaxis_label))
            f.write('@yaxis label char size 1.5\n')
            f.write('@yaxis ticklabel char size 1.5\n')

            f.write('@xaxis  bar linewidth 2.0\n')
            f.write('@xaxis label char size 1.5\n')
            f.write('@xaxis tick major linewidth 1.6\n')
            f.write('@xaxis tick major grid on\n')
            f.write('@xaxis tick spec type both\n')

            f.write('@xaxis tick spec {0:d}\n'.format(len(xlabels)))
            # Rotate long tick labels
            if len(max(xlabels, key=len)) <= 11:
                f.write('@xaxis ticklabel char size 1.5\n')
            else:
                f.write('@xaxis ticklabel char size 1.0\n')
                f.write('@xaxis ticklabel angle 315\n')

            for i, label in enumerate(xlabels):
                f.write('@xaxis tick major {0:d},{1:8.3f}\n'.format(
                    i, abscissa[qpts_with_labels[i]]))
                f.write('@xaxis ticklabel {0:d},"{1}"\n'.format(i, label))

            # Write frequencies
            for i in range(data.n_branches):
                n_sets = 0
                if up:
                    f.write('@target G0.S{0:d}\n'.format(n_sets))
                    f.write('@type xy\n')
                    for j, freq in enumerate(data.freqs[:, i].magnitude):
                        f.write('{0: .15e} {1: .15e}\n'.format(
                            abscissa[j], freq))
                    n_sets += 1
                if (down and hasattr(data, 'freq_down')
                        and len(data.freq_down) > 0):
                    f.write('@target G0.S{0:d}\n'.format(n_sets))
                    f.write('@type xy\n')
                    for j, freq in enumerate(data.freq_down[:, i].magnitude):
                        f.write('{0: .15e} {1: .15e}\n'.format(
                            abscissa[j], freq))
                    n_sets += 1
                f.write('&\n')

            # Write Fermi level
            if hasattr(data, 'fermi'):
                for i, ef in enumerate(data.fermi.magnitude):
                    f.write('@ G0.S{0:d} line linestyle 3\n'.format(
                        data.n_branches + i))
                    f.write(
                        '@ G0.S{0:d} line color 1\n'.format(data.n_branches +
                                                            i))
                    f.write('@target G0.S{0:d}\n'.format(data.n_branches + i))
                    f.write('@type xy\n')
                    f.write('{0: .15e} {1: .15e}\n'.format(0, ef))
                    f.write('{0: .15e} {1: .15e}\n'.format(abscissa[-1], ef))
                    f.write('&\n')
コード例 #6
0
def plot_sqw_map(data,
                 vmin=None,
                 vmax=None,
                 ratio=None,
                 ewidth=0,
                 qwidth=0,
                 cmap='viridis',
                 title=''):
    """
    Plots an q-E scattering plot using imshow

    Parameters
    ----------
    data : PhononData or InterpolationData object
        Data object for which calculate_sqw_map has been called, containing
        sqw_map and sqw_ebins attributes for plotting
    vmin : float, optional
        Minimum of data range for colormap. See Matplotlib imshow docs
        Default: None
    vmax : float, optional
        Maximum of data range for colormap. See Matplotlib imshow docs
        Default: None
    ratio : float, optional
        Ratio of the size of the y and x axes. e.g. if ratio is 2, the y-axis
        will be twice as long as the x-axis
        Default: None
    ewidth : float, optional, default 0
        The FWHM of the Gaussian energy resolution function in the same units
        as freqs
    qwidth : float, optional, default 0
        The FWHM of the Gaussian q-vector resolution function
    cmap : string, optional, default 'viridis'
        Which colormap to use, see Matplotlib docs
    title : string, optional
        The figure title. Default: ''

    Returns
    -------
    fig : matplotlib.figure.Figure or None
        If matplotlib.pyplot can be imported, is a Figure with a single
        subplot, otherwise is None
    ims : (n_qpts,) 'matplotlib.image.AxesImage' ndarray or None
        If matplotlib.pyplot can be imported, is an array of AxesImage objects,
        one for each q-point, for easier access to some attributes/functions.
        Otherwise is None
    """
    try:
        import matplotlib as mpl
        import matplotlib.pyplot as plt
    except ImportError:
        warnings.warn(('Cannot import Matplotlib to plot S(q,w) (maybe '
                       'Matplotlib is not installed?). To install Euphonic\'s'
                       ' optional Matplotlib dependency, try:\n\npip install'
                       ' euphonic[matplotlib]\n'))
        raise

    ebins = data.sqw_ebins.magnitude
    # Apply broadening
    if ewidth or qwidth:
        qbin_width = np.linalg.norm(
            np.mean(np.absolute(np.diff(data.qpts, axis=0)), axis=0))
        qbins = np.linspace(0, qbin_width * data.n_qpts + qbin_width,
                            data.n_qpts + 1)
        # If no width has been set, make widths small enough to have
        # effectively no broadening
        if not qwidth:
            qwidth = (qbins[1] - qbins[0]) / 10
        if not ewidth:
            ewidth = (ebins[1] - ebins[0]) / 10
        sqw_map = signal.fftconvolve(
            data.sqw_map,
            np.transpose(gaussian_2d(qbins, ebins, qwidth, ewidth)), 'same')
    else:
        sqw_map = data.sqw_map

    # Calculate qbin edges
    cell_vec = (data.cell_vec.to('angstrom').magnitude)
    recip = reciprocal_lattice(cell_vec)
    abscissa = calc_abscissa(data.qpts, recip)
    qmid = (abscissa[1:] + abscissa[:-1]) / 2
    qwidths = qmid + qmid[0]
    qbins = np.concatenate(([0], qwidths, [2 * qwidths[-1] - qwidths[-2]]))

    if ratio:
        ymax = qbins[-1] / ratio
    else:
        ymax = 1.0
    if vmin is None:
        vmin = np.amin(sqw_map)
    if vmax is None:
        vmax = np.amax(sqw_map)

    fig, ax = plt.subplots(1, 1)
    ims = np.empty((data.n_qpts), dtype=mpl.image.AxesImage)
    for i in range(data.n_qpts):
        ims[i] = ax.imshow(np.transpose(sqw_map[i, np.newaxis]),
                           interpolation='none',
                           origin='lower',
                           extent=[qbins[i], qbins[i + 1], 0, ymax],
                           vmin=vmin,
                           vmax=vmax,
                           cmap=cmap)
    ax.set_ylim(0, ymax)
    ax.set_xlim(qbins[0], qbins[-1])

    # Calculate energy tick labels
    ytick_spacing_opts = [100, 50, 20, 10, 5, 2, 1, 0.1]
    min_n_yticks = 5
    ytick_spacing = ytick_spacing_opts[np.argmax(
        (ebins[-1] - ebins[0]) / ytick_spacing_opts > min_n_yticks)]
    ytick_min = np.ceil(ebins[0] / ytick_spacing) * ytick_spacing
    ytick_max = np.ceil(ebins[-1] / ytick_spacing) * ytick_spacing
    ylabels = np.arange(ytick_min, ytick_max, ytick_spacing)
    yticks = (ylabels - ebins[0]) / (ebins[-1] - ebins[0]) * ymax
    ax.set_yticks(yticks)
    ax.set_yticklabels(ylabels)
    units_str = data._e_units
    inverse_unit_index = units_str.find('/')
    if inverse_unit_index > -1:
        units_str = units_str[inverse_unit_index + 1:]
        ax.set_ylabel('Energy (' + units_str + r'$^{-1}$)')
    else:
        ax.set_ylabel('Energy (' + units_str + ')')

    # Calculate q-space ticks and labels
    xlabels, qpts_with_labels = recip_space_labels(data)
    for i, label in enumerate(xlabels):
        if label == 'GAMMA':
            xlabels[i] = r'$\Gamma$'
    if np.all(xlabels == ''):
        xlabels = np.around(data.qpts[qpts_with_labels, :], decimals=2)
    xticks = (qbins[qpts_with_labels] + qbins[qpts_with_labels + 1]) / 2
    # Set high symmetry point x-axis ticks/labels
    ax.set_xticks(xticks)
    ax.xaxis.grid(True, which='major')
    # Convert xlabels to list from Numpy array to avoid elementwise
    # comparison FutureWarning when calling set_xticklabels
    if not isinstance(xlabels, list):
        xlabels = xlabels.tolist()
    # Rotate long tick labels
    if len(max(xlabels, key=len)) >= 11:
        ax.set_xticklabels(xlabels, rotation=90)
    else:
        ax.set_xticklabels(xlabels)

    fig.suptitle(title)

    return fig, ims
コード例 #7
0
def _read_phonon_data(seedname, path):
    """
    Reads data from a .phonon file and returns it in a dictionary

    Parameters
    ----------
    seedname : str
        Seedname of file(s) to read
    path : str
        Path to dir containing the file(s), if in another directory

    Returns
    -------
    data_dict : dict
        A dict with the following keys: 'n_ions', 'n_branches', 'n_qpts'
        'cell_vec', 'recip_vec', 'ion_r', 'ion_type', 'ion_mass', 'qpts',
        'weights', 'freqs', 'eigenvecs', 'split_i', 'split_freqs',
        'split_eigenvecs'
        Meta information: 'seedname', 'path' and 'model'.
    """
    file = os.path.join(path, seedname + '.phonon')
    with open(file, 'r') as f:

        (n_ions, n_branches, n_qpts, cell_vec, ion_r, ion_type,
         ion_mass) = _read_phonon_header(f)

        qpts = np.zeros((n_qpts, 3))
        weights = np.zeros(n_qpts)
        freqs = np.zeros((n_qpts, n_branches))
        ir = np.array([])
        raman = np.array([])
        eigenvecs = np.zeros((n_qpts, n_branches, n_ions, 3),
                             dtype='complex128')
        split_i = np.array([], dtype=np.int32)
        split_freqs = np.empty((0, n_branches))
        split_eigenvecs = np.empty((0, n_branches, n_ions, 3))

        # Need to loop through file using while rather than number of q-points
        # as sometimes points are duplicated
        first_qpt = True
        qpt_line = f.readline()
        prev_qpt_num = -1
        qpt_num_patt = re.compile('q-pt=\s*(\d+)')
        float_patt = re.compile('-?\d+\.\d+')
        while qpt_line:
            qpt_num = int(re.search(qpt_num_patt, qpt_line).group(1)) - 1
            floats = re.findall(float_patt, qpt_line)
            qpts[qpt_num] = [float(x) for x in floats[:3]]
            weights[qpt_num] = float(floats[3])

            freq_lines = [f.readline().split() for i in range(n_branches)]
            tmp = np.array([float(line[1]) for line in freq_lines])
            if qpt_num != prev_qpt_num:
                freqs[qpt_num, :] = tmp
            elif is_gamma(qpts[qpt_num]):
                split_i = np.concatenate((split_i, [qpt_num]))
                split_freqs = np.concatenate((split_freqs, tmp[np.newaxis]))
            ir_index = 2
            raman_index = 3
            if is_gamma(qpts[qpt_num]):
                ir_index += 1
                raman_index += 1
            if len(freq_lines[0]) > ir_index:
                if first_qpt:
                    ir = np.zeros((n_qpts, n_branches))
                ir[qpt_num, :] = [float(line[ir_index]) for line in freq_lines]
            if len(freq_lines[0]) > raman_index:
                if first_qpt:
                    raman = np.zeros((n_qpts, n_branches))
                raman[qpt_num, :] = [
                    float(line[raman_index]) for line in freq_lines
                ]

            [f.readline() for x in range(2)]  # Skip 2 label lines
            lines = np.array(
                [f.readline().split()[2:] for x in range(n_ions * n_branches)],
                dtype=np.float64)
            lines_i = np.column_stack(([
                lines[:, 0] + lines[:, 1] * 1j, lines[:, 2] + lines[:, 3] * 1j,
                lines[:, 4] + lines[:, 5] * 1j
            ]))
            tmp = np.zeros((n_branches, n_ions, 3), dtype=np.complex128)
            for i in range(n_branches):
                tmp[i, :, :] = lines_i[i * n_ions:(i + 1) * n_ions, :]
            if qpt_num != prev_qpt_num:
                eigenvecs[qpt_num] = tmp
            elif is_gamma(qpts[qpt_num]):
                split_eigenvecs = np.concatenate(
                    (split_eigenvecs, tmp[np.newaxis]))
            first_qpt = False
            qpt_line = f.readline()
            prev_qpt_num = qpt_num

    data_dict = {}
    data_dict['n_ions'] = n_ions
    data_dict['n_branches'] = n_branches
    data_dict['n_qpts'] = n_qpts
    data_dict['cell_vec'] = (cell_vec * ureg('angstrom').to('bohr')).magnitude
    data_dict['recip_vec'] = ((reciprocal_lattice(cell_vec) /
                               ureg.angstrom).to('1/bohr')).magnitude
    data_dict['ion_r'] = ion_r
    data_dict['ion_type'] = ion_type
    data_dict['ion_mass'] = ion_mass * (ureg('amu')).to('e_mass').magnitude
    data_dict['qpts'] = qpts
    data_dict['weights'] = weights
    data_dict['freqs'] = ((freqs * (1 / ureg.cm)).to('E_h',
                                                     'spectroscopy')).magnitude
    data_dict['eigenvecs'] = eigenvecs
    data_dict['split_i'] = split_i
    data_dict['split_freqs'] = ((split_freqs * (1 / ureg.cm)).to(
        'E_h', 'spectroscopy')).magnitude
    data_dict['split_eigenvecs'] = split_eigenvecs

    # Meta information
    data_dict['model'] = 'CASTEP'
    data_dict['seedname'] = seedname
    data_dict['path'] = path

    return data_dict
コード例 #8
0
def _read_bands_data(seedname, path):
    """
    Reads data from a .bands file (and a .castep file if available) and
    returns it in a dictionary

    Parameters
    ----------
    seedname : str
        Seedname of file(s) to read
    path : str
        Path to dir containing the file(s), if in another directory

    Returns
    -------
    data_dict : dict
        A dict with the following keys: 'n_qpts', 'n_spins', 'n_branches',
        'fermi', 'cell_vec', 'recip_vec', 'qpts', 'weights', 'freqs',
        'freq_down'. If a .castep file is available to read, the keys 'n_ions',
        'ion_r' and 'ion_type' are also present.
        Meta information: 'seedname', 'path' and 'model'.
    """

    file = os.path.join(path, seedname + '.bands')
    with open(file, 'r') as f:
        n_qpts = int(f.readline().split()[3])
        n_spins = int(f.readline().split()[4])
        f.readline()  # Skip number of electrons line
        n_branches = int(f.readline().split()[3])
        fermi = np.array([float(x) for x in f.readline().split()[5:]])
        f.readline()  # Skip unit cell vectors line
        cell_vec = [[float(x) for x in f.readline().split()[0:3]]
                    for i in range(3)]

        qpts = np.zeros((n_qpts, 3))
        weights = np.zeros(n_qpts)
        freqs_qpt = np.zeros(n_branches)
        freqs = np.zeros((n_qpts, n_branches))
        if n_spins == 2:
            freq_down = np.zeros((n_qpts, n_branches))
        else:
            freq_down = np.array([])

        # Need to loop through file using while rather than number of k-points
        # as sometimes points are duplicated
        line = f.readline().split()
        while line:
            qpt_num = int(line[1]) - 1
            qpts[qpt_num, :] = [float(x) for x in line[2:5]]
            weights[qpt_num] = float(line[5])

            for j in range(n_spins):
                spin = int(f.readline().split()[2])

                # Read frequencies
                for k in range(n_branches):
                    freqs_qpt[k] = float(f.readline())

                if spin == 1:
                    freqs[qpt_num, :] = freqs_qpt
                elif spin == 2:
                    freq_down[qpt_num, :] = freqs_qpt

            line = f.readline().split()

    data_dict = {}
    data_dict['n_qpts'] = n_qpts
    data_dict['n_spins'] = n_spins
    data_dict['n_branches'] = n_branches
    data_dict['fermi'] = fermi
    data_dict['cell_vec'] = cell_vec
    data_dict['recip_vec'] = reciprocal_lattice(cell_vec)
    data_dict['qpts'] = qpts
    data_dict['weights'] = weights
    data_dict['freqs'] = freqs
    data_dict['freq_down'] = freq_down

    # Try to get extra data (ionic species, coords) from .castep file
    try:
        n_ions, ion_r, ion_type = _read_castep_data(seedname, path)
        data_dict['n_ions'] = n_ions
        data_dict['ion_r'] = ion_r
        data_dict['ion_type'] = ion_type
    except IOError:
        pass

    # Meta information
    data_dict['model'] = 'CASTEP'
    data_dict['seedname'] = seedname
    data_dict['path'] = path

    return data_dict
コード例 #9
0
def _read_interpolation_data(seedname, path):
    """
    Reads data from a .castep_bin or .check file and returns it in a dictionary

    Parameters
    ----------
    seedname : str
        Seedname of file(s) to read
    path : str
        Path to dir containing the file(s), if in another directory

    Returns
    -------
    data_dict : dict
        A dict with the following keys: 'n_ions', 'n_branches', 'cell_vec',
        'recip_vec', 'ion_r', 'ion_type', 'ion_mass', 'force_constants',
        'sc_matrix', 'n_cells_in_sc' and 'cell_origins'. Also contains 'born'
        and 'dielectric' if they are present in the .castep_bin or .check file.
        Meta information: 'seedname', 'path' and 'model'.
    """
    file = os.path.join(path, seedname + '.castep_bin')
    if not os.path.isfile(file):
        print(
            '{:s}.castep_bin file not found, trying to read {:s}.check'.format(
                seedname, seedname))
        file = os.path.join(path, seedname + '.check')

    with open(file, 'rb') as f:
        int_type = '>i4'
        float_type = '>f8'
        header = ''
        first_cell_read = True
        while header.strip() != b'END':
            header = _read_entry(f)
            if header.strip() == b'BEGIN_UNIT_CELL':
                # CASTEP writes the cell twice: the first is the geometry
                # optimised cell, the second is the original cell. We only
                # want the geometry optimised cell.
                if first_cell_read:
                    n_ions, cell_vec, ion_r, ion_mass, ion_type = _read_cell(
                        f, int_type, float_type)
                    first_cell_read = False
            elif header.strip() == b'FORCE_CON':
                sc_matrix = np.transpose(
                    np.reshape(_read_entry(f, int_type), (3, 3)))
                n_cells_in_sc = int(
                    np.rint(np.absolute(np.linalg.det(sc_matrix))))
                # Transpose and reshape fc so it is indexed [nc, i, j]
                force_constants = np.ascontiguousarray(
                    np.transpose(
                        np.reshape(_read_entry(f, float_type),
                                   (n_cells_in_sc, 3 * n_ions, 3 * n_ions)),
                        axes=[0, 2, 1]))
                cell_origins = np.reshape(_read_entry(f, int_type),
                                          (n_cells_in_sc, 3))
                fc_row = _read_entry(f, int_type)
            elif header.strip() == b'BORN_CHGS':
                born = np.reshape(_read_entry(f, float_type), (n_ions, 3, 3))
            elif header.strip() == b'DIELECTRIC':
                dielectric = np.transpose(
                    np.reshape(_read_entry(f, float_type), (3, 3)))

    data_dict = {}
    data_dict['n_ions'] = n_ions
    data_dict['n_branches'] = 3 * n_ions
    data_dict['cell_vec'] = cell_vec
    data_dict['recip_vec'] = reciprocal_lattice(cell_vec)
    data_dict['ion_r'] = ion_r - np.floor(ion_r)  # Normalise ion coordinates
    data_dict['ion_type'] = ion_type
    data_dict['ion_mass'] = ion_mass

    # Set entries relating to 'FORCE_CON' block
    try:
        data_dict['force_constants'] = force_constants
        data_dict['sc_matrix'] = sc_matrix
        data_dict['n_cells_in_sc'] = n_cells_in_sc
        data_dict['cell_origins'] = cell_origins
    except NameError:
        raise Exception(
            ('Force constants matrix could not be found in {:s}.\n Ensure '
             'PHONON_WRITE_FORCE_CONSTANTS: true has been set when running '
             'CASTEP').format(file))

    # Set entries relating to dipoles
    try:
        data_dict['born'] = born
        data_dict['dielectric'] = dielectric
    except UnboundLocalError:
        pass

    # Meta information
    data_dict['model'] = 'CASTEP'
    data_dict['seedname'] = seedname
    data_dict['path'] = path

    return data_dict