Source code for hiphive.input_output.shengBTE

"""
The ``io.shengBTE`` module provides functions for reading and writing data
files in shengBTE format.
"""

import numpy as np
from itertools import product
from collections import namedtuple
from ..core.structures import Supercell
import ase
import hiphive


[docs] def read_shengBTE_fc3( filename: str, prim: ase.Atoms, supercell: ase. Atoms, symprec: float = 1e-5, ): """Reads third-order force constants file in shengBTE format. Parameters ---------- filename Input file name. prim Primitive structure for the force constants. supercell Supercell supercell onto which to map the force constants. symprec Structural symmetry tolerance. Returns ------- Third-order force constants for the specified supercell as :class:`ForceConstants` object. """ raw_sheng = _read_raw_sheng(filename) sheng = _raw_to_fancy(raw_sheng, prim.cell) fcs = _sheng_to_fcs(sheng, prim, supercell, symprec) return fcs
[docs] def write_shengBTE_fc3( filename: str, fcs, prim: ase.Atoms, symprec: float = 1e-5, cutoff: float = np.inf, fc_tol: float = 1e-8, ): """Writes third-order force constants to file in shengBTE format. Parameters ----------- filename Output file name. fcs Force constants in :class:`ForceConstants` format; the supercell associated with this object must be based on :attr:`prim`. prim Primitive structure (must be equivalent to structure used in the shengBTE calculation). symprec Structural symmetry tolerance. cutoff All atoms in cluster must be within this cutoff. fc_tol If the absolute value of the largest entry in a force constant is less than :attr:`fc_tol` it will not be included in the output. """ sheng = _fcs_to_sheng(fcs, prim, symprec, cutoff, fc_tol) raw_sheng = _fancy_to_raw(sheng) _write_raw_sheng(raw_sheng, filename)
def _read_raw_sheng(filename: str) -> list: """ Read shengBTE fc3 file. Parameters ---------- filename Input file name. Returns ------- list with shengBTE block entries, where an entry consist of `[i, j, k, cell_pos2, cell_pos3, fc3]`. """ lines_per_fc_block = 32 fc3_data_shengBTE = [] with open(filename, 'r') as f: lines = f.readlines() num_fcs = int(lines[0]) lind = 2 for i in range(1, num_fcs+1): # sanity check assert int(lines[lind]) == i, (int(lines[lind]), i) # read cell poses cell_pos2 = np.array([float(fld) for fld in lines[lind+1].split()]) cell_pos3 = np.array([float(fld) for fld in lines[lind+2].split()]) # read basis indices i, j, k = (int(fld) for fld in lines[lind+3].split()) # read fc fc3_ijk = np.zeros((3, 3, 3)) for n in range(27): x, y, z = [int(fld) - 1 for fld in lines[lind+4+n].split()[:3]] fc3_ijk[x, y, z] = float(lines[lind+4+n].split()[-1]) # append entry entry = [i, j, k, cell_pos2, cell_pos3, fc3_ijk] fc3_data_shengBTE.append(entry) lind += lines_per_fc_block return fc3_data_shengBTE _ShengEntry = namedtuple('Entry', ['site_0', 'site_1', 'site_2', 'pos_1', 'pos_2', 'fc', 'offset_1', 'offset_2']) def _raw_to_fancy(raw_sheng, cell): """ Converts force constants as read from shengBTE file to the namedtuple format defined above (_ShengEntry). """ sheng = [] for raw_entry in raw_sheng: p1, p2 = raw_entry[3:5] offset_1 = np.linalg.solve(cell.T, p1).round(0).astype(int) offset_2 = np.linalg.solve(cell.T, p2).round(0).astype(int) entry = _ShengEntry(*(i - 1 for i in raw_entry[:3]), *raw_entry[3:], offset_1, offset_2) sheng.append(entry) return sheng def _fancy_to_raw(sheng): """ Converts force constants namedtuple format defined above (_ShengEntry) to format used for writing shengBTE files. """ raw_sheng = [] for entry in sheng: raw_entry = list(entry[:6]) raw_entry[0] += 1 raw_entry[1] += 1 raw_entry[2] += 1 raw_sheng.append(raw_entry) return raw_sheng def _write_raw_sheng(raw_sheng, filename): """ See corresponding read function. """ with open(filename, 'w') as f: f.write('{}\n\n'.format(len(raw_sheng))) for index, fc3_row in enumerate(raw_sheng, start=1): i, j, k, cell_pos2, cell_pos3, fc3_ijk = fc3_row f.write('{:5d}\n'.format(index)) f.write((3*'{:14.10f} '+'\n').format(*cell_pos2)) f.write((3*'{:14.10f} '+'\n').format(*cell_pos3)) f.write((3*'{:5d}'+'\n').format(i, j, k)) for x, y, z in product(range(3), repeat=3): f.write((3*' {:}').format(x+1, y+1, z+1)) f.write(' {:14.10f}\n'.format(fc3_ijk[x, y, z])) f.write('\n') def _fcs_to_sheng(fcs, prim, symprec, cutoff, fc_tol): """ Produces a list containing fcs in shengBTE format prim and fcs.supercell must be aligned. """ supercell = Supercell(fcs.supercell, prim, symprec) assert all(fcs.supercell.pbc) and all(prim.pbc) n_atoms = len(supercell) D = fcs.supercell.get_all_distances(mic=False, vector=True) D_mic = fcs.supercell.get_all_distances(mic=True, vector=True) M = np.eye(n_atoms, dtype=bool) for i in range(n_atoms): for j in range(i + 1, n_atoms): M[i, j] = (np.allclose(D[i, j], D_mic[i, j], atol=symprec, rtol=0) and np.linalg.norm(D[i, j]) < cutoff) M[j, i] = M[i, j] data = {} for a0 in supercell: for a1 in supercell: if not M[a0.index, a1.index]: continue for a2 in supercell: if not (M[a0.index, a2.index] and M[a1.index, a2.index]): continue offset_1 = np.subtract(a1.offset, a0.offset) offset_2 = np.subtract(a2.offset, a0.offset) sites = (a0.site, a1.site, a2.site) key = sites + tuple(offset_1) + tuple(offset_2) ijk = (a0.index, a1.index, a2.index) fc = fcs[ijk] if key in data: assert np.allclose(data[key], fc, atol=fc_tol) else: data[key] = fc sheng = [] for k, fc in data.items(): if np.max(np.abs(fc)) < fc_tol: continue offset_1 = k[3:6] pos_1 = np.dot(offset_1, prim.cell) offset_2 = k[6:9] pos_2 = np.dot(offset_2, prim.cell) entry = _ShengEntry(*k[:3], pos_1, pos_2, fc, offset_1, offset_2) sheng.append(entry) return sheng def _sheng_to_fcs(sheng, prim, supercell, symprec): supercell_map = Supercell(supercell, prim, symprec) fc_array = np.zeros((len(supercell),) * 3 + (3,) * 3) mapped_clusters = np.zeros((len(supercell),) * 3, dtype=bool) for atom in supercell_map: i = atom.index for entry in sheng: if atom.site != entry.site_0: continue j = supercell_map.index(entry.site_1, entry.offset_1 + atom.offset) k = supercell_map.index(entry.site_2, entry.offset_2 + atom.offset) ijk = (i, j, k) if mapped_clusters[ijk]: raise Exception('Ambiguous force constant.' ' Supercell is too small') fc_array[ijk] = entry.fc mapped_clusters[ijk] = True fcs = hiphive.force_constants.ForceConstants.from_arrays( supercell, fc3_array=fc_array) return fcs