예제 #1
0
    def test_dummyprop(self):

        # Define a dummy derived property and test it

        class DummyProperty(AtomsProperty):

            default_name = 'dummy'
            default_params = {'mul': 2.0}

            @staticmethod
            def extract(s, mul):
                return s.positions.shape[0] * mul

        # Now two atoms objects to test it on
        a1 = Atoms('C')
        a2 = Atoms('CC')

        dummyDoubled = DummyProperty(name='doubledummy', mul=4)

        self.assertEqual(DummyProperty.get(a1), 2.0)
        self.assertEqual(dummyDoubled(a2), 8.0)

        # Also check that improper parameters are rejected
        self.assertRaises(ValueError, DummyProperty, wrong='this is wrong')

        # Test behaviour on a collection instead
        c1 = AtomsCollection([a1, a2])
        c2 = AtomsCollection([a2, a1])

        DummyProperty.get(c1, store_array=True)
        dummyDoubled(c2, store_array=True)
        self.assertTrue(np.all(c1.get_array('dummy') == [2, 4]))
        self.assertTrue(np.all(c2.get_array('doubledummy') == [8, 4]))
예제 #2
0
    def test_loadres(self):

        # Load some test files and check if regular loading works
        reslist = glob.glob(
            os.path.join(_TESTDATA_DIR, 'rescollection', '*.res'))
        testcoll = AtomsCollection(reslist)
        self.assertEqual(testcoll.length, len(reslist))

        # Now try the same, but with single atoms objects
        aselist = [io.read(str(fname)) for fname in reslist]
        testcoll = AtomsCollection(aselist)
        self.assertEqual(testcoll.length, len(reslist))
예제 #3
0
    def test_chunks(self):

        full_len = 10
        chunk_len = 3
        chunk_n = 2

        # Generate a few random structures
        elrnd = ['H', 'C', 'O', 'N']
        asernd = []
        for n in range(full_len):
            aselen = np.random.randint(1, 10)
            asernd.append(
                Atoms(symbols=np.random.choice(elrnd, aselen),
                      positions=np.random.random((aselen, 3))))

        testcoll = AtomsCollection(asernd)

        # Test with size
        chunks = testcoll.chunkify(chunk_size=chunk_len)
        self.assertEqual(len(chunks), np.ceil(full_len / (1.0 * chunk_len)))
        self.assertEqual(chunks[-1].length, full_len % chunk_len)

        # Test with number
        chunks = testcoll.chunkify(chunk_n=chunk_n)
        self.assertEqual(len(chunks), chunk_n)
예제 #4
0
    def test_airss(self):

        to_gen = 10

        # Load the Al.cell file (capture the annoying ASE output...)
        _stdout, sys.stdout = sys.stdout, StringIO()
        agen = airssGen(os.path.join(_TESTDATA_DIR, 'Al.cell'), n=to_gen)

        try:
            acoll = AtomsCollection(agen)
        except RuntimeError as e:
            if 'Buildcell' in str(e):
                sys.stdout = _stdout
                # Then we just don't have the program
                print('WARNING - The AIRSS generator could not be tested as '
                      'no AIRSS installation has been found on this system.')
                return
            else:
                raise e

        sys.stdout = _stdout

        # Some basic checks
        self.assertEqual(acoll.length, to_gen)
        self.assertTrue(
            all([chem == 'Al8' for chem in acoll.all.get_chemical_formula()]))
예제 #5
0
    def test_calculator(self):

        from ase.calculators.test import TestPotential
        from ase.calculators.lj import LennardJones

        # Generate a few random structures
        elrnd = ['H', 'C', 'O', 'N']
        asernd = []
        for n in range(4):
            aselen = np.random.randint(2, 10)
            asernd.append(
                Atoms(symbols=np.random.choice(elrnd, aselen),
                      positions=np.random.random((aselen, 3))))

        testcoll = AtomsCollection(asernd)

        testcoll.set_calculators(LennardJones,
                                 labels=['a', 'b', 'c', 'd'],
                                 params={'epsilon': 1.2})
        testcoll.run_calculators()

        def extract_nrg(s):
            return s.calc.results['energy']

        energies = np.array(testcoll.all.map(extract_nrg))

        self.assertTrue(not np.isnan(energies).any())
예제 #6
0
    def test_diprotavg(self):
        # Test dipolar rotational averaging

        eth = io.read(os.path.join(_TESTDATA_DIR, 'ethanol.magres'))

        from ase.quaternions import Quaternion
        from soprano.properties.transform import Rotate
        from soprano.collection import AtomsCollection
        from soprano.collection.generate import transformGen
        from soprano.properties.nmr import DipolarTensor

        N = 30  # Number of averaging steps
        axis = np.array([1.0, 1.0, 0])
        axis /= np.linalg.norm(axis)

        rot = Rotate(quaternion=Quaternion.from_axis_angle(
            axis, 2*np.pi/N))

        rot_eth = AtomsCollection(transformGen(eth, rot, N))

        rot_dip = [D[(0, 1)]
                   for D in DipolarTensor.get(rot_eth, sel_i=[0], sel_j=[1])]

        dip_avg_num = np.average(rot_dip, axis=0)

        dip_tens = NMRTensor.make_dipolar(eth, 0, 1, rotation_axis=axis)
        dip_avg_tens = dip_tens.data

        self.assertTrue(np.allclose(dip_avg_num, dip_avg_tens))

        # Test eigenvectors
        evecs = dip_tens.eigenvectors
        self.assertTrue(np.allclose(np.dot(evecs.T, evecs), np.eye(3)))
