<file_sep>__author__ = '<NAME>, <NAME>'
__copyright__ = 'Approved for public release: AFRL DSRC Case Number 88ABW-2016-0260'
__version__ = '0.5'
__maintainer__ = '<NAME>'
__email__ = '<EMAIL>; <EMAIL>'
__date__ = '9/14/15'

import os
import sys
import six
import glob
import shutil
import string
import logging
import datetime
import traceback

from monty.serialization import loadfn

from pymatgen import Structure
from pymatgen.io.vasp.inputs import Kpoints
from pymatgen.io.vasp.sets import MinimalVaspInputSet
from fireworks import Firework, FireTaskBase, FWAction, explicit_serialize, Workflow, LaunchPad

from custodian import Custodian
from custodian.vasp.jobs import VaspJob
from custodian_afrl.vasp.validators import VasprunXMLValidator

logger = logging.getLogger(__name__)
MODULE_DIR = os.path.dirname(os.path.abspath(__file__))

def load_class(mod, name):
    mod = __import__(mod, globals(), locals(), [name], 0)
    return getattr(mod, name)


class VaspInputInterface:
    """
    This is an interface class to WriteVaspInputTask(FireTaskBase).  

    Required Params:
        -s (obj):               Pymatgen Structure Object used to for
                                creating VASP input files

    Optional Params:
        -vis (Vasp Input Set):  Input Set from pymatgen.io.vasp.sets to use
                                for default input parameters to VASP
        -isp (Input Set param): Custom VASP input parameters to overwrite the
                                default parameters from the VASP Input Set file.
        -custom_params ({dict}): Used to pass custom KPOINT settings for 
                                a VASP calculation.
        -config_file (str):     Location of a configuration file used to overwrite
                                VASP input set parameters.  If none is given, 
                                it will check for vasp_interface_defaults.yaml in 
                                the user's HOME directory.
    """

    def __init__(self,s=None,vis='MinimalVaspInputSet',isp={}, custom_params={}, config_file=None):
        # Set ignore list for ignoring certain __dict__ items in member functions
        self.__dict__['ignore_list'] = ['input','custom_settings',
                'user_incar','vasp_is','ignore_list', 'input_set_params', 
                'kpts_params', 'params']

        if config_file:
            config_dict = loadfn(config_file)
        elif os.path.exists(os.path.join(os.environ['HOME'], 'vasp_interface_defaults.yaml')):
            config_dict = loadfn(os.path.join(os.environ['HOME'], 'vasp_interface_defaults.yaml'))
        else:
            config_dict = {}
            self.__dict__['vasp_is'] = vis
            self.__dict__['user_incar'] = isp
            self.__dict__['custom_settings'] = custom_params

            print("\n*********************************************\n"
                    "Configuration File not supplied and\n"
                    "${HOME}/vasp_interface_defaults.yaml does\n"
                    "not exist. You can copy this file from\n"
                    "pymatgen/io/vasp/InterfaceExamples/test_files/\n"
                    "vasp_interface_defaults.yaml and modify it \n"
                    "for your project's needs.\n"
                    "*********************************************\n\n"
                    "Will continue with MinimalVaspInputSet parameters.\n")
            
        if config_dict:
            self.__dict__['vasp_is'] = config_dict.get('VASP_INPUT_SET', 'MinimalVaspInputSet')
            self.__dict__['user_incar'] = {} if config_dict['INPUT_SET_PARAMS']['user_incar_settings'] == None else config_dict['INPUT_SET_PARAMS']
            self.__dict__['custom_settings'] = config_dict.get('CUSTOM_PARAMS', {}) 
            if isp:
                self.user_incar['user_incar_settings'].update(isp)
            if custom_params:
                self.custom_settings['user_kpts_settings'].update(custom_params)

        self.__dict__['input']=WriteVaspInputTask(structure=s, vasp_input_set=self.vasp_is,
                input_set_params=self.user_incar, custom_params=self.custom_settings)

        self.__dict__['params'] = [u'NELMDL', u'LMUSIC', u'WC', u'LCHIMAG', u'INIWAV', u'APACO', 
                  u'IBRION', u'ICORELEVEL', u'MODEL_GW', u'IRESTART', u'MAGMOM', 
                  u'PARAM2', u'POMASS', u'LRPA', u'ADDGRID', u'OFIELD_KAPPA', 
                  u'MAXMIX', u'EMAX', u'LFXHEG', u'LBERRY', u'ENAUG', u'LHFONE', 
                  u'ISYM', u'NREBOOT', u'MODEL_ALPHA', u'AEXX', u'LTCTE', 
                  u'IDIOT', u'ENINI', u'LELF', u'HFALPHA', u'NBANDS', u'LMAXPAW', 
                  u'LSPIRAL', u'ISMEAR', u'ENCUTGWSOFT', u'EBREAK', u'NBANDSO', 
                  u'IWAVPR', u'LCHARG', u'WEIMIN', u'NEDOS', u'LCOMPAT', 
                  u'EFIELD', u'LVEL', u'LDAU', u'LMAXMIX', u'NOMEGAR', 
                  u'ODDONLYGW', u'ISPIN', u'EVENONLY', u'BMIX_MAG', u'LUSE_VDW',
                  u'NMAXFOCKAE', u'SYMPREC', u'LSYMGRAD', u'LVHAR', u'IBSE', 
                  u'LVTOT', u'LGAUGE', u'TEEND', u'AVECCONST', u'MAGDIPOL', 
                  u'ODDONLY', u'ORBITALMAG', u'MCALPHA', u'LZEROZ', u'SIGMA',
                  u'SAXIS', u'NGZ', u'TURBO', u'NBLOCK', u'TELESCOPE', u'NCORE',
                  u'NRMM', u'IDIPOL', u'HFSCREENC', u'EVENONLYGW', u'LRHFCALC',
                  u'EFERMI', u'NBANDSGWLOW', u'LSCAAWARE', u'OMEGATL', u'EDIFF', 
                  u'LEPSILON', u'FOURORBIT', u'DIPOL', u'LTRIPLET', u'LDIPOL', 
                  u'LSORBIT', u'NUPDOWN', u'PRECFOCK', u'HFSCREEN', u'LTHOMAS', 
                  u'LSCALAPACK', u'NBLK', u'NFREE', u'OFIELD_Q6_FAR', u'ENCUT4O', 
                  u'GGA_COMPAT', u'NELECT', u'LMAXMP2', u'LCORR', u'NBANDSV', 
                  u'EXXOEP', u'AMIX', u'TIME', u'LORBIT', u'RWIGS', u'QSPIRAL', 
                  u'NKREDLFX', u'NKREDLFY', u'NKREDLFZ', u'NKREDLF', u'ALGO', 
                  u'IALGO', u'LPARD', u'PSTRESS', u'PARAM3', u'LMODELHF', 
                  u'PARAM1', u'CSHIFT', u'NGYF', u'LWAVE', u'SELFENERGY', u'NPACO', 
                  u'NELMIN', u'LSPECTRAL', u'ENCUTLF', u'EMIN', u'LMAGBLOCH', 
                  u'LSCALU', u'ICHARG', u'NSIM', u'LHARTREE', u'MODEL_EPS0', 
                  u'I_CONSTRAINED_M', u'GGA', u'OMEGAMIN', u'TEBEG', u'DEPER', 
                  u'NMIN', u'DEG_THRESHOLD', u'NLSPLINE', u'LNICSALL', u'MREMOVE', 
                  u'SCALEE', u'INIMIX', u'AMIN', u'NGZF', u'LMETAGGA', u'NPAR', 
                  u'LUSEW', u'MIXPRE', u'MAXMEM', u'ALDAC', u'L2ORDER', u'NELM', 
                  u'OMEGAGRID', u'PREC', u'TAUPAR', u'NWRITE', u'ALDAX', u'EREF', 
                  u'EDIFFG', u'LHFCALC', u'NBANDSGW', u'SHIFTRED', u'LSUBROT', 
                  u'LFXC', u'KSPACING', u'DIM', u'LFOCKAEDFT', u'NUCIND', u'KPAR',
                  u'OFIELD_Q6_NEAR', u'NKREDX', u'NKREDY', u'NKREDZ', u'KINTER', 
                  u'LASPH', u'VOSKOWN', u'ISIF', u'KGAMMA', u'SMASS', u'ENMAX', 
                  u'LFXCEPS', u'AMIX_MAG', u'OFIELD_K', u'BMIX', u'ISTART', 
                  u'AGGAC', u'NGX', u'LDIAG', u'NGY', u'LOPTICS', u'LASYNC', 
                  u'Zab_VDW', u'AGGAX', u'NSW', u'ENCUTGW', u'ROPT', u'STM', 
                  u'LNABLA', u'NOMEGA', u'SCISSOR', u'POTIM', u'LPLANE', u'LREAL', 
                  u'KBLOCK', u'NGXF', u'DARWINR', u'MAGATOM', u'DARWINV', u'NKRED',
                  u'LORBITALREAL', u'EPSILON', u'LNONCOLLINEAR', u'OMEGAMAX', 
                  u'SYSTEM', u'IMIX', u'MAGPOS', u'LMAXFOCK', u'OFIELD_A', 
                  u'LMONO', u'ANTIRES', u'LTETE', u'LADDER', u'ENCUT', u'ZVAL']

        self.__dict__['kpts_params'] = ['kpts_style', 'kpts', 'kpts_shift']
        self.__dict__['input_set_params'] = ['constrain_total_magmom','sort_structure',
                'ediff_per_atom','potcar_functional','force_gamma','reduce_structure']

        #Set dictionaries in case they do not exsist
        try:
            self.input['input_set_params']['user_incar_settings']
        except:
            self.input['input_set_params']['user_incar_settings'] = {}
        try:
            self.input['custom_params']['user_kpts_settings']
        except:
            self.input['custom_params']['user_kpts_settings'] = {}
        
        
    def __setattr__(self, key, val):
        '''
        Automatically adds Class Object Attributes directly to their
        appropriate dictionary for the WriteVaspInputTask

        Example:
        This will put NEDOS=2048 into the WriteVaspInputTask['input_set_params']\
                    ['user_incar_settings']['NEDOS']=2048
            input = VaspInputInterface(s=s)
            input.NEDOS=2048
        
        This will put KPTS=[12,12,12] into WriteVaspInputTask['custom_params']\
                    ['user_kpts_settings']['kpts']=[12,12,12]
            input.kpts=[12,12,12]
        '''

        self.proc_key_val(key.strip(), val.strip()) if isinstance(
                val, six.string_types) else self.proc_key_val(key.strip(),val)

    def __setitem__(self, key, val):
        '''
        Automatically adds Class Dictionary Entries directly to their 
        appropriate dictionary for WriteVaspInputTask.  
        
        Example:
        This will put NEDOS=2048 into the WriteVaspInputTask['input_set_params']\
                    ['user_incar_settings']['NEDOS']=2048
            input = VaspInputInterface(s=s)
            input['NEDOS']=2048
        '''
        self.proc_key_val(key.strip(), val.strip()) if isinstance(
                val, six.string_types) else self.proc_key_val(key.strip(),val)

    def proc_key_val(self, key, val):
        '''
        Internal method for sorting Class attributes and Class Dictionary
        entries into their appropriate WriteVaspInputTask Dictionary
        '''

        incar_dict = self.input.get('input_set_params', 
                {}).get('user_incar_settings', {})
        kpts_dict = self.input.get('custom_params', 
                {}).get('user_kpts_settings', {})

        if key not in self.params and key not in self.kpts_params and \
                key not in self.ignore_list and key not in self.input_set_params:
            print "Parameter '{}' not found in any parameter list".format(key)
            print "Please check spelling and try again."
            print("Adding {} to base <VaspInputInterface Obj> Attribute\n\n".format(key))

        if key in self.params:
            incar_dict.update({key: val})
        elif key in self.kpts_params:
            kpts_dict.update({key: val})
        elif key in self.input_set_params:
            self.input['input_set_params'].update({key:val})
        else:
            self.__dict__.update({key:val})


    def write_input(self,dir='.'):
        self.input.write_input_task(dir)

    def get_nelect(self):
        return self.input.get_nelect_task()

    def add_to_allowed_list(self,parameter):
        self.__dict__['params'].append(parameter)


