Ejemplo n.º 1
0
    def test_sequence_of_swap_moves(self):
        if (not has_ase_with_ce):
            self.skipTest("ASE does not have CE")
            return
        calc,ceBulk,eci = self.get_calc("fcc")
        corr_func = CorrFunction(ceBulk)
        cf = corr_func.get_cf(ceBulk.atoms)
        n_tests = 10

        # Insert 10 Mg atoms
        for i in range(n_tests):
            calc.calculate( ceBulk.atoms, ["energy"], [(i,"Al","Mg")] )

        # Swap Al and Mg atoms
        changes = []
        for i in range(n_tests):
            indx1 = i
            indx2 = len(ceBulk.atoms)-i-1
            symb1 = "Mg"
            symb2 = "Al"
            changes += [(indx1,symb1,symb2),(indx2,symb2,symb1)]

        calc.calculate( ceBulk.atoms, ["energy"], changes )
        updated_cf = calc.get_cf()
        brute_force = corr_func.get_cf_by_cluster_names( ceBulk.atoms, updated_cf.keys() )
        for key,value in brute_force.items():
            self.assertAlmostEqual( value, updated_cf[key] )
Ejemplo n.º 2
0
    def test_double_swaps(self):
        if not has_ase_with_ce:
            self.skipTest("ASE version does not have CE")
            return

        for lat in self.lattices:
            atoms, ceBulk, _ = self.get_calc(lat)
            calc = atoms.get_calculator()
            corr_func = CorrFunction(ceBulk)
            n_tests = 10
            symbs = [atom.symbol for atom in atoms]
            # Insert 10 Mg atoms
            for i in range(10):
                symbs[i] = "Mg"
            calc.set_symbols(symbs)

            # Swap Al and Mg atoms
            for i in range(n_tests):
                indx1 = np.random.randint(low=0, high=len(ceBulk.atoms))
                symb1 = atoms[indx1].symbol
                indx2 = indx1
                symb2 = symb1
                while symb2 == symb1:
                    indx2 = np.random.randint(low=0, high=len(atoms))
                    symb2 = atoms[indx2].symbol
                calc.calculate(atoms, ["energy"], [(indx1, symb1, symb2),(indx2, symb2, symb1)])
                updated_cf = calc.get_cf()
                brute_force = corr_func.get_cf_by_cluster_names(atoms, updated_cf.keys())
                for key,value in brute_force.items():
                    self.assertAlmostEqual(value, updated_cf[key])
Ejemplo n.º 3
0
    def test_double_swaps(self):
        if not has_ase_with_ce:
            self.skipTest("ASE version does not have CE")
            return

        for lat in self.lattices:
            calc, ceBulk, eci = self.get_calc(lat)
            corr_func = CorrFunction(ceBulk)
            cf = corr_func.get_cf(ceBulk.atoms)
            n_tests = 10

            # Insert 25 Mg atoms
            for i in range(25):
                calc.calculate( ceBulk.atoms, ["energy"], [(i,"Al","Mg")] )

            # Swap Al and Mg atoms
            for i in range(n_tests):
                indx1 = np.random.randint(low=0, high=len(ceBulk.atoms))
                symb1 = ceBulk.atoms[indx1].symbol
                indx2 = indx1
                symb2 = symb1
                while symb2 == symb1:
                    indx2 = np.random.randint(low=0, high=len(ceBulk.atoms))
                    symb2 = ceBulk.atoms[indx2].symbol
                calc.calculate(ceBulk.atoms, ["energy"], [(indx1,symb1,symb2),(indx2,symb2,symb1)])
                updated_cf = calc.get_cf()
                brute_force = corr_func.get_cf_by_cluster_names( ceBulk.atoms, updated_cf.keys() )
                for key,value in brute_force.items():
                    self.assertAlmostEqual( value, updated_cf[key] )