예제 #7
0
    def test_cluster(self):

        a1 = Atoms(cell=[1, 1, 1], info={'name': 'a1'})
        a2 = Atoms(cell=[1, 1, 2], info={'name': 'a2'})
        a3 = Atoms(cell=[3, 3, 3], info={'name': 'a3'})

        c1 = AtomsCollection([a1, a2, a3])
        p1 = PhylogenCluster(c1, [Gene('latt_abc_len', 1.0, {})])

        clinds, clslice = p1.get_hier_clusters(0.5, method='complete')

        getname = lambda a: a.info['name']

        cnames = [sorted(c1[sl].all.map(getname)) for sl in clslice]
        cnames.sort(key=lambda x: len(x))

        self.assertTrue(cnames == [['a3'], ['a1', 'a2']])

        # Retry with k-means

        clustc = p1.get_kmeans_clusters(2)

        cnames = [sorted(c1[sl].all.map(getname)) for sl in clslice]
        cnames.sort(key=lambda x: len(x))

        self.assertTrue(cnames == [['a3'], ['a1', 'a2']])
예제 #8
0
    def test_tree(self):

        aselist = [Atoms('C'), Atoms('H'), Atoms('N'), Atoms('O')]

        testcoll = AtomsCollection(aselist)

        testcoll.save_tree('test_save', 'xyz', safety_check=2)
        loadcoll = AtomsCollection.load_tree('test_save', 'xyz')

        self.assertTrue(''.join(loadcoll.all.get_chemical_formula()) == 'CHNO')

        # Try a custom save format
        def custom_save(a, path, format_string):
            with open(os.path.join(path, 'struct.custom'), 'w') as f:
                f.write(format_string.format(''.join(
                    a.get_chemical_formula())))

        def custom_load(path, marker):
            with open(os.path.join(path, 'struct.custom')) as f:
                l = f.read().strip()
                return Atoms(l.split(marker)[1].strip())

        testcoll.save_tree('test_save',
                           custom_save,
                           opt_args={'format_string': 'Formula:{0}'},
                           safety_check=2)
        loadcoll = AtomsCollection.load_tree('test_save',
                                             custom_load,
                                             opt_args={'marker': ':'})

        self.assertTrue(''.join(loadcoll.all.get_chemical_formula()) == 'CHNO')
예제 #9
0
def generate_muairss_collection(struct, params):

    if params["mu_symbol"] in struct.get_chemical_symbols():
        print(
            "WARNING: chosen muon symbol conflicts with existing elements in"
            " the starting unit cell. This could cause mistakes"
        )

    # Make a supercell
    sm = make_3x3(params["supercell"])
    # ASE's make_supercell is weird, avoid if not necessary...
    smdiag = np.diag(sm).astype(int)
    if np.all(np.diag(smdiag) == sm):
        scell0 = struct.repeat(smdiag)
    else:
        scell0 = make_supercell(struct, sm)

    reduced_struct = find_primitive_structure(struct)

    print("Generating defect configurations...")
    # Seed the random generator
    Random.reseed(params["random_seed"])
    # Now generate the defect configurations
    defect_gen = defectGen(
        reduced_struct,
        "H",
        poisson_r=params["poisson_r"],
        vdw_scale=params["vdw_scale"],
    )
    defect_collection = AtomsCollection(defect_gen)
    print("{0} configurations generated".format(len(defect_collection)))

    collection = []
    for atoms in defect_collection:
        # Where's the muon?
        # We rely on the fact that it's always put at the first place
        mupos = atoms.get_positions()[0]
        scell = scell0.copy() + Atoms(
            "H", positions=[mupos], masses=[constants.m_mu_amu]
        )
        # Add castep custom species
        csp = scell0.get_chemical_symbols() + [params["mu_symbol"]]
        scell.set_array("castep_custom_species", np.array(csp))
        scell.set_pbc(params["dftb_pbc"])
        collection.append(scell)

    return AtomsCollection(collection)
예제 #10
0
    def test_propertymap(self):

        from soprano.properties.basic import NumAtoms

        num_atoms = np.random.randint(1, 11, size=10)
        coll = AtomsCollection([Atoms('H'*n) for n in num_atoms])

        num_atoms_prop = coll.all.map(NumAtoms.get)

        self.assertTrue((num_atoms == num_atoms_prop).all)
