Example #1
0
def structure_from_file(structure_file):
    """
    Attempts to reconstruct a PyChemia Structure from the contents of any given file. Valid entries

    :param structure_file: The path to a file where the structure can be reconstructed
    :type structure_file: str
    :return: PyChemia Structure if succeed, None otherwise
    """
    st = None
    basename = os.path.basename(structure_file)
    if not os.path.isfile(structure_file):
        raise ValueError("ERROR: Could not open file '%s'" % structure_file)
    if basename[-4:].lower() == 'json':
        st = Structure.load_json(structure_file)
    elif basename[-3:].lower() == 'cif' and HAS_PYMATGEN:
        import pychemia.external.pymatgen
        st = pychemia.external.pymatgen.cif2structure(structure_file)[0]
    elif 'poscar' in basename.lower():
        st = read_poscar(structure_file)
    elif 'contcar' in basename.lower():
        st = read_poscar(structure_file)
    elif 'abinit' in basename.lower():
        av = AbinitInput(structure_file)
        st = av.get_structure()
    else:
        try:
            st = read_poscar(structure_file)
        except ValueError:
            raise ValueError('Ćould not convert file as POSCAR')
    if st is None:
        pcm_log.debug("ERROR: Could not extract structure from file '%s'" %
                      structure_file)
    return st
Example #2
0
def structure_from_file(structure_file):
    st = None
    if not os.path.isfile(structure_file):
        print("ERROR: Could not open file '%s'" % structure_file)
        sys.exit(1)
    if structure_file[-4:].lower() == 'json':
        st = Structure.load_json(structure_file)
    elif structure_file[-3:].lower() == 'cif' and HAS_PYMATGEN:
        import pychemia.external.pymatgen
        st = pychemia.external.pymatgen.cif2structure(structure_file)[0]
    elif structure_file[-6:].lower() == 'poscar':
        st = read_poscar(structure_file)
    elif structure_file[-6:].lower() == 'contcar':
        st = read_poscar(structure_file)
    elif structure_file[-6:].lower() == 'abinit.in':
        av = InputVariables(structure_file)
        av.get_structure()
    else:
        try:
            st = read_poscar(structure_file)
        except ValueError:
            print("ERROR: Could not extract structure from file '%s'" %
                  structure_file)
            exit(1)
    return st
    def __init__(self, name, source_dir='.', mag_atoms=None, magmom_magnitude=2.0, distance_tolerance=0.1,
                 incar_extra=None, debug=False):
        """
        This class provides a population of Magnetic Moment vectors for the same structure and was created to be used
        on VASP.
        The magnetic moment is set in cartesian coordinates with 3 numbers for each atom in the unit cell.
        This population provides methods to manipulate the magnetic moments between different candidates in order to
        optimize the magnetic orientations using the global-search methods implemented on PyChemia.

        :param name: The name of the database to be created or directly the PyChemiaDB database object.
                     The name is used when the database can be created without username, password and no encryption.
                     Otherwise the database must be created first and its object be 'name' argument.
        :param source_dir: Directory that contains the basic 4 files for VASP: 'POSCAR', 'POTCAR', 'KPOINTS' and 'INCAR'
                            Except for 'INCAR' the files are linked symbolically on each directory that will run VASP
                            The input variables on 'INCAR' changing only MAGMOM and I_CONSTRAINED_M.
                            The 'INCAR' file could contain generic variables and some other variables could be directly
                            specified using the dictionary 'incar_extra'.
        :param mag_atoms: List of atoms for which the Magnetic Moments are changed. If the variable is None, the list
                            is inferred from the original INCAR file. The numbering of atoms start with 0.
        :param magmom_magnitude: Fix value for the magnitude of the magnetic moment imposed for all the atoms in
                                'mag_atoms' list
        :param distance_tolerance: Maximal distance in magnetic moments to consider two candidates as equivalent.
        :param incar_extra: Extra variables for INCAR file that are added or replaced from the INCAR read with
                            'source_dir'
        :param debug: If True produce a verbose output during the different calls to the methods.
        """
        Population.__init__(self, name, 'global', distance_tolerance=distance_tolerance)
        if not os.path.isfile(source_dir + os.sep + 'INCAR'):
            raise ValueError("INCAR not found")
        if not os.path.isfile(source_dir + os.sep + 'POSCAR'):
            raise ValueError("POSCAR not found")
        if not os.path.isfile(source_dir + os.sep + 'POTCAR'):
            raise ValueError("POTCAR not found")
        if not os.path.isfile(source_dir + os.sep + 'KPOINTS'):
            raise ValueError("KPOINTS not found")
        self.input = read_incar(source_dir + os.sep + 'INCAR')
        self.source_dir = source_dir

        if 'MAGMOM' not in self.input:
            raise ValueError('INCAR should define the MAGMOM variable')
        magmom = np.array(self.input.MAGMOM).reshape((-1, 3))

        self.structure = read_poscar(source_dir + os.sep + 'POSCAR')
        if mag_atoms is None:
            self.mag_atoms = list(np.where(np.apply_along_axis(np.linalg.norm, 1, magmom) > 0.0)[0])
            self.mag_atoms = [int(x) for x in self.mag_atoms]
        else:
            self.mag_atoms = mag_atoms
        self.magmom_magnitude = magmom_magnitude
        if incar_extra is None:
            self.incar_extra = {'IBRION': -1,
                                'LWAVE': True,
                                'LAMBDA': 10,
                                'NSW': 0,
                                'I_CONSTRAINED_M': 1}

        else:
            self.incar_extra = incar_extra
        self.debug = debug