Ejemplo n.º 4
0
    def __init__(self, BC, eci=None, initial_cf=None):
        Calculator.__init__(self)
        self.BC = BC

        # NOTE: This should be handled in the CE code
        self.BC._info_entries_to_list()
        self.corrFunc = CorrFunction(self.BC)
        cf_names = list(eci.keys())
        if initial_cf is None:
            msg = "Calculating {} correlation ".format(len(cf_names))
            msg += "functions from scratch"
            print(msg)
            self.cf = self.corrFunc.get_cf_by_cluster_names(
                self.BC.atoms, cf_names)
        else:
            self.cf = initial_cf
        print("Correlation functions initialized...")

        self.eci = eci

        self.atoms = self.BC.atoms

        # Keep a copy of the original symbols
        symbols = [atom.symbol for atom in self.BC.atoms]
        self._check_trans_mat_dimensions()

        self.old_cfs = []
        self.old_atoms = self.atoms.copy()
        self.changes = []
        self.ctype = {}
        # self.convert_cluster_indx_to_list()

        if isinstance(self.BC.trans_matrix, np.ndarray):
            self.BC.trans_matrix = np.array(self.BC.trans_matrix).astype(
                np.int32)

        self.updater = None
        if use_cpp:
            print("Initializing C++ calculator...")
            self.updater = PyCEUpdater(self.BC, self.cf, self.eci)
            # self.updater.init(self.BC, self.cf, self.eci)
            print("C++ module initialized...")

        if use_cpp:
            self.clear_history = self.updater.clear_history
            self.undo_changes = self.updater.undo_changes
            self.update_cf = self.updater.update_cf
        else:
            raise ImportError("Could not find C++ backend for the updater!")

        # Set the symbols back to their original value
        self.set_symbols(symbols)
        self._linear_vib_correction = None
 def __init__(self, BC):
     self.bc = BC
     cf_obj = CorrFunction(self.bc)
     self.init_cf = cf_obj.get_cf(self.bc.atoms)
     ecis = {key: 1.0
             for key in self.init_cf.keys()}  # ECIs do not matter here
     self.calc = CE(self.bc, ecis, initial_cf=self.init_cf)
     self.bc.atoms.set_calculator(self.calc)
     self.elements = self.bc.basis_elements[
         0]  # This should be updated to handle different site types
     self.status_every_sec = 30
     N = len(ecis.keys()) - 1
     self.cov_matrix = np.zeros((N, N))
     self.exp_value = np.zeros(N)
Ejemplo n.º 6
0
    def test_double_swaps_ternary( self ):
        if ( not has_ase_with_ce ): # Disable this test
            self.skipTest("ASE version has not cluster expansion")
            return

        db_name = "test_db_ternary.db"
        conc_args = {
            "conc_ratio_min_1":[[4,0,0]],
            "conc_ratio_max_1":[[0,4,0]],
            "conc_ratio_min_1":[[2,2,0]],
            "conc_ratio_max_2":[[1,1,2]]
        }
        max_dia = get_max_cluster_dia_name()
        size_arg = {max_dia:4.05}
        ceBulk = CEBulk( crystalstructure="fcc", a=4.05, size=[4,4,4], basis_elements=[["Al","Mg","Si"]], \
                              conc_args=conc_args, db_name=db_name, max_cluster_size=3, **size_arg)
        ceBulk.reconfigure_settings()
        corr_func = CorrFunction( ceBulk )
        cf = corr_func.get_cf( ceBulk.atoms )
        #prefixes = [name.rpartition("_")[0] for name in cf.keys()]
        #prefixes.remove("")
        eci = {name:1.0 for name in cf.keys()}
        calc = CE( ceBulk, eci )
        n_tests = 10

        # Insert 25 Mg atoms and 25 Si atoms
        n = 18
        for i in range(n):
            calc.calculate( ceBulk.atoms, ["energy"], [(i,"Al","Mg")])
            calc.calculate( ceBulk.atoms, ["energy"], [(i+n,"Al","Si")])
            updated_cf = calc.get_cf()
            brute_force = corr_func.get_cf_by_cluster_names( ceBulk.atoms, updated_cf.keys() )
            for key in updated_cf.keys():
                self.assertAlmostEqual( brute_force[key], updated_cf[key])

        # Swap atoms
        for i in range(n_tests):
            indx1 = np.random.randint(low=0,high=len(ceBulk.atoms))
            symb1 = ceBulk.atoms[indx1].symbol
            indx2 = indx1
            symb2 = symb1
            while( symb2 == symb1 ):
                indx2 = np.random.randint( low=0, high=len(ceBulk.atoms) )
                symb2 = ceBulk.atoms[indx2].symbol
            calc.calculate( ceBulk.atoms, ["energy"], [(indx1,symb1,symb2),(indx2,symb2,symb1)])
            update_cf = calc.get_cf()
            brute_force = corr_func.get_cf_by_cluster_names( ceBulk.atoms, update_cf.keys() )
            for key,value in brute_force.items():
                self.assertAlmostEqual( value, update_cf[key])
