def test_roundtrip_simplified(): cp2k_parser = CP2KInputParserSimplified(DEFAULT_CP2K_INPUT_XML) with open(TEST_DIR.joinpath("inputs/test01.inp"), "r") as fhandle: ref_tree = cp2k_parser.parse(fhandle) cp2k_generator = CP2KInputGenerator(DEFAULT_CP2K_INPUT_XML) fhandle = io.StringIO("\n".join(cp2k_generator.line_iter(ref_tree))) # reinitialize parser and generators to clear any internal state they might have cp2k_parser = CP2KInputParserSimplified(DEFAULT_CP2K_INPUT_XML) fhandle.name = "StringIO" # add a filename (required by parser for context) assert cp2k_parser.parse(fhandle) == ref_tree
def test_repeated_kinds_simplified_clash(): cp2k_parser = CP2KInputParserSimplified(DEFAULT_CP2K_INPUT_XML) with open( TEST_DIR.joinpath("inputs/keyword_parameter_clash_simplified.inp"), "r") as fhandle: tree = cp2k_parser.parse(fhandle) # the BS parameter clashes with the BS (broken symmetry) keyword of the KIND section assert tree["force_eval"]["subsys"]["kind"] == [ { "_": "BS", "element": "O", "potential": "GTH-PBE-q6", "basis_set": ("ORB", "TZVP-MOLOPT-SR-GTH") }, { "_": "C", "element": "C", "potential": "GTH-PBE-q4", "basis_set": ("ORB", "TZVP-MOLOPT-SR-GTH") }, { "_": "Ti", "element": "Ti", "potential": "GTH-PBE-q12", "basis_set": ("ORB", "TZVP-MOLOPT-SR-GTH") }, ]
def cp2kget(fhandle, paths, base_dir, var_values, canonical): """Get values by path from a CP2K input file Examples for paths: force_eval/dft/mgrid/cutoff """ if canonical: cp2k_parser = CP2KInputParser(base_dir=base_dir, key_trafo=str.lower) else: cp2k_parser = CP2KInputParserSimplified(base_dir=base_dir, key_trafo=str.lower) tree = cp2k_parser.parse(fhandle, dict(var_values)) def _(val): if isinstance(val, list): return ", ".join(str(v) for v in val) return val for path in paths: sections = path.split("/") ref = tree for section in sections: if isinstance(ref, (list, tuple)): section = int( section ) # if we encounter a list, convert the respective path element ref = ref[ section] # exploit Python using references into dicts/lists print(f"{path}: {_(ref)}")
def test_simple_simplified(): cp2k_parser = CP2KInputParserSimplified(DEFAULT_CP2K_INPUT_XML) with open(TEST_DIR.joinpath("inputs/test01.inp"), "r") as fhandle: tree = cp2k_parser.parse(fhandle) assert isinstance(tree, dict) assert "global" in tree
def test_conditional_inclusion(): cp2k_parser = CP2KInputParserSimplified( DEFAULT_CP2K_INPUT_XML, base_dir=TEST_DIR.joinpath("inputs/")) with open(TEST_DIR.joinpath("inputs/test03.inp"), "r") as fhandle: tree = cp2k_parser.parse(fhandle) assert isinstance(tree, dict) assert "global" in tree assert "force_eval" not in tree
def test_repeated_keywords_tuples(): """Verify bug https://github.com/cp2k/cp2k-input-tools/issues/32 for repeated BASIS_SET is fixed""" cp2k_parser = CP2KInputParserSimplified(key_trafo=str.lower) with (TEST_DIR / "inputs" / "repeated_keywords_tuples.inp").open("r") as fhandle: tree = cp2k_parser.parse(fhandle) assert tree["force_eval"]["subsys"]["kind"]["H"]["basis_set"] == [ ("AUX_FIT", "pFIT3"), "TZV2P-MOLOPT-GTH" ]
def test_simple_simplified_inclusion(): cp2k_parser = CP2KInputParserSimplified( DEFAULT_CP2K_INPUT_XML, base_dir=TEST_DIR.joinpath("inputs/")) with open(TEST_DIR.joinpath("inputs/test02.inp"), "r") as fhandle: tree = cp2k_parser.parse(fhandle) assert isinstance(tree, dict) assert "global" in tree assert "force_eval" in tree # the simplified parser collapses repeated sections containing a single element assert isinstance(tree["force_eval"], dict)
def test_simplified_no_unpack(): cp2k_parser = CP2KInputParserSimplified(key_trafo=str.upper, multi_value_unpack=False, repeated_section_unpack=False) with (TEST_DIR / "inputs" / "deltatest_C_0.98.inp").open("r") as fhandle: tree = cp2k_parser.parse(fhandle) assert isinstance(tree, dict) assert tree["FORCE_EVAL"]["SUBSYS"]["KIND"] == { "_": "C", "ELEMENT": "C", "POTENTIAL": "GTH-PBE-q4", "BASIS_SET": "ORB TZVP-MOLOPT-SR-GTH", }
def test_simple_simplified_unitconv(): """ Testing the unit conversion from CP2K supported units (also custom defined ones) using pint to CP2K's default units (which is what will be used to store them in the tree representation. """ cp2k_parser = CP2KInputParserSimplified(DEFAULT_CP2K_INPUT_XML) with open(TEST_DIR.joinpath("inputs/test01_units.inp"), "r") as fhandle: tree = cp2k_parser.parse(fhandle) assert tree["force_eval"]["subsys"]["cell"]["a"][0] == pytest.approx( 4.07419, 1e-3) assert tree["force_eval"]["dft"]["mgrid"]["cutoff"] == pytest.approx( 1000, 1e-3) assert tree["force_eval"]["dft"]["scf"]["smear"][ "electronic_temperature"] == pytest.approx(300, 1e-3)
def fromcp2k(fhandle, oformat, canonical, base_dir, trafo, var_values): """Convert CP2K input to JSON (default), YAML or an aiida-cp2k run script template""" if oformat == "aiida-cp2k-calc": if canonical: print( "The --canonical argument is ignored when generating an aiida-cp2k run script template", file=sys.stderr) if trafo != Trafos.auto: print( "Any key transformation function other than 'auto' is ignored when generating an aiida-cp2k run script template", file=sys.stderr, ) cp2k_parser = CP2KInputParserAiiDA(base_dir=base_dir) elif canonical: cp2k_parser = CP2KInputParser(base_dir=base_dir, key_trafo=trafo.value) else: cp2k_parser = CP2KInputParserSimplified(base_dir=base_dir, key_trafo=trafo.value) tree = cp2k_parser.parse(fhandle, dict(var_values)) if oformat == "json": print(json.dumps(tree, indent=2)) elif oformat == "yaml": from ruamel.yaml import YAML yaml = YAML() yaml.dump(tree, sys.stdout) elif oformat == "aiida-cp2k-calc": from jinja2 import Environment, PackageLoader env = Environment( loader=PackageLoader("cp2k_input_tools", "templates")) env.globals.update({ "isinstance": isinstance, "Mapping": Mapping, "MutableSequence": MutableSequence }) env.filters["quoted"] = lambda item: f'"{item}"' if isinstance( item, str) else item template = env.get_template("aiida_cp2k_calc.py.j2") print(template.render(tree=tree))
def test_repeated_kinds_simplified(): cp2k_parser = CP2KInputParserSimplified(DEFAULT_CP2K_INPUT_XML) with open(TEST_DIR.joinpath("inputs/test04.inp"), "r") as fhandle: tree = cp2k_parser.parse(fhandle) assert tree["force_eval"]["subsys"]["kind"] == { "O": { "element": "O", "potential": "GTH-PBE-q6", "basis_set": ("ORB", "TZVP-MOLOPT-SR-GTH") }, "C": { "element": "C", "potential": "GTH-PBE-q4", "basis_set": ("ORB", "TZVP-MOLOPT-SR-GTH") }, "Ti": { "element": "Ti", "potential": "GTH-PBE-q12", "basis_set": ("ORB", "TZVP-MOLOPT-SR-GTH") }, }
def cp2kgen(fhandle, expressions, base_dir, canonical, var_values): """ Generates variations of the given CP2K input file Examples for generator expressions: "force_eval/dft/mgrid/cutoff=[300,400,500,800]", "force_eval/subsys/cell/a/0=10.0" """ if canonical: cp2k_parser = CP2KInputParser(base_dir=base_dir, key_trafo=str.lower) else: cp2k_parser = CP2KInputParserSimplified(base_dir=base_dir, key_trafo=str.lower) tree = cp2k_parser.parse(fhandle, dict(var_values)) # list of substitutions/transformations to apply substitutions = [] for expression in expressions: try: kpath, value = expression.split("=", maxsplit=1) except ValueError: raise ValueError( "an expression must be of the form 'path/to/key=...'" ) from None if re.match(r"^\[.+\]$", value): values = [v.strip() for v in value.strip("[]").split(",")] substitutions += [(kpath, values)] else: substitutions += [(kpath, [value])] fpath = pathlib.Path(fhandle.name) onameprefix = fpath.stem onamesuffix = fpath.suffix # first generate a list of list of tuples [ [(key/a, 10), (key/a, 20), ...], [(key/b, 100), ...], ...] for substtuple in itertools.product(*[[(k, v) for v in values] for k, values in substitutions]): # ... then iterate over the cartesian product curr_tree = deepcopy(tree) # create a full copy of the initial tree onameparts = [] # output name parts for kpath, value in substtuple: ref = curr_tree sections = kpath.split("/") for section in sections[:-1]: if isinstance(ref, list): section = int( section ) # if we encounter a list, convert the respective path element ref = ref[ section] # exploit Python using references into dicts/lists attr = sections[-1] if isinstance(ref, (list, tuple)): attr = int(attr) if isinstance(ref, tuple): # we only get tuples for keywords which can take multiple words, hence this should be safe lref = list(ref) lref[attr] = value ref = tuple(lref) else: ref[attr] = value # take only the attribute name onameparts += [f"{attr}_{value}"] opath = pathlib.Path( f"{onameprefix}-{'-'.join(onameparts)}{onamesuffix}") print(f"Writing '{opath}'...") with opath.open("w") as fouthandle: fouthandle.write( f"! Generated with the CP2K input tool cp2kgen v{__version__}\n! " ) fouthandle.write(" \\\n! ".join(f"'{arg}'" for arg in sys.argv)) fouthandle.write("\n") cp2k_generator = CP2KInputGenerator() for line in cp2k_generator.line_iter(curr_tree): fouthandle.write(f"{line}\n")