示例#1
0
    def calc_aim_cubes(self,
                       iseed,
                       elements,
                       ijob_dir,
                       result_dir='cube_files',
                       gzip=True,
                       backup_existing=True,
                       purge_existing=True,
                       version=1,
                       verbose=False):
        """
        Function that calculations a cube file from a CASTEP run. Note that
        this routine does not prepare any input files, you should use
        "prepare_continuation_calculation()" for this purpose!

        Parameters
        ----------
        ''iseed''
            string
            Common seed for the cube calculation.

        ''elements''
            list of strings
            Elements for which to calculate the Hirshfeld decomposition.

        ''ijob_dir''
            string
            Working directory, i.e. the directory where the input has been
            prepared. Use the "prepare_continuation_calculation()" method for preparation!

        ''result_dir''
            string (default = 'cube_files')
            Directory into which the output files are moved. It will always be
            a subdirectory of 'ijob_dir' in the sense of
            <ijob_dir>/<result_dir>. So do not pass absolute paths here.

        ''gzip''
            boolean, optional (default = True)
            Determines whether the output files are zipped or not.

        ''backup_existing''
            boolean, optional (default = True)
            Flag that is directly passed to the "mkdir()" routine of rtools.
            See documentation there.

        ''purge_existing''
            boolean, optional (default = True)
            Flag that is directly passed to the "mkdir()" routine of rtools.
            See documentation there.

        ''version''
            integer, optional (default = 1)
            Which version of the castep_Hirshfeld binary is used. The original
            JM binary is version 1, the extended binary by SPR is version 2.
            Version 2 does not require an additional castep2cube run and avoids
            output of some unnecessary files.

        ''verbose''
            boolean, optional (default = False)
            Print some more information to stdout.

        Returns
        -------
        None
        """
        origin_dir = os.getcwd()
        ijob_dir = os.path.abspath(ijob_dir)
        result_dir = os.path.join(ijob_dir, result_dir)

        # check if all files are there...
        requirements = [
            '{}.{}'.format(iseed, suffix) for suffix in ('cell', 'param')
        ]
        self._check_requirements(requirements, ijob_dir)

        # make sure there is a proper result directory
        mkdir(result_dir,
              backup_existing=backup_existing,
              purge_existing=purge_existing,
              verbose=verbose)

        if verbose:
            print('Running LDFA-AIM calculation for seed: {}'.format(iseed))
            print('\tJob folder    : {}'.format(ijob_dir))
            print('\tResult folder : {}'.format(result_dir))

        if version < 2:
            # Do the cube calculation first
            self.calc_cube(
                iseed=iseed,
                ijob_dir=ijob_dir,
                # result dir stuff will be done by this routine
                result_dir='',
                # we will do the gzipping afterwards
                gzip=False,
                # we already initialized
                init=False,
                verbose=verbose)
        # change to working directory
        os.chdir(ijob_dir)

        # link the binary only if it is not in path...
        castep_hirshfeld_bin = self._link_binary(self.castep_hirshfeld_bin,
                                                 verbose=verbose)

        # run the Hirshfeld decomposition
        castep_hirshfeld_str = r'{0} {1}'.format(castep_hirshfeld_bin, iseed)

        if verbose:
            print('Running Hirshfeld decomposition')
            print('\t' + castep_hirshfeld_str)

        shell_stdouterr(castep_hirshfeld_str)

        # find unneeded elements automatically
        atoms = read_cell(iseed + '.cell')
        delete_elements = set(
            [a.symbol for a in atoms if a.symbol not in elements])

        delete_pattern = '{}'.format('|'.join(list(delete_elements)))

        # deleting unnecessary output
        if verbose:
            print('Removing unnecessary output files:')
        for f in os.listdir('.'):
            pattern = r'Hirshfeld_rho_ba.*' + format(delete_pattern)
            if re.search(pattern, f) or re.search(r'Hirshfeld_w-',
                                                  f) or re.search(r'\.err', f):
                if verbose:
                    print('\t{}'.format(f))
                os.remove(f)

        # renaming remaining output files
        if verbose:
            print('Renaming remaining output files (just for convenience):')
        for e in elements:
            for f in os.listdir('.'):
                # we want to get rid of the 'ns' flag
                pattern = r'(.*)(-Hirshfeld_rho_ba)-ns_[0-9]+(.*)'
                search_obj = re.search(pattern, f)
                if search_obj:
                    old = search_obj.group()
                    new = ''.join(search_obj.groups())
                    if verbose:
                        print('\t{} --> {}'.format(old, new))
                    os.rename(old, new)

        # get the difference density cube file
        cube_subtract_bin = self._link_binary(self.cube_subtract_bin,
                                              verbose=verbose)

        if verbose:
            print('Calculating density differences')

        r = re.compile(r'.*-chargeden\.cube')
        interacting_density = filter(r.match, os.listdir('.'))[-1]

        r = re.compile(r'.*Hirshfeld_rho_ba.*')
        hirshfeld_densities = filter(r.match, os.listdir('.'))

        for hirsh in hirshfeld_densities:
            cube_subtract_str = r'{0} {1} {2}'.format(cube_subtract_bin,
                                                      interacting_density,
                                                      hirsh)
            if verbose:
                print('\t' + cube_subtract_str)

            shell_stdouterr(cube_subtract_str)

            # get the species identifyer
            pattern = r'.*-Hirshfeld_rho_ba-(.*)'
            ispec = re.match(pattern, hirsh).groups()[-1]

            # rename the resulting file
            os.rename(interacting_density + '-minus-' + hirsh,
                      iseed + '-{}-'.format(self._prefix) + ispec)

        if gzip:
            if verbose:
                print('Gzipping results')
            for f in os.listdir('.'):
                if re.search(r'\.cube.*', f):
                    gzip_file(f)
                    if verbose:
                        print('\t' + f)

        if not os.path.samefile(ijob_dir, result_dir):
            if verbose:
                print('Moving results to resultfolder')
            for f in os.listdir('.'):
                if re.search(r'\.cube.*', f):
                    shutil.move(f, result_dir)

        # change back to originfolder
        os.chdir(origin_dir)