Ejemplo n.º 7
0
    def test_set_singlets( self ):
        if ( not has_ase_with_ce ):
            self.skipTest( "ASE version does not have CE" )
            return

        system_types = [["Al","Mg"],["Al","Mg","Si"],["Al","Mg","Si","Cu"]]

        db_name = "test_singlets.db"
        n_concs = 4
        no_throw = True
        msg = ""
        try:
            for basis_elems in system_types:
                conc_args = {
                    "conc_ratio_min_1":[[1,0]],
                    "conc_ratio_max_1":[[0,1]],
                }
                a = 4.05
                mx_dia_name = get_max_cluster_dia_name()
                size_arg = {mx_dia_name:a}
                ceBulk = CEBulk( crystalstructure="fcc", a=a, size=[5, 5, 5], basis_elements=[basis_elems], conc_args=conc_args, \
                db_name=db_name, max_cluster_size=2,**size_arg)
                ceBulk.reconfigure_settings()
                cf = CorrFunction(ceBulk)
                corrfuncs = cf.get_cf(ceBulk.atoms)
                eci = {name:1.0 for name in corrfuncs.keys()}
                calc = CE( ceBulk,eci )
                for _ in range(n_concs):
                    conc = np.random.rand(len(basis_elems))*0.97
                    conc /= np.sum(conc)
                    conc_dict = {}
                    for i in range(len(basis_elems)):
                        conc_dict[basis_elems[i]] = conc[i]
                    calc.set_composition(conc_dict)
                    ref_cf = calc.get_cf()

                    singlets = {}
                    for key,value in ref_cf.items():
                        if ( key.startswith("c1") ):
                            singlets[key] = value
                    comp = calc.singlet2comp(singlets)
                    dict_comp = "Ref {}. Computed {}".format(conc_dict,comp)
                    for key in comp.keys():
                        self.assertAlmostEqual( comp[key], conc_dict[key], msg=dict_comp, places=1 )
                calc.set_singlets(singlets)
        except Exception as exc:
            msg = str(exc)
            no_throw = False
        self.assertTrue( no_throw, msg=msg )
Ejemplo n.º 8
0
    def get_calc(self, lat):
        db_name = "test_db_{}.db".format(lat)

        conc = Concentration(basis_elements=[["Al", "Mg"]])
        a = 4.05
        ceBulk = CEBulk(crystalstructure=lat, a=a, size=[3, 3, 3],
                        concentration=conc, db_name=db_name,
                        max_cluster_size=3, max_cluster_dia=6.0)
        ceBulk.reconfigure_settings()
        cf = CorrFunction(ceBulk)
        corrfuncs = cf.get_cf(ceBulk.atoms)
        eci = {name: 1.0 for name in corrfuncs.keys()}
        atoms = ceBulk.atoms.copy()
        calc = CE(atoms, ceBulk, eci)
        atoms.set_calculator(calc)
        return atoms, ceBulk, eci
Ejemplo n.º 9
0
    def test_supercell( self ):
        if ( not has_ase_with_ce ):
            self.skipTest("ASE version does not have CE")
            return

        calc, ceBulk, eci = self.get_calc("fcc")
        db_name = "test_db_fcc_super.db"

        conc_args = {
            "conc_ratio_min_1":[[1,0]],
            "conc_ratio_max_1":[[0,1]],
        }

        kwargs = {
            "crystalstructure": "fcc",
            "a": 4.05,
            "size":[3, 3, 3],
            "basis_elements":[["Al","Mg"]],
            "conc_args": conc_args,
            "db_name": db_name,
            "max_cluster_size": 3,
            "max_cluster_dia": 4.05
        }

        kwargs_template = copy.deepcopy(kwargs)
        kwargs_template["size"] = [4, 4, 4]
        kwargs_template["db_name"] = "template_bc.db"
        template_supercell_bc = CEBulk(**kwargs_template)
        template_supercell_bc.reconfigure_settings()

        ceBulk = CEBulk(**kwargs)
        ceBulk.reconfigure_settings()
        calc = get_ce_calc(ceBulk, kwargs, eci, size=[4, 4, 4],
                           db_name="sc4x4x.db")
        ceBulk = calc.BC
        ceBulk.atoms.set_calculator(calc)
        corr_func = CorrFunction(template_supercell_bc)
        for i in range(10):
            calc.calculate(ceBulk.atoms, ["energy"], [(i, "Al", "Mg")])
            updated_cf = calc.get_cf()
            brute_force = corr_func.get_cf_by_cluster_names(
                ceBulk.atoms, list(updated_cf.keys()))

            for key, value in brute_force.items():
                self.assertAlmostEqual(value, updated_cf[key])
        os.remove("sc4x4x.db")