예제 #11
0
    def test_loadarray(self):

        g0 = Gene('latt_abc_len', 1.0, {})

        c1 = AtomsCollection([Atoms('C'), Atoms('C')])
        c1.set_array('latt_abc_len', [1, 2])
        p1 = PhylogenCluster(c1, [])

        p1.set_genes([g0], load_arrays=True)

        self.assertTrue(np.all(p1.get_genome_vectors()[0] == [[1], [2]]))
예제 #12
0
    def test_mapsel(self):

        from soprano.collection import AtomsCollection

        rand_el = ['H' if x > 0.5 else 'C' for x in np.random.random(10)]
        coll = AtomsCollection([Atoms(el) for el in rand_el])

        h_sel = coll.all.map(AtomSelection.from_element, element='H')

        self.assertTrue(all([len(s) == 1 if rand_el[i] == 'H'
                             else len(s) == 0
                             for i, s in enumerate(h_sel)]))
예제 #13
0
    def test_gene(self):

        c1 = AtomsCollection([
            Atoms('CCC',
                  positions=[[0, 0, 0], [0.2, 0, 0], [0.85, 0, 0]],
                  cell=[1] * 3)
        ])
        g1 = Gene('linkage_list', 1.0, {'size': 3})
        p1 = PhylogenCluster(c1, [g1])

        graw = p1.get_genome_vectors()
        self.assertTrue(np.allclose(graw[0], [0.15, 0.2, 0.35]))
예제 #14
0
    def test_sum(self):

        # Generate a few random structures
        elrnd = ['H', 'C', 'O', 'N']
        asernd = []
        for n in range(4):
            aselen = np.random.randint(1, 10)
            asernd.append(
                Atoms(symbols=np.random.choice(elrnd, aselen),
                      positions=np.random.random((aselen, 3))))

        testcoll1 = AtomsCollection(asernd[:2])
        testcoll2 = AtomsCollection(asernd[2:])

        testcoll1.set_array('joint', ['t', 'e'])
        testcoll2.set_array('joint', ['s', 't'])

        testcoll = testcoll1
        testcoll += testcoll2

        self.assertTrue(''.join(testcoll.get_array('joint')) == 'test')
예제 #15
0
    def test_linspace(self):

        a1 = Atoms('CO', [[0.0, 0.0, 0.0], [0.0, 0.2, 0.0]], cell=[1] * 3)
        a2 = Atoms('CO', [[0.0, 0.0, 0.0], [0.0, 0.8, 0.0]], cell=[1] * 3)

        lgen = linspaceGen(a1, a2, steps=5, periodic=False)
        lcoll = AtomsCollection(lgen)

        self.assertTrue(
            np.all(
                np.isclose(lcoll.all.get_positions()[:, 1, 1],
                           np.linspace(0.2, 0.8, 5))))

        # With periodicity
        lgen = linspaceGen(a1, a2, steps=5, periodic=True)
        lcoll = AtomsCollection(lgen)

        self.assertTrue(
            np.all(
                np.isclose(lcoll.all.get_positions()[:, 1, 1],
                           np.linspace(0.2, -0.2, 5))))
예제 #16
0
def generate_muairss_collection(struct, params):

    if params['mu_symbol'] in struct.get_chemical_symbols():
        print('WARNING: chosen muon symbol conflicts with existing elements in'
              ' the starting unit cell. This could cause mistakes')

    # Make a supercell
    sm = make_3x3(params['supercell'])
    # ASE's make_supercell is weird, avoid if not necessary...
    smdiag = np.diag(sm).astype(int)
    if np.all(np.diag(smdiag) == sm):
        scell0 = struct.repeat(smdiag)
    else:
        scell0 = make_supercell(struct, sm)

    reduced_struct = find_primitive_structure(struct)

    print('Generating defect configurations...')
    # Now generate the defect configurations
    defect_gen = defectGen(reduced_struct,
                           'H',
                           poisson_r=params['poisson_r'],
                           vdw_scale=params['vdw_scale'])
    defect_collection = AtomsCollection(defect_gen)
    print('{0} configurations generated'.format(len(defect_collection)))

    collection = []
    for atoms in defect_collection:
        # Where's the muon?
        # We rely on the fact that it's always put at the first place
        mupos = atoms.get_positions()[0]
        scell = scell0.copy() + Atoms('H', positions=[mupos])
        # Add castep custom species
        csp = scell0.get_chemical_symbols() + [params['mu_symbol']]
        scell.set_array('castep_custom_species', np.array(csp))
        scell.set_pbc(params['dftb_pbc'])
        collection.append(scell)

    return AtomsCollection(collection)
예제 #17
0
    def test_slices(self):

        aselist = [Atoms('C'), Atoms('C'), Atoms('H'), Atoms('C')]

        testcoll = AtomsCollection(aselist)

        coll_h = testcoll[2]
        coll_c = testcoll[(0, 1, 3)]

        self.assertTrue(
            all([f == 'H' for f in coll_h.all.get_chemical_formula()]))
        self.assertTrue(
            all([f == 'C' for f in coll_c.all.get_chemical_formula()]))
