def perform_workflow(self, from_scratch=False):
        #TODO: Make the directory structure more organic (avoid duplicate
        # "initial_structure.vasp" files e.g.)
        ##relaxation_dir = os.path.join(self.run_location, 'acc_std_relax')
        relaxation_dir = os.path.join(self.run_location)
        ### if from_scratch, delete the relaxation folder, if it exists
        ##if from_scratch and os.path.isdir(relaxation_dir):
        ##    shutil.rmtree(relaxation_dir)
        ### create a "relaxation" folder, if one doesn't already exist
        ##os.makedirs(relaxation_dir, exist_ok=True)
        relaxation_settings = DEFAULT_VASP_INCAR_SETTINGS['acc_std_relax']
        relaxation_settings.update(self.custom_calculation_settings.get('acc_std_relax', {}))
        initial_structure = self.initial_structure
        with files_and_folders.change_working_dir(relaxation_dir):
            if not from_scratch:
                previous_outcar = os.path.join(relaxation_dir, 'OUTCAR')
                previous_contcar = os.path.join(relaxation_dir, 'CONTCAR')
                previous_poscar = os.path.join(relaxation_dir, 'POSCAR')
                if os.path.isfile(previous_outcar) and os.path.getsize(previous_outcar):
                    files_and_folders.backup_files()
                if os.path.isfile(previous_contcar) and os.path.getsize(previous_contcar):
                    initial_structure = io.read_poscar(previous_contcar)
                elif os.path.isfile(previous_poscar) and os.path.getsize(previous_poscar):
                    initial_structure = io.read_poscar(previous_poscar)
            vcd, converged = self.do_relaxation(structure=initial_structure,
                                                settings=relaxation_settings,
                                                mpi_call=self.mpi_call,
                                                **self.kwargs)
        with open('acc_std_relax_data.json', 'w') as fw:
            json.dump(vcd.as_dict(), fw, indent=2)

        if not converged:
            msg = 'Error while performing the relaxation run(s)'
            raise KelpieWorkflowError(msg)
    def perform_workflow(self, from_scratch=False):
        relaxation_dir = os.path.join(self.run_location, 'relaxation')
        # if from_scratch, delete the "relaxation" folder, if it exists
        if from_scratch and os.path.isdir(relaxation_dir):
            shutil.rmtree(relaxation_dir)
        # create a "relaxation" folder, if one doesn't already exist
        os.makedirs(relaxation_dir, exist_ok=True)
        relaxation_settings = DEFAULT_VASP_INCAR_SETTINGS['relaxation']
        relaxation_settings.update(self.custom_calculation_settings.get('relaxation', {}))
        initial_structure = self.initial_structure
        with files_and_folders.change_working_dir(relaxation_dir):
            if not from_scratch:
                previous_outcar = os.path.join(relaxation_dir, 'OUTCAR')
                previous_contcar = os.path.join(relaxation_dir, 'CONTCAR')
                previous_poscar = os.path.join(relaxation_dir, 'POSCAR')
                if os.path.isfile(previous_outcar) and os.path.getsize(previous_outcar):
                    files_and_folders.backup_files()
                if os.path.isfile(previous_contcar) and os.path.getsize(previous_contcar):
                    initial_structure = io.read_poscar(previous_contcar)
                elif os.path.isfile(previous_poscar) and os.path.getsize(previous_poscar):
                    initial_structure = io.read_poscar(previous_poscar)
            vcd, converged = self.do_relaxation(structure=initial_structure,
                                                settings=relaxation_settings,
                                                mpi_call=self.mpi_call,
                                                **self.kwargs)
        with open('relaxation_data.json', 'w') as fw:
            json.dump(vcd.as_dict(), fw, indent=2)

        if not converged:
            msg = 'Error while performing the relaxation run(s)'
            raise KelpieWorkflowError(msg)

        relaxation_output_structure = os.path.join(relaxation_dir, 'CONTCAR')
        initial_structure = io.read_poscar(relaxation_output_structure)
        static_dir = os.path.join(self.run_location, 'static')
        os.makedirs(static_dir, exist_ok=True)
        static_settings = DEFAULT_VASP_INCAR_SETTINGS['static']
        static_settings.update(self.custom_calculation_settings.get('static', {}))
        files_and_folders.copy_files(src_folder='relaxation',
                                     dest_folder='static',
                                     list_of_filenames=['CHGCAR'])
        with files_and_folders.change_working_dir(static_dir):
            vcd, converged = self.do_static(structure=initial_structure,
                                            settings=static_settings,
                                            mpi_call=self.mpi_call,
                                            **self.kwargs)
        with open('static_data.json', 'w') as fw:
            json.dump(vcd.as_dict(), fw, indent=2)

        if not converged:
            msg = 'Error while performing the final static run'
            raise KelpieWorkflowError(msg)
    def do_static(self, structure=None, settings=None, mpi_call=None, **kwargs):
        # propagate "n_attempts" to keep track of the number of VASP runs
        # if this is the first do_relaxation() call, initialize "n_attempts"
        if not hasattr(kwargs, 'n_attempts'):
            kwargs['n_attempts'] = 0

        # write input files with the given structure and settings
        ig = VaspInputGenerator(structure=structure,
                                calculation_settings=settings,
                                **kwargs)
        ig.write_vasp_input_files()
        # use the MPI call specified to run VASP
        vasp_process = self.run_vasp(mpi_call)
        # did the VASP run OK?
        if vasp_process.returncode != 0:
            msg = 'Something went wrong with the MPI VASP run'
            raise KelpieWorkflowError(msg)
        # increase "n_attempts": the number of VASP runs
        kwargs['n_attempts'] += 1
        # parse VASP output files to check for convergence
        vcd = VaspCalculationData(vasprunxml_file='vasprun.xml',
                                  vasp_outcar_file='OUTCAR')
        converged = vcd.is_fully_converged(scf_thresh=settings.get('ediff'))
        # if the calculation has converged or the maximum number of attempts
        # has been reached, return the most recent calculation data
        max_nattempts = kwargs.get('max_nattempts', 2)
        if converged or kwargs['n_attempts'] >= max_nattempts:
            return vcd, converged
        # if not try to fix any errors and/or convergence issues
        # get all runtime errors reported by VASP
        with open('stdout.txt', 'r') as fr:
            vasp_stdout = fr.read()
        vasp_errors = self.get_vasp_errors(vasp_stdout=vasp_stdout)
        if not vcd.is_number_of_bands_converged():
            vasp_errors.add('bands')
        # try to address any errors encountered above
        # except when VASP didn't run at all: request user intervention
        if any([e in vasp_errors for e in ['empty_stdout', 'input_error']]):
            msg = 'Error running VASP. Problem with the INCAR?'
            raise KelpieWorkflowError(msg)
        new_sett = self.address_vasp_errors(errors=vasp_errors,
                                            current_sett=settings,
                                            calc_data=vcd)
        settings.update(new_sett)
        # restart the static calculation
        files_and_folders.backup_files()
        return self.do_static(structure=structure,
                              settings=settings,
                              mpi_call=mpi_call,
                              **kwargs)
    def perform_workflow(self, from_scratch=False):
        calc_dir = os.path.join(self.run_location)
        calc_settings = DEFAULT_VASP_INCAR_SETTINGS['sc_forces']
        calc_settings.update(self.custom_calculation_settings.get('sc_forces', {}))
        initial_structure = self.initial_structure
        with files_and_folders.change_working_dir(calc_dir):
            if not from_scratch:
                previous_outcar = os.path.join(calc_dir, 'OUTCAR')
                if os.path.isfile(previous_outcar) and os.path.getsize(previous_outcar):
                    files_and_folders.backup_files()
            vcd, converged = self.do_static(structure=initial_structure,
                                            settings=calc_settings,
                                            mpi_call=self.mpi_call,
                                            **self.kwargs)
        with open('sc_forces_data.json', 'w') as fw:
            json.dump(vcd.as_dict(), fw, indent=2)

        if not converged:
            msg = 'Error during (scf) calculation of forces'
            raise KelpieWorkflowError(msg)