Ejemplo n.º 10
0
    def get_calc(self, lat):
        db_name = "test_db_{}.db".format(lat)

        conc_args = {
            "conc_ratio_min_1": [[1, 0]],
            "conc_ratio_max_1": [[0, 1]],
        }
        a = 4.05
        ceBulk = CEBulk(crystalstructure=lat, a=a, size=[3, 3, 3],
                             basis_elements=[["Al", "Mg"]],
                             conc_args=conc_args,
                             db_name=db_name, max_cluster_size=3)
        ceBulk.reconfigure_settings()
        cf = CorrFunction(ceBulk)
        corrfuncs = cf.get_cf(ceBulk.atoms)
        eci = {name: 1.0 for name in corrfuncs.keys()}
        calc = CE(ceBulk, eci)
        return calc, ceBulk, eci
Ejemplo n.º 11
0
 def test_update(self):
     if not has_ase_with_ce:
         self.skipTest("ASE version does not have CE")
     for lat in self.lattices:
         msg = "Failed for lattice {}".format(lat)
         calc, ceBulk, eci = self.get_calc(lat)
         cf = CorrFunction(ceBulk)
         n_tests = 10
         for i in range(n_tests):
             old_symb = ceBulk.atoms[i].symbol
             if old_symb == "Al":
                 new_symb = "Mg"
             else:
                 new_symb = "Al"
             calc.update_cf((i, old_symb, new_symb))
             updated_cf = calc.get_cf()
             brute_force = cf.get_cf(ceBulk.atoms)
             for key, value in updated_cf.items():
                 self.assertAlmostEqual(value, brute_force[key], msg=msg)
Ejemplo n.º 12
0
 def test_binary_spacegroup( self ):
     if ( not has_ase_with_ce ):
         self.skipTest("ASE version does not have CE")
         return
     bs, db_name = get_bulkspacegroup_binary()
     cf = CorrFunction( bs )
     cf_vals = cf.get_cf( bs.atoms )
     ecis = {name:1.0 for name in cf_vals.keys()}
     calc = CE( bs, ecis )
     bs.atoms.set_calculator( calc )
     for i in range(25):
         if ( bs.atoms[i].symbol == "Al" ):
             new_symb = "Mg"
             old_symb = "Al"
         else:
             new_symb = "Al"
             old_symb = "Mg"
         calc.calculate( bs.atoms, ["energy"], [(i,old_symb,new_symb)] )
         updated_cf = calc.get_cf()
         brute_force = cf.get_cf_by_cluster_names( bs.atoms, updated_cf.keys() )
         for key,value in brute_force.items():
             self.assertAlmostEqual( value, updated_cf[key] )
Ejemplo n.º 13
0
    def test_random_swaps(self):
        if not has_ase_with_ce:
            self.skipTest("ASE version does not have CE")
            return

        for lat in self.lattices:
            msg = "Failed for lattice {}".format(lat)
            calc, ceBulk, eci = self.get_calc(lat)
            n_tests = 10
            corr_func = CorrFunction(ceBulk)
            cf = corr_func.get_cf(ceBulk.atoms)
            for i in range(n_tests):
                indx = np.random.randint(low=0, high=len(ceBulk.atoms))
                old_symb = ceBulk.atoms[indx].symbol
                if old_symb == "Al":
                    new_symb = "Mg"
                else:
                    new_symb = "Al"
                calc.calculate(ceBulk.atoms, ["energy"],
                               [(indx, old_symb, new_symb)])
                updated_cf = calc.get_cf()
                brute_force = corr_func.get_cf_by_cluster_names(ceBulk.atoms, updated_cf.keys())
                for key, value in updated_cf.items():
                    self.assertAlmostEqual(value, brute_force[key], msg=msg)