예제 #18
0
    def test_customgene(self):

        c1 = AtomsCollection(
            [Atoms('C', positions=[[i / 5.0, 0, 0]]) for i in range(5)])

        # First, check failure
        self.assertRaises(RuntimeError, Gene, 'first_x_coord')

        # Then actual success
        def first_x_parser(c):
            return np.array(c.all.get_positions())[:, 0, 0]

        g1 = Gene('first_x_coord', parser=first_x_parser)
        self.assertTrue(np.all(g1.evaluate(c1) == [i / 5.0 for i in range(5)]))
예제 #19
0
    def test_save(self):

        # Test saving and loading collection
        testcoll = AtomsCollection([Atoms('H')])
        testcoll.set_array('test', np.array([2]))

        outf = os.path.join(_TEST_DIR, 'collection.pkl')
        testcoll.save(outf)

        # Reload
        testcoll = AtomsCollection.load(outf)
        self.assertEqual(testcoll.get_array('test')[0], 2)

        os.remove(outf)
예제 #20
0
    def test_loadgene(self):

        gfpath = os.path.join(_TESTDATA_DIR, 'testfile.gene')

        g0 = Gene('latt_abc_len', 1.0, {})
        g1 = Gene('linkage_list', 0.5, {'size': 3})
        glist = load_genefile(gfpath)

        self.assertEqual(glist[0], g0)
        self.assertEqual(glist[1], g1)

        # Now test that it works also when instantiating a cluster
        c1 = AtomsCollection(
            [Atoms('CCC', positions=np.random.random((3, 3)), cell=[1] * 3)])
        p1 = PhylogenCluster(c1, gfpath)
예제 #21
0
    def test_rattle(self):

        a = Atoms('CO', [[0.0, 0.0, 0.0], [0.0, 0.5, 0.0]])
        pos = a.get_positions()

        # Some exception tests
        wronggen = rattleGen(a, [3, 4, 5])
        self.assertRaises(ValueError, next, wronggen)
        wronggen = rattleGen(a, [[1, 2], [4, 5]])
        self.assertRaises(ValueError, next, wronggen)

        rgen = rattleGen(a, [[0.01, 0, 0], [0, 0.02, 0]])
        rcoll = AtomsCollection(rgen)
        rpos = rcoll.all.get_positions()

        self.assertTrue(np.all(np.abs((rpos - pos)[:, 0, 0]) <= 0.01))
        self.assertTrue(np.all(np.abs((rpos - pos)[:, 1, 1]) <= 0.02))
예제 #22
0
    def test_defect(self):

        si2 = bulk('Si')

        poisson_r = 0.5

        dGen = defectGen(si2, 'H', poisson_r)
        dColl = AtomsCollection(dGen)

        dPos = dColl.all.get_positions()[:, 0]

        holds = True
        for i, p1 in enumerate(dPos[:-1]):
            vecs, _ = minimum_periodic(dPos[i + 1:] - p1, si2.get_cell())
            p_holds = (np.linalg.norm(vecs, axis=1) >= poisson_r).all()
            holds = holds and p_holds

        self.assertTrue(holds)
예제 #23
0
    def test_sorting(self):

        # Generate a few structures
        struct_n = 5

        aselist = []
        for n in range(struct_n):
            aselist.append(Atoms())

        testcoll = AtomsCollection(aselist)

        testcoll.set_array('sorter', np.array(range(struct_n, 0, -1)))
        testcoll.set_array('sorted', np.array(range(1, struct_n + 1)))

        testcoll = testcoll.sorted_byarray('sorter')

        self.assertTrue(
            np.all(testcoll.get_array('sorted') == range(struct_n, 0, -1)))
예제 #24
0
    def test_coordhist(self):

        ala = ase.io.read(os.path.join(_TESTDATA_DIR, 'mol_crystal.cif'))
        nh3 = ase.io.read(os.path.join(_TESTDATA_DIR, 'nh3.cif'))

        c = AtomsCollection([ala, nh3])

        g = Gene('coord_histogram')
        h = g.evaluate(c)

        self.assertTrue((h[0] == [0, 4, 0, 4, 0, 0, 0]).all())
        self.assertTrue((h[1] == 0).all())  # No C at all in this one

        # Now for something different...
        g = Gene('coord_histogram', params={'s1': 'N'})
        h = g.evaluate(c)

        self.assertTrue((h[0] == [0, 0, 0, 4, 0, 0, 0]).all())  # 4 NH3 groups
        self.assertTrue((h[1] == [0, 0, 0, 1, 0, 0, 0]).all())
예제 #25
0
    def test_arrays(self):

        # Generate a few random structures
        elrnd = ['H', 'C', 'O', 'N']
        asernd = []
        for n in range(4):
            aselen = np.random.randint(1, 10)
            asernd.append(
                Atoms(symbols=np.random.choice(elrnd, aselen),
                      positions=np.random.random((aselen, 3))))

        testcoll = AtomsCollection(asernd)
        # Now try assigning some arrays
        arr = np.arange(testcoll.length)
        testcoll.set_array('testarr', arr, shape=(1, ))
        testcoll.set_array('testarr_2', list(zip(arr, arr)), shape=(2, ))
        testcoll.set_array('testarr_func',
                           lambda a: len(a.get_positions()),
                           shape=(1, ))

        self.assertTrue(np.all(testcoll.get_array('testarr') == arr))
