From 6d603a3dcfdfab49948bb1545f4801da78d0c1d3 Mon Sep 17 00:00:00 2001 From: James Kermode Date: Wed, 20 Sep 2023 15:20:44 +0100 Subject: [PATCH] allow comment line to be overrriden so that plain xyz files can be read --- libextxyz/extxyz.c | 13 ++++++++++--- libextxyz/extxyz.h | 7 ++++--- python/extxyz/cextxyz.py | 17 +++++++++++------ python/extxyz/cli.py | 4 +++- python/extxyz/extxyz.py | 8 +++++--- setup.py | 2 +- 6 files changed, 34 insertions(+), 17 deletions(-) diff --git a/libextxyz/extxyz.c b/libextxyz/extxyz.c index 2407bc2..9b94de1 100644 --- a/libextxyz/extxyz.c +++ b/libextxyz/extxyz.c @@ -568,8 +568,8 @@ char *read_line(char **line, unsigned long *line_len, FILE *fp) { return *line; } -int extxyz_read_ll(cleri_grammar_t *kv_grammar, FILE *fp, int *nat, DictEntry **info, DictEntry **arrays) { - char *line; +int extxyz_read_ll(cleri_grammar_t *kv_grammar, FILE *fp, int *nat, DictEntry **info, DictEntry **arrays, char *comment) { + char *line, *temp_line; unsigned long line_len; unsigned long line_len_init = 1024; @@ -603,8 +603,15 @@ int extxyz_read_ll(cleri_grammar_t *kv_grammar, FILE *fp, int *nat, DictEntry ** free(line); return 0; } - // actually parse + // actually parse - optionally replace line read from file with `comment` argument + if (comment != NULL) { + temp_line = line; + line = comment; + } cleri_parse_t * tree = cleri_parse(kv_grammar, line); + if (comment != NULL) { + line = temp_line; + } if (! tree->is_valid) { fprintf(stderr, "Failed to parse string at pos %zd\n", tree->pos); cleri_parse_free(tree); diff --git a/libextxyz/extxyz.h b/libextxyz/extxyz.h index 89e4f26..73ffd9e 100644 --- a/libextxyz/extxyz.h +++ b/libextxyz/extxyz.h @@ -1,6 +1,6 @@ /* interface for the the low-level C wrapper around libcleri parsed extxyz comment lines. - int extxyz_read_ll(kv_grammar, fp, nat, info, arrays) + int extxyz_read_ll(kv_grammar, fp, nat, info, arrays, comment) Parameters: cleri_grammar_t *kv_grammar: grammar, passed in so it does not have to be compiled in every time. calling routine should compile once and save indefinitely @@ -8,6 +8,7 @@ int *nat: storage for number of atoms DictEntry **info: pointer to allocated storage for info dict, will return pointer to first entry in linked list DictEntry **arrays: pointer to allocated storage for arrays dict, will return pointer to first entry in linked list + char *comment: NULL or pointer to replacement comment line. Useful if a previous call failed due to parse error. Returns int 0 for failure and 1 for success. @@ -67,6 +68,6 @@ typedef struct dict_entry_struct { void print_dict(DictEntry *dict); void free_dict(DictEntry *dict); -int extxyz_read_ll(cleri_grammar_t *kv_grammar, FILE *fp, int *nat, DictEntry **info, DictEntry **arrays); +int extxyz_read_ll(cleri_grammar_t *kv_grammar, FILE *fp, int *nat, DictEntry **info, DictEntry **arrays, char *comment); int extxyz_write_ll(FILE *fp, int nat, DictEntry *info, DictEntry *arrays); -void* extxyz_malloc(size_t nbytes); \ No newline at end of file +void* extxyz_malloc(size_t nbytes); diff --git a/python/extxyz/cextxyz.py b/python/extxyz/cextxyz.py index 4bc39c5..d5bacaf 100644 --- a/python/extxyz/cextxyz.py +++ b/python/extxyz/cextxyz.py @@ -49,7 +49,8 @@ class Dict_entry_struct(ctypes.Structure): extxyz.extxyz_read_ll.args = [ctypes.c_void_p, ctypes.c_void_p, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(Dict_entry_ptr), - ctypes.POINTER(Dict_entry_ptr)] + ctypes.POINTER(Dict_entry_ptr), + ctypes.c_void_p] extxyz.extxyz_write_ll.args = [ctypes.c_void_p, ctypes.c_int, Dict_entry_ptr, Dict_entry_ptr] @@ -209,12 +210,13 @@ def cfclose(fp): fclose(fp) -def read_frame_dicts(fp, verbose=False): +def read_frame_dicts(fp, verbose=False, comment=None): """Read a single frame using extxyz_read_ll() C function Args: fp (FILE_ptr): open file pointer, as returned by `cfopen()` verbose (bool, optional): Dump C dictionaries to stdout. Defaults to False. + comment (str, optional): Overrride comment line with specified string. Returns: nat, info, arrays: int, dict, dict @@ -225,11 +227,14 @@ def read_frame_dicts(fp, verbose=False): eof = False try: + if comment is not None: + comment = comment.encode('utf-8') if not extxyz.extxyz_read_ll(_kv_grammar, - fp, - ctypes.byref(nat), - ctypes.byref(info), - ctypes.byref(arrays)): + fp, + ctypes.byref(nat), + ctypes.byref(info), + ctypes.byref(arrays), + comment): eof = True raise EOFError() diff --git a/python/extxyz/cli.py b/python/extxyz/cli.py index 0140745..50fddc0 100644 --- a/python/extxyz/cli.py +++ b/python/extxyz/cli.py @@ -19,6 +19,7 @@ def main(): parser.add_argument('-R', '--round-trip', action='store_true') parser.add_argument('-P', '--profile', action='store_true') parser.add_argument('-C', '--cextxyz', action='store_true') + parser.add_argument('--comment', action='store', default=None) args = parser.parse_args() if args.round_trip: args.write = True # -R implies -w too @@ -37,7 +38,8 @@ def main(): use_regex=args.regex, create_calc=args.create_calc, calc_prefix=args.calc_prefix, - use_cextxyz=args.cextxyz) + use_cextxyz=args.cextxyz, + comment=args.comment) tr = time.time() - t0 if args.verbose: print("main output of read()") diff --git a/python/extxyz/extxyz.py b/python/extxyz/extxyz.py index 380623d..f209899 100644 --- a/python/extxyz/extxyz.py +++ b/python/extxyz/extxyz.py @@ -584,14 +584,16 @@ def read_frame_dicts(file, verbose=0, use_regex=True): def read_frame(file, verbose=0, use_cextxyz=True, - use_regex=True, create_calc=False, calc_prefix=''): + use_regex=True, create_calc=False, calc_prefix='', + comment=None): """ Read a single frame in extxyz format from `file`. """ try: if use_cextxyz: - natoms, info, arrays = cextxyz.read_frame_dicts(file, verbose=verbose) + natoms, info, arrays = cextxyz.read_frame_dicts(file, verbose=verbose, + comment=comment) properties = info.pop('Properties', 'species:S:1:pos:R:3') properties = Properties(property_string=properties) data = np.zeros(natoms, properties.dtype_vector) @@ -599,7 +601,7 @@ def read_frame(file, verbose=0, use_cextxyz=True, data[name] = value else: natoms, info, data, properties = read_frame_dicts(file, verbose=verbose, - use_regex=use_regex) + use_regex=use_regex) except EOFError: return None diff --git a/setup.py b/setup.py index 2eea0f9..8a44d5b 100644 --- a/setup.py +++ b/setup.py @@ -126,7 +126,7 @@ def get_ext_filename(self, ext_name): setup( name='extxyz', - version='0.1.3', + version='0.1.4', author='various', packages=['extxyz'], package_dir={'': 'python'},