def test_potcar_is_unique_method(interactive_potcar_file): # generate arbitrary potential and process it using the potcar parser potential_contents = "\n".join([ "functional Si 01Jan2000", "parameters from PSCTR are:", "VRHFIN =Si: s100p100d100", "TITEL = functional Xy 01Jan1000" "END of PSCTR-controll parameters", ]) interactive_potcar_file.open("POTCAR") interactive_potcar_file.write(potential_contents) path_to_potcar = interactive_potcar_file.filepath potcar_parser = PotcarParser(path_to_potcar, functional='pbe', name='Si_abc') # check and assert uniqueness for clean database assert VaspPotcarFile.is_unique(potcar_parser) is True # now store the very same potential to the clean database and assert # that is_unique() raises node = VaspPotcarFile(path_to_potcar, potcar_parser.name, potcar_parser.element, potcar_parser.version, potcar_parser.functional, potcar_parser.hash) node.store() with pytest.raises(MultiplePotcarError) as exception: VaspPotcarFile.is_unique(potcar_parser) assert "Identical potential" in str(exception.value) # change the hash and check error message has changed potcar_parser.hash = "hash123" with pytest.raises(MultiplePotcarError) as exception: VaspPotcarFile.is_unique(potcar_parser) assert "Potential with matching identifiers" in str(exception.value)
def test_store_and_load_potcar_data(interactive_potcar_file): from aiida.orm import load_node # generate arbitrary potential and process it using the potcar parser potential_contents = "\n".join([ "functional Si 01Jan1000", "parameters from PSCTR are:", "VRHFIN =Si: s100p100d100", "TITEL = functional Si 01Jan1000" "END of PSCTR-controll parameters", ]) interactive_potcar_file.open("POTCAR") interactive_potcar_file.write(potential_contents) path_to_potcar = pathlib.Path(interactive_potcar_file.filepath) potcar_file_node = VaspPotcarFile.add_potential(path_to_potcar, name='Si', functional='pbe') potcar_file_node.store() # create VaspPotcarData instance associated with the stored potential potcar_data_set = VaspPotcarData(name='Si', version=10000101, functional='pbe') uuid = potcar_data_set.store().uuid # load the data node and compare stored contents potcar_data_get = load_node(uuid) assert potcar_data_get.name == potcar_file_node.name assert potcar_data_get.version == potcar_file_node.version assert potcar_data_get.functional == potcar_file_node.functional assert potcar_data_get.element == potcar_file_node.element assert potcar_data_get.hash == potcar_file_node.hash assert potcar_data_get.filenode_uuid == potcar_file_node.uuid
def test_get_potential_from_tags(query_args, num_match_expected, interactive_potcar_file): # build smalle potential database interactive_potcar_file.open("POTCAR") path_to_potcar = interactive_potcar_file.filepath potcar_identifiers = [ ['Ge_a', 'Ge', 10000101, 'lda_us', 'hash1'], ['Ge_a', 'Ge', 10000102, 'lda_us', 'hash2'], ['Ge_b', 'Ge', 10000101, 'lda_us', 'hash3'], ['Ge_b', 'Ge', 10000102, 'lda_us', 'hash4'], ['Si_a', 'Si', 10000101, 'lda_us', 'hash5'], ['Si_a', 'Si', 10000102, 'lda_us', 'hash6'], ['Si_b', 'Si', 10000101, 'lda_us', 'hash7'], ['Si_b', 'Si', 10000102, 'lda_us', 'hash8'], ] for identifiers in potcar_identifiers: node = VaspPotcarFile(path_to_potcar, *identifiers) node.store() # perform different queries using the from_tags() method query_result = VaspPotcarFile.from_tags(**query_args) assert len(query_result) == num_match_expected
def test_potcar_from_linklist(with_pbe_potcars, multi_component_structure): from aiida_cusp.data.inputs.vasp_potcar import VaspPotcarFile from aiida_cusp.data.inputs.vasp_poscar import VaspPoscarData poscar = VaspPoscarData(structure=multi_component_structure) potmap = VaspPotcarData.from_structure(multi_component_structure, 'pbe') complete_potcar = VaspPotcarData.potcar_from_linklist(poscar, potmap) # build the expected potcar potcar_contents = [] for symbol in poscar.get_poscar().site_symbols: potcar_file = VaspPotcarFile.from_tags(name=symbol)[0] potcar_contents.append(potcar_file.get_content()) expected_potcar_contents = "\n".join(potcar_contents) + "\n" assert str(complete_potcar) == expected_potcar_contents
def test_from_structure_classmethod_single(name, interactive_potcar_file, version, functional, structure_type, minimal_pymatgen_structure): # create non-ordered structures of different types supercell = minimal_pymatgen_structure * (2, 2, 2) if structure_type == 'pymatgen': structure = supercell elif structure_type == 'aiida': structure = StructureData(pymatgen_structure=supercell) elif structure_type == 'poscar': structure = Poscar(supercell) elif structure_type == 'aiida_cusp_poscar': structure = VaspPoscarData(structure=supercell) # populate database with potentials potcar_args = [ ['H', 'H', 10000101, 'pbe', 'hash1'], ['H', 'H', 10000102, 'pbe', 'hash2'], ['H_pv', 'H', 10000101, 'pbe', 'hash3'], ['H_pv', 'H', 10000102, 'pbe', 'hash4'], ['H', 'H', 10000101, 'pw91', 'hash5'], ['H', 'H', 10000102, 'pw91', 'hash6'], ['H_pv', 'H', 10000101, 'pw91', 'hash7'], ['H_pv', 'H', 10000102, 'pw91', 'hash8'], ] interactive_potcar_file.open("POTCAR") path = pathlib.Path(interactive_potcar_file.filepath).absolute() for args in potcar_args: VaspPotcarFile(path, *args).store() # setup alternative (non-default) potcar parameters potcar_params = {'H': {}} if name is not None: potcar_params['H'].update({'name': name}) if version is not None: potcar_params['H'].update({'version': version}) if not potcar_params['H']: potcar_params = {} # create the element-potential map potential_map = VaspPotcarData.from_structure(structure, functional, potcar_params=potcar_params) # check that the map contains only a single entry assert len(potential_map.keys()) == 1 assert list(potential_map.keys()) == ['H'] # assert potential properties match the wanted ones expected_name = name or 'H' expected_version = version or 10000102 assert potential_map['H'].name == expected_name assert potential_map['H'].version == expected_version assert potential_map['H'].functional == functional.lower() # check that we indeed return a VaspPotcarData instance and nothing else assert isinstance(potential_map['H'], VaspPotcarData) is True
def test_load_potential_file_node_properties_match(interactive_potcar_file, change_prop): interactive_potcar_file.open("POTCAR") path = pathlib.Path(interactive_potcar_file.filepath).absolute() args = ['Si', 'Si', 10000101, 'pbe', 'hash'] file_node = VaspPotcarFile(path, *args).store() # create VaspPotcarData instance associated with the stored potential and # change one of the properties potcar_data = VaspPotcarData(name='Si', version=10000101, functional='pbe') potcar_data.update_dict({change_prop: None}) # try loading the potential file which should raise an attribute error # due to the mismatch in the stored potential properties with pytest.raises(AssertionError) as exception: potcar_file_get = potcar_data.load_potential_file_node()
def test_store_and_load_potcar_file(interactive_potcar_file): from aiida.orm import load_node # potcar contents and attributes contents = "potcar file contents!" name = 'Ge_abc_de' element = 'Ge' version = 10000101 functional = 'lda_us' checksum = 'hash12345' interactive_potcar_file.open("POTCAR") interactive_potcar_file.write(contents) path_to_potcar = interactive_potcar_file.filepath # initialize the VaspPotcarFile node potcar_set = VaspPotcarFile(path_to_potcar, name, element, version, functional, checksum) uuid = potcar_set.store().uuid potcar_get = load_node(uuid) assert potcar_get.name == name assert potcar_get.version == version assert potcar_get.element == element assert potcar_get.hash == checksum assert potcar_get.functional == functional assert potcar_get.get_content() == contents
def test_load_potential_file_raises_on_undiscoverable(interactive_potcar_file): # generate arbitrary potential and process it using the potcar parser interactive_potcar_file.open("POTCAR") path = pathlib.Path(interactive_potcar_file.filepath).absolute() args = ['Si', 'Si', 10000101, 'pbe', 'hash'] potcar_file_set = VaspPotcarFile(path, *args).store() # create VaspPotcarData instance associated with the stored potential but # change uuid and hash to make the potentials undiscoverable potcar_data = VaspPotcarData(name='Si', version=10000101, functional='pbe') potcar_data.update_dict({'filenode_uuid': "changed_uuid"}) potcar_data.update_dict({'hash': "changed_hash"}) # try loading the potential using the stored hash instead of the uuid with pytest.raises(VaspPotcarDataError) as exception: potcar_file_get = potcar_data.load_potential_file_node() assert "Unable to discover associated potential" in str(exception.value)
def test_load_potential_file_node_from_uuid(interactive_potcar_file): # generate arbitrary potential and process it using the potcar parser interactive_potcar_file.open("POTCAR") path = pathlib.Path(interactive_potcar_file.filepath).absolute() args = ['Si', 'Si', 10000101, 'pbe', 'hash'] potcar_file_set = VaspPotcarFile(path, *args).store() # create VaspPotcarData instance associated with the stored potential potcar_data = VaspPotcarData(name='Si', version=10000101, functional='pbe') # load node from potcar_data instance potcar_file_get = potcar_data.load_potential_file_node() assert potcar_file_get.uuid == potcar_file_set.uuid assert potcar_file_get.hash == potcar_file_set.hash assert potcar_file_get.element == potcar_file_set.element assert potcar_file_get.functional == potcar_file_set.functional assert potcar_file_get.name == potcar_file_set.name
def test_load_potential_file_node_from_hash(interactive_potcar_file): # generate arbitrary potential and process it using the potcar parser interactive_potcar_file.open("POTCAR") path = pathlib.Path(interactive_potcar_file.filepath).absolute() args = ['Si', 'Si', 10000101, 'pbe', 'hash'] potcar_file_set = VaspPotcarFile(path, *args).store() # create VaspPotcarData instance associated with the stored potential but # change the associated uuid potcar_data = VaspPotcarData(name='Si', version=10000101, functional='pbe') potcar_data.update_dict({'filenode_uuid': "changed_uuid"}) assert potcar_data.filenode_uuid == "changed_uuid" # try loading the potential using the stored hash instead of the uuid potcar_file_get = potcar_data.load_potential_file_node() assert potcar_file_get.uuid == potcar_file_set.uuid assert potcar_file_get.hash == potcar_file_set.hash assert potcar_file_get.element == potcar_file_set.element assert potcar_file_get.functional == potcar_file_set.functional assert potcar_file_get.name == potcar_file_set.name
def list_potcar(name, element, functional): """ List VASP pseudo-potentials available in the AiiDA database. Generate and display a list of all pseudo-potentials available in the database with certain properties (i.e. name, functional or the associated element). If multiple identifiers are specified only pseudo-potentials matching all identifiers simultaneously will be shown """ from tabulate import tabulate from aiida_cusp.data.inputs.vasp_potcar import VaspPotcarFile if not any([name, element, functional]): click.echo("Please specify a potential name, element or a " "functional") return # query the database for stored pseudo-potentials matching all of the # given identifiers simultaneously potential_nodes = VaspPotcarFile.from_tags(name=name, element=element, functional=functional) # exit immediately if no potentials are found if len(potential_nodes) == 0: click.echo("No pseudo-potentials found for the given identifiers") return click.echo("") click.echo("Showing available pseudo-potentials for") click.echo("\tname: {}".format(name or 'all')) click.echo("\telement: {}".format(element or 'all')) click.echo("\tfunctional: {}".format(functional or 'all')) click.echo("") tab_entries = [] for potential_node in potential_nodes: tab_entries.append([ potential_node.id, potential_node.uuid, potential_node.name, potential_node.element, potential_node.functional, ]) tab_headers = ['id', 'uuid', 'name', 'element', 'functional'] table = tabulate(tab_entries, headers=tab_headers, tablefmt='simple') click.echo(table)
def test_add_potential_classmethod(interactive_potcar_file): # generate arbitrary potential and process it using the potcar parser potential_contents = "\n".join([ "functional Si 01Jan2000", "parameters from PSCTR are:", "VRHFIN =Si: s100p100d100", "TITEL = functional Xy 01Jan1000" "END of PSCTR-controll parameters", ]) interactive_potcar_file.open("POTCAR") interactive_potcar_file.write(potential_contents) path_to_potcar = pathlib.Path(interactive_potcar_file.filepath) potcar_parser = PotcarParser(path_to_potcar, functional='pbe', name='Si_abc') potcar_node = VaspPotcarFile.add_potential(path_to_potcar, name='Si_abc', functional='pbe') assert potcar_node.name == potcar_parser.name assert potcar_node.element == potcar_parser.element assert potcar_node.version == potcar_parser.version assert potcar_node.hash == potcar_parser.hash assert potcar_node.functional == potcar_parser.functional.lower()
def with_pbe_potcars(interactive_potcar_file): """ Create and store a set of (PBE) potcars used in the different tests """ import pathlib from aiida_cusp.data.inputs.vasp_potcar import VaspPotcarFile # define the basic attributes of the potentials stored to the set potcar_contents = [ ("Li", "pbe", "\n".join([ " PAW_PBE Li 17Jan2003", " 1.00000000000000000", " parameters from PSCTR are:", " VRHFIN =Li: s1p0", " TITEL = PAW_PBE Li 17Jan2003", "END of PSCTR-controll parameters", ])), # end entry: Li ("Br", "pbe", "\n".join([ " PAW_PBE Br 06Sep2000", " 7.00000000000000000", " parameters from PSCTR are:", " VRHFIN =Br: s2p5", " TITEL = PAW_PBE Br 06Sep2000", "END of PSCTR-controll parameters", ])), # end entry: Br ("S", "pbe", "\n".join([ " PAW_PBE S 17Jan2003", " 6.00000000000000000", " parameters from PSCTR are:", " VRHFIN =S : s2p4", " TITEL = PAW_PBE S 17Jan2003", "END of PSCTR-controll parameters", ])), # end entry: S ("P", "pbe", "\n".join([ " PAW_PBE P 17Jan2003", " 5.00000000000000000", " parameters from PSCTR are:", " VRHFIN =P : s2p3", " TITEL = PAW_PBE P 17Jan2003", "END of PSCTR-controll parameters", ])), # end entry: P ("H", "pbe", "\n".join([ " PAW_PBE H 15Jun2001", " 1.00000000000000000", " parameters from PSCTR are:", " VRHFIN =H: ultrasoft test", " TITEL = PAW_PBE H 15Jun2001", "END of PSCTR-controll parameters", ])), # end entry: H ] # end potcar_contents # store defined potentials for name, functional, potcar_content in potcar_contents: # open interative potcar file and erase all possible contents interactive_potcar_file.open("POTCAR") interactive_potcar_file.erase() # write the new pseudo-potential contents to the file and store it # to the database interactive_potcar_file.write(potcar_content) path_to_potcar = pathlib.Path(interactive_potcar_file.filepath) node = VaspPotcarFile.add_potential(path_to_potcar, name=name, functional=functional) node.store()
def add_potcar_family(path): """ Adds a VASP pseudo-potential family to an existing AiiDA database. Recursively searches the given folder [PATH] for files named POTCAR and adds all discovered potentials to the database. Note that this only works if the folder structure corresponds to that of the shipped VASP pseudo-potential libraries, i.e. every file path complies with the scheme \b /path/{FUNCTIONAL_FOLDER}/{POTENTIAL_NAME}/POTCAR where {FUNCTIONAL_FOLDER} contains one of the potential library archive names identifying the corresponding functional: \b - potuspp_lda - potpaw_lda - potpaw_lda.52 - potpaw_lda.54 - potpaw_pbe - potpaw_pbe.52 - potpaw_pbe.54 - potuspp_gga - potpaw_gga and the parent folder name {POTENTIAL_NAME} is identified with the potential's unique name that is used to store the potential to the database: \b - Li - Li_sv - Si_pv_GW - etc. """ import pathlib import warnings from tabulate import tabulate from aiida_cusp.data.inputs.vasp_potcar import VaspPotcarFile from aiida_cusp.utils.defaults import VaspDefaults from aiida_cusp.utils.potcar import PotcarPathParser from aiida_cusp.utils.exceptions import (VaspPotcarFileError, MultiplePotcarError, CommandLineError) # verify is folder path = pathlib.Path(path) if not path.is_dir(): raise CommandLineError("Given path does not point to a folder") # recurse through the folder to find all POTCAR files potcar_file_paths = list(path.rglob(VaspDefaults.FNAMES['potcar'])) potentials_to_store = [] potentials_present = [] potentials_skipped = [] tab_entries = [] for potcar_file_path in potcar_file_paths: # parse functional and name from path (will raise if functional # cannot be parsed from the path) parsed_path = PotcarPathParser(potcar_file_path) functional = parsed_path.functional name = parsed_path.name try: potential_file_node = VaspPotcarFile.add_potential( potcar_file_path, name=name, functional=functional) potentials_to_store.append(potential_file_node) tab_entries.append([ potential_file_node.name, potential_file_node.element, potential_file_node.functional, potential_file_node.version, str(potcar_file_path.absolute()), ]) except VaspPotcarFileError: warnings.warn("Skipping '{}' due to a parsing error".format( potcar_file_path.absolute())) potentials_skipped.append(potcar_file_path) continue except MultiplePotcarError: potentials_present.append(potcar_file_path) # create a tabulated overview over the new potentials tab_headers = ['name', 'element', 'functional', 'version', 'path'] table = tabulate(tab_entries, headers=tab_headers, tablefmt='simple') click.echo("") click.echo("New pseudo-potential(s) to be stored:") click.echo("") click.echo(table) # print summary to the screen num_new = len(potentials_to_store) num_present = len(potentials_present) num_skipped = len(potentials_skipped) click.echo("") click.echo("File location: {}".format(path.absolute())) click.echo("") click.echo( "Discovered a total of {} POTCAR file(s) of which".format(num_new + num_present + num_skipped)) click.echo("\t{}\twill be stored to the database,".format(num_new)) click.echo( "\t{}\tare already available in the database and".format(num_present)) click.echo("\t{}\twill be skipped due to errors".format(num_skipped)) click.echo("") # exit if no potentials will be stored anyway if num_new == 0: return # ask for confirmation before the actual storing is performed if click.confirm("Before continuing, please check the displayed list " "for possible errors! Continue and store?"): for potcar_file_node in potentials_to_store: stored_node = potcar_file_node.store() click.echo( "Created new VaspPotcarFile node with UUID {} at ID {}".format( stored_node.uuid, stored_node.id)) else: click.echo("Aborting. No potential was stored to the database!")
def add_potcar_single(path, name, functional): """ Adds a single VASP pseudo-potential to an existing AiiDA database. Requires the explicit definition of the pseudo-potential's functional, i.e. one of \b - lda_us (Ultrasoft LDA potential) - lda - lda_52 - lda_54 - pbe - pbe_52 - pbe_54 - pw91_us (Ultrasoft GGA potential) - pw91 and the pseudo-potential's unique name that is used to store and disover it in the AiiDA database: \b - Li - Li_sv - Si_pv_GW - etc. """ import pathlib import warnings from tabulate import tabulate from aiida_cusp.data.inputs.vasp_potcar import VaspPotcarFile from aiida_cusp.utils.defaults import VaspDefaults from aiida_cusp.utils.exceptions import (VaspPotcarFileError, MultiplePotcarError, CommandLineError) # verify is potcar file path = pathlib.Path(path) if not path.is_file(): raise CommandLineError("Given path does not point to a file.") if not path.name == VaspDefaults.FNAMES['potcar']: raise CommandLineError("The specified file does not seem to be a " "VASP pseudo-potential file (Expected filename " "'POTCAR'") # do the actual parsing functional = functional.lower() potentials_to_store = [] potentials_present = [] potentials_skipped = [] tab_entries = [] try: potential_file_node = VaspPotcarFile.add_potential( path, name=name, functional=functional) potentials_to_store.append(potential_file_node) tab_entries.append([ potential_file_node.name, potential_file_node.element, potential_file_node.functional, potential_file_node.version, str(path.absolute()), ]) except VaspPotcarFileError: warnings.warn("Skipping '{}' due to a parsing error".format( path.absolute())) potentials_skipped.append(path) except MultiplePotcarError: potentials_present.append(path.absolute()) # create a tabulated overview over the new potentials tab_headers = ['name', 'element', 'functional', 'version', 'path'] table = tabulate(tab_entries, headers=tab_headers, tablefmt='simple') click.echo("") click.echo("New pseudo-potential(s) to be stored:") click.echo("") click.echo(table) # print summary to the screen num_new = len(potentials_to_store) num_present = len(potentials_present) num_skipped = len(potentials_skipped) click.echo("") click.echo("File location: {}".format(path.absolute())) click.echo("") click.echo( "Discovered a total of {} POTCAR file(s) of which".format(num_new + num_present + num_skipped)) click.echo("\t{}\twill be stored to the database,".format(num_new)) click.echo( "\t{}\tare already available in the database and".format(num_present)) click.echo("\t{}\twill be skipped due to errors".format(num_skipped)) click.echo("") # exit if no potentials will be stored anyway if num_new == 0: return # ask for confirmation before the actual storing is performed if click.confirm("Before continuing, please check the displayed list " "for possible errors! Continue and store?"): for potcar_file_node in potentials_to_store: stored_node = potcar_file_node.store() click.echo( "Created new VaspPotcarFile node with UUID {} at ID {}".format( stored_node.uuid, stored_node.id)) else: click.echo("Aborting. No potential was stored to the database!")
def test_invalid_attributes_raise(interactive_potcar_file, potential_attrs): interactive_potcar_file.open("POTCAR") path_to_potcar = interactive_potcar_file.filepath with pytest.raises(VaspPotcarFileError) as exception: potcar_node = VaspPotcarFile(path_to_potcar, *potential_attrs)