예제 #26
0
def muon_vibrational_average_write(cell_file,
                                   method='independent',
                                   mu_index=-1,
                                   mu_symbol='H:mu',
                                   grid_n=20,
                                   sigma_n=3,
                                   avgprop='hyperfine',
                                   calculator='castep',
                                   displace_T=0,
                                   phonon_source_file=None,
                                   phonon_source_type='castep',
                                   **kwargs):
    """
    Write input files to compute a vibrational average for a quantity on a muon
    in a given system.

    | Pars:
    |   cell_file (str):    Filename for input structure file
    |   method (str):       Method to use for the average. Options are 'independent',
    |                       'montecarlo'. Default is 'independent'.
    |   mu_index (int):     Position of the muon in the given cell file.
    |                       Default is -1.
    |   mu_symbol (str):    Use this symbol to look for the muon among
    |                       CASTEP custom species. Overrides muon_index if
    |                       present in cell.
    |   grid_n (int):       Number of configurations used for sampling. Applies slightly
    |                       differently to different schemes.
    |   sigma_n (int):      Number of sigmas of the harmonic wavefunction used
    |                       for sampling.
    |   avgprop (str):      Property to calculate and average. Default is 'hyperfine'.
    |   calculator (str):   Source of the property to calculate and average.
    |                       Can be 'castep' or 'dftb+'. Default is 'castep'.
    |   phonon_source (str):Source of the phonon data. Can be 'castep' or 'asedftbp'.
    |                       Default is 'castep'.
    |   **kwargs:           Other arguments (such as specific arguments for the given
    |                       phonon method)
    """

    # Open the structure file
    cell = io.read(cell_file)
    path = os.path.split(cell_file)[0]
    sname = seedname(cell_file)
    num_atoms = len(cell)

    cell.info['name'] = sname

    # Fetch species
    try:
        species = cell.get_array('castep_custom_species')
    except KeyError:
        species = np.array(cell.get_chemical_symbols())

    mu_indices = np.where(species == mu_symbol)[0]
    if len(mu_indices) > 1:
        raise MuonAverageError('More than one muon found in the system')
    elif len(mu_indices) == 1:
        mu_index = mu_indices[0]
    else:
        species = list(species)
        species[mu_index] = mu_symbol
        species = np.array(species)

    cell.set_array('castep_custom_species', species)

    # Load the phonons
    if phonon_source_type == 'castep':
        if phonon_source_file is not None:
            phpath, phfile = os.path.split(phonon_source_file)
            phfile = seedname(phfile)
        else:
            phpath = path
            phfile = sname
        ph_evals, ph_evecs = read_castep_gamma_phonons(phfile, phpath)
    elif phonon_source_type == 'dftb+':
        if phonon_source_file is None:
            phonon_source_file = os.path.join(path, sname + '.phonons.pkl')

        with open(phonon_source_file, 'rb') as f:
            phdata = pickle.load(f)
            # Find the gamma point
            gamma_i = None
            for i, p in enumerate(phdata.path):
                if (p == 0).all():
                    gamma_i = i
                    break
            try:
                ph_evals = phdata.frequencies[gamma_i]
                ph_evecs = phdata.modes[gamma_i]
            except TypeError:
                raise RuntimeError(('Phonon file {0} does not contain gamma '
                                    'point data').format(phonon_source_file))

    # Fetch masses
    try:
        masses = parse_castep_masses(cell)
    except AttributeError:
        # Just fall back on ASE standard masses if not available
        masses = cell.get_masses()
    masses[mu_index] = constants.m_mu_amu
    cell.set_masses(masses)

    # Now create the distribution scheme
    if method == 'independent':
        displsch = IndependentDisplacements(ph_evals, ph_evecs, masses,
                                            mu_index, sigma_n)
    elif method == 'montecarlo':
        displsch = MonteCarloDisplacements(ph_evals, ph_evecs, masses)

    displsch.recalc_displacements(n=grid_n, T=displace_T)

    # Make it a collection
    pos = cell.get_positions()
    displaced_cells = []
    for i, d in enumerate(displsch.displacements):
        dcell = cell.copy()
        dcell.set_positions(pos + d)
        if calculator == 'dftb' and not kwargs['dftb_pbc']:
            dcell.set_pbc(False)
        dcell.info['name'] = sname + '_displaced_{0}'.format(i)
        displaced_cells.append(dcell)

    if kwargs['write_allconf']:
        # Write a global configuration structure
        allconf = sum(displaced_cells, cell.copy())
        if all(allconf.get_pbc()):
            io.write(sname + '_allconf.cell', allconf)
        else:
            io.write(sname + '_allconf.xyz', allconf)

    # Get a calculator
    if calculator == 'castep':
        writer_function = castep_write_input
        calc = create_hfine_castep_calculator(
            mu_symbol=mu_symbol,
            calc=cell.calc,
            param_file=kwargs['castep_param'],
            kpts=kwargs['k_points_grid'])
    elif calculator == 'dftb+':
        writer_function = dftb_write_input
        kpts = kwargs['k_points_grid'] if kwargs['dftb_pbc'] else None
        calc = create_spinpol_dftbp_calculator(param_set=kwargs['dftb_set'],
                                               kpts=kpts)

    displaced_coll = AtomsCollection(displaced_cells)
    displaced_coll.info['displacement_scheme'] = displsch
    displaced_coll.info['muon_index'] = mu_index
    displaced_coll.save_tree(sname + '_displaced',
                             writer_function,
                             opt_args={
                                 'calc': calc,
                                 'script': kwargs['script_file']
                             })