示例#2
0
    def _prepare(
        self,
        task,
        var,
        # just a dummy, no longe required with latest ase
        pspot_suffix='OTF',
    ):
        """
        Function that wraps all necessary tasks to run several calculations.
        Does not actually run the calculation.

        Arguments
        ---------
        ''task''
            string
            Actual task to carry out. Will be filtered via `_normalize_task()`.
            Currently available since implemented are
                * 'kpoints'
                * 'cutoff'
                * 'vacuum' / 'vacuumwithadsorbate'

        ''var''
            str/generic
            Values that the varied parameter should correspond to. For
            `cutoff` and `vacuum`, these may be floats or integers, for
            `kpoints` we require strings like 'X Y Z', which are directly
            passed to the `KPOINTS_MP_GRID` variable in the `*.cell` file.

        ''pspot_suffix''
            string, optional (default = 'OTF')
            Usually CASTEP USPP are named like <Elem>_<pspot_suffix>.usp. This
            suffix ensures to correctly create the cell file.


        Returns
        -------
        ''atoms''
            readily prepared atoms object.

        ''iseed''
            The iseed of the prepared calculation

        ''idir''
            The idir of the prepared calculation.
        """
        # get the indivial variables
        iseed = self.get_iseed(var=var, task=task)
        idir = self.get_idir(var=var, task=task)

        # create folder if necessary
        # if results exist: skip!
        if mkdir(idir,
                 backup_existing=False,
                 purge_existing=False,
                 verbose=False):
            pass
        else:
            return [None] * 3

        # get the calculator and set label and dir
        calc = self.get_calc()
        calc._label = iseed
        calc._directory = idir

        # get the atoms
        atoms = self.get_atoms()

        if task == 'kpoints':
            # set the k point mesh
            calc.cell.kpoints_mp_grid = var

        elif task == 'kpointspacing':
            calc.cell.kpoints_mp_spacing = float(var)

        elif task == 'cutoff':
            # set the cutoff
            calc.cut_off_energy = int(var)

        elif task == 'vacuum':
            # resize the separating distance
            atoms.center(vacuum=var / 2., axis=2)

        elif task == 'vacuumwithadsorbate':
            atoms = self._get_slab()
            atoms.center(vacuum=var / 2., axis=2)
            self._add_adsorbate(atoms)

        # set the calculator
        atoms.set_calculator(calc)

        # we need it hard code it here...
        # calc.set_pspot(pspot = pspot_suffix)

        atoms.calc.prepare_input_files()

        return atoms, iseed, idir