class VaspFirework():
    """
    This is an interface to Fireworks for VaspInputInterface workflows

    Required Params:
        -vasp_task (obj):   FireTask Object used to create a Firework.
                            2 FireTasks are automatically created upon constructing
                            the VaspFirework object from a WriteVaspInputTask: 
                            [<WriteVaspInputTask()>, <RunCustodianTask()>].

    Optional Params:
        -name (str):        Name given to the Firework
    """

    def __init__(self,vasp_task=None,name='vaspfw',handlers=None, handler_params=None, config_file=None):
        self.name = name
        self.handlers=handlers if handlers else []
        self.handler_params=handler_params if handler_params else {}

        if config_file:
            config_dict = loadfn(config_file)
        elif os.path.exists(os.path.join(os.environ['HOME'], 'vasp_interface_defaults.yaml')):
            config_dict = loadfn(os.path.join(os.environ['HOME'], 'vasp_interface_defaults.yaml'))
        else:
            config_dict = {}

        if config_dict:
            self.custodian_opts = config_dict.get('CUSTODIAN_PARAMS', {})
            if self.custodian_opts.get('handlers', []):
                self.handlers.extend(self.custodian_opts.get('handlers', []))
            self.handler_params.update(self.custodian_opts.get('handler_params', {}))

        self.tasks=[vasp_task.input,RunCustodianTask(handlers=self.handlers, 
                handler_params=self.handler_params)] if isinstance(vasp_task, 
                VaspInputInterface) else [vasp_task]
        self.Firework=Firework(self.tasks,name=self.name)
        # Try to establish connection with Launchpad
        try:
            self.LaunchPad=LaunchPad.from_file(os.path.join(os.environ["HOME"], ".fireworks", "my_launchpad.yaml"))
        except:
            self.LaunchPad = None

    def add_task(self, task):
        '''
        Function used to add another FireTask to the Firework.  If
        given task is a VaspInputInterface Object, it will create the WriteInputTask
        as well as add another RunCustodianTask() to the FireTask list.
        '''

        if isinstance(task, VaspInputInterface):
            self.tasks.extend([task.input, 
                    RunCustodianTask(handlers=self.handlers,
                            handler_params=self.handler_params)])
        else:
            self.tasks.append(task)
        self.Firework=Firework(self.tasks,name=self.name)

    def to_file(self,file_name):
        self.Firework.to_file(file_name)

    def add_fw_to_launchpad(self):
        if self.LaunchPad:
            self.Workflow=Workflow([self.Firework])
            self.LaunchPad.add_wf(self.Workflow)
        else:
            print("No connection to LaunchPad. \n"
                "Use 'to_file(<filename>)' to write a yaml file\n"
                "to manually add Firework to LaunchPad later.\n")

    def copy_files_from_previous(self, *args, **kwargs):
        '''
        Function used to automatically insert VASPTransferTask 
        between WriteVaspInputTask() and RunCustodianTask() in the 
        FireTask list.  This is used to copy files from a previous 
        VASP Firework Calculation or from a known directory.  
        Known directories require the full path of the directory.
        This function should not be used to copy files from a 
        previous Firetask within the same Firework.

        Parameters:
            -mode (str):    Mode to use in transferring files:
                            move, mv, copy, cp, copy2, copytree, copyfile
                            Default:  'copy'
            -ignore_errors (bool):  Whether to quit upon error.
                            Default: True

            -dir (str):     Directory to transfer files from if not copying
                            directly from a previous Firework
                            Default: None

        Example:
            fw2.copy_files_from_previous('file1', 'file2', ... , 'filen',  mode='copy', ignore_errors=False)

        Example to copy files from directory other than previous Firework:
            fw2.copy_files_from_previous('file1', 'file2', ... , 'filen',
                                        dir='/path/to/dir', mode='copy', ignore_errors=False)
        '''

        mode = kwargs.get('mode', 'copy')
        ignore_errors = kwargs.get('ignore_errors', True)
        files = args[0] if isinstance(args[0], list) else [k for k in args]
        self.dir = kwargs.get('dir', None)

        if self.dir:
            self.transfer=VaspTransferTask(files=files, dir=self.dir, 
                                mode=mode, ignore_errors=ignore_errors)
        else:
            self.transfer=VaspTransferTask(files=files, mode=mode, ignore_errors=ignore_errors)

        if isinstance(self.tasks[-1], RunCustodianTask):
            self.tasks.pop(-1)
            self.add_task(self.transfer)
            self.add_task(RunCustodianTask(handlers=self.handlers, 
                                handler_params=self.handler_params))
        else:
            self.add_task(self.transfer)

    def add_handler(self, handler, **kwargs):
        '''
        Member function to add handler and handler options to 
        all Fireworks in the VaspFirework.tasks list.

        Example (assuming fw1 is the firework object):
        fw1.add_handler('WalltimeHandler', wall_time=3600, buffer_time=300)
        fw1.add_handler('FrozenJobErrorHandler')
        '''
        for i,j in enumerate(self.Firework.tasks):
            if isinstance(j, RunCustodianTask):
                cust_handlers = j.get('handlers', [])
                cust_params = j.get('handler_params', {})
                if handler not in cust_handlers:
                    j['handlers'].append(handler)
                    j['handler_params'].update(kwargs)
        self.Firework.spec['_tasks']=[t.to_dict() for t in
                self.Firework.tasks] #re-initialize FW.spec