예제 #27
0
import ase
from ase import io as ase_io

from soprano.collection import AtomsCollection
"""
1 - LOADING STRUCTURES

Soprano can handle multiple structure loading into a single AtomsCollection object.
The structures are loaded singularly as ASE (Atomic Simulation Environment) Atoms objects.
"""

# List all files in the tutorial directory
cifs = glob.glob(os.path.join(datapath, 'struct*.cif'))

aColl = AtomsCollection(
    cifs, progress=True)  # "progress" means we will visualize a loading bar
print
"""
2 - HANDLING COLLECTIONS

Collections are a convenient way of manipulating multiple structures. They allow for many operations that act
collectively on all Atoms objects, or return values from them all at once.
"""

# To access an individual structure, one can simply use indexing:
a0 = aColl.structures[0]
print '---- struct_0.cif positions ----\n'
print a0.get_positions(), '\n\n'

# All properties and methods of Atoms objects are available on an entire collection too, by using
# the meta-element 'all'
예제 #28
0
def write_cluster_report(args, params, clusters):

    if params['clustering_method'] == 'hier':
        clustinfo = """
Clustering method: Hierarchical
    t = {t}
""".format(t=params['clustering_hier_t'])
    elif params['clustering_method'] == 'kmeans':
        clustinfo = """
Clustering method: k-Means
    k = {k}
""".format(k=params['clustering_kmeans_k'])

    with open(params['name'] + '_clusters.txt', 'w') as f:

        f.write("""
****************************
|                          |
|         MUAIRSS          |
|    Clustering report     |
|                          |
****************************

Name: {name}
Date: {date}
Structure file(s): {structs}
Parameter file: {param}
{clustinfo}

*******************

""".format(name=params['name'],
           date=datetime.now(),
           structs=args.structures,
           param=args.parameter_file,
           clustinfo=clustinfo))

        for name, cdata in clusters.items():

            f.write('Clusters for {0}:\n'.format(name))

            if params['clustering_save_min'] is not None or \
                    params['clustering_save_type'] is not None:

                if params['clustering_save_folder'] is not None:
                    clustering_save_path = safe_create_folder(
                        params['clustering_save_folder'])
                else:
                    clustering_save_path = safe_create_folder(
                        '{0}_clusters'.format(params['name']))
                if not clustering_save_path:
                    raise RuntimeError('Could not create folder {0}')

            for calc, clusts in cdata.items():

                # Computer readable
                fdat = open(
                    params['name'] +
                    '_{0}_{1}_clusters.dat'.format(name, calc), 'w')

                f.write('CALCULATOR: {0}\n'.format(calc))
                (cinds, cgroups), ccolls, gvecs = clusts

                f.write('\t{0} clusters found\n'.format(max(cinds)))

                min_energy_structs = []

                for i, g in enumerate(cgroups):

                    f.write('\n\n\t-----------\n\tCluster '
                            '{0}\n\t-----------\n'.format(i + 1))
                    f.write('\tStructures: {0}\n'.format(len(g)))
                    coll = ccolls[i + 1]
                    E = gvecs[g, 0]
                    Emin = np.amin(E)
                    Eavg = np.average(E)
                    Estd = np.std(E)

                    f.write('\n\tEnergy (eV):\n')
                    f.write('\tMinimum\t\tAverage\t\tStDev\n')
                    f.write('\t{0:.2f}\t\t{1:.2f}\t\t{2:.2f}\n'.format(
                        Emin, Eavg, Estd))

                    fdat.write('\t'.join(
                        map(str, [
                            i + 1,
                            len(g), Emin, Eavg, Estd, coll[np.argmin(
                                E)].structures[0].positions[-1][0],
                            coll[np.argmin(E)].structures[0].positions[-1][1],
                            coll[np.argmin(E)].structures[0].positions[-1][2]
                        ])) + '\n')

                    f.write('\n\tMinimum energy structure: {0}\n'.format(
                        coll[np.argmin(E)].structures[0].info['name']))

                    # Save minimum energy structure
                    if params['clustering_save_type'] == 'structures' or \
                            params['clustering_save_min']:
                        # For backwards-compatability with old pymuonsuite
                        # versions
                        if params['clustering_save_min']:
                            if params['clustering_save_format'] is None:
                                params['clustering_save_format'] = 'cif'

                        try:
                            calc_path = os.path.join(clustering_save_path,
                                                     calc)
                            if not os.path.exists(calc_path):
                                os.mkdir(calc_path)
                            fname = ('{0}_{1}_min_cluster_'
                                     '{2}.{3}'.format(
                                         params['name'], calc, i + 1,
                                         params['clustering_save_format']))
                            with silence_stdio():
                                io.write(os.path.join(calc_path, fname),
                                         coll[np.argmin(E)].structures[0])
                        except (io.formats.UnknownFileTypeError) as e:
                            print("ERROR: File format '{0}' is not "
                                  "recognised. Modify 'clustering_save_format'"
                                  " and try again.".format(e))
                            return
                        except ValueError as e:
                            print("ERROR: {0}. Modify 'clustering_save_format'"
                                  "and try again.".format(e))
                            return

                    min_energy_structs.append(coll[np.argmin(E)].structures[0])

                    f.write('\n\n\tStructure list:')

                    for j, s in enumerate(coll):
                        if j % 4 == 0:
                            f.write('\n\t')
                        f.write('{0}\t'.format(s.info['name']))

                fdat.close()

                if params['clustering_save_type'] == 'input':
                    calc_path = os.path.join(clustering_save_path, calc)

                    sname = "{0}_min_cluster".format(params['name'])

                    io_formats = {
                        'castep': ReadWriteCastep,
                        'dftb+': ReadWriteDFTB,
                    }
                    try:
                        write_method = io_formats[
                            params['clustering_save_format']](params).write
                    except KeyError as e:
                        print("ERROR: Calculator type {0} is not "
                              "recognised. Modify 'clustering_save_format'"
                              " to be one of: {1}".format(
                                  e, list(io_formats.keys())))
                        return

                    if params['clustering_save_format'] == 'dftb+':
                        from pymuonsuite.data.dftb_pars import get_license
                        with open(
                                os.path.join(clustering_save_path,
                                             'dftb.LICENSE'),
                                'w') as license_file:
                            license_file.write(get_license())

                    min_energy_structs = AtomsCollection(min_energy_structs)
                    # here we remove the structure's name so the original
                    # numbering of the structs is removed:
                    for i, a in enumerate(min_energy_structs):
                        min_energy_structs.structures[i].info.pop('name', None)

                    min_energy_structs.save_tree(
                        calc_path,
                        write_method,
                        name_root=sname,
                        opt_args={'calc_type': 'GEOM_OPT'},
                        safety_check=2)

                # Print distance matrix

                f.write('\n\n\t----------\n\n\tSimilarity (ranked):\n')

                centers = np.array(
                    [np.average(gvecs[g], axis=0) for g in cgroups])
                dmat = np.linalg.norm(centers[:, None] - centers[None, :],
                                      axis=-1)

                inds = np.triu_indices(len(cgroups), k=1)
                for i in np.argsort(dmat[inds]):
                    c1 = inds[0][i]
                    c2 = inds[1][i]
                    d = dmat[c1, c2]
                    f.write('\t{0} <--> {1} (distance = {2:.3f})\n'.format(
                        c1 + 1, c2 + 1, d))

            f.write('\n--------------------------\n\n')

        f.write('\n==========================\n\n')
