def test_roundtrip_canonical(): cp2k_parser = CP2KInputParser(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 = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) fhandle.name = "StringIO" # add a filename (required by parser for context) assert cp2k_parser.parse(fhandle) == ref_tree
def test_missing_section_end(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) fhandle = io.StringIO(""" &GLOBAL ! &END GLOBAL &FORCE_EVAL &END FORCE_EVAL """) with pytest.raises(InvalidSectionError) as excinfo: cp2k_parser.parse(fhandle) assert "invalid section" in excinfo.value.args[0] fhandle = io.StringIO(""" &GLOBAL &END GLOBAL &FORCE_EVAL """) with pytest.raises(SectionMismatchError) as excinfo: cp2k_parser.parse(fhandle) assert "not closed" in excinfo.value.args[0]
def test_inline_comment(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) with open(TEST_DIR.joinpath("inputs/inline_comment.inp"), "r") as fhandle: tree = cp2k_parser.parse(fhandle) assert tree
def test_repeated_kinds_canonical(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) with open(TEST_DIR.joinpath("inputs/test04.inp"), "r") as fhandle: tree = cp2k_parser.parse(fhandle) assert tree["+force_eval"][0]["+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 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_start_empty_lines(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) with open(TEST_DIR.joinpath("inputs/empty_lines.inp"), "r") as fhandle: tree = cp2k_parser.parse(fhandle) assert tree
def test_simple_canonical(): cp2k_parser = CP2KInputParser(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_unterminated_string(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) with open(TEST_DIR.joinpath("inputs/unterminated_string.inp"), "r") as fhandle: with pytest.raises(UnterminatedStringError) as excinfo: cp2k_parser.parse(fhandle) assert excinfo.value.args[1]["linenr"] == 14
def test_roundtrip2_canonical(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) with open(TEST_DIR.joinpath("inputs/test04.inp"), "r") as fhandle: ref_tree = cp2k_parser.parse(fhandle) ref_tree["+force_eval"][0]["+subsys"]["+kind"].sort( key=lambda d: d["_"]) 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 = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) fhandle.name = "StringIO" # add a filename (required by parser for context) tree = cp2k_parser.parse(fhandle) tree["+force_eval"][0]["+subsys"]["+kind"].sort(key=lambda d: d["_"]) assert tree == ref_tree
def test_endif_garbage(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) fhandle = io.StringIO("""@IF 1\n@ENDIF foo""") with pytest.raises(PreprocessorError) as excinfo: cp2k_parser.parse(fhandle) assert "garbage found after @ENDIF" in excinfo.value.args[0]
def test_unknown_preprocessor(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) fhandle = io.StringIO("""@FOOBAR foo""") with pytest.raises(PreprocessorError) as excinfo: cp2k_parser.parse(fhandle) assert "unknown preprocessor directive found" in excinfo.value.args[0]
def test_endif_without_if(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) fhandle = io.StringIO("""@ENDIF""") with pytest.raises(PreprocessorError) as excinfo: cp2k_parser.parse(fhandle) assert "found @ENDIF without a previous @IF" in excinfo.value.args[0]
def test_nested_if(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) fhandle = io.StringIO("""@IF 1\n@IF 0\n@ENDIF\n@ENDIF""") with pytest.raises(PreprocessorError) as excinfo: cp2k_parser.parse(fhandle) assert "nested @IF are not allowed" in excinfo.value.args[0]
def test_undefined_var(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) fhandle = io.StringIO("""${undef}""") with pytest.raises(PreprocessorError) as excinfo: cp2k_parser.parse(fhandle) assert "undefined variable 'undef'" in excinfo.value.args[0]
def test_include_multiple(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) fhandle = io.StringIO("@INCLUDE 'foo' 'bar'") with pytest.raises(PreprocessorError) as excinfo: cp2k_parser.parse(fhandle) assert "@INCLUDE requires exactly one argument" in excinfo.value.args[0]
def test_xctype(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML, base_dir=[TEST_DIR.joinpath("inputs/")]) with open(TEST_DIR.joinpath("inputs/He_PBE.inp"), "r") as fhandle: tree = cp2k_parser.parse(fhandle) assert tree assert tree["+force_eval"][0]["+dft"]["+xc"]["+xc_functional"][ "_"] == "PBE"
def test_if_without_endif(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) fhandle = io.StringIO("""@IF 1\n""") with pytest.raises(PreprocessorError) as excinfo: cp2k_parser.parse(fhandle) assert "conditional block not closed at end of file" in excinfo.value.args[ 0]
def test_undefined_preprocessor_var(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) with open(TEST_DIR.joinpath("inputs/preprocesser_undefined_var.inp"), "r") as fhandle: with pytest.raises(PreprocessorError) as excinfo: cp2k_parser.parse(fhandle) assert "undefined variable 'HP'" in excinfo.value.args[0] assert excinfo.value.args[1]["linenr"] == 30
def test_fractional_values(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) with open(TEST_DIR.joinpath("inputs/fractional_values.inp"), "r") as fhandle: tree = cp2k_parser.parse(fhandle) assert tree assert tree["+force_eval"][0]["+subsys"]["+cell"]["a"][ 0] == 4.0 # specified as 8/2
def test_var_default_val(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) with open(TEST_DIR.joinpath("inputs/default_var_val.inp"), "r") as fhandle: tree_with = cp2k_parser.parse(fhandle) fhandle.seek(0) tree_without = cp2k_parser.parse(fhandle, initial_variable_values={"HP": 0}) assert "+kpoints" in tree_with["+force_eval"][0]["+dft"] assert "+kpoints" not in tree_without["+force_eval"][0]["+dft"]
def test_simple_canonical_inclusion(): cp2k_parser = CP2KInputParser(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 assert isinstance(tree["+force_eval"], list) assert isinstance(tree["+force_eval"][0], dict)
def test_error_invalid_number_of_parameters(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) with open(TEST_DIR.joinpath("inputs/error_nvar.inp"), "r") as fhandle: with pytest.raises(InvalidParameterError) as excinfo: cp2k_parser.parse(fhandle) assert "invalid values for keyword: A" in excinfo.value.args[0] assert excinfo.value.args[1]["linenr"] == 41 assert isinstance(excinfo.value.__cause__, InvalidParameterError) assert "keyword expects exactly 3 values, 2 were given" in excinfo.value.__cause__.args[ 0]
def __init__(self, cp2k_inputs_file: str, logger: logging.Logger = None): if logger is None: self.logger = logging.getLogger(__name__) else: self.logger = logger with open(cp2k_inputs_file) as f: parser = CP2KInputParser() self.cp2k_dict = parser.parse(f) self._atoms = None self._init_free_energy_section() self._init_print_section()
def test_section_parameter_error(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) fhandle = io.StringIO(""" &GLOBAL invalidparam &END GLOBAL """) with pytest.raises(InvalidParameterError) as excinfo: cp2k_parser.parse(fhandle) assert "section parameters given for non-parametrized section" in excinfo.value.args[ 0]
def test_section_end_mismatch(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) fhandle = io.StringIO(""" &GLOBAL &END GLOBI &FORCE_EVAL &END FORCE_EVAL """) with pytest.raises(SectionMismatchError) as excinfo: cp2k_parser.parse(fhandle) assert "could not match open section" in excinfo.value.args[0]
def cp2klint(fhandle, var_values, base_dir): """Check the passed CP2K file for syntax errors""" cp2k_parser = CP2KInputParser(base_dir=base_dir) try: cp2k_parser.parse(fhandle, dict(var_values)) except (TokenizerError, ParserError) as exc: ctx = exc.args[1] line = ctx.line.rstrip() print(f"Syntax error: {exc.args[0]}, in {ctx.filename}:") if exc.__cause__: print(f" {exc.__cause__}") print(f"line {ctx.linenr:>4}: {line}") if ctx.colnr is not None: count = 0 # number of underline chars after (positiv) or before (negative) the marker if ref_colnr given nchars = ctx.colnr # relevant line length if ctx.ref_colnr is not None: count = ctx.ref_colnr - ctx.colnr nchars = min(ctx.ref_colnr, ctx.colnr) # correct if ref comes before if ctx.colnrs: # shift by the number of left-stripped ws # ctx.colnrs contains the left shift for each possibly continued line nchars += ctx.colnrs[0] # assume no line-continuation for now # replace all non-ws chars with spaces: # - assuming a monospace font # - preserving other whitespace we don't know the width underline = re.sub(r"\S", " ", ctx.line[:nchars]) if count >= 0: print(f"{str():>9} {underline}^{str():~>{count}}") else: print(f"{str():>9} {underline}{str():~>{-count}}^") if ctx.ref_line is not None: print("previous definition:") print(f"line {str():>4}: {ctx.ref_line.rstrip()}") sys.exit(1) print("All done! Happy calculating!")
def test_line_continuation(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) with open(TEST_DIR.joinpath("inputs/line_continuation.inp"), "r") as fhandle: tree = cp2k_parser.parse(fhandle) assert isinstance(tree, dict) assert tree == { "+global": { "print_level": "medium", "project_name": "fatman.calc", "run_type": "energy" } }
def test_multiple_defined_non_repeating_section(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) fhandle = io.StringIO(""" &GLOBAL &END GLOBAL &GLOBAL &END GLOBAL """) with pytest.raises(InvalidNameError) as excinfo: cp2k_parser.parse(fhandle) assert "the section 'GLOBAL' can not be defined multiple times" in excinfo.value.args[ 0]
def test_invalid_keyword(): cp2k_parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) fhandle = io.StringIO(""" &FORCE_EVAL &SUBSYS BASIS_SET TZVPd-MOLOPT-SR-GTH &END SUBSYS &END FORCE_EVAL """) with pytest.raises(InvalidNameError) as excinfo: cp2k_parser.parse(fhandle) assert "invalid keyword" in excinfo.value.args[0]
def validate_inputs(self, inputs: dict) -> (bool, str): if "cp2k_inputs" not in inputs: return False, "cp2k_inputs required for cp2k" # Validate the CP2K input file. Parser will throw exceptions if invalid # TODO: More specific error handling for .inp file try: with open(inputs["cp2k_inputs"]) as f: parser = CP2KInputParser() parser.parse(f) except Exception as e: return False, f"cp2k_inputs: {str(e)}" # Otherwise let the base class validate return super().validate_inputs(inputs)