def test_main(require_vasp): assert installed() # simple test calculation of CO molecule d = 1.14 co = Atoms('CO', positions=[(0, 0, 0), (0, 0, d)], pbc=True) co.center(vacuum=5.) calc = Vasp(xc='PBE', prec='Low', algo='Fast', ismear=0, sigma=1., istart=0, lwave=False, lcharg=False, ldipol=True) co.calc = calc energy = co.get_potential_energy() forces = co.get_forces() dipole_moment = co.get_dipole_moment() # check that parsing of vasprun.xml file works conf = read('vasprun.xml') assert conf.calc.parameters['kpoints_generation'] assert conf.calc.parameters['sigma'] == 1.0 assert conf.calc.parameters['ialgo'] == 68 assert energy - conf.get_potential_energy() == 0.0 assert np.allclose(conf.get_forces(), forces) assert np.allclose(conf.get_dipole_moment(), dipole_moment, atol=1e-6) # Cleanup calc.clean()
def test_vasp2_co(require_vasp): """ Run some VASP tests to ensure that the VASP calculator works. This is conditional on the existence of the VASP_COMMAND or VASP_SCRIPT environment variables """ from ase.test.calculator.vasp import installed2 as installed assert installed() from ase import Atoms from ase.io import write from ase.calculators.vasp import Vasp2 as Vasp import numpy as np def array_almost_equal(a1, a2, tol=np.finfo(type(1.0)).eps): """Replacement for old numpy.testing.utils.array_almost_equal.""" return (np.abs(a1 - a2) < tol).all() d = 1.14 co = Atoms('CO', positions=[(0, 0, 0), (0, 0, d)], pbc=True) co.center(vacuum=5.) calc = Vasp(xc='PBE', prec='Low', algo='Fast', ismear=0, sigma=1., istart=0, lwave=False, lcharg=False) co.calc = calc en = co.get_potential_energy() write('vasp_co.traj', co) assert abs(en + 14.918933) < 5e-3 # Secondly, check that restart from the previously created VASP output works calc2 = Vasp(restart=True) co2 = calc2.get_atoms() # Need tolerance of 1e-14 because VASP itself changes coordinates # slightly between reading POSCAR and writing CONTCAR even if no ionic # steps are made. assert array_almost_equal(co.positions, co2.positions, 1e-14) assert en - co2.get_potential_energy() == 0. assert array_almost_equal(calc.get_stress(co), calc2.get_stress(co2)) assert array_almost_equal(calc.get_forces(co), calc2.get_forces(co2)) assert array_almost_equal(calc.get_eigenvalues(), calc2.get_eigenvalues()) assert calc.get_number_of_bands() == calc2.get_number_of_bands() assert calc.get_xc_functional() == calc2.get_xc_functional() # Cleanup calc.clean()
def test_vasp2_import(require_vasp): """ Test if we can find vasp2 using get_calculator() """ from ase.test.calculator.vasp import installed2 as installed from ase.calculators.calculator import get_calculator_class assert installed() get_calculator_class('vasp2')
def test_main(require_vasp): assert installed() # simple test calculation of CO molecule d = 1.14 co = Atoms('CO', positions=[(0, 0, 0), (0, 0, d)], pbc=True) co.center(vacuum=5.) calc = Vasp(xc='LDA', prec='Low', algo='Fast', ismear=0, sigma=1., nbands=12, istart=0, nelm=3, lwave=False, lcharg=False, ldipol=True) co.calc = calc energy = co.get_potential_energy() forces = co.get_forces() dipole_moment = co.get_dipole_moment() # check that parsing of vasprun.xml file works conf = read('vasprun.xml') assert conf.calc.parameters['kpoints_generation'] assert conf.calc.parameters['sigma'] == 1.0 assert conf.calc.parameters['ialgo'] == 68 assert energy - conf.get_potential_energy() == 0.0 # Check some arrays assert np.allclose(conf.get_forces(), forces) assert np.allclose(conf.get_dipole_moment(), dipole_moment, atol=1e-6) # Check k-point-dependent properties assert len(conf.calc.get_eigenvalues(spin=0)) >= 12 assert conf.calc.get_occupation_numbers()[2] == 2 assert conf.calc.get_eigenvalues(spin=1) is None kpt = conf.calc.get_kpt(0) assert kpt.weight == 1. # Perform a spin-polarised calculation co.calc.set(ispin=2, ibrion=-1) co.get_potential_energy() conf = read('vasprun.xml') assert len(conf.calc.get_eigenvalues(spin=1)) >= 12 assert conf.calc.get_occupation_numbers(spin=1)[0] == 1. # Cleanup calc.clean()
def test_vasp2_cell(require_vasp): """ Check the unit cell is handled correctly """ import pytest from ase.test.calculator.vasp import installed2 as installed from ase.calculators.vasp import Vasp2 as Vasp from ase.build import molecule assert installed() # Molecules come with no unit cell atoms = molecule('CH4') calc = Vasp() with pytest.raises(ValueError): atoms.calc = calc atoms.get_total_energy()
def test_vasp2_xc(require_vasp): """ Run some tests to ensure that the xc setting in the VASP calculator works. """ from ase.test.calculator.vasp import installed2 as installed from ase.calculators.vasp import Vasp2 as Vasp assert installed() def dict_is_subset(d1, d2): """True if all the key-value pairs in dict 1 are in dict 2""" for key, value in d1.items(): if key not in d2: return False elif d2[key] != value: return False else: return True calc_vdw = Vasp(xc='optb86b-vdw') assert dict_is_subset({ 'param1': 0.1234, 'param2': 1.0 }, calc_vdw.float_params) calc_hse = Vasp(xc='hse06', hfscreen=0.1, gga='RE', encut=400, sigma=0.5) assert dict_is_subset({ 'hfscreen': 0.1, 'encut': 400, 'sigma': 0.5 }, calc_hse.float_params) assert dict_is_subset({'gga': 'RE'}, calc_hse.string_params)
def test_vasp2_wdir(require_vasp): """ Run tests to ensure that the VASP txt and label arguments function correctly, i.e. correctly sets the working directories and works in that directory. This is conditional on the existence of the ASE_VASP_COMMAND, VASP_COMMAND or VASP_SCRIPT environment variables """ import filecmp import os from ase.test.calculator.vasp import installed2 as installed from ase import Atoms from ase.calculators.vasp import Vasp2 as Vasp assert installed() def compare_paths(path1, path2): assert os.path.abspath(path1) == os.path.abspath(path2) # Test setup system, borrowed from vasp_co.py d = 1.14 atoms = Atoms('CO', positions=[(0, 0, 0), (0, 0, d)], pbc=True) atoms.center(vacuum=5.) file1 = '_vasp_dummy_str.out' file2 = '_vasp_dummy_io.out' file3 = '_vasp_dummy_2.out' testdir = '_dummy_txt_testdir' label = os.path.join(testdir, 'vasp') # Test settings = dict(label=label, xc='PBE', prec='Low', algo='Fast', ismear=0, sigma=1., istart=0, lwave=False, lcharg=False) # Make 2 copies of the calculator object calc = Vasp(**settings) calc2 = Vasp(**settings) # Check the calculator path is the expected path compare_paths(calc.directory, testdir) calc.set(txt=file1) atoms.calc = calc en1 = atoms.get_potential_energy() # Check that the output files are in the correct directory for fi in ['OUTCAR', 'CONTCAR', 'vasprun.xml']: fi = os.path.join(testdir, fi) assert os.path.isfile(fi) # We open file2 in our current directory, so we don't want it to write # in the label directory with open(file2, 'w') as f: calc2.set(txt=f) atoms.calc = calc2 atoms.get_potential_energy() # Make sure the two outputfiles are identical assert filecmp.cmp(os.path.join(calc.directory, file1), file2) # Test restarting from working directory in test directory label2 = os.path.join(testdir, file3) calc2 = Vasp(restart=label, label=label2) # Check the calculator path is the expected path compare_paths(calc2.directory, testdir) assert not calc2.calculation_required(calc2.atoms, ['energy', 'forces']) en2 = calc2.get_potential_energy() # Check that the restarted calculation didn't run, i.e. write to output file assert not os.path.isfile(os.path.join(calc.directory, file3)) # Check that we loaded energy correctly assert en1 == en2
def test_vasp2_kpoints(require_vasp): """ Check the many ways of specifying KPOINTS """ import os from ase.calculators.vasp import Vasp2 as Vasp from ase.build import bulk from ase.test.calculator.vasp import installed2 as installed assert installed() Al = bulk('Al', 'fcc', a=4.5, cubic=True) def check_kpoints_line(n, contents): """Assert the contents of a line""" with open('KPOINTS', 'r') as f: lines = f.readlines() assert lines[n] == contents # Default to (1 1 1) calc = Vasp(gamma=True) calc.write_kpoints() check_kpoints_line(2, 'Gamma\n') check_kpoints_line(3, '1 1 1 \n') calc.clean() # 3-tuple prints mesh calc = Vasp(gamma=False, kpts=(4, 4, 4)) calc.write_kpoints() check_kpoints_line(2, 'Monkhorst-Pack\n') check_kpoints_line(3, '4 4 4 \n') calc.clean() # Auto mode calc = Vasp(kpts=20) calc.write_kpoints() check_kpoints_line(1, '0\n') check_kpoints_line(2, 'Auto\n') check_kpoints_line(3, '20 \n') calc.clean() # 1-element list ok, Gamma ok calc = Vasp(kpts=[20], gamma=True) calc.write_kpoints() check_kpoints_line(1, '0\n') check_kpoints_line(2, 'Auto\n') check_kpoints_line(3, '20 \n') calc.clean() # KSPACING suppresses KPOINTS file calc = Vasp(kspacing=0.23) calc.initialize(Al) calc.write_kpoints() calc.write_incar(Al) assert not os.path.isfile('KPOINTS') with open('INCAR', 'r') as f: assert ' KSPACING = 0.230000\n' in f.readlines() calc.clean() # Negative KSPACING raises an error calc = Vasp(kspacing=-0.5) try: calc.write_kpoints() except ValueError: pass else: raise AssertionError("Negative KSPACING did not raise ValueError") calc.clean() # Explicit weighted points with nested lists, Cartesian if not specified calc = Vasp( kpts=[[0.1, 0.2, 0.3, 2], [0.0, 0.0, 0.0, 1], [0.0, 0.5, 0.5, 2]]) calc.write_kpoints() with open('KPOINTS.ref', 'w') as f: f.write("""KPOINTS created by Atomic Simulation Environment 3 Cartesian 0.100000 0.200000 0.300000 2.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.500000 0.500000 2.000000 """) assert filecmp_ignore_whitespace('KPOINTS', 'KPOINTS.ref') os.remove('KPOINTS.ref') # Explicit points as list of tuples, automatic weighting = 1. calc = Vasp( kpts=[(0.1, 0.2, 0.3), (0.0, 0.0, 0.0), (0.0, 0.5, 0.5)], reciprocal=True) calc.write_kpoints() with open('KPOINTS.ref', 'w') as f: f.write("""KPOINTS created by Atomic Simulation Environment 3 Reciprocal 0.100000 0.200000 0.300000 1.0 0.000000 0.000000 0.000000 1.0 0.000000 0.500000 0.500000 1.0 """) assert filecmp_ignore_whitespace('KPOINTS', 'KPOINTS.ref') os.remove('KPOINTS.ref')
def test_vasp2_Al_volrelax(require_vasp): """ Run VASP tests to ensure that relaxation with the VASP calculator works. This is conditional on the existence of the VASP_COMMAND or VASP_SCRIPT environment variables. """ from ase.test.calculator.vasp import installed2 as installed assert installed() import numpy as np from ase import io # QuasiNewton nowadays is an alias for BFGSLineSearch, which is # broken. Use BFGS instead. from ase.optimize import BFGS as QuasiNewton from ase.build import bulk from ase.calculators.vasp import Vasp2 as Vasp # -- Perform Volume relaxation within Vasp def vasp_vol_relax(): Al = bulk('Al', 'fcc', a=4.5, cubic=True) calc = Vasp(xc='LDA', isif=7, nsw=5, ibrion=1, ediffg=-1e-3, lwave=False, lcharg=False) calc.calculate(Al) # Explicitly parse atomic position output file from Vasp CONTCAR_Al = io.read('CONTCAR', format='vasp') print('Stress after relaxation:\n', calc.read_stress()) print('Al cell post relaxation from calc:\n', calc.get_atoms().get_cell()) print('Al cell post relaxation from atoms:\n', Al.get_cell()) print('Al cell post relaxation from CONTCAR:\n', CONTCAR_Al.get_cell()) # All the cells should be the same. assert (calc.get_atoms().get_cell() == CONTCAR_Al.get_cell()).all() assert (Al.get_cell() == CONTCAR_Al.get_cell()).all() return Al # -- Perform Volume relaxation using ASE with Vasp as force/stress calculator def ase_vol_relax(): Al = bulk('Al', 'fcc', a=4.5, cubic=True) calc = Vasp(xc='LDA') Al.calc = calc from ase.constraints import StrainFilter sf = StrainFilter(Al) qn = QuasiNewton(sf, logfile='relaxation.log') qn.run(fmax=0.1, steps=5) print('Stress:\n', calc.read_stress()) print('Al post ASE volume relaxation\n', calc.get_atoms().get_cell()) return Al # Test function for comparing two cells def cells_almost_equal(cellA, cellB, tol=0.01): return (np.abs(cellA - cellB) < tol).all() # Correct LDA relaxed cell a_rel = 4.18 LDA_cell = np.diag([a_rel, a_rel, a_rel]) Al_vasp = vasp_vol_relax() Al_ase = ase_vol_relax() assert cells_almost_equal(LDA_cell, Al_vasp.get_cell()) assert cells_almost_equal(LDA_cell, Al_ase.get_cell()) # Cleanup Al_ase.calc.clean()
def test_vasp_net_charge(require_vasp): """ Run VASP tests to ensure that determining number of electrons from user-supplied net charge (via the deprecated net_charge parameter) works correctly. This is conditional on the existence of the VASP_COMMAND or VASP_SCRIPT environment variables. This is mainly a slightly reduced duplicate of the vasp_charge test, but with flipped signs and with checks that ensure FutureWarning is emitted. Should be removed along with the net_charge parameter itself at some point. """ assert installed() system = bulk('Al', 'fcc', a=4.5, cubic=True) # Dummy calculation to let VASP determine default number of electrons calc = Vasp(xc='LDA', nsw=-1, ibrion=-1, nelm=1, lwave=False, lcharg=False) calc.calculate(system) default_nelect_from_vasp = calc.get_number_of_electrons() assert default_nelect_from_vasp == 12 # Compare VASP's output nelect from before + net charge to default nelect # determined by us + net charge with pytest.warns(FutureWarning): net_charge = -2 calc = Vasp(xc='LDA', nsw=-1, ibrion=-1, nelm=1, lwave=False, lcharg=False, net_charge=net_charge) calc.initialize(system) calc.write_input(system) calc.read_incar() assert calc.float_params['nelect'] == default_nelect_from_vasp + net_charge # Test that conflicts between explicitly given nelect and net charge are # detected with pytest.raises(ValueError): with pytest.warns(FutureWarning): calc = Vasp(xc='LDA', nsw=-1, ibrion=-1, nelm=1, lwave=False, lcharg=False, nelect=default_nelect_from_vasp + net_charge + 1, net_charge=net_charge) calc.calculate(system) # Test that conflicts between charge and net_charge are detected with pytest.raises(ValueError): with pytest.warns(FutureWarning): calc = Vasp(xc='LDA', nsw=-1, ibrion=-1, nelm=1, lwave=False, lcharg=False, charge=-net_charge - 1, net_charge=net_charge) calc.calculate(system) # Test that nothing is written if net charge is 0 and nelect not given with pytest.warns(FutureWarning): calc = Vasp(xc='LDA', nsw=-1, ibrion=-1, nelm=1, lwave=False, lcharg=False, net_charge=0) calc.initialize(system) calc.write_input(system) calc.read_incar() assert calc.float_params['nelect'] is None
def test_vasp_setup(require_vasp): """ Run some tests to ensure that VASP calculator constructs correct POTCAR files """ from os import remove from os.path import isfile from ase.atoms import Atoms from ase.calculators.vasp import Vasp from ase.test.calculator.vasp import installed assert installed() def check_potcar(setups, filename='POTCAR'): """Return true if labels in setups are found in POTCAR""" pp = [] with open(filename, 'r') as f: for line in f: if 'TITEL' in line.split(): pp.append(line.split()[3]) for setup in setups: assert setup in pp # Write some POTCARs and check they are ok potcar = 'POTCAR' try: atoms = Atoms('CaGdCs', positions=[[0, 0, 1], [0, 0, 2], [0, 0, 3]], cell=[5, 5, 5]) calc = Vasp(xc='pbe') calc.initialize(atoms) calc.write_potcar() check_potcar(('Ca_pv', 'Gd', 'Cs_sv'), filename=potcar) calc = Vasp(xc='pbe', setups='recommended') calc.initialize(atoms) calc.write_potcar() check_potcar(('Ca_sv', 'Gd_3', 'Cs_sv'), filename=potcar) calc = Vasp(xc='pbe', setups='materialsproject') calc.initialize(atoms) calc.write_potcar() check_potcar(('Ca_sv', 'Gd', 'Cs_sv'), filename=potcar) atoms = Atoms('CaInI', positions=[[0, 0, 1], [0, 0, 2], [0, 0, 3]], cell=[5, 5, 5]) calc = Vasp(xc='pbe', setups={'base': 'gw'}) calc.initialize(atoms) calc.write_potcar() check_potcar(('Ca_sv_GW', 'In_d_GW', 'I_GW'), filename=potcar) calc = Vasp(xc='pbe', setups={'base': 'gw', 'I': ''}) calc.initialize(atoms) calc.write_potcar() check_potcar(('Ca_sv_GW', 'In_d_GW', 'I'), filename=potcar) calc = Vasp(xc='pbe', setups={'base': 'gw', 'Ca': '_sv', 2: 'I'}) calc.initialize(atoms) calc.write_potcar() check_potcar(('Ca_sv', 'In_d_GW', 'I'), filename=potcar) finally: if isfile(potcar): remove(potcar)
def test_vasp2_check_state(require_vasp): """ Run tests to ensure that the VASP check_state() function call works correctly, i.e. correctly sets the working directories and works in that directory. This is conditional on the existence of the VASP_COMMAND or VASP_SCRIPT environment variables """ from ase.test.calculator.vasp import installed2 as installed import os from ase import Atoms from ase.calculators.vasp import Vasp2 as Vasp assert installed() # Test setup system, borrowed from vasp_co.py d = 1.14 atoms = Atoms('CO', positions=[(0, 0, 0), (0, 0, d)], pbc=True) atoms.extend(Atoms('CO', positions=[(0, 2, 0), (0, 2, d)])) atoms.center(vacuum=5.) # Test settings = dict(xc='LDA', prec='Low', algo='Fast', ismear=0, sigma=1., istart=0, lwave=False, lcharg=False) s1 = atoms.get_chemical_symbols() calc = Vasp(**settings) atoms.calc = calc en1 = atoms.get_potential_energy() # Test JSON dumping and restarting works fi = 'json_test.json' calc.write_json(filename=fi) assert os.path.isfile(fi) calc2 = Vasp() calc2.read_json(fi) assert not calc2.calculation_required(atoms, ['energy', 'forces']) en2 = calc2.get_potential_energy() assert abs(en1 - en2) < 1e-8 os.remove(fi) # Clean up the JSON file # Check that the symbols remain in order (non-sorted) s2 = calc.atoms.get_chemical_symbols() assert s1 == s2 s3 = sorted(s2) assert s2 != s3 # Check that get_atoms() doesn't reset results r1 = dict(calc.results) # Force a copy calc.get_atoms() r2 = dict(calc.results) assert r1 == r2 # Make a parameter change to the calculator calc.set(sigma=0.5) # Check that we capture a change for float params assert calc.check_state(atoms) == ['float_params'] assert calc.calculation_required(atoms, ['energy', 'forces']) en2 = atoms.get_potential_energy() # The change in sigma should result in a small change in energy assert (en1 - en2) > 1e-7 # Now we make a change in input_params instead calc.kpts = 2 # Check that this requires a new calculation assert calc.check_state(atoms) == ['input_params'] assert calc.calculation_required(atoms, ['energy', 'forces']) # Clean up calc.clean()
def test_vasp_charge(require_vasp): """ Run VASP tests to ensure that determining number of electrons from user-supplied charge works correctly. This is conditional on the existence of the VASP_COMMAND or VASP_SCRIPT environment variables. """ import pytest from ase.build import bulk from ase.calculators.vasp import Vasp from ase.test.calculator.vasp import installed assert installed() system = bulk('Al', 'fcc', a=4.5, cubic=True) # Dummy calculation to let VASP determine default number of electrons calc = Vasp(xc='LDA', nsw=-1, ibrion=-1, nelm=1, lwave=False, lcharg=False) calc.calculate(system) default_nelect_from_vasp = calc.get_number_of_electrons() assert default_nelect_from_vasp == 12 # Make sure that no nelect was written into INCAR yet (as it wasn't necessary) calc = Vasp() calc.read_incar() assert calc.float_params['nelect'] is None # Compare VASP's output nelect from before minus charge to default nelect # determined by us minus charge charge = -2 calc = Vasp(xc='LDA', nsw=-1, ibrion=-1, nelm=1, lwave=False, lcharg=False, charge=charge) calc.initialize(system) calc.write_input(system) calc.read_incar() assert calc.float_params['nelect'] == default_nelect_from_vasp - charge # Test that conflicts between explicitly given nelect and charge are detected with pytest.raises(ValueError): calc = Vasp(xc='LDA', nsw=-1, ibrion=-1, nelm=1, lwave=False, lcharg=False, nelect=default_nelect_from_vasp - charge + 1, charge=charge) calc.calculate(system) # Test that nothing is written if charge is 0 and nelect not given calc = Vasp(xc='LDA', nsw=-1, ibrion=-1, nelm=1, lwave=False, lcharg=False, charge=0) calc.initialize(system) calc.write_input(system) calc.read_incar() assert calc.float_params['nelect'] is None # Test that explicitly given nelect still works as expected calc = Vasp(xc='LDA', nsw=-1, ibrion=-1, nelm=1, lwave=False, lcharg=False, nelect=15) calc.calculate(system) assert calc.get_number_of_electrons() == 15