예제 #29
0
def muon_vibrational_average_write(structure,
                                   method="independent",
                                   mu_index=-1,
                                   mu_symbol="H:mu",
                                   grid_n=20,
                                   sigma_n=3,
                                   avgprop="hyperfine",
                                   calculator="castep",
                                   displace_T=0,
                                   phonon_source_file=None,
                                   phonon_source_type="castep",
                                   **kwargs):
    """
    Write input files to compute a vibrational average for a quantity on a muon
    in a given system.

    | Pars:
    |   structure (str):    Filename for input structure file
    |   method (str):       Method to use for the average. Options are
    |                       'independent', 'montecarlo'.
    |                       Default is 'independent'.
    |   mu_index (int):     Position of the muon in the given cell file.
    |                       Default is -1.
    |   mu_symbol (str):    Use this symbol to look for the muon among
    |                       CASTEP custom species. Overrides muon_index if
    |                       present in cell.
    |   grid_n (int):       Number of configurations used for sampling.
    |                       Applies slightly
    |                       differently to different schemes.
    |   sigma_n (int):      Number of sigmas of the harmonic wavefunction used
    |                       for sampling.
    |   avgprop (str):      Property to calculate and average. Default is
    |                       'hyperfine'.
    |   calculator (str):   Source of the property to calculate and average.
    |                       Can be 'castep' or 'dftb+'. Default is 'castep'.
    |   phonon_source (str):Source of the phonon data. Can be 'castep' or
    |                       'asedftbp'. Default is 'castep'.
    |   **kwargs:           Other arguments (such as specific arguments for
    |                       the given phonon method)
    """

    # Open the structure file
    with silence_stdio():
        cell = io.read(structure)
    path = os.path.split(structure)[0]
    sname = seedname(structure)

    cell.info["name"] = sname

    # Fetch species
    try:
        species = cell.get_array("castep_custom_species")
    except KeyError:
        species = np.array(cell.get_chemical_symbols())

    mu_indices = np.where(species == mu_symbol)[0]
    if len(mu_indices) > 1:
        raise MuonAverageError("More than one muon found in the system")
    elif len(mu_indices) == 1:
        mu_index = mu_indices[0]
    else:
        species = list(species)
        species[mu_index] = mu_symbol
        species = np.array(species)

    cell.set_array("castep_custom_species", species)

    io_formats = {"castep": ReadWriteCastep, "dftb+": ReadWriteDFTB}

    # Load the phonons
    if phonon_source_file is not None:
        phpath, phfile = os.path.split(phonon_source_file)
        phfile = seedname(seedname(phfile))  # have to do twice for dftb case
    else:
        phpath = path
        phfile = sname

    try:
        rw = io_formats[phonon_source_type]()
        atoms = rw.read(phpath, phfile, read_phonons=True)
        ph_evals = atoms.info["ph_evals"]
        ph_evecs = atoms.info["ph_evecs"]
    except IOError:
        raise
        return
    except KeyError:
        phonon_source_file = os.path.join(phpath, phfile + ".phonon")
        if phonon_source_type == "dftb+":
            phonon_source_file = phonon_source_file + "s.pkl"
        raise (IOError(
            "Phonon file {0} could not be read.".format(phonon_source_file)))
        return

    # Fetch masses
    try:
        masses = parse_castep_masses(cell)
    except AttributeError:
        # Just fall back on ASE standard masses if not available
        masses = cell.get_masses()
    masses[mu_index] = constants.m_mu_amu
    cell.set_masses(masses)

    # Now create the distribution scheme
    if method == "independent":
        displsch = IndependentDisplacements(ph_evals, ph_evecs, masses,
                                            mu_index, sigma_n)
    elif method == "montecarlo":
        # Set seed
        np.random.seed(kwargs["random_seed"])
        displsch = MonteCarloDisplacements(ph_evals, ph_evecs, masses)

    displsch.recalc_displacements(n=grid_n, T=displace_T)

    # Make it a collection
    pos = cell.get_positions()
    displaced_cells = []
    for i, d in enumerate(displsch.displacements):
        dcell = cell.copy()
        dcell.set_positions(pos + d)
        if calculator == "dftb" and not kwargs["dftb_pbc"]:
            dcell.set_pbc(False)
        dcell.info["name"] = sname + "_displaced_{0}".format(i)
        displaced_cells.append(dcell)

    if kwargs["write_allconf"]:
        # Write a global configuration structure
        allconf = sum(displaced_cells, cell.copy())
        with silence_stdio():
            if all(allconf.get_pbc()):
                io.write(sname + "_allconf.cell", allconf)
            else:
                io.write(sname + "_allconf.xyz", allconf)

    # Get a calculator
    if calculator == "castep":
        params = {
            "castep_param": kwargs["castep_param"],
            "k_points_grid": kwargs["k_points_grid"],
            "mu_symbol": mu_symbol,
        }
        io_format = ReadWriteCastep(params=params,
                                    calc=cell.calc,
                                    script=kwargs["script_file"])
        opt_args = {"calc_type": "MAGRES"}

    elif calculator == "dftb+":
        params = {
            "dftb_set":
            kwargs["dftb_set"],
            "dftb_pbc":
            kwargs["dftb_pbc"],
            "k_points_grid":
            kwargs["k_points_grid"] if kwargs["dftb_pbc"] else None,
        }
        io_format = ReadWriteDFTB(params=params,
                                  calc=cell.calc,
                                  script=kwargs["script_file"])
        opt_args = {"calc_type": "SPINPOL"}

    displaced_coll = AtomsCollection(displaced_cells)
    displaced_coll.info["displacement_scheme"] = displsch
    displaced_coll.info["muon_index"] = mu_index
    displaced_coll.save_tree(sname + "_displaced",
                             io_format.write,
                             opt_args=opt_args)
예제 #30
0
parser.add_argument('-corrmat_minforce',     type=float, default=0.01,
                    help="Correlation matrix minimum force")
parser.add_argument('-corrmat_maxforce',     type=float, default=1.0,
                    help="Correlation matrix maximum force")
parser.add_argument('-savepkl',  type=str, default=None,
                    help="Directory to save collection as pickle file")

args = parser.parse_args()

# First, parse the gene list
gene_list = load_genefile(args.seedname + '.gene')

# Then create an AtomsCollection
if not args.pkl:
    aC = AtomsCollection(args.input_files,
                         info={'name': args.seedname},
                         cell_reduce=True,
                         progress=True)
else:
    aC = AtomsCollection([])
    for f in args.input_files:
        aC += AtomsCollection.load(f)

# Take only n lowest energies if required
if (args.n is not None):
    # Sort by energy
    CalcEnergy.get(aC, store_array=True)
    aC = aC.sorted_byarray('calc_energy')[:args.n]

#discard_Z = args.Z is not None

# Calculate genomes