示例#3
0
    def calc_cube(self, iseed,
                        ijob_dir, 
                        result_dir = 'cube_files', 
                        gzip = True, 
                        backup_existing = True, 
                        purge_existing = True,
                        init = True,
                        verbose = False):
        """
        Function that calculations a cube file from a CASTEP run. Note that
        this routine does not prepare any input files, you should use
        "prepare_continuation_calculation()" for this purpose!  
        
        Parameters
        ----------
        ''iseed''
            string
            Common seed for the cube calculation.
        
        ''ijob_dir''
            string
            Working directory, i.e. the directory where the input has been
            prepared. Use the "prepare_continuation_calculation()" method for preparation!
        
        ''result_dir''
            string (default = 'cube_files')
            Directory into which the output files are moved. It will always be
            a subdirectory of 'ijob_dir' in the sense of
            <ijob_dir>/<result_dir>. So do not pass absolute paths here.
        
        ''gzip''
            boolean, optional (default = True)
            Determines whether the output files are zipped or not.                  
        
        ''backup_existing'' 
            boolean, optional (default = True) 
            Flag that is directly passed to the "mkdir()" routine of rtools.
            See documentation there.

        ''purge_existing''
            boolean, optional (default = True)
            Flag that is directly passed to the "mkdir()" routine of rtools.
            See documentation there.
        
        ''init''
            boolean, optional (default = True)
            Check for requirements and create directories if necessary. Turn
            of, if you want to avoid double checking when using this routine in
            other routines.
        
        ''verbose''
            boolean, optional (default = False)
            Print some more information to stdout.
        
        
        Returns
        -------
        None
        """
        
        origin_dir  = os.getcwd()
        ijob_dir = os.path.abspath(ijob_dir)
        result_dir  = os.path.join(ijob_dir, result_dir)
            
            
        if init: 
            # check if all files are there...
            requirements = ['{}.{}'.format(iseed, suffix) for suffix in ('cell','param')] 
            self._check_requirements(requirements, ijob_dir)
            
            # make sure there is a proper result directory
            mkdir(result_dir, backup_existing = backup_existing, 
                              purge_existing = purge_existing, 
                              verbose = verbose) 
        if verbose: 
            print('Running cube calculation for seed: {}'.format(iseed))
            print('\tJob folder    : {}'.format(ijob_dir))
            print('\tResult folder : {}'.format(result_dir))
        
        # change to working directory
        os.chdir(ijob_dir)
        
        castep2cube_bin = self._link_binary(self.castep2cube_bin,
                                            verbose = verbose)
        
        castep2cube_str = r'{0} {1}'.format(castep2cube_bin, iseed)   
            
        if verbose:
            print('Running castep2cube:')
            print('\t' + castep2cube_str)
        
        shell_stdouterr(castep2cube_str) 
        
        # rename output 
        os.rename(iseed+'.chargeden_cube', iseed+'-chargeden.cube')
        
        # remove all unnecessary files
        if verbose:
            print('Removing unnecessary output files:')
        for f in os.listdir('.'):
            if re.search(r'_xsf_|_esp_|chdiff_cube|\.err', f):
                if verbose:
                    print('\t{}'.format(f))
                os.remove(f)
        
        outfile = iseed+'-chargeden.cube'
        
        if gzip:
            if verbose:
                print('Gzipping results')
            gzip_file(outfile)
            outfile += '.gz'
        

        if not os.path.samefile(ijob_dir, result_dir):
            if verbose:
                print('Moving results to resultfolder')
            shutil.move(outfile, result_dir)
        
        os.chdir(origin_dir)