class VaspWorkflow():
    """
    A VaspWorkflow encapsulates multiple VaspFirework objects into a Single Workflow.  
    If the kwarg "dependency" is not set, it will create a Sequential Workflow where the next 
    Firework in the Workflow will not start before the currently running Firework in the 
    Workflow completes.

    Parameters:
        -args (obj):        List of VaspFirework objects
        -deps_dict {dict}:  Specifies the dependency of the VaspInputInterface objects given.
                            If no dependency is given, Firworks are assumed to be
                            sequentially dependent.
        -name (str):        Name to be given to the Workflow


    Example:
        VaspWorkflow(FW1, FW2, FW3, FW4, deps_dict={FW1: [FW2, FW3], FW2: [FW4], FW3: [FW4]}, name='Example WF')
        
        This will create a Workflow containing the 4 given VaspFirework objects
        with a Workflow name of 'Example WF' and the given dependencies.
        Dependency Dictionary Explanation:
            In the above example, FW2 and FW3 will not start before FW1 is complete.
            Likewise, FW4 depends on the completion of FW2 and FW3 before starting.
    """

    def __init__(self, *args, **kwargs):
        '''
        :param args:       (VaspFirework objects) objects to create Workflow from.  No limit
                           on the amount of VaspInputInterface objects to be given.  Entered as just
                           comma separated objects passed to class.
        :param deps_dict:  (dict) specifies the dependency of the VaspInputInterface objects given.  
                           If no dependency is given, Firworks are assumed to be 
                           sequentially dependent.
        :param name        (str) Name given to Workflow
        '''
        self.fws = []
        self.name = kwargs.get('name', 'Sequential WF')
        self.deps_dict = kwargs.get('deps_dict', {})
        self.dependency = {}
        if self.deps_dict:
            for i in self.deps_dict.keys():
                fw_deps = []
                for j in self.deps_dict[i]:
                    fw_deps.append(j.Firework)                    
                self.dependency[i.Firework]=fw_deps
        self.deps = True if self.dependency else False
        for id, fw_task in enumerate(args):
            self.fws.append(fw_task.Firework)
            if not self.deps and id != 0:
                self.dependency[self.fws[id-1]]=[fw_task.Firework]
        self.wf = Workflow(self.fws, self.dependency, name=self.name)
        # Try to establish connection with Launchpad
        try:
            self.LaunchPad=LaunchPad.from_file(os.path.join(os.environ["HOME"], ".fireworks", "my_launchpad.yaml"))
        except:
            self.LaunchPad = None


    def add_fw(self, fw_task, deps=None):
        self.fws.append(fw_task.Firework)
        if deps:
            for i in deps.keys():
                fw_deps = []
                for j in deps[i]:
                    fw_deps.append(j.Firework)
                self.dependency[i.Firework]=fw_deps
        else:
            id = len(self.fws) - 2
            self.dependency[self.fws[id]]=[fw_task.Firework]
        self.wf=Workflow(self.fws, self.dependency, name=self.name)


    def to_file(self,filename):
        self.wf.to_file(filename)


    def add_wf_to_launchpad(self):
        if self.LaunchPad:
            self.LaunchPad.add_wf(self.Workflow)
        else:
            print("No connection to LaunchPad. \n"
                "Use 'to_file(<filename>)' to write a yaml file\n"
                "to manually add Workflow to LaunchPad later.\n")