Example #4
0
def structure_from_file(structure_file):
    st = None
    if not os.path.isfile(structure_file):
        print("ERROR: Could not open file '%s'" % structure_file)
        sys.exit(1)
    if structure_file[-4:].lower() == 'json':
        st = Structure.load_json(structure_file)
    elif structure_file[-3:].lower() == 'cif' and HAS_PYMATGEN:
        import pychemia.external.pymatgen
        st = pychemia.external.pymatgen.cif2structure(structure_file)[0]
    elif structure_file[-6:].lower() == 'poscar':
        st = read_poscar(structure_file)
    elif structure_file[-6:].lower() == 'contcar':
        st = read_poscar(structure_file)
    elif structure_file[-6:].lower() == 'abinit.in':
        av = AbinitInput(structure_file)
        av.get_structure()
    else:
        try:
            st = read_poscar(structure_file)
        except ValueError:
            print("ERROR: Could not extract structure from file '%s'" % structure_file)
            exit(1)
    return st
Example #5
0
    def __init__(self, name, source_dir='.', mag_atoms=None, magmom_magnitude=2.0, distance_tolerance=0.1):
        Population.__init__(self, name, 'global')
        if not os.path.isfile(source_dir + os.sep + 'INCAR'):
            raise ValueError("INCAR not found")
        if not os.path.isfile(source_dir + os.sep + 'POSCAR'):
            raise ValueError("POSCAR not found")
        self.input = read_incar(source_dir + os.sep + 'INCAR')
        magmom = np.array(self.input.get_value('MAGMOM')).reshape((-1, 3))

        self.structure = read_poscar(source_dir + os.sep + 'POSCAR')
        if mag_atoms is None:
            self.mag_atoms = list(np.where(np.apply_along_axis(np.linalg.norm, 1, magmom) > 0.0)[0])
            self.mag_atoms = [int(x) for x in self.mag_atoms]
        else:
            self.mag_atoms = mag_atoms
        self.magmom_magnitude = magmom_magnitude
        self.distance_tolerance = distance_tolerance
