diff --git a/.gitignore b/.gitignore index e3dc7a5..0683bcc 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,8 @@ config.log config.status config.sub configure +config.h.in +config.h depcomp include/config.h.in include/stamp-h1 @@ -19,10 +21,15 @@ install-sh libtool ltmain.sh ltximg/ -Makefile missing +Makefile +Makefile.in pkgconfig/trexio.pc -test_write.h5 +trexio.pc +trexio.mod +test-driver +test-suite.log +*.h5 trexio-*.tar.gz diff --git a/Makefile.am b/Makefile.am index d606f39..2560b5a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -91,7 +91,7 @@ $(test_trexio_f): $(trexio_f) cp $(trexio_f) $(test_trexio_f) clean-local: - -rm -rf -- test_write.dir/ test_write_f.dir/ test_write.h5 + -rm -rf -- test_write.dir/ test_write_f.dir/ test_write.h5 test_write_f.h5 if TREXIO_DEVEL diff --git a/configure.ac b/configure.ac index f2ad108..f442fe6 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,6 @@ AC_SUBST([VERSION_MINOR]) AC_SUBST([VERSION_PATCH]) - ## ------------------- ## Checks for programs ## ------------------- @@ -88,8 +87,7 @@ CPPFLAGS="${HDF5_CPPFLAGS} ${CPPFLAGS}" LDFLAGS="${HDF5_LDFLAGS} ${LDFLAGS}" LIBS="${HDF5_LIBS} ${LIBS}" - -# Check if hdf5 if available with pkg-config +# Check if HDF5 if available with pkg-config PKG_CHECK_MODULES([HDF5], [hdf5 >= 1.8], [ PKG_HDF5="hdf5" @@ -169,7 +167,8 @@ LDFLAGS .......: ${LDFLAGS} LIBS ..........: ${LIBS} Package features: - Compilation with HDF5: ${with_hdf5} + Compilation with HDF5...: ${with_hdf5} + HDF5 version............: ${HDF5_VERSION} Now type 'make @<:@@:>@' where the optional is: diff --git a/src/.gitignore b/src/.gitignore index 4c12f54..c485d0b 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,6 +1,7 @@ templates_front/*.c templates_front/*.h templates_front/*.f90 +templates_front/*.fh_90 templates_front/*.dump templates_front/populated/ diff --git a/src/templates_front/build.sh b/src/templates_front/build.sh index f20f78a..25d2261 100644 --- a/src/templates_front/build.sh +++ b/src/templates_front/build.sh @@ -12,6 +12,9 @@ cat populated/pop_*.h >> trexio.h # fortran front end cat populated/pop_*.f90 >> trexio_f.f90 +# add helper functions +cat helper_fortran.f90 >> trexio_f.f90 +cat populated/pop_*.fh_90 >> trexio_f.f90 # suffixes cat suffix_s_front.h >> trexio_s.h diff --git a/src/templates_front/templator_front.org b/src/templates_front/templator_front.org index 9b8ac23..6602c47 100644 --- a/src/templates_front/templator_front.org +++ b/src/templates_front/templator_front.org @@ -31,6 +31,8 @@ module trexio integer(trexio_backend), parameter :: TREXIO_TEXT = 1 ! integer(trexio_backend), parameter :: TREXIO_JSON = 2 integer(trexio_backend), parameter :: TREXIO_INVALID_BACK_END = 2 + + character(kind=c_char), parameter :: TREXIO_DELIM = c_new_line #+end_src #+end_src @@ -144,6 +146,7 @@ typedef int32_t trexio_exit_code; | ~TREXIO_GROUP_WRITE_ERROR~ | 20 | 'Error writing group' | | ~TREXIO_ELEM_READ_ERROR~ | 21 | 'Error reading element' | | ~TREXIO_ELEM_WRITE_ERROR~ | 22 | 'Error writing element' | + | ~TREXIO_INVALID_STR_LEN~ | 30 | 'Invalid max_str_len' | # We need to force Emacs not to indent the Python code: # -*- org-src-preserve-indentation: t @@ -174,7 +177,7 @@ return '\n'.join(result) #+RESULTS: :results: - #+begin_src c :tangle prefix_front.h :exports none + #+begin_src c :tangle prefix_front.h #define TREXIO_FAILURE ((trexio_exit_code) -1) #define TREXIO_SUCCESS ((trexio_exit_code) 0) #define TREXIO_INVALID_ARG_1 ((trexio_exit_code) 1) @@ -199,9 +202,10 @@ return '\n'.join(result) #define TREXIO_GROUP_WRITE_ERROR ((trexio_exit_code) 20) #define TREXIO_ELEM_READ_ERROR ((trexio_exit_code) 21) #define TREXIO_ELEM_WRITE_ERROR ((trexio_exit_code) 22) + #define TREXIO_INVALID_STR_LEN ((trexio_exit_code) 30) #+end_src - #+begin_src f90 :tangle prefix_fortran.f90 :exports none + #+begin_src f90 :tangle prefix_fortran.f90 integer(trexio_exit_code), parameter :: TREXIO_FAILURE = -1 integer(trexio_exit_code), parameter :: TREXIO_SUCCESS = 0 integer(trexio_exit_code), parameter :: TREXIO_INVALID_ARG_1 = 1 @@ -226,6 +230,7 @@ return '\n'.join(result) integer(trexio_exit_code), parameter :: TREXIO_GROUP_WRITE_ERROR = 20 integer(trexio_exit_code), parameter :: TREXIO_ELEM_READ_ERROR = 21 integer(trexio_exit_code), parameter :: TREXIO_ELEM_WRITE_ERROR = 22 + integer(trexio_exit_code), parameter :: TREXIO_INVALID_STR_LEN = 30 #+end_src :end: @@ -388,6 +393,8 @@ typedef int32_t back_end_t; #define TREXIO_TEXT ( (back_end_t) 1 ) /*#define TREXIO_JSON ( (back_end_t) 2 )*/ #define TREXIO_INVALID_BACK_END ( (back_end_t) 2 ) + +#define TREXIO_DELIM "\n" #+end_src ** Read/write behavior @@ -712,30 +719,8 @@ interface end interface #+end_src -** C helper functions - - #+begin_src c :tangle prefix_front.c -trexio_exit_code transform_str (char** dest, const char** src, uint64_t str_max_num, uint32_t str_max_len){ - - if (dest == NULL) return TREXIO_INVALID_ARG_1; - assert (str_max_num > 0); - assert (str_max_len > 0); - - char* tmp_str = (char*)calloc(str_max_num*(str_max_len+1)+1,sizeof(char)); - - for (int i=0; iback_end) { case TREXIO_TEXT: - rc = trexio_text_write_$group_num$(file, (int64_t) num); + return trexio_text_write_$group_num$(file, (int64_t) num); break; case TREXIO_HDF5: - rc = trexio_hdf5_write_$group_num$(file, (int64_t) num); + return trexio_hdf5_write_$group_num$(file, (int64_t) num); break; /* case TREXIO_JSON: - rc = trexio_json_write_$group_num$(file, (int64_t) num); + return trexio_json_write_$group_num$(file, (int64_t) num); break; ,*/ } - if (rc != TREXIO_SUCCESS) return rc; - return TREXIO_SUCCESS; + return TREXIO_FAILURE; } #+end_src @@ -941,26 +923,23 @@ trexio_write_$group_num$_32 (trexio_t* const file, const int32_t num) if (num < 0 ) return TREXIO_INVALID_ARG_2; if (trexio_has_$group_num$(file) == TREXIO_SUCCESS) return TREXIO_NUM_ALREADY_EXISTS; - trexio_exit_code rc = TREXIO_GROUP_WRITE_ERROR; - switch (file->back_end) { case TREXIO_TEXT: - rc = trexio_text_write_$group_num$(file, (int64_t) num); + return trexio_text_write_$group_num$(file, (int64_t) num); break; case TREXIO_HDF5: - rc = trexio_hdf5_write_$group_num$(file, (int64_t) num); + return trexio_hdf5_write_$group_num$(file, (int64_t) num); break; /* case TREXIO_JSON: - rc = trexio_json_write_$group_num$(file, (int64_t) num); + return trexio_json_write_$group_num$(file, (int64_t) num); break; ,*/ } - if (rc != TREXIO_SUCCESS) return rc; - return TREXIO_SUCCESS; + return TREXIO_FAILURE; } #+end_src @@ -1004,8 +983,8 @@ trexio_has_$group_num$ (trexio_t* const file) break; ,*/ } - return TREXIO_FAILURE; + return TREXIO_FAILURE; } #+end_src @@ -1083,7 +1062,7 @@ interface end interface #+end_src -** Templates for front end has/read/write a dataset +** Templates for front end has/read/write a dataset of numerical data This section concerns API calls related to datasets. @@ -1106,7 +1085,7 @@ end interface The basic (non-suffixed) API call on datasets deals with double precision (see Table above). - #+begin_src c :tangle hrw_dset_front.h :exports none + #+begin_src c :tangle hrw_dset_data_front.h :exports none trexio_exit_code trexio_has_$group_dset$(trexio_t* const file); trexio_exit_code trexio_read_$group_dset$(trexio_t* const file, $group_dset_dtype_default$* const $group_dset$); trexio_exit_code trexio_write_$group_dset$(trexio_t* const file, const $group_dset_dtype_default$* $group_dset$); @@ -1116,7 +1095,7 @@ trexio_exit_code trexio_read_$group_dset$_64(trexio_t* const file, $group_dset_d trexio_exit_code trexio_write_$group_dset$_64(trexio_t* const file, const $group_dset_dtype_double$* $group_dset$); #+end_src - #+begin_src c :tangle read_dset_64_front.c + #+begin_src c :tangle read_dset_data_64_front.c trexio_exit_code trexio_read_$group_dset$_64 (trexio_t* const file, $group_dset_dtype_double$* const $group_dset$) { @@ -1171,7 +1150,7 @@ trexio_read_$group_dset$_64 (trexio_t* const file, $group_dset_dtype_double$* co } #+end_src - #+begin_src c :tangle write_dset_64_front.c + #+begin_src c :tangle write_dset_data_64_front.c trexio_exit_code trexio_write_$group_dset$_64 (trexio_t* const file, const $group_dset_dtype_double$* $group_dset$) { @@ -1192,7 +1171,7 @@ trexio_write_$group_dset$_64 (trexio_t* const file, const $group_dset_dtype_doub uint32_t rank = $group_dset_rank$; uint64_t dims[$group_dset_rank$] = {$group_dset_dim_list$}; - $group_dset_dtype_double$* $group_dset$_p = $group_dset$; + $group_dset_dtype_double$* $group_dset$_p = ($group_dset_dtype_double$*) $group_dset$; /* Handle index type */ if ($is_index$) { @@ -1238,7 +1217,7 @@ trexio_write_$group_dset$_64 (trexio_t* const file, const $group_dset_dtype_doub } #+end_src - #+begin_src c :tangle read_dset_32_front.c + #+begin_src c :tangle read_dset_data_32_front.c trexio_exit_code trexio_read_$group_dset$_32 (trexio_t* const file, $group_dset_dtype_single$* const $group_dset$) { @@ -1305,7 +1284,7 @@ trexio_read_$group_dset$_32 (trexio_t* const file, $group_dset_dtype_single$* co } #+end_src - #+begin_src c :tangle write_dset_32_front.c + #+begin_src c :tangle write_dset_data_32_front.c trexio_exit_code trexio_write_$group_dset$_32 (trexio_t* const file, const $group_dset_dtype_single$* $group_dset$) { @@ -1371,7 +1350,7 @@ trexio_write_$group_dset$_32 (trexio_t* const file, const $group_dset_dtype_sing } #+end_src - #+begin_src c :tangle read_dset_def_front.c + #+begin_src c :tangle read_dset_data_def_front.c trexio_exit_code trexio_read_$group_dset$ (trexio_t* const file, $group_dset_dtype_default$* const $group_dset$) { @@ -1379,7 +1358,7 @@ trexio_read_$group_dset$ (trexio_t* const file, $group_dset_dtype_default$* cons } #+end_src - #+begin_src c :tangle write_dset_def_front.c + #+begin_src c :tangle write_dset_data_def_front.c trexio_exit_code trexio_write_$group_dset$ (trexio_t* const file, const $group_dset_dtype_default$* $group_dset$) { @@ -1387,7 +1366,7 @@ trexio_write_$group_dset$ (trexio_t* const file, const $group_dset_dtype_default } #+end_src - #+begin_src c :tangle has_dset_front.c + #+begin_src c :tangle has_dset_data_front.c trexio_exit_code trexio_has_$group_dset$ (trexio_t* const file) { @@ -1420,7 +1399,7 @@ trexio_has_$group_dset$ (trexio_t* const file) The ~Fortran~ templates that provide an access to the ~C~ API calls from ~Fortran~. These templates are based on the use of ~iso_c_binding~. Pointers have to be passed by value. - #+begin_src f90 :tangle write_dset_64_front_fortran.f90 + #+begin_src f90 :tangle write_dset_data_64_front_fortran.f90 interface integer function trexio_write_$group_dset$_64 (trex_file, dset) bind(C) use, intrinsic :: iso_c_binding @@ -1430,7 +1409,7 @@ interface end interface #+end_src - #+begin_src f90 :tangle read_dset_64_front_fortran.f90 + #+begin_src f90 :tangle read_dset_data_64_front_fortran.f90 interface integer function trexio_read_$group_dset$_64 (trex_file, dset) bind(C) use, intrinsic :: iso_c_binding @@ -1440,7 +1419,7 @@ interface end interface #+end_src - #+begin_src f90 :tangle write_dset_32_front_fortran.f90 + #+begin_src f90 :tangle write_dset_data_32_front_fortran.f90 interface integer function trexio_write_$group_dset$_32 (trex_file, dset) bind(C) use, intrinsic :: iso_c_binding @@ -1450,7 +1429,7 @@ interface end interface #+end_src - #+begin_src f90 :tangle read_dset_32_front_fortran.f90 + #+begin_src f90 :tangle read_dset_data_32_front_fortran.f90 interface integer function trexio_read_$group_dset$_32 (trex_file, dset) bind(C) use, intrinsic :: iso_c_binding @@ -1460,7 +1439,7 @@ interface end interface #+end_src - #+begin_src f90 :tangle write_dset_def_front_fortran.f90 + #+begin_src f90 :tangle write_dset_data_def_front_fortran.f90 interface integer function trexio_write_$group_dset$ (trex_file, dset) bind(C) use, intrinsic :: iso_c_binding @@ -1470,7 +1449,7 @@ interface end interface #+end_src - #+begin_src f90 :tangle read_dset_def_front_fortran.f90 + #+begin_src f90 :tangle read_dset_data_def_front_fortran.f90 interface integer function trexio_read_$group_dset$ (trex_file, dset) bind(C) use, intrinsic :: iso_c_binding @@ -1480,7 +1459,7 @@ interface end interface #+end_src - #+begin_src f90 :tangle has_dset_front_fortran.f90 + #+begin_src f90 :tangle has_dset_data_front_fortran.f90 interface integer function trexio_has_$group_dset$ (trex_file) bind(C) use, intrinsic :: iso_c_binding @@ -1603,6 +1582,524 @@ trexio_read_chunk_ao_2e_int_eri_value_64(trexio_t* const file, } #+END_SRC +** Templates for front end has/read/write a dataset of strings +*** Introduction + This section concerns API calls related to datasets of strings. + + | Function name | Description | + |-----------------------------+-------------------------------------| + | ~trexio_has_$group_dset$~ | Check if a dataset exists in a file | + | ~trexio_read_$group_dset$~ | Read a dataset | + | ~trexio_write_$group_dset$~ | Write a dataset | + +*** C templates for front end + + First parameter is the ~TREXIO~ file handle. Second parameter is the variable to be written/read + to/from the ~TREXIO~ file (except for ~trexio_has_~ functions). + + + #+begin_src c :tangle hrw_dset_str_front.h :exports none +trexio_exit_code trexio_has_$group_dset$(trexio_t* const file); +trexio_exit_code trexio_read_$group_dset$_low(trexio_t* const file, char* dset, const uint32_t max_str_len); +trexio_exit_code trexio_write_$group_dset$_low(trexio_t* const file, const char* dset, const uint32_t max_str_len); +trexio_exit_code trexio_read_$group_dset$(trexio_t* const file, char** dset, const uint32_t max_str_len); +trexio_exit_code trexio_write_$group_dset$(trexio_t* const file, const char** dset, const uint32_t max_str_len); + #+end_src + + #+begin_src c :tangle read_dset_str_front.c +trexio_exit_code +trexio_read_$group_dset$_low (trexio_t* const file, char* dset, const uint32_t max_str_len) +{ + + if (file == NULL) return TREXIO_INVALID_ARG_1; + if (dset == NULL) return TREXIO_INVALID_ARG_2; + if (max_str_len <= 0) return TREXIO_INVALID_ARG_3; + + trexio_exit_code rc; + int64_t $group_dset_dim$ = 0; + + /* Error handling for this call is added by the generator */ + rc = trexio_read_$group_dset_dim$_64(file, &($group_dset_dim$)); + + if ($group_dset_dim$ == 0L) return TREXIO_INVALID_NUM; + + uint32_t rank = $group_dset_rank$; + uint64_t dims[$group_dset_rank$] = {$group_dset_dim_list$}; + + assert(file->back_end < TREXIO_INVALID_BACK_END); + switch (file->back_end) { + + case TREXIO_TEXT: + return trexio_text_read_$group_dset$(file, dset, rank, dims, max_str_len); + break; + + case TREXIO_HDF5: + return trexio_hdf5_read_$group_dset$(file, dset, rank, dims, max_str_len); + break; +/* + case TREXIO_JSON: + rc = trexio_json_read_$group_dset$(file, dset, rank, dims); + break; +,*/ + } + +} + +trexio_exit_code +trexio_read_$group_dset$ (trexio_t* const file, char** dset, const uint32_t max_str_len) +{ + + if (file == NULL) return TREXIO_INVALID_ARG_1; + if (dset == NULL) return TREXIO_INVALID_ARG_2; + if (max_str_len <= 0) return TREXIO_INVALID_ARG_3; + + assert(file->back_end < TREXIO_INVALID_BACK_END); + + trexio_exit_code rc; + int64_t dset_dim = 0; + + /* Error handling for this call is added by the generator */ + rc = trexio_read_$group_dset_dim$_64(file, &(dset_dim)); + + if (dset_dim == 0L) return TREXIO_INVALID_NUM; + + char* str_compiled = CALLOC(dset_dim*(max_str_len+1) + 1, char); + if (str_compiled == NULL) return TREXIO_ALLOCATION_FAILED; + + rc = trexio_read_$group_dset$_low(file, str_compiled, max_str_len); + if (rc != TREXIO_SUCCESS) { + FREE(str_compiled); + return rc; + } + + char * pch; + for (uint64_t i=0; i < dset_dim; i++) { + + pch = i == 0 ? strtok(str_compiled, TREXIO_DELIM) : strtok(NULL, TREXIO_DELIM) ; + if (pch == NULL) { + FREE(str_compiled); + return TREXIO_FAILURE; + } + + strcpy(dset[i], ""); + strcat(dset[i], pch); + + } + + FREE(str_compiled); + return TREXIO_SUCCESS; + +} + #+end_src + + #+begin_src c :tangle write_dset_str_front.c +trexio_exit_code +trexio_write_$group_dset$_low (trexio_t* const file, const char* dset, const uint32_t max_str_len) +{ + + if (file == NULL) return TREXIO_INVALID_ARG_1; + if (dset == NULL) return TREXIO_INVALID_ARG_2; + if (max_str_len <= 0) return TREXIO_INVALID_ARG_3; + if (trexio_has_$group_dset$(file) == TREXIO_SUCCESS) return TREXIO_DSET_ALREADY_EXISTS; + + trexio_exit_code rc; + int64_t $group_dset_dim$ = 0; + + /* Error handling for this call is added by the generator */ + rc = trexio_read_$group_dset_dim$_64(file, &($group_dset_dim$)); + + if ($group_dset_dim$ == 0L) return TREXIO_INVALID_NUM; + + uint32_t rank = $group_dset_rank$; + uint64_t dims[$group_dset_rank$] = {$group_dset_dim_list$}; + + assert(file->back_end < TREXIO_INVALID_BACK_END); + + char* tmp_str = CALLOC(dims[0]*(max_str_len+1), char); + if (tmp_str == NULL) return TREXIO_ALLOCATION_FAILED; + char** dset_str = CALLOC(dims[0], char*); + if (dset_str == NULL) return TREXIO_ALLOCATION_FAILED; + + char* pch; + size_t pch_len; + /* parse the string using strtok */ + for(uint64_t i=0; i max_str_len) { + FREE(dset_str[0]); + FREE(dset_str); + return TREXIO_INVALID_STR_LEN; + } + + dset_str[i] = tmp_str; + strncpy(tmp_str, pch, pch_len); + tmp_str += pch_len + 1; + } + + rc = TREXIO_FAILURE; + switch (file->back_end) { + + case TREXIO_TEXT: + rc = trexio_text_write_$group_dset$(file, (const char**) dset_str, rank, dims); + break; + + case TREXIO_HDF5: + rc = trexio_hdf5_write_$group_dset$(file, (const char**) dset_str, rank, dims); + break; +/* + case TREXIO_JSON: + rc = trexio_json_write_$group_dset$(file, dset, rank, dims); + break; +,*/ + } + + FREE(dset_str[0]); + FREE(dset_str); + + return rc; + +} + +trexio_exit_code +trexio_write_$group_dset$ (trexio_t* const file, const char** dset, const uint32_t max_str_len) +{ + + if (file == NULL) return TREXIO_INVALID_ARG_1; + if (dset == NULL) return TREXIO_INVALID_ARG_2; + if (max_str_len <= 0) return TREXIO_INVALID_ARG_3; + if (trexio_has_$group_dset$(file) == TREXIO_SUCCESS) return TREXIO_DSET_ALREADY_EXISTS; + + assert(file->back_end < TREXIO_INVALID_BACK_END); + + trexio_exit_code rc; + int64_t dset_dim = 0; + + /* Error handling for this call is added by the generator */ + rc = trexio_read_$group_dset_dim$_64(file, &(dset_dim)); + + if (dset_dim == 0L) return TREXIO_INVALID_NUM; + + char* str_compiled = CALLOC(dset_dim*max_str_len + 1, char); + if (str_compiled == NULL) return TREXIO_ALLOCATION_FAILED; + + strcpy(str_compiled, ""); + for (uint64_t i=0; i < dset_dim; i++) { + strcat(str_compiled, dset[i]); + strcat(str_compiled, TREXIO_DELIM); + } + + rc = trexio_write_$group_dset$_low(file, str_compiled, max_str_len); + + FREE(str_compiled); + + return rc; + +} + #+end_src + + #+begin_src c :tangle has_dset_str_front.c +trexio_exit_code +trexio_has_$group_dset$ (trexio_t* const file) +{ + + if (file == NULL) return TREXIO_INVALID_ARG_1; + + assert(file->back_end < TREXIO_INVALID_BACK_END); + + switch (file->back_end) { + + case TREXIO_TEXT: + return trexio_text_has_$group_dset$(file); + break; + + case TREXIO_HDF5: + return trexio_hdf5_has_$group_dset$(file); + break; +/* + case TREXIO_JSON: + return trexio_json_has_$group_dset$(file); + break; +,*/ + } + return TREXIO_FAILURE; +} + #+end_src + +*** Fortran templates for front end + + The ~Fortran~ templates that provide an access to the ~C~ API calls from ~Fortran~. + These templates are based on the use of ~iso_c_binding~. Pointers have to be passed by value. + + #+begin_src f90 :tangle write_dset_str_front_fortran.f90 +interface + integer function trexio_write_$group_dset$_low (trex_file, dset, max_str_len) bind(C) + use, intrinsic :: iso_c_binding + integer(8), intent(in), value :: trex_file + character, intent(in) :: dset(*) + integer(4), intent(in), value :: max_str_len + end function trexio_write_$group_dset$_low +end interface + #+end_src + + #+begin_src f90 :tangle read_dset_str_front_fortran.f90 +interface + integer function trexio_read_$group_dset$_low (trex_file, dset, max_str_len) bind(C) + use, intrinsic :: iso_c_binding + integer(8), intent(in), value :: trex_file + character, intent(out) :: dset(*) + integer(4), intent(in), value :: max_str_len + end function trexio_read_$group_dset$_low +end interface + #+end_src + + #+begin_src f90 :tangle has_dset_str_front_fortran.f90 +interface + integer function trexio_has_$group_dset$ (trex_file) bind(C) + use, intrinsic :: iso_c_binding + integer(8), intent(in), value :: trex_file + end function trexio_has_$group_dset$ +end interface + #+end_src + + #+begin_src f90 :tangle helper_read_dset_str_front_fortran.fh_90 + integer function trexio_read_$group_dset$ (trex_file, dset, max_str_len) + implicit none + integer(8), intent(in), value :: trex_file + integer(4), intent(in), value :: max_str_len + character(len=*), intent(inout) :: dset(*) + + character, allocatable :: str_compiled(:) + integer(8) :: $group_dset_dim$ + integer :: rc + + rc = trexio_read_$group_dset_dim$_64(trex_file, $group_dset_dim$) + if (rc /= TREXIO_SUCCESS) trexio_read_$group_dset$ = rc + + allocate(str_compiled($group_dset_dim$*(max_str_len+1)+1)) + + rc = trexio_read_$group_dset$_low(trex_file, str_compiled, max_str_len) + if (rc /= TREXIO_SUCCESS) then + deallocate(str_compiled) + trexio_read_$group_dset$ = rc + else + call trexio_str2strarray(str_compiled, $group_dset_dim$, max_str_len, dset) + deallocate(str_compiled) + trexio_read_$group_dset$ = TREXIO_SUCCESS + endif + + end function trexio_read_$group_dset$ + #+end_src + + #+begin_src f90 :tangle helper_write_dset_str_front_fortran.fh_90 + integer function trexio_write_$group_dset$ (trex_file, dset, max_str_len) + implicit none + integer(8), intent(in), value :: trex_file + integer(4), intent(in), value :: max_str_len + character(len=*), intent(in) :: dset(*) + + character(len=:), allocatable :: str_compiled + integer(8) :: $group_dset_dim$ + integer :: rc + + rc = trexio_read_$group_dset_dim$_64(trex_file, $group_dset_dim$) + if (rc /= TREXIO_SUCCESS) then + trexio_write_$group_dset$ = rc + else + call trexio_strarray2str(dset, $group_dset_dim$, max_str_len, str_compiled) + trexio_write_$group_dset$ = trexio_write_$group_dset$_low(trex_file, str_compiled, max_str_len) + endif + + end function trexio_write_$group_dset$ + #+end_src + +** Templates for front end has/read/write a single string attribute +*** Introduction + + This section concerns API calls related to string attributes. + + | Function name | Description | + |----------------------------+----------------------------------------------| + | ~trexio_has_$group_str$~ | Check if a string attribute exists in a file | + | ~trexio_read_$group_str$~ | Read a string attribute | + | ~trexio_write_$group_str$~ | Write a string attribute | + +*** C templates for front end + + #+begin_src c :tangle hrw_attr_str_front.h :exports none +trexio_exit_code trexio_has_$group_str$(trexio_t* const file); +trexio_exit_code trexio_read_$group_str$(trexio_t* const file, char* const str, const uint32_t max_str_len); +trexio_exit_code trexio_write_$group_str$(trexio_t* const file, const char* str, const uint32_t max_str_len); + #+end_src + + #+begin_src c :tangle read_attr_str_front.c +trexio_exit_code +trexio_read_$group_str$ (trexio_t* const file, char* const str, const uint32_t max_str_len) +{ + + if (file == NULL) return TREXIO_INVALID_ARG_1; + if (str == NULL) return TREXIO_INVALID_ARG_2; + if (max_str_len <= 0) return TREXIO_INVALID_ARG_3; + + trexio_exit_code rc = TREXIO_FAILURE; + + switch (file->back_end) { + + case TREXIO_TEXT: + return trexio_text_read_$group_str$(file, str, max_str_len); + break; + + case TREXIO_HDF5: + return trexio_hdf5_read_$group_str$(file, str, max_str_len); + break; +/* + case TREXIO_JSON: + return trexio_json_read_$group_str$(file, str); + break; +,*/ + } + +} + #+end_src + + #+begin_src c :tangle write_attr_str_front.c +trexio_exit_code +trexio_write_$group_str$ (trexio_t* const file, const char* str, const uint32_t max_str_len) +{ + + if (file == NULL) return TREXIO_INVALID_ARG_1; + if (str == NULL) return TREXIO_INVALID_ARG_2; + if (max_str_len <= 0) return TREXIO_INVALID_ARG_3; + if (trexio_has_$group_str$(file) == TREXIO_SUCCESS) return TREXIO_NUM_ALREADY_EXISTS; + + size_t len_write = strlen(str); + if (max_str_len < len_write) return TREXIO_INVALID_STR_LEN; + + switch (file->back_end) { + + case TREXIO_TEXT: + return trexio_text_write_$group_str$(file, str); + break; + + case TREXIO_HDF5: + return trexio_hdf5_write_$group_str$(file, str); + break; +/* + case TREXIO_JSON: + return trexio_json_write_$group_str$(file, str); + break; +,*/ + } + +} + #+end_src + + #+begin_src c :tangle has_attr_str_front.c +trexio_exit_code +trexio_has_$group_str$ (trexio_t* const file) +{ + + if (file == NULL) return TREXIO_INVALID_ARG_1; + + assert(file->back_end < TREXIO_INVALID_BACK_END); + + switch (file->back_end) { + + case TREXIO_TEXT: + return trexio_text_has_$group_str$(file); + break; + + case TREXIO_HDF5: + return trexio_hdf5_has_$group_str$(file); + break; +/* + case TREXIO_JSON: + return trexio_json_has_$group_str$(file); + break; +,*/ + } + + return TREXIO_FAILURE; +} + #+end_src + +*** Fortran templates for front end + + The ~Fortran~ templates that provide an access to the ~C~ API calls from Fortran. + These templates are based on the use of ~iso_c_binding~. Pointers have to be passed by value. + + + #+begin_src f90 :tangle write_attr_str_front_fortran.f90 +interface + integer function trexio_write_$group_str$_c (trex_file, str, max_str_len) & + bind(C, name="trexio_write_$group_str$") + use, intrinsic :: iso_c_binding + integer(8), intent(in), value :: trex_file + character, intent(in) :: str(*) + integer(4), intent(in), value :: max_str_len + end function trexio_write_$group_str$_c +end interface + #+end_src + + #+begin_src f90 :tangle read_attr_str_front_fortran.f90 +interface + integer function trexio_read_$group_str$_c (trex_file, str, max_str_len) & + bind(C, name="trexio_read_$group_str$") + use, intrinsic :: iso_c_binding + integer(8), intent(in), value :: trex_file + character, intent(out) :: str(*) + integer(4), intent(in), value :: max_str_len + end function trexio_read_$group_str$_c +end interface + #+end_src + + #+begin_src f90 :tangle has_attr_str_front_fortran.f90 +interface + integer function trexio_has_$group_str$ (trex_file) bind(C) + use, intrinsic :: iso_c_binding + integer(8), intent(in), value :: trex_file + end function trexio_has_$group_str$ +end interface + #+end_src + + #+begin_src f90 :tangle helper_read_attr_str_front_fortran.fh_90 + integer function trexio_read_$group_str$ (trex_file, str, max_str_len) + implicit none + integer(8), intent(in), value :: trex_file + integer(4), intent(in), value :: max_str_len + character, intent(out) :: str(*) + + trexio_read_$group_str$ = trexio_read_$group_str$_c(trex_file, str, max_str_len) + + end function trexio_read_$group_str$ + #+end_src + + #+begin_src f90 :tangle helper_write_attr_str_front_fortran.fh_90 + integer function trexio_write_$group_str$ (trex_file, str, max_str_len) + use, intrinsic :: iso_c_binding, only : c_null_char + implicit none + integer(8), intent(in), value :: trex_file + integer(4), intent(in), value :: max_str_len + character(len=*), intent(in) :: str + + character(len=len_trim(str)+1) :: str_c + + str_c = trim(str) // c_null_char + + trexio_write_$group_str$ = trexio_write_$group_str$_c(trex_file, str_c, max_str_len) + + end function trexio_write_$group_str$ + #+end_src + * Fortran helper/wrapper functions The function below adapts the original C-based ~trexio_open~ for Fortran. @@ -1610,12 +2107,12 @@ trexio_read_chunk_ao_2e_int_eri_value_64(trexio_t* const file, unlike strings in Fortran. Note, that Fortran interface calls the main ~TREXIO~ API, which is written in C. - #+begin_src f90 :tangle suffix_fortran.f90 + #+begin_src f90 :tangle helper_fortran.f90 contains integer(8) function trexio_open (filename, mode, backend) - use, intrinsic :: iso_c_binding + use, intrinsic :: iso_c_binding, only : c_null_char implicit none - character(len=*) :: filename + character(len=*), intent(in) :: filename character, intent(in), value :: mode integer(trexio_backend), intent(in), value :: backend character(len=len_trim(filename)+1) :: filename_c @@ -1634,6 +2131,93 @@ contains end function trexio_open #+end_src + The subroutine below transforms an array of Fortran strings into one big string using ~TREXIO_DELIM~ symbol + as a delimeter and adds ~NULL~ character in the end in order to properly pass the desired string to + C API. This is needed due to the fact that strings in C are terminated by ~NULL~ character ~\0~. + + #+begin_src f90 :tangle helper_fortran.f90 + subroutine trexio_strarray2str(str_array, max_num_str, max_len_str, str_res) + use, intrinsic :: iso_c_binding, only : c_null_char + implicit none + + integer(8), intent(in), value :: max_num_str ! number of elements in strign array + integer, intent(in), value :: max_len_str ! maximum length of a string in an array + character(len=*), intent(in) :: str_array(*) + character(len=:), allocatable, intent(out) :: str_res + integer :: i + + str_res = '' + do i = 1, max_num_str + str_res = str_res // trim(str_array(i)) // TREXIO_DELIM + enddo + str_res = str_res // c_null_char + + end subroutine trexio_strarray2str + #+end_src + + The subroutine below does the reverse tranformation from one big string with delimeters into an array of Fortran strings. + + #+begin_src f90 :tangle helper_fortran.f90 + subroutine trexio_str2strarray(str_flat, max_num_str, max_len_str, str_array) + implicit none + + integer(8), intent(in), value :: max_num_str ! number of elements in strign array + integer, intent(in), value :: max_len_str ! maximum length of a string in an array + character, intent(in) :: str_flat(*) + character(len=*), intent(inout) :: str_array(*) + + character(len=max_len_str) :: tmp_str + integer :: i, j, k, ind, offset + integer(8) :: len_flat + + len_flat = (max_len_str+1)*max_num_str + 1 + + ind=1 + offset=1 + do i=1,max_num_str + k = 1 + tmp_str='' + do j=ind,len_flat + if (str_flat(j) == TREXIO_DELIM) then + ind=j+1 + exit + endif + tmp_str(k:k) = str_flat(j) + k = k + 1 + enddo + str_array(i)=tmp_str + offset=ind + enddo + + end subroutine trexio_str2strarray + #+end_src + + + The subroutine is a Fortran analogue of ~assert~ in C. It check that the the return code of the + TREXIO API call is equal to a given return code. It can optionally print a success message if the + two code are identical, i.e. if the ~assert~ statement pass. + + #+begin_src f90 :tangle helper_fortran.f90 + subroutine trexio_assert(trexio_rc, check_rc, success_message) + implicit none + + integer, intent(in), value :: trexio_rc + integer, intent(in), value :: check_rc + character(len=*), intent(in), optional :: success_message + + character*(128) :: str + + if (trexio_rc == check_rc) then + if (present(success_message)) write(*,*) success_message + else + call trexio_string_of_error(trexio_rc, str) + print *, trim(str) + call exit(1) + endif + + end subroutine trexio_assert + #+end_src + * File suffixes :noexport: #+begin_src c :tangle suffix_front.h diff --git a/src/templates_hdf5/build.sh b/src/templates_hdf5/build.sh index 8e01f5a..137c7a5 100644 --- a/src/templates_hdf5/build.sh +++ b/src/templates_hdf5/build.sh @@ -7,14 +7,10 @@ cat populated/pop_def_hdf5.c >> trexio_hdf5.c cat populated/pop_struct_hdf5.h >> trexio_hdf5.h cat populated/pop_basic_hdf5.c >> trexio_hdf5.c -cat populated/pop_has_dset_hdf5.c >> trexio_hdf5.c -cat populated/pop_has_num_hdf5.c >> trexio_hdf5.c -cat populated/pop_read_dset_hdf5.c >> trexio_hdf5.c -cat populated/pop_read_num_hdf5.c >> trexio_hdf5.c -cat populated/pop_write_dset_hdf5.c >> trexio_hdf5.c -cat populated/pop_write_num_hdf5.c >> trexio_hdf5.c -cat populated/pop_hrw_num_hdf5.h >> trexio_hdf5.h -cat populated/pop_hrw_dset_hdf5.h >> trexio_hdf5.h +cat populated/pop_has_*.c >> trexio_hdf5.c +cat populated/pop_read_*.c >> trexio_hdf5.c +cat populated/pop_write_*.c >> trexio_hdf5.c +cat populated/pop_hrw_*.h >> trexio_hdf5.h cat suffix_hdf5.h >> trexio_hdf5.h diff --git a/src/templates_hdf5/templator_hdf5.org b/src/templates_hdf5/templator_hdf5.org index f6510b9..83146e8 100644 --- a/src/templates_hdf5/templator_hdf5.org +++ b/src/templates_hdf5/templator_hdf5.org @@ -39,7 +39,7 @@ #include #include "hdf5.h" -#include "hdf5_hl.h" // needed for high-level APIs like H5LT, requires additional linking in Makefile +#include "hdf5_hl.h" #+end_src @@ -56,6 +56,7 @@ #define $GROUP$_GROUP_NAME "$group$" #define $GROUP_NUM$_NAME "$group_num$" #define $GROUP_DSET$_NAME "$group_dset$" +#define $GROUP_STR$_NAME "$group_str$" #+end_src ** Template for HDF5 structures @@ -152,7 +153,7 @@ trexio_hdf5_deinit (trexio_t* const file) } #+end_src -** Template for HDF5 has/read/write a number +** Template for HDF5 has/read/write a single dimensioning variable #+begin_src c :tangle hrw_num_hdf5.h :exports none trexio_exit_code trexio_hdf5_has_$group_num$ (trexio_t* const file); @@ -173,11 +174,14 @@ trexio_hdf5_read_$group_num$ (trexio_t* const file, uint64_t* const num) /* Quit if the dimensioning attribute is missing in the file */ if (H5Aexists(f->$group$_group, $GROUP_NUM$_NAME) == 0) return TREXIO_FAILURE; - /* Read the nucleus_num attribute of nucleus group */ + /* Read the $group_num$ attribute of $group$ group */ const hid_t num_id = H5Aopen(f->$group$_group, $GROUP_NUM$_NAME, H5P_DEFAULT); if (num_id <= 0) return TREXIO_INVALID_ID; const herr_t status = H5Aread(num_id, H5T_NATIVE_UINT64, num); + + H5Aclose(num_id); + if (status < 0) return TREXIO_FAILURE; return TREXIO_SUCCESS; @@ -276,18 +280,17 @@ trexio_hdf5_has_$group_num$ (trexio_t* const file) } #+end_src -** Template for HDF5 has/read/write a dataset +** Template for HDF5 has/read/write a dataset of numerical data - #+begin_src c :tangle hrw_dset_hdf5.h :exports none + #+begin_src c :tangle hrw_dset_data_hdf5.h :exports none trexio_exit_code trexio_hdf5_has_$group_dset$(trexio_t* const file); trexio_exit_code trexio_hdf5_read_$group_dset$(trexio_t* const file, $group_dset_dtype$* const $group_dset$, const uint32_t rank, const uint64_t* dims); trexio_exit_code trexio_hdf5_write_$group_dset$(trexio_t* const file, const $group_dset_dtype$* $group_dset$, const uint32_t rank, const uint64_t* dims); #+end_src - #+begin_src c :tangle read_dset_hdf5.c + #+begin_src c :tangle read_dset_data_hdf5.c trexio_exit_code -trexio_hdf5_read_$group_dset$ (trexio_t* const file, $group_dset_dtype$* const $group_dset$, - const uint32_t rank, const uint64_t* dims) +trexio_hdf5_read_$group_dset$ (trexio_t* const file, $group_dset_dtype$* const $group_dset$, const uint32_t rank, const uint64_t* dims) { if (file == NULL) return TREXIO_INVALID_ARG_1; @@ -317,17 +320,17 @@ trexio_hdf5_read_$group_dset$ (trexio_t* const file, $group_dset_dtype$* const $ H5Dclose(dset_id); if (status < 0) { - free(ddims); + FREE(ddims); return TREXIO_FAILURE; } for (uint32_t i=0; i$group$_group, @@ -340,10 +343,9 @@ trexio_hdf5_read_$group_dset$ (trexio_t* const file, $group_dset_dtype$* const $ } #+end_src - #+begin_src c :tangle write_dset_hdf5.c + #+begin_src c :tangle write_dset_data_hdf5.c trexio_exit_code -trexio_hdf5_write_$group_dset$ (trexio_t* const file, const $group_dset_dtype$* $group_dset$, - const uint32_t rank, const uint64_t* dims) +trexio_hdf5_write_$group_dset$ (trexio_t* const file, const $group_dset_dtype$* $group_dset$, const uint32_t rank, const uint64_t* dims) { if (file == NULL) return TREXIO_INVALID_ARG_1; @@ -386,7 +388,7 @@ trexio_hdf5_write_$group_dset$ (trexio_t* const file, const $group_dset_dtype$* } #+end_src - #+begin_src c :tangle has_dset_hdf5.c + #+begin_src c :tangle has_dset_data_hdf5.c trexio_exit_code trexio_hdf5_has_$group_dset$ (trexio_t* const file) { @@ -408,6 +410,336 @@ trexio_hdf5_has_$group_dset$ (trexio_t* const file) } #+end_src +** Template for HDF5 has/read/write a dataset of strings + + #+begin_src c :tangle hrw_dset_str_hdf5.h :exports none +trexio_exit_code trexio_hdf5_has_$group_dset$(trexio_t* const file); +trexio_exit_code trexio_hdf5_read_$group_dset$(trexio_t* const file, char* const $group_dset$, const uint32_t rank, const uint64_t* dims, const uint32_t max_str_len); +trexio_exit_code trexio_hdf5_write_$group_dset$(trexio_t* const file, const char** $group_dset$, const uint32_t rank, const uint64_t* dims); + #+end_src + + #+begin_src c :tangle read_dset_str_hdf5.c +trexio_exit_code +trexio_hdf5_read_$group_dset$ (trexio_t* const file, char* const $group_dset$, const uint32_t rank, const uint64_t* dims, const uint32_t max_str_len) +{ + + if (file == NULL) return TREXIO_INVALID_ARG_1; + if ($group_dset$ == NULL) return TREXIO_INVALID_ARG_2; + + const trexio_hdf5_t* f = (const trexio_hdf5_t*) file; + + herr_t status; + + // open the dataset to get its dimensions + hid_t dset_id = H5Dopen(f->$group$_group, $GROUP_DSET$_NAME, H5P_DEFAULT); + if (dset_id <= 0) return TREXIO_INVALID_ID; + + // allocate space for the dimensions to be read + hsize_t* ddims = CALLOC( (int) rank, hsize_t); + if (ddims == NULL) { + H5Dclose(dset_id); + return TREXIO_ALLOCATION_FAILED; + } + + hid_t dspace = H5Dget_space(dset_id); + if (dset_id <= 0) { + FREE(ddims); + H5Dclose(dset_id); + return TREXIO_INVALID_ID; + } + + // get the rank of the dataset in a file + int rrank = H5Sget_simple_extent_dims(dspace, ddims, NULL); + + if (rrank != (int) rank) { + FREE(ddims); + H5Dclose(dset_id); + H5Sclose(dspace); + return TREXIO_INVALID_ARG_3; + } + + for (int i=0; i$group$_group, $GROUP_DSET$_NAME) != 1 ) { + + /* code to create dataset */ + hid_t filetype = H5Tcopy (H5T_FORTRAN_S1); + status = H5Tset_size (filetype, H5T_VARIABLE); + + hid_t dspace = H5Screate_simple( (int) rank, (const hsize_t*) dims, NULL); + + dset_id = H5Dcreate (f->$group$_group, $GROUP_DSET$_NAME, filetype, dspace, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (dset_id <= 0) return TREXIO_INVALID_ID; + + status = H5Dwrite (dset_id, memtype, + H5S_ALL, H5S_ALL, H5P_DEFAULT, + $group_dset$); + + H5Dclose (dset_id); + H5Sclose (dspace); + H5Tclose (filetype); + H5Tclose (memtype); + + if (status < 0) return TREXIO_FAILURE; + + } else { + + dset_id = H5Dopen(f->$group$_group, $GROUP_DSET$_NAME, H5P_DEFAULT); + if (dset_id <= 0) return TREXIO_INVALID_ID; + + /* code to write dataset */ + status = H5Dwrite(dset_id, memtype, + H5S_ALL, H5S_ALL, H5P_DEFAULT, + $group_dset$); + + H5Dclose(dset_id); + H5Tclose(memtype); + + if (status < 0) return TREXIO_FAILURE; + + } + + return TREXIO_SUCCESS; + +} + #+end_src + + #+begin_src c :tangle has_dset_str_hdf5.c +trexio_exit_code +trexio_hdf5_has_$group_dset$ (trexio_t* const file) +{ + + if (file == NULL) return TREXIO_INVALID_ARG_1; + + trexio_hdf5_t* f = (trexio_hdf5_t*) file; + + herr_t status = H5LTfind_dataset(f->$group$_group, $GROUP_DSET$_NAME); + /* H5LTfind_dataset returns 1 if dataset exists, 0 otherwise */ + if (status == 1){ + return TREXIO_SUCCESS; + } else if (status == 0) { + return TREXIO_HAS_NOT; + } else { + return TREXIO_FAILURE; + } + +} + #+end_src + +** Template for HDF5 has/read/write a single string attribute + + #+begin_src c :tangle hrw_attr_str_hdf5.h :exports none +trexio_exit_code trexio_hdf5_has_$group_str$ (trexio_t* const file); +trexio_exit_code trexio_hdf5_read_$group_str$ (trexio_t* const file, char* const str, const uint32_t max_str_len); +trexio_exit_code trexio_hdf5_write_$group_str$(trexio_t* const file, const char* str); + #+end_src + + + #+begin_src c :tangle read_attr_str_hdf5.c +trexio_exit_code +trexio_hdf5_read_$group_str$ (trexio_t* const file, char* const str, const uint32_t max_str_len) +{ + + if (file == NULL) return TREXIO_INVALID_ARG_1; + if (str == NULL) return TREXIO_INVALID_ARG_2; + + const trexio_hdf5_t* f = (const trexio_hdf5_t*) file; + /* Quit if the string attribute is missing in the file */ + if (H5Aexists(f->$group$_group, $GROUP_STR$_NAME) == 0) return TREXIO_HAS_NOT; + + /* Read the $group_str$ attribute of $group$ group */ + const hid_t str_id = H5Aopen(f->$group$_group, $GROUP_STR$_NAME, H5P_DEFAULT); + if (str_id <= 0) return TREXIO_INVALID_ID; + + const hid_t ftype_id = H5Aget_type(str_id); + if (ftype_id <= 0) return TREXIO_INVALID_ID; + uint64_t sdim = H5Tget_size(ftype_id); + if (sdim <= 0) return TREXIO_FAILURE; + sdim++; /* Make room for null terminator */ + + const hid_t mem_id = H5Tcopy(H5T_C_S1); + if (mem_id <= 0) return TREXIO_INVALID_ID; + + herr_t status; + status = (max_str_len+1) > sdim ? H5Tset_size(mem_id, sdim) : H5Tset_size(mem_id, max_str_len+1) ; + if (status < 0) return TREXIO_FAILURE; + + status = H5Aread(str_id, mem_id, str); + if (status < 0) return TREXIO_FAILURE; + + H5Aclose(str_id); + H5Tclose(mem_id); + H5Tclose(ftype_id); + + return TREXIO_SUCCESS; + +} + #+end_src + + + #+begin_src c :tangle write_attr_str_hdf5.c +trexio_exit_code +trexio_hdf5_write_$group_str$ (trexio_t* const file, const char* str) +{ + + if (file == NULL) return TREXIO_INVALID_ARG_1; + if (str == NULL) return TREXIO_INVALID_ARG_2; + + trexio_hdf5_t* const f = (trexio_hdf5_t*) file; + + + /* Setup the dataspace */ + const hid_t dtype_id = H5Tcopy(H5T_C_S1); + if (dtype_id <= 0) return TREXIO_INVALID_ID; + + size_t str_attr_len = strlen(str) + 1; + + herr_t status; + status = H5Tset_size(dtype_id, str_attr_len); + if (status < 0) return TREXIO_FAILURE; + + status = H5Tset_strpad(dtype_id, H5T_STR_NULLTERM); + if (status < 0) return TREXIO_FAILURE; + + const hid_t dspace_id = H5Screate(H5S_SCALAR); + if (dspace_id <= 0) return TREXIO_INVALID_ID; + + /* Create the $group_str$ attribute of $group$ group */ + const hid_t str_id = H5Acreate(f->$group$_group, $GROUP_STR$_NAME, dtype_id, dspace_id, + H5P_DEFAULT, H5P_DEFAULT); + + if (str_id <= 0) { + H5Sclose(dspace_id); + H5Tclose(dtype_id); + return TREXIO_INVALID_ID; + } + + status = H5Awrite(str_id, dtype_id, str); + if (status < 0) { + H5Aclose(str_id); + H5Sclose(dspace_id); + H5Tclose(dtype_id); + return TREXIO_FAILURE; + } + + H5Aclose(str_id); + H5Sclose(dspace_id); + H5Tclose(dtype_id); + return TREXIO_SUCCESS; + +} + #+end_src + + #+begin_src c :tangle has_attr_str_hdf5.c +trexio_exit_code +trexio_hdf5_has_$group_str$ (trexio_t* const file) +{ + + if (file == NULL) return TREXIO_INVALID_ARG_1; + + const trexio_hdf5_t* f = (const trexio_hdf5_t*) file; + + htri_t status = H5Aexists(f->$group$_group, $GROUP_STR$_NAME); + /* H5Aexists returns positive value if attribute exists, 0 if does not, negative if error */ + if (status > 0){ + return TREXIO_SUCCESS; + } else if (status == 0) { + return TREXIO_HAS_NOT; + } else { + return TREXIO_FAILURE; + } + +} + #+end_src * Constant file suffixes (not used by the generator) :noexport: #+begin_src c :tangle suffix_hdf5.h diff --git a/src/templates_text/build.sh b/src/templates_text/build.sh index af759d9..dba0bfb 100644 --- a/src/templates_text/build.sh +++ b/src/templates_text/build.sh @@ -17,14 +17,22 @@ cat populated/pop_free_group_text.h >> trexio_text.h cat populated/pop_read_group_text.h >> trexio_text.h cat populated/pop_flush_group_text.h >> trexio_text.h -cat populated/pop_has_dset_text.c >> trexio_text.c +cat populated/pop_has_dset_data_text.c >> trexio_text.c +cat populated/pop_has_dset_str_text.c >> trexio_text.c cat populated/pop_has_num_text.c >> trexio_text.c -cat populated/pop_read_dset_text.c >> trexio_text.c +cat populated/pop_has_attr_str_text.c >> trexio_text.c +cat populated/pop_read_dset_data_text.c >> trexio_text.c +cat populated/pop_read_dset_str_text.c >> trexio_text.c +cat populated/pop_read_attr_str_text.c >> trexio_text.c cat populated/pop_read_num_text.c >> trexio_text.c -cat populated/pop_write_dset_text.c >> trexio_text.c +cat populated/pop_write_dset_data_text.c >> trexio_text.c +cat populated/pop_write_dset_str_text.c >> trexio_text.c +cat populated/pop_write_attr_str_text.c >> trexio_text.c cat populated/pop_write_num_text.c >> trexio_text.c cat populated/pop_hrw_num_text.h >> trexio_text.h -cat populated/pop_hrw_dset_text.h >> trexio_text.h +cat populated/pop_hrw_dset_data_text.h >> trexio_text.h +cat populated/pop_hrw_dset_str_text.h >> trexio_text.h +cat populated/pop_hrw_attr_str_text.h >> trexio_text.h cat rdm_text.c >> trexio_text.c cat rdm_text.h >> trexio_text.h diff --git a/src/templates_text/templator_text.org b/src/templates_text/templator_text.org index 7d3dd2c..aee64ba 100644 --- a/src/templates_text/templator_text.org +++ b/src/templates_text/templator_text.org @@ -83,6 +83,8 @@ typedef struct $group$_s { uint32_t rank_$group_dset$; uint32_t to_flush; uint64_t dims_$group_dset$[16]; + uint64_t len_$group_str$; + char* $group_str$; char file_name[TREXIO_MAX_FILENAME_LENGTH]; } $group$_t; #+end_src @@ -298,7 +300,7 @@ trexio_text_read_$group$ (trexio_text_t* const file) /* Read the dimensioning variables */ int rc; - // START REPEAT GROUP_DSET + // START REPEAT GROUP_DSET_ALL rc = fscanf(f, "%1023s", buffer); if ((rc != 1) || (strcmp(buffer, "rank_$group_dset$") != 0)) { FREE(buffer); @@ -319,9 +321,9 @@ trexio_text_read_$group$ (trexio_text_t* const file) uint64_t size_$group_dset$ = 0; if ($group$->rank_$group_dset$ != 0) size_$group_dset$ = 1; - for (unsigned int i=0; i<$group$->rank_$group_dset$; ++i){ + for (uint32_t i=0; i<$group$->rank_$group_dset$; ++i){ - unsigned int j=0; + uint32_t j=0; rc = fscanf(f, "%1023s %u", buffer, &j); if ((rc != 2) || (strcmp(buffer, "dims_$group_dset$") != 0) || (j!=i)) { @@ -342,7 +344,7 @@ trexio_text_read_$group$ (trexio_text_t* const file) size_$group_dset$ *= $group$->dims_$group_dset$[i]; } - // END REPEAT GROUP_DSET + // END REPEAT GROUP_DSET_ALL // START REPEAT GROUP_NUM /* Read data */ @@ -365,13 +367,68 @@ trexio_text_read_$group$ (trexio_text_t* const file) } // END REPEAT GROUP_NUM - // START REPEAT GROUP_DSET + // START REPEAT GROUP_ATTR_STR + rc = fscanf(f, "%1023s", buffer); + assert(!((rc != 1) || (strcmp(buffer, "len_$group_str$") != 0))); + if ((rc != 1) || (strcmp(buffer, "len_$group_str$") != 0)) { + FREE(buffer); + fclose(f); + FREE($group$); + return NULL; + } + + rc = fscanf(f, "%" SCNu64 "", &($group$->len_$group_str$)); + assert(!(rc != 1)); + if (rc != 1) { + FREE(buffer); + fclose(f); + FREE($group$); + return NULL; + } + + rc = fscanf(f, "%1023s", buffer); + assert(!((rc != 1) || (strcmp(buffer, "$group_str$") != 0))); + if ((rc != 1) || (strcmp(buffer, "$group_str$") != 0)) { + FREE(buffer); + fclose(f); + FREE($group$->$group_str$); + FREE($group$); + return NULL; + } + + if ($group$->len_$group_str$ != 0) { + + $group$->$group_str$ = CALLOC($group$->len_$group_str$, char); + assert (!($group$->$group_str$ == NULL)); + if ($group$->$group_str$ == NULL) { + FREE(buffer); + fclose(f); + FREE($group$->$group_str$); + FREE($group$); + return NULL; + } + + rc = fscanf(f, " %1023[^\n]", $group$->$group_str$); + assert(!(rc != 1)); + if (rc != 1) { + FREE(buffer); + fclose(f); + FREE($group$->$group_str$); + FREE($group$); + return NULL; + } + + } + // END REPEAT GROUP_ATTR_STR + + // START REPEAT GROUP_DSET_NUM /* Allocate arrays */ $group$->$group_dset$ = CALLOC(size_$group_dset$, $group_dset_dtype$); assert (!($group$->$group_dset$ == NULL)); if ($group$->$group_dset$ == NULL) { FREE(buffer); fclose(f); + FREE($group$->$group_dset$); FREE($group$); return NULL; } @@ -397,7 +454,57 @@ trexio_text_read_$group$ (trexio_text_t* const file) return NULL; } } - // END REPEAT GROUP_DSET + // END REPEAT GROUP_DSET_NUM + + size_t tmp_len; + + // START REPEAT GROUP_DSET_STR + /* Allocate arrays */ + $group$->$group_dset$ = CALLOC(size_$group_dset$, $group_dset_dtype$); + assert (!($group$->$group_dset$ == NULL)); + if ($group$->$group_dset$ == NULL) { + FREE(buffer); + fclose(f); + FREE($group$->$group_dset$); + FREE($group$); + return NULL; + } + + rc = fscanf(f, "%1023s", buffer); + assert(!((rc != 1) || (strcmp(buffer, "$group_dset$") != 0))); + if ((rc != 1) || (strcmp(buffer, "$group_dset$") != 0)) { + FREE(buffer); + fclose(f); + FREE($group$->$group_dset$); + FREE($group$); + return NULL; + } + + /* WARNING: this tmp array allows to avoid allocation of space for each element of array of string + , BUT it's size has to be number_of_str*max_len_str where max_len_str is somewhat arbitrary, e.g. 32. + ,*/ + char* tmp_$group_dset$; + if(size_$group_dset$ != 0) tmp_$group_dset$ = CALLOC(size_$group_dset$*32, char); + tmp_len = 0; + for (uint64_t i=0 ; i$group_dset$[i] = tmp_$group_dset$; + /* conventional fcanf with "%s" only return the string before the first space character + ,* to read string with spaces use "%[^\n]" possible with space before or after, i.e. " %[^\n]" + ,* Q: depending on what ? */ + rc = fscanf(f, " %1023[^\n]", tmp_$group_dset$); + assert(!(rc != 1)); + if (rc != 1) { + FREE(buffer); + fclose(f); + FREE($group$->$group_dset$); + FREE($group$); + return NULL; + } + + tmp_len = strlen($group$->$group_dset$[i]); + tmp_$group_dset$ += tmp_len + 1; + } + // END REPEAT GROUP_DSET_STR FREE(buffer); fclose(f); @@ -437,7 +544,7 @@ trexio_text_flush_$group$ (trexio_text_t* const file) if (f == NULL) return TREXIO_INVALID_ARG_1; /* Write the dimensioning variables */ - // START REPEAT GROUP_DSET + // START REPEAT GROUP_DSET_ALL fprintf(f, "rank_$group_dset$ %u\n", $group$->rank_$group_dset$); // workaround for the case of missing blocks in the file uint64_t size_$group_dset$ = 0; @@ -447,21 +554,26 @@ trexio_text_flush_$group$ (trexio_text_t* const file) fprintf(f, "dims_$group_dset$ %u %" PRIu64 "\n", i, $group$->dims_$group_dset$[i]); size_$group_dset$ *= $group$->dims_$group_dset$[i]; } - - // END REPEAT GROUP_DSET + // END REPEAT GROUP_DSET_ALL // START REPEAT GROUP_NUM fprintf(f, "$group_num$ %" PRIu64 "\n", $group$->$group_num$); // END REPEAT GROUP_NUM + // START REPEAT GROUP_ATTR_STR + fprintf(f, "len_$group_str$ %" PRIu64 "\n", $group$->len_$group_str$); + fprintf(f, "$group_str$\n"); + if ($group$->len_$group_str$ != 0) fprintf(f, "%s\n", $group$->$group_str$); + // END REPEAT GROUP_ATTR_STR + /* Write arrays */ - // START REPEAT GROUP_DSET + // START REPEAT GROUP_DSET_ALL fprintf(f, "$group_dset$\n"); for (uint64_t i=0 ; i$group_dset$[i]); } - // END REPEAT GROUP_DSET + // END REPEAT GROUP_DSET_ALL fclose(f); $group$->to_flush = 0; @@ -493,11 +605,20 @@ trexio_text_free_$group$ (trexio_text_t* const file) $group$_t* $group$ = file->$group$; if ($group$ == NULL) return TREXIO_SUCCESS; - // START REPEAT GROUP_DSET + // START REPEAT GROUP_DSET_NUM + if ($group$->$group_dset$ != NULL) FREE ($group$->$group_dset$); + // END REPEAT GROUP_DSET_NUM + + // START REPEAT GROUP_DSET_STR if ($group$->$group_dset$ != NULL) { + if($group$->rank_$group_dset$ != 0) FREE ($group$->$group_dset$[0]); FREE ($group$->$group_dset$); } - // END REPEAT GROUP_DSET + // END REPEAT GROUP_DSET_STR + + // START REPEAT GROUP_ATTR_STR + if ($group$->$group_str$ != NULL) FREE ($group$->$group_str$); + // END REPEAT GROUP_ATTR_STR FREE ($group$); return TREXIO_SUCCESS; @@ -505,7 +626,7 @@ trexio_text_free_$group$ (trexio_text_t* const file) } #+end_src -** Template for has/read/write the ~$group_num$~ attribute +** Template for has/read/write the num attribute #+begin_src c :tangle hrw_num_text.h :exports none trexio_exit_code trexio_text_has_$group_num$ (trexio_t* const file); @@ -568,17 +689,17 @@ trexio_text_has_$group_num$ (trexio_t* const file) } #+end_src -** Template for has/read/write the ~$group_dset$~ dataset +** Template for has/read/write the dataset of numerical data The ~group_dset~ array is assumed allocated with the appropriate size. - #+begin_src c :tangle hrw_dset_text.h :exports none + #+begin_src c :tangle hrw_dset_data_text.h :exports none trexio_exit_code trexio_text_has_$group_dset$ (trexio_t* const file); trexio_exit_code trexio_text_read_$group_dset$ (trexio_t* const file, $group_dset_dtype$* const $group_dset$, const uint32_t rank, const uint64_t* dims); trexio_exit_code trexio_text_write_$group_dset$(trexio_t* const file, const $group_dset_dtype$* $group_dset$, const uint32_t rank, const uint64_t* dims); #+end_src - #+begin_src c :tangle read_dset_text.c + #+begin_src c :tangle read_dset_data_text.c trexio_exit_code trexio_text_read_$group_dset$ (trexio_t* const file, $group_dset_dtype$* const $group_dset$, const uint32_t rank, const uint64_t* dims) @@ -593,7 +714,7 @@ trexio_text_read_$group_dset$ (trexio_t* const file, $group_dset_dtype$* const $ if (rank != $group$->rank_$group_dset$) return TREXIO_INVALID_ARG_3; uint64_t dim_size = 1; - for (unsigned int i=0; idims_$group_dset$[i]) return TREXIO_INVALID_ARG_4; dim_size *= dims[i]; } @@ -607,7 +728,7 @@ trexio_text_read_$group_dset$ (trexio_t* const file, $group_dset_dtype$* const $ } #+end_src - #+begin_src c :tangle write_dset_text.c + #+begin_src c :tangle write_dset_data_text.c trexio_exit_code trexio_text_write_$group_dset$ (trexio_t* const file, const $group_dset_dtype$* $group_dset$, const uint32_t rank, const uint64_t* dims) @@ -628,7 +749,7 @@ trexio_text_write_$group_dset$ (trexio_t* const file, const $group_dset_dtype$* $group$->rank_$group_dset$ = rank; uint64_t dim_size = 1; - for (unsigned int i=0; i<$group$->rank_$group_dset$; ++i){ + for (uint32_t i=0; i<$group$->rank_$group_dset$; ++i){ $group$->dims_$group_dset$[i] = dims[i]; dim_size *= dims[i]; } @@ -645,7 +766,7 @@ trexio_text_write_$group_dset$ (trexio_t* const file, const $group_dset_dtype$* } #+end_src - #+begin_src c :tangle has_dset_text.c + #+begin_src c :tangle has_dset_data_text.c trexio_exit_code trexio_text_has_$group_dset$ (trexio_t* const file) { @@ -663,7 +784,182 @@ trexio_text_has_$group_dset$ (trexio_t* const file) } #+end_src +** Template for has/read/write the dataset of strings + The ~group_dset~ array is assumed allocated with the appropriate size. + + #+begin_src c :tangle hrw_dset_str_text.h :exports none +trexio_exit_code trexio_text_has_$group_dset$ (trexio_t* const file); +trexio_exit_code trexio_text_read_$group_dset$ (trexio_t* const file, char* const dset, const uint32_t rank, const uint64_t* dims, const uint32_t max_str_len); +trexio_exit_code trexio_text_write_$group_dset$ (trexio_t* const file, const char** dset, const uint32_t rank, const uint64_t* dims); + #+end_src + + #+begin_src c :tangle read_dset_str_text.c +trexio_exit_code +trexio_text_read_$group_dset$ (trexio_t* const file, char* const dset, const uint32_t rank, const uint64_t* dims, const uint32_t max_str_len) +{ + + if (file == NULL) return TREXIO_INVALID_ARG_1; + if (dset == NULL) return TREXIO_INVALID_ARG_2; + + $group$_t* const $group$ = trexio_text_read_$group$((trexio_text_t*) file); + if ($group$ == NULL) return TREXIO_FAILURE; + + if (rank != $group$->rank_$group_dset$) return TREXIO_INVALID_ARG_3; + + for (uint32_t i=0 ; idims_$group_dset$[i]) return TREXIO_INVALID_ARG_4; + } + + strcpy(dset, ""); + for (uint64_t i=0 ; i$group_dset$[i], max_str_len); + strcat(dset, TREXIO_DELIM); + } + + return TREXIO_SUCCESS; + +} + #+end_src + + #+begin_src c :tangle write_dset_str_text.c +trexio_exit_code +trexio_text_write_$group_dset$ (trexio_t* const file, const char** dset, const uint32_t rank, const uint64_t* dims) +{ + + if (file == NULL) return TREXIO_INVALID_ARG_1; + if (dset == NULL) return TREXIO_INVALID_ARG_2; + + if (file->mode == 'r') return TREXIO_READONLY; + + $group$_t* const $group$ = trexio_text_read_$group$((trexio_text_t*) file); + if ($group$ == NULL) return TREXIO_FAILURE; + + if ($group$->$group_dset$ != NULL) { + if ($group$->rank_$group_dset$ != 0) FREE($group$->$group_dset$[0]); + FREE($group$->$group_dset$); + } + + $group$->rank_$group_dset$ = rank; + + for (uint32_t i=0; i<$group$->rank_$group_dset$; ++i){ + $group$->dims_$group_dset$[i] = dims[i]; + } + + $group$->$group_dset$ = CALLOC(dims[0], char*); + if ($group$->$group_dset$ == NULL) return TREXIO_ALLOCATION_FAILED; + + char* tmp_str = CALLOC(dims[0]*32 + 1, char); + if (tmp_str == NULL) return TREXIO_ALLOCATION_FAILED; + + size_t tmp_len = 0; + for (uint64_t i=0 ; i$group_dset$[i] = tmp_str; + strncpy(tmp_str, dset[i], tmp_len); + tmp_str += tmp_len + 1; + } + + $group$->to_flush = 1; + + return TREXIO_SUCCESS; + +} + #+end_src + + #+begin_src c :tangle has_dset_str_text.c +trexio_exit_code +trexio_text_has_$group_dset$ (trexio_t* const file) +{ + + if (file == NULL) return TREXIO_INVALID_ARG_1; + + $group$_t* const $group$ = trexio_text_read_$group$((trexio_text_t*) file); + if ($group$ == NULL) return TREXIO_FAILURE; + + if ($group$->rank_$group_dset$ > 0){ + return TREXIO_SUCCESS; + } else { + return TREXIO_HAS_NOT; + } + +} + #+end_src +** Template for has/read/write the string attribute + + #+begin_src c :tangle hrw_attr_str_text.h :exports none +trexio_exit_code trexio_text_has_$group_str$ (trexio_t* const file); +trexio_exit_code trexio_text_read_$group_str$ (trexio_t* const file, char* const str, const uint32_t max_str_len); +trexio_exit_code trexio_text_write_$group_str$ (trexio_t* const file, const char* str); + #+end_src + + #+begin_src c :tangle read_attr_str_text.c +trexio_exit_code +trexio_text_read_$group_str$ (trexio_t* const file, char* const str, const uint32_t max_str_len) +{ + + if (file == NULL) return TREXIO_INVALID_ARG_1; + if (str == NULL) return TREXIO_INVALID_ARG_2; + + $group$_t* const $group$ = trexio_text_read_$group$((trexio_text_t*) file); + if ($group$ == NULL) return TREXIO_FAILURE; + + strncpy(str, $group$->$group_str$, max_str_len); + + return TREXIO_SUCCESS; + +} + #+end_src + + #+begin_src c :tangle write_attr_str_text.c +trexio_exit_code +trexio_text_write_$group_str$ (trexio_t* const file, const char *str) +{ + + if (file == NULL) return TREXIO_INVALID_ARG_1; + if (str == NULL) return TREXIO_INVALID_ARG_2; + + if (file->mode == 'r') return TREXIO_READONLY; + + $group$_t* const $group$ = trexio_text_read_$group$((trexio_text_t*) file); + if ($group$ == NULL) return TREXIO_FAILURE; + + if ($group$->$group_str$ != NULL) FREE($group$->$group_str$); + + size_t tmp_len = strlen(str); + + $group$->$group_str$ = CALLOC(tmp_len + 1, char); + if ($group$->$group_str$ == NULL) return TREXIO_ALLOCATION_FAILED; + + $group$->len_$group_str$ = tmp_len + 1; + + strncpy($group$->$group_str$, str, tmp_len + 1); + + $group$->to_flush = 1; + + return TREXIO_SUCCESS; + +} + #+end_src + + #+begin_src c :tangle has_attr_str_text.c +trexio_exit_code +trexio_text_has_$group_str$ (trexio_t* const file) +{ + + if (file == NULL) return TREXIO_INVALID_ARG_1; + + $group$_t* const $group$ = trexio_text_read_$group$((trexio_text_t*) file); + if ($group$ == NULL) return TREXIO_FAILURE; + + if ($group$->len_$group_str$ > 0){ + return TREXIO_SUCCESS; + } else { + return TREXIO_HAS_NOT; + } + +} + #+end_src ** RDM struct (hard-coded) *** Read the complete struct diff --git a/tests/.gitignore b/tests/.gitignore index 3bb9e8a..9a43619 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -6,6 +6,9 @@ trexio.mod test_c test_f +*.log +*.trs + *.h5 */ diff --git a/tests/test.c b/tests/test.c index 9b038d6..92d90dd 100644 --- a/tests/test.c +++ b/tests/test.c @@ -2,6 +2,7 @@ #include #include #include +#include int test_write(const char* file_name, const back_end_t backend); int test_read(const char* file_name, const back_end_t backend); @@ -54,6 +55,20 @@ int test_write(const char* file_name, const back_end_t backend) { 0.00000000 , 2.47304151 , 0.00000000 , }; + const char* label[] = {"C" , + "Na", + "ClH asdasdasdas" , + "C" , + "C 666" , + "C" , + "H" , + "Ru", + "H" , + "H 999asdasd" , + "H" , + "H" }; + + const char* sym = "B3U and some stuff"; /*================= START OF TEST ==================*/ // open file in 'write' mode @@ -72,11 +87,28 @@ int test_write(const char* file_name, const back_end_t backend) { rc = trexio_write_nucleus_coord(file,coord); assert (rc == TREXIO_SUCCESS); + rc = trexio_write_nucleus_label(file, label, 32); + assert (rc == TREXIO_SUCCESS); + rc = trexio_write_nucleus_point_group(file, sym, 32); + assert (rc == TREXIO_SUCCESS); + + // close current session + rc = trexio_close(file); + assert (rc == TREXIO_SUCCESS); + + // reopen file in 'write' mode + file = trexio_open(file_name, 'w', backend); + assert (file != NULL); + // check if the written data exists in the file rc = trexio_has_nucleus_num(file); assert (rc == TREXIO_SUCCESS); rc = trexio_has_nucleus_coord(file); assert (rc == TREXIO_SUCCESS); + rc = trexio_has_nucleus_label(file); + assert (rc == TREXIO_SUCCESS); + rc = trexio_has_nucleus_point_group(file); + assert (rc == TREXIO_SUCCESS); // should not work: try to overwrite the num variable rc = trexio_write_nucleus_num(file,25); @@ -118,6 +150,8 @@ int test_read(const char* file_name, const back_end_t backend) { int num; double* coord; + char** label; + char* point_group; /*================= START OF TEST ==================*/ @@ -137,6 +171,37 @@ int test_read(const char* file_name, const back_end_t backend) { double x = coord[30] - 2.14171677; assert( x*x < 1.e-14); + free(coord); + + // read nucleus_label + label = (char**) malloc(num*sizeof(char*)); + for (int i=0; i open with 'w' -! hdf5 backend -> open with 'a' -! ---------------------------------- ! - -!! trex_file = trexio_open('trexio_test_fort', 'w', TREXIO_TEXT); -!! trex_file = trexio_open('test_hdf5_fort.h5', 'a', TREXIO_HDF5) - -! coord(1) = 666.666 - -! rc = trexio_write_nucleus_coord(trex_file,coord) -! if (rc == TREXIO_SUCCESS) write(*,*) 'SUCCESS MODIFY COORD' - -! rc = trexio_close(trex_file) -! if (rc == TREXIO_SUCCESS) write(*,*) 'SUCCESS CLOSE' + call trexio_assert(rc, TREXIO_SUCCESS, 'SUCCESS CLOSE') ! ================= END OF TEST ===================== ! end subroutine test_write - - subroutine test_read(file_name, back_end) ! ============ Test read functionality =============== ! @@ -177,10 +120,11 @@ subroutine test_read(file_name, back_end) implicit none character*(*), intent(in) :: file_name - integer(trexio_backend), intent(in) :: back_end + integer, intent(in) :: back_end integer(8) :: trex_file + integer :: i, j, k, ind, offset, flag integer :: rc = 1 integer :: num, num_read @@ -188,6 +132,12 @@ subroutine test_read(file_name, back_end) double precision :: charge(12) double precision :: coord(3,12) + character :: label_str(128) + character(len=4) :: tmp_str + character(len=4) :: label(12) ! also works with allocatable arrays + + character(len=32) :: sym_str + character*(128) :: str num = 12 @@ -196,56 +146,69 @@ subroutine test_read(file_name, back_end) trex_file = trexio_open(file_name, 'r', back_end) - rc = trexio_read_nucleus_num(trex_file, num_read) - if (rc == TREXIO_SUCCESS .and. num_read == num) then + rc = trexio_read_nucleus_num(trex_file, num_read) + call trexio_assert(rc, TREXIO_SUCCESS) + if (num_read == num) then write(*,*) 'SUCCESS READ NUM' else - call trexio_string_of_error(rc,str) - print *, trim(str) - call exit(1) + print *, 'FAILURE NUM CHECK' + call exit(-1) endif rc = trexio_read_nucleus_charge(trex_file, charge) - - if (rc == TREXIO_SUCCESS .and. (dabs(charge(11) - 1.d0) < 1.0D-8) ) then + call trexio_assert(rc, TREXIO_SUCCESS) + if (dabs(charge(11) - 1.d0) < 1.0D-8) then write(*,*) 'SUCCESS READ CHARGE' else - call trexio_string_of_error(rc,str) - print *, trim(str) + print *, 'FAILURE CHARGE CHECK' call exit(-1) endif + rc = trexio_read_nucleus_coord(trex_file, coord) - - if (rc == TREXIO_SUCCESS .and. (dabs(coord(2,1) - 1.39250319d0) < 1.0D-8) ) then + call trexio_assert(rc, TREXIO_SUCCESS) + if (dabs(coord(2,1) - 1.39250319d0) < 1.0D-8) then write(*,*) 'SUCCESS READ COORD' else - call trexio_string_of_error(rc,str) - print *, trim(str) + print *, 'FAILURE COORD CHECK' call exit(-1) endif + + rc = trexio_read_nucleus_label(trex_file, label, 2) + call trexio_assert(rc, TREXIO_SUCCESS) + if (trim(label(2)) == 'Na') then + write(*,*) 'SUCCESS READ LABEL' + else + print *, 'FAILURE LABEL CHECK' + call exit(-1) + endif + + rc = trexio_read_basis_nucleus_index(trex_file, basis_nucleus_index) - if (rc == TREXIO_SUCCESS .and. (basis_nucleus_index(12) == 12) ) then + call trexio_assert(rc, TREXIO_SUCCESS) + if (basis_nucleus_index(12) == 12) then write(*,*) 'SUCCESS READ INDEX' else - call trexio_string_of_error(rc,str) - print *, trim(str) + print *, 'FAILURE INDEX CHECK' + call exit(-1) + endif + + + rc = trexio_read_nucleus_point_group(trex_file, sym_str, 10) + call trexio_assert(rc, TREXIO_SUCCESS) + if (sym_str(1:3) == 'B3U') then + write(*,*) 'SUCCESS READ POINT GROUP' + else + print *, 'FAILURE POINT GROUP CHECK' call exit(-1) endif rc = trexio_close(trex_file) - if (rc == TREXIO_SUCCESS) then - write(*,*) 'SUCCESS CLOSE' - else - call trexio_string_of_error(rc,str) - print *, trim(str) - call exit(1) - endif - + call trexio_assert(rc, TREXIO_SUCCESS) ! ================= END OF TEST ===================== ! diff --git a/tools/generator.py b/tools/generator.py index 6a74116..58c4d9a 100644 --- a/tools/generator.py +++ b/tools/generator.py @@ -18,12 +18,15 @@ print('Strings I/O currently not supported') # -------------------- GET ATTRIBUTES FROM THE CONFIGURATION ---------------- # group_dict = get_group_dict(trex_config) -detailed_num = get_detailed_num_dict(trex_config) -# helper dictionaries contain group, num or dset names as keys -datasets = get_dset_dict(trex_config) -detailed_dset_nostr, detailed_dset_str = split_dset_dict_detailed(datasets) +detailed_nums = get_detailed_num_dict(trex_config) +detailed_strs = get_detailed_str_dict(trex_config) +# helper dictionaries that contain names of groups, nums or dsets as keys +dsets = get_dset_dict(trex_config) +detailed_dsets_nostr, detailed_dsets_str = split_dset_dict_detailed(dsets) +detailed_dsets = detailed_dsets_nostr.copy() +detailed_dsets.update(detailed_dsets_str) # consistency check for dimensioning variables -check_dim_consistency(detailed_num, datasets) +check_dim_consistency(detailed_nums, dsets) # --------------------------------------------------------------------------- # # -------------------- GET TEMPLATED FILES TO BE POPULATED ------------------ # @@ -40,18 +43,26 @@ files_todo = get_files_todo(source_files) # populate files with iterative scheme, i.e. for unique functions for fname in files_todo['auxiliary']: - iterative_populate_file(fname, template_paths, group_dict, detailed_dset_nostr, detailed_num) + iterative_populate_file(fname, template_paths, group_dict, detailed_dsets, detailed_nums, detailed_strs) # populate has/read/write_num functions with recursive scheme for fname in files_todo['num']: - recursive_populate_file(fname, template_paths, detailed_num) + recursive_populate_file(fname, template_paths, detailed_nums) -# populate has/read/write_dset functions with recursive scheme -for fname in files_todo['dset']: - recursive_populate_file(fname, template_paths, detailed_dset_nostr) +# populate has/read/write_str functions with recursive scheme +for fname in files_todo['attr_str']: + recursive_populate_file(fname, template_paths, detailed_strs) + +# populate has/read/write_dset (numerical) functions with recursive scheme +for fname in files_todo['dset_data']: + recursive_populate_file(fname, template_paths, detailed_dsets_nostr) + +# populate has/read/write_dset (strings) functions with recursive scheme +for fname in files_todo['dset_str']: + recursive_populate_file(fname, template_paths, detailed_dsets_str) # populate group-related functions with mixed (iterative+recursive) scheme [text backend] for fname in files_todo['group']: - special_populate_text_group(fname, template_paths, group_dict, detailed_dset_nostr, detailed_num) + special_populate_text_group(fname, template_paths, group_dict, detailed_dsets, detailed_nums, detailed_strs) # --------------------------------------------------------------------------- # diff --git a/tools/generator_tools.py b/tools/generator_tools.py index d9a496e..988a5b1 100644 --- a/tools/generator_tools.py +++ b/tools/generator_tools.py @@ -39,11 +39,11 @@ def get_files_todo(source_files: dict) -> dict: files_todo = {} #files_todo['all'] = list(filter(lambda x: 'read' in x or 'write' in x or 'has' in x or 'hrw' in x or 'flush' in x or 'free' in x, all_files)) files_todo['all'] = [f for f in all_files if 'read' in f or 'write' in f or 'has' in f or 'flush' in f or 'free' in f or 'hrw' in f] - for key in ['dset', 'num', 'group']: + for key in ['dset_data', 'dset_str', 'num', 'attr_str', 'group']: files_todo[key] = list(filter(lambda x: key in x, files_todo['all'])) files_todo['group'].append('struct_text_group_dset.h') - # files that correspond to todo1 group (e.g. only iterative population within the function body) + # files that correspond to iterative population (e.g. the code is repeated within the function body but the function itself is unique) files_todo['auxiliary'] = ['def_hdf5.c', 'basic_hdf5.c', 'basic_text_group.c', 'struct_hdf5.h', 'struct_text_group.h'] return files_todo @@ -104,7 +104,7 @@ def recursive_populate_file(fname: str, paths: dict, detailed_source: dict) -> N 'group_dset_f_dtype_default', 'group_dset_f_dtype_double', 'group_dset_f_dtype_single', 'group_dset_dtype_default', 'group_dset_dtype_double', 'group_dset_dtype_single', 'group_dset_rank', 'group_dset_dim_list', 'group_dset_f_dims', - 'group_dset', 'group_num', 'group'] + 'group_dset', 'group_num', 'group_str', 'group'] for item in detailed_source.keys(): with open(join(templ_path,fname), 'r') as f_in : @@ -119,7 +119,7 @@ def recursive_populate_file(fname: str, paths: dict, detailed_source: dict) -> N if not dim.isdigit() and not dim in num_written: num_written.append(dim) templine = line.replace('$group_dset_dim$', dim) - if '_read' in templine: + if '_read' in templine and (not 'fortran' in fname): line_toadd = indentlevel*" " + rc_line templine += line_toadd @@ -168,7 +168,7 @@ def recursive_replace_line (input_line: str, triggers: list, source: dict) -> st return output_line -def iterative_populate_file (filename: str, paths: dict, groups: dict, datasets: dict, numbers: dict) -> None: +def iterative_populate_file (filename: str, paths: dict, groups: dict, datasets: dict, numbers: dict, strings: dict) -> None: """ Iteratively populate files with unique functions that contain templated variables. @@ -178,12 +178,13 @@ def iterative_populate_file (filename: str, paths: dict, groups: dict, datasets: groups (dict) : dictionary of groups datasets (dict) : dictionary of datasets with substitution details numbers (dict) : dictionary of numbers with substitution details + strings (dict) : dictionary of strings with substitution details Returns: None """ add_trigger = 'rc = trexio_text_free_$group$' - triggers = [add_trigger, '$group_dset$', '$group_num$', '$group$'] + triggers = [add_trigger, '$group_dset$', '$group_num$', '$group_str$', '$group$'] templ_path = get_template_path(filename, paths) filename_out = join('populated',f'pop_{filename}') @@ -197,7 +198,7 @@ def iterative_populate_file (filename: str, paths: dict, groups: dict, datasets: if id == 0: # special case for proper error handling when deallocting text groups error_handler = ' if (rc != TREXIO_SUCCESS) return rc;\n' - populated_line = iterative_replace_line(line, triggers[3], groups, add_line=error_handler) + populated_line = iterative_replace_line(line, '$group$', groups, add_line=error_handler) f_out.write(populated_line) elif id == 1: populated_line = iterative_replace_line(line, triggers[id], datasets, None) @@ -206,6 +207,9 @@ def iterative_populate_file (filename: str, paths: dict, groups: dict, datasets: populated_line = iterative_replace_line(line, triggers[id], numbers, None) f_out.write(populated_line) elif id == 3: + populated_line = iterative_replace_line(line, triggers[id], strings, None) + f_out.write(populated_line) + elif id == 4: populated_line = iterative_replace_line(line, triggers[id], groups, None) f_out.write(populated_line) else: @@ -257,7 +261,7 @@ def check_triggers (input_line: str, triggers: list) -> int: return out_id -def special_populate_text_group(fname: str, paths: dict, group_dict: dict, detailed_dset: dict, detailed_numbers: dict) -> None: +def special_populate_text_group(fname: str, paths: dict, group_dict: dict, detailed_dset: dict, detailed_numbers: dict, detailed_strings: dict) -> None: """ Special population for group-related functions in the TEXT back end. @@ -267,6 +271,7 @@ def special_populate_text_group(fname: str, paths: dict, group_dict: dict, detai group_dict (dict) : dictionary of groups detailed_dset (dict) : dictionary of datasets with substitution details detailed_numbers (dict) : dictionary of numbers with substitution details + detailed_strings (dict) : dictionary of string attributes with substitution details Returns: None @@ -275,7 +280,7 @@ def special_populate_text_group(fname: str, paths: dict, group_dict: dict, detai templ_path = get_template_path(fname, paths) triggers = ['group_dset_dtype', 'group_dset_std_dtype_out', 'group_dset_std_dtype_in', - 'group_dset', 'group_num', 'group'] + 'group_dset', 'group_num', 'group_str', 'group'] for group in group_dict.keys(): @@ -286,13 +291,15 @@ def special_populate_text_group(fname: str, paths: dict, group_dict: dict, detai subloop_num = False loop_body = '' dset_allocated = [] + str_allocated = [] for line in f_in : if 'START REPEAT GROUP_DSET' in line: subloop_dset = True continue - elif 'START REPEAT GROUP_NUM' in line: + # this can be merged in one later using something like START REPEAT GROUP_ATTR in line + elif 'START REPEAT GROUP_NUM' in line or 'START REPEAT GROUP_ATTR_STR' in line: subloop_num = True continue @@ -302,6 +309,11 @@ def special_populate_text_group(fname: str, paths: dict, group_dict: dict, detai if group != detailed_dset[dset]['group']: continue + if ('REPEAT GROUP_DSET_STR' in line) and (detailed_dset[dset]['dtype'] != 'char*'): + continue + if ('REPEAT GROUP_DSET_NUM' in line) and (detailed_dset[dset]['dtype'] == 'char*'): + continue + dset_allocated.append(dset) if 'FREE($group$->$group_dset$)' in loop_body: @@ -309,7 +321,7 @@ def special_populate_text_group(fname: str, paths: dict, group_dict: dict, detai for dset_alloc in dset_allocated: tmp_string += f'FREE({group}->{dset_alloc});\n ' - tmp_body = loop_body.replace('FREE($group$->$group_dset$);',tmp_string) + tmp_body = loop_body.replace('FREE($group$->$group_dset$);', tmp_string) populated_body = recursive_replace_line(tmp_body, triggers, detailed_dset[dset]) f_out.write(populated_body) @@ -336,6 +348,33 @@ def special_populate_text_group(fname: str, paths: dict, group_dict: dict, detai loop_body = '' continue + elif 'END REPEAT GROUP_ATTR_STR' in line: + for str in detailed_strings.keys(): + if group != detailed_strings[str]['group']: + continue + + str_allocated.append(str) + + if 'FREE($group$->$group_str$)' in loop_body: + tmp_string = '' + for str_alloc in str_allocated: + tmp_string += f'FREE({group}->{str_alloc});\n ' + + tmp_body = loop_body.replace('FREE($group$->$group_str$);', tmp_string) + + populated_body = recursive_replace_line(tmp_body, triggers, detailed_strings[str]) + f_out.write(populated_body) + else: + save_body = loop_body + populated_body = recursive_replace_line(save_body, triggers, detailed_strings[str]) + f_out.write(populated_body) + + subloop_num = False + loop_body = '' + str_allocated = [] + + continue + if not subloop_num and not subloop_dset: # NORMAL CASE WITHOUT SUBLOOPS if '$group_dset' in line: @@ -344,6 +383,12 @@ def special_populate_text_group(fname: str, paths: dict, group_dict: dict, detai continue populated_line = recursive_replace_line(line, triggers, detailed_dset[dset]) f_out.write(populated_line) + elif '$group_str' in line: + for str in detailed_strings.keys(): + if group != detailed_strings[str]['group']: + continue + populated_line = recursive_replace_line(line, triggers, detailed_strings[str]) + f_out.write(populated_line) elif '$group_num$' in line: for dim in detailed_numbers.keys(): if group != detailed_numbers[dim]['group']: @@ -420,6 +465,31 @@ def get_detailed_num_dict (configuration: dict) -> dict: return num_dict +def get_detailed_str_dict (configuration: dict) -> dict: + """ + Returns the dictionary of all `str`-like attributes. + Keys are names, values are subdictionaries containing corresponding group and group_str names. + + Parameters: + configuration (dict) : configuration from `trex.json` + + Returns: + str_dict (dict) : dictionary of string attributes + """ + str_dict = {} + for k1,v1 in configuration.items(): + for k2,v2 in v1.items(): + if len(v2[1]) == 0: + tmp_str = f'{k1}_{k2}' + if 'str' in v2[0]: + tmp_dict = {} + tmp_dict['group'] = k1 + tmp_dict['group_str'] = tmp_str + str_dict[tmp_str] = tmp_dict + + return str_dict + + def get_dset_dict (configuration: dict) -> dict: """ Returns the dictionary of datasets. @@ -485,8 +555,12 @@ def split_dset_dict_detailed (datasets: dict) -> tuple: group_dset_std_dtype_out = '" PRId64 "' group_dset_std_dtype_in = '" SCNd64 "' elif v[0] == 'str': - # TODO - datatype = 'string' + datatype = 'char*' + group_dset_h5_dtype = 'c_s1' + group_dset_f_dtype_default = 'character(len=*)' + group_dset_dtype_default = 'char*' + group_dset_std_dtype_out = 's' + group_dset_std_dtype_in = 's' # add the dset name for templates tmp_dict['group_dset'] = k @@ -532,7 +606,7 @@ def split_dset_dict_detailed (datasets: dict) -> tuple: tmp_dict['group'] = v[2] # split datasets in numeric- and string- based - if (datatype == 'string'): + if (datatype == 'char*'): dset_string_dict[k] = tmp_dict else: dset_numeric_dict[k] = tmp_dict