def test_parameteric_constr(): import numpy as np from ase.build import bulk from ase.constraints import ( dict2constraint, FixScaledParametricRelations, FixCartesianParametricRelations, ) from ase.calculators.emt import EMT # Build the atoms object and attach a calculator a = bulk("Ni", cubic=True) a.calc = EMT() # Get adjusted cell cell = a.cell + 0.01 # Generate lattice constraint param_lat = ["a"] expr_lat = [ "a", "0", "0", "0", "a", "0", "0", "0", "a", ] constr_lat = FixCartesianParametricRelations.from_expressions( indices=[0, 1, 2], params=param_lat, expressions=expr_lat, use_cell=True, ) # Check expression generator for const_expr, passed_expr in zip(constr_lat.expressions.flatten(), expr_lat): assert const_expr == passed_expr # Check adjust_cell constr_lat.adjust_cell(a, cell) # Check serialization and construction from dict constr_lat_dict = constr_lat.todict() dict2constraint(constr_lat_dict) cell_diff = (cell - a.cell).flatten() expected_cell_diff = np.array( [0.01, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.01]) assert np.max(np.abs(cell_diff - expected_cell_diff)) < 1e-12 # Check adjust_stress a.cell += 0.01 stress = a.get_stress().copy() constr_lat.adjust_stress(a, stress) stress_rat = stress / a.get_stress() assert np.max( np.abs(stress_rat - np.array([1., 1., 1., 0., 0., 0.]))) < 1e-12 # Reset cell a.cell -= 0.01 # Get adjusted cell/positions for the system pos = a.get_positions().copy() + 0.01 # Generate proper atomic constraints constr_atom = FixScaledParametricRelations( [0, 1, 2, 3], np.ndarray((12, 0)), a.get_scaled_positions().flatten(), ) # Check serialization and construction from dict constr_atom_dict = constr_atom.todict() dict2constraint(constr_atom_dict) # Check adjust_positions constr_atom.adjust_positions(a, pos) assert np.max(np.abs(a.get_positions() - pos)) < 1e-12 # Check adjust_forces assert np.max(np.abs(a.get_forces())) < 1e-12 # Check non-empty constraint param_atom = ["dis"] expr_atom = [ "dis", "dis", "dis", "dis", "-0.5", "0.5", "0.5", "dis", "0.5", "0.5", "0.5", "dis", ] constr_atom = FixScaledParametricRelations.from_expressions( indices=[0, 1, 2, 3], params=param_atom, expressions=expr_atom, ) # Restart position adjustment pos += 0.01 * a.cell[0, 0] # Check adjust_positions constr_atom.adjust_positions(a, pos) scaled_pos = a.cell.scaled_positions(pos) pos_diff = (scaled_pos - a.get_scaled_positions()).flatten() expected_pos_diff = np.array( [0.01, 0.01, 0.01, 0.01, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.01]) assert np.max(np.abs(pos_diff - expected_pos_diff)) < 1e-12 # Check adjust_forces a.set_positions(pos + 0.3) forces = a.get_forces() constr_atom.adjust_forces(a, forces) forces_rat = forces / a.get_forces() assert np.max( np.abs(forces_rat.flatten() / 100.0 - expected_pos_diff)) < 1e-12 # Check auto-remapping/expression generation, the -0.5 should now be 0.5 expr_atom[4] = "0.5" current_expression = constr_atom.expressions.flatten() for const_expr, passed_expr in zip(current_expression, expr_atom): assert const_expr == passed_expr # Check with Cartesian parametric constraints now expr_atom = [ "dis", "dis", "dis", "dis", "1.76", "1.76", "1.76", "dis", "1.76", "1.76", "1.76", "dis", ] constr_atom = FixCartesianParametricRelations.from_expressions( indices=[0, 1, 2, 3], params=param_atom, expressions=expr_atom, ) # Restart position adjustment a.set_positions(pos) pos += 0.01 # Check adjust_positions constr_atom.adjust_positions(a, pos) pos_diff = (pos - a.get_positions()).flatten() expected_pos_diff = np.array( [0.01, 0.01, 0.01, 0.01, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.01]) assert np.max(np.abs(pos_diff - expected_pos_diff)) < 1e-12 # Check adjust_forces a.set_positions(pos + 0.3) forces = a.get_forces() constr_atom.adjust_forces(a, forces) forces_rat = forces / a.get_forces() assert np.max( np.abs(forces_rat.flatten() / 100.0 - expected_pos_diff)) < 1e-12
def read_aims(filename, apply_constraints=True): """Import FHI-aims geometry type files. Reads unitcell, atom positions and constraints from a geometry.in file. If geometric constraint (symmetry parameters) are in the file include that information in atoms.info["symmetry_block"] """ from ase import Atoms from ase.constraints import ( FixAtoms, FixCartesian, FixScaledParametricRelations, FixCartesianParametricRelations, ) import numpy as np atoms = Atoms() with open(filename, "r") as fd: lines = fd.readlines() positions = [] cell = [] symbols = [] velocities = [] magmoms = [] symmetry_block = [] charges = [] fix = [] fix_cart = [] xyz = np.array([0, 0, 0]) i = -1 n_periodic = -1 periodic = np.array([False, False, False]) cart_positions, scaled_positions = False, False for line in lines: inp = line.split() if inp == []: continue if inp[0] == "atom": cart_positions = True if xyz.all(): fix.append(i) elif xyz.any(): fix_cart.append(FixCartesian(i, xyz)) floatvect = float(inp[1]), float(inp[2]), float(inp[3]) positions.append(floatvect) magmoms.append(0.0) charges.append(0.0) symbols.append(inp[-1]) i += 1 xyz = np.array([0, 0, 0]) elif inp[0] == "atom_frac": scaled_positions = True if xyz.all(): fix.append(i) elif xyz.any(): fix_cart.append(FixCartesian(i, xyz)) floatvect = float(inp[1]), float(inp[2]), float(inp[3]) positions.append(floatvect) magmoms.append(0.0) symbols.append(inp[-1]) i += 1 xyz = np.array([0, 0, 0]) elif inp[0] == "lattice_vector": floatvect = float(inp[1]), float(inp[2]), float(inp[3]) cell.append(floatvect) n_periodic = n_periodic + 1 periodic[n_periodic] = True elif inp[0] == "initial_moment": magmoms[-1] = float(inp[1]) elif inp[0] == "initial_charge": charges[-1] = float(inp[1]) elif inp[0] == "constrain_relaxation": if inp[1] == ".true.": fix.append(i) elif inp[1] == "x": xyz[0] = 1 elif inp[1] == "y": xyz[1] = 1 elif inp[1] == "z": xyz[2] = 1 elif inp[0] == "velocity": floatvect = [v_unit * float(l) for l in inp[1:4]] velocities.append(floatvect) elif inp[0] in [ "symmetry_n_params", "symmetry_params", "symmetry_lv", "symmetry_frac", ]: symmetry_block.append(" ".join(inp)) if xyz.all(): fix.append(i) elif xyz.any(): fix_cart.append(FixCartesian(i, xyz)) if cart_positions and scaled_positions: raise Exception("Can't specify atom positions with mixture of " "Cartesian and fractional coordinates") elif scaled_positions and periodic.any(): atoms = Atoms(symbols, scaled_positions=positions, cell=cell, pbc=periodic) else: atoms = Atoms(symbols, positions) if len(velocities) > 0: if len(velocities) != len(positions): raise Exception( "Number of positions and velocities have to coincide.") atoms.set_velocities(velocities) fix_params = [] if len(symmetry_block) > 5: params = symmetry_block[1].split()[1:] lattice_expressions = [] lattice_params = [] atomic_expressions = [] atomic_params = [] n_lat_param = int(symmetry_block[0].split(" ")[2]) lattice_params = params[:n_lat_param] atomic_params = params[n_lat_param:] for ll, line in enumerate(symmetry_block[2:]): expression = " ".join(line.split(" ")[1:]) if ll < 3: lattice_expressions += expression.split(",") else: atomic_expressions += expression.split(",") fix_params.append( FixCartesianParametricRelations.from_expressions( list(range(3)), lattice_params, lattice_expressions, use_cell=True, )) fix_params.append( FixScaledParametricRelations.from_expressions( list(range(len(atoms))), atomic_params, atomic_expressions)) if any(magmoms): atoms.set_initial_magnetic_moments(magmoms) if any(charges): atoms.set_initial_charges(charges) if periodic.any(): atoms.set_cell(cell) atoms.set_pbc(periodic) if len(fix): atoms.set_constraint([FixAtoms(indices=fix)] + fix_cart + fix_params) else: atoms.set_constraint(fix_cart + fix_params) if fix_params and apply_constraints: atoms.set_positions(atoms.get_positions()) return atoms
# Generate lattice constraint param_lat = ["a"] expr_lat = [ "a", "0", "0", "0", "a", "0", "0", "0", "a", ] constr_lat = FixCartesianParametricRelations.from_expressions( indices=[0, 1, 2], params=param_lat, expressions=expr_lat, use_cell=True, ) # Check expression generator for const_expr, passed_expr in zip(constr_lat.expressions.flatten(), expr_lat): assert const_expr == passed_expr # Check adjust_cell constr_lat.adjust_cell(a, cell) # Check serialization and construction from dict constr_lat_dict = constr_lat.todict() dict2constraint(constr_lat_dict) cell_diff = (cell - a.cell).flatten()