Example #6
0
    def update(self, workdir):
        """
        This routine determines how to proceed with the relaxation
        for one specific work directory

        :param workdir: (str) String representation of the id in the mongodb
        :return:
        """

        # workdir = self.basedir + os.sep + entry_id
        entry_id = os.path.basename(workdir)
        vj = self.vasp_jobs[entry_id]
        runj = self.runs[entry_id]
        if os.path.isfile(workdir + os.sep + 'OUTCAR'):
            vj.get_outputs()
        self.update_history(entry_id)

        if os.path.isfile(workdir + os.sep + 'RELAXED'):
            self.add_status(entry_id, 'RELAXED')
        elif not os.path.isfile(workdir + os.sep + 'PROCAR'):
            self.add_status(entry_id, 'NOPROCAR')
        else:
            self.del_status(entry_id, 'NOPROCAR')
            if not os.path.isfile(workdir + os.sep + 'OUTCAR'):
                self.add_status(entry_id, 'NOOUTCAR')
            else:
                self.del_status(entry_id, 'NOOUTCAR')
                print('-')
                vo = VaspOutput(workdir + os.sep + 'OUTCAR')
                relaxation_info = vo.relaxation_info()
                if len(relaxation_info) != 3:
                    print('[' + str(entry_id) + ']' +
                          ' Missing some data in OUTCAR (forces or stress)')
                    self.add_status(entry_id, 'NOOUTCAR')

                print('[' + str(entry_id) + ']' + 'Results:')
                for i in relaxation_info:
                    print('[' + str(entry_id) + '] %20s %12.5e' %
                          (i, relaxation_info[i]))

                # Conditions to consider the structure relaxed
                if relaxation_info['avg_force'] < self.target_force:
                    if relaxation_info['avg_stress_diag'] < self.target_stress:
                        if relaxation_info[
                                'avg_stress_non_diag'] < self.target_stress:
                            wf = open(workdir + os.sep + 'RELAXED', 'w')
                            for i in relaxation_info:
                                wf.write("%15s %12.3f" %
                                         (i, relaxation_info[i]))
                            wf.close()
                            wf = open(workdir + os.sep + 'COMPLETE', 'w')
                            for i in relaxation_info:
                                wf.write("%15s %12.3f" %
                                         (i, relaxation_info[i]))
                            wf.close()
                            self.add_status(entry_id, 'RELAXED')

        if self.modify_input(entry_id):

            # How to change ISIF
            if relaxation_info['avg_force'] < 0.1:
                if relaxation_info['avg_stress_diag'] < 0.1:
                    if relaxation_info['avg_stress_non_diag'] < 0.1:
                        vj.input_variables.variables['ISIF'] = 3
                    else:
                        vj.input_variables.variables['ISIF'] = 3
                else:
                    vj.input_variables.variables['ISIF'] = 3
            else:
                vj.input_variables.variables['ISIF'] = 2

            # How to change IBRION
            # if info['avg_force'] < 0.1 and info['avg_stress_diag'] < 0.1 and info['avg_stress_non_diag'] < 0.1:
            #    vj.input_variables.variables['IBRION'] = 1
            # elif info['avg_force'] < 1 and info['avg_stress_diag'] < 1 and info['avg_stress_non_diag'] < 1:
            #    vj.input_variables.variables['IBRION'] = 2
            # else:
            #    vj.input_variables.variables['IBRION'] = 3

            # How to change EDIFF
            if vj.input_variables.variables['EDIFF'] > 2 * 1E-4:
                vj.input_variables.variables['EDIFF'] = round_small(
                    vj.input_variables.variables['EDIFF'] / 2)
            else:
                vj.input_variables.variables['EDIFF'] = 1E-4

            # How to change EDIFFG
            if vj.input_variables.variables['EDIFFG'] < -2 * self.target_force:
                vj.input_variables.variables['EDIFFG'] = round_small(
                    vj.input_variables.variables['EDIFFG'] / 2)
            else:
                vj.input_variables.variables['EDIFFG'] = -self.target_force

            # Print new values
            print('[' + str(entry_id) + ']' + 'New Values:')
            for i in ['ISIF', 'IBRION', 'EDIFF', 'EDIFFG']:
                print('[' + str(entry_id) + ']' + i + ' : ',
                      vj.input_variables.variables[i])
            print('-')

            for i in ['OUTCAR']:
                if not os.path.exists(workdir + os.sep + i):
                    wf = open(workdir + os.sep + i, 'w')
                    wf.write('')
                    wf.close()
                log = logging.handlers.RotatingFileHandler(workdir + os.sep +
                                                           i,
                                                           maxBytes=1,
                                                           backupCount=1000)
                log.doRollover()

            try:
                vj.structure = read_poscar(workdir + os.sep + 'CONTCAR')
            except ValueError:
                print('Error reading CONTCAR')

            vj.set_inputs()
            properties = vj.outcar
            status = self.status[entry_id]
            newentry = self.population.db.update(entry_id,
                                                 structure=vj.structure,
                                                 properties=properties,
                                                 status=status)

            vj.save_json(workdir + os.sep + 'vaspjob.json')
            wf = open(workdir + os.sep + 'entry.json', 'w')
            json.dump(newentry,
                      wf,
                      sort_keys=True,
                      indent=4,
                      separators=(',', ': '))
            wf.close()
            return True
        else:
            vj.set_inputs()
            status = self.status[entry_id]
            newentry = self.population.db.update(entry_id,
                                                 structure=vj.structure,
                                                 status=status)

            vj.save_json(workdir + os.sep + 'vaspjob.json')
            wf = open(workdir + os.sep + 'entry.json', 'w')
            json.dump(newentry,
                      wf,
                      sort_keys=True,
                      indent=4,
                      separators=(',', ': '))
            wf.close()
            return True