@explicit_serialize
class WriteVaspInputTask(FireTaskBase):
    """ 
    Writes VASP Input files.

    Required params:
        structure (dict): An input structure in pymatgen's Structure.to_dict
            format.
        vasp_input_set (str): A string name for the VASP input set. E.g.,
            "MPVaspInputSet" or "MITVaspInputSet".

    Optional params:
        input_set_params (dict): If the input set requires some additional
            parameters, specify them using input_set_params. Note that if you
            want to change the user_incar_settings, you need to provide
            {"input_set_params": {"user_incar_settings": ...}}. This is
            because input_set_params caters to not just user_incar_settings,
            but all other potential args to the input set.
    """
    required_params = ["structure", "vasp_input_set"]
    optional_params = ["input_set_params","custom_params"]


    def run_task(self, fw_spec):

        prev_dir = fw_spec.get('PREV_DIR', None)
        self.custom_params = self.get('custom_params', None)   

        if isinstance(self["structure"], Structure):
            s = self["structure"]
        elif isinstance(self["structure"], dict):
            s = Structure.from_dict(self["structure"])
        else:
            s = Structure.from_file(os.path.join(prev_dir, self["structure"]))


        vis = load_class("pymatgen.io.vasp.sets", self["vasp_input_set"])(
                         **self.get("input_set_params", {}))
        vis.write_input(s, ".")


        # Write Custom KPOINTS settings if necessary
        ksettings = self.custom_params.get('user_kpts_settings', None) if isinstance(
                self.custom_params, dict) else None
        if ksettings:
            style = ksettings.get('kpts_style', 'Gamma')
            kpoints = ksettings.get('kpts', [16,16,16])
            shift = ksettings.get('kpts_shift', [0,0,0])
            k = Kpoints(kpts=[kpoints], kpts_shift=shift)
            k.style = style
            k.write_file("KPOINTS")
            

    def write_input_task(self, dir='.'):
        self.custom_params = self.get('custom_params', None)

        if isinstance(self["structure"], Structure):
            s = self["structure"]
        elif isinstance(self["structure"], dict):
            s = Structure.from_dict(self["structure"])
        else:
            s = Structure.from_file(self["structure"])


        if os.environ.get('VASP_PSP_DIR') == None:
            print "VASP_PSP_DIR not set.  Checking User's HOME directory for VASP potentials."
            if os.path.exists(os.path.join(os.environ.get('HOME'), 'Potentials')):
                os.environ['VASP_PSP_DIR'] = os.path.join(os.environ.get('HOME'), 'Potentials')
            else:
                print "VASP Potentials not found!"
                print "Please copy the Potentials Folder"
                print "from VASP into your HOME directory"
                sys.exit()

        vis = load_class("pymatgen.io.vasp.sets", self["vasp_input_set"])(
                         **self.get("input_set_params", {}))
        vis.write_input(s, dir)

        # Write Custom KPOINTS settings if necessary
        ksettings = self.custom_params.get('user_kpts_settings', None) if isinstance(
                self.custom_params, dict) else None
        if ksettings:
            style = ksettings.get('kpts_style', 'Gamma')
            kpoints = ksettings.get('kpts', [16,16,16])
            shift = ksettings.get('kpts_shift', [0,0,0])
            k = Kpoints(kpts=[kpoints], kpts_shift=shift)
            k.style = style
            filename = os.path.join(dir, 'KPOINTS')
            k.write_file(filename)
        
        print "Wrote VASP input files to '{}'".format(dir)

    def get_nelect_task(self):
        
        if isinstance(self["structure"], Structure):
            s = self["structure"]
        elif isinstance(self["structure"], dict):
            s = Structure.from_dict(self["structure"])
        else:
            s = Structure.from_file(self["structure"])


        if os.environ.get('VASP_PSP_DIR') == None:
            if os.path.exists(os.path.join(os.environ.get('HOME'), 'Potentials')):
                os.environ['VASP_PSP_DIR'] = os.path.join(os.environ.get('HOME'), 'Potentials')
            else:
                print "VASP_PSP_DIR not set.  Check User's HOME directory for VASP potentials."
                print "Potentials not found!"
                sys.exit()

        vis = load_class("pymatgen.io.vasp.sets", self["vasp_input_set"])(
                         **self.get("input_set_params", {}))
        return vis.get_nelect(s)

@explicit_serialize
class VaspTransferTask(FireTaskBase):
    """
    A FireTask to Transfer files from a preivous VASP run in the same Workflow.
    Required params:
        - mode: (str) - move, mv, copy, cp, copy2, copytree, copyfile
        - files: [list] - list of files to pull from previous Firework in the Workflow

    Optional params:
        - ignore_errors (bool) - Whether or not to raise Errors and terminate task.
        - dir (str)  - Directory from which to copy files from, otherwise PREV_DIR
                       is used.
    """
    required_params = ['mode', 'files']
    optional_params = ['ignore_errors', 'dir']
    
    fn_list = {
        "move": shutil.move,
        "mv": shutil.move,
        "copy": shutil.copy,
        "cp": shutil.copy,
        "copy2": shutil.copy2,
        "copytree": shutil.copytree,
        "copyfile": shutil.copyfile,        
    }
    
    def run_task(self, fw_spec):
        prev_dir = fw_spec.get('PREV_DIR', self.get('dir', None))
        if not prev_dir or not os.path.exists(prev_dir):
            raise RuntimeError(
                    'Source Directory not found or was not specified\n'
                    'Looked for: {}'.format(prev_dir))
        ignore_errors = self.get('ignore_errors', False)
        mode = self.get('mode', 'copy')

        for f in self['files']:
            try:
                if '*' in f:
                    file_list = glob.glob(os.path.join(prev_dir,f))
                    for src in file_list:
                        VaspTransferTask.fn_list[mode](src, '.')
                else:
                    src = os.path.join(prev_dir, f)
                    VaspTransferTask.fn_list[mode](src, '.')
            except:
                if not ignore_errors:
                    traceback.print_exc()
                    raise ValueError(
                            "There was an error performing operation {} from {} "
                            "to '.'".format(mode, self["files"]))


@explicit_serialize
class RunCustodianTask(FireTaskBase):
    """
    Runs VASP using Custodian.

    Optional Params:
        handlers ([str]): List of error handler names to use. See custodian
        .vasp.handlers for list of applicable handlers. Note that
        default args are assumed for all handlers for simplicity. A
        special option is "all", which simply uses a set of common
        handlers for relaxation jobs.
    """

    # Insert way to Add Handler Options and 
    def run_task(self, fw_spec):
        FORMAT = '%(asctime)s  %(levelname)s:  %(pathname)s\n\t%(module)s.%(funcName)s:  %(message)s'
        logging.basicConfig(format=FORMAT, level=logging.INFO, filename="run.log")
        job = VaspJob(["vasp"],default_vasp_input_set=MinimalVaspInputSet(),auto_npar=False)

        #Set up Handlers to be used
        handler_param_dict = {'VaspErrorHandler': ['output_filename'], 
                'AliasingErrorHandler': ['output_filename'], 
                'MeshSymmetryErrorHandler': ['output_filename', 'output_vasprun'],
                'UnconvergedErrorHandler': ['output_filename'],
                'MaxForceErrorHandler': ['output_filename', 'max_force_threshold'],
                'PotimErrorHandler': ['input_filename', 
                            'output_filename', 'dE_threshold'],
                'FrozenJobErrorHandler': ['output_filename', 'timeout'],
                'NonConvergingErrorHandler': ['output_filename', 'nionic_steps',
                            'change_algo'],
                'WalltimeHandler': ['wall_time', 'buffer_time',
                            'electronic_step_stop'],
                'CheckpointHandler': ['interval'],
                'PositiveEnergyErrorHandler': ['output_filename']}

        hnames = self.get('handlers')
        handler_params = self.get("handler_params", {})
        logging.info("handler names:  {}".format(hnames))
        handlers = []
        for n in hnames:
            np = {}
            for m in handler_params:
                if m in handler_param_dict[n]:
                    np[m] = handler_params[m]
            handlers.append(load_class("custodian.vasp.handlers", n)(np))

        c = Custodian(handlers=handlers, validators=[VasprunXMLValidator()], jobs=[job])
        output = c.run()
        return FWAction(stored_data=output, mod_spec=[{'_set': {'PREV_DIR': os.getcwd()}}])