Ejemplo n.º 14
0
class CEcalc(Calculator):
    implemented_properties = ['energy']

    def __init__(self, ecis, BC):
        Calculator.__init__(self)
        self.corrFunc = CorrFunction(BC)
        self.eci = ecis

    def calculate(self, atoms, properties, system_changes):
        Calculator.calculate(self, atoms)

        cf_by_name = self.corrFunc.get_cf_by_cluster_names(
            atoms, self.eci.keys())
        energy = 0.0
        for key, value in self.eci.items():
            energy += value * cf_by_name[key]
        self.results["energy"] = energy * len(
            atoms)  # Should not return energy per atom!
Ejemplo n.º 15
0
 def __init__(self, ecis, BC):
     Calculator.__init__(self)
     self.corrFunc = CorrFunction(BC)
     self.eci = ecis
Ejemplo n.º 16
0
class CE(Calculator):
    """
    Class for updating the CE when symbols change

    :param ClusterExpansionSetting BC: Settings from ASE
    :param dict eci: Effective cluster interactions
    :param initial_cf: Initial correlation functions, if None all the
        required correlation functions are calculated from scratch
    :type initial_cf: dict or None
    """

    implemented_properties = ["energy"]

    def __init__(self, atoms, BC, eci=None, initial_cf=None):
        Calculator.__init__(self)
        self.BC = BC

        if self._has_self_interaction(BC.cluster_info):
            raise SelfInteractionError(
                'The simulation cell is so small that the same site '
                'is present multiple times within one cluster. '
                'Increase the size of the simulation cell.')

        # Make sure there is an ECI corresponding to
        # the emptry cluster
        if 'c0' not in eci.keys():
            eci['c0'] = 0.0

            if initial_cf is not None:
                initial_cf['c0'] = 1.0

        # NOTE: This should be handled in the CE code
        self.BC._info_entries_to_list()
        self.corrFunc = CorrFunction(self.BC)
        cf_names = list(eci.keys())
        if initial_cf is None:
            msg = "Calculating {} correlation ".format(len(cf_names))
            msg += "functions from scratch"
            print(msg)
            self.cf = self.corrFunc.get_cf_by_cluster_names(atoms, cf_names)
        else:
            self.cf = initial_cf
        print("Correlation functions initialized...")

        self.eci = eci

        self.atoms = atoms

        # Attach the calculator to the atoms object
        self.atoms.set_calculator(self)

        # Keep a copy of the original symbols
        symbols = [atom.symbol for atom in self.atoms]
        self._check_trans_mat_dimensions()

        self.ctype = {}
        # self.convert_cluster_indx_to_list()

        if isinstance(self.BC.trans_matrix, np.ndarray):
            self.BC.trans_matrix = np.array(self.BC.trans_matrix).astype(
                np.int32)

        self.updater = None
        if use_cpp:
            print("Initializing C++ calculator...")
            self.updater = PyCEUpdater(self.atoms, self.BC, self.cf, self.eci)
            print("C++ module initialized...")

        if use_cpp:
            self.clear_history = self.updater.clear_history
            self.undo_changes = self.updater.undo_changes
            self.update_cf = self.updater.update_cf
        else:
            raise ImportError("Could not find C++ backend for the updater!")

        # Set the symbols back to their original value
        self.set_symbols(symbols)
        self._linear_vib_correction = None

    def copy(self):
        """Create a copy of the calculator.

        :return: New CE instance
        :rtype: CE
        """
        from copy import deepcopy
        self.atoms.set_calculator(None)
        new_bc = deepcopy(self.BC)
        self.atoms.set_calculator(self)
        atoms = self.atoms.copy()
        new_calc = CE(atoms, new_bc, eci=self.eci, initial_cf=self.get_cf())
        #new_calc.atoms.set_calculator(new_calc)
        return new_calc

    def _check_trans_mat_dimensions(self):
        """
        Check that dimension of the trans matrix matches the number of atoms
        """
        if isinstance(self.BC.trans_matrix, list):
            n_sites = len(self.BC.trans_matrix)
        elif isinstance(self.BC.trans_matrix, np.ndarray):
            n_sites = self.BC.trans_matrix.shape[0]

        # Make sure that the database information fits
        if len(self.atoms) != n_sites:
            msg = "The number of atoms and the dimension of the translation "
            msg += "matrix is inconsistent\n"
            msg += "Num atoms: {}. ".format(len(self.atoms))
            msg += "Num row trans mat: {}".format(n_sites)
            raise ValueError(msg)

    def get_full_cluster_names(self, cnames):
        """
        Returns the full cluster names with decoration info in the end

        :param list cnames: List of the current cluster names
        """
        full_names = self.cf.keys()
        only_prefix = [name.rpartition("_")[0] for name in full_names]
        full_cluster_names = []

        # First insert the one body names, nothing to be done for them
        for name in cnames:
            if name.startswith("c1"):
                full_cluster_names.append(name)
        for name in cnames:
            if name.startswith("c1"):
                continue
            indx = only_prefix.index(name)
            full_cluster_names.append(full_names[indx])
        return full_cluster_names

    @property
    def linear_vib_correction(self):
        return self._linear_vib_correction

    @linear_vib_correction.setter
    def linear_vib_correction(self, linvib):
        if not isinstance(linvib, lvc.LinearVibCorrection):
            raise TypeError(
                "Linear vib correction has to be of type LinearVibCorrection!")
        if (self.linear_vib_correction is not None):
            orig_eci = self.linear_vib_correction.reset(self.eci)
            if (orig_eci is not None):
                self.eci = orig_eci
            self.update_ecis(self.eci)
        self._linear_vib_correction = linvib
        if self.updater is not None:
            # This just initialize a LinearVibCorrection object, it does not
            # change the ECIs
            self.updater.add_linear_vib_correction(linvib.eci_per_kbT)

    def include_linvib_in_ecis(self, T):
        """
        Includes the effect of linear vibration correction in the ECIs

        :param float T: Temperature in Kelvin
        """
        if (self.linear_vib_correction is None):
            return

        # Reset the ECIs to the original
        self.update_ecis(self.eci)
        self.ecis = self.linear_vib_correction.include(self.eci, T)
        self.update_ecis(self.eci)

    def vib_energy(self, T):
        """
        Returns the vibration energy per atom

        :param float T: Temperature in kelving

        :return: Vibration energy
        :rtype: float
        """
        if (self.updater is not None):
            return self.updater.vib_energy(T)

        if (self.linear_vib_correction is not None):
            return self.linear_vib_correction.energy(T, self.cf)
        return 0.0

    def get_energy(self):
        """
        Returns the energy of the system

        :return: Energy of the system
        :rtype: float
        """
        energy = 0.0
        if (self.updater is None):
            for key, value in self.eci.items():
                energy += value * self.cf[key]
            energy *= len(self.atoms)
        else:
            energy = self.updater.get_energy()
        return energy

    def create_ctype_lookup(self):
        """
        Creates a lookup table for cluster types based on the prefix
        """
        for n in range(2, len(self.BC.cluster_names)):
            for ctype in range(len(self.BC.cluster_names[n])):
                name = self.BC.cluster_names[n][ctype]
                prefix = name  # name.rpartition('_')[0]
                self.ctype[prefix] = (n, ctype)

    def calculate(self, atoms, properties, system_changes):
        """
        Calculates the energy. The system_changes is assumed to be a list
        of tuples of the form (indx,old_symb,new_symb)

        :param Atoms atoms: This is not used
            to fit the signature of this function in ASE. The energy returned,
            is the one of the internal atoms object *after* system_changes is
            applied
        :param list properties: Has to be ["energy"]
        :param list system_changes: Updates to the system. Same signature as
            :py:meth:`cemc.mcmc.MCObserver.__call__`

        :return: Energy of the system
        :rtype: float
        """
        energy = self.updater.calculate(system_changes)
        self.cf = self.updater.get_cf()
        self.results["energy"] = energy
        return self.results["energy"]

    def get_cf(self):
        """
        Returns the correlation functions

        :return: Correlation functions
        :rtype: dict
        """
        if (self.updater is None):
            return self.cf
        else:
            return self.updater.get_cf()

    def update_ecis(self, new_ecis):
        """
        Updates the ecis

        :param dict new_ecis: New ECI values
        """
        self.eci = new_ecis
        if (self.updater is not None):
            self.updater.set_ecis(self.eci)

    def get_singlets(self):
        """
        Return the singlets

        :return: Correlation functions corresponding to singlets
        :rtype: dict
        """
        return self.updater.get_singlets()

    def set_composition(self, comp):
        """
        Change composition of an object.

        :param dict comp: New composition. If you want
            to set the composition to for instance 20%% Mg and 80 %% Al, this
            argument should be {"Mg":0.2, "Al":0.8}
        """
        # Verify that the sum of the compositions is one
        tot_conc = 0.0
        max_element = None
        max_conc = 0.0
        for key, conc in comp.items():
            tot_conc += conc
            if (conc > max_conc):
                max_element = key
                max_conc = conc

        if (np.abs(tot_conc - 1.0) > 1E-6):
            raise ValueError("The specified concentration does not sum to 1!")

        # Change all atoms to the one with the highest concentration
        init_elm = max_element
        for i in range(len(self.atoms)):
            # self.update_cf( (i,self.atoms[i].symbol,init_elm) ) # Set all
            # atoms to init element
            self.calculate(self.atoms, ["energy"],
                           [(i, self.atoms[i].symbol, init_elm)])
        start = 0
        for elm, conc in comp.items():
            if (elm == init_elm):
                continue
            n_at = int(round(conc * len(self.atoms)))
            for i in range(start, start + n_at):
                self.update_cf((i, init_elm, elm))
            start += n_at
        self.clear_history()

    def set_symbols(self, symbs):
        """
        Change the symbols of the entire atoms object

        :param list symbs: List of new symbols
        """
        if (len(symbs) != len(self.atoms)):
            raise ValueError("Length of the symbols array has to match"
                             "the length of the atoms object.!")
        for i, symb in enumerate(symbs):
            self.update_cf((i, self.atoms[i].symbol, symb))
        self.clear_history()

    def singlet2comp(self, singlets):
        """
        Convert singlet to compositions

        :param dict singlets: Singlet values

        :return: Concentrations corresponding to the singlets
        :rtype: dict
        """
        bfs = self.BC.basis_functions

        if (len(singlets.keys()) != len(bfs)):
            msg = "The number singlet terms specified is different "
            msg += "from the number of basis functions\n"
            msg += "Given singlet terms: {}\n".format(singlets)
            msg += "Basis functions: {}\n".format(bfs)
            raise ValueError(msg)

        # Generate system of equations
        rhs = np.zeros(len(bfs))
        # Concentration of this element is implicitly determined via the others
        spec_element = list(bfs[0].keys())[0]
        for key, value in singlets.items():
            dec = int(key[-1])
            rhs[dec] = value - bfs[dec][spec_element]

        matrix = np.zeros((len(bfs), len(bfs)))
        for key in singlets.keys():
            row = int(key[-1])
            col = 0
            for element in bfs[0].keys():
                if (element == spec_element):
                    continue
                matrix[row, col] = bfs[row][element] - bfs[row][spec_element]
                col += 1
        concs = np.linalg.solve(matrix, rhs)

        eps = 1E-6
        # Allow some uncertainty in the solution
        for i in range(len(concs)):
            if (concs[i] < 0.0 and concs[i] > -eps):
                concs[i] = 0.0
        conc_spec_element = 1.0 - np.sum(concs)

        if (conc_spec_element < 0.0 and conc_spec_element > -eps):
            conc_spec_element = 0.0

        # Some trivial checks
        if (conc_spec_element > 1.0 or conc_spec_element < 0.0):
            msg = "Something strange happened when converting singlets to "
            msg += "composition\n"
            msg += "Concentration of one of the implicitly "
            msg += "determined element is {}".format(conc_spec_element)
            raise RuntimeError(msg)

        if (np.any(concs > 1.0) or np.any(concs < 0.0)):
            msg = "Something went wrong when the linear system of "
            msg += "equations were solved.\n"
            msg += "Final concentration is {}".format(concs)
            raise RuntimeError(msg)

        conc_dict = {}
        counter = 0
        for element in bfs[0].keys():
            if (element == spec_element):
                conc_dict[element] = conc_spec_element
            else:
                conc_dict[element] = concs[counter]
                counter += 1
        return conc_dict

    def set_singlets(self, singlets):
        """
        Brings the system into a certain configuration such that the singlet
        has a certain value. Note that depending on the size of atoms,
        it is not all singlet value that are possible. So the composition
        obtained in the end, may differ slightly from the intended value.

        :param dict singlets: Singlet values
        """
        conc = self.singlet2comp(singlets)
        self.set_composition(conc)

    def backup_dict(self):
        """Return a dictionary containing all arguments for backup.

        :return: All nessecary arguments to reconstruct the calculator in the
            current state
        :rtype: dict
        """
        backup_data = {}
        backup_data["cf"] = self.get_cf()
        backup_data["symbols"] = [atom.symbol for atom in self.atoms]
        backup_data["setting_kwargs"] = self.BC.kwargs
        backup_data["setting_kwargs"]["classtype"] = type(self.BC).__name__
        backup_data["eci"] = self.eci
        return backup_data

    def save(self, fname):
        """
        Store all nessecary information required to restart the calculation.

        :param str fname: Filename
        """
        import json
        with open(fname, 'w') as outfile:
            json.dump(self.backup_dict(),
                      outfile,
                      indent=2,
                      separators=(",", ": "))

    def __reduce__(self):
        return (CE.load_from_dict, (self.backup_dict(), ))

    @staticmethod
    def load(fname):
        """Rebuild the calculator.

        :param str fname: Filename
        """
        import json
        with open(fname, 'r') as infile:
            backup_data = json.load(infile)
        return CE.load_from_dict(backup_data)

    @staticmethod
    def load_from_dict(backup_data):
        """Rebuild the calculator from dictionary

        :param dict backup_data: All nessecary arguments
        """
        from ase.clease import CEBulk, CECrystal

        classtype = backup_data["setting_kwargs"].pop("classtype")
        if classtype == "CEBulk":
            bc = CEBulk(**backup_data["setting_kwargs"])
        elif classtype == "CECrystal":
            bc = CECrystal(**backup_data["setting_kwargs"])
        else:
            raise ValueError("Unknown setting classtype: {}"
                             "".format(backup_data["setting_kwargs"]))

        atoms = bc.atoms.copy()
        for symb in backup_data["symbols"]:
            atoms.symbol = symb

        return CE(atoms,
                  bc,
                  eci=backup_data["eci"],
                  initial_cf=backup_data["cf"])

    def _has_self_interaction(self, cluster_info):
        """Check if self interactions exists

        :param list: Cluster info. Cluster information from CLEASE
        """
        for info in cluster_info:
            for k, cluster in info.items():
                for sub in cluster['indices']:
                    if cluster['ref_indx'] in sub:
                        return True
                    if len(set(sub)) != len(sub):
                        return True
        return False

    def set_num_threads(self, num_threads):
        """
        Set the number of threads to use when updating the number
        of threads

        :param int num_threads: New number of threads
        """
        self.updater.set_num_threads(num_threads)