Example #7
0
    def update(self, workdir):
        """
        This routine determines how to proceed with the relaxation
        for one specific work directory

        :param workdir: (str) String representation of the id in the mongodb
        :return:
        """

        # workdir = self.basedir + os.sep + entry_id
        entry_id = os.path.basename(workdir)
        vj = self.vasp_jobs[entry_id]
        runj = self.runs[entry_id]
        if os.path.isfile(workdir + os.sep + 'OUTCAR'):
            vj.get_outputs()
        self.update_history(entry_id)

        if os.path.isfile(workdir + os.sep + 'RELAXED'):
            self.add_status(entry_id, 'RELAXED')
        elif not os.path.isfile(workdir + os.sep + 'PROCAR'):
            self.add_status(entry_id, 'NOPROCAR')
        else:
            self.del_status(entry_id, 'NOPROCAR')
            if not os.path.isfile(workdir + os.sep + 'OUTCAR'):
                self.add_status(entry_id, 'NOOUTCAR')
            else:
                self.del_status(entry_id, 'NOOUTCAR')
                print('-')
                vo = VaspOutput(workdir + os.sep + 'OUTCAR')
                relaxation_info = vo.relaxation_info()
                if len(relaxation_info) != 3:
                    print('[' + str(entry_id) + ']' + ' Missing some data in OUTCAR (forces or stress)')
                    self.add_status(entry_id, 'NOOUTCAR')

                print('[' + str(entry_id) + ']' + 'Results:')
                for i in relaxation_info:
                    print('[' + str(entry_id) + '] %20s %12.5e' % (i, relaxation_info[i]))

                # Conditions to consider the structure relaxed
                if relaxation_info['avg_force'] < self.target_force:
                    if relaxation_info['avg_stress_diag'] < self.target_stress:
                        if relaxation_info['avg_stress_non_diag'] < self.target_stress:
                            wf = open(workdir + os.sep + 'RELAXED', 'w')
                            for i in relaxation_info:
                                wf.write("%15s %12.3f" % (i, relaxation_info[i]))
                            wf.close()
                            wf = open(workdir + os.sep + 'COMPLETE', 'w')
                            for i in relaxation_info:
                                wf.write("%15s %12.3f" % (i, relaxation_info[i]))
                            wf.close()
                            self.add_status(entry_id, 'RELAXED')

        if self.modify_input(entry_id):

            # How to change ISIF
            if relaxation_info['avg_force'] < 0.1:
                if relaxation_info['avg_stress_diag'] < 0.1:
                    if relaxation_info['avg_stress_non_diag'] < 0.1:
                        vj.input_variables.variables['ISIF'] = 3
                    else:
                        vj.input_variables.variables['ISIF'] = 3
                else:
                    vj.input_variables.variables['ISIF'] = 3
            else:
                vj.input_variables.variables['ISIF'] = 2

            # How to change IBRION
            # if info['avg_force'] < 0.1 and info['avg_stress_diag'] < 0.1 and info['avg_stress_non_diag'] < 0.1:
            #    vj.input_variables.variables['IBRION'] = 1
            # elif info['avg_force'] < 1 and info['avg_stress_diag'] < 1 and info['avg_stress_non_diag'] < 1:
            #    vj.input_variables.variables['IBRION'] = 2
            # else:
            #    vj.input_variables.variables['IBRION'] = 3

            # How to change EDIFF
            if vj.input_variables.variables['EDIFF'] > 2 * 1E-4:
                vj.input_variables.variables['EDIFF'] = round_small(vj.input_variables.variables['EDIFF'] / 2)
            else:
                vj.input_variables.variables['EDIFF'] = 1E-4

            # How to change EDIFFG
            if vj.input_variables.variables['EDIFFG'] < - 2 * self.target_force:
                vj.input_variables.variables['EDIFFG'] = round_small(vj.input_variables.variables['EDIFFG'] / 2)
            else:
                vj.input_variables.variables['EDIFFG'] = - self.target_force

            # Print new values
            print('[' + str(entry_id) + ']' + 'New Values:')
            for i in ['ISIF', 'IBRION', 'EDIFF', 'EDIFFG']:
                print('[' + str(entry_id) + ']' + i + ' : ', vj.input_variables.variables[i])
            print('-')

            for i in ['OUTCAR']:
                if not os.path.exists(workdir + os.sep + i):
                    wf = open(workdir + os.sep + i, 'w')
                    wf.write('')
                    wf.close()
                log = logging.handlers.RotatingFileHandler(workdir + os.sep + i, maxBytes=1, backupCount=1000)
                log.doRollover()

            try:
                vj.structure = read_poscar(workdir + os.sep + 'CONTCAR')
            except ValueError:
                print('Error reading CONTCAR')

            vj.set_inputs()
            properties = vj.outcar
            status = self.status[entry_id]
            newentry = self.population.db.update(entry_id, structure=vj.structure, properties=properties, status=status)

            vj.save_json(workdir + os.sep + 'vaspjob.json')
            wf = open(workdir + os.sep + 'entry.json', 'w')
            json.dump(newentry, wf, sort_keys=True, indent=4, separators=(',', ': '))
            wf.close()
            return True
        else:
            vj.set_inputs()
            status = self.status[entry_id]
            newentry = self.population.db.update(entry_id, structure=vj.structure, status=status)

            vj.save_json(workdir + os.sep + 'vaspjob.json')
            wf = open(workdir + os.sep + 'entry.json', 'w')
            json.dump(newentry, wf, sort_keys=True, indent=4, separators=(',', ': '))
            wf.close()
            return True
Example #8
0
    def __init__(self,
                 name,
                 source_dir='.',
                 mag_atoms=None,
                 magmom_magnitude=2.0,
                 distance_tolerance=0.1,
                 incar_extra=None,
                 debug=False):
        """
        This class provides a population of Magnetic Moment vectors for the same structure and was created to be used
        on VASP.
        The magnetic moment is set in cartesian coordinates with 3 numbers for each atom in the unit cell.
        This population provides methods to manipulate the magnetic moments between different candidates in order to
        optimize the magnetic orientations using the global-search methods implemented on PyChemia.

        :param name: The name of the database to be created or directly the PyChemiaDB database object.
                     The name is used when the database can be created without username, password and no encryption.
                     Otherwise the database must be created first and its object be 'name' argument.
        :param source_dir: Directory that contains the basic 4 files for VASP: 'POSCAR', 'POTCAR', 'KPOINTS' and 'INCAR'
                            Except for 'INCAR' the files are linked symbolically on each directory that will run VASP
                            The input variables on 'INCAR' changing only MAGMOM and I_CONSTRAINED_M.
                            The 'INCAR' file could contain generic variables and some other variables could be directly
                            specified using the dictionary 'incar_extra'.
        :param mag_atoms: List of atoms for which the Magnetic Moments are changed. If the variable is None, the list
                            is inferred from the original INCAR file. The numbering of atoms start with 0.
        :param magmom_magnitude: Fix value for the magnitude of the magnetic moment imposed for all the atoms in
                                'mag_atoms' list
        :param distance_tolerance: Maximal distance in magnetic moments to consider two candidates as equivalent.
        :param incar_extra: Extra variables for INCAR file that are added or replaced from the INCAR read with
                            'source_dir'
        :param debug: If True produce a verbose output during the different calls to the methods.
        """
        Population.__init__(self,
                            name,
                            'global',
                            distance_tolerance=distance_tolerance)
        if not os.path.isfile(source_dir + os.sep + 'INCAR'):
            raise ValueError("INCAR not found")
        if not os.path.isfile(source_dir + os.sep + 'POSCAR'):
            raise ValueError("POSCAR not found")
        if not os.path.isfile(source_dir + os.sep + 'POTCAR'):
            raise ValueError("POTCAR not found")
        if not os.path.isfile(source_dir + os.sep + 'KPOINTS'):
            raise ValueError("KPOINTS not found")
        self.input = read_incar(source_dir + os.sep + 'INCAR')
        self.source_dir = source_dir

        if 'MAGMOM' not in self.input:
            raise ValueError('INCAR should define the MAGMOM variable')
        magmom = np.array(self.input.MAGMOM).reshape((-1, 3))

        self.structure = read_poscar(source_dir + os.sep + 'POSCAR')
        if mag_atoms is None:
            self.mag_atoms = list(
                np.where(
                    np.apply_along_axis(np.linalg.norm, 1, magmom) > 0.0)[0])
            self.mag_atoms = [int(x) for x in self.mag_atoms]
        else:
            self.mag_atoms = mag_atoms
        self.magmom_magnitude = magmom_magnitude
        if incar_extra is None:
            self.incar_extra = {
                'IBRION': -1,
                'LWAVE': True,
                'LAMBDA': 10,
                'NSW': 0,
                'I_CONSTRAINED_M': 1
            }

        else:
            self.incar_extra = incar_extra
        self.debug = debug