<file_sep>#!/usr/bin/env python

import os
import sys
import string
import json

import inspect

from pymatgen import Structure
from fireworks import Firework, Workflow, LaunchPad
from pymatgen.io.vasp.interfaces import VaspInputInterface, VaspFirework, VaspWorkflow

# get structure from Crystallographic Information File (CIF)
s = Structure.from_file('./mp-33088_Cr2FeO4.cif')

input=VaspInputInterface(s)
input.NEDOS=2000 # override default or add INCAR parameter

# Dump VASP Input into current directory for inspection
input.write_input()

# Complete definition of Firework Task(s) and add to
# Firework
task=VaspFirework(input)

# Save specification to yaml file for later inspection
# or manual add to launchpad with lpad script
task.to_file("simple_task.yaml")

# Adds single Firework to launchpad database
task.add_fw_to_launchpad()

<file_sep>"""
Created on Aug 7, 2015
"""


__author__ = "<NAME>"
__copyright__ = "Approved for public release: AFRL DSRC Case Number 88ABW-2016-0260"
__version__ = "0.1"
__maintainer__ = "<NAME>"
__email__ = "<EMAIL>"
__date__ = "2016-01-29"

import unittest
import shutil
import pkgutil
import os

if pkgutil.find_loader('fireworks'):
    from pymatgen.io.vasp.interfaces import VaspInputInterface, VaspFirework, VaspWorkflow, VaspTransferTask
    from fireworks import ScriptTask

from pymatgen import Structure

MODULE_DIR = os.path.dirname(os.path.abspath(__file__))
test_dir = os.path.join(MODULE_DIR, '..', 'InterfaceExamples', 'test_files')

@unittest.skipUnless(1 if pkgutil.find_loader("fireworks") else 0, "Requires Fireworks")
class VaspInputInterfaceTest(unittest.TestCase):
    def setUp(self):
        self.maxDiff=None
        self.mol_dict = {u'@class': 'Structure',
            u'@module': 'pymatgen.core.structure',
            u'lattice': {u'a': 3.09681658,
                u'alpha': 60.00000352,
                u'b': 3.09681633,
                u'beta': 60.000006230000004,
                u'c': 3.096817,
                u'gamma': 60.00000143999999,
                u'matrix': [[2.6819219975054747, 0.0, 1.5484079983838697],
                            [0.8939740004171242, 2.5285400003050134, 1.5484080002345615],
                            [0.0, 0.0, 3.096817]],
                u'volume': 21.00059082235557},
            u'sites': [{u'abc': [0.25, 0.25, 0.25],
                        u'species': [{u'element': u'Si', u'occu': 1.0}]},
                        {u'abc': [0.0, 0.0, 0.0], 
                        u'species': [{u'element': u'C', u'occu': 1.0}]}]}

        self.mol_file = os.path.join(test_dir, 'SiC_0.cif')
        self.config_file = os.path.join(test_dir, 'vasp_interface_defaults.yaml')

        self.isp = {'ALGO': 'Fast', 'LOPTICS':False}
        self.custom_params = {'kpts': [8,8,8], 'kpts_shift':[1,0,0]}

        self.gdic = {u'_fw_name': '{{pymatgen.io.vasp.interfaces.WriteVaspInputTask}}',
                'custom_params': {'user_kpts_settings': {'kpts': [6, 6, 6],
                    'kpts_shift': [0, 0, 0],
                    'kpts_style': 'Gamma'}},
                'input_set_params': {'user_incar_settings': {'ALGO': 'Normal',
                    'CSHIFT': 0.1,
                    'LOPTICS': '.TRUE.',
                    'NEDOS': 4096,
                    'SIGMA': 0.01}},
                'structure': {'@class': 'Structure',
                    '@module': 'pymatgen.core.structure',
                    'lattice': {'a': 3.09681658,
                        'alpha': 60.00000352,
                        'b': 3.09681633,
                        'beta': 60.000006230000004,
                        'c': 3.096817,
                        'gamma': 60.00000143999999,
                    'matrix': [[2.6819219975054747, 0.0, 1.5484079983838697],
                        [0.8939740004171242, 2.5285400003050134, 1.5484080002345615],
                        [0.0, 0.0, 3.096817]],
                    'volume': 21.00059082235557},
                    'sites': [{'abc': [0.25, 0.25, 0.25],
                        'species': [{'element': 'Si', 'occu': 1.0}]},
                        {'abc': [0.0, 0.0, 0.0], 'species': [{'element': 'C', 'occu': 1.0}]}]},
                'vasp_input_set': 'MinimalVaspInputSet'}

    def tearDown(self):
        pass

    def test_input_from_dict(self):
        input = VaspInputInterface(s=self.mol_dict, config_file=self.config_file)
        inp_dic = input.input.as_dict()
        self.assertDictEqual(inp_dic, self.gdic, msg=None)

    def test_input_from_file(self):
        input = VaspInputInterface(s=self.mol_file, config_file=self.config_file)
        inp_dic = input.input.as_dict()
        cdic = {u'_fw_name': '{{pymatgen.io.vasp.interfaces.WriteVaspInputTask}}',
                'custom_params': {'user_kpts_settings': {'kpts': [6, 6, 6],
                    'kpts_shift': [0, 0, 0],
                    'kpts_style': 'Gamma'}},
                'input_set_params': {'user_incar_settings': {'ALGO': 'Normal',
                    'CSHIFT': 0.1,
                    'LOPTICS': '.TRUE.',
                    'NEDOS': 4096,
                    'SIGMA': 0.01}},
                'structure': self.mol_file,
                'vasp_input_set': 'MinimalVaspInputSet'}
        self.assertDictEqual(inp_dic, cdic, msg=None)

    def test_input_from_structure(self):
        s = Structure.from_file(self.mol_file)
        input = VaspInputInterface(s=s, config_file=self.config_file)
        inp_dic = input.input.as_dict()
        self.assertDictEqual(inp_dic, self.gdic, msg=None)

    def test_custom_incar_and_kpoints(self):
        input = VaspInputInterface(s=self.mol_dict, isp=self.isp,
                            custom_params=self.custom_params,
                            config_file=self.config_file)
        inp_dic = input.input.as_dict()
        cdic = {u'_fw_name': '{{pymatgen.io.vasp.interfaces.WriteVaspInputTask}}',
            'custom_params': {'user_kpts_settings': {'kpts': [8, 8, 8],
                'kpts_shift': [1, 0, 0],
                'kpts_style': 'Gamma'}},
            'input_set_params': {'user_incar_settings': {'ALGO': 'Fast',
                'CSHIFT': 0.1,
                'LOPTICS': False,
                'NEDOS': 4096,
                'SIGMA': 0.01}},
            'structure': {'@class': 'Structure',
                '@module': 'pymatgen.core.structure',
                'lattice': {'a': 3.09681658,
                'alpha': 60.00000352,
                'b': 3.09681633,
                'beta': 60.000006230000004,
                'c': 3.096817,
                'gamma': 60.00000143999999,
            'matrix': [[2.6819219975054747, 0.0, 1.5484079983838697],
                [0.8939740004171242, 2.5285400003050134, 1.5484080002345615],
                [0.0, 0.0, 3.096817]],
            'volume': 21.00059082235557},
            'sites': [{'abc': [0.25, 0.25, 0.25],
                'species': [{'element': 'Si', 'occu': 1.0}]},
                {'abc': [0.0, 0.0, 0.0], 'species': [{'element': 'C', 'occu': 1.0}]}]},
            'vasp_input_set': 'MinimalVaspInputSet'}
        self.assertDictEqual(inp_dic, cdic, msg=None)

    def test_user_friendly_custom_incar(self):
        input = VaspInputInterface(s=self.mol_file, config_file=self.config_file)
        input.LOPTICS=False
        input.ALGO='Exact'
        input.EDIFF=1e-05
        input.kpts=[8,8,8]
        input.kpts_shift=[1,0,0]
        inp_dic = input.input.as_dict()

        cdic = {u'_fw_name': '{{pymatgen.io.vasp.interfaces.WriteVaspInputTask}}',
            'custom_params': {'user_kpts_settings': {'kpts': [8, 8, 8],
                'kpts_shift': [1, 0, 0],
                'kpts_style': 'Gamma'}},
            'input_set_params': {'user_incar_settings': {'ALGO': 'Exact',
                'CSHIFT': 0.1,
                'EDIFF': 1e-05,
                'LOPTICS': False,
                'NEDOS': 4096,
                'SIGMA': 0.01}},
            'structure': self.mol_file,
            'vasp_input_set': 'MinimalVaspInputSet'}
        self.assertDictEqual(inp_dic, cdic, msg=None)

    def test_get_nelect_from_interface(self):
        input = VaspInputInterface(s=self.mol_file, config_file=self.config_file)
        nelect = input.get_nelect()
        self.assertEqual(nelect, 8.0, msg=None)


