diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..16bf723 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,52 @@ +COMPILER=GNU +#COMPILER=INTEL +#COMPILER=LLVM + +ifeq ($(COMPILER),GNU) +CC=gcc -g +CFLAGS=-fPIC -fexceptions -Wall -Werror -Wpedantic -Wextra + +LIBS= #-lgfortran -lm +endif + +ifeq ($(COMPILER),INTEL) +CC=icc -xHost +CFLAGS=-fPIC -g -O2 + +FC=ifort -xHost +FFLAGS=-fPIC -g -O2 + +LIBS= #-lm +endif + +#TODO +ifeq ($(COMPILER),LLVM) +CC=clang +CFLAGS=-fPIC -g -O2 + +FC=flang +FFLAGS=fPIC -g -O2 + +LIBS=-lm +endif + + +OBJECT_FILES= trio.o trio_text.o +HEADER_FILES= trio.h trio_text.h trio_s.h + +export CC CFLAGS LIBS + +.PHONY: clean + +libtrio.so: $(OBJECT_FILES) $(HEADER_FILES) + $(CC) -shared $(OBJECT_FILES) -o libtrio.so + +test: libtrio.so test.c + $(CC) $(CFLAGS) -Wl,-rpath,$(PWD) -L. test.c -ltrio $(LIBS) -o test + +clean: + rm -f *.o libtrio.so + +%.o: %.c $(HEADER_FILES) + $(CC) $(CFLAGS) -c $*.c -o $*.o + diff --git a/src/trio.org b/src/trio.org new file mode 100644 index 0000000..4c86f65 --- /dev/null +++ b/src/trio.org @@ -0,0 +1,684 @@ +#+TITLE: TREX Input/Ouput library (TRIO) + +* File prefixes :noxport: + + #+begin_src c :tangle trio.h +#ifndef _TRIO_H +#define _TRIO_H + +#include + + #+end_src + + #+begin_src c :tangle trio.c +#include +#include +#include +#include +#include + +#include "trio.h" +#include "trio_s.h" +#include "trio_text.h" +/* +#include "trio_hdf5.h" +#include "trio_json.h" +,*/ + + #+end_src + + + #+begin_src c :tangle trio_s.h +#ifndef _TRIO_S_H +#define _TRIO_S_H + +#include "trio.h" +#include +#include + #+end_src + + #+begin_src c :tangle trio_text.h +#ifndef _TRIO_TEXT_H +#define _TRIO_TEXT_H + +#include "trio.h" +#include "trio_s.h" +#include +#include +#include +#include +#include + + #+end_src + + #+begin_src c :tangle trio_text.c +#include "trio_text.h" + #+end_src + +* Coding conventions + + - integer types will be defined using types given in ~stdint.h~ + - pointers are always initialized to ~NULL~ + - when memory is freed, the pointer is set to ~NULL~ + - ~assert.h~ should be used extensively + - variable names are in lower case + - ~#define~ constants are in upper case + - structs are suffixed by ~_s~ + - types are suffixed by ~_t~ + +* Front end + + All calls to TRIO are thread-safe. + +** Error handling + #+begin_src c :tangle trio.h +typedef int32_t trio_exit_code; + +#define TRIO_SUCCESS ( (trio_exit_code) 0 ) +#define TRIO_FAILURE ( (trio_exit_code) 1 ) + #+end_src + + +** Back ends + + #+begin_src c :tangle trio.h +typedef uint32_t back_end_t; + +#define TRIO_HDF5 ( (back_end_t) 0 ) +#define TRIO_TEXT ( (back_end_t) 1 ) +#define TRIO_JSON ( (back_end_t) 2 ) +#define TRIO_INVALID_BACK_END ( (back_end_t) 3 ) + #+end_src + +** Read/write behavior + + Every time a reading function is called, the data is read from the + disk. If data needs to be cached, this is left to the user of the + library. + + Writing to TRIO files is done with transactions (all-or-nothing + effect) in a per-group fashion. File writes are attempted by + calling explicitly the flush function, or when the TRIO file is + closed. If writing is impossible because the data is not valid, no + data is written. + + The order in which the data is written is not necessarily consistent + with the order in which the function calls were made. + + The TRIO files are supposed to be opened by only one program at a + time: if the same TRIO file is modified simultaneously by multiple + concurrent programs, the behavior is not specified. + +** TRIO file type + + ~trio_s~ is the the main type for TRIO files, visible to the users + of the library. This type is kept opaque, and all modifications to + the files will be necessarily done through the use of functions, + taking such a type as argument. + + File creation and opening functions will return /TRIO file handles/, + namely pointers to ~trio_s~ types. All functions accessing to the + TRIO files will have as a first argument the TRIO file handle. + + #+begin_src c :tangle trio.h +typedef struct trio_s trio_t; + #+end_src + + #+begin_src c :tangle trio_s.h +struct trio_s { + char* file_name; + pthread_mutex_t thread_lock; + back_end_t back_end; + char mode; + char padding[7]; /* Ensures the proper alignment of back-ends */ +}; + #+end_src + +** Polymorphism of the file handle + + Polymorphism of the ~trio_t~ type is handled by ensuring that the + corresponding types for all back ends can be safely casted to + ~trio_t~. This is done by making the back end structs start with + ~struct trio_s~: + + #+begin_src c +struct trio_back_end_s { + trio_t parent ; + /* add below specific back end data */ +} + #+end_src + +** File creation and opening + + #+begin_src c :tangle trio.h +trio_t* trio_create(const char* file_name, back_end_t back_end); + #+end_src + + + #+begin_src c :tangle trio.c +trio_t* trio_create(const char* file_name, back_end_t back_end) { + + /* Check that file name is not NULL or empty */ + assert (file_name != NULL); + assert (file_name[0] != '\0'); + + /* Check that back_end is valid */ + assert (back_end < TRIO_INVALID_BACK_END); + + trio_t* result = NULL; + + switch (back_end) { + + case TRIO_TEXT: + result = (trio_t*) malloc (sizeof(trio_text_t)); + break; +/* + case TRIO_HDF5: + result = (trio_t*) malloc (sizeof(trio_hdf5_t)); + break; + + case TRIO_JSON: + result = (trio_t*) malloc (sizeof(trio_json_t)); + break; +,*/ + default: + assert (1 == 0); /* Impossible case */ + } + + /* TODO: Error handling */ + assert (result != NULL); + + result->file_name = (char*) calloc(strlen(file_name)+1,sizeof(char)); + strcpy(result->file_name, file_name); + result->back_end = back_end; + result->mode = 'w'; /* Upon creation, mode=write */ + pthread_mutex_init ( &(result->thread_lock), NULL); + + trio_exit_code rc = TRIO_FAILURE; + + switch (back_end) { + + case TRIO_TEXT: + rc = trio_text_init(result); + break; +/* + case TRIO_HDF5: + rc = trio_hdf5_init(result); + break; + + case TRIO_JSON: + rc = trio_json_init(result); + break; +*/ + default: + assert (1 == 0); /* Impossible case */ + } + assert (rc == TRIO_SUCCESS); + + return result; +} + #+end_src + +** Reading/writing data + + #+begin_src c :tangle trio.h +trio_exit_code trio_read_nucleus_num(trio_t* file, int64_t* num); + #+end_src + + #+begin_src c :tangle trio.c +trio_exit_code trio_read_nucleus_num(trio_t* file, int64_t* num) { + if (file == NULL) return TRIO_FAILURE; + + switch (file->back_end) { + + case TRIO_TEXT: + return trio_text_read_nucleus_num(file, num); + break; +/* + case TRIO_HDF5: + return trio_hdf5_read_nucleus_num(file, num); + break; + + case TRIO_JSON: + return trio_json_read_nucleus_num(file, num); + break; +*/ + default: + return TRIO_FAILURE; /* Impossible case */ + } +} + #+end_src + + + #+begin_src c :tangle trio.h +trio_exit_code trio_read_nucleus_coord(trio_t* file, double* coord); + #+end_src + + #+begin_src c :tangle trio.c +trio_exit_code trio_read_nucleus_coord(trio_t* file, double* coord) { + if (file == NULL) return TRIO_FAILURE; + + switch (file->back_end) { + + case TRIO_TEXT: + return trio_text_read_nucleus_coord(file, coord); + break; +/* + case TRIO_HDF5: + return trio_hdf5_read_nucleus_coord(file, coord); + break; + + case TRIO_JSON: + return trio_json_read_nucleus_coord(file, coord); + break; +*/ + default: + return TRIO_FAILURE; /* Impossible case */ + } +} + #+end_src + + + #+begin_src c :tangle trio.h +trio_exit_code trio_read_nucleus_charge(trio_t* file, double* charge); + #+end_src + + #+begin_src c :tangle trio.c +trio_exit_code trio_read_nucleus_charge(trio_t* file, double* charge) { + if (file == NULL) return TRIO_FAILURE; + + switch (file->back_end) { + + case TRIO_TEXT: + return trio_text_read_nucleus_charge(file, charge); + break; +/* + case TRIO_HDF5: + return trio_hdf5_read_nucleus_charge(file, charge); + break; + + case TRIO_JSON: + return trio_json_read_nucleus_charge(file, charge); + break; +*/ + default: + return TRIO_FAILURE; /* Impossible case */ + } +} + #+end_src + +* Back ends + + TRIO has multiple possible back ends: + + - HDF5: The most efficient back-end, by default + - Text files: not to be used for production, but useful for debugging + - JSON: for portability + +** TEXT Back end + + #+begin_src c :tangle trio_text.h +typedef struct nucleus_s { + double* coord; + double* charge; + int64_t num; +} nucleus_t; + +typedef struct electron_s { + int64_t alpha_num; + int64_t beta_num; +} electron_t; + +typedef struct trio_text_s { + trio_t parent ; + char* nucleus_file_name; + char* electron_file_name; +} trio_text_t; + + #+end_src + + + #+begin_src c :tangle trio_text.h +trio_exit_code trio_text_init(trio_t* file); + #+end_src + + #+begin_src c :tangle trio_text.c +trio_exit_code trio_text_init(trio_t* file) { + + trio_text_t* f = (trio_text_t*) file; + + const char* nucleus_file_name = "/nucleus.txt"; + f->nucleus_file_name = (char*) + calloc( strlen(file->file_name) + strlen(nucleus_file_name) + 1, + sizeof(char)); + assert (f->nucleus_file_name != NULL); + strcat (f->nucleus_file_name, nucleus_file_name); + + + const char* electron_file_name = "/electron.txt"; + f->nucleus_file_name = (char*) + calloc( strlen(file->file_name) + strlen(electron_file_name) + 1, + sizeof(char)); + assert (f->nucleus_file_name != NULL); + strcat (f->nucleus_file_name, electron_file_name); + + return TRIO_SUCCESS; +} + + #+end_src + + + #+begin_src c :tangle trio_text.h +trio_exit_code trio_text_finalize(trio_t* file); + #+end_src + + #+begin_src c :tangle trio_text.c +trio_exit_code trio_text_finalize(trio_t* file) { + + trio_text_t* f = (trio_text_t*) file; + + free (f->nucleus_file_name); + f->nucleus_file_name = NULL; + + free (f->electron_file_name); + f->electron_file_name = NULL; + + return TRIO_SUCCESS; +} + #+end_src + + + +*** Read/write the nucleus struct + + #+begin_src c :tangle trio_text.c +nucleus_t* trio_text_read_nucleus(const trio_text_t* file) { + + /* Allocate the data structure */ + nucleus_t* nucleus = (nucleus_t*) malloc(sizeof(nucleus_t)); + assert (nucleus != NULL); + + nucleus->num = 0; + nucleus->coord = NULL; + nucleus->charge = NULL; + + /* Try to open the file. If the file does not exist, return */ + FILE* f = fopen(file->nucleus_file_name,"r"); + if (f == NULL) { + return nucleus; + } + + /* Find size of file to allocate the max size of the string buffer */ + fseek(f, 0L, SEEK_END); + size_t sz = ftell(f); + fseek(f, 0L, SEEK_SET); + char* buffer = (char*) malloc(sz*sizeof(char)); + + /* Read the dimensioning variables */ + fscanf(f, "%s", buffer); + assert (strcmp(buffer, "num") == 0); + + fscanf(f, "%ld", &(nucleus->num)); + assert (nucleus->num > 0); + + /* Allocate arrays */ + nucleus->charge = (double*) calloc(nucleus->num, sizeof(double)); + assert (nucleus->charge != NULL); + + nucleus->coord = (double*) calloc(3 * nucleus->num, sizeof(double)); + assert (nucleus->coord != NULL); + + /* Read arrays */ + fscanf(f, "%s", buffer); + assert (strcmp(buffer, "charge") == 0); + + for (int i=0 ; inum ; i++) { + fscanf(f, "%lf", &(nucleus->charge[i])); + } + + fscanf(f, "%s", buffer); + assert (strcmp(buffer, "coord") == 0); + + for (int i=0 ; i<3*nucleus->num ; i++) { + fscanf(f, "%lf", &(nucleus->coord[i])); + } + free(buffer); + fclose(f); + return nucleus; +} + + +trio_exit_code trio_text_write_nucleus(const trio_text_t* file, nucleus_t* nucleus) { + assert (nucleus != NULL); + + FILE* f = fopen(file->nucleus_file_name,"w"); + assert (f != NULL); + + /* Write the dimensioning variables */ + fprintf(f, "num %ld\n", nucleus->num); + + /* Write arrays */ + fprintf(f, "charge\n"); + for (int i=0 ; inum ; i++) { + fprintf(f, "%lf\n", nucleus->charge[i]); + } + + fprintf(f, "coord\n"); + for (int i=0 ; i<3*nucleus->num ; i++) { + fprintf(f, "%lf\n", nucleus->coord[i]); + } + + fclose(f); + return TRIO_SUCCESS; +} + #+end_src + +*** Free memory + + Memory is allocated when reading. The followig function frees memory. + + #+begin_src c :tangle trio_text.c +trio_exit_code trio_text_free_nucleus(nucleus_t* nucleus) { + + if (nucleus == NULL) { + return TRIO_FAILURE; + } + + if (nucleus->coord != NULL) { + free (nucleus->coord); + } + nucleus->coord = NULL; + + if (nucleus->charge != NULL) { + free (nucleus->charge); + } + nucleus->charge = NULL; + + free (nucleus); + return TRIO_SUCCESS; +} + #+end_src + +*** Read/Write the num attribute + + #+begin_src c :tangle trio_text.h +trio_exit_code trio_text_read_nucleus_num(const trio_t* file, int64_t* num); +trio_exit_code trio_text_write_nucleus_num(const trio_t* file, const int64_t num); + #+end_src + + #+begin_src c :tangle trio_text.c +trio_exit_code trio_text_read_nucleus_num(const trio_t* file, int64_t* num) { + + assert (file != NULL); + assert (num != NULL); + + nucleus_t* nucleus = trio_text_read_nucleus((trio_text_t*) file); + + if (nucleus == NULL) { + return TRIO_FAILURE; + } + + /**/ *num = nucleus->num; + + trio_text_free_nucleus(nucleus); + return TRIO_SUCCESS; +} + + +trio_exit_code trio_text_write_nucleus_num(const trio_t* file, const int64_t num) { + + assert (num > 0L); + assert (file != NULL); + + nucleus_t* nucleus = trio_text_read_nucleus((trio_text_t*) file); + + assert (nucleus != NULL); + + if (nucleus->num != num) { + + nucleus->num = num; + + if (nucleus->charge != NULL) free(nucleus->charge); + nucleus->charge = NULL; + + nucleus->charge = (double*) calloc(num, sizeof(double)); + assert (nucleus->charge != NULL); + + if (nucleus->coord != NULL) free(nucleus->coord ); + nucleus->coord = NULL; + + nucleus->coord = (double*) calloc(3*num, sizeof(double)); + assert (nucleus->coord != NULL); + + } else { + nucleus->num = num; + } + + trio_exit_code rc = trio_text_write_nucleus((trio_text_t*) file, nucleus); + assert (rc == TRIO_SUCCESS); + + trio_text_free_nucleus(nucleus); + + return TRIO_SUCCESS; +} + #+end_src + +*** Read/Write the coord attribute + + The ~coord~ array is assumed allocated with the appropriate size. + + #+begin_src c :tangle trio_text.h +trio_exit_code trio_text_read_nucleus_coord(const trio_t* file, double* coord); +trio_exit_code trio_text_write_nucleus_coord(const trio_t* file, const double* coord); + #+end_src + + #+begin_src c :tangle trio_text.c +trio_exit_code trio_text_read_nucleus_coord(const trio_t* file, double* coord) { + + assert (file != NULL); + assert (file != NULL); + nucleus_t* nucleus = trio_text_read_nucleus((trio_text_t*) file); + + if (nucleus == NULL) { + return TRIO_FAILURE; + } + + assert (coord != NULL); + + for (int i=0 ; i<3*nucleus->num ; i++) { + coord[i] = nucleus->coord[i]; + } + + trio_text_free_nucleus(nucleus); + return TRIO_SUCCESS; +} + + +trio_exit_code trio_text_write_nucleus_coord(const trio_t* file, const double* coord) { + + assert (coord != NULL); + assert (file != NULL); + + nucleus_t* nucleus = trio_text_read_nucleus((trio_text_t*) file); + assert (nucleus != NULL); + + for (int i=0 ; i<3*nucleus->num ; i++) { + nucleus->coord[i] = coord[i]; + } + + trio_exit_code rc = trio_text_write_nucleus((trio_text_t*) file, nucleus); + assert (rc == TRIO_SUCCESS); + + trio_text_free_nucleus(nucleus); + + return TRIO_SUCCESS; +} + #+end_src +*** Read/Write the charge attribute + + The ~charge~ array is assumed allocated with the appropriate size. + + #+begin_src c :tangle trio_text.h +trio_exit_code trio_text_read_nucleus_charge(const trio_t* file, double* coord); +trio_exit_code trio_text_write_nucleus_charge(const trio_t* file, const double* coord); + #+end_src + + #+begin_src c :tangle trio_text.c +trio_exit_code trio_text_read_nucleus_charge(const trio_t* file, double* charge) { + + assert (file != NULL); + assert (file != NULL); + nucleus_t* nucleus = trio_text_read_nucleus((trio_text_t*)file); + + if (nucleus == NULL) { + return TRIO_FAILURE; + } + + assert (charge != NULL); + + for (int i=0 ; inum ; i++) { + charge[i] = nucleus->charge[i]; + } + + trio_text_free_nucleus(nucleus); + return TRIO_SUCCESS; +} + + +trio_exit_code trio_text_write_nucleus_charge(const trio_t* file, const double* charge) { + + assert (charge != NULL); + assert (file != NULL); + + nucleus_t* nucleus = trio_text_read_nucleus((trio_text_t*)file); + assert (nucleus != NULL); + + for (int i=0 ; inum ; i++) { + nucleus->charge[i] = charge[i]; + } + + trio_exit_code rc = trio_text_write_nucleus((trio_text_t*) file, nucleus); + assert (rc == TRIO_SUCCESS); + + trio_text_free_nucleus(nucleus); + + return TRIO_SUCCESS; +} + #+end_src +** HDF5 Back end +* File suffixes :noxport: + + #+begin_src c :tangle trio.h +#endif + #+end_src + + #+begin_src c :tangle trio_s.h +#endif + #+end_src + + #+begin_src c :tangle trio_text.h +#endif + #+end_src +* TODO Things to be done :noexport: + - [ ] Thread safety + - [ ] Error handling with errno