def worker_maise(db_settings, entry_id, workdir, relaxator_params):
    """
    Relax and return evaluate the energy of the structure stored with identifier 'entry_id'
     using the MAISE code

    :param db_settings: (dict) Dictionary of DB parameters needed to create a PyChemiaDB object
    :param entry_id: MongoDB identifier of one entry of the database created from db_settings
    :param workdir: (str) Working directory where input and output from MAISE is written
    :param relaxator_params: (dict) Arguments needed to control the relaxation using MAISE
                                Arguments are store as keys and they include:
                                'target_forces' : Used to defined the tolerance to consider one candidate as relaxed.
                                'source_dir': Directory with executable maise and directory INI
    :return:
    """
    max_ncalls = 6
    pcdb = get_database(db_settings)
    target_forces = relaxator_params['target_forces']
    source_dir = relaxator_params['source_dir']

    pcm_log.info('[%s]: Starting relaxation. Target forces: %7.3e' % (str(entry_id), target_forces))

    if pcdb.is_locked(entry_id):
        return
    else:
        pcdb.lock(entry_id)
    structure = pcdb.get_structure(entry_id)
    status = pcdb.get_dicts(entry_id)[2]

    if 'ncalls' in status and status['ncalls'] > 0:
        ncalls = status['ncalls'] + 1
        print('ncalls = ', status['ncalls'])
    else:
        ncalls = 1
    print('Verifing initial structure...')
    while np.min(structure.distance_matrix()+(np.eye(structure.natom)*5)) < 1.9:
        print('ERROR: Bad initial guess, two atoms are to close. Creating new random structure for id: %s' %
              str(entry_id))
        write_poscar(structure, workdir + os.sep + 'Fail_initial_POSCAR')  # WIH
        structure = Structure.random_cell(structure.composition)

    write_poscar(structure, workdir + os.sep + 'POSCAR')
    if not os.path.exists(workdir + os.sep + 'setup') and ncalls == 1:     # WIH
        print('First run.')  # WIH
        #   print('Verifying that everything runs smoothly') # WIH
        print(workdir + os.sep + 'setup')
        shutil.copy2(source_dir + os.sep + 'setup_1', workdir + os.sep + 'setup')   # WIH
    elif ncalls > 1:  # WIH
        shutil.copy2(source_dir + os.sep + 'setup_2', workdir + os.sep + 'setup')   # WIH
    if not os.path.exists(workdir + os.sep + 'INI'):
        os.symlink(source_dir + os.sep + 'INI', workdir + os.sep + 'INI')
    if not os.path.exists(workdir + os.sep + 'maise'):
        os.symlink(source_dir + os.sep + 'maise', workdir + os.sep + 'maise')

    # Get the Current Working Directory
    # cwd = os.getcwd()

    # Move to the actual directory where maise will run
    os.chdir(workdir)

    wf = open('maise.stdout', 'w')
    subprocess.call(['./maise'], stdout=wf)
    wf.close()

    if os.path.isfile('OSZICAR'):
        energies = np.loadtxt('OSZICAR')
    else:
        energies = None

    forces = None
    stress = None
    stress_kb = None
    if os.path.isfile('OUTCAR'):
        rf = open('OUTCAR', 'r')
        data = rf.read()

        pos_forces = re.findall(r'TOTAL-FORCE \(eV/Angst\)\s*-*\s*([-.\d\s]+)\s+-{2}', data)
        pos_forces = np.array([x.split() for x in pos_forces], dtype=float)

        if len(pos_forces) > 0 and len(pos_forces[-1]) % 7 == 0:
            pos_forces.shape = (len(pos_forces), -1, 7)
            forces = pos_forces[:, :, 3:6]
            # positions = pos_forces[:, :, :3]
        else:
            print('Forces and Positions could not be parsed : ', pos_forces.shape)
            print('pos_forces =\n%s ' % pos_forces)

        str_stress = re.findall('Total([.\d\s-]*)in', data)
        if len(str_stress) == 2:
            stress = np.array([[float(y) for y in x.split()] for x in str_stress])
        str_stress = re.findall('in kB([.\d\s-]*)energy', data)
        if len(str_stress) == 2:
            stress_kb = np.array([[float(y) for y in x.split()] for x in str_stress])

    create_new = False
    if not os.path.isfile('CONTCAR') or os.path.getsize("CONTCAR") == 0:
        create_new = True
        print('CONTCAR not found in entry: %s' % str(entry_id))
        i = 1
        while True:
            if not os.path.isfile('POSCAR-failed-%03s' % str(i)):
                os.rename('POSCAR', 'POSCAR-failed-%03s' % str(i))
                break
            else:
                i += 1
    else:
        new_structure = read_poscar('CONTCAR')
        # min_dist = np.min(new_structure.distance_matrix+np.ones((new_structure.natom,new_structure.natom)))
    min_dist = np.min(new_structure.distance_matrix()+(np.eye(new_structure.natom)*5))   # WIH
    print('Minimal distance= %8.7f' % min_dist)   # WIH

    if min_dist < 2.0:
        print('ERROR: MAISE finished with and structure with distances too close:', entry_id)  # WIH
        write_poscar(new_structure, workdir + os.sep + 'Collapsed_CONTCAR')  # WIH
        create_new = True   # WIH

    if create_new:
        new_structure = Structure.random_cell(structure.composition)
        ncalls = 0    # WIH

    if ncalls > max_ncalls:
        print('WARNING: Too many calls to MAISE and no relaxation succeeded, replacing structure: ', entry_id)    # WIH
        new_structure = Structure.random_cell(structure.composition)
        pcdb.entries.update({'_id': entry_id}, {'$set': {'status.ncalls': 0}})
        create_new = True
    else:
        pcdb.entries.update({'_id': entry_id}, {'$set': {'status.ncalls': ncalls}})
    pcdb.update(entry_id, structure=new_structure, properties={})

    # if not create_new and energies is not None and forces is not None and stress is not None:
    if energies is not None and forces is not None and stress is not None:

        te = energies[1]
        pcdb.entries.update({'_id': entry_id},
                            {'$set': {'status.relaxation': 'succeed',
                                      'status.target_forces': target_forces,
                                      'properties.initial_forces': generic_serializer(forces[0]),
                                      'properties.initial_stress': generic_serializer(stress[0]),
                                      'properties.initial_stress_kB': generic_serializer(stress_kb[0]),
                                      'properties.forces': generic_serializer(forces[1]),
                                      'properties.stress': generic_serializer(stress[1]),
                                      'properties.stress_kB': generic_serializer(stress_kb[1]),
                                      'properties.energy': te,
                                      'properties.energy_pa': te / new_structure.natom,
                                      'properties.energy_pf': te / new_structure.get_composition().gcd}})

    for ifile in ['POSCAR', 'CONTCAR', 'setup', 'OUTCAR', 'maise.stdout', 'list.dat']:
        if not os.path.exists(ifile):
            wf = open(ifile, 'w')
            wf.write('')
            wf.close()
        n = 1
        while True:
            if os.path.exists(ifile + ('_%03d' % n)):
                n += 1
            else:
                break
        os.rename(ifile, ifile+('_%03d' % n))

    pcm_log.info('[%s]: Unlocking the entry' % str(entry_id))
    pcdb.unlock(entry_id)