@unittest.skipUnless(1 if pkgutil.find_loader("fireworks") else 0, "Requires Fireworks")
class VaspFireworkTest(unittest.TestCase):
    def setUp(self):
        self.mol_file = 'SiC_0.cif'
        self.config_file = os.path.join(test_dir, 'vasp_interface_defaults.yaml')
        self.input = VaspInputInterface(s=self.mol_file, config_file=self.config_file)
        self.misc_task = ScriptTask.from_str("echo 'Hello World!'")

    def tearDown(self):
        pass


    def test_add_task_VaspInputInterface(self):
        fw = VaspFirework(self.input, config_file=self.config_file)
        fw.add_task(self.input)
        fw_dic = fw.Firework.as_dict()
        for i in ['created_on', 'updated_on', 'fw_id']:
            del fw_dic[i]

        gdic = {'name': 'vaspfw',
            'spec': {'_tasks': [{'_fw_name': '{{pymatgen.io.vasp.interfaces.WriteVaspInputTask}}',
                'custom_params': {'user_kpts_settings': {'kpts': [6, 6, 6],
                    'kpts_shift': [0, 0, 0],
                    'kpts_style': 'Gamma'}},
                'input_set_params': {'user_incar_settings': {'ALGO': 'Normal',
                    'CSHIFT': 0.1,
                    'LOPTICS': '.TRUE.',
                    'NEDOS': 4096,
                    'SIGMA': 0.01}},
                'structure': 'SiC_0.cif',
                'vasp_input_set': 'MinimalVaspInputSet'},
            {'_fw_name': '{{pymatgen.io.vasp.interfaces.RunCustodianTask}}',
                'handler_params': {},
                'handlers': []},
            {'_fw_name': '{{pymatgen.io.vasp.interfaces.WriteVaspInputTask}}',
                'custom_params': {'user_kpts_settings': {'kpts': [6, 6, 6],
                    'kpts_shift': [0, 0, 0],
                    'kpts_style': 'Gamma'}},
                'input_set_params': {'user_incar_settings': {'ALGO': 'Normal',
                    'CSHIFT': 0.1,
                    'LOPTICS': '.TRUE.',
                    'NEDOS': 4096,
                    'SIGMA': 0.01}},
                'structure': 'SiC_0.cif',
                'vasp_input_set': 'MinimalVaspInputSet'},
            {'_fw_name': '{{pymatgen.io.vasp.interfaces.RunCustodianTask}}',
                'handler_params': {},
                'handlers': []}]}}

        self.assertDictEqual(fw_dic, gdic, msg=None)

    def test_add_task_misc_task(self):
        fw = VaspFirework(self.input, config_file=self.config_file)
        fw.add_task(self.misc_task)
        fw_dic = fw.Firework.as_dict()
        for i in ['created_on', 'updated_on', 'fw_id']:
            del fw_dic[i]

        gdic = {'name': 'vaspfw',
            'spec': {'_tasks': [{'_fw_name': '{{pymatgen.io.vasp.interfaces.WriteVaspInputTask}}',
                'custom_params': {'user_kpts_settings': {'kpts': [6, 6, 6],
                    'kpts_shift': [0, 0, 0],
                    'kpts_style': 'Gamma'}},
                'input_set_params': {'user_incar_settings': {'ALGO': 'Normal',
                    'CSHIFT': 0.1,
                    'LOPTICS': '.TRUE.',
                    'NEDOS': 4096,
                    'SIGMA': 0.01}},
                'structure': 'SiC_0.cif',
                'vasp_input_set': 'MinimalVaspInputSet'},
            {'_fw_name': '{{pymatgen.io.vasp.interfaces.RunCustodianTask}}',
                'handler_params': {},
                'handlers': []},
            {'_fw_name': 'ScriptTask',
                'script': ["echo 'Hello World!'"],
                'use_shell': True}]}}

        self.assertDictEqual(fw_dic, gdic, msg=None)

    def test_copy_from_previous(self):
        fw = VaspFirework(self.input, config_file=self.config_file)
        fw.copy_files_from_previous('WAVECAR', 'WAVEDER', mode='copy', ignore_errors=True)
        fw_dic = fw.Firework.as_dict()
        for i in ['created_on', 'updated_on', 'fw_id']:
            del fw_dic[i]
        
        gdic = {'name': 'vaspfw',
            'spec': {'_tasks': [{'_fw_name': '{{pymatgen.io.vasp.interfaces.WriteVaspInputTask}}',
                'custom_params': {'user_kpts_settings': {'kpts': [6, 6, 6],
                    'kpts_shift': [0, 0, 0],
                    'kpts_style': 'Gamma'}},
                'input_set_params': {'user_incar_settings': {'ALGO': 'Normal',
                    'CSHIFT': 0.1,
                    'LOPTICS': '.TRUE.',
                    'NEDOS': 4096,
                    'SIGMA': 0.01}},
                'structure': 'SiC_0.cif',
                'vasp_input_set': 'MinimalVaspInputSet'},
            {'_fw_name': '{{pymatgen.io.vasp.interfaces.VaspTransferTask}}',
                'files': ['WAVECAR', 'WAVEDER'],
                'ignore_errors': True,
                'mode': 'copy'},
            {'_fw_name': '{{pymatgen.io.vasp.interfaces.RunCustodianTask}}',
                'handler_params': {},
                'handlers': []}]}}

        self.assertDictEqual(fw_dic, gdic, msg=None)

    def test_copy_from_dir(self):
        out_name = 'FW_copy_from_dir.yaml'
        fw = VaspFirework(self.input, config_file=self.config_file)
        fw.copy_files_from_previous('WAVECAR', 'WAVEDER', mode='copy', 
                                    dir='../../test_files/VASP_INTERFACES_TEST_FILES', 
                                    ignore_errors=True)
        fw_dic = fw.Firework.as_dict()
        for i in ['created_on', 'updated_on', 'fw_id']:
            del fw_dic[i]
        
        gdic = {'name': 'vaspfw',
            'spec': {'_tasks': [{'_fw_name': '{{pymatgen.io.vasp.interfaces.WriteVaspInputTask}}',
                'custom_params': {'user_kpts_settings': {'kpts': [6, 6, 6],
                    'kpts_shift': [0, 0, 0],
                    'kpts_style': 'Gamma'}},
                'input_set_params': {'user_incar_settings': {'ALGO': 'Normal',
                    'CSHIFT': 0.1,
                    'LOPTICS': '.TRUE.',
                    'NEDOS': 4096,
                    'SIGMA': 0.01}},
                'structure': 'SiC_0.cif',
                'vasp_input_set': 'MinimalVaspInputSet'},
            {'_fw_name': '{{pymatgen.io.vasp.interfaces.VaspTransferTask}}',
                'dir': '../../test_files/VASP_INTERFACES_TEST_FILES',
                'files': ['WAVECAR', 'WAVEDER'],
                'ignore_errors': True,
                'mode': 'copy'},
            {'_fw_name': '{{pymatgen.io.vasp.interfaces.RunCustodianTask}}',
                'handler_params': {},
                'handlers': []}]}}

        self.assertDictEqual(fw_dic, gdic, msg=None)

    def test_combo_misc_add_task(self):
        out_name = 'FW_combo_add_tasks.yaml'
        fw = VaspFirework(self.input, config_file=self.config_file)
        fw.add_task(self.misc_task)
        fw.add_task(self.input)
        fw_dic = fw.Firework.as_dict()
        for i in ['created_on', 'updated_on', 'fw_id']:
            del fw_dic[i]

        gdic = {'name': 'vaspfw',
            'spec': {'_tasks': [{'_fw_name': '{{pymatgen.io.vasp.interfaces.WriteVaspInputTask}}',
                'custom_params': {'user_kpts_settings': {'kpts': [6, 6, 6],
                    'kpts_shift': [0, 0, 0],
                    'kpts_style': 'Gamma'}},
                'input_set_params': {'user_incar_settings': {'ALGO': 'Normal',
                    'CSHIFT': 0.1,
                    'LOPTICS': '.TRUE.',
                    'NEDOS': 4096,
                    'SIGMA': 0.01}},
                'structure': 'SiC_0.cif',
                'vasp_input_set': 'MinimalVaspInputSet'},
            {'_fw_name': '{{pymatgen.io.vasp.interfaces.RunCustodianTask}}',
                'handler_params': {},
                'handlers': []},
            {'_fw_name': 'ScriptTask',
                'script': ["echo 'Hello World!'"],
                'use_shell': True},
            {'_fw_name': '{{pymatgen.io.vasp.interfaces.WriteVaspInputTask}}',
                'custom_params': {'user_kpts_settings': {'kpts': [6, 6, 6],
                    'kpts_shift': [0, 0, 0],
                    'kpts_style': 'Gamma'}},
                'input_set_params': {'user_incar_settings': {'ALGO': 'Normal',
                    'CSHIFT': 0.1,
                    'LOPTICS': '.TRUE.',
                    'NEDOS': 4096,
                    'SIGMA': 0.01}},
                'structure': 'SiC_0.cif',
                'vasp_input_set': 'MinimalVaspInputSet'},
            {'_fw_name': '{{pymatgen.io.vasp.interfaces.RunCustodianTask}}',
                'handler_params': {},
                'handlers': []}]}}

        self.assertDictEqual(fw_dic, gdic, msg=None)