Ejemplo n.º 17
0
    def __init__(self, atoms, BC, eci=None, initial_cf=None):
        Calculator.__init__(self)
        self.BC = BC

        if self._has_self_interaction(BC.cluster_info):
            raise SelfInteractionError(
                'The simulation cell is so small that the same site '
                'is present multiple times within one cluster. '
                'Increase the size of the simulation cell.')

        # Make sure there is an ECI corresponding to
        # the emptry cluster
        if 'c0' not in eci.keys():
            eci['c0'] = 0.0

            if initial_cf is not None:
                initial_cf['c0'] = 1.0

        # NOTE: This should be handled in the CE code
        self.BC._info_entries_to_list()
        self.corrFunc = CorrFunction(self.BC)
        cf_names = list(eci.keys())
        if initial_cf is None:
            msg = "Calculating {} correlation ".format(len(cf_names))
            msg += "functions from scratch"
            print(msg)
            self.cf = self.corrFunc.get_cf_by_cluster_names(atoms, cf_names)
        else:
            self.cf = initial_cf
        print("Correlation functions initialized...")

        self.eci = eci

        self.atoms = atoms

        # Attach the calculator to the atoms object
        self.atoms.set_calculator(self)

        # Keep a copy of the original symbols
        symbols = [atom.symbol for atom in self.atoms]
        self._check_trans_mat_dimensions()

        self.ctype = {}
        # self.convert_cluster_indx_to_list()

        if isinstance(self.BC.trans_matrix, np.ndarray):
            self.BC.trans_matrix = np.array(self.BC.trans_matrix).astype(
                np.int32)

        self.updater = None
        if use_cpp:
            print("Initializing C++ calculator...")
            self.updater = PyCEUpdater(self.atoms, self.BC, self.cf, self.eci)
            print("C++ module initialized...")

        if use_cpp:
            self.clear_history = self.updater.clear_history
            self.undo_changes = self.updater.undo_changes
            self.update_cf = self.updater.update_cf
        else:
            raise ImportError("Could not find C++ backend for the updater!")

        # Set the symbols back to their original value
        self.set_symbols(symbols)
        self._linear_vib_correction = None