示例#4
0
    def _calculate(self,
                   points,
                   pspot_suffix='OTF',
                   submit_func=None,
                   force_resubmit_empty=False,
                   force_resubmit_all=False,
                   verbose=False,
                   try_reuse_previous=False,
                   **kwargs):
        """
        Function that wraps all necessary tasks to run several calculations.

        This function is generic in the sense, that the actual submit function
        may be changed.

        Arguments
        ---------
        ''submit_func'' : rtools submit agent function
            Function that does all the PBS related communication. See the
            submitagents provided with rtools for more details.

        ''points''
            list
            List of values that the varied parameter should correspond to.

        ''pspot_suffix''
            string, optional (default = 'OTF')
            Usually CASTEP USPP are named like <Elem>_<pspot_suffix>.usp. This
            suffix ensures to correctly create the cell file.

        ''force_resubmit_empty''
            boolean, optional (default = False)
            Resubmit if the results folder is empty. Be careful, your job may
            still be running.

        ''force_resubmit_all''
            boolean, optional (default = False)
            Resubmit regardless of whether there are any existing files or
            folders.

        ''verbose''
            boolean, optional (default=False)
            Print "Job X / Y" before submitting/calculating.

        ''try_reuse_previous''
            boolean, optional (default=False)
            Try to fetch the check file of the previous run and use it to speed
            up the next simulation thanks to density extrapolation. Note that
            this option should *never* be used togehter with a cluster and thus
            distributed computations.

        ''**kwargs''
            are directly passed to the `submit_func()` function.

        Returns
        -------
        None
        """
        njobs = len(points)
        nsubmitted = 0
        nskipped = 0
        nprocessed = 0

        print(self._lim)
        print('Requested {} jobs in total'.format(njobs))
        print(self._lim)

        _prev_point = None

        for point in points:
            if verbose:
                info = '| Job {{:{0}d}} / {{:{0}d}} |'.format(len(
                    str(njobs))).format(nprocessed + 1, njobs)
                lim = '+' + '-' * (len(info) - 2) + '+'
                print(lim)
                print(info)
                print(lim)

            # get the indivial variables
            iseed = self.get_iseed(point)
            idir = self.get_idir(point)
            idir_export = self.get_idir_export(point)

            # create folder if necessary
            if mkdir(idir,
                     backup_existing=False,
                     purge_existing=False,
                     verbose=False):
                pass

            # "results" is hard coded, see below

            elif os.path.exists(os.path.join(idir, 'results')) and os.listdir(
                    os.path.join(idir, 'results')) and force_resubmit_empty:
                print(
                    'Resubmitting job "{}" due to empty result folder. Existing files are not backed up.'
                    .format(iseed))
                pass

            elif not force_resubmit_all:
                # folder already exists...
                print('Skipping job "{}" due to existing files'.format(iseed))
                nskipped += 1
                nprocessed += 1
                _prev_point = point
                continue

            # get the calculator and set label and dir
            calc = self.get_calc()
            calc._label = iseed
            calc._directory = idir

            # get the atoms
            atoms = self.get_atoms(point)

            # set the calculator
            atoms.set_calculator(calc)

            # we need it hard coded here...
            atoms.calc.set_pspot(pspot=pspot_suffix)

            if nprocessed > 0 and try_reuse_previous:
                _prev_iseed = self.get_iseed(_prev_point)
                _prev_idir = self.get_idir(_prev_point)
                _prev_idir_export = self.get_idir_export(_prev_point)

                # try to fetch a check file (either on /data or on the /export
                # partition; we do not know as this is determined by the submit
                # function)
                locations = [
                    os.path.join(_prev_idir, 'results',
                                 _prev_iseed + '.check'),
                    os.path.join(_prev_idir_export, _prev_iseed + '.check'),
                ]

                _prev_icheck = None
                for f in locations:
                    if os.path.exists(f):
                        _prev_icheck = f
                        break

                if _prev_icheck:
                    if verbose:
                        print(
                            'Info: Fetched previous checkfile -- will be reused'
                            .format(_prev_icheck))

                    # Symlink the check file (makes life easier...)
                    os.symlink(_prev_icheck,
                               os.path.join(idir, _prev_iseed + '.check'))

                    atoms.calc.param.reuse = _prev_iseed + '.check'
                else:
                    if verbose:
                        print(
                            'Info: Unable to fetch previous checkfile -- start from scratch'
                        )

            # prepare input files and submit
            atoms.calc.prepare_input_files()

            # make sure that user does not override the result default
            result_dir = kwargs.pop('result_dir', None)
            if result_dir:
                print(
                    'Argument "result_dir" was set to "{}"'.format(result_dir))
                print(
                    'Will be changed to "<job_dir>/results" to maintain compatibility with reading routines'
                )

            submit_func(seed=iseed,
                        job_dir=idir,
                        result_dir='results',
                        export_dir=idir_export,
                        **kwargs)

            nprocessed += 1
            nsubmitted += 1

            # this is for the reuse feature
            _prev_point = point
        print(self._lim)
        print('Submitted : {0:>4d} / {1:d} jobs'.format(nsubmitted, njobs))
        print('Skipped   : {0:>4d} / {1:d} jobs'.format(nskipped, njobs))
        print(self._lim)