@unittest.skipUnless(1 if pkgutil.find_loader("fireworks") else 0, "Requires Fireworks")
class VaspWorkflowTest(unittest.TestCase):
    def setUp(self):
        self.mol_file = 'SiC_0.cif'
        self.config_file = os.path.join(test_dir, 'vasp_interface_defaults.yaml')
        self.input = VaspInputInterface(s=self.mol_file, config_file=self.config_file)
        self.fw1 = VaspFirework(self.input, config_file=self.config_file)
        self.fw2 = VaspFirework(self.input, config_file=self.config_file)
        self.fw3 = VaspFirework(self.input, config_file=self.config_file)

        self.links1 = {'-1': [-2], '-2': [-3], '-3': []}
        self.links2 = {'-1': [-2, -3], '-2': [], '-3': []}

    def tearDown(self):
        pass


    def test_make_seq_workflow_from_multi_fws(self):
        wf1 = VaspWorkflow(self.fw1, self.fw2, self.fw3)
        wf_dic = wf1.wf.as_dict()
        ldic = wf_dic['links']

        # Rebuild WF1 Dictionary to reflect FWs '-1', '-2', and '-3' instead
        # of higher FW_ID numbers
        min_fw_id = int(min(ldic.keys()))
        modifier = -( min_fw_id + 1 )
        link_dic = {str(min_fw_id + modifier): [ldic[str(min_fw_id)][0] + modifier],
                str(min_fw_id + modifier - 1): [ldic[str(min_fw_id-1)][0] + modifier],
                str(min_fw_id + modifier - 2): []}

        self.assertDictEqual(link_dic, self.links1, msg=None)

    def test_add_fw(self):
        wf1 = VaspWorkflow(self.fw1)
        wf1.add_fw(self.fw2)
        wf1.add_fw(self.fw3)

        wf_dic = wf1.wf.as_dict()
        ldic = wf_dic['links']

        # Rebuild WF1 Dictionary to reflect FWs '-1', '-2', and '-3' instead
        # of higher FW_ID numbers
        min_fw_id = int(min(ldic.keys()))
        modifier = -( min_fw_id + 1 )
        link_dic = {str(min_fw_id + modifier): [ldic[str(min_fw_id)][0] + modifier],
                str(min_fw_id + modifier - 1): [ldic[str(min_fw_id-1)][0] + modifier],
                str(min_fw_id + modifier - 2): []}
        
        self.assertDictEqual(link_dic, self.links1, msg=None)

    def test_multifw_wf_custom_deps(self):
        wf1 = VaspWorkflow(self.fw1, self.fw2, self.fw3, 
                        deps_dict={self.fw1: [self.fw2, self.fw3]})
         
        wf_dic = wf1.wf.as_dict()
        ldic = wf_dic['links']

        # Rebuild WF1 Dictionary to reflect FWs '-1', '-2', and '-3' instead
        # of higher FW_ID numbers
        min_fw_id = int(min(ldic.keys()))
        modifier = -( min_fw_id + 1 )
        link_dic = {str(min_fw_id + modifier): [
                    ldic[str(min_fw_id)][0] + modifier, ldic[str(min_fw_id)][1] + modifier],
                str(min_fw_id + modifier - 1): [],
                str(min_fw_id + modifier - 2): []}

        self.assertDictEqual(link_dic, self.links2, msg=None)