Example #10
0
def worker_maise(db_settings, entry_id, workdir, target_forces, relaxator_params):

    max_ncalls = 6
    pcdb = get_database(db_settings)
    pcm_log.info('[%s]: Starting relaxation. Target forces: %7.3e' % (str(entry_id), target_forces))

    if pcdb.is_locked(entry_id):
        return
    else:
        pcdb.lock(entry_id)
    structure = pcdb.get_structure(entry_id)
    status = pcdb.get_dicts(entry_id)[2]

    if 'ncalls' in status:
        ncalls = status['ncalls'] + 1 
    else:
        ncalls = 1 

    #print('Current directory: '+os.getcwd() )
    #print('Working directory: '+workdir)
    write_poscar(structure,workdir+os.sep+'POSCAR')
    if not os.path.exists(workdir+os.sep+'setup'):
        shutil.copy2('setup', workdir)
    if not os.path.exists(workdir+os.sep+'INI'):
        os.symlink(os.getcwd()+os.sep+'INI', workdir+os.sep+'INI')
    if not os.path.exists(workdir+os.sep+'maise'):
        os.symlink(os.getcwd()+os.sep+'maise', workdir+os.sep+'maise')
    cwd=os.getcwd()
    os.chdir(workdir)
    wf=open('maise.stdout','w')
    subprocess.call(['./maise'], stdout=wf)
    wf.close()
    if os.path.isfile('OSZICAR'):
        energies=np.loadtxt('OSZICAR')
    else:
        energies=None
    if os.path.isfile('OUTCAR'):
        rf = open('OUTCAR', 'r')
        data = rf.read()
        
        pos_forces = re.findall(r'TOTAL-FORCE \(eV/Angst\)\s*-*\s*([-.\d\s]+)\s+-{2}', data)
        pos_forces = np.array([x.split() for x in pos_forces], dtype=float)

        if len(pos_forces) > 0 and len(pos_forces[-1]) % 7 == 0:
            pos_forces.shape = (len(pos_forces), -1, 7)
            forces = pos_forces[:, :, 3:6]
            positions = pos_forces[:, :, :3]
        else:
            print('Forces and Positions could not be parsed : ', pos_forces.shape)
            print('pos_forces =\n%s ' % pos_forces)
            
        str_stress=re.findall('Total([\.\d\s-]*)in',data)
        if len(str_stress)==2:
            stress = np.array([[float(y) for y in x.split()] for x in str_stress])
        str_stress=re.findall('in kB([\.\d\s-]*)energy',data)
        if len(str_stress)==2:
            stress_kB = np.array([[float(y) for y in x.split()] for x in str_stress])
    else:
        forces=None
        stress=None
        stress_kB=None

    new_structure=read_poscar('CONTCAR')
    if np.min(new_structure.distance_matrix()+np.eye(new_structure.natom))<0.23:
        print('WARNING: Structure collapse 2 atoms, creating a new random structure')
        new_structure=Structure.random_cell(new_structure.composition)
    if ncalls > max_ncalls:
        print('WARNING: Too many calls to MAISE and no relaxation succeeded, replacing structure')
        new_structure=Structure.random_cell(new_structure.composition)
        pcdb.entries.update({'_id': entry_id}, {'$set': {'status.ncalls': 0}})
    else:
        pcdb.entries.update({'_id': entry_id}, {'$set': {'status.ncalls': ncalls}})
    pcdb.update(entry_id, structure=new_structure)

    
    if energies is not None and forces is not None and stress is not None:

        te = energies[1]
        pcdb.entries.update({'_id': entry_id},
                            {'$set': {'status.relaxation': 'succeed',
                                      'status.target_forces': target_forces,
                                      'properties.initial_forces': generic_serializer(forces[0]),
                                      'properties.initial_stress': generic_serializer(stress[0]),
                                      'properties.initial_stress_kB': generic_serializer(stress_kB[0]),
                                      'properties.forces': generic_serializer(forces[1]),
                                      'properties.stress': generic_serializer(stress[1]),
                                      'properties.stress_kB': generic_serializer(stress_kB[1]),
                                      'properties.energy': te,
                                      'properties.energy_pa': te / new_structure.natom,
                                      'properties.energy_pf': te / new_structure.get_composition().gcd}})

    for ifile in ['POSCAR', 'CONTCAR', 'setup', 'OUTCAR', 'maise.stdout', 'list.dat']:
        if not os.path.exists(ifile):
            wf = open(ifile, 'w')
            wf.write('')
            wf.close()
        n=1
        while True:
           if os.path.exists(ifile+ ('_%03d' % n)):
               n+=1
           else:
               break
        os.rename(ifile,ifile+('_%03d' % n))


    pcm_log.info('[%s]: Unlocking the entry' % str(entry_id))
    pcdb.unlock(entry_id)