示例#5
0
    def write_ascii(self, node='PES',
                          observable='energy_normalized',
                          fname='PES.dat',
                          comment='no unit info provided',
                          include_points=True,
                          **kwargs):

        """
        Routine to actually dump things from the database to a clear-text ascii
        file.

        Parameters
        ----------
        ''node''
            string, optional (default = 'PES')
            The node that is to be considered. It will be '/analysis/<node>',
            as we do not want raw data dumping to clear text. This is what the
            HDF5 database is for.

        ''observable''
            string/list of strings, optional (default = 'energy_normalized')
            The column in the dataframe that is printed next to the respective
            coordinates. Can also be a list of strings for several output
            quantities.

        ''fname''
            string, optional (default = 'PES.dat')
            The filename to be written to. You can specify an entire path as
            well, folders will be created on demand.

        ''comment''
            string, optional (default = 'no unit info provided')
            Some string that comments on the data in the file. This is up to
            the user.

        ''include_points''
            boolean, optional (default=True)
            Include the point identifyer, even if not specified as observable
            (fallback mode)

        **kwargs
            Passed to the dataframe.to_string() method. Allows more finegrained
            control on the format.

        Returns
        -------
        <None>
        """
        # check if we need to create the directory
        dirname = os.path.dirname(fname)
        if dirname:
            mkdir(dirname, backup_existing = False,
                           purge_existing = False,
                           verbose = False)

        df = self.store['/analysis/{}'.format(node)]

        if not isinstance(observable, list):
            observable = [observable]

        if include_points:
            idx = self.point_names + observable
        else:
            idx = observable

        print('Writing data to ascii file : {}'.format(fname))
        print('\tObservable(s):\n\t* {}'.format('\n\t* '.join(observable)))
        with open(fname, 'w') as f:
            f.write('# {}'.format(f.name))
            f.write('\n# file written on: {}'.format(time.strftime('%c')))
            if comment:
                f.write('\n#')
                comment = comment.split('\n')
                for c in comment:
                    for line in textwrap.wrap(c, width=78):
                        f.write('\n# {}'.format(line))

            data='#' + df[idx].to_string(index=False, **kwargs)[1::]
            f.write('\n#\n#\n{}'.format(data))
示例#6
0
    def prepare_continuation_calculation(self,
                                         DFT_idir,
                                         ijob_dir,
                                         iseed=None,
                                         DFT_iseed=None,
                                         backup_existing=True,
                                         verbose=False):
        """
        Function that prepares a CASTEP continuation calculation.  The
        function creates a <iseed>.cell and <iseed>.param file in <ijob_dir> from
        a given SCF calculation in <DFT_idir> using the ase CASTEP calculator.
        The latter will be appended a "continuation : <checkfile>" and possibly
        existing "reuse" statements are remove due to conflicting options.

        Parameters
        ----------
        ''DFTdir''
            string
            Path to the output of the CASTEP SCF calculation (*including in
            particular the .check file*), on which the continuation calcultion
            shall be based on.

        ''ijob_dir''
            string
            Path where to prepare the continuation calculation.

        ''iseed''
            string, optional (default = None)
            Seed for the new files in 'ijob_dir'. If None, the iseed from the DFT
            calculations will be re-used.

        ''DFT_iseed''
            string (default = None)
            CASTEP iseed. If not specified, the routine will glob using a
            wildcard.  In this case, you should make sure that there is
            actually only one .cell, .param, and .check file in 'DFT_idir'.

        ''backup_existing''
            boolean, optional (default = True)
            Flag that is directly passed to the "mkdir()" routine of rtools.
            Note that "purge_existing" is always <True>. *This means that your
            files will be irrevesibly gone if you do not set
            "backup_existing = True".

        ''verbose''
            boolean, optional (default = False)
            Print some more information to stdout.

        Returns
        -------
        None
        """

        # make sure we have absolute path names
        ijob_dir = os.path.abspath(ijob_dir)
        DFT_idir = os.path.abspath(DFT_idir)

        # holds all DFT names
        DFT_info = self._get_DFT_infos(DFT_idir=DFT_idir, DFT_iseed=DFT_iseed)

        # use the same iseed as the DFT calculation if none is given
        if iseed == None:
            iseed = DFT_info['iseed']

        if verbose:
            print('Preparing calculation for seed: {}'.format(iseed))
            print('\tSource     : {}'.format(DFT_idir))
            print('\tJob folder : {}'.format(ijob_dir))

        # create the folder if not already there
        mkdir(ijob_dir,
              backup_existing=backup_existing,
              purge_existing=True,
              verbose=verbose)

        self._prepare_castep_files(DFT_info,
                                   iseed=iseed,
                                   ijob_dir=ijob_dir,
                                   verbose=verbose)

        return None