@unittest.skipUnless(1 if pkgutil.find_loader("fireworks") else 0, "Requires Fireworks")
class VaspTransferTaskTest(unittest.TestCase):
    def setUp(self):
        self.src_dir  = os.path.join(test_dir, 'TRNFR_TEST_FILES')
        self.dest_dir = os.path.join(os.environ['HOME'], 'TRNFR_TEST')
        if not os.path.exists(self.dest_dir):
            os.mkdir(self.dest_dir)

    def tearDown(self):
        os.chdir(MODULE_DIR)
        if os.path.exists(self.dest_dir):
            shutil.rmtree(self.dest_dir)


    def test_file_transfer_fail1(self):
        os.chdir(self.dest_dir)
        fw_fail1 = VaspTransferTask(mode='copy', dir=self.src_dir,
                        files=['test_f1.txt', 'test_f2.txt', 'test_f3.txt'], 
                        ignore_errors=False)
        self.assertRaises(ValueError, fw_fail1.run_task, fw_spec={})

    def test_file_transfer_fail2(self):
        os.chdir(self.dest_dir)
        fw_fail2 = VaspTransferTask(mode='copy', 
                        files=['test_f1.txt'], ignore_errors=False)
        self.assertRaises(RuntimeError, fw_fail2.run_task, fw_spec={})

    def test_file_transfer_fail3(self):
        os.chdir(self.dest_dir)
        fw_fail3  = VaspTransferTask(mode='copy',
                        files=['test_f1.txt', 'test_f2.txt'],
                        ignore_errors=False)
        self.assertRaises(TypeError, fw_fail3.run_task)

    def test_file_transfer_succeed1(self):
        fw_succ1 = VaspTransferTask(mode='copy', dir=self.src_dir,
                        files=['test_f1.txt', 'test_f2.txt', 'test_f3.txt'],
                        ignore_errors=True)
        os.chdir(self.dest_dir)
        fw_succ1.run_task(fw_spec={})

        self.assertListEqual(os.listdir(self.dest_dir), os.listdir(self.src_dir))

    def test_file_transfer_succeed2(self):
        fw_succ2 = VaspTransferTask(mode='copy', dir=self.src_dir,
                        files=['test_f1.txt', 'test_f2.txt'],
                        ignore_errors=False)
        os.chdir(self.dest_dir)
        fw_succ2.run_task(fw_spec={})

        self.assertListEqual(os.listdir(self.dest_dir), os.listdir(self.src_dir))

    def test_file_transfer_from_previous(self):
        fw1 = VaspTransferTask(mode='copy', 
                        files=['test_f1.txt', 'test_f2.txt'], 
                        ignore_errors=False)
        os.chdir(self.dest_dir)
        fw1.run_task(fw_spec= {'PREV_DIR': self.src_dir})

        self.assertListEqual(os.listdir(self.dest_dir), os.listdir(self.src_dir))
        

if __name__ == '__main__':
    unittest.main()
<file_sep>Change log
==========

v3.3.4
------
* Procar now supports parsing of phase factors.
* Miscellaneous bug fixes.
<file_sep>#!/usr/bin/env python

import os
import sys
import string
import json

from pymatgen import Structure
from fireworks import Firework, Workflow, LaunchPad
from pymatgen.io.vasp.interfaces import VaspInputInterface, VaspFirework, VaspWorkflow
from fireworks.user_objects.firetasks.script_task import ScriptTask


# get structure for use in VASP input
material='Si_example.cif'
s=Structure.from_file(material)
pf=s.formula

material_file_name="{}_tddft.yaml".format(pf)
mname="{}_tddft".format(pf)
print "creating VASP jobs specifications in ",material_file_name


# and make it dependent on the number of cores
ncores=512
kpar_dft=32
kpar_chi=32
ncore_per_kpoint_dft=ncores/kpar_dft
ncore_per_kpoint_chi=ncores/kpar_chi
remainder=ncore_per_kpoint_dft%ncore_per_kpoint_chi

fw1_task1=ScriptTask.from_str("pwd")
fw1=VaspFirework(fw1_task1,name=mname)

# standard DFT first
vasp1=VaspInputInterface(s)
nelect=vasp1.get_nelect()
nbands=nelect*3/2
remainder=nbands%ncore_per_kpoint_dft
add=ncore_per_kpoint_dft-remainder
nbands=int(nbands+add)
vasp1.ALGO='Normal'
vasp1.KPAR=kpar_dft
vasp1.NBANDS=nbands
vasp1.NEDOS=4096
vasp1.kpts_style='Gamma'
vasp1.kpts=[6,6,6]
vasp1.kpts_shift=[0,0,0]

fw1.add_task(vasp1)
fw1.add_handler('FrozenJobErrorHandler')

fw1_task2=ScriptTask.from_str("pwd; mkdir OrbDir")
fw1_task3=ScriptTask.from_str("mv CHG* CONTCAR DOSCAR EIGENVAL I* K* OSZICAR OUTCAR P* W* X* vasp* OrbDir")
fw1_task4=ScriptTask.from_str("cp OrbDir/WAVECAR . ; cp OrbDir/CONTCAR .")
fw1.add_task(fw1_task2)
fw1.add_task(fw1_task3)
fw1.add_task(fw1_task4)

# create VASP IPA LOPTICS input job yaml file
vasp2=VaspInputInterface('CONTCAR')
vasp2.ISTART=1
vasp2.ALGO='Normal'
vasp2.LHFCALC='.TRUE.'
vasp2.LOPTICS='True'
vasp2.CSHIFT=0.1
vasp2.ISTART=1
vasp2.HFSCREEN=0.2
vasp2.NEDOS=4096
vasp2.KPAR=kpar_dft
vasp2.NBANDS=nbands
vasp2.kpts=[6,6,6]
vasp2.kpts_shift=[0,0,0]
vasp2.kpts_style='Gamma'
vasp2.TIME="0.4"
vasp2.PRECFOCK="Fast"
vasp2.NKRED=3

fw1.add_task(vasp2)

# Create VASP LFE Optics input.  This is for a new FireWorker
vasp3=VaspInputInterface('CONTCAR')
vasp3.ALGO='Chi'
vasp3.LHFCALC='.TRUE.'
vasp3.HFSCREEN=0.2
vasp3.LSPECTRAL='False'
vasp3.NEDOS=4096
vasp3.NOMEGA=1024
vasp3.NBANDS=nbands
vasp3.LOPTICS='False'
vasp3.LRPA='.FALSE.'
vasp3.LFXC='True'
vasp3.NKREDLFX=3
vasp3.NKREDLFY=3
vasp3.NKREDLFZ=3
vasp3.KPAR=kpar_chi
vasp3.kpts_style='Gamma'
vasp3.kpts=[6,6,6]
vasp3.kpts_shift=[0,0,0]

fw2=VaspFirework(vasp3, name=mname)
fw2.copy_files_from_previous('WAVECAR', 'WAVEDER', mode='copy', ignore_errors=True)


# Make Sequential Workflow
#wf1 = VASPWorkflow(fw1,fw2,fw3, name=material_file_name.split('.')[0])
wf=VaspWorkflow(fw1,fw2,name=material_file_name.split('.')[0])
wf.to_file(material_file_name)

# save specification to yaml file for later inspection
# or manual add to lauchpad with lpad script
#wf.add_wf_to_launchpad()

