11# -*- coding: utf-8 -*-
22from __future__ import print_function
33import sys , os
4+ import warnings
45import numpy as np
56import time
67
3334
3435import sscha .Parallel as Parallel
3536from sscha .Parallel import pprint as print
37+ from sscha .Tools import NumpyEncoder
38+
39+ import json
3640
3741import difflib
3842
9599UNITS_DEFAULT = "default"
96100UNITS_HARTREE = "hartree"
97101SUPPORTED_UNITS = [UNITS_DEFAULT , UNITS_HARTREE ]
98-
102+ class NumpyEncoder (json .JSONEncoder ):
103+ def default (self , obj ):
104+ if isinstance (obj , np .ndarray ):
105+ return obj .tolist ()
106+ return json .JSONEncoder .default (self , obj )
99107
100108class Ensemble :
101109 __debug_index__ = 0
@@ -179,7 +187,7 @@ def __init__(self, dyn0, T0, supercell = None, **kwargs):
179187 self .stress_computed = None
180188
181189 # Get the extra quantities
182- self .extra_quantities = {}
190+ self .all_properties = []
183191
184192
185193 # Setup the attribute control
@@ -610,7 +618,7 @@ def load_from_calculator_output(self, directory, out_ext = ".pwo"):
610618
611619 # Get the stress if any
612620 try :
613- stress = ase_struct .get_stress (voigt = False )
621+ stress = - ase_struct .get_stress (voigt = False )
614622 # eV/A^3 -> Ry/bohr^3
615623 stress /= Rydberg / Bohr ** 3
616624 count_stress += 1
@@ -762,6 +770,10 @@ def save_bin(self, data_dir, population_id = 1):
762770 np .save ("%s/stresses_pop%d.npy" % (data_dir , population_id ), self .stresses )
763771
764772 self .dyn_0 .save_qe ("%s/dyn_gen_pop%d_" % (data_dir , population_id ))
773+
774+ if len (self .all_properties ):
775+ with open (os .path .join (data_dir , "all_properties_pop%d.json" % population_id ), "w" ) as fp :
776+ json .dump ({"properties" : self .all_properties }, fp , cls = NumpyEncoder )
765777
766778 def save_enhanced_xyz (self , filename , append_mode = True , stress_key = "virial" , forces_key = "force" , energy_key = "energy" ):
767779 """
@@ -861,7 +873,8 @@ def save_raw(self, root_directory, type_dict = None):
861873 np .savetxt (os .path .join (root_directory , "coord.raw" ), self .xats .reshape ((self .N , 3 * nat )))
862874
863875 # Save the box
864- np .savetxt (os .path .join (root_directory , "box.raw" ), np .tile (self .current_dyn .structure .unit_cell .ravel (), (self .N , 1 )))
876+ #Prepare an array with all the unit cells
877+ np .savetxt (os .path .join (root_directory , "box.raw" ), np .array ([s .unit_cell .ravel () for s in self .structures ]))
865878
866879 # Save the forces
867880 np .savetxt (os .path .join (root_directory , "force.raw" ), self .forces .reshape ((self .N , 3 * nat )) * Rydberg )
@@ -949,6 +962,16 @@ def load_bin(self, data_dir, population_id = 1, avoid_loading_dyn = False):
949962 # Setup that both forces and stresses are not computed
950963 self .stress_computed = np .ones (self .N , dtype = bool )
951964 self .force_computed = np .ones (self .N , dtype = bool )
965+
966+ all_prop_fname = os .path .join (data_dir , "all_properties_pop%d.json" % population_id )
967+ if os .path .exists (all_prop_fname ):
968+ with open (os .path .join (data_dir , "all_properties_pop%d.json" % population_id ), "w" ) as fp :
969+ props = json .load (fp )
970+ if "properties" in props :
971+ self .all_properties = props ["properties" ]
972+ else :
973+ warnings .warn ("WARNING: found file {} but not able to load the properties keyword." .format (all_prop_fname ))
974+
952975
953976
954977 def init_from_structures (self , structures ):
@@ -1616,6 +1639,39 @@ def get_free_energy(self, return_error = False):
16161639 return free_energy , error
16171640 return free_energy
16181641
1642+ def get_entropy (self , return_error = False ):
1643+ r"""
1644+ GET THE ENTROPY
1645+ ===============
1646+
1647+ Get the total anharmonic entropy.
1648+
1649+ The equation implemented is the analytical derivative of the free energy,
1650+ and assumes that the SSCHA free energy is minimized.
1651+
1652+ .. math::
1653+
1654+ S = - \frac{dF}{dT}
1655+
1656+ S = S_{harm} - \left<V - {\mathcal V}\right>\sum_\mu \frac{1}{1 + 2n_\mu} \frac{dn_\mu}{dT}
1657+
1658+
1659+ where :math:`S_{harm}` is the 'harmonic' entropy computed from the dynamucal matrix,
1660+ plus a correction accounting for the ensemble anharmonicity.
1661+
1662+
1663+ Parameters
1664+ ----------
1665+ return_error : bool
1666+ If true, returns also the error
1667+
1668+ Results
1669+ -------
1670+ entropy, error : float
1671+ Returns the entropy and [optionally] the stochastic error.
1672+ """
1673+ raise NotImplementedError ("Error, to be implemented" )
1674+
16191675
16201676 def get_free_energy_interpolating (self , target_supercell , support_dyn_coarse = None , support_dyn_fine = None , error_on_imaginary_frequency = True , return_error = False ):
16211677 """
@@ -1625,6 +1681,9 @@ def get_free_energy_interpolating(self, target_supercell, support_dyn_coarse = N
16251681 This is a trick to interpolate the free energy in the
16261682 infinite volume limit.
16271683
1684+ Note, this function report the free eenrgy in the primitive cell, while the method get_free_energy
1685+ returns the energy in the supercell.
1686+
16281687 Parameters
16291688 ----------
16301689 target_supercell : list (N, N, N)
@@ -1659,12 +1718,14 @@ def get_free_energy_interpolating(self, target_supercell, support_dyn_coarse = N
16591718
16601719
16611720 # Interpolate the dynamical matrix
1662- new_dyn = self .current_dyn .Interpolate ( self .current_dyn .GetSupercell (),
1663- target_supercell ,
1664- support_dyn_coarse ,
1665- support_dyn_fine )
1721+ if support_dyn_fine is not None :
1722+ new_dyn = self .current_dyn .Interpolate ( self .current_dyn .GetSupercell (),
1723+ target_supercell ,
1724+ support_dyn_coarse ,
1725+ support_dyn_fine )
1726+ else :
1727+ new_dyn = self .current_dyn .InterpolateMesh (target_supercell )
16661728
1667- # TODO: Allow double interpolation in case of support dyn
16681729
16691730 # Get the new harmonic free energy
16701731 harm_fe = new_dyn .GetHarmonicFreeEnergy (self .current_T ,
@@ -1863,10 +1924,14 @@ def get_preconditioned_gradient(self, subtract_sscha = True, return_error = Fals
18631924 if np .prod (self .dyn_0 .GetSupercell ()) > 1 :
18641925
18651926 # Perform the fourier transform
1866- q_grad = CC .Phonons .GetDynQFromFCSupercell (grad , np .array (self .current_dyn .q_tot ),
1927+ if return_error :
1928+ q_grad ,q_grad_err = CC .Phonons .GetDynQFromFCSupercell (grad , np .array (self .current_dyn .q_tot ),
1929+ self .current_dyn .structure , supercell_dyn .structure ,fc2 = grad_err )
1930+ else :
1931+ q_grad = CC .Phonons .GetDynQFromFCSupercell (grad , np .array (self .current_dyn .q_tot ),
18671932 self .current_dyn .structure , supercell_dyn .structure )
1868- q_grad_err = CC .Phonons .GetDynQFromFCSupercell (grad_err , np .array (self .current_dyn .q_tot ),
1869- self .current_dyn .structure , supercell_dyn .structure )
1933+ # q_grad_err = CC.Phonons.GetDynQFromFCSupercell(grad_err, np.array(self.current_dyn.q_tot),
1934+ # self.current_dyn.structure, supercell_dyn.structure)
18701935 else :
18711936 nat3 , _ = grad .shape
18721937 q_grad = np .zeros ( (1 , nat3 , nat3 ), dtype = np .double )
@@ -2671,7 +2736,7 @@ def get_dynamical_bubble(self, q, w, smearing = 1e-5):
26712736
26722737
26732738 def get_free_energy_hessian (self , include_v4 = False , get_full_hessian = True , verbose = False , \
2674- use_symmetries = True ):
2739+ use_symmetries = True , return_d3 = False ):
26752740 """
26762741 GET THE FREE ENERGY ODD CORRECTION
26772742 ==================================
@@ -2696,11 +2761,16 @@ def get_free_energy_hessian(self, include_v4 = False, get_full_hessian = True, v
26962761 use_symmetries : bool
26972762 If true, the d3 and d4 are symmetrized in real space.
26982763 It requires that spglib is installed to detect symmetries in the supercell correctly.
2764+ return_d3 : bool
2765+ If true, returns also the tensor of three phonon scattering.
26992766
27002767 Returns
27012768 -------
27022769 phi_sc : Phonons()
27032770 The dynamical matrix of the free energy hessian in (Ry/bohr^2)
2771+ d3 : ndarray (size = (3*nat_sc, 3*nat_sc, 3*nat_sc), Optional
2772+ Return the three-phonon-scattering tensor (in Ry atomic units).
2773+ Only if return_d3 is True.
27042774 """
27052775 # For now the v4 is not implemented
27062776 # if include_v4:
@@ -2836,6 +2906,8 @@ def get_free_energy_hessian(self, include_v4 = False, get_full_hessian = True, v
28362906 dyn_hessian .dynmats [iq ] = dynq_odd [iq , :, :]
28372907
28382908
2909+ if return_d3 :
2910+ return dyn_hessian , d3 * 2.0 # Ha to Ry
28392911 return dyn_hessian
28402912
28412913
0 commit comments