Example #11
0
def worker_maise(db_settings, entry_id, workdir, relaxator_params):
    """
    Relax and return evaluate the energy of the structure stored with identifier 'entry_id'
     using the MAISE code

    :param db_settings: (dict) Dictionary of DB parameters needed to create a PyChemiaDB object
    :param entry_id: MongoDB identifier of one entry of the database created from db_settings
    :param workdir: (str) Working directory where input and output from MAISE is written
    :param relaxator_params: (dict) Arguments needed to control the relaxation using MAISE
                                Arguments are store as keys and they include:
                                'target_forces' : Used to defined the tolerance to consider one candidate as relaxed.
                                'source_dir': Directory with executable maise and directory INI
    :return:
    """
    max_ncalls = 6
    pcdb = get_database(db_settings)
    target_forces = relaxator_params['target_forces']
    source_dir = relaxator_params['source_dir']

    pcm_log.info('[%s]: Starting relaxation. Target forces: %7.3e' %
                 (str(entry_id), target_forces))

    if pcdb.is_locked(entry_id):
        return
    else:
        pcdb.lock(entry_id)
    structure = pcdb.get_structure(entry_id)
    status = pcdb.get_dicts(entry_id)[2]

    if 'ncalls' in status and status['ncalls'] > 0:
        ncalls = status['ncalls'] + 1
        print('ncalls = ', status['ncalls'])
    else:
        ncalls = 1
    print('Verifing initial structure...')
    while np.min(structure.distance_matrix() +
                 (np.eye(structure.natom) * 5)) < 1.9:
        print(
            'ERROR: Bad initial guess, two atoms are to close. Creating new random structure for id: %s'
            % str(entry_id))
        write_poscar(structure,
                     workdir + os.sep + 'Fail_initial_POSCAR')  # WIH
        structure = Structure.random_cell(structure.composition)

    write_poscar(structure, workdir + os.sep + 'POSCAR')
    if not os.path.exists(workdir + os.sep + 'setup') and ncalls == 1:  # WIH
        print('First run.')  # WIH
        #   print('Verifying that everything runs smoothly') # WIH
        print(workdir + os.sep + 'setup')
        shutil.copy2(source_dir + os.sep + 'setup_1',
                     workdir + os.sep + 'setup')  # WIH
    elif ncalls > 1:  # WIH
        shutil.copy2(source_dir + os.sep + 'setup_2',
                     workdir + os.sep + 'setup')  # WIH
    if not os.path.exists(workdir + os.sep + 'INI'):
        os.symlink(source_dir + os.sep + 'INI', workdir + os.sep + 'INI')
    if not os.path.exists(workdir + os.sep + 'maise'):
        os.symlink(source_dir + os.sep + 'maise', workdir + os.sep + 'maise')

    # Get the Current Working Directory
    # cwd = os.getcwd()

    # Move to the actual directory where maise will run
    os.chdir(workdir)

    wf = open('maise.stdout', 'w')
    subprocess.call(['./maise'], stdout=wf)
    wf.close()

    if os.path.isfile('OSZICAR'):
        energies = np.loadtxt('OSZICAR')
    else:
        energies = None

    forces = None
    stress = None
    stress_kb = None
    if os.path.isfile('OUTCAR'):
        rf = open('OUTCAR', 'r')
        data = rf.read()

        pos_forces = re.findall(
            r'TOTAL-FORCE \(eV/Angst\)\s*-*\s*([-.\d\s]+)\s+-{2}', data)
        pos_forces = np.array([x.split() for x in pos_forces], dtype=float)

        if len(pos_forces) > 0 and len(pos_forces[-1]) % 7 == 0:
            pos_forces.shape = (len(pos_forces), -1, 7)
            forces = pos_forces[:, :, 3:6]
            # positions = pos_forces[:, :, :3]
        else:
            print('Forces and Positions could not be parsed : ',
                  pos_forces.shape)
            print('pos_forces =\n%s ' % pos_forces)

        str_stress = re.findall('Total([.\d\s-]*)in', data)
        if len(str_stress) == 2:
            stress = np.array([[float(y) for y in x.split()]
                               for x in str_stress])
        str_stress = re.findall('in kB([.\d\s-]*)energy', data)
        if len(str_stress) == 2:
            stress_kb = np.array([[float(y) for y in x.split()]
                                  for x in str_stress])

    create_new = False
    if not os.path.isfile('CONTCAR') or os.path.getsize("CONTCAR") == 0:
        create_new = True
        print('CONTCAR not found in entry: %s' % str(entry_id))
        i = 1
        while True:
            if not os.path.isfile('POSCAR-failed-%03s' % str(i)):
                os.rename('POSCAR', 'POSCAR-failed-%03s' % str(i))
                break
            else:
                i += 1
    else:
        new_structure = read_poscar('CONTCAR')
        # min_dist = np.min(new_structure.distance_matrix+np.ones((new_structure.natom,new_structure.natom)))
    min_dist = np.min(new_structure.distance_matrix() +
                      (np.eye(new_structure.natom) * 5))  # WIH
    print('Minimal distance= %8.7f' % min_dist)  # WIH

    if min_dist < 2.0:
        print(
            'ERROR: MAISE finished with and structure with distances too close:',
            entry_id)  # WIH
        write_poscar(new_structure,
                     workdir + os.sep + 'Collapsed_CONTCAR')  # WIH
        create_new = True  # WIH

    if create_new:
        new_structure = Structure.random_cell(structure.composition)
        ncalls = 0  # WIH

    if ncalls > max_ncalls:
        print(
            'WARNING: Too many calls to MAISE and no relaxation succeeded, replacing structure: ',
            entry_id)  # WIH
        new_structure = Structure.random_cell(structure.composition)
        pcdb.entries.update({'_id': entry_id}, {'$set': {'status.ncalls': 0}})
        create_new = True
    else:
        pcdb.entries.update({'_id': entry_id},
                            {'$set': {
                                'status.ncalls': ncalls
                            }})
    pcdb.update(entry_id, structure=new_structure, properties={})

    # if not create_new and energies is not None and forces is not None and stress is not None:
    if energies is not None and forces is not None and stress is not None:

        te = energies[1]
        pcdb.entries.update({'_id': entry_id}, {
            '$set': {
                'status.relaxation': 'succeed',
                'status.target_forces': target_forces,
                'properties.initial_forces': generic_serializer(forces[0]),
                'properties.initial_stress': generic_serializer(stress[0]),
                'properties.initial_stress_kB': generic_serializer(
                    stress_kb[0]),
                'properties.forces': generic_serializer(forces[1]),
                'properties.stress': generic_serializer(stress[1]),
                'properties.stress_kB': generic_serializer(stress_kb[1]),
                'properties.energy': te,
                'properties.energy_pa': te / new_structure.natom,
                'properties.energy_pf':
                te / new_structure.get_composition().gcd
            }
        })

    for ifile in [
            'POSCAR', 'CONTCAR', 'setup', 'OUTCAR', 'maise.stdout', 'list.dat'
    ]:
        if not os.path.exists(ifile):
            wf = open(ifile, 'w')
            wf.write('')
            wf.close()
        n = 1
        while True:
            if os.path.exists(ifile + ('_%03d' % n)):
                n += 1
            else:
                break
        os.rename(ifile, ifile + ('_%03d' % n))

    pcm_log.info('[%s]: Unlocking the entry' % str(entry_id))
    pcdb.unlock(entry_id)