def predict_on_structure(structure: Structure, gp: GaussianProcess, n_cpus: int = None) -> ('np.ndarray', 'np.ndarray'): """ Return the forces/std. dev. uncertainty associated with each individual atom in a structure. Forces are stored directly to the structure and are also returned. :param structure: FLARE structure to obtain forces for, with N atoms :param gp: Gaussian Process model :return: N x 3 numpy array of foces, Nx3 numpy array of uncertainties :rtype: (np.ndarray, np.ndarray) """ # Loop through individual atoms, cast to atomic environments, # make predictions for n in range(structure.nat): chemenv = AtomicEnvironment(structure, n, gp.cutoffs) for i in range(3): force, var = gp.predict(chemenv, i + 1) structure.forces[n][i] = float(force) structure.stds[n][i] = np.sqrt(np.abs(var)) forces = np.array(structure.forces) stds = np.array(structure.stds) return forces, stds
def predict_on_structure_par_en(structure: Structure, gp: GaussianProcess, no_cpus=None): if (no_cpus is 1): predict_on_structure_en(structure, gp) atom_list = [(structure, atom, gp) for atom in range(structure.nat)] local_energies = [0 for n in range(structure.nat)] results = [] if (no_cpus is None): pool = mp.Pool(processes=mp.cpu_count()) else: pool = mp.Pool(processes=no_cpus) for atom in range(structure.nat): results.append( pool.apply_async(predict_on_atom_en, args=[(structure, atom, gp)])) pool.close() pool.join() for i in range(structure.nat): r = results[i].get() structure.forces[i] = r[0] structure.stds[i] = r[1] local_energies[i] = r[2] forces = np.array(structure.forces) stds = np.array(structure.stds) return forces, stds, local_energies
def __init__(self, dt: float, number_of_steps: int, gp: GaussianProcess, pos_init: np.ndarray, species, cell, masses, prev_pos_init: np.ndarray=None, par: bool=False, skip: int=0, output_name='otf_run.out'): self.dt = dt self.Nsteps = number_of_steps self.gp = gp self.structure = Structure(cell=cell, species=species, positions=pos_init, mass_dict=masses, prev_positions=prev_pos_init) self.noa = self.structure.positions.shape[0] self.atom_list = list(range(self.noa)) self.curr_step = 0 # choose prediction function if par is True: self.pred_func = self.predict_on_structure_par_en else: self.pred_func = self.predict_on_structure_en # initialize local energies self.local_energies = np.zeros(self.noa) self.pes = [] self.kes = [] self.output = Output(output_name)
def test_from_pmg_structure(): pmg_struc = pmgstruc.Structure(lattice= np.eye(3), species=['H'], coords=[[.25, .5, 0]], site_properties={ 'force': [np.array((1., 1., 1.))], 'std':[np.array((1., 1., 1.))]}, coords_are_cartesian=True) new_struc = Structure.from_pmg_structure(pmg_struc) assert len(new_struc) == 1 assert np.equal(new_struc.positions, np.array([.25, .5, 0])).all() assert new_struc.coded_species == [1] assert new_struc.species_labels[0] == 'H' assert np.equal(new_struc.forces, np.array([1., 1., 1.])).all() pmg_struc = pmgstruc.Structure(lattice= np.diag([1.2,0.8,1.5]), species=['H'], coords=[[.25, .5, 0]], site_properties={ 'force': [np.array((1., 1., 1.))], 'std':[np.array((1., 1., 1.))]}, coords_are_cartesian=True) new_struc = Structure.from_pmg_structure(pmg_struc) assert len(new_struc) == 1 assert np.equal(new_struc.positions, np.array([.25, .5, 0])).all() assert new_struc.coded_species == [1] assert new_struc.species_labels[0] == 'H' assert np.equal(new_struc.forces, np.array([1., 1., 1.])).all()
def varied_test_struc(): struc = Structure(np.eye(3), species=[1, 2, 2, 3, 3, 4, 4, 4, 4, 3], positions=np.array( [np.random.uniform(-1, 1, 3) for i in range(10)])) struc.forces = np.array([np.random.uniform(-1, 1, 3) for _ in range(10)]) return struc
def test_prev_positions_arg(): np.random.seed(0) positions = [] prev_positions = [] species = [1] * 5 cell = np.eye(3) for n in range(5): positions.append(np.random.uniform(-1, 1, 3)) prev_positions.append(np.random.uniform(-1, 1, 3)) test_structure1 = Structure(cell, species, positions) test_structure2 = Structure(cell, species, positions, prev_positions=positions) test_structure3 = Structure(cell, species, positions, prev_positions=prev_positions) assert np.equal(test_structure1.positions, test_structure2.positions).all() assert np.equal(test_structure1.prev_positions, test_structure2.prev_positions).all() assert np.equal(test_structure2.positions, test_structure2.prev_positions).all() assert not np.equal(test_structure3.positions, test_structure3.prev_positions).all()
def predict_on_structure_en( structure: Structure, gp: GaussianProcess, n_cpus: int = None) -> ('np.ndarray', 'np.ndarray', 'np.ndarray'): """ Return the forces/std. dev. uncertainty / local energy associated with each individual atom in a structure. Forces are stored directly to the structure and are also returned. :param structure: FLARE structure to obtain forces for, with N atoms :param gp: Gaussian Process model :param n_cpus: Dummy parameter passed as an argument to allow for flexibility when the callable may or may not be parallelized :return: N x 3 array of forces, N x 3 array of uncertainties, N-length array of energies :rtype: (np.ndarray, np.ndarray, np.ndarray) """ # Set up local energy array local_energies = np.array([0 for _ in range(structure.nat)]) # Loop through atoms in structure and predict forces, uncertainties, # and energies for n in range(structure.nat): chemenv = AtomicEnvironment(structure, n, gp.cutoffs) for i in range(3): force, var = gp.predict(chemenv, i + 1) structure.forces[n][i] = float(force) structure.stds[n][i] = np.sqrt(np.abs(var)) local_energies[n] = gp.predict_local_energy(chemenv) forces = np.array(structure.forces) stds = np.array(structure.stds) return forces, stds, local_energies
def otf_run(self, steps): """Perform a number of time steps.""" # initialize gp by a dft calculation if not self.atoms.calc.gp_model.training_data: self.dft_count = 0 self.std_in_bound = False self.target_atom = 0 self.stds = [] dft_forces = self.call_DFT() f = dft_forces # update gp model atom_struc = Structure(np.array(self.atoms.cell), self.atoms.get_atomic_numbers(), self.atoms.positions) self.atoms.calc.gp_model.update_db(atom_struc, dft_forces, custom_range=self.init_atoms) # train calculator self.train() print('mgp model:', self.atoms.calc.mgp_model) if self.md_engine == 'NPT': if not self.initialized: self.initialize() else: if self.have_the_atoms_been_changed(): raise NotImplementedError( "You have modified the atoms since the last timestep.") for i in range(steps): print('step:', i) if self.md_engine == 'NPT': self.step() else: f = self.step(f) self.nsteps += 1 self.stds = self.atoms.get_uncertainties() # figure out if std above the threshold self.call_observers() curr_struc = Structure.from_ase_atoms(self.atoms) curr_struc.stds = self.stds noise = self.atoms.calc.gp_model.hyps[-1] self.std_in_bound, self.target_atoms = is_std_in_bound(\ noise, self.std_tolerance, curr_struc, self.max_atoms_added) #self.is_std_in_bound([]) if not self.std_in_bound: # call dft/eam print('calling dft') dft_forces = self.call_DFT() # update gp print('updating gp') self.update_GP(dft_forces) self.observers[0][0].run_complete()
def predict_on_structure_en( structure: Structure, gp: GaussianProcess, n_cpus: int = None, write_to_structure: bool = True, selective_atoms: List[int] = None, skipped_atom_value=0) -> ('np.ndarray', 'np.ndarray', 'np.ndarray'): """ Return the forces/std. dev. uncertainty / local energy associated with each individual atom in a structure. Forces are stored directly to the structure and are also returned. :param structure: FLARE structure to obtain forces for, with N atoms :param gp: Gaussian Process model :param n_cpus: Dummy parameter passed as an argument to allow for flexibility when the callable may or may not be parallelized :return: N x 3 array of forces, N x 3 array of uncertainties, N-length array of energies :rtype: (np.ndarray, np.ndarray, np.ndarray) """ # Set up local energy array forces = np.zeros((structure.nat, 3)) stds = np.zeros((structure.nat, 3)) local_energies = np.zeros(structure.nat) forces = np.zeros(shape=(structure.nat, 3)) stds = np.zeros(shape=(structure.nat, 3)) if selective_atoms: forces.fill(skipped_atom_value) stds.fill(skipped_atom_value) local_energies.fill(skipped_atom_value) else: selective_atoms = [] # Loop through atoms in structure and predict forces, uncertainties, # and energies for n in range(structure.nat): if selective_atoms and n not in selective_atoms: continue chemenv = AtomicEnvironment(structure, n, gp.cutoffs, cutoffs_mask=gp.hyps_mask) for i in range(3): force, var = gp.predict(chemenv, i + 1) forces[n][i] = float(force) stds[n][i] = np.sqrt(np.abs(var)) if write_to_structure and structure.forces is not None: structure.forces[n][i] = float(force) structure.stds[n][i] = np.sqrt(np.abs(var)) local_energies[n] = gp.predict_local_energy(chemenv) return forces, stds, local_energies
def test_struc_to_ase(): from ase import Atoms uc = Structure(species=['Pd' for i in range(10)]+['Ag' for i in range(10)], positions=np.random.rand(20, 3), cell=np.random.rand(3, 3)) new_atoms = Structure.to_ase_atoms(uc) assert np.all(uc.species_labels == new_atoms.get_chemical_symbols()) assert np.all(uc.positions == new_atoms.get_positions()) assert np.all(uc.cell == new_atoms.get_cell())
def predict_on_structure(structure: Structure, gp: GaussianProcess): for n in range(structure.nat): chemenv = AtomicEnvironment(structure, n, gp.cutoffs) for i in range(3): force, var = gp.predict(chemenv, i + 1) structure.forces[n][i] = float(force) structure.stds[n][i] = np.sqrt(np.abs(var)) forces = np.array(structure.forces) stds = np.array(structure.stds) return forces, stds
def predict_on_structure( structure: Structure, gp: GaussianProcess, n_cpus: int = None, write_to_structure: bool = True, selective_atoms: List[int] = None, skipped_atom_value=0, ) -> ("np.ndarray", "np.ndarray"): """ Return the forces/std. dev. uncertainty associated with each individual atom in a structure. Forces are stored directly to the structure and are also returned. :param structure: FLARE structure to obtain forces for, with N atoms :param gp: Gaussian Process model :param write_to_structure: Write results to structure's forces, std attributes :param selective_atoms: Only predict on these atoms; e.g. [0,1,2] will only predict and return for those atoms :param skipped_atom_value: What value to use for atoms that are skipped. Defaults to 0 but other options could be e.g. NaN. Will NOT write this to the structure if write_to_structure is True. :return: N x 3 numpy array of foces, Nx3 numpy array of uncertainties :rtype: (np.ndarray, np.ndarray) """ forces = np.zeros((structure.nat, 3)) stds = np.zeros((structure.nat, 3)) if selective_atoms: forces.fill(skipped_atom_value) stds.fill(skipped_atom_value) else: selective_atoms = [] for n in range(structure.nat): # Skip the atoms which we aren't predicting on if # selective atoms is on. if n not in selective_atoms and selective_atoms: continue chemenv = AtomicEnvironment(structure, n, gp.cutoffs, cutoffs_mask=gp.hyps_mask) force, var = gp.predict_force_xyz(chemenv) std = np.sqrt(np.abs(var)) forces[n] = force stds[n] = std if write_to_structure: structure.forces[n] = force structure.stds[n] = std return forces, stds
def another_env(cutoffs, delt): cell = 10.0 * np.eye(3) # atomic structure 1 pos_1 = np.vstack([[0, 0, 0], 0.1*random([3, 3])]) pos_1[1, 1] += 1 pos_1[2, 0] += 1 pos_1[3, :2] += 1 pos_2 = deepcopy(pos_1) pos_2[0][0] = delt pos_3 = deepcopy(pos_1) pos_3[0][0] = -delt species_1 = [1, 1, 1, 1] test_structure_1 = Structure(cell, species_1, pos_1) test_structure_2 = Structure(cell, species_1, pos_2) test_structure_3 = Structure(cell, species_1, pos_3) # atom 0, original position env1_1_0 = AtomicEnvironment(test_structure_1, 0, cutoffs) # atom 0, 0 perturbe along x env1_2_0 = AtomicEnvironment(test_structure_2, 0, cutoffs) # atom 1, 0 perturbe along x env1_2_1 = AtomicEnvironment(test_structure_2, 1, cutoffs) # atom 2, 0 perturbe along x env1_2_2 = AtomicEnvironment(test_structure_2, 2, cutoffs) # atom 0, 0 perturbe along -x env1_3_0 = AtomicEnvironment(test_structure_3, 0, cutoffs) # atom 1, 0 perturbe along -x env1_3_1 = AtomicEnvironment(test_structure_3, 1, cutoffs) # atom 2, 0 perturbe along -x env1_3_2 = AtomicEnvironment(test_structure_3, 2, cutoffs) # create env 2 pos_1 = np.vstack([[0, 0, 0], 0.1*random([3, 3])]) pos_1[1, 1] += 1 pos_1[2, 0] += 1 pos_1[3, :2] += 1 pos_2 = deepcopy(pos_1) pos_2[0][0] = delt pos_3 = deepcopy(pos_1) pos_3[0][0] = -delt species_2 = [1, 2, 2, 1] test_structure_1 = Structure(cell, species_2, pos_1) env2_1_0 = AtomicEnvironment(test_structure_1, 0, cutoffs) return env1_1_0, env1_2_0, env1_3_0, \ env1_2_1, env1_3_1, env1_2_2, env1_3_2, env2_1_0
def predict_on_structure_par(structure: Structure, gp: GaussianProcess): n = 0 atom_list = [(structure, atom, gp) for atom in range(structure.nat)] with concurrent.futures.ProcessPoolExecutor() as executor: for res in executor.map(predict_on_atom, atom_list): for i in range(3): structure.forces[n][i] = res[0][i] structure.stds[n][i] = res[1][i] n += 1 forces = np.array(structure.forces) stds = np.array(structure.stds) return forces, stds
def test_uncertainty_threshold(fake_gp): tt = TrajectoryTrainer([], fake_gp, rel_std_tolerance=.5, abs_std_tolerance=.01) fake_structure = Structure(cell=np.eye(3), species=["H"], positions=np.array([[0, 0, 0]])) # Test a structure with no variance passes fake_structure.stds = np.array([[0, 0, 0]]) res1, res2 = tt.is_std_in_bound(fake_structure) assert res1 is True assert res2 == [-1] # Test that the absolute criteria trips the threshold fake_structure.stds = np.array([[.02, 0, 0]]) res1, res2 = tt.is_std_in_bound(fake_structure) assert res1 is False assert res2 == [0] tt.abs_std_tolerance = 100 # Test that the relative criteria trips the threshold fake_structure.stds = np.array([[.6, 0, 0]]) res1, res2 = tt.is_std_in_bound(fake_structure) assert res1 is False assert res2 == [0] # Test that 'test mode' works, where no GP modification occurs tt.abs_std_tolerance = 0 tt.rel_std_tolerance = 0 res1, res2 = tt.is_std_in_bound(fake_structure) assert res1 is True assert res2 == [-1] # Test permutations of one / another being off tt.abs_std_tolerance = 1 tt.rel_std_tolerance = 0 res1, res2 = tt.is_std_in_bound(fake_structure) assert res1 is True assert res2 == [-1] tt.abs_std_tolerance = 0 tt.rel_std_tolerance = 1 res1, res2 = tt.is_std_in_bound(fake_structure) assert res1 is True assert res2 == [-1]
def test_to_from_methods(varied_test_struc): test_dict = varied_test_struc.as_dict() assert isinstance(test_dict, dict) assert (test_dict["forces"] == varied_test_struc.forces).all() new_struc_1 = Structure.from_dict(test_dict) new_struc_2 = Structure.from_dict(loads(varied_test_struc.as_str())) for new_struc in [new_struc_1, new_struc_2]: assert np.equal(varied_test_struc.positions, new_struc.positions).all() assert np.equal(varied_test_struc.cell, new_struc.cell).all() assert np.equal(varied_test_struc.forces, new_struc.forces).all()
def test_struc_from_ase(): from ase import Atoms from ase.calculators.singlepoint import SinglePointCalculator results = { "forces": np.random.randn(20, 3), "energy": np.random.rand(), "stress": np.random.randn(6), } uc = Atoms( ["Pd" for i in range(10)] + ["Ag" for i in range(10)], positions=np.random.rand(20, 3), cell=np.random.rand(3, 3), ) calculator = SinglePointCalculator(uc, **results) uc.set_calculator(calculator) new_struc = Structure.from_ase_atoms(uc) assert np.all(new_struc.species_labels == uc.get_chemical_symbols()) assert np.all(new_struc.positions == uc.get_positions()) assert np.all(new_struc.cell == uc.get_cell()) assert np.all(new_struc.forces == results["forces"]) assert np.all(new_struc.energy == results["energy"]) assert np.all(new_struc.stress == results["stress"])
def test_auto_sweep(): """Test that the number of neighbors inside the local environment is correctly computed.""" # Make an arbitrary non-cubic structure. cell = np.array([[1.3, 0.5, 0.8], [-1.2, 1, 0.73], [-0.8, 0.1, 0.9]]) positions = np.array([[1.2, 0.7, 2.3], [3.1, 2.5, 8.9], [-1.8, -5.8, 3.0], [0.2, 1.1, 2.1], [3.2, 1.1, 3.3]]) species = np.array([1, 2, 3, 4, 5]) arbitrary_structure = Structure(cell, species, positions) # Construct an environment. cutoffs = np.array([4., 3.]) arbitrary_environment = \ AtomicEnvironment(arbitrary_structure, 0, cutoffs) # Count the neighbors. n_neighbors_1 = len(arbitrary_environment.etypes) # Reduce the sweep value, and check that neighbors are missing. sweep_val = arbitrary_environment.sweep_val arbitrary_environment.sweep_array = \ np.arange(-sweep_val + 1, sweep_val, 1) arbitrary_environment.compute_env() n_neighbors_2 = len(arbitrary_environment.etypes) assert (n_neighbors_1 > n_neighbors_2) # Increase the sweep value, and check that the count is the same. arbitrary_environment.sweep_array = \ np.arange(-sweep_val - 1, sweep_val + 2, 1) arbitrary_environment.compute_env() n_neighbors_3 = len(arbitrary_environment.etypes) assert (n_neighbors_1 == n_neighbors_3)
def test_update_L_alpha(): # set up gp model kernel = mc_simple.two_plus_three_body_mc kernel_grad = mc_simple.two_plus_three_body_mc_grad cutoffs = [6.0, 5.0] hyps = np.array([0.001770, 0.183868, -0.001415, 0.372588, 0.026315]) # get an otf traj from file for training data old_otf = OtfAnalysis('test_files/AgI_snippet.out') call_no = 1 cell = old_otf.header['cell'] gp_model = old_otf.make_gp(kernel=kernel, kernel_grad=kernel_grad, call_no=call_no, cutoffs=cutoffs, hyps=hyps) # update database & use update_L_alpha to get ky_mat for n in range(call_no, call_no + 1): positions = old_otf.gp_position_list[n] species = old_otf.gp_species_list[n] atoms = old_otf.gp_atom_list[n] forces = old_otf.gp_force_list[n] struc_curr = Structure(cell, species, positions) gp_model.update_db(struc_curr, forces, custom_range=atoms) gp_model.update_L_alpha() ky_mat_from_update = np.copy(gp_model.ky_mat) # use set_L_alpha to get ky_mat gp_model.set_L_alpha() ky_mat_from_set = np.copy(gp_model.ky_mat) assert (np.all(np.absolute(ky_mat_from_update - ky_mat_from_set)) < 1e-6)
def test_load_one_frame_and_run(): the_gp = GaussianProcess( kernel_name="2+3_mc", hyps=np.array([ 3.75996759e-06, 1.53990678e-02, 2.50624782e-05, 5.07884426e-01, 1.70172923e-03, ]), cutoffs=np.array([5, 3]), hyp_labels=["l2", "s2", "l3", "s3", "n0"], maxiter=1, opt_algorithm="L-BFGS-B", ) with open(path.join(TEST_FILE_DIR, "methanol_frames.json"), "r") as f: frames = [Structure.from_dict(loads(s)) for s in f.readlines()] tt = TrajectoryTrainer( frames, gp=the_gp, shuffle_frames=True, print_as_xyz=True, rel_std_tolerance=0, abs_std_tolerance=0, skip=15, ) tt.run() for f in glob(f"gp_from_aimd*"): remove(f)
def test_subset_of_frame_by_element(): spec_list = ["H", "H", "O", "O", "O", "C"] test_struc_1 = Structure( cell=np.eye(3), species=spec_list, positions=np.zeros(shape=(len(spec_list), 3)) ) assert np.array_equal( subset_of_frame_by_element(test_struc_1, {}), list(range(len(test_struc_1))) ) assert np.array_equal( subset_of_frame_by_element(test_struc_1, {"H": 2, "O": 3}), list(range(len(test_struc_1))), ) assert np.array_equal( subset_of_frame_by_element(test_struc_1, {"H": 2, "O": 15}), list(range(len(test_struc_1))), ) assert set(subset_of_frame_by_element(test_struc_1, {"H": 1, "O": 1})).issubset( range(len(spec_list)) ) assert len(subset_of_frame_by_element(test_struc_1, {"H": 1, "O": 1, "C": 1})) == 3 assert subset_of_frame_by_element(test_struc_1, {"H": 0, "O": 0, "C": 0}) == [] assert subset_of_frame_by_element(test_struc_1, {"H": 0, "O": 0, "C": 1}) == [5]
def restart(self): # Recover atomic configuration: positions, velocities, forces positions, self.nsteps = self.read_frame('positions.xyz', -1) self.atoms.set_positions(positions) self.atoms.set_velocities(self.read_frame('velocities.dat', -1)[0]) self.atoms.calc.results['forces'] = self.read_frame('forces.dat', -1)[0] print('Last frame recovered') # Recover training data set gp_model = self.atoms.calc.gp_model atoms = deepcopy(self.atoms) nat = len(self.atoms.positions) dft_positions = self.read_all_frames('dft_positions.xyz', nat) dft_forces = self.read_all_frames('dft_forces.dat', nat) added_atoms = self.read_all_frames('added_atoms.dat', 1, 1, 'int') for i, frame in enumerate(dft_positions): atoms.set_positions(frame) curr_struc = Structure.from_ase_atoms(atoms) gp_model.update_db(curr_struc, dft_forces[i], added_atoms[i]) gp_model.set_L_alpha() print('GP training set ready') # Recover FLARE calculator gp_model.ky_mat_inv = np.load(self.restart_from + '/ky_mat_inv.npy') gp_model.alpha = np.load(self.restart_from + '/alpha.npy') if self.atoms.calc.use_mapping: for map_3 in self.atoms.calc.mgp_model.maps_3: map_3.load_grid = self.restart_from + '/' self.atoms.calc.build_mgp(skip=False) print('GP and MGP ready') self.l_bound = 10
def predict_on_structure_en(structure: Structure, gp: GaussianProcess, no_cpus=None): local_energies = [0 for _ in range(structure.nat)] for n in range(structure.nat): chemenv = AtomicEnvironment(structure, n, gp.cutoffs) for i in range(3): force, var = gp.predict(chemenv, i + 1) structure.forces[n][i] = float(force) structure.stds[n][i] = np.sqrt(np.abs(var)) local_energies[n] = gp.predict_local_energy(chemenv) forces = np.array(structure.forces) stds = np.array(structure.stds) return forces, stds, local_energies
def generate_mb_envs_pos(positions0, species_1, cutoffs, cell, delt, d1, mask=None): positions = [positions0] noa = len(positions0) positions_2 = deepcopy(positions0) positions_2[0][d1 - 1] = delt positions += [positions_2] positions_3 = deepcopy(positions[0]) positions_3[0][d1 - 1] = -delt positions += [positions_3] test_struc = [] for i in range(3): test_struc += [Structure(cell, species_1, positions[i])] env_0 = [] env_p = [] env_m = [] for i in range(noa): env_0 += [AtomicEnvironment(test_struc[0], i, cutoffs, cutoffs_mask=mask)] env_p += [AtomicEnvironment(test_struc[1], i, cutoffs, cutoffs_mask=mask)] env_m += [AtomicEnvironment(test_struc[2], i, cutoffs, cutoffs_mask=mask)] return [env_0, env_p, env_m]
def update_GP(self, dft_forces): atom_count = 0 atom_list = [] calc = self.atoms.calc.gp_model while (not self.std_in_bound and atom_count < self.max_atoms_added): # build gp structure from atoms atom_struc = Structure(self.atoms.cell, ['A'] * len(self.atoms.positions), self.atoms.positions) # update gp model calc.update_db(atom_struc, dft_forces, custom_range=[self.target_atom]) if calc.alpha is None: calc.set_L_alpha() else: calc.update_L_alpha() atom_list.append(self.target_atom) forces = self.atoms.get_forces() # use this function to get k_v self.stds = self.atoms.get_uncertainties() # write added atom to the log file, # refer to ase.optimize.optimize.Dynamics self.observers[0][0].add_atom_info(self.target_atom, self.stds[self.target_atom]) self.is_std_in_bound(atom_list) atom_count += 1 if not self.freeze_hyps: calc.train()
def parse_dft_input(input: str): """ Returns the positions, species, and cell of a POSCAR file. Outputs are specced for OTF module. :param input: POSCAR file input :return: """ pmg_structure = Poscar.from_file(input).structure flare_structure = Structure.from_pmg_structure(pmg_structure) positions = flare_structure.positions species = flare_structure.species_labels cell = flare_structure.cell # TODO Allow for custom masses in POSCAR elements = set(species) # conversion from amu to md units mass_dict = { elt: Element(elt).atomic_mass * 0.000103642695727 for elt in elements } return positions, species, cell, mass_dict
def test_wrapped_coordinates(): """Check that wrapped coordinates are equivalent to Cartesian coordinates up to lattice translations.""" cell = np.random.rand(3, 3) positions = np.random.rand(10, 3) species = ["Al"] * len(positions) test_struc = Structure(cell, species, positions) wrap_diff = test_struc.positions - test_struc.wrapped_positions wrap_rel = test_struc.raw_to_relative(wrap_diff, test_struc.cell_transpose, test_struc.cell_dot_inverse) assert np.isclose( np.round(wrap_rel) - wrap_rel, np.zeros(positions.shape)).all()
def calculate_mgp_serial(self, atoms): nat = len(atoms) struc_curr = Structure(np.array(atoms.cell), atoms.get_atomic_numbers(), atoms.positions) forces = np.zeros((nat, 3)) stress = np.zeros((nat, 6)) stds = np.zeros((nat, 3)) for n in range(nat): chemenv = AtomicEnvironment(struc_curr, n, self.mgp_model.cutoffs) f, v, vir = self.mgp_model.predict(chemenv, mean_only=False) forces[n] = f stress[n] = vir stds[n] = np.sqrt(np.absolute(v)) self.results['forces'] = forces self.results['stds'] = stds self.results['stresses'] = stress self.results['stress'] = np.sum(stress, axis=0) # TODO: implement energy mapping self.results['local_energies'] = np.zeros(forces.shape) self.results['energy'] = 0 atoms.get_uncertainties = self.get_uncertainties return forces
def test_espresso_input_edit(): """ Load a structure in from qe_input_1, change the position and cell, then edit and re-parse :return: """ os.system('cp test_files/qe_input_1.in .') positions, species, cell, masses = parse_dft_input('./qe_input_1.in') _, coded_species = get_unique_species(species) structure = Structure(cell, coded_species, positions, masses, species_labels=species) structure.vec1 += np.random.randn(3) structure.positions[0] += np.random.randn(3) new_file = edit_dft_input_positions('./qe_input_1.in', structure=structure) positions, species, cell, masses = parse_dft_input(new_file) assert np.equal(positions[0], structure.positions[0]).all() assert np.equal(structure.vec1, cell[0, :]).all() os.remove('qe_input_1.in')
def test_cp2k_input_edit(): """ Load a structure in from cp2k_input_1, change the position and cell, then edit and re-parse :return: """ positions, species, cell, masses = parse_dft_input( "./test_files/cp2k_input_1.in") _, coded_species = get_unique_species(species) structure = Structure(cell, coded_species, positions, masses, species_labels=species) structure.positions[0] += np.random.randn(3) newfilename = edit_dft_input_positions("./test_files/cp2k_input_1.in", structure=structure) positions, species, cell, masses = parse_dft_input(newfilename) assert np.isclose(positions[0], structure.positions[0]).all() assert np.isclose(structure.vec1, cell[0, :]).all() remove(newfilename) cleanup()