Source code for mpinterfaces.measurement

# coding: utf-8
# Copyright (c) Henniggroup.
# Distributed under the terms of the MIT License.

from __future__ import division, print_function, unicode_literals, \
    absolute_import

"""
Defines measurement jobs
"""

from six.moves import zip

import sys
import shutil
import os
import json
import itertools

from pymatgen.io.vasp.inputs import Incar, Poscar
from pymatgen.io.vasp.inputs import Potcar, Kpoints

from mpinterfaces.calibrate import CalibrateMolecule
from mpinterfaces.calibrate import CalibrateSlab
from mpinterfaces.calibrate import CalibrateInterface
from mpinterfaces.interface import Interface
from mpinterfaces.default_logger import get_default_logger

__author__ = "Kiran Mathew, Joshua J. Gabriel"
__copyright__ = "Copyright 2017, Henniggroup"
__maintainer__ = "Joshua J. Gabriel"
__email__ = "joshgabriel92@gmail.com"
__status__ = "Production"
__date__ = "March 3, 2017"

logger = get_default_logger(__name__)


[docs]class Measurement(object): """ Takes in calibrate objects and use that to perform various measurement calculations such as solvation, ligand binding energy etc. The default behaviour is to setup and run static calculations for all the given calibrate jobs. Serves as Base Class. Override this class for custom measurements. Args: cal_objs: List of Calibration Object Names parent_job_dir: Directory in which measuremnt is setup job_dir: Path name to directory for running the Measurement modules """ def __init__(self, cal_objs, parent_job_dir='.', job_dir='./Measurement'): self.jobs = [] self.handlers = [] self.calmol = [] self.calslab = [] self.calbulk = [] self.cal_objs = cal_objs self.job_dir = job_dir for obj in cal_objs: obj.old_jobs = obj.jobs obj.jobs = [] obj.old_job_dir_list = obj.job_dir_list obj.job_dir_list = []
[docs] def setup(self): """ setup static jobs for all the calibrate objects copies CONTCAR to POSCAR sets NSW = 0 """ for cal in self.cal_objs: for i, jdir in enumerate(cal.old_job_dir_list): job_dir = self.job_dir + os.sep \ + jdir.replace(os.sep, '_').replace('.', '_') \ + os.sep + 'STATIC' logger.info('setting up job in {}'.format(job_dir)) cal.incar = Incar.from_file(jdir + os.sep + 'INCAR') cal.incar['EDIFF'] = '1E-6' cal.incar['NSW'] = 0 cal.potcar = Potcar.from_file(jdir + os.sep + 'POTCAR') cal.kpoints = Kpoints.from_file(jdir + os.sep + 'KPOINTS') contcar_file = jdir + os.sep + 'CONTCAR' if os.path.isfile(contcar_file): logger.info('setting poscar file from {}' .format(contcar_file)) cal.poscar = Poscar.from_file(contcar_file) cal.add_job(job_dir=job_dir) else: logger.critical("""CONTCAR doesnt exist. Setting up job using input set in the old calibration directory""") cal.poscar = Poscar.from_file(jdir + os.sep + 'POSCAR') cal.add_job(job_dir=job_dir)
[docs] def run(self, job_cmd=None): """ run jobs """ for cal in self.cal_objs: cal.run(job_cmd=job_cmd)
[docs] def get_energy(self, cal): """ measures the energy of a single cal object a single cal object can have multiple calculations returns energies lists """
pass # energies = [] # for job_dir in cal.job_dir_list: # drone = MPINTVaspDrone(inc_structure=True, # inc_incar_n_kpoints=False) # bg = BorgQueen(drone) # # bg.parallel_assimilate(rootpath) # bg.serial_assimilate(job_dir) # allentries = bg.get_data() # for e in allentries: # if e: # energies.append(e.energy) # logger.debug('energy from directory {0} : {1}' # .format(job_dir, e.energy)) # return energies
[docs] def make_measurements(self): """ gets the energies and processes it override this for custom measurements """ energies = [] for cal in self.cal_objs: energies.append(self.get_energy(cal))
[docs]class MeasurementSolvation(Measurement): """ Solvation with poisson-boltzmann(test verison) """ def __init__(self, cal_obj, parent_job_dir='.', job_dir='./MeasurementSolvation', sol_params=None): Measurement.__init__(self, cal_objs=cal_obj, parent_job_dir=parent_job_dir, job_dir=job_dir) self.sol_params = sol_params or {'EB_K': [78.4], 'TAU': [0], 'LAMBDA_D_K': [3.0], 'NELECT': []}
[docs] def setup(self): """ setup solvation jobs for the calibrate objects copies WAVECAR and sets the solvation params in the incar file also dumps system.json file in each directory for the database crawler mind: works only for cal objects that does only single calculations """ for cal in self.cal_objs: jdir = cal.old_job_dir_list[0] cal.poscar = Poscar.from_file(jdir + os.sep + 'POSCAR') cal.potcar = Potcar.from_file(jdir + os.sep + 'POTCAR') cal.kpoints = Kpoints.from_file(jdir + os.sep + 'KPOINTS') cal.incar = Incar.from_file(jdir + os.sep + 'INCAR') cal.incar['LSOL'] = '.TRUE.' syms = [site.specie.symbol for site in cal.poscar.structure] zvals = {p.symbol: p.nelectrons for p in cal.potcar} nelectrons = sum([zvals[a[0]] * len(tuple(a[1])) for a in itertools.groupby(syms)]) keys = [k for k in self.sol_params.keys() if self.sol_params[k]] prod_list = [self.sol_params.get(k) for k in keys] for params in itertools.product(*tuple(prod_list)): job_dir = self.job_dir + os.sep \ + cal.old_job_dir_list[0].replace(os.sep, '_').replace('.', '_') \ + os.sep + 'SOL' for i, k in enumerate(keys): if k == 'NELECT': cal.incar[k] = params[i] + nelectrons else: cal.incar[k] = params[i] job_dir = job_dir + os.sep + k + os.sep + str( cal.incar[k]).replace('.', '_') if not os.path.exists(job_dir): os.makedirs(job_dir) with open(job_dir + os.sep + 'system.json', 'w') as f: json.dump(dict(list(zip(keys, params))), f) wavecar_file = cal.old_job_dir_list[0] + os.sep + 'WAVECAR' if os.path.isfile(wavecar_file): shutil.copy(wavecar_file, job_dir + os.sep + 'WAVECAR') cal.add_job(job_dir=job_dir) else: logger.critical('WAVECAR doesnt exist. Aborting ...') sys.exit(0)
[docs] def make_measurements(self): """ get solvation energies """ pass
[docs]class MeasurementInterface(Measurement): """ Interface Takes list of Calibration Objects of Interface, Slab and Ligand and separates them Args: cal_objs: List of Calibration Objects """ def __init__(self, cal_objs, parent_job_dir='.', job_dir='./MeasurementInterface'): Measurement.__init__(self, cal_objs=cal_objs, parent_job_dir=parent_job_dir, job_dir=job_dir) self.cal_slabs = [] self.cal_interfaces = [] self.cal_ligands = [] for cal in self.cal_objs: if isinstance(cal, CalibrateSlab): self.cal_slabs.append(cal) elif isinstance(cal, CalibrateInterface): self.cal_interfaces.append(cal) elif isinstance(cal, CalibrateMolecule): self.cal_ligands.append(cal)
[docs] def setup(self): """ setup static jobs for the calibrate objects copies CONTCAR to POSCAR sets NSW = 0 write system.json file for database crawler """ d = {} for cal in self.cal_objs: for i, jdir in enumerate(cal.old_job_dir_list): job_dir = self.job_dir + os.sep \ + jdir.replace(os.sep, '_').replace('.', '_') + \ os.sep + 'STATIC' cal.incar = Incar.from_file(jdir + os.sep + 'INCAR') cal.incar['EDIFF'] = '1E-6' cal.incar['NSW'] = 0 cal.potcar = Potcar.from_file(jdir + os.sep + 'POTCAR') cal.kpoints = Kpoints.from_file(jdir + os.sep + 'KPOINTS') contcar_file = jdir + os.sep + 'CONTCAR' if os.path.isfile(contcar_file): cal.poscar = Poscar.from_file(contcar_file) if cal in self.cal_slabs or cal in self.cal_interfaces: try: d['hkl'] = cal.system['hkl'] except: logger.critical("""the calibrate object doesnt have a system set for calibrating""") if cal in self.cal_interfaces: try: d['ligand'] = cal.system['ligand']['name'] except: logger.critical("""the calibrate object doesnt have a system set for calibrating""") if not os.path.exists(job_dir): os.makedirs(job_dir) if d: with open(job_dir + os.sep + 'system.json', 'w') as f: json.dump(d, f) cal.add_job(job_dir=job_dir) else: logger.critical("""CONTCAR doesnt exist. Setting up job using input set in the old calibration directory""") cal.poscar = Poscar.from_file(jdir + os.sep + 'POSCAR') cal.add_job(job_dir=job_dir)
[docs] def make_measurements(self): """ get the slab, ligand and interface energies compute binding energies """ E_interfaces = {} E_slabs = {} E_ligands = {} E_binding = {} for cal in self.cal_slabs: key = str(cal.system['hkl']) E_slabs[key] = self.get_energy(cal) for cal in self.cal_ligands: key = cal.system['ligand']['name'] E_ligands[key] = self.get_energy(cal) for cal in self.cal_interfaces: key_slab = str(cal.system['hkl']) key_ligand = cal.system['ligand']['name'] key = key_slab + key_ligand E_interfaces[key] = self.get_energy(cal) E_binding[key] = E_interfaces[key] \ - E_slabs[key_slab] \ - cal.system['num_ligands'] * E_ligands[ key_ligand] logger.info('Binding energy = {}'.format(E_binding))
# test if __name__ == '__main__': from pymatgen.core.structure import Structure, Molecule from mpinterfaces.interface import Ligand # PbS 100 surface with single hydrazine as ligand strt = Structure.from_file("POSCAR.mp-21276_PbS") mol_struct = Structure.from_file("POSCAR_diacetate") mol = Molecule(mol_struct.species, mol_struct.cart_coords) hydrazine = Ligand([mol]) supercell = [1, 1, 1] hkl = [1, 1, 1] min_thick = 10 min_vac = 12 surface_coverage = 0.01 adsorb_on_species = 'S' adatom_on_lig = 'Pb' displacement = 3.0 iface = Interface(strt, hkl=hkl, min_thick=min_thick, min_vac=min_vac, supercell=supercell, surface_coverage=0.01, ligand=hydrazine, displacement=displacement, adatom_on_lig=adatom_on_lig, adsorb_on_species=adsorb_on_species, primitive=False, coverage_tol=0.5) iface.create_interface() iface.sort() incarparams = {'System': 'test', 'ENCUT': 400, 'ISMEAR': 1, 'SIGMA': 0.1, 'EDIFF': 1E-6} incar = Incar(params=incarparams) poscar = Poscar(iface) potcar = Potcar(symbols=poscar.site_symbols, functional='PBE', sym_potcar_map=None) kpoints = Kpoints.monkhorst_automatic(kpts=(16, 16, 16), shift=(0, 0, 0)) cal = CalibrateInterface(incar, poscar, potcar, kpoints, system=iface.as_dict(), job_dir='test', job_cmd=['ls', '-lt']) cal.setup() cal.run() # list of calibrate objects cal_objs = [cal] # check whether the cal jobs were done # Calibrate.check_calcs(cal_objs) # set the measurement # measure = MeasurementInterface(cal_objs, job_dir='./Measurements') measure = MeasurementSolvation(cal_objs, job_dir='./MSR_SOL', sol_params={'EB_K': [78.4], 'TAU': [0], 'LAMBDA_D_K': [3.0], 'NELECT': [1, -1]}) measure.setup() measure.run()