Coverage for hiphive/input_output/phonopy.py: 92%
104 statements
« prev ^ index » next coverage.py v7.6.8, created at 2024-11-28 11:20 +0000
« prev ^ index » next coverage.py v7.6.8, created at 2024-11-28 11:20 +0000
1"""
2This module provides functions for reading and writing data files
3in phonopy and phono3py formats.
4"""
6import os
7import warnings
8from itertools import product
10import hiphive
11import numpy as np
12from .logging_tools import logger
14with warnings.catch_warnings():
15 warnings.filterwarnings('ignore', category=FutureWarning)
16 import h5py
19logger = logger.getChild('io_phonopy')
22def _filename_to_format(filename: str) -> str:
23 """ Tries to guess the format from the filename """
24 basename = os.path.basename(filename)
26 if basename == 'FORCE_CONSTANTS':
27 return 'text'
29 if '.' in basename:
30 extension = basename.split('.')[-1]
31 if extension == 'hdf5':
32 return 'hdf5'
33 raise ValueError('Could not guess file format')
36def read_phonopy_fc2(filename: str, format: str = None) -> np.ndarray:
37 """Parses a second-order force constant file in phonopy format.
39 Parameters
40 ----------
41 filename : str
42 input file name
43 format : str
44 specify the file-format, if None tries to guess the format from filename;
45 possible values: "text", "hdf5"
47 Returns
48 -------
49 numpy.ndarray
50 second-order force constant matrix (`N,N,3,3` array)
51 where `N` is the number of atoms
52 """
53 fc2_readers = {
54 'text': _read_phonopy_fc2_text,
55 'hdf5': _read_phonopy_fc2_hdf5
56 }
58 if format is None:
59 format = _filename_to_format(filename)
60 if format not in fc2_readers.keys():
61 raise ValueError('Did not recognize format {}'.format(format))
62 return fc2_readers[format](filename)
65def write_phonopy_fc2(filename: str,
66 fc2,
67 format: str = None) -> None:
68 """Writes second-order force constant matrix in phonopy format.
70 Parameters
71 ----------
72 filename : str
73 output file name
74 fc2 : ForceConstants or numpy.ndarray
75 second-order force constant matrix
76 format : str
77 specify the file-format, if None try to guess the format from filename;
78 possible values: "text", "hdf5"
79 """
81 fc2_writers = {
82 'text': _write_phonopy_fc2_text,
83 'hdf5': _write_phonopy_fc2_hdf5
84 }
86 # get fc2_array
87 if isinstance(fc2, hiphive.ForceConstants):
88 fc2_array = fc2.get_fc_array(order=2)
89 elif isinstance(fc2, np.ndarray):
90 fc2_array = fc2
91 else:
92 raise TypeError('fc2 should be ForceConstants or NumPy array')
94 # check that fc2 has correct shape
95 n_atoms = fc2_array.shape[0]
96 if fc2_array.shape != (n_atoms, n_atoms, 3, 3):
97 raise ValueError('fc2 has wrong shape')
99 # write
100 if format is None:
101 format = _filename_to_format(filename)
102 if format not in fc2_writers.keys():
103 raise ValueError('Did not recognize format {}'.format(format))
104 fc2_writers[format](filename, fc2_array)
107def read_phonopy_fc3(filename: str) -> np.ndarray:
108 """Parses a third order force constant file in phonopy hdf5 format.
110 Parameters
111 ----------
112 filename : str
113 input file name
115 Returns
116 -------
117 numpy.ndarray
118 third order force constant matrix
119 """
120 with h5py.File(filename, 'r') as hf:
121 if 'fc3' in hf.keys(): 121 ↛ 124line 121 didn't jump to line 124 because the condition on line 121 was always true
122 fc3 = hf['fc3'][:]
123 else:
124 raise IOError('Could not find fc3 in file {}'.format(filename))
125 return fc3
128def write_phonopy_fc3(filename: str, fc3) -> None:
129 """Writes third order force constant matrix in phonopy hdf5 format.
131 Parameters
132 ----------
133 filename : str
134 output file name
135 fc3 : ForceConstants or numpy.ndarray
136 third order force constant matrix
137 """
139 if isinstance(fc3, hiphive.ForceConstants):
140 fc3_array = fc3.get_fc_array(order=3)
141 elif isinstance(fc3, np.ndarray):
142 fc3_array = fc3
143 else:
144 raise TypeError('fc3 should be ForceConstants or NumPy array')
146 # check that fc3 has correct shape
147 n_atoms = fc3_array.shape[0]
148 if fc3_array.shape != (n_atoms, n_atoms, n_atoms, 3, 3, 3):
149 raise ValueError('fc3 has wrong shape')
151 with h5py.File(filename, 'w') as hf:
152 hf.create_dataset('fc3', data=fc3_array, compression='gzip')
153 hf.flush()
156def _read_phonopy_fc2_text(filename: str) -> np.ndarray:
157 """ Reads phonopy-fc2 file in text format. """
158 with open(filename, 'r') as f:
160 # read shape of force constants
161 line = f.readline()
162 line_ints = [int(x) for x in line.split()]
163 if len(line_ints) == 1: 163 ↛ 164line 163 didn't jump to line 164 because the condition on line 163 was never true
164 n_atoms = line_ints[0]
165 elif len(line_ints) == 2: 165 ↛ 169line 165 didn't jump to line 169 because the condition on line 165 was always true
166 assert line_ints[0] == line_ints[1]
167 n_atoms = line_ints[0]
168 else:
169 raise ValueError('Unsupported or incorrect phonopy format')
170 fc2 = np.full((n_atoms, n_atoms, 3, 3), np.nan)
172 # read force constants
173 lines = f.readlines()
174 for n, line in enumerate(lines):
175 flds = line.split()
176 if len(flds) == 2:
177 i = int(flds[0]) - 1 # phonopy index starts with 1
178 j = int(flds[1]) - 1
179 for x in range(3):
180 fc_row = lines[n + x + 1].split()
181 for y in range(3):
182 fc2[i][j][x][y] = float(fc_row[y])
183 return fc2
186def _read_phonopy_fc2_hdf5(filename: str) -> np.ndarray:
187 """ Reads phonopy-fc2 file in hdf5 format. """
188 with h5py.File(filename, 'r') as hf:
189 if 'force_constants' in hf.keys(): 189 ↛ 190line 189 didn't jump to line 190 because the condition on line 189 was never true
190 fc2 = hf['force_constants'][:]
191 elif 'fc2' in hf.keys(): 191 ↛ 194line 191 didn't jump to line 194 because the condition on line 191 was always true
192 fc2 = hf['fc2'][:]
193 else:
194 raise IOError('Could not find fc2 in file {}'.format(filename))
195 return fc2
198def _write_phonopy_fc2_text(filename: str, fc2: np.ndarray) -> None:
199 """ Writes second-order force constants to file in text format. """
200 n_atoms = fc2.shape[0]
201 with open(filename, 'w') as f:
202 f.write('{:5d} {:5d}\n'.format(n_atoms, n_atoms))
203 for i, j in product(range(n_atoms), repeat=2):
204 fc2_ij = fc2[(i, j)]
205 if fc2_ij.shape != (3, 3): 205 ↛ 206line 205 didn't jump to line 206 because the condition on line 205 was never true
206 raise ValueError('Invalid shape for fc2[({},{})]'.format(i, j))
207 f.write('{:-5d}{:5d}\n'.format(i + 1, j + 1))
208 for row in fc2_ij:
209 f.write((3*' {:22.15f}'+'\n').format(*tuple(row)))
212def _write_phonopy_fc2_hdf5(filename: str, fc2: np.ndarray) -> None:
213 """ Writes second-order force constants to file in hdf5 format. """
214 with h5py.File(filename, 'w') as hf:
215 hf.create_dataset('fc2', data=fc2, compression='gzip')
216 hf.flush()