from itertools import permutations, product
import ase
import numpy as np
[docs]
def write_fcs_gpumd(
fname_fc: str,
fname_clusters: str,
fcs,
order: int,
tol: float = 1e-10,
) -> None:
"""
Writes force constants of given order in GPUMD format.
Parameters
----------
fname_fc
Name of file which contains the lookup force constants.
fname_clusters
Name of file which contains the clusters and the force constant lookup index.
fcs
Force constants in :class:`ForceConstants` format.
order
Force constants for this order will be written to file.
tol
If the norm of a force constant term is less than this value it will be excluded
from the output;
if two force-constants differ by this value or less, they are considered equal.
"""
cluster_lookup, fc_lookup = _get_lookup_data_smart(fcs, order, tol)
_write_clusters(fname_clusters, cluster_lookup, order)
_write_fc_lookup(fname_fc, fc_lookup, order)
def _write_fc_lookup(fname, fc_lookup, order):
""" Writes the lookup force constants to file """
fmt = '{}' + ' {}'*order
with open(fname, 'w') as f:
f.write(str(len(fc_lookup)) + '\n\n')
for fc in fc_lookup:
for xyz in product(range(3), repeat=order):
f.write(fmt.format(*xyz, fc[xyz])+'\n')
f.write('\n')
def _write_clusters(fname, cluster_lookup, order):
""" Writes the cluster lookup to file """
fmt = '{}' + ' {}'*order
with open(fname, 'w') as f:
f.write(str(len(cluster_lookup)) + '\n\n')
for c, i in cluster_lookup.items():
line = fmt.format(*c, i) + '\n'
f.write(line)
def _get_clusters(fcs,
order: int,
tol: float):
""" Collect all relevant clusters; for 2nd and 3rd-order force constants
all permutations are included.
"""
if order in [2, 3]:
clusters = []
for c in fcs._fc_dict.keys():
if len(c) == order and np.linalg.norm(fcs[c]) > tol:
for ci in permutations(c):
clusters.append(ci)
clusters = list(sorted(set(clusters)))
else:
clusters = [c for c in fcs._fc_dict.keys() if len(c) == order and np.linalg.norm(fcs[c]) > tol] # noqa
return clusters
def _get_lookup_data_naive(fcs,
order: int,
tol: float):
""" Groups force constants for a given order into groups for which the
force constant is identical. """
fc_lookup = []
cluster_lookup = dict()
clusters = _get_clusters(fcs, order, tol)
for c in clusters:
fc1 = fcs[c]
if np.linalg.norm(fc1) < tol:
continue
for i, fc2 in enumerate(fc_lookup):
if np.linalg.norm(fc1 - fc2) < tol:
cluster_lookup[c] = i
break
else:
cluster_lookup[c] = len(fc_lookup)
fc_lookup.append(fc1)
return cluster_lookup, fc_lookup
def _get_lookup_data_smart(fcs,
order: int,
tol: float):
""" Groups force constants for a given order into groups for which the
force constant is identical. """
fc_lookup = []
cluster_lookup = dict()
axis = tuple(range(1, order+1))
clusters = _get_clusters(fcs, order, tol)
fc_all = np.array([fcs[c] for c in clusters])
indices = list(range(len(clusters)))
while len(indices) > 0:
i = indices[0]
delta = fc_all[indices] - fc_all[i]
delta_norm = np.sqrt(np.sum(delta**2, axis=axis))
inds_to_del = [indices[x] for x in np.where(delta_norm < tol)[0]]
assert i in inds_to_del
fc_lookup.append(fc_all[i])
for j in inds_to_del:
indices.remove(j)
cluster_lookup[clusters[j]] = len(fc_lookup)-1
return cluster_lookup, fc_lookup
[docs]
def write_fcp_txt(
fname: str,
path: str,
n_types: int,
max_order: int,
heat_current_order: int = 2,
) -> None:
""" Write driver potential file for GPUMD.
Parameters
----------
fname
Output file name.
path
Path to directory with force constant file.
n_types
Number of atom types.
max_order
Maximum order of the force constant potential.
heat_current_order
Heat current order used in thermal conductivity.
Format is a simple file containing the following::
fcp number_of_atom_types
highest_force_order heat_current_order
path_to_force_constant_files
which in practice for a binary system with a sixth order model,
consider third-order heat-currents, would mean::
fcp 2
6 3
/path/to/your/folder
"""
with open(fname, 'w') as f:
f.write('fcp {}\n'.format(n_types))
f.write('{} {}\n'.format(max_order, heat_current_order))
f.write('{}'.format(path.rstrip('/'))) # without a trailing '/'
[docs]
def write_r0(fname: str, atoms: ase.Atoms):
"""
Write the GPUMD r0 file with the reference atomic positions.
Parameters
----------
fname
Name of file to which to write the atomic positions.
atoms
Input structure.
"""
line = '{} {} {}\n'
with open(fname, 'w') as f:
for a in atoms:
f.write(line.format(*a.position))