commit d72b5a1f3584b63a53d9648e37a367f1e9151883 Author: Max Lübke Date: Wed Nov 18 14:24:12 2020 +0100 Initial commit diff --git a/README.md b/README.md new file mode 100644 index 000000000..28763df54 --- /dev/null +++ b/README.md @@ -0,0 +1,63 @@ + + + +# install libraries from MDL + + library(devtools) + devtools::install_git("https://gitext.gfz-potsdam.de/delucia/RedModRphree.git") + devtools::install_git("https://gitext.gfz-potsdam.de/delucia/Rmufits.git") + +# USAGE + +mpirun ./kin + +OPTIONS: + +--work-package-size=<1-n> ... size of work packages (default 5) + +--ignore-result ... disables store of simulation resuls + +DHT: + +--dht ... enable dht (default is off) + +--dht-log ... enable logarithm application before rounding (default is off) + +--dht-signif=<1-n> ... set rounding to number of significant digits (default 5) + (only used if no vector is given in setup file) + (for individual values per column use R vector "signif_vector" in setup file) + +--dht-strategy=<0-1> ... change dht strategy, not implemented yet (default 0, dht on workers) + +--dht-size=<1-n> ... size of dht per process involved (see dht-strategy) in byte (default 1GiB) + +--dht-snaps=<0-2> ... enable or disable storage of DHT snapshots + 0 = snapshots are disabled + 1 = only stores snapshot at the end of the simulation with name .dht + 2 = stores snapshot at the end and after each iteration + iteration snapshot files are stored in /iter.dht + +--dht-file= ... initializes DHT with the given snapshot file + +############################################################################### + + +# about the usage of MPI_Wtime() +From the OpenMPI Man Page: + +For example, on platforms that support it, the clock_gettime() function will be used +to obtain a monotonic clock value with whatever precision is supported on that platform (e.g., nanoseconds). + +# External Libraries +Cmdline Parsing -> https://github.com/adishavit/argh + + +# Examples included (more to come) +1) SimDol2D.R ... simple chemistry (Calcite/Dolomite) on a 50x50 2D grid, 20 time steps +2) SimDolKtz.R ... simple chemistry (Calcite/Dolomite) on Ketzin grid (~650k elements), 20 time steps + The flow snapshots are NOT INCLUDED in svn but must be provided separately + + + diff --git a/src/DHT.cpp b/src/DHT.cpp new file mode 100644 index 000000000..546f7aa50 --- /dev/null +++ b/src/DHT.cpp @@ -0,0 +1,423 @@ +#include "DHT.h" +#include +#include +#include +#include + +/* + * determining destination rank and index by hash of key + * + * return values by reference + */ +static void determine_dest(uint64_t hash, int comm_size, unsigned int table_size, unsigned int *dest_rank, unsigned int *index, unsigned int index_count) { + uint64_t tmp; + int char_hop = 9-index_count; + unsigned int i; + for (i = 0; i < index_count ; i++) { + tmp = 0; + memcpy(&tmp,(unsigned char *)&hash+i, char_hop); + index[i] = (unsigned int) (tmp % table_size); + } + *dest_rank = (unsigned int) (hash % comm_size); +} + +/** + * set write flag to 1 + */ +static void set_flag(char* flag_byte) { + *flag_byte = 0; + *flag_byte |= (1 << 0); +} + +/** + * return 1 if write flag is set + * else 0 + */ +static int read_flag(char flag_byte) { + if ((flag_byte & 0x01) == 0x01) { + return 1; + } else return 0; +} + +/* + * allocating memory for DHT object and buckets. + * creating MPI window for OSC + * filling DHT object with passed by parameters, window, 2 counters for R/W errors and 2 pointers with allocated memory for further use + * return DHT object + */ +DHT* DHT_create(MPI_Comm comm, unsigned int size, int data_size, int key_size, uint64_t(*hash_func) (int, void*)) { + DHT *object; + MPI_Win window; + void* mem_alloc; + int comm_size, tmp; + + tmp = (int) ceil(log2(size)); + if (tmp%8 != 0) tmp = tmp + (8-(tmp%8)); + + object = (DHT*) malloc(sizeof(DHT)); + if (object == NULL) return NULL; + + //every memory allocation has 1 additional byte for flags etc. + if (MPI_Alloc_mem(size * (1 + data_size + key_size), MPI_INFO_NULL, &mem_alloc) != 0) return NULL; + if (MPI_Comm_size(comm, &comm_size) != 0) return NULL; + + memset(mem_alloc, '\0', size * (1 + data_size + key_size)); + + if (MPI_Win_create(mem_alloc, size * (1 + data_size + key_size), (1 + data_size + key_size), MPI_INFO_NULL, comm, &window) != 0) return NULL; + + object->data_size = data_size; + object->key_size = key_size; + object->table_size = size; + object->window = window; + object->hash_func = hash_func; + object->comm_size = comm_size; + object->communicator = comm; + object->read_misses = 0; + object->collisions = 0; + object->recv_entry = malloc(1 + data_size + key_size); + object->send_entry = malloc(1 + data_size + key_size); + object->index_count = 9-(tmp/8); + object->index = (unsigned int*) malloc((9-(tmp/8))*sizeof(int)); + object->mem_alloc = mem_alloc; + + DHT_stats *stats; + + stats = (DHT_stats*) malloc(sizeof(DHT_stats)); + if (stats == NULL) return NULL; + + object->stats = stats; + object->stats->writes_local = (int*) calloc(comm_size, sizeof(int)); + object->stats->old_writes = 0; + object->stats->read_misses = 0; + object->stats->collisions = 0; + object->stats->w_access = 0; + object->stats->r_access = 0; + + return object; +} + +/* + * puts passed by data with key to DHT + * + * returning DHT_MPI_ERROR = -1 if MPI error occurred + * else DHT_SUCCESS = 0 if success + */ +int DHT_write(DHT *table, void* send_key, void* send_data) { + unsigned int dest_rank, i; + int result = DHT_SUCCESS; + + table->stats->w_access++; + + //determine destination rank and index by hash of key + determine_dest(table->hash_func(table->key_size, send_key), table->comm_size, table->table_size, &dest_rank, table->index, table->index_count); + + //concatenating key with data to write entry to DHT + set_flag((char *) table->send_entry); + memcpy((char *) table->send_entry + 1, (char *) send_key, table->key_size); + memcpy((char *) table->send_entry + table->key_size + 1, (char *) send_data, table->data_size); + + //locking window of target rank with exclusive lock + if (MPI_Win_lock(MPI_LOCK_EXCLUSIVE, dest_rank, 0, table->window) != 0) + return DHT_MPI_ERROR; + for (i = 0; i < table->index_count; i++) + { + if (MPI_Get(table->recv_entry, 1 + table->data_size + table->key_size, MPI_BYTE, dest_rank, table->index[i], 1 + table->data_size + table->key_size, MPI_BYTE, table->window) != 0) return DHT_MPI_ERROR; + if (MPI_Win_flush(dest_rank, table->window) != 0) return DHT_MPI_ERROR; + + //increment collision counter if receiving key doesn't match sending key + //,entry has write flag + last index is reached + + if (read_flag(*(char *)table->recv_entry)) { + if (memcmp(send_key, (char *) table->recv_entry + 1, table->key_size) != 0) { + if (i == (table->index_count)-1) { + table->collisions += 1; + table->stats->collisions += 1; + result = DHT_WRITE_SUCCESS_WITH_COLLISION; + break; + } + } else break; + } else { + table->stats->writes_local[dest_rank]++; + break; + } + } + + //put data to DHT + if (MPI_Put(table->send_entry, 1 + table->data_size + table->key_size, MPI_BYTE, dest_rank, table->index[i], 1 + table->data_size + table->key_size, MPI_BYTE, table->window) != 0) return DHT_MPI_ERROR; + //unlock window of target rank + if (MPI_Win_unlock(dest_rank, table->window) != 0) return DHT_MPI_ERROR; + + return result; +} + +/* + * gets data from the DHT by key + * + * return DHT_SUCCESS = 0 if success + * DHT_MPI_ERROR = -1 if MPI error occurred + * DHT_READ_ERROR = -2 if receiving key doesn't match sending key + */ +int DHT_read(DHT *table, void* send_key, void* destination) { + unsigned int dest_rank, i; + + table->stats->r_access++; + + //determine destination rank and index by hash of key + determine_dest(table->hash_func(table->key_size, send_key), table->comm_size, table->table_size, &dest_rank, table->index, table->index_count); + + //locking window of target rank with shared lock + if (MPI_Win_lock(MPI_LOCK_SHARED, dest_rank, 0, table->window) != 0) return DHT_MPI_ERROR; + //receive data + for (i = 0; i < table->index_count; i++) { + if (MPI_Get(table->recv_entry, 1 + table->data_size + table->key_size, MPI_BYTE, dest_rank, table->index[i], 1 + table->data_size + table->key_size, MPI_BYTE, table->window) != 0) return DHT_MPI_ERROR; + if (MPI_Win_flush(dest_rank, table->window) != 0) return DHT_MPI_ERROR; + + //increment read error counter if write flag isn't set or key doesn't match passed by key + last index reached + //else copy data to dereference of passed by destination pointer + + if ((read_flag(*(char *) table->recv_entry)) == 0) { + table->read_misses += 1; + table->stats->read_misses += 1; + if (MPI_Win_unlock(dest_rank, table->window) != 0) return DHT_MPI_ERROR; + return DHT_READ_ERROR; + } + + if (memcmp(((char*)table->recv_entry) + 1, send_key, table->key_size) != 0) { + if (i == (table->index_count)-1) { + table->read_misses += 1; + table->stats->read_misses += 1; + if (MPI_Win_unlock(dest_rank, table->window) != 0) return DHT_MPI_ERROR; + return DHT_READ_ERROR; + } + } else break; + } + + //unlock window of target rank + if (MPI_Win_unlock(dest_rank, table->window) != 0) return DHT_MPI_ERROR; + + memcpy((char *) destination, (char *) table->recv_entry + table->key_size + 1, table->data_size); + + return DHT_SUCCESS; +} + +int DHT_to_file(DHT* table, char* filename) { + //open file + MPI_File file; + if (MPI_File_open(table->communicator, filename, MPI_MODE_CREATE|MPI_MODE_WRONLY, MPI_INFO_NULL, &file) != 0) return DHT_FILE_IO_ERROR; + + int rank; + MPI_Comm_rank(table->communicator, &rank); + + //write header (key_size and data_size) + if (rank == 0) { + if (MPI_File_write(file, &table->key_size, 1, MPI_INT, MPI_STATUS_IGNORE) != 0) return DHT_FILE_WRITE_ERROR; + if (MPI_File_write(file, &table->data_size, 1, MPI_INT, MPI_STATUS_IGNORE) != 0) return DHT_FILE_WRITE_ERROR; + } + + if (MPI_File_seek_shared(file, DHT_HEADER_SIZE, MPI_SEEK_SET) != 0) return DHT_FILE_IO_ERROR; + + char* ptr; + int bucket_size = table->key_size + table->data_size + 1; + + //iterate over local memory + for (unsigned int i = 0; i < table->table_size; i++) { + ptr = (char *) table->mem_alloc + (i * bucket_size); + //if bucket has been written to (checked by written_flag)... + if (read_flag(*ptr)) { + //write key and data to file + if (MPI_File_write_shared(file, ptr + 1, bucket_size - 1, MPI_BYTE, MPI_STATUS_IGNORE) != 0) return DHT_FILE_WRITE_ERROR; + } + } + //close file + if (MPI_File_close(&file) != 0) return DHT_FILE_IO_ERROR; + + return DHT_SUCCESS; +} + +int DHT_from_file(DHT* table, char* filename) { + MPI_File file; + MPI_Offset f_size; + int e_size, m_size, cur_pos, rank, offset; + char* buffer; + void* key; + void* data; + + if (MPI_File_open(table->communicator, filename, MPI_MODE_RDONLY, MPI_INFO_NULL, &file) != 0) return DHT_FILE_IO_ERROR; + + if (MPI_File_get_size(file, &f_size) != 0) return DHT_FILE_IO_ERROR; + + MPI_Comm_rank(table->communicator, &rank); + + e_size = table->key_size + table->data_size; + m_size = e_size > DHT_HEADER_SIZE ? e_size : DHT_HEADER_SIZE; + buffer = (char *) malloc(m_size); + + if (MPI_File_read(file, buffer, DHT_HEADER_SIZE, MPI_BYTE, MPI_STATUS_IGNORE) != 0) return DHT_FILE_READ_ERROR; + + if (*(int *) buffer != table->key_size) return DHT_WRONG_FILE; + if (*(int *) (buffer + 4) != table->data_size) return DHT_WRONG_FILE; + + offset = e_size*table->comm_size; + + if (MPI_File_seek(file, DHT_HEADER_SIZE, MPI_SEEK_SET) != 0) return DHT_FILE_IO_ERROR; + cur_pos = DHT_HEADER_SIZE + (rank * e_size); + + while(cur_pos < f_size) { + if (MPI_File_seek(file, cur_pos, MPI_SEEK_SET) != 0) return DHT_FILE_IO_ERROR; + MPI_Offset tmp; + MPI_File_get_position(file, &tmp); + if (MPI_File_read(file, buffer, e_size, MPI_BYTE, MPI_STATUS_IGNORE) != 0) return DHT_FILE_READ_ERROR; + key = buffer; + data = (buffer+table->key_size); + if (DHT_write(table, key, data) == DHT_MPI_ERROR) return DHT_MPI_ERROR; + + cur_pos += offset; + } + + free (buffer); + if (MPI_File_close(&file) != 0) return DHT_FILE_IO_ERROR; + + return DHT_SUCCESS; +} + +/* + * frees up memory and accumulate counter + */ +int DHT_free(DHT* table, int* collision_counter, int* readerror_counter) { + int buf; + + if (collision_counter != NULL) { + buf = 0; + if (MPI_Reduce(&table->collisions, &buf, 1, MPI_INT, MPI_SUM, 0, table->communicator) != 0) return DHT_MPI_ERROR; + *collision_counter = buf; + } + if (readerror_counter != NULL) { + buf = 0; + if (MPI_Reduce(&table->read_misses, &buf, 1, MPI_INT, MPI_SUM, 0, table->communicator) != 0) return DHT_MPI_ERROR; + *readerror_counter = buf; + } + if (MPI_Win_free(&(table->window)) != 0) return DHT_MPI_ERROR; + if (MPI_Free_mem(table->mem_alloc) != 0) return DHT_MPI_ERROR; + free(table->recv_entry); + free(table->send_entry); + free(table->index); + + free(table->stats->writes_local); + free(table->stats); + + free(table); + + return DHT_SUCCESS; +} + +/* + * prints a table with statistics about current use of DHT + * for each participating process and summed up results containing: + * 1. occupied buckets (in respect to the memory of this process) + * 2. free buckets (in respect to the memory of this process) + * 3. calls of DHT_write (w_access) + * 4. calls of DHT_read (r_access) + * 5. read misses (see DHT_READ_ERROR) + * 6. collisions (see DHT_WRITE_SUCCESS_WITH_COLLISION) + * 3-6 will reset with every call of this function + * finally the amount of new written entries is printed out (in relation to last call of this funtion) + */ +int DHT_print_statistics(DHT *table) { + int *written_buckets; + int *read_misses, sum_read_misses; + int *collisions, sum_collisions; + int sum_w_access, sum_r_access, *w_access, *r_access; + int rank; + + MPI_Comm_rank(table->communicator, &rank); + + //disable possible warning of unitialized variable, which is not the case + //since we're using MPI_Gather to obtain all values only on rank 0 + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" + + //obtaining all values from all processes in the communicator + if (rank == 0) read_misses = (int*) malloc(table->comm_size*sizeof(int)); + if (MPI_Gather(&table->stats->read_misses, 1, MPI_INT, read_misses, 1, MPI_INT, 0, table->communicator) != 0) return DHT_MPI_ERROR; + if (MPI_Reduce(&table->stats->read_misses, &sum_read_misses, 1, MPI_INT, MPI_SUM, 0, table->communicator) != 0) return DHT_MPI_ERROR; + table->stats->read_misses = 0; + + if (rank == 0) collisions = (int*) malloc(table->comm_size*sizeof(int)); + if (MPI_Gather(&table->stats->collisions, 1, MPI_INT, collisions, 1, MPI_INT, 0, table->communicator) != 0) return DHT_MPI_ERROR; + if (MPI_Reduce(&table->stats->collisions, &sum_collisions, 1, MPI_INT, MPI_SUM, 0, table->communicator) != 0) return DHT_MPI_ERROR; + table->stats->collisions = 0; + + if (rank == 0) w_access = (int*) malloc(table->comm_size*sizeof(int)); + if (MPI_Gather(&table->stats->w_access, 1, MPI_INT, w_access, 1, MPI_INT, 0, table->communicator) != 0) return DHT_MPI_ERROR; + if (MPI_Reduce(&table->stats->w_access, &sum_w_access, 1, MPI_INT, MPI_SUM, 0, table->communicator) != 0) return DHT_MPI_ERROR; + table->stats->w_access = 0; + + if (rank == 0) r_access = (int*) malloc(table->comm_size*sizeof(int)); + if (MPI_Gather(&table->stats->r_access, 1, MPI_INT, r_access, 1, MPI_INT, 0, table->communicator) != 0) return DHT_MPI_ERROR; + if (MPI_Reduce(&table->stats->r_access, &sum_r_access, 1, MPI_INT, MPI_SUM, 0, table->communicator) != 0) return DHT_MPI_ERROR; + table->stats->r_access = 0; + + if (rank == 0) written_buckets = (int*) calloc(table->comm_size, sizeof(int)); + if (MPI_Reduce(table->stats->writes_local, written_buckets, table->comm_size, MPI_INT, MPI_SUM, 0, table->communicator) != 0) return DHT_MPI_ERROR; + + if (rank == 0) { //only process with rank 0 will print out results as a table + int sum_written_buckets = 0; + + for (int i=0; i < table->comm_size; i++) { + sum_written_buckets += written_buckets[i]; + } + + int members = 7; + int padsize = (members*12)+1; + char pad[padsize+1]; + + memset(pad, '-', padsize*sizeof(char)); + pad[padsize]= '\0'; + printf("\n"); + printf("%-35s||resets with every call of this function\n", " "); + printf("%-11s|%-11s|%-11s||%-11s|%-11s|%-11s|%-11s\n", + "rank", + "occupied", + "free", + "w_access", + "r_access", + "read misses", + "collisions"); + printf("%s\n", pad); + for (int i = 0; i < table->comm_size; i++) { + printf("%-11d|%-11d|%-11d||%-11d|%-11d|%-11d|%-11d\n", + i, + written_buckets[i], + table->table_size-written_buckets[i], + w_access[i], + r_access[i], + read_misses[i], + collisions[i]); + } + printf("%s\n", pad); + printf("%-11s|%-11d|%-11d||%-11d|%-11d|%-11d|%-11d\n", + "sum", + sum_written_buckets, + (table->table_size*table->comm_size)-sum_written_buckets, + sum_w_access, + sum_r_access, + sum_read_misses, + sum_collisions); + + printf("%s\n", pad); + printf("%s %d\n", + "new entries:", + sum_written_buckets - table->stats->old_writes); + + printf("\n"); + fflush(stdout); + + table->stats->old_writes = sum_written_buckets; + } + + //enable warning again + #pragma GCC diagnostic pop + + MPI_Barrier(table->communicator); + return DHT_SUCCESS; +} \ No newline at end of file diff --git a/src/DHT.h b/src/DHT.h new file mode 100644 index 000000000..1b118e690 --- /dev/null +++ b/src/DHT.h @@ -0,0 +1,112 @@ +/* + * File: DHT.h + * Author: max luebke + * + * Created on 16. November 2017, 09:14 + */ + +#ifndef DHT_H +#define DHT_H + +#include +#include + +#define DHT_MPI_ERROR -1 +#define DHT_READ_ERROR -2 +#define DHT_SUCCESS 0 +#define DHT_WRITE_SUCCESS_WITH_COLLISION 1 + +#define DHT_WRONG_FILE 11 +#define DHT_FILE_IO_ERROR 12 +#define DHT_FILE_READ_ERROR 13 +#define DHT_FILE_WRITE_ERROR 14 + +#define DHT_HEADER_SIZE 8 + +typedef struct {; + int *writes_local, old_writes; + int read_misses, collisions; + int w_access, r_access; +} DHT_stats; + +typedef struct { + MPI_Win window; + int data_size; + int key_size; + unsigned int table_size; + MPI_Comm communicator; + int comm_size; + uint64_t(*hash_func) (int, void*); + void* recv_entry; + void* send_entry; + void* mem_alloc; + int read_misses; + int collisions; + unsigned int *index; + unsigned int index_count; + DHT_stats *stats; +} DHT; + + + +/* + * parameters: + * MPI_Comm comm - communicator of processes that are holding the DHT + * int size_per_process - number of buckets each process will create + * int data_size - size of data in bytes + * int key_size - size of key in bytes + * *hash_func - pointer to hashfunction + * + * return: + * NULL if error during initialization + * DHT* if success + */ +extern DHT* DHT_create(MPI_Comm comm, unsigned int size_per_process, int data_size, int key_size, uint64_t(*hash_func)(int, void*)); + +/* + * parameters: + * DHT *table - DHT_object created by DHT_create + * void* data - pointer to data + * void* - pointer to key + * + * return: + * error value (see above) + */ +extern int DHT_write(DHT *table, void* key, void* data); + +/* + * parameters: + * DHT *table - DHT_object created by DHT_create + * void* key - pointer to key + * void* destination - pointer which will hold the resulting data from DHT + * + * return: + * error value (see above) + */ +extern int DHT_read(DHT *table, void* key, void* destination); + +extern int DHT_to_file(DHT *table, char* filename); + +extern int DHT_from_file(DHT *table, char* filename); + +/* + * parameters: + * DHT *table - DHT_object created by DHT_create + * int* collision_counter - pointer which will hold the total count of collisions + * int* readerror_counter - pointer which will hold the total count of read errors + * + * return: + * error value (see above) + */ +extern int DHT_free(DHT *table, int* collision_counter, int* readerror_counter); + +/* + * parameters: + * DHT *table - DHT_object created by DHT_create + * + * return: + * error value (DHT_SUCCESS or DHT_MPI_ERROR) + */ +extern int DHT_print_statistics(DHT *table); + +#endif /* DHT_H */ \ No newline at end of file diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 000000000..e8f8c3036 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,51 @@ +## Simple Makefile for MPI use of RInside + +## comment this out if you need a different version of R, +## and set R_HOME accordingly as an environment variable +R_HOME := $(shell R RHOME) + +sources := $(wildcard *.cpp) +programs := kin + + +# OpenMPI header and libraries +MPICPPFLAGS := $(shell mpic++ -showme:compile) +MPILIBS := $(shell mpic++ -showme:link) + +## include headers and libraries for R +RCPPFLAGS := $(shell $(R_HOME)/bin/R CMD config --cppflags) +RLDFLAGS := $(shell $(R_HOME)/bin/R CMD config --ldflags) +RBLAS := $(shell $(R_HOME)/bin/R CMD config BLAS_LIBS) +RLAPACK := $(shell $(R_HOME)/bin/R CMD config LAPACK_LIBS) + + +## include headers and libraries for Rcpp interface classes +## note that RCPPLIBS will be empty with Rcpp (>= 0.11.0) and can be omitted +RCPPINCL := $(shell echo 'Rcpp:::CxxFlags()' | $(R_HOME)/bin/R --vanilla --slave) +RCPPLIBS := $(shell echo 'Rcpp:::LdFlags()' | $(R_HOME)/bin/R --vanilla --slave) + + +## include headers and libraries for RInside embedding classes +RINSIDEINCL := $(shell echo 'RInside:::CxxFlags()' | $(R_HOME)/bin/R --vanilla --slave) +RINSIDELIBS := $(shell echo 'RInside:::LdFlags()' | $(R_HOME)/bin/R --vanilla --slave) + + +## compiler etc settings used in default make rules +CXX := $(shell $(R_HOME)/bin/R CMD config CXX) +CPPFLAGS := -D STRICT_R_HEADERS -Wall -ggdb -O3 $(shell $(R_HOME)/bin/R CMD config CPPFLAGS) +CXXFLAGS := $(MPICPPFLAGS) $(RCPPFLAGS) $(RCPPINCL) $(RINSIDEINCL) $(shell $(R_HOME)/bin/R CMD config CXXFLAGS) +LDLIBS := $(MPILIBS) $(RLDFLAGS) $(RBLAS) $(RLAPACK) $(RCPPLIBS) $(RINSIDELIBS) -lcrypto ## -fpermissive + +default: all + +kin : DHT.cpp worker.cpp dht_wrapper.cpp r_utils.cpp kin.cpp + +all : $(programs) + @test -x /usr/bin/strip && strip $^ + +run : $(programs) + @test -x /usr/bin/mpirun && for p in $(programs); do echo; echo "Running $$p:"; mpirun -n 4 ./$$p; done + +clean: + rm -vf $(programs) + diff --git a/src/RFun_Eval.R b/src/RFun_Eval.R new file mode 100644 index 000000000..935ee0c09 --- /dev/null +++ b/src/RFun_Eval.R @@ -0,0 +1,214 @@ +## Simple library of functions to assess and visualize the results of the coupled simulations + +## Time-stamp: "Last modified 2020-02-04 23:21:37 delucia" + +require(RedModRphree) +require(Rmufits) ## essentially for PlotCartCellData + + +## function which reads all simulation results in a given directory +ReadRTSims <- function(dir) { + files_full <- list.files(dir, pattern="iter.*rds", full.names=TRUE) + files_name <- list.files(dir, pattern="iter.*rds", full.names=FALSE) + res <- lapply(files_full, readRDS) + names(res) <- gsub(".rds","",files_name, fixed=TRUE) + return(res) +} + +## function which reads all successive DHT stored in a given directory +ReadAllDHT <- function(dir) { + files_full <- list.files(dir, pattern="iter.*dht", full.names=TRUE) + files_name <- list.files(dir, pattern="iter.*dht", full.names=FALSE) + res <- lapply(files_full, ReadDHT) + names(res) <- gsub(".rds","",files_name, fixed=TRUE) + return(res) +} + +## function which reads one .dht file and gives a matrix +ReadDHT <- function(file) +{ + conn <- file(file, "rb") ## open for reading in binary mode + if (!isSeekable(conn)) + stop("Connection not seekable") + + ## we first reposition ourselves to the end of the file... + tmp <- seek(conn, where=0, origin = "end") + ## ... and then back to the origin so to store the length in bytes + flen <- seek(conn, where=0, origin = "start") + + ## we read the first 2 integers (4 bytes each) containing dimensions in bytes + dims <- readBin(conn, what="integer", n=2) + + ## compute dimensions of the data + tots <- sum(dims) + ncol <- tots/8 + nrow <- (flen - 8)/tots ## 8 here is 2*sizeof("int") + buff <- readBin(conn, what="double", n=ncol*nrow) + ## close connection + close(conn) + res <- matrix(buff, nrow=nrow, ncol=ncol, byrow=TRUE) + return(res) +} + +## Scatter plots of each variable in the iteration +PlotScatter <- function(sam1, sam2, which=NULL, labs=c("NO DHT", "DHT"), pch=".", cols=3, ...) +{ + if ((!is.data.frame(sam1)) & ("T" %in% names(sam1))) + sam1 <- sam1$C + if ((!is.data.frame(sam2)) & ("T" %in% names(sam2))) + sam2 <- sam2$C + if (is.numeric(which)) + inds <- which + else if (is.character(which)) + inds <- match(which, colnames(sam1)) + else if (is.null(which)) + inds <- seq_along(colnames(sam1)) + + rows <- nrow(matrix(seq_along(inds), ncol=cols)) + par(mfrow=c(rows, cols)) + a <- lapply(inds, function(x) { + plot(sam1[,x], sam2[,x], main=colnames(sam1)[x], xlab=labs[1], ylab=labs[2], pch=pch, col="red", ...) + abline(0,1, col="grey", cex=1.5) + }) + invisible() +} + +##### Some metrics for relative comparison + +## Using range as norm +RranRMSE <- function (pred, obs) + sqrt(mean((pred - obs)^2))/abs(max(pred) - min(pred)) + +## Using max val as norm +RmaxRMSE <- function (pred, obs) + sqrt(mean((pred - obs)^2)/abs(max(pred))) + +## Using sd as norm +RsdRMSE <- function (pred, obs) + sqrt(mean((pred - obs)^2))/sd(pred) + +## Using mean as norm +RmeanRMSE <- function (pred, obs) + sqrt(mean((pred - obs)^2))/mean(pred) + +## Using mean as norm +RAEmax <- function (pred, obs) + mean(abs(pred - obs))/max(pred) + +## Max absolute error +MAE <- function (pred, obs) + max(abs(pred - obs)) + +## workhorse function for ComputeErrors and its use with mapply +AppliedFun <- function(a, b, .fun) mapply(.fun, as.list(a$C), as.list(b$C)) + +## Compute the diffs between two simulation, iter by iter, +## with a given metric (passed in form of function name to this function) +ComputeErrors <- function(sim1, sim2, FUN=RMSE) +{ + if (length(sim1)!= length(sim2)) { + cat("The simulations do not have the same length, subsetting to the shortest\n") + a <- min(length(sim1), length(sim2)) + sim1 <- sim1[1:a] + sim2 <- sim2[1:a] + } + if (!is.function(match.fun(FUN))) { + cat("Invalid function\n") + + } + + t(mapply(AppliedFun, sim1, sim2, MoreArgs=list(.fun=FUN))) +} + +## Function to display the error progress between 2 simulations +ErrorProgress <- function(mat, ignore, colors, metric, ...) +{ + if (missing(colors)) + colors <- sample(rainbow(ncol(mat))) + + if (missing(metric)) + metric <- "Metric" + + ## if the optional argument "ignore" (a character vector) is + ## passed, we remove the matching column names + if (!missing(ignore)) { + to_remove <- match(ignore, colnames(mat)) + mat <- mat[, -to_remove] + colors <- colors[-to_remove] + } + + yc <- mat[nrow(mat),] + par(mar=c(5,4,2,6)) + matplot(mat, type="l", lty=1, lwd=2, col=colors, xlab="iteration", ylab=metric, ...) + mtext(colnames(mat), side = 4, line = 2, outer = FALSE, at = yc, adj = 0.5, col = colors, las=2) +} + +## Function which exports all simulations to ParaView's .vtu Requires +## package RcppVTK +ExportToParaview <- function(vtu, nameout, results){ + require(RcppVTK) + n <- length(results) + vars <- colnames(results[[1]]) + ## strip eventually present ".vtu" from nameout + nameout <- sub(".vtu", "", nameout, fixed=TRUE) + namesteps <- paste0(nameout, ".", sprintf("%04d",seq(1,n)), ".vtu") + for (step in seq_along(results)) { + file.copy(from=vtu, to=namesteps[step], overwrite = TRUE) + cat(paste("Saving step ", step, " in file ", namesteps[step], "\n")) + ret <- ExportMatrixToVTU (fin=vtu, fout=namesteps[step], names=colnames(results[[step]]), mat=results[[step]]) + } + invisible(ret) +} + + +## Version of Rmufits::PlotCartCellData with the ability to fix the +## "breaks" for color coding of 2D simulations +Plot2DCellData <- function (data, grid, nx, ny, contour = TRUE, + nlevels = 12, breaks, palette = "heat.colors", + rev.palette = TRUE, scale = TRUE, ...) +{ + if (!missing(grid)) { + xc <- unique(sort(grid$cell$XCOORD)) + yc <- unique(sort(grid$cell$YCOORD)) + nx <- length(xc) + ny <- length(yc) + if (!length(data) == nx * ny) + stop("Wrong nx, ny or grid") + } + else { + xc <- seq(1, nx) + yc <- seq(1, ny) + } + z <- matrix(round(data, 6), ncol = nx, nrow = ny, byrow = TRUE) + pp <- t(z[rev(seq(1, nrow(z))), ]) + + if (missing(breaks)) { + breaks <- pretty(data, n = nlevels) + } + + breakslen <- length(breaks) + colors <- do.call(palette, list(n = breakslen - 1)) + if (rev.palette) + colors <- rev(colors) + if (scale) { + par(mfrow = c(1, 2)) + nf <- layout(matrix(c(1, 2), 1, 2, byrow = TRUE), widths = c(4, + 1)) + } + par(las = 1, mar = c(5, 5, 3, 1)) + image(xc, yc, pp, xlab = "X [m]", ylab = "Y[m]", las = 1, + asp = 1, breaks = breaks, col = colors, axes = FALSE, + ...) + axis(1) + axis(2) + if (contour) + contour(unique(sort(xc)), unique(sort(yc)), pp, breaks = breaks, + add = TRUE) + if (scale) { + par(las = 1, mar = c(5, 1, 5, 5)) + PlotImageScale(data, breaks = breaks, add.axis = FALSE, + axis.pos = 4, col = colors) + axis(4, at = breaks) + } + invisible(pp) +} diff --git a/src/SimComp2D.R b/src/SimComp2D.R new file mode 100644 index 000000000..e605aef7a --- /dev/null +++ b/src/SimComp2D.R @@ -0,0 +1,128 @@ +## chemical database +db <- RPhreeFile("mdl_quint_kin.dat", is.db=TRUE) +phreeqc::phrLoadDatabaseString(db) + +## only the directory +demodir <- system.file("extdata", "demo_rtwithmufits", package="Rmufits") + +prop <- c("Al", "C","Ca","Cl","Fe", "K", "Mg","Na", "Si", "pH", ## "pe", + "Albite", "Calcite", "Chlorite", "Illite", "Kaolinite") + +signif_vector <- c(7,7,7,7,7,7,7,7,7,6, 5,5,5,5,5) +prop_type <- rep("normal", length(signif_vector)) + +base <- c("SOLUTION 1", + "units mol/kgw", + "pH 6.77", + "temp 35", + "-water 1", + "Al 8.06386e-09", + "C 0.0006108294", + "Ca 0.09709463", + "Cl 4.340042", + "Fe 1.234357e-05", + "K 0.01117434", + "Mg 0.0406959", + "Na 4.189209", + "Si 0.0001935754", + "INCREMENTAL_REACTIONS true", + "KINETICS 1 ", + "-steps 86400", + "-bad_step_max 10000", + "-cvode true", + "Albite", + "-m 8.432165", ## 1540.0", + "-parms 01.54 100", + "Calcite", + "-m 0.0", + "-parms 10 100", + "Chlorite", + "-m 1.106585", ## 202.100", + "-parms 64.84 100", + "Illite", + "-m 0.9549153", ## 174.400", + "-parms 43.38 100", + "Kaolinite", + "-m 0.0", + "-parms 29.17 100", + "END") + +selout <- c("KNOBS", + "-convergence_tolerance 1E-6", + "SELECTED_OUTPUT", + "-reset false", + "USER_PUNCH", + "-head Al C Ca Cl Fe K Mg Na Si pH Albite Calcite Chlorite Illite Kaolinite", ## pe + "10 PUNCH TOT(\"Al\"), TOT(\"C\"), TOT(\"Ca\"), TOT(\"Cl\"), TOT(\"Fe\"), TOT(\"K\"), TOT(\"Mg\"), TOT(\"Na\"), TOT(\"Si\"), -LA(\"H+\"), KIN(\"Albite\"), KIN(\"Calcite\"), KIN(\"Chlorite\"), KIN(\"Illite\"), KIN(\"Kaolinite\")" ) + +## Define initial conditions as equilibrium with primary minerals +ipr <- c(Al = 8.689e-10, + C = 0.0006108, + Ca = 0.09709, + Cl = 4.34, + Fe = 1.802e-06, + K = 0.01131, + Mg = 0.04074, + Na = 4.189, + Si = 7.653e-05, + pH = 6.889, + Albite = 5.0, + Calcite = 0.0, + Chlorite = 10.0, + Illite = 2.0, + Kaolinite = 0.0 + ) + +initstate <- matrix(rep(ipr, 2500), byrow=TRUE, ncol=length(ipr)) +colnames(initstate) <- names(ipr) + +vecinj <- c(Al= 8.694e-10, + C = 8.182e-01, + Ca= 9.710e-02, + Cl= 4.340e+00, + Fe= 1.778e-06, + K = 1.131e-02, + Mg= 4.074e-02, + Na= 4.189e+00, + Si= 7.652e-05, + pH= 2.556228) + + +## setup boundary conditions for transport - we have already read the +## GRID with the following code: +## grid <- Rmufits::ReadGrid(paste0(demodir,"/d2ascii.run.GRID.SUM")) +## cbound <- which(grid$cell$ACTNUM == 2) +## dput(cbound) +cbound <- c(1L, 50L, 100L, 150L, 200L, 250L, 300L, 350L, 400L, 450L, 500L, + 550L, 600L, 650L, 700L, 750L, 800L, 850L, 900L, 950L, 1000L, + 1050L, 1100L, 1150L, 1200L, 1250L, 1300L, 1350L, 1400L, 1450L, + 1500L, 1550L, 1600L, 1650L, 1700L, 1750L, 1800L, 1850L, 1900L, + 1950L, 2000L, 2050L, 2100L, 2150L, 2200L, 2250L, 2300L, 2350L, + 2400L, 2450L, 2451L, 2452L, 2453L, 2454L, 2455L, 2456L, 2457L, + 2458L, 2459L, 2460L, 2461L, 2462L, 2463L, 2464L, 2465L, 2466L, + 2467L, 2468L, 2469L, 2470L, 2471L, 2472L, 2473L, 2474L, 2475L, + 2476L, 2477L, 2478L, 2479L, 2480L, 2481L, 2482L, 2483L, 2484L, + 2485L, 2486L, 2487L, 2488L, 2489L, 2490L, 2491L, 2492L, 2493L, + 2494L, 2495L, 2496L, 2497L, 2498L, 2499L, 2500L) + +boundary_matrix <- matrix(rep(ipr[1:10], length(cbound)), byrow=TRUE, nrow=length(cbound)) +colnames(boundary_matrix) <- names(ipr[1:10]) +boundary_matrix[1, ] <- vecinj + +boundary_matrix <- cbind(cbound,boundary_matrix) + +setup <- list(n = 2500, + bound = boundary_matrix, + base = base, + first = selout, + initsim = initstate, + Cf = 1, + prop = prop, + immobile = seq(11,15), + kin = seq(11,15), + phase = "FLUX1", + density = "DEN1", + reduce = FALSE, + snapshots = demodir, ## directory where we will read MUFITS SUM files + gridfile = paste0(demodir,"/d2ascii.run.GRID.SUM") + ) diff --git a/src/SimCompKtz.R b/src/SimCompKtz.R new file mode 100644 index 000000000..335d71b5e --- /dev/null +++ b/src/SimCompKtz.R @@ -0,0 +1,131 @@ +db <- RPhreeFile("mdl_quint_kin.dat", is.db=TRUE) +phreeqc::phrLoadDatabaseString(db) + +## only the directory +demodir <- "./snaps/" + +base <- c("SOLUTION 1", + "units mol/kgw", + "pH 6.77", + "temp 35", + "-water 1", + "Al 8.06386e-09", + "C 0.0006108294", + "Ca 0.09709463", + "Cl 4.340042", + "Fe 1.234357e-05", + "K 0.01117434", + "Mg 0.0406959", + "Na 4.189209", + "Si 0.0001935754", + "INCREMENTAL_REACTIONS true", + "KINETICS 1 ", + "-steps 86400", + "-bad_step_max 10000", + "-cvode true", + "Albite", + "-m 8.432165", ## 1540.0", + "-parms 01.54 100", + "Calcite", + "-m 0.0", + "-parms 10 100", + "Chlorite", + "-m 1.106585", ## 202.100", + "-parms 64.84 100", + "Illite", + "-m 0.9549153", ## 174.400", + "-parms 43.38 100", + "Kaolinite", + "-m 0.0", + "-parms 29.17 100", + "END") + +selout <- c("KNOBS", + "-convergence_tolerance 1E-6", + "SELECTED_OUTPUT", + "-reset false", + "USER_PUNCH", + "-head Al C Ca Cl Fe K Mg Na Si pH Albite Calcite Chlorite Illite Kaolinite", ## pe + "10 PUNCH TOT(\"Al\"), TOT(\"C\"), TOT(\"Ca\"), TOT(\"Cl\"), TOT(\"Fe\"), TOT(\"K\"), TOT(\"Mg\"), TOT(\"Na\"), TOT(\"Si\"), -LA(\"H+\"), KIN(\"Albite\"), KIN(\"Calcite\"), KIN(\"Chlorite\"), KIN(\"Illite\"), KIN(\"Kaolinite\")" ) + +## Define initial conditions as equilibrium with primary minerals +ipr <- c(Al = 8.689e-10, + C = 0.0006108, + Ca = 0.09709, + Cl = 4.34, + Fe = 1.802e-06, + K = 0.01131, + Mg = 0.04074, + Na = 4.189, + Si = 7.653e-05, + pH = 6.889, + Albite = 5.0, + Calcite = 0.0, + Chlorite = 10.0, + Illite = 2.0, + Kaolinite = 0.0 + ) + +initstate <- matrix(rep(ipr, 648420), byrow=TRUE, ncol=length(ipr)) +colnames(initstate) <- names(ipr) + +vecinj <- c(Al= 8.694e-10, + C = 8.182e-01, + Ca= 9.710e-02, + Cl= 4.340e+00, + Fe= 1.778e-06, + K = 1.131e-02, + Mg= 4.074e-02, + Na= 4.189e+00, + Si= 7.652e-05, + pH= 2.556228) + + + + +prop <- c("Al", "C","Ca","Cl","Fe", "K", "Mg","Na", "Si", "pH", ## "pe", + "Albite", "Calcite", "Chlorite", "Illite", "Kaolinite") + +bound_elm <- c(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 11L, 12L, 13L, 14L, + 15L, 16L, 17L, 18L, 19L, 20L, 21L, 22L, 23L, 24L, 25L, 26L, 27L, + 28L, 29L, 30L, 31L, 32L, 33L, 34L, 35L, 36L, 37L, 38L, 39L, 40L, + 41L, 42L, 43L, 44L, 45L, 46L, 47L, 48L, 49L, 50L, 51L, 52L, 53L, + 54L, 55L, 56L, 57L, 58L, 59L, 60L, 61L, 62L, 63L, 64L, 65L, 66L, + 67L, 68L, 69L, 70L, 71L, 72L, 73L, 74L, 75L, 76L, 77L, 78L, 79L, + 80L, 81L, 82L, 83L, 84L, 85L, 86L, 87L, 88L, 89L, 90L, 91L, 92L, + 93L, 94L, 95L, 96L, 97L, 98L, 99L, 100L, 101L, 102L, 103L, 104L, + 105L, 106L, 107L, 108L, 214L, 215L) + +inj_elm <- c(7426L, 18233L, 29040L, 39847L, + 50654L, 61461L, 72268L, 83075L, 93882L, 104689L, 115496L, 126303L, + 137110L, 147917L, 158724L, 169531L, 180338L, 191145L, 201952L, + 212759L, 223566L, 234373L, 245180L, 255987L, 266794L, 277601L, + 288408L, 299215L, 310022L, 320829L, 331636L, 342443L, 353250L, + 364057L, 374864L, 385671L, 396478L, 407285L, 418092L, 428899L, + 439706L, 450513L, 461320L, 472127L, 482934L, 493741L, 504548L, + 515355L) + +cbound <- inj_elm + +boundinit <- matrix(rep(vecinj, length(cbound)), ncol=length(vecinj), byrow=TRUE) +myboundmat <- cbind(cbound,boundinit) + +## distinguish between injection and real boundaries +colnames(myboundmat) <- c("cbound", names(vecinj)) + +setup <- list(n=648420, + base=base, + bound=myboundmat, + first=selout, + initsim=initstate, + Cf=1, + prop=prop, + immobile=seq(11,15), + kin= seq(11,15), + phase="FLUX1", + density="DENS", + reduce=TRUE, + snapshots="snaps/AllSnaps_cmp_v3.rds", + gridfile ="snaps/GridKtz_cmp_v3.rds") + + diff --git a/src/SimDol2D.R b/src/SimDol2D.R new file mode 100644 index 000000000..991dc2bfa --- /dev/null +++ b/src/SimDol2D.R @@ -0,0 +1,118 @@ +## chemical database +db <- RPhreeFile(system.file("extdata", "phreeqc_kin.dat", + package="RedModRphree"), is.db=TRUE) + +phreeqc::phrLoadDatabaseString(db) + +## only the directory +demodir <- system.file("extdata", "demo_rtwithmufits", package="Rmufits") + +prop <- c("C","Ca","Cl","Mg","pH","pe","O2g", "Calcite","Dolomite") + +signif_vector <- c(7,7,7,7,7,7,7,5,5) +prop_type <- c("act","act","act","act","logact","logact","ignore","act","act") + + +base <- c("SOLUTION 1", + "units mol/kgw", + "temp 25.0", + "water 1", + "pH 9.91 charge", + "pe 4.0", + "C 1.2279E-04", + "Ca 1.2279E-04", + "Mg 0.001", + "Cl 0.002", + "PURE 1", + "O2g -0.1675 10", + "KINETICS 1", + "-steps 100", + "-step_divide 100", + "-bad_step_max 2000", + "Calcite", "-m 0.000207", + "-parms 0.0032", + "Dolomite", + "-m 0.0", + "-parms 0.00032", + "END") + +selout <- c("SELECTED_OUTPUT", "-high_precision true", "-reset false", + "-time", "-soln", "-temperature true", "-water true", + "-pH", "-pe", "-totals C Ca Cl Mg", + "-kinetic_reactants Calcite Dolomite", "-equilibrium O2g") + +initsim <- c("SELECTED_OUTPUT", "-high_precision true", + "-reset false", + "USER_PUNCH", + "-head C Ca Cl Mg pH pe O2g Calcite Dolomite", + "10 PUNCH TOT(\"C\"), TOT(\"Ca\"), TOT(\"Cl\"), TOT(\"Mg\"), -LA(\"H+\"), -LA(\"e-\"), EQUI(\"O2g\"), EQUI(\"Calcite\"), EQUI(\"Dolomite\")", + "SOLUTION 1", + "units mol/kgw", + "temp 25.0", "water 1", + "pH 9.91 charge", + "pe 4.0", + "C 1.2279E-04", + "Ca 1.2279E-04", + "Cl 1E-12", + "Mg 1E-12", + "PURE 1", + "O2g -0.6788 10.0", + "Calcite 0.0 2.07E-3", + "Dolomite 0.0 0.0", + "END") + +vecinj <- c("C"= 0, + "Ca" = 0, + "Cl" = 0.002, + "Mg" = 0.001, + "pe" = 4, + "pH" = 7) + +init <- c("C(4)"= 1.2279E-4, + "Ca" =1.2279E-4, + "Cl" =0, + "Mg" =0, + "pe" =4, + "pH" =7, + "Calcite"= 2.07e-4, + "Dolomite"= 0) + + +## setup boundary conditions for transport - we have already read the +## GRID with the following code: +## grid <- Rmufits::ReadGrid(paste0(demodir,"/d2ascii.run.GRID.SUM")) +## cbound <- which(grid$cell$ACTNUM == 2) +## dput(cbound) +cbound <- c(1L, 50L, 100L, 150L, 200L, 250L, 300L, 350L, 400L, 450L, 500L, + 550L, 600L, 650L, 700L, 750L, 800L, 850L, 900L, 950L, 1000L, + 1050L, 1100L, 1150L, 1200L, 1250L, 1300L, 1350L, 1400L, 1450L, + 1500L, 1550L, 1600L, 1650L, 1700L, 1750L, 1800L, 1850L, 1900L, + 1950L, 2000L, 2050L, 2100L, 2150L, 2200L, 2250L, 2300L, 2350L, + 2400L, 2450L, 2451L, 2452L, 2453L, 2454L, 2455L, 2456L, 2457L, + 2458L, 2459L, 2460L, 2461L, 2462L, 2463L, 2464L, 2465L, 2466L, + 2467L, 2468L, 2469L, 2470L, 2471L, 2472L, 2473L, 2474L, 2475L, + 2476L, 2477L, 2478L, 2479L, 2480L, 2481L, 2482L, 2483L, 2484L, + 2485L, 2486L, 2487L, 2488L, 2489L, 2490L, 2491L, 2492L, 2493L, + 2494L, 2495L, 2496L, 2497L, 2498L, 2499L, 2500L) + +boundinit <- matrix(rep(init[-c(7,8)], length(cbound)), byrow=TRUE, nrow=length(cbound)) +myboundmat <- cbind(cbound,boundinit) +myboundmat[cbound==1, c(2:7)] <- vecinj +colnames(myboundmat) <- c("cbound", names(vecinj)) + +setup <- list(n=2500, + bound=myboundmat, + base=base, + first=selout, + initsim=initsim, + Cf=1, + prop=prop, + immobile=c(7,8,9), + kin= c(8,9), + ann=list(O2g=-0.1675), + phase="FLUX1", + density="DEN1", + reduce=FALSE, + snapshots=demodir, ## directory where we will read MUFITS SUM files + gridfile=paste0(demodir,"/d2ascii.run.GRID.SUM") + ) diff --git a/src/SimDolKtz.R b/src/SimDolKtz.R new file mode 100644 index 000000000..f52b65df2 --- /dev/null +++ b/src/SimDolKtz.R @@ -0,0 +1,130 @@ +# library(RedModRphree) +# library(Rmufits) +# library(RcppVTK) + +db <- RPhreeFile(system.file("extdata", "phreeqc_kin.dat", + package="RedModRphree"), is.db=TRUE) + +phreeqc::phrLoadDatabaseString(db) + +prop <- c("C","Ca","Cl","Mg","pH","pe","O2g", "Calcite","Dolomite") +signif_vector <- c(7,7,7,7,7,7,7,5,5) +prop_type <- c("act","act","act","act","logact","logact","ignore","act","act") + + +base <- c("SOLUTION 1", + "units mol/kgw", + "temp 25.0", + "water 1", + "pH 9.91 charge", + "pe 4.0", + "C 1.2279E-04", + "Ca 1.2279E-04", + "Mg 0.001", + "Cl 0.002", + "PURE 1", + "O2g -0.1675 10", + "KINETICS 1", + "-steps 100", + "-step_divide 100", + "-bad_step_max 2000", + "Calcite", "-m 0.000207", + "-parms 0.0032", + "Dolomite", + "-m 0.0", + "-parms 0.00032", + "END") + +selout <- c("SELECTED_OUTPUT", + "-high_precision true", + "-reset false", + "-time", + "-soln", + "-temperature true", + "-water true", + "-pH", + "-pe", + "-totals C Ca Cl Mg", + "-kinetic_reactants Calcite Dolomite", + "-equilibrium O2g") + +initsim <- c("SELECTED_OUTPUT", + "-high_precision true", + "-reset false", + "USER_PUNCH", + "-head C Ca Cl Mg pH pe O2g Calcite Dolomite", + "10 PUNCH TOT(\"C\"), TOT(\"Ca\"), TOT(\"Cl\"), TOT(\"Mg\"), -LA(\"H+\"), -LA(\"e-\"), EQUI(\"O2g\"), EQUI(\"Calcite\"), EQUI(\"Dolomite\")", + "SOLUTION 1", + "units mol/kgw", + "temp 25.0", "water 1", + "pH 9.91 charge", + "pe 4.0", + "C 1.2279E-04", + "Ca 1.2279E-04", + "Cl 1E-12", + "Mg 1E-12", + "PURE 1", + "O2g -0.6788 10.0", + "Calcite 0.0 2.07E-3", + "Dolomite 0.0 0.0", + "END") + +vecinj <- c("C"= 0, + "Ca" = 0, + "Cl" = 0.002, + "Mg" = 0.001, + "pe" = 4, + "pH" = 7) + +init <- c("C(4)"= 1.2279E-4, + "Ca" =1.2279E-4, + "Cl" =0, + "Mg" =0, + "pe" =4, + "pH" =7, + "Calcite"= 2.07e-4, + "Dolomite"= 0) + +bound_elm <- c(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 11L, 12L, 13L, 14L, + 15L, 16L, 17L, 18L, 19L, 20L, 21L, 22L, 23L, 24L, 25L, 26L, 27L, + 28L, 29L, 30L, 31L, 32L, 33L, 34L, 35L, 36L, 37L, 38L, 39L, 40L, + 41L, 42L, 43L, 44L, 45L, 46L, 47L, 48L, 49L, 50L, 51L, 52L, 53L, + 54L, 55L, 56L, 57L, 58L, 59L, 60L, 61L, 62L, 63L, 64L, 65L, 66L, + 67L, 68L, 69L, 70L, 71L, 72L, 73L, 74L, 75L, 76L, 77L, 78L, 79L, + 80L, 81L, 82L, 83L, 84L, 85L, 86L, 87L, 88L, 89L, 90L, 91L, 92L, + 93L, 94L, 95L, 96L, 97L, 98L, 99L, 100L, 101L, 102L, 103L, 104L, + 105L, 106L, 107L, 108L, 214L, 215L) + +inj_elm <- c(7426L, 18233L, 29040L, 39847L, + 50654L, 61461L, 72268L, 83075L, 93882L, 104689L, 115496L, 126303L, + 137110L, 147917L, 158724L, 169531L, 180338L, 191145L, 201952L, + 212759L, 223566L, 234373L, 245180L, 255987L, 266794L, 277601L, + 288408L, 299215L, 310022L, 320829L, 331636L, 342443L, 353250L, + 364057L, 374864L, 385671L, 396478L, 407285L, 418092L, 428899L, + 439706L, 450513L, 461320L, 472127L, 482934L, 493741L, 504548L, + 515355L) + +cbound <- inj_elm + +boundinit <- matrix(rep(vecinj, length(cbound)), ncol=length(vecinj), byrow=TRUE) +myboundmat <- cbind(cbound,boundinit) + +## distinguish between injection and real boundaries +colnames(myboundmat) <- c("cbound", names(vecinj)) + +setup <- list(n=648420, + bound=myboundmat, + base=base, + first=selout, + initsim=initsim, + Cf=1, + prop=prop, + immobile=c(7,8,9), + kin= c(8,9), + ann=list(O2g=-0.1675), + phase="FLUX1", + density="DENS", + reduce=FALSE, + snapshots="snaps/AllSnaps_cmp_v3.rds", + gridfile ="snaps/GridKtz_cmp_v3.rds" + ) diff --git a/src/Test_Pro.R b/src/Test_Pro.R new file mode 100644 index 000000000..62e4a6b80 --- /dev/null +++ b/src/Test_Pro.R @@ -0,0 +1,115 @@ +## Example script for assessing simulation results with "RFun_Eval.R" +## Time-stamp: "Last modified 2020-02-05 03:36:05 delucia" + +## Read all the needed functions +source("RFun_Eval.R") + + + +## read the simulation results of a SimDol2D variant with setup$prolong +sd <- ReadRTSims("test_prol4") + +simtimes <- sapply(sd, "[","simtime") + + +## we still need to read the grid to perform 2D visualization +demodir <- system.file("extdata", "demo_rtwithmufits", package="Rmufits") +grid <- ReadGrid(paste0(demodir,"/d2ascii.run.GRID.SUM")) + +## fix the color scale +br <- seq(0, 0.0012, length=13) + + +## we want animation +library(animation) + +## workhorse function to be used with package "animation" +PlotAn <- function(tot, prop, grid, breaks) +{ + for (step in seq(1, length(tot))) { + snap <- tot[[step]]$C + time <- tot[[step]]$simtime/3600/24 + ind <- match(prop, colnames(snap)) + Plot2DCellData(snap[,ind], grid=grid, contour=FALSE, breaks=breaks, nlevels=length(breaks), scale=TRUE, main=paste0(prop," after ", time, "days")) + } +} + +### simple animations ### + +## As gif +saveGIF({ + PlotAn(tot=sd, prop="Dolomite", grid=grid, breaks=br) + }, img.name = 'Dolomite_plot', movie.name="Dol2Dbreaks.gif", + interval = 0.5, nmax = length(sd)) + +## as HTML +saveHTML({ + PlotAn(tot=sd, prop="Dolomite", grid=grid, breaks=br) + }, img.name = 'Dolomite_plot', htmlfile="dolomite.html", + interval = 0.5, nmax = length(sd)) + +## For inclusion in latex +saveLatex({ + PlotAn(tot=sd, prop="Dolomite", grid=grid, breaks=br) + }, img.name = 'Dolomite_plot', + latex.filename = 'dolomite_prolong.tex', + interval = 0.5, nmax = length(sd)) + + + +sd <- ReadRTSims("test_prol4") + +source("RFun_Eval.R") + +library(RedModRphree) +library(Rmufits) +library(RcppVTK) + + +#### Evaluation of discrepancies of KtzDol_6p200_1_nodht_180 / KtzDol_6p200_1_dhtlog_180 +dht <- ReadRTSims("KtzDol_6p200_1_dhtlog_180") +ref <- ReadRTSims("KtzDol_6p200_1_nodht_180") + +rmse <- ComputeErrors(dht, ref, FUN=RMSE) + +rae <- ComputeErrors(dht, ref, FUN=RAEmax) + +mae <- ComputeErrors(dht, ref, FUN=MAE) + +rel_max_rmse <- ComputeErrors(dht, ref, FUN=RmaxRMSE) + +## Visualize the 2 computed relative errors +## start by defining a nice color palette using RColorBrewer +mycol <- RColorBrewer::brewer.pal(8, "Dark2") + +## uncomment the next line to save this image to a pdf file +cairo_pdf("Ref_VS_DHT.pdf", height=4, width=12) +par(mfrow=c(1,2), family="serif") + +ErrorProgress(rmse, colors=mycol, ignore=c("O2g", "pe", "Cl", "C"), log="y", + ylim=c(1E-12, 1E-3), las=1, main="Mean RMSE, reference vs. dht", metric="RMSE") +abline(h=10^-seq(11,2), col="grey", lwd=1, lty="dashed") + +ErrorProgress(mae, colors=mycol, ignore=c("O2g", "pe", "Cl", "C"), log="y", + ylim=c(1E-11, 1E-2), las=1, main="Max Absolute Error, reference vs. dht", metric="MAE") +abline(h=10^-seq(10,3), col="grey", lwd=1, lty="dashed") +dev.off() + +cairo_pdf("Scatter_Ref_VS_DHT.pdf", height=5, width=9) +par(family="serif") +PlotScatter(ref[[200]], dht[[200]], + which=c("Calcite", "Dolomite", "Ca", "Mg", "C", "pH"), + cols=3, labs=c("Reference", "DHT"), pch=".", cex=2) +dev.off() + +time_ref <- readRDS("KtzDol_6p200_1_nodht_180/timings.rds") +time_dht <- readRDS("KtzDol_6p200_1_dhtlog_180/timings.rds") + + +## export to paraview +resk <- lapply(res, function(x) return(data.matrix(x$C))) +ExportToParaview("template_vtu_ketzinlarge.vtu", "KtzDol_6p200_1/paraview", results=resk) + + + + diff --git a/src/Test_Validation.R b/src/Test_Validation.R new file mode 100644 index 000000000..483171791 --- /dev/null +++ b/src/Test_Validation.R @@ -0,0 +1,92 @@ +## Example script for assessing simulation results with "RFun_Eval.R" +## Time-stamp: "Last modified 2020-01-27 17:23:25 delucia" + +## Read all the needed functions +source("RFun_Eval.R") + + +## Read all .rds in a given directory +trds <- ReadRTSims("TestDHT_Log") +## Read all .dht in a given directory +tdht <- ReadAllDHT("TestDHT_Log") + +## control the content of a .dht +rc <- trds[[1]]$C +rt <- trds[[1]]$T +dht <- tdht[[1]][, 10:18] +## extract the unique rows of the first iteration (3 rows expected, as per dht) +rtu <- mgcv::uniquecombs(rt) +rcu <- mgcv::uniquecombs(rc) +all.equal(attr(rtu,"index"), attr(rcu,"index")) +colnames(dht) <- colnames(rcu) + + +## compare _Log _NoLog and reference +## First read all the simulations +rlog <- ReadRTSims("TestDHT_Log") ## all default values, np 4, with --dht-log +nlog <- ReadRTSims("TestDHT_NoLog") ## all default values, np 4, without --dht-log +ref <- ReadRTSims("TestNoDHT") ## all default values, np 4 + +## Compute absolute RMSE for each variable +rmse_lnl <- ComputeErrors(rlog, nlog, FUN=RMSE) +## Compute relative RMSE for each variable using sd as norm +rel_sd_rmse_lnl <- ComputeErrors(rlog, nlog, FUN=RsdRMSE) +## Compute relative RMSE for each variable using MAX as norm +rel_max_rmse_lnl <- ComputeErrors(rlog, nlog, FUN=RmaxRMSE) + +## Visualize the 2 computed relative errors +## start by defining a nice color palette using RColorBrewer +mycol <- RColorBrewer::brewer.pal(8, "Dark2") + +## uncomment the next line to save this image to a pdf file +## cairo_pdf("RelErr_Dol_dhtlog_vs_nolog.pdf", height=9, width=12) +par(mfrow=c(1,2)) +ErrorProgress(rel_sd_rmse_lnl, colors=mycol, ignore=c("O2g"), log="y", ylim=c(1E-08, 1E-3), las=1, main="Rel.err dht-log vs dht, norm=SD", metric="RMSE_SD") +## add a grid +abline(h=10^-seq(8,3), col="grey", lwd=0.5, lty="dashed") +ErrorProgress(rel_max_rmse_lnl, colors=mycol, ignore=c("O2g"), log="y", ylim=c(1E-08, 1E-3), las=1, main="Rel.err dht-log vs dht, norm=MAX", metric="RMSE_MAX") +abline(h=10^-seq(8,3), col="grey", lwd=0.5, lty="dashed") + +## uncomment the next line when saving to pdf +## dev.off() + + +## Visualize Scatter Plot of each variable +PlotScatter(rlog[[20]], nlog[[20]], labs=c("DHT LOG", "DHT")) + +PlotScatter(rlog[[4]], nlog[[4]], labs=c("DHT LOG", "DHT")) + +## Same as before but between dht-log and ref +rmse_logref <- ComputeErrors(rlog, ref, FUN=RMSE) +rel_sd_rmse_logref <- ComputeErrors(rlog, ref, FUN=RsdRMSE) +rel_max_rmse_logref <- ComputeErrors(rlog, ref, FUN=RmaxRMSE) + +cairo_pdf("RelErr_Dol_ref_vs_dhtlog.pdf", height=9, width=12) +par(mfrow=c(1,2)) +ErrorProgress(rel_sd_rmse_logref, colors=mycol, ignore=c("O2g"), log="y", ylim=c(1E-08, 1E-3), las=1, main="Rel.err dht-log vs Ref, norm=SD", metric="RMSE_SD") +abline(h=10^-seq(8,3), col="grey", lwd=0.5, lty="dashed") +ErrorProgress(rel_max_rmse_logref, colors=mycol, ignore=c("O2g"), log="y", ylim=c(1E-08, 1E-3), las=1, main="Rel.err dht-log vs Ref, norm=MAX", metric="RMSE_MAX") +abline(h=10^-seq(8,3), col="grey", lwd=0.5, lty="dashed") + +dev.off() + +x11(); PlotScatter(rlog[[20]]$C, ref[[20]]$C, labs=c("DHT log", "ref")) + +rmse_nlogref <- ComputeErrors(nlog, ref, FUN=RMSE) +rel_sd_rmse_nlogref <- ComputeErrors(nlog, ref, FUN=RsdRMSE) +ErrorProgress(rel_sd_rmse_nlogref, log="y") +PlotScatter(nlog[[20]]$C, ref[[20]]$C, labs=c("DHT no log", "ref")) + +## Same as before but between dht-nolog and ref +rmse_nlogref <- ComputeErrors(nlog, ref, FUN=RMSE) +rel_sd_rmse_nlogref <- ComputeErrors(nlog, ref, FUN=RsdRMSE) +rel_max_rmse_nlogref <- ComputeErrors(nlog, ref, FUN=RmaxRMSE) + +cairo_pdf("RelErr_Dol_ref_vs_dht.pdf", height=9, width=12) +par(mfrow=c(1,2)) +ErrorProgress(rel_sd_rmse_nlogref, colors=mycol, ignore=c("O2g"), log="y", ylim=c(1E-08, 1E-3), las=1, main="Rel.err dht-nolog vs Ref, norm=SD", metric="RMSE_SD") +abline(h=10^-seq(8,3), col="grey", lwd=0.5, lty="dashed") +ErrorProgress(rel_max_rmse_nlogref, colors=mycol, ignore=c("O2g"), log="y", ylim=c(1E-08, 1E-3), las=1, main="Rel.err dht-nolog vs Ref, norm=MAX", metric="RMSE_MAX") +abline(h=10^-seq(8,3), col="grey", lwd=0.5, lty="dashed") + +dev.off() diff --git a/src/argh.h b/src/argh.h new file mode 100644 index 000000000..422aa4c78 --- /dev/null +++ b/src/argh.h @@ -0,0 +1,432 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace argh +{ + // Terminology: + // A command line is composed of 2 types of args: + // 1. Positional args, i.e. free standing values + // 2. Options: args beginning with '-'. We identify two kinds: + // 2.1: Flags: boolean options => (exist ? true : false) + // 2.2: Parameters: a name followed by a non-option value + +#if !defined(__GNUC__) || (__GNUC__ >= 5) + using string_stream = std::istringstream; +#else + // Until GCC 5, istringstream did not have a move constructor. + // stringstream_proxy is used instead, as a workaround. + class stringstream_proxy + { + public: + stringstream_proxy() = default; + + // Construct with a value. + stringstream_proxy(std::string const& value) : + stream_(value) + {} + + // Copy constructor. + stringstream_proxy(const stringstream_proxy& other) : + stream_(other.stream_.str()) + { + stream_.setstate(other.stream_.rdstate()); + } + + void setstate(std::ios_base::iostate state) { stream_.setstate(state); } + + // Stream out the value of the parameter. + // If the conversion was not possible, the stream will enter the fail state, + // and operator bool will return false. + template + stringstream_proxy& operator >> (T& thing) + { + stream_ >> thing; + return *this; + } + + + // Get the string value. + std::string str() const { return stream_.str(); } + + std::stringbuf* rdbuf() const { return stream_.rdbuf(); } + + // Check the state of the stream. + // False when the most recent stream operation failed + operator bool() const { return !!stream_; } + + ~stringstream_proxy() = default; + private: + std::istringstream stream_; + }; + using string_stream = stringstream_proxy; +#endif + + class parser + { + public: + enum Mode { PREFER_FLAG_FOR_UNREG_OPTION = 1 << 0, + PREFER_PARAM_FOR_UNREG_OPTION = 1 << 1, + NO_SPLIT_ON_EQUALSIGN = 1 << 2, + SINGLE_DASH_IS_MULTIFLAG = 1 << 3, + }; + + parser() = default; + + parser(std::initializer_list pre_reg_names) + { add_params(pre_reg_names); } + + parser(const char* const argv[], int mode = PREFER_FLAG_FOR_UNREG_OPTION) + { parse(argv, mode); } + + parser(int argc, const char* const argv[], int mode = PREFER_FLAG_FOR_UNREG_OPTION) + { parse(argc, argv, mode); } + + void add_param(std::string const& name); + void add_params(std::initializer_list init_list); + + void parse(const char* const argv[], int mode = PREFER_FLAG_FOR_UNREG_OPTION); + void parse(int argc, const char* const argv[], int mode = PREFER_FLAG_FOR_UNREG_OPTION); + + std::multiset const& flags() const { return flags_; } + std::map const& params() const { return params_; } + std::vector const& pos_args() const { return pos_args_; } + + // begin() and end() for using range-for over positional args. + std::vector::const_iterator begin() const { return pos_args_.cbegin(); } + std::vector::const_iterator end() const { return pos_args_.cend(); } + size_t size() const { return pos_args_.size(); } + + ////////////////////////////////////////////////////////////////////////// + // Accessors + + // flag (boolean) accessors: return true if the flag appeared, otherwise false. + bool operator[](std::string const& name) const; + + // multiple flag (boolean) accessors: return true if at least one of the flag appeared, otherwise false. + bool operator[](std::initializer_list init_list) const; + + // returns positional arg string by order. Like argv[] but without the options + std::string const& operator[](size_t ind) const; + + // returns a std::istream that can be used to convert a positional arg to a typed value. + string_stream operator()(size_t ind) const; + + // same as above, but with a default value in case the arg is missing (index out of range). + template + string_stream operator()(size_t ind, T&& def_val) const; + + // parameter accessors, give a name get an std::istream that can be used to convert to a typed value. + // call .str() on result to get as string + string_stream operator()(std::string const& name) const; + + // accessor for a parameter with multiple names, give a list of names, get an std::istream that can be used to convert to a typed value. + // call .str() on result to get as string + // returns the first value in the list to be found. + string_stream operator()(std::initializer_list init_list) const; + + // same as above, but with a default value in case the param was missing. + // Non-string def_val types must have an operator<<() (output stream operator) + // If T only has an input stream operator, pass the string version of the type as in "3" instead of 3. + template + string_stream operator()(std::string const& name, T&& def_val) const; + + // same as above but for a list of names. returns the first value to be found. + template + string_stream operator()(std::initializer_list init_list, T&& def_val) const; + + private: + string_stream bad_stream() const; + std::string trim_leading_dashes(std::string const& name) const; + bool is_number(std::string const& arg) const; + bool is_option(std::string const& arg) const; + bool got_flag(std::string const& name) const; + bool is_param(std::string const& name) const; + + private: + std::vector args_; + std::map params_; + std::vector pos_args_; + std::multiset flags_; + std::set registeredParams_; + std::string empty_; + }; + + + ////////////////////////////////////////////////////////////////////////// + + inline void parser::parse(const char * const argv[], int mode) + { + int argc = 0; + for (auto argvp = argv; *argvp; ++argc, ++argvp); + parse(argc, argv, mode); + } + + ////////////////////////////////////////////////////////////////////////// + + inline void parser::parse(int argc, const char* const argv[], int mode /*= PREFER_FLAG_FOR_UNREG_OPTION*/) + { + // convert to strings + args_.resize(argc); + std::transform(argv, argv + argc, args_.begin(), [](const char* const arg) { return arg; }); + + // parse line + for (auto i = 0u; i < args_.size(); ++i) + { + if (!is_option(args_[i])) + { + pos_args_.emplace_back(args_[i]); + continue; + } + + auto name = trim_leading_dashes(args_[i]); + + if (!(mode & NO_SPLIT_ON_EQUALSIGN)) + { + auto equalPos = name.find('='); + if (equalPos != std::string::npos) + { + params_.insert({ name.substr(0, equalPos), name.substr(equalPos + 1) }); + continue; + } + } + + // if the option is unregistered and should be a multi-flag + if (1 == (args_[i].size() - name.size()) && // single dash + argh::parser::SINGLE_DASH_IS_MULTIFLAG & mode && // multi-flag mode + !is_param(name)) // unregistered + { + std::string keep_param; + + if (!name.empty() && is_param(std::string(1ul, name.back()))) // last char is param + { + keep_param += name.back(); + name.resize(name.size() - 1); + } + + for (auto const& c : name) + { + flags_.emplace(std::string{ c }); + } + + if (!keep_param.empty()) + { + name = keep_param; + } + else + { + continue; // do not consider other options for this arg + } + } + + // any potential option will get as its value the next arg, unless that arg is an option too + // in that case it will be determined a flag. + if (i == args_.size() - 1 || is_option(args_[i + 1])) + { + flags_.emplace(name); + continue; + } + + // if 'name' is a pre-registered option, then the next arg cannot be a free parameter to it is skipped + // otherwise we have 2 modes: + // PREFER_FLAG_FOR_UNREG_OPTION: a non-registered 'name' is determined a flag. + // The following value (the next arg) will be a free parameter. + // + // PREFER_PARAM_FOR_UNREG_OPTION: a non-registered 'name' is determined a parameter, the next arg + // will be the value of that option. + + assert(!(mode & argh::parser::PREFER_FLAG_FOR_UNREG_OPTION) + || !(mode & argh::parser::PREFER_PARAM_FOR_UNREG_OPTION)); + + bool preferParam = mode & argh::parser::PREFER_PARAM_FOR_UNREG_OPTION; + + if (is_param(name) || preferParam) + { + params_.insert({ name, args_[i + 1] }); + ++i; // skip next value, it is not a free parameter + continue; + } + else + { + flags_.emplace(name); + } + }; + } + + ////////////////////////////////////////////////////////////////////////// + + inline string_stream parser::bad_stream() const + { + string_stream bad; + bad.setstate(std::ios_base::failbit); + return bad; + } + + ////////////////////////////////////////////////////////////////////////// + + inline bool parser::is_number(std::string const& arg) const + { + // inefficient but simple way to determine if a string is a number (which can start with a '-') + std::istringstream istr(arg); + double number; + istr >> number; + return !(istr.fail() || istr.bad()); + } + + ////////////////////////////////////////////////////////////////////////// + + inline bool parser::is_option(std::string const& arg) const + { + assert(0 != arg.size()); + if (is_number(arg)) + return false; + return '-' == arg[0]; + } + + ////////////////////////////////////////////////////////////////////////// + + inline std::string parser::trim_leading_dashes(std::string const& name) const + { + auto pos = name.find_first_not_of('-'); + return std::string::npos != pos ? name.substr(pos) : name; + } + + ////////////////////////////////////////////////////////////////////////// + + inline bool argh::parser::got_flag(std::string const& name) const + { + return flags_.end() != flags_.find(trim_leading_dashes(name)); + } + + ////////////////////////////////////////////////////////////////////////// + + inline bool argh::parser::is_param(std::string const& name) const + { + return registeredParams_.count(name); + } + + ////////////////////////////////////////////////////////////////////////// + + inline bool parser::operator[](std::string const& name) const + { + return got_flag(name); + } + + ////////////////////////////////////////////////////////////////////////// + + inline bool parser::operator[](std::initializer_list init_list) const + { + return std::any_of(init_list.begin(), init_list.end(), [&](char const* const name) { return got_flag(name); }); + } + + ////////////////////////////////////////////////////////////////////////// + + inline std::string const& parser::operator[](size_t ind) const + { + if (ind < pos_args_.size()) + return pos_args_[ind]; + return empty_; + } + + ////////////////////////////////////////////////////////////////////////// + + inline string_stream parser::operator()(std::string const& name) const + { + auto optIt = params_.find(trim_leading_dashes(name)); + if (params_.end() != optIt) + return string_stream(optIt->second); + return bad_stream(); + } + + ////////////////////////////////////////////////////////////////////////// + + inline string_stream parser::operator()(std::initializer_list init_list) const + { + for (auto& name : init_list) + { + auto optIt = params_.find(trim_leading_dashes(name)); + if (params_.end() != optIt) + return string_stream(optIt->second); + } + return bad_stream(); + } + + ////////////////////////////////////////////////////////////////////////// + + template + string_stream parser::operator()(std::string const& name, T&& def_val) const + { + auto optIt = params_.find(trim_leading_dashes(name)); + if (params_.end() != optIt) + return string_stream(optIt->second); + + std::ostringstream ostr; + ostr << def_val; + return string_stream(ostr.str()); // use default + } + + ////////////////////////////////////////////////////////////////////////// + + // same as above but for a list of names. returns the first value to be found. + template + string_stream parser::operator()(std::initializer_list init_list, T&& def_val) const + { + for (auto& name : init_list) + { + auto optIt = params_.find(trim_leading_dashes(name)); + if (params_.end() != optIt) + return string_stream(optIt->second); + } + std::ostringstream ostr; + ostr << def_val; + return string_stream(ostr.str()); // use default + } + + ////////////////////////////////////////////////////////////////////////// + + inline string_stream parser::operator()(size_t ind) const + { + if (pos_args_.size() <= ind) + return bad_stream(); + + return string_stream(pos_args_[ind]); + } + + ////////////////////////////////////////////////////////////////////////// + + template + string_stream parser::operator()(size_t ind, T&& def_val) const + { + if (pos_args_.size() <= ind) + { + std::ostringstream ostr; + ostr << def_val; + return string_stream(ostr.str()); + } + + return string_stream(pos_args_[ind]); + } + + ////////////////////////////////////////////////////////////////////////// + + inline void parser::add_param(std::string const& name) + { + registeredParams_.insert(trim_leading_dashes(name)); + } + + ////////////////////////////////////////////////////////////////////////// + + inline void parser::add_params(std::initializer_list init_list) + { + for (auto& name : init_list) + registeredParams_.insert(trim_leading_dashes(name)); + } +} + + diff --git a/src/dht_wrapper.cpp b/src/dht_wrapper.cpp new file mode 100644 index 000000000..44f0a865c --- /dev/null +++ b/src/dht_wrapper.cpp @@ -0,0 +1,175 @@ +#include "dht_wrapper.h" +#include + +/*init globals*/ +bool dht_enabled; +int dht_snaps; +int dht_strategy; +int dht_significant_digits; +std::string dht_file; +std::vector dht_significant_digits_vector; +std::vector prop_type_vector; +bool dht_logarithm; +uint64_t dht_size_per_process; +uint64_t dht_hits, dht_miss, dht_collision; +RInside *R_DHT; +std::vector dht_flags; +DHT *dht_object; + +double *fuzzing_buffer; + +bool dt_differ; +/*functions*/ + +uint64_t get_md5(int key_size, void *key) { + MD5_CTX ctx; + unsigned char sum[MD5_DIGEST_LENGTH]; + uint64_t retval, *v1, *v2; + + MD5_Init(&ctx); + MD5_Update(&ctx, key, key_size); + MD5_Final(sum, &ctx); + + v1 = (uint64_t *)&sum[0]; + v2 = (uint64_t *)&sum[8]; + retval = *v1 ^ *v2; + + return retval; +} + +double Round_off(RInside &R, double N, double n) { + double result; + R["roundsig"] = n; + R["roundin"] = N; + + result = R.parseEval("signif(roundin, digits=roundsig)"); + + return result; +} + +/* +* Stores fuzzed version of key in fuzzing_buffer +*/ +void fuzz_for_dht(RInside &R, int var_count, void *key, double dt) { + unsigned int i = 0; + //introduce fuzzing to allow more hits in DHT + for (i = 0; i < (unsigned int)var_count; i++) { + if (prop_type_vector[i] == "act") { + //with log10 + if (dht_logarithm) { + if (((double *)key)[i] < 0) + cerr << "dht_wrapper.cpp::fuzz_for_dht(): Warning! Negative value at key!" << endl; + else if (((double *)key)[i] == 0) + fuzzing_buffer[i] = 0; + else + //fuzzing_buffer[i] = Round_off(R, std::log10(((double *)key)[i]), dht_significant_digits_vector[i] - 1); + fuzzing_buffer[i] = ROUND(-(std::log10(((double *)key)[i])), dht_significant_digits_vector[i]); + } else { + //without log10 + //fuzzing_buffer[i] = Round_off(R, ((double *)key)[i], dht_significant_digits_vector[i]); + fuzzing_buffer[i] = ROUND((((double *)key)[i]), dht_significant_digits_vector[i]); + } + } else if (prop_type_vector[i] == "logact") { + //fuzzing_buffer[i] = Round_off(R, ((double *)key)[i], dht_significant_digits_vector[i]); + fuzzing_buffer[i] = ROUND((((double *)key)[i]), dht_significant_digits_vector[i]); + } else if (prop_type_vector[i] == "ignore") { + fuzzing_buffer[i] = 0; + } else { + cerr << "dht_wrapper.cpp::fuzz_for_dht(): Warning! Probably wrong prop_type!" << endl; + } + } + if (dt_differ) + fuzzing_buffer[var_count] = dt; +} + +void check_dht(RInside &R, int length, std::vector &out_result_index, double *work_package) { + void *key; + int res; + int var_count = prop_type_vector.size(); + double dt; + + dt = R.parseEval("mysetup$dt"); + + for (int i = 0; i < length; i++) { + key = (void *)&(work_package[i * var_count]); + + //fuzz data (round, logarithm etc.) + fuzz_for_dht(R, var_count, key, dt); + + //overwrite input with data from DHT, IF value is found in DHT + res = DHT_read(dht_object, fuzzing_buffer, key); + + if (res == DHT_SUCCESS) { + //flag that this line is replaced by DHT-value, do not simulate!! + out_result_index[i] = false; + dht_hits++; + } else if (res == DHT_READ_ERROR) { + //this line is untouched, simulation is needed + out_result_index[i] = true; + dht_miss++; + } else { + //MPI ERROR ... WHAT TO DO NOW? + //RUNNING CIRCLES WHILE SCREAMING + } + } +} + +void fill_dht(RInside &R, int length, std::vector &result_index, double *work_package, double *results) { + void *key; + void *data; + int res; + int var_count = prop_type_vector.size(); + double dt; + + dt = R.parseEval("mysetup$dt"); + + for (int i = 0; i < length; i++) { + key = (void *)&(work_package[i * var_count]); + data = (void *)&(results[i * var_count]); + + if (result_index[i]) { + //If true -> was simulated, needs to be inserted into dht + + //fuzz data (round, logarithm etc.) + fuzz_for_dht(R, var_count, key, dt); + + res = DHT_write(dht_object, fuzzing_buffer, data); + + if (res != DHT_SUCCESS) { + if (res == DHT_WRITE_SUCCESS_WITH_COLLISION) { + dht_collision++; + } else { + //MPI ERROR ... WHAT TO DO NOW? + //RUNNING CIRCLES WHILE SCREAMING + } + } + } + } +} + +void print_statistics() { + int res; + + res = DHT_print_statistics(dht_object); + + if (res != DHT_SUCCESS) { + //MPI ERROR ... WHAT TO DO NOW? + //RUNNING CIRCLES WHILE SCREAMING + } +} + +int table_to_file(char *filename) { + int res = DHT_to_file(dht_object, filename); + return res; +} + +int file_to_table(char *filename) { + + int res = DHT_from_file(dht_object, filename); + if (res != DHT_SUCCESS) + return res; + + DHT_print_statistics(dht_object); + + return DHT_SUCCESS; +} diff --git a/src/dht_wrapper.h b/src/dht_wrapper.h new file mode 100644 index 000000000..3557f7f5b --- /dev/null +++ b/src/dht_wrapper.h @@ -0,0 +1,50 @@ +#pragma once +#include +#include +#include +#include +#include "DHT.h" + +using namespace std; +using namespace Rcpp; + +/*Functions*/ +uint64_t get_md5(int key_size, void* key); +void fuzz_for_dht(RInside &R, int var_count, void *key, double dt); +void check_dht(RInside &R, int length, std::vector &out_result_index, double *work_package); +void fill_dht(RInside &R, int length, std::vector &result_index, double *work_package, double *results); +void print_statistics(); +int table_to_file(char* filename); +int file_to_table(char* filename); + +/*globals*/ +extern bool dht_enabled; +extern int dht_snaps; +extern std::string dht_file; +extern bool dt_differ; + +//Default DHT Size per process in Byte (defaults to 1 GiB) +#define DHT_SIZE_PER_PROCESS 1073741824 + +//sets default dht access and distribution strategy +#define DHT_STRATEGY 0 +// 0 -> DHT is on workers, access from workers only +// 1 -> DHT is on workers + master, access from master only !NOT IMPLEMENTED YET! + +#define ROUND(value,signif) (((int) (pow(10.0, (double) signif) * value)) * pow(10.0, (double) -signif)) + +extern int dht_strategy; +extern int dht_significant_digits; +extern std::vector dht_significant_digits_vector; +extern std::vector prop_type_vector; +extern bool dht_logarithm; +extern uint64_t dht_size_per_process; + +//global DHT object, can be NULL if not initialized, check strategy +extern DHT* dht_object; + +//DHT Performance counter +extern uint64_t dht_hits, dht_miss, dht_collision; + +extern double* fuzzing_buffer; +extern std::vector dht_flags; diff --git a/src/global_buffer.h b/src/global_buffer.h new file mode 100644 index 000000000..69cc742f2 --- /dev/null +++ b/src/global_buffer.h @@ -0,0 +1,9 @@ +#pragma once + +#define BUFFER_OFFSET 5 + +/*Globals*/ +extern double* mpi_buffer; +extern double* mpi_buffer_results; + +extern uint32_t work_package_size; \ No newline at end of file diff --git a/src/kin.cpp b/src/kin.cpp new file mode 100644 index 000000000..4d19a72d8 --- /dev/null +++ b/src/kin.cpp @@ -0,0 +1,818 @@ +#include +#include +#include +#include + +#include // for the embedded R via RInside + +#include // mpi header file + +#include "argh.h" // Argument handler https://github.com/adishavit/argh BSD-licenced +#include "DHT.h" // MPI-DHT Implementation +#include "worker.h" +#include "r_utils.h" +#include "dht_wrapper.h" +#include "global_buffer.h" + +using namespace std; +using namespace Rcpp; + +double* mpi_buffer; +double* mpi_buffer_results; + +uint32_t work_package_size; +#define WORK_PACKAGE_SIZE_DEFAULT 5 + +bool store_result; + +std::set paramList() { + std::set options; + //global + options.insert("work-package-size"); + //only DHT + options.insert("dht-signif"); + options.insert("dht-strategy"); + options.insert("dht-size"); + options.insert("dht-snaps"); + options.insert("dht-file"); + + return options; +} + +std::set flagList() { + std::set options; + //global + options.insert("ignore-result"); + //only DHT + options.insert("dht"); + options.insert("dht-log"); + + return options; +} + +std::list checkOptions(argh::parser cmdl) { + std::list retList; + std::set flist = flagList(); + std::set plist = paramList(); + + for (auto& flag: cmdl.flags()) { + if (!(flist.find(flag) != flist.end())) retList.push_back(flag); + } + + for (auto& param: cmdl.params()) { + if (!(plist.find(param.first) != plist.end())) retList.push_back(param.first); + } + + return retList; +} + +typedef struct +{ + char has_work; + double* send_addr; +} worker_struct; + + +int main(int argc, char *argv[]) { + double sim_start, sim_b_transport, sim_a_transport, sim_b_chemistry, sim_a_chemistry, + sim_end; + + double cummul_transport = 0.f; + double cummul_chemistry = 0.f; + double cummul_workers = 0.f; + double cummul_chemistry_master = 0.f; + + double cummul_master_seq_pre_loop = 0.f; + double cummul_master_seq_loop = 0.f; + double master_idle = 0.f; + + double master_send_a, master_send_b; + double cummul_master_send = 0.f; + double master_recv_a, master_recv_b; + double cummul_master_recv = 0.f; + + double sim_a_seq, sim_b_seq, sim_c_seq, sim_d_seq; + double idle_a, idle_b; + + double sim_c_chemistry, sim_d_chemistry; + double sim_e_chemistry, sim_f_chemistry; + + argh::parser cmdl(argv); + + // cout << "CPP: Start Init (MPI)" << endl; + + MPI_Init( &argc, &argv ); + + int world_size; + MPI_Comm_size(MPI_COMM_WORLD, &world_size); + + int world_rank; + MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); + + /*Create custom Communicator with all processes except 0 (the master) for DHT storage*/ + //only needed if strategy == 0, but done anyway + MPI_Group group_world; + MPI_Group dht_group; + MPI_Comm dht_comm; + int* process_ranks; + + // make a list of processes in the new communicator + process_ranks= (int*) malloc(world_size*sizeof(int)); + for(int I = 1; I < world_size; I++) + process_ranks[I-1] = I; + + //get the group under MPI_COMM_WORLD + MPI_Comm_group(MPI_COMM_WORLD, &group_world); + //create the new group + MPI_Group_incl(group_world, world_size-1, process_ranks, &dht_group); + // create the new communicator + MPI_Comm_create(MPI_COMM_WORLD, dht_group, &dht_comm); + free (process_ranks); //cleanup + // cout << "Done"; + + if (cmdl[{"help", "h"}]) { + if (world_rank == 0) { + cout << "Todo" << endl << + "See README.md for further information." << endl; + } + MPI_Finalize(); + return EXIT_SUCCESS; + } + + /*INIT is now done separately in an R file provided here as argument!*/ + if (!cmdl(2)) { + if (world_rank == 0) { + cerr << "ERROR. Kin needs 2 positional arguments: " << endl << + "1) the R script defining your simulation and" << endl << + "2) the directory prefix where to save results and profiling" << endl; + } + MPI_Finalize(); + return EXIT_FAILURE; + } + + std::list optionsError = checkOptions(cmdl); + if (!optionsError.empty()) { + if (world_rank == 0) { + cerr << "Unrecognized option(s):\n" << endl; + for (auto option: optionsError) { + cerr << option << endl; + } + cerr << "\nMake sure to use available options. Exiting!" << endl; + } + MPI_Finalize(); + return EXIT_FAILURE; + } + + /*Parse DHT arguments*/ + dht_enabled = cmdl["dht"]; + // cout << "CPP: DHT is " << ( dht_enabled ? "ON" : "OFF" ) << '\n'; + + if (dht_enabled) { + cmdl("dht-strategy", 0) >> dht_strategy; + // cout << "CPP: DHT strategy is " << dht_strategy << endl; + + cmdl("dht-signif", 5) >> dht_significant_digits; + // cout << "CPP: DHT significant digits = " << dht_significant_digits << endl; + + dht_logarithm = cmdl["dht-log"]; + // cout << "CPP: DHT logarithm before rounding: " << ( dht_logarithm ? "ON" : "OFF" ) << endl; + + cmdl("dht-size", DHT_SIZE_PER_PROCESS) >> dht_size_per_process; + // cout << "CPP: DHT size per process (Byte) = " << dht_size_per_process << endl; + + cmdl("dht-snaps", 0) >> dht_snaps; + + cmdl("dht-file") >> dht_file; + } + + /*Parse work package size*/ + cmdl("work-package-size", WORK_PACKAGE_SIZE_DEFAULT) >> work_package_size; + + + /*Parse output options*/ + store_result = !cmdl["ignore-result"]; + + + if (world_rank==0) { + cout << "CPP: Complete results storage is " << ( store_result ? "ON" : "OFF" ) << endl; + cout << "CPP: Work Package Size: " << work_package_size << endl; + cout << "CPP: DHT is " << ( dht_enabled ? "ON" : "OFF" ) << '\n'; + + if (dht_enabled) { + cout << "CPP: DHT strategy is " << dht_strategy << endl; + cout << "CPP: DHT key default digits (ignored if 'signif_vector' is defined) = " << dht_significant_digits << endl; + cout << "CPP: DHT logarithm before rounding: " << ( dht_logarithm ? "ON" : "OFF" ) << endl; + cout << "CPP: DHT size per process (Byte) = " << dht_size_per_process << endl; + cout << "CPP: DHT save snapshots is " << dht_snaps << endl; + cout << "CPP: DHT load file is " << dht_file << endl; + } + } + + cout << "CPP: R Init (RInside) on process " << world_rank << endl; + RInside R(argc, argv); + + // if local_rank == 0 then master else worker + R["local_rank"] = world_rank; + + /*Loading Dependencies*/ + std::string r_load_dependencies = + "suppressMessages(library(Rmufits));" + "suppressMessages(library(RedModRphree));" + "source('kin_r_library.R');" + "source('parallel_r_library.R');"; + R.parseEvalQ(r_load_dependencies); + + std::string filesim; + cmdl(1) >> filesim; // <- first positional argument + R["filesim"] = wrap(filesim); // assign a char* (string) to 'filesim' + R.parseEvalQ("source(filesim)"); // eval the init string, ignoring any returns + + std::string out_dir; + if (world_rank == 0) { // only rank 0 initializes goes through the whole initialization + cmdl(2) >> out_dir; // <- second positional argument + R["fileout"] = wrap(out_dir); // assign a char* (string) to 'fileout' + + // Note: R::sim_init() checks if the directory already exists, + // if not it makes it + + // pass the boolean "store_result" to the R process + R["store_result"] = store_result; + + //get timestep vector from grid_init function ... + std::string master_init_code = "mysetup <- master_init(setup=setup)"; + R.parseEval(master_init_code); + + dt_differ = R.parseEval("mysetup$dt_differ"); + MPI_Bcast(&dt_differ, 1, MPI_C_BOOL, 0, MPI_COMM_WORLD); + } else { // workers will only read the setup DataFrame defined by input file + R.parseEval("mysetup <- setup"); + MPI_Bcast(&dt_differ, 1, MPI_C_BOOL, 0, MPI_COMM_WORLD); + } + + if (world_rank==0) { + cout << "CPP: R init done on process with rank " << world_rank << endl; + } + + //initialize chemistry on all processes + std::string init_chemistry_code = "mysetup <- init_chemistry(setup=mysetup)"; + R.parseEval(init_chemistry_code); + + /* Retrieve state_C from R context for MPI buffer generation */ + Rcpp::DataFrame state_C = R.parseEval("mysetup$state_C"); + + /* Init Parallel helper functions */ + R["n_procs"] = world_size-1; /* worker count */ + R["work_package_size"] = work_package_size; + + // Removed additional field for ID in previous versions + if (world_rank == 0) + { + mpi_buffer = (double*) calloc(state_C.nrow() * (state_C.ncol()), sizeof(double)); + } else + { + mpi_buffer = (double*) calloc((work_package_size * (state_C.ncol())) + BUFFER_OFFSET, sizeof(double)); + mpi_buffer_results = (double*) calloc(work_package_size * (state_C.ncol()), sizeof(double)); + } + + if (world_rank==0) { + cout << "CPP: parallel init completed (buffers allocated)!" << endl; + } + + // MDL: pass to R the DHT stuff (basically, only for storing of + // simulation parameters). These 2 variables are always defined: + R["dht_enabled"] = dht_enabled; + R["dht_log"] = dht_logarithm; + + if (dht_enabled) + { + //cout << "\nCreating DHT\n"; + //determine size of dht entries + int dht_data_size = state_C.ncol() * sizeof(double); + int dht_key_size = state_C.ncol() * sizeof(double) + (dt_differ * sizeof(double)); + + //determine bucket count for preset memory usage + //bucket size is key + value + 1 byte for status + int dht_buckets_per_process = dht_size_per_process / (1 + dht_data_size + dht_key_size); + + // MDL : following code moved here from worker.cpp + /*Load significance vector from R setup file (or set default)*/ + bool signif_vector_exists = R.parseEval("exists('signif_vector')"); + if (signif_vector_exists) + { + dht_significant_digits_vector = as>(R["signif_vector"]); + } else + { + dht_significant_digits_vector.assign(dht_object->key_size / sizeof(double), dht_significant_digits); + } + + /*Load property type vector from R setup file (or set default)*/ + bool prop_type_vector_exists = R.parseEval("exists('prop_type')"); + if (prop_type_vector_exists) + { + prop_type_vector = as>(R["prop_type"]); + } else + { + prop_type_vector.assign(dht_object->key_size / sizeof(double), "act"); + } + + if(world_rank == 0) + { + //print only on master, values are equal on all workes + cout << "CPP: dht_data_size: " << dht_data_size << "\n"; + cout << "CPP: dht_key_size: " << dht_key_size << "\n"; + cout << "CPP: dht_buckets_per_process: " << dht_buckets_per_process << endl; + + // MDL: new output on signif_vector and prop_type + if (signif_vector_exists) { + cout << "CPP: using problem-specific rounding digits: " << endl; + R.parseEval("print(data.frame(prop=prop, type=prop_type, digits=signif_vector))"); + } else + { + cout << "CPP: using DHT default rounding digits = " << dht_significant_digits << endl; + } + + // MDL: pass to R the DHT stuff. These variables exist + // only if dht_enabled is true + R["dht_final_signif"] = dht_significant_digits_vector; + R["dht_final_proptype"] = prop_type_vector; + } + + if (dht_strategy == 0) + { + if(world_rank != 0) { + dht_object = DHT_create(dht_comm, dht_buckets_per_process, dht_data_size, dht_key_size, get_md5); + + //storing for access from worker and callback functions + fuzzing_buffer = (double *) malloc (dht_key_size); + } + } else { + dht_object = DHT_create(MPI_COMM_WORLD, dht_buckets_per_process, dht_data_size, dht_key_size, get_md5); + } + + if (world_rank==0) { + cout << "CPP: DHT successfully created!" << endl; + } + } + + + // MDL: store all parameters + if (world_rank==0) { + cout << "CPP: Calling R Function to store calling parameters" << endl; + R.parseEvalQ("StoreSetup(setup=mysetup)"); + + } + + MPI_Barrier(MPI_COMM_WORLD); + + if (world_rank == 0) + { /* This is executed by the master */ + + Rcpp::NumericVector master_send; + Rcpp::NumericVector master_recv; + + sim_a_seq = MPI_Wtime(); + + worker_struct* workerlist = (worker_struct*) calloc(world_size-1, sizeof(worker_struct)); + int need_to_receive; + MPI_Status probe_status; + double* timings; + uint64_t* dht_perfs = NULL; + + int local_work_package_size; + + // a temporary send buffer + double* send_buffer; + send_buffer = (double*) calloc((work_package_size * (state_C.ncol() )) + BUFFER_OFFSET, sizeof(double)); + + // helper variables + int iteration; + double dt, current_sim_time; + + + int n_wp = 1; // holds the actual number of wp which is + // computed later in R::distribute_work_packages() + std::vector wp_sizes_vector; // vector with the sizes of + // each package + + sim_start = MPI_Wtime(); + + //Iteration Count is dynamic, retrieving value from R (is only needed by master for the following loop) + uint32_t maxiter = R.parseEval("mysetup$maxiter"); + + sim_b_seq = MPI_Wtime(); + + cummul_master_seq_pre_loop += sim_b_seq - sim_a_seq; + + /*SIMULATION LOOP*/ + for(uint32_t iter = 1; iter < maxiter+1; iter++) + { + sim_a_seq = MPI_Wtime(); + + cummul_master_send = 0.f; + cummul_master_recv = 0.f; + + + cout << "CPP: Evaluating next time step" << endl; + R.parseEvalQ("mysetup <- master_iteration_setup(mysetup)"); + + /*displaying iteration number, with C++ and R iterator*/ + cout << "CPP: Going through iteration " << iter << endl; + cout << "CPP: R's $iter: "<< ((uint32_t) (R.parseEval("mysetup$iter"))) <<". Iteration" << endl; + + cout << "CPP: Calling Advection" << endl; + + sim_b_transport = MPI_Wtime(); + R.parseEvalQ("mysetup <- master_advection(setup=mysetup)"); + sim_a_transport = MPI_Wtime(); + + + cout << "CPP: Chemistry" << endl; + /*Fallback for sequential execution*/ + sim_b_chemistry = MPI_Wtime(); + + if(world_size == 1) + { + // MDL : the transformation of values into pH and pe + // takes now place in master_advection() so the + // following line is unneeded + // R.parseEvalQ("mysetup$state_T <- RedModRphree::Act2pH(mysetup$state_T)"); + R.parseEvalQ("result <- slave_chemistry(setup=mysetup, data=mysetup$state_T)"); + R.parseEvalQ("mysetup <- master_chemistry(setup=mysetup, data=result)"); + } else { /*send work to workers*/ + + + // NEW: only in the first iteration we call + // R::distribute_work_packages()!! + if (iter==1) + { + R.parseEvalQ("wp_ids <- distribute_work_packages(len=nrow(mysetup$state_T), package_size=work_package_size)"); + + // we only sort once the vector + R.parseEvalQ("ordered_ids <- order(wp_ids)"); + R.parseEvalQ("wp_sizes_vector <- compute_wp_sizes(wp_ids)"); + n_wp = (int) R.parseEval("length(wp_sizes_vector)"); + wp_sizes_vector = as>(R["wp_sizes_vector"]); + cout << "CPP: Total number of work packages: " << n_wp << endl; + R.parseEval("stat_wp_sizes(wp_sizes_vector)"); + + } + + /* shuffle and extract data + MDL: we now apply :Act2pH directly in master_advection + */ + // R.parseEval("tmp <- shuffle_field(RedModRphree::Act2pH(mysetup$state_T), ordered_ids)"); + R.parseEval("tmp <- shuffle_field(mysetup$state_T, ordered_ids)"); + Rcpp::DataFrame chemistry_data = R.parseEval("tmp"); + + convert_R_Dataframe_2_C_buffer(mpi_buffer, chemistry_data); + // cout << "CPP: shuffle_field() done" << endl; + + /* send and receive work; this is done by counting + * the wp */ + int pkg_to_send = n_wp; + int pkg_to_recv = n_wp; + size_t colCount = chemistry_data.ncol(); + int free_workers = world_size-1; + double* work_pointer = mpi_buffer; + sim_c_chemistry = MPI_Wtime(); + + /* visual progress */ + float progress = 0.0; + int barWidth = 70; + + //retrieve data from R runtime + iteration = (int) R.parseEval("mysetup$iter"); + dt = (double) R.parseEval("mysetup$requested_dt"); + current_sim_time = (double) R.parseEval("mysetup$simulation_time-mysetup$requested_dt"); + + int count_pkgs = 0; + + sim_b_seq = MPI_Wtime(); + + sim_c_chemistry = MPI_Wtime(); + + while (pkg_to_recv > 0) // start dispatching work packages + { + /* visual progress */ + progress = (float) (count_pkgs+1)/n_wp; + + cout << "["; + int pos = barWidth * progress; + for (int iprog = 0; iprog < barWidth; ++iprog) { + if (iprog < pos) + cout << "="; + else if (iprog == pos) + cout << ">"; + else + cout << " "; + } + std::cout << "] " << int(progress * 100.0) << " %\r"; + std::cout.flush(); + /* end visual progress */ + + + if (pkg_to_send > 0) { + + master_send_a = MPI_Wtime(); + /*search for free workers and send work*/ + for (int p = 0; p < world_size-1; p++) { + if (workerlist[p].has_work == 0 && pkg_to_send > 0) /* worker is free */ { + + // to enable different work_package_size, set local copy of work_package_size to + // either global work_package size or remaining 'to_send' packages + // to_send >= work_package_size ? local_work_package_size = work_package_size : local_work_package_size = to_send; + + local_work_package_size = (int) wp_sizes_vector[count_pkgs]; + count_pkgs++; + + // cout << "CPP: sending pkg n. " << count_pkgs << " with size " << local_work_package_size << endl; + + /*push pointer forward to next work package, after taking the current one*/ + workerlist[p].send_addr = work_pointer; + + int end_of_wp = local_work_package_size * colCount; + work_pointer = &(work_pointer[end_of_wp]); + + // fill send buffer starting with work_package ... + std::memcpy(send_buffer, workerlist[p].send_addr, (end_of_wp) * sizeof(double)); + // followed by: work_package_size + send_buffer[end_of_wp] = (double) local_work_package_size; + // current iteration of simulation + send_buffer[end_of_wp + 1] = (double) iteration; + // size of timestep in seconds + send_buffer[end_of_wp + 2] = dt; + // current time of simulation (age) in seconds + send_buffer[end_of_wp + 3] = current_sim_time; + // placeholder for work_package_count + send_buffer[end_of_wp + 4] = 0.; + + /* ATTENTION Worker p has rank p+1 */ + MPI_Send(send_buffer, end_of_wp + BUFFER_OFFSET, MPI_DOUBLE, p+1, TAG_WORK, MPI_COMM_WORLD); + + workerlist[p].has_work = 1; + free_workers--; + pkg_to_send -= 1; + } + } + master_send_b = MPI_Wtime(); + cummul_master_send += master_send_b - master_send_a; + } + + + + /*check if there are results to receive and receive them*/ + need_to_receive = 1; + master_recv_a = MPI_Wtime(); + while(need_to_receive && pkg_to_recv > 0) + { + + if (pkg_to_send > 0 && free_workers > 0) + MPI_Iprobe(MPI_ANY_SOURCE, TAG_WORK, MPI_COMM_WORLD, &need_to_receive, &probe_status); + else { + idle_a = MPI_Wtime(); + MPI_Probe(MPI_ANY_SOURCE, TAG_WORK, MPI_COMM_WORLD, &probe_status); + idle_b = MPI_Wtime(); + master_idle += idle_b - idle_a; + } + + if(need_to_receive) + { + int p = probe_status.MPI_SOURCE; + int size; + MPI_Get_count(&probe_status, MPI_DOUBLE, &size); + MPI_Recv(workerlist[p-1].send_addr, size, MPI_DOUBLE, p, TAG_WORK, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + workerlist[p-1].has_work = 0; + pkg_to_recv -= 1; + free_workers++; + } + } + master_recv_b = MPI_Wtime(); + cummul_master_recv += master_recv_b - master_recv_a; + } + + sim_c_seq = MPI_Wtime(); + + // don't overwrite last progress + cout << endl; + + sim_d_chemistry = MPI_Wtime(); + cummul_workers += sim_d_chemistry - sim_c_chemistry; + + convert_C_buffer_2_R_Dataframe(mpi_buffer, chemistry_data); + + R["chemistry_data"] = chemistry_data; + + /* unshuffle results */ + R.parseEval("result <- unshuffle_field(chemistry_data, ordered_ids)"); + + /* do master stuff */ + sim_e_chemistry = MPI_Wtime(); + R.parseEvalQ("mysetup <- master_chemistry(setup=mysetup, data=result)"); + sim_f_chemistry = MPI_Wtime(); + cummul_chemistry_master += sim_f_chemistry - sim_e_chemistry; + + } + sim_a_chemistry = MPI_Wtime(); + + // MDL master_iteration_end just writes on disk state_T and + // state_C after every iteration if the cmdline option + // --ignore-results is not given (and thus the R variable + // store_result is TRUE) + R.parseEvalQ("mysetup <- master_iteration_end(setup=mysetup)"); + + cummul_transport += sim_a_transport - sim_b_transport; + cummul_chemistry += sim_a_chemistry - sim_b_chemistry; + + cout << endl << "CPP: End of *coupling* iteration "<< iter <<"/" << maxiter << endl << endl; + + if (dht_enabled) { + for (int i=1; i < world_size; i++) { + MPI_Send(NULL, 0, MPI_DOUBLE, i, TAG_DHT_STATS, MPI_COMM_WORLD); + } + + // MPI_Barrier(MPI_COMM_WORLD); + + if (dht_snaps == 2) { + std::stringstream outfile; + outfile << out_dir << "/iter_" << std::setfill('0') << std::setw(3) << iter << ".dht"; + for (int i=1; i < world_size; i++) { + MPI_Send(outfile.str().c_str(), outfile.str().size(), MPI_CHAR, i, TAG_DHT_STORE, MPI_COMM_WORLD); + } + MPI_Barrier(MPI_COMM_WORLD); + } + } + + + sim_d_seq = MPI_Wtime(); + + cummul_master_seq_loop += ((sim_b_seq - sim_a_seq) - (sim_a_transport - sim_b_transport)) + (sim_d_seq - sim_c_seq); + master_send.push_back(cummul_master_send, "it_" + to_string(iter)); + master_recv.push_back(cummul_master_recv, "it_" + to_string(iter)); + + } // END SIMULATION LOOP + + sim_end = MPI_Wtime(); + + if (dht_enabled && dht_snaps > 0) { + cout << "CPP: Master: Instruct workers to write DHT to file ..." << endl; + std::string outfile; + outfile = out_dir + ".dht"; + for (int i=1; i < world_size; i++) { + MPI_Send(outfile.c_str(), outfile.size(), MPI_CHAR, i, TAG_DHT_STORE, MPI_COMM_WORLD); + } + MPI_Barrier(MPI_COMM_WORLD); + cout << "CPP: Master: ... done" << endl; + } + + Rcpp::NumericVector phreeqc_time; + Rcpp::NumericVector dht_get_time; + Rcpp::NumericVector dht_fill_time; + Rcpp::IntegerVector phreeqc_counts; + Rcpp::NumericVector idle_worker; + + int phreeqc_tmp; + + timings = (double*) calloc(3, sizeof(double)); + + if (dht_enabled) { + dht_hits = 0; + dht_miss = 0; + dht_collision = 0; + dht_perfs = (uint64_t*) calloc(3, sizeof(uint64_t)); + } + + double idle_worker_tmp; + + for (int p = 0; p < world_size-1; p++) + { + /* ATTENTION Worker p has rank p+1 */ + /* Send termination message to worker */ + MPI_Send(NULL, 0, MPI_DOUBLE, p+1, TAG_FINISH, MPI_COMM_WORLD); + + + MPI_Recv(timings, 3, MPI_DOUBLE, p+1, TAG_TIMING, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + phreeqc_time.push_back(timings[0], "w" + to_string(p+1)); + + MPI_Recv(&phreeqc_tmp, 1, MPI_INT, p+1, TAG_TIMING, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + phreeqc_counts.push_back(phreeqc_tmp, "w" + to_string(p+1)); + + MPI_Recv(&idle_worker_tmp, 1, MPI_DOUBLE, p+1, TAG_TIMING, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + idle_worker.push_back(idle_worker_tmp, "w" + to_string(p+1)); + + if (dht_enabled) + { + dht_get_time.push_back(timings[1], "w" + to_string(p+1)); + dht_fill_time.push_back(timings[2], "w" + to_string(p+1)); + + MPI_Recv(dht_perfs, 3, MPI_UNSIGNED_LONG_LONG, p+1, TAG_DHT_PERF, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + dht_hits += dht_perfs[0]; + dht_miss += dht_perfs[1]; + dht_collision += dht_perfs[2]; + } + } + + R.parseEvalQ("profiling <- list()"); + + R["simtime"] = sim_end - sim_start; + R.parseEvalQ("profiling$simtime <- simtime"); + R["simtime_transport"] = cummul_transport; + R.parseEvalQ("profiling$simtime_transport <- simtime_transport"); + R["simtime_chemistry"] = cummul_chemistry; + R.parseEvalQ("profiling$simtime_chemistry <- simtime_chemistry"); + R["simtime_workers"] = cummul_workers; + R.parseEvalQ("profiling$simtime_workers <- simtime_workers"); + R["simtime_chemistry_master"] = cummul_chemistry_master; + R.parseEvalQ("profiling$simtime_chemistry_master <- simtime_chemistry_master"); + + R["seq_master_prep"] = cummul_master_seq_pre_loop; + R.parseEvalQ("profiling$seq_master_prep <- seq_master_prep"); + R["seq_master_loop"] = cummul_master_seq_loop; + R.parseEvalQ("profiling$seq_master_loop <- seq_master_loop"); + + // R["master_send"] = master_send; + // R.parseEvalQ("profiling$master_send <- master_send"); + // R["master_recv"] = master_recv; + // R.parseEvalQ("profiling$master_recv <- master_recv"); + + R["idle_master"] = master_idle; + R.parseEvalQ("profiling$idle_master <- idle_master"); + R["idle_worker"] = idle_worker; + R.parseEvalQ("profiling$idle_worker <- idle_worker"); + + R["phreeqc_time"] = phreeqc_time; + R.parseEvalQ("profiling$phreeqc <- phreeqc_time"); + + R["phreeqc_count"] = phreeqc_counts; + R.parseEvalQ("profiling$phreeqc_count <- phreeqc_count"); + + if (dht_enabled) + { + R["dht_hits"] = dht_hits; + R.parseEvalQ("profiling$dht_hits <- dht_hits"); + R["dht_miss"] = dht_miss; + R.parseEvalQ("profiling$dht_miss <- dht_miss"); + R["dht_collision"] = dht_collision; + R.parseEvalQ("profiling$dht_collisions <- dht_collision"); + R["dht_get_time"] = dht_get_time; + R.parseEvalQ("profiling$dht_get_time <- dht_get_time"); + R["dht_fill_time"] = dht_fill_time; + R.parseEvalQ("profiling$dht_fill_time <- dht_fill_time"); + } + + + free(workerlist); + free(timings); + + if (dht_enabled) + free(dht_perfs); + + cout << "CPP: Done! Results are stored as R objects into <" << out_dir << "/timings.rds>" << endl; + /*exporting results and profiling data*/ + + std::string r_vis_code; + r_vis_code = "saveRDS(profiling, file=paste0(fileout,'/timings.rds'));"; + R.parseEval(r_vis_code); + } else { /*This is executed by the workers*/ + if (!dht_file.empty()) { + int res = file_to_table((char *) dht_file.c_str()); + if (res != DHT_SUCCESS) { + if (res == DHT_WRONG_FILE) { + if (world_rank == 2) cerr << "CPP: Worker: Wrong File" << endl; + } else { + if (world_rank == 2) cerr << "CPP: Worker: Error in loading current state of DHT from file" << endl; + } + return EXIT_FAILURE; + } else { + if (world_rank == 2) cout << "CPP: Worker: Successfully loaded state of DHT from file " << dht_file << endl; + std::cout.flush(); + } + } + worker_function(R); + free(mpi_buffer_results); + } + + cout << "CPP: finished, cleanup of process " << world_rank << endl; + + if (dht_enabled) + { + + if (dht_strategy == 0) + { + if(world_rank != 0) { + DHT_free(dht_object, NULL, NULL); + } + } else { + DHT_free(dht_object, NULL, NULL); + } + } + + free(mpi_buffer); + MPI_Finalize(); + + if (world_rank==0) { + cout << "CPP: done, bye!" << endl; + } + + exit(0); +} + diff --git a/src/kin_r_library.R b/src/kin_r_library.R new file mode 100644 index 000000000..fe39533ef --- /dev/null +++ b/src/kin_r_library.R @@ -0,0 +1,662 @@ +## Simple function to check file extension. It is needed to check if +## the GridFile is SUM (MUFITS format) or rds/RData +FileExt <- function (x) +{ + pos <- regexpr("\\.([[:alnum:]]+)$", x) + ifelse(pos > -1L, substring(x, pos + 1L), "") +} + +### This function is only called by the master process. It sets up +### the data structures for the coupled simulations and performs the +### first timestep or "iteration zero", possibly involving the phreeqc +### calculation of the initial state (if it is not already provided as +### matrix) and the first Advection iteration +master_init <- function(setup) +{ + msgm("Process with rank 0 reading GRID and MUFITS_sims") + + ## MDL TODO: actually grid and all the MUFITS snapshots should be + ## read and stored only by the master process!! + + ## Setup the directory where we will store the results + verb <- FALSE + if (local_rank==0) { + verb <- TRUE ## verbosity loading MUFITS results + if (!dir.exists(fileout)) { + dir.create(fileout) + msgm("created directory ", fileout) + } else { + msgm("dir ", fileout," already exists, I will overwrite!") + } + if (!exists("store_result")) + msgm("store_result doesn't exist!") + else + msgm("store_result is ", store_result) + } else { + + } + + ## to enhance flexibility, gridfile can be now given in SUM or, + ## already processed, in rds format. We check for extension first. + gridext <- FileExt(setup$gridfile) + if (gridext=="SUM") { + msgm("Generating grid from MUFITS SUM file...") + mufits_grid <- ReadGrid(setup$gridfile, verbose=verb) + } else { + msgm("Reading grid from rds file...") + mufits_grid <- readRDS(setup$gridfile) + } + + ## load all the snapshots at once. The setup argument "snapshots" + ## now can be either a *directory* where the .SUM files are stored + ## or a rds file. + if (dir.exists(setup$snapshots)) { + msgm(" points to a directory; reading from SUM files in there...") + mufits_sims <- LoadMufitsSumRes(dir=setup$snapshots, verbose=verb) + } else { + msgm(" points to a file. Reading as rds...") + mufits_sims <- readRDS(setup$snapshots) + } + + ## Since this function is evaluated by the R process called from + ## the C++ program, we need to make available all these variables + ## to the R parent frame! + assign("mufits_grid", mufits_grid, pos=parent.frame()) + assign("mufits_sims", mufits_sims, pos=parent.frame()) + ## cat(local_rank, "assignement complete\n") + + + ## we calculate the *coupling* iterations + nstep <- length(mufits_sims) + msgm("using", nstep,"flow snapshots") + ## cat(local_rank, "nstep:", nstep, "\n") + ## cat(local_rank, "names:", paste(names(mufits_sims), collate=","), "\n") + + ## we have these snapshots; we output the results of the coupling + ## after each timesteps + timesteps <- diff(sapply(mufits_sims, function(x) {return(x$info)})) + ## cat(local_rank, "timesteps:", paste0(timesteps, collate=" "), "\n") + + dt_differ <- abs(max(timesteps) - min(timesteps)) != 0 + + maxiter <- length(timesteps) + + + ## steady state after last flow snapshot? It is controlled by + ## "setup$prolong", containing an integer which represents the + ## number of further iterations + setup$steady <- FALSE + if ("prolong" %in% names(setup)) { + + last_dt <- timesteps[length(timesteps)] + timesteps <- c(timesteps, rep(last_dt, setup$prolong)) + msgm("simulation prolonged for", setup$prolong, "additional iterations") + ## we set this flag to TRUE to check afterwards which snapshot we need to use at each iteration + setup$steady <- TRUE + setup$last_snapshot <- maxiter + maxiter <- maxiter + setup$prolong + + } + + ## now that we know how many iterations we're gonna have, we can + ## setup the optional output + if (local_rank==0) { + if (is.null(setup$iter_output)) { + ## "iter_output" is not specified: store all iterations + setup$out_save <- seq(1,maxiter) + msgm("setup$iter_output unspecified, storing all iterations") + } else if (setup$iter_output=="all") { + ## "iter_output" is "all": store all iterations + setup$out_save <- seq(1,maxiter) + msgm("storing all iterations") + } else if (is.numeric(setup$iter_output)) { + msgm("storing iterations:", paste(setup$iter_output, collapse=", ")) + setup$out_save <- as.integer(setup$iter_output) + } else if (setup$iter_output=="last") { + msgm("storing only the last iteration") + setup$out_save <- maxiter + } else {## fallback to "all" + setup$out_save <- seq(1,maxiter) + msgm("invalid setup$iter_output: storing all iterations") + } + } + + setup$iter <- 1 + setup$maxiter <- maxiter + setup$timesteps <- timesteps + setup$simulation_time <- 0 + setup$dt_differ <- dt_differ + + if (nrow(setup$bound)==1) { + boundmatAct <- t(RedModRphree::pH2Act(setup$bound)) + msg("formed correct matrix from setup$bound:") + print(boundmatAct) + } else { + boundmatAct <- RedModRphree::pH2Act(setup$bound) + } + + setup$boundmatAct <- boundmatAct + return(setup) +} + +## This function is called by all processes. +## Since the worker processes need state_C in worker.cpp -> worker_function() +## even the worker need to run this code. +## TODO: worker.cpp -> worker_function() --> state_C is only needed to obtain headers +## and size of dataframe ... if this will be adjusted, this function will be +## unnecessary for workers +init_chemistry <- function(setup) +{ + ## setup the chemistry + if (!is.matrix(setup$initsim)) { + msgm("initial state defined through PHREEQC simulation, assuming homogeneous medium!") + tmpfirstrun <- RunPQC(setup$initsim, second=TRUE) + + ## if (local_rank==0){ + ## print(tmpfirstrun) + ## } + remcolnames <- colnames(tmpfirstrun) + if (nrow(tmpfirstrun) > 1) { + ## msgm("Iter 0 selecting second row") + firstrun <- matrix(tmpfirstrun[2,], nrow=1, byrow=TRUE) + colnames(firstrun) <- remcolnames + } else { + firstrun <- tmpfirstrun + } + state_C <- matrix(rep(firstrun,setup$n), byrow=TRUE, ncol=length(setup$prop)) + colnames(state_C) <- colnames(firstrun) + + ## if (local_rank==0) + ## saveRDS(state_C, "initstate.rds") + } else { + msgm("given initial state") + state_C <- setup$initsim + } + ## save state_T and state_C + setup$state_C <- state_C + + return(setup) +} + +## relic code, may be useful in future +finit <- function(setup) +{ + ## saved <- setup$saved + ## state_C <- setup$state_C + ## timesteps <- setup$timesteps + ## out_save <- setup$out_save + ## to_save <- setup$to_save + + ## if (out_save) { + ## msgm("saving ") + ## attr(saved,"savedtimesteps") <- timesteps[save] + ## attr(saved,"timesteps") <- timesteps + ## return(saved) + ## } else { + ## attr(saved,"timesteps") <- timesteps + ## return(state_C) + ## } +} + +## This function, called only by the master, computes the *inner* +## timesteps for transport given the requested simulation timestep and +## the current snapshot (through the Courant Fredrich Levy condition) +## NB: we always iterate: 1)T 2)C +master_iteration_setup <- function(setup) +{ + ## in this version, "grid" and "mufits_sims" are variables already + ## living in the environment of the R process with local_rank 0 + + ## if (local_rank == 0) { + ## cat("Process ", local_rank, "iteration_start\n") + ## } else { + ## cat("Process ", local_rank, "SHOULD NOT call iteration_start!\n") + ## } + + + ## in C++, "iter" starts from 1 + iter <- setup$iter + + next_dt <- setup$timesteps[iter] + + ## setting the current flow snapshot - we have to check if + ## we need prolongation or not + if (setup$steady) { + if (iter > setup$last_snapshot) + snap <- mufits_sims[[setup$last_snapshot]] + else + snap <- mufits_sims[[iter]] + } else + snap <- mufits_sims[[iter]] + + msgm("Current simulation time:", round(setup$simulation_time),"[s] or ", round(setup$simulation_time/3600/24,2), "[day]") + msgm("Requested time step for current iteration:", round(next_dt),"[s] or ", round(next_dt/3600/24,2), "[day]") + setup$simulation_time <- setup$simulation_time+next_dt + msgm("Target simulation time:", round(setup$simulation_time),"[s] or ", round((setup$simulation_time)/3600/24,2), "[day]") + + + ## since the phase and density may change in MUFITS + ## simulations/snapshots, we MUST tell their name at setup. Here + ## we match the right column for both variables + nphase <- match(setup$phase, colnames(snap$conn)) + ndensity <- match(setup$density, colnames(snap$cell)) + + ## the "new cfl" + flux <- snap$conn[, nphase] + cellporvol <- mufits_grid$cell$PORO * mufits_grid$cell$CELLVOL + + ## in MUFITS, positive flux means from CELLID1 to CELLID2 + pos_flux <- ifelse(flux>0, snap$conn$CONNID1, snap$conn$CONNID2) + poro <- mufits_grid$cell$PORO[pos_flux] + vol <- mufits_grid$cell$CELLVOL[snap$conn$CONNID1] + + ## extract the right density for the right fluxes + density <- snap$cell[pos_flux, ndensity] + + ## transform flux from Mufits ton/day to m3/s. This is a VOLUMETRIC FLUX + fluxv <- flux/3600/24*1000/density + + ## now we want the darcy velocity + excl0 <- which(abs(fluxv)<.Machine$double.eps) + ## msgm("excl0"); print(excl0) + ## msgm("length(excl0)"); print(length(excl0)) + + ## The CFL condition is expressed in terms of total flux and total + ## pore volume + cfl <- as.numeric(min(abs(vol*poro/fluxv)[-excl0])) + + if (!is.finite(cfl)) + stop(msgm("CFL is ", cfl,"; quitting")) + + allowed_dt <- setup$Cf*cfl + requested_dt <- next_dt ## target_time - setup$simulation_time + msgm("CFL allows dt of <", round(allowed_dt, 2)," [s]; multiplicator is ", setup$Cf, + "; requested_dt is: ", round(requested_dt, 2)) + + if (requested_dt > allowed_dt) { + inniter <- requested_dt%/%allowed_dt ## integer division + inner_timesteps <- c(rep(allowed_dt, inniter), requested_dt%%allowed_dt) + ## was: inner_timesteps <- c(rep(allowed_dt, inniter), requested_dt - allowed_dt * inniter) + } else { + inner_timesteps <- requested_dt + inniter <- 1 + } + + msgm("Performing ", inniter, " inner iterations") + + setup$inner_timesteps <- inner_timesteps + setup$requested_dt <- requested_dt + setup$allowed_dt <- allowed_dt + setup$inniter <- inniter + setup$iter <- iter + + ## TODO these 3 can be actually spared + setup$fluxv <- fluxv + ## setup$no_transport_conn <- excl0 + setup$cellporvol <- cellporvol + + return(setup) +} + + +## This function, called only by master, stores on disk the last +## calculated time step if store_result is TRUE and increments the +## iteration counter +master_iteration_end <- function(setup) { + iter <- setup$iter + ## MDL Write on disk state_T and state_C after every iteration + ## comprised in setup$out_save + if (store_result) { + if (iter %in% setup$out_save) { + nameout <- paste0(fileout, '/iter_', sprintf('%03d', iter), '.rds') + info <- list(tr_req_dt = as.integer(setup$requested_dt), + tr_allow_dt = setup$allowed_dt, + tr_inniter = as.integer(setup$inniter) + ) + saveRDS(list(T=setup$state_T, C=setup$state_C, + simtime=as.integer(setup$simulation_time), + tr_info=info), file=nameout) + msgm("results stored in <", nameout, ">") + } + } + msgm("done iteration", iter, "/", setup$maxiter) + setup$iter <- setup$iter + 1 + return(setup) +} + +## master: compute advection +master_advection <- function(setup) { + msgm("requested dt=", setup$requested_dt) + concmat <- RedModRphree::pH2Act(setup$state_C) + inner_timesteps <- setup$inner_timesteps + Cf <- setup$Cf + iter <- setup$iter + + ## MDL: not used at the moment, so commenting it out + ## excl <- setup$no_transport_conn + ## msgm("excl"); print(excl) + immobile <- setup$immobile + boundmat <- setup$boundmatAct + + ## setting the current flow snapshot - we have to check if + ## we are in "prolongation" or not + if (setup$steady) { + if (iter > setup$last_snapshot) + snap <- mufits_sims[[setup$last_snapshot]] + else + snap <- mufits_sims[[iter]] + } else + snap <- mufits_sims[[iter]] + + ## conc is a matrix with colnames + if (is.matrix(concmat)) { + if (length(immobile)>0) + val <- concmat[,-immobile] + else + val <- concmat + + ## sort the columns of the matrix matching the names of boundary + sptr <- colnames(val) + inflow <- boundmat[, colnames(boundmat)!="cbound", drop=FALSE] + spin <- colnames(inflow) + ind <- match(spin,sptr) + if (TRUE %in% is.na(ind)) { + msgm("Missing boundary conditions for some species") + val <- cbind(val,matrix(0,ncol=sum(is.na(ind)),nrow=nrow(val) )) + colnames(val) <- c(sptr,spin[is.na(ind)]) + } + + sptr <- colnames(val) + ind <- match(sptr,spin) + ## msgm("ind"); print(ind) + + cnew <- val + + msgm("Computing transport of ", ncol(val), " species") + ## if (local_rank==0) { + ## saveRDS(list(conc=newconc, times=times, activeCells=grid$cell$CELLID[-exclude_cell], fluxv=fluxv), + ## file=paste0("TranspUpwind_", local_rank, ".RData")) + ## } + ## cnew[ boundmat[, 1] ] <- boundmat[, 2] + + ## MDL 20200227: Here was the bug: "excl" refers to + ## CONNECTIONS and not GRID_CELLS!! + + ## cells where we won't update the concentrations: union of + ## boundary and no-flow cells + ## if (length(excl) == 0) + ## exclude_cell <- sort(c(boundmat[, 1])) + ## else + ## exclude_cell <- sort(c(boundmat[, 1], excl)) + exclude_cell <- sort(c(boundmat[, 1])) + + ## msgm("mufits_grid$cell$CELLID[-exclude_cell]:") + ## print(mufits_grid$cell$CELLID[-exclude_cell]) + + for (i in seq(1, ncol(val))) { + ## form a 2-column matrix with cell id and boundary + ## concentration for those elements + newboundmat <- boundmat[,c(1, ind[i]+1)] + ## vector with the old concentrations + concv <- val[,i] + ## apply boundary conditions to the concentration vector + ## (they should stay constant but better safe than sorry) + concv[newboundmat[, 1]] <- newboundmat[, 2] + + ## call the function + cnew[,i] <- CppTransportUpwindIrr(concv = concv, + times = inner_timesteps, + activeCells = mufits_grid$cell$CELLID[-exclude_cell], + fluxv = setup$fluxv, + listconn = mufits_grid$listconn, + porVol = setup$cellporvol) + } + + colnames(cnew) <- colnames(val) + + if ( length(immobile) > 0) { + res <- cbind(cnew, concmat[,immobile]) + } else { + res <- cnew + } + + ## check for negative values. This SHOULD NOT OCCUR and may be + ## the result of numerical dispersion (or bug in my transport + ## code!) + if (any(res <0 )) { + rem_neg <- which(res<0, arr.ind=TRUE) + a <- nrow(rem_neg) + res[res < 0 ] <- 0 + msgm("-> ", a, "concentrations were negative") + print(rem_neg) + } + ## msgm("state_T after iteration", setup$iter, ":") + ## print(head(res)) + } else { + msgm("state_C at iteration", setup$iter, " is not a Matrix, doing nothing!") + } + + ## retransform concentrations H+ and e- into pH and pe + state_T <- RedModRphree::Act2pH(res) + + setup$state_T <- state_T + + return(setup) +} + + +## function for the workers to compute chemistry through PHREEQC +slave_chemistry <- function(setup, data) +{ + base <- setup$base + first <- setup$first + prop <- setup$prop + immobile <- setup$immobile + kin <- setup$kin + ann <- setup$ann + + if (local_rank == 0) { + iter <- setup$iter + timesteps <- setup$timesteps + dt <- timesteps[iter] + } + + state_T <- data ## not the global field, but the work-package + + ## treat special H+/pH, e-/pe cases + state_T <- RedModRphree::Act2pH(state_T) + + ## reduction of the problem + if(setup$reduce) + reduced <- ReduceStateOmit(state_T, omit=setup$ann) + else + reduced <- state_T + + ## if (local_rank==1) { + ## msg("worker", local_rank,"; iter=", iter, "dt=", dt) + ## msg("reduce is", setup$reduce) + ## msg("data:") + ## print(reduced) + ## msg("base:") + ## print(base) + ## msg("first:") + ## print(first) + ## msg("ann:") + ## print(ann) + ## msg("prop:") + ## print(prop) + ## msg("immobile:") + ## print(immobile) + ## msg("kin:") + ## print(kin) + ## } + + ## form the PHREEQC input script for the current work package + inplist <- splitMultiKin(data=reduced, procs=1, base=base, first=first, + ann=ann, prop=prop, minerals=immobile, kin=kin, dt=dt) + + ## if (local_rank==1 & iter==1) + ## RPhreeWriteInp("FirstInp", inplist) + + tmpC <- RunPQC(inplist, procs=1, second=TRUE) + + ## recompose after the reduction + if (setup$reduce) + state_C <- RecomposeState(tmpC, reduced) + else { + state_C <- tmpC + } + + ## the next line is needed since we don't need all columns of + ## PHREEQC output + return(state_C[, prop]) +} + + + +## This function, called by master +master_chemistry <- function(setup, data) +{ + state_T <- setup$state_T + + msgm(" chemistry iteration", setup$iter) + + ## treat special H+/pH, e-/pe cases + state_T <- RedModRphree::Act2pH(state_T) + + ## reduction of the problem + if(setup$reduce) + reduced <- ReduceStateOmit(state_T, omit=setup$ann) + else + reduced <- state_T + + ### inject data from workers + res_C <- data + + rownames(res_C) <- NULL + + ## print(res_C) + + if (nrow(res_C) > nrow(reduced)) { + res_C <- res_C[seq(2,nrow(res_C), by=2),] + } + + ## recompose after the reduction + if (setup$reduce) + state_C <- RecomposeState(res_C, reduced) + else { + state_C <- res_C + } + + setup$state_C <- state_C + setup$reduced <- reduced + + return(setup) +} + + +## Adapted version for "reduction" +ReduceStateOmit <- function (data, omit=NULL, sign=6) +{ + require(mgcv) + + rem <- colnames(data) + if (is.list(omit)) { + indomi <- match(names(omit), colnames(data)) + datao <- data[, -indomi] + } else datao <- data + + datao <- signif(datao, sign) + red <- mgcv::uniquecombs(datao) + inds <- attr(red, "index") + now <- ncol(red) + + + ## reattach the omitted column(s) + ## FIXME: control if more than one ann is present + if (is.list(omit)) { + red <- cbind(red, rep( data[ 1, indomi], nrow(red))) + + colnames(red)[now+1] <- names(omit) + + ret <- red[, colnames(data)] + } else { + ret <- red + } + rownames(ret) <- NULL + attr(ret, "index") <- inds + return(ret) +} + + + +## Attach the name of the calling function to the message displayed on +## R's stdout +msgm <- function (...) +{ + + if (local_rank==0) { + fname <- as.list(sys.call(-1))[[1]] + prefix <- paste0("R: ", fname, " ::") + cat(paste(prefix, ..., "\n")) + } + invisible() +} + + +## Function called by master R process to store on disk all relevant +## parameters for the simulation +StoreSetup <- function(setup) { + + to_store <- vector(mode="list", length=5) + names(to_store) <- c("Sim", "Flow", "Transport", "Chemistry", "DHT") + + ## read the setup R file, which is sourced in kin.cpp + tmpbuff <- file(filesim, "r") + setupfile <- readLines(tmpbuff) + close.connection(tmpbuff) + + to_store$Sim <- setupfile + + to_store$Flow <- list( + snapshots = setup$snapshots, + gridfile = setup$gridfile, + phase = setup$phase, + density = setup$density, + dt_differ = setup$dt_differ, + prolong = setup$prolong, + maxiter = setup$maxiter, + saved_iter = setup$iter_output, + out_save = setup$out_save ) + + to_store$Transport <- list( + boundary = setup$bound, + Cf = setup$Cf, + prop = setup$prop, + immobile = setup$immobile, + reduce = setup$reduce ) + + to_store$Chemistry <- list( + nprocs = n_procs, + wp_size = work_package_size, + base = setup$base, + first = setup$first, + init = setup$initsim, + db = db, + kin = setup$kin, + ann = setup$ann) + + if (dht_enabled) { + to_store$DHT <- list( + enabled = dht_enabled, + log = dht_log, + signif = dht_final_signif, + proptype = dht_final_proptype) + + } else { + to_store$DHT <- FALSE + } + + saveRDS(to_store, file=paste0(fileout,'/setup.rds')) + msgm("initialization stored in ", paste0(fileout,'/setup.rds')) +} diff --git a/src/kin_r_library_warnings.R b/src/kin_r_library_warnings.R new file mode 100644 index 000000000..f7ad279b0 --- /dev/null +++ b/src/kin_r_library_warnings.R @@ -0,0 +1,584 @@ +## Simple function to check file extension. It is needed to check if +## the GridFile is SUM (MUFITS format) or rds/RData +FileExt <- function (x) +{ + pos <- regexpr("\\.([[:alnum:]]+)$", x) + ifelse(pos > -1L, substring(x, pos + 1L), "") +} + +### This function is only called by the master process. It sets up +### the data structures for the coupled simulations and performs the +### first timestep or "iteration zero", possibly involving the phreeqc +### calculation of the initial state (if it is not already provided as +### matrix) and the first Advection iteration +master_init <- function(setup) +{ + msgm("Process with rank 0 reading GRID and MUFITS_sims") + + ## MDL TODO: actually grid and all the MUFITS snapshots should be + ## read and stored only by the master process!! + + ## Setup the directory where we will store the results + verb <- FALSE + if (local_rank==0) { + verb <- TRUE ## verbosity loading MUFITS results + if (!dir.exists(fileout)) { + dir.create(fileout) + msgm("created directory ", fileout) + } else { + msgm("dir ", fileout," already exists, I will overwrite!") + } + if (!exists("store_result")) + msgm("store_result doesn't exist!") + else + msgm("store_result is ", store_result) + + } else { + + } + + ## to enhance flexibility, gridfile can be now given in SUM or, + ## already processed, in rds format. We check for extension first. + gridext <- FileExt(setup$gridfile) + if (gridext=="SUM") { + msgm("Generating grid from MUFITS SUM file...") + mufits_grid <- ReadGrid(setup$gridfile, verbose=verb) + } else { + msgm("Reading grid from rds file...") + mufits_grid <- readRDS(setup$gridfile) + } + + ## load all the snapshots at once. The setup argument "snapshots" + ## now can be either a *directory* where the .SUM files are stored + ## or a rds file. + if (dir.exists(setup$snapshots)) { + msgm(" points to a directory; reading from SUM files in there...") + mufits_sims <- LoadMufitsSumRes(dir=setup$snapshots, verbose=verb) + } else { + msgm(" points to a file. Reading as rds...") + mufits_sims <- readRDS(setup$snapshots) + } + + ## Since this function is evaluated by the R process called from + ## the C++ program, we need to make available all these variables + ## to the R parent frame! + assign("mufits_grid", mufits_grid, pos=parent.frame()) + assign("mufits_sims", mufits_sims, pos=parent.frame()) + ## cat(local_rank, "assignement complete\n") + + + ## we calculate the *coupling* iterations + nstep <- length(mufits_sims) + msgm("using", nstep,"flow snapshots") + ## cat(local_rank, "nstep:", nstep, "\n") + ## cat(local_rank, "names:", paste(names(mufits_sims), collate=","), "\n") + + ## we have these snapshots; we output the results of the coupling + ## after each timesteps + timesteps <- diff(sapply(mufits_sims, function(x) {return(x$info)})) + ## cat(local_rank, "timesteps:", paste0(timesteps, collate=" "), "\n") + + maxiter <- length(timesteps) + + ## steady case not treated in this version!!!! + steady <- FALSE + + setup$iter <- 1 + setup$maxiter <- maxiter + setup$timesteps <- timesteps + setup$simulation_time <- 0 + + setup$steady <- steady + + if (nrow(setup$bound)==1) { + boundmatAct <- t(RedModRphree::pH2Act(setup$bound)) + msg("formed correct matrix from setup$bound:") + print(boundmatAct) + } else { + boundmatAct <- RedModRphree::pH2Act(setup$bound) + } + + setup$boundmatAct <- boundmatAct + return(setup) +} + +## This function is called by all processes. +## Since the worker processes need state_C in worker.cpp -> worker_function() +## even the worker need to run this code. +## TODO: worker.cpp -> worker_function() --> state_C is only needed to obtain headers +## and size of dataframe ... if this will be adjusted, this function will be +## unnecessary for workers +init_chemistry <- function(setup) +{ + ## setup the chemistry + if (!is.matrix(setup$initsim)) { + msgm("initial state defined through PHREEQC simulation, assuming homogeneous medium!") + tmpfirstrun <- RunPQC_Warnings(setup$initsim, second=TRUE) + + ## if (local_rank==0){ + ## print(tmpfirstrun) + ## } + remcolnames <- colnames(tmpfirstrun) + if (nrow(tmpfirstrun) > 1) { + ## msgm("Iter 0 selecting second row") + firstrun <- matrix(tmpfirstrun[2,], nrow=1, byrow=TRUE) + colnames(firstrun) <- remcolnames + } else { + firstrun <- tmpfirstrun + } + state_C <- matrix(rep(firstrun,setup$n), byrow=TRUE, ncol=length(setup$prop)) + colnames(state_C) <- colnames(firstrun) + + ## if (local_rank==0) + ## saveRDS(state_C, "initstate.rds") + } else { + msgm("given initial state") + state_C <- setup$initsim + } + ## save state_T and state_C + setup$state_C <- state_C + + return(setup) +} + +## relic code, may be useful in future +finit <- function(setup) +{ + ## saved <- setup$saved + ## state_C <- setup$state_C + ## timesteps <- setup$timesteps + ## out_save <- setup$out_save + ## to_save <- setup$to_save + + ## if (out_save) { + ## msgm("saving ") + ## attr(saved,"savedtimesteps") <- timesteps[save] + ## attr(saved,"timesteps") <- timesteps + ## return(saved) + ## } else { + ## attr(saved,"timesteps") <- timesteps + ## return(state_C) + ## } +} + +## This function, called only by the master, computes the *inner* +## timesteps for transport given the requested simulation timestep and +## the current snapshot (through the Courant Fredrich Levy condition) +## NB: we always iterate: 1)T 2)C +master_iteration_setup <- function(setup) +{ + ## in this version, "grid" and "mufits_sims" are variables already + ## living in the environment of the R process with local_rank 0 + + ## if (local_rank == 0) { + ## cat("Process ", local_rank, "iteration_start\n") + ## } else { + ## cat("Process ", local_rank, "SHOULD NOT call iteration_start!\n") + ## } + + + ## in C++, "iter" starts from 1 + iter <- setup$iter + + next_dt <- setup$timesteps[iter] + + ## setting the current snapshot + snap <- mufits_sims[[iter]] + + msgm("Current simulation time:", round(setup$simulation_time),"[s] or ", round(setup$simulation_time/3600/24,2), "[day]") + msgm("Time step for current iteration:", round(next_dt),"[s] or ", round(next_dt/3600/24,2), "[day]") + setup$simulation_time <- setup$simulation_time+next_dt + msgm("Target simulation time:", round(setup$simulation_time),"[s] or ", round((setup$simulation_time)/3600/24,2), "[day]") + + + ## since the phase and density may change in MUFITS + ## simulations/snapshots, we MUST tell their name at setup. Here + ## we match the right column for both variables + nphase <- match(setup$phase, colnames(snap$conn)) + ndensity <- match(setup$density, colnames(snap$cell)) + + ## the "new cfl" + flux <- snap$conn[, nphase] + cellporvol <- mufits_grid$cell$PORO * mufits_grid$cell$CELLVOL + + ## in MUFITS, positive flux means from CELLID1 to CELLID2 + pos_flux <- ifelse(flux>0, snap$conn$CONNID1, snap$conn$CONNID2) + poro <- mufits_grid$cell$PORO[pos_flux] + vol <- mufits_grid$cell$CELLVOL[snap$conn$CONNID1] + + ## extract the right density for the right fluxes + density <- snap$cell[pos_flux, ndensity] + + ## transform flux from Mufits ton/day to m3/s. This is a VOLUMETRIC FLUX + fluxv <- flux/3600/24*1000/density + + ## now we want the darcy velocity + excl0 <- which(abs(fluxv)<.Machine$double.eps) + ## msgm("excl0"); print(excl0) + ## msgm("length(excl0)"); print(length(excl0)) + ## The CFL condition is expressed in terms of total flux and total + ## pore volume + cfl <- as.numeric(min(abs(vol*poro/fluxv)[-excl0])) + + if (!is.finite(cfl)) + stop(msgm("CFL is ", cfl,"; quitting")) + + allowed_dt <- setup$Cf*cfl + requested_dt <- next_dt ## target_time - setup$simulation_time + msgm("CFL allows dt of <", round(cfl, 2)," [s]; multiplicator is ", setup$Cf, + "; requested_dt is: ", round(requested_dt, 2)) + + if (requested_dt > allowed_dt) { + inniter <- floor(requested_dt/allowed_dt) + inner_timesteps <- c(rep(allowed_dt, inniter), requested_dt - allowed_dt * inniter) + msgm("Performing ", inniter, " inner iterations") + } else { + inner_timesteps <- requested_dt + inniter <- 1 + } + + setup$inner_timesteps <- inner_timesteps + setup$requested_dt <- requested_dt + setup$allowed_dt <- allowed_dt + setup$inniter <- inniter + setup$iter <- iter + setup$fluxv <- fluxv + setup$no_transport_cells <- excl0 + setup$cellporvol <- cellporvol + + return(setup) +} + + +## This function, called only by master, stores on disk the last +## calculated time step if store_result is TRUE and increments the +## iteration counter +master_iteration_end <- function(setup) { + iter <- setup$iter + ## MDL Write on disk state_T and state_C after every iteration + if (store_result) { + nameout <- paste0(fileout, '/iter_', sprintf('%03d', iter), '.rds') + saveRDS(list(T=setup$state_T, C=setup$state_C), file=nameout) + msgm("results stored in <", nameout, ">") + } + msgm("done iteration", iter, "/", setup$maxiter) + setup$iter <- setup$iter + 1 + return(setup) +} + +## master: compute advection +master_advection <- function(setup) { + msgm("requested dt=", setup$requested_dt) + concmat <- RedModRphree::pH2Act(setup$state_C) + inner_timesteps <- setup$inner_timesteps + Cf <- setup$Cf + iter <- setup$iter + excl <- setup$no_transport_cells + ## msgm("excl"); print(excl) + immobile <- setup$immobile + boundmat <- setup$boundmatAct + snap <- mufits_sims[[setup$iter]] + + ## conc is a matrix with colnames + if (is.matrix(concmat)) { + if (length(immobile)>0) + val <- concmat[,-immobile] + else + val <- concmat + + ## sort the columns of the matrix matching the names of boundary + sptr <- colnames(val) + inflow <- boundmat[, colnames(boundmat)!="cbound", drop=FALSE] + spin <- colnames(inflow) + ind <- match(spin,sptr) + if (TRUE %in% is.na(ind)) { + msgm("Missing boundary conditions for some species") + val <- cbind(val,matrix(0,ncol=sum(is.na(ind)),nrow=nrow(val) )) + colnames(val) <- c(sptr,spin[is.na(ind)]) + } + + sptr <- colnames(val) + ind <- match(sptr,spin) + ## msgm("ind"); print(ind) + + cnew <- val + + msgm("Computing transport of ", ncol(val), " species") + ## if (local_rank==0) { + ## saveRDS(list(conc=newconc, times=times, activeCells=grid$cell$CELLID[-exclude_cell], fluxv=fluxv), + ## file=paste0("TranspUpwind_", local_rank, ".RData")) + ## } + ## cnew[ boundmat[, 1] ] <- boundmat[, 2] + + ## cells where we won't update the concentrations: union of + ## boundary and no-flow cells + if (length(excl) == 0) + exclude_cell <- sort(c(boundmat[, 1])) + else + exclude_cell <- sort(c(boundmat[, 1], excl)) + + ## msgm("mufits_grid$cell$CELLID[-excludecell]:") + ## print(mufits_grid$cell$CELLID[-exclude_cell]) + + for (i in seq(1, ncol(val))) { + ## form a 2-column matrix with cell id and boundary + ## concentration for those elements + newboundmat <- boundmat[,c(1, ind[i]+1)] + ## vector with the old concentrations + concv <- val[,i] + ## apply boundary conditions to the concentration vector + ## (they should stay constant but better safe than sorry) + concv[newboundmat[, 1]] <- newboundmat[, 2] + + ## call the function + cnew[,i] <- CppTransportUpwindIrr(concv = concv, + times = inner_timesteps, + activeCells = mufits_grid$cell$CELLID[-exclude_cell], + fluxv = setup$fluxv, + listconn = mufits_grid$listconn, + porVol = setup$cellporvol) + } + + colnames(cnew) <- colnames(val) + + if ( length(immobile) > 0) { + res <- cbind(cnew, concmat[,immobile]) + } else { + res <- cnew + } + + ## check for negative values. This SHOULD NOT OCCUR and may be + ## the result of numerical dispersion (or bug in my transport + ## code!) + if (any(res <0 )) { + a <- length(res[res < 0 ]) + res[res < 0 ] <- 0 + msgm("-> ", a, "concentrations were negative") + } + ## msgm("state_T after iteration", setup$iter, ":") + ## print(head(res)) + } else { + msgm("state_C at iteration", setup$iter, " is not a Matrix, doing nothing!") + } + + setup$state_T <- res + + return(setup) +} + + +## function for the workers to compute chemistry through PHREEQC +slave_chemistry <- function(setup, data) +{ + base <- setup$base + first <- setup$first + prop <- setup$prop + immobile <- setup$immobile + kin <- setup$kin + ann <- setup$ann + + if (local_rank == 0) { + iter <- setup$iter + timesteps <- setup$timesteps + dt <- timesteps[iter] + } + + state_T <- data ## not the global field, but the work-package + + ## treat special H+/pH, e-/pe cases + state_T <- RedModRphree::Act2pH(state_T) + + ## reduction of the problem + if(setup$reduce) + reduced <- ReduceStateOmit(state_T, omit=setup$ann) + else + reduced <- state_T + + ## if (local_rank==1) { + ## msg("worker", local_rank,"; iter=", iter, "dt=", dt) + ## msg("reduce is", setup$reduce) + ## msg("data:") + ## print(reduced) + ## msg("base:") + ## print(base) + ## msg("first:") + ## print(first) + ## msg("ann:") + ## print(ann) + ## msg("prop:") + ## print(prop) + ## msg("immobile:") + ## print(immobile) + ## msg("kin:") + ## print(kin) + ## } + + ## form the PHREEQC input script for the current work package + inplist <- splitMultiKin(data=reduced, procs=1, base=base, first=first, + ann=ann, prop=prop, minerals=immobile, kin=kin, dt=dt) + + ## if (local_rank==1 & iter==1) + ## RPhreeWriteInp("FirstInp", inplist) + + tmpC <- RunPQC_Warnings(inplist, procs=1, second=TRUE) + + ## recompose after the reduction + if (setup$reduce) + state_C <- RecomposeState(tmpC, reduced) + else { + state_C <- tmpC + } + + ## the next line is needed since we don't need all columns of + ## PHREEQC output + return(state_C[, prop]) +} + + + +## This function, called by master +master_chemistry <- function(setup, data) +{ + state_T <- setup$state_T + + msgm(" chemistry iteration", setup$iter) + + ## treat special H+/pH, e-/pe cases + state_T <- RedModRphree::Act2pH(state_T) + + ## reduction of the problem + if(setup$reduce) + reduced <- ReduceStateOmit(state_T, omit=setup$ann) + else + reduced <- state_T + + ### inject data from workers + res_C <- data + + rownames(res_C) <- NULL + + ## print(res_C) + + if (nrow(res_C) > nrow(reduced)) { + res_C <- res_C[seq(2,nrow(res_C), by=2),] + } + + ## recompose after the reduction + if (setup$reduce) + state_C <- RecomposeState(res_C, reduced) + else { + state_C <- res_C + } + + setup$state_C <- state_C + setup$reduced <- reduced + + return(setup) +} + + +## Adapted version for "reduction" +ReduceStateOmit <- function (data, omit=NULL, sign=6) +{ + require(mgcv) + + rem <- colnames(data) + if (is.list(omit)) { + indomi <- match(names(omit), colnames(data)) + datao <- data[, -indomi] + } else datao <- data + + datao <- signif(datao, sign) + red <- mgcv::uniquecombs(datao) + inds <- attr(red, "index") + now <- ncol(red) + + + ## reattach the omitted column(s) + ## FIXME: control if more than one ann is present + if (is.list(omit)) { + red <- cbind(red, rep( data[ 1, indomi], nrow(red))) + + colnames(red)[now+1] <- names(omit) + + ret <- red[, colnames(data)] + } else { + ret <- red + } + rownames(ret) <- NULL + attr(ret, "index") <- inds + return(ret) +} + + + +## Attach the name of the calling function to the message displayed on +## R's stdout +msgm <- function (...) +{ + + if (local_rank==0) { + fname <- as.list(sys.call(-1))[[1]] + prefix <- paste0("R: ", fname, " ::") + cat(paste(prefix, ..., "\n")) + } + invisible() +} + + +RunPQC_Warnings <- function (input, procs = 1, second = TRUE) +{ + + .runPQC <- function(input, onlysecond) { + require(phreeqc) + phreeqc::phrSetErrorStringsOn(TRUE) + error_count <- phreeqc::phrRunString(input) + warnings <- phreeqc::phrGetWarningStrings() + checked_warn <- CheckWarnings(warnings) + + if (is.null(error_count)) { + out <- phreeqc::phrGetSelectedOutput()[[1]] + nso <- colnames(out) + nso <- sub("..mol.kgw.", "", nso, fixed = TRUE) + nso <- sub(".mol.kgw.", "", nso, fixed = TRUE) + nso <- sub("^k_", "", nso) + nso <- sub(".g.", "(g)", nso, fixed = TRUE) + if (second) + out <- out[seq(2, nrow(out), by = 2), ] + colnames(out) <- nso + return(data.matrix(out)) + } else { + msgm("Error in simmulation!") + cat(phrGetErrorStrings(), sep="\n") + } + } + + if (!is.list(input)) { + if (is.character(input)) { + res <- .runPQC(input, onlysecond = second) + } else { + stopmsg("something wrong with the input, dying!") + } + } + else { + res <- foreach(i = seq_along(input), .combine = rbind) %dopar% + .runPQC(input[[i]], onlysecond = second) + } + return(res) +} + + +CheckWarnings <- function(warnings) +{ + ## get rid of "Negative moles in solution" warnings + recover <- grep("Negative moles", warnings, fixed=TRUE) + warnings <- warnings[-recover] + + lw <- length(warnings) + if (lw>0) { + ## nfile <- paste0("warnings_", local_rank, ".txt") + nfile <- "Warnings.txt" + cat(warnings, file=nfile, sep="\n", append=TRUE) + } + return(lw) +} diff --git a/src/mdl_quint_kin.dat b/src/mdl_quint_kin.dat new file mode 100644 index 000000000..f4835bcdf --- /dev/null +++ b/src/mdl_quint_kin.dat @@ -0,0 +1,2625 @@ +SOLUTION_MASTER_SPECIES +O H2O 0.0 15.9994 15.9994 +Al Al+3 0.0 26.9815 26.9815 +Ba Ba+2 0.0 137.327 137.327 +Br Br- 0.0 79.904 79.904 +C HCO3- 0.0 12.011 12.011 +Ca Ca+2 0.0 40.078 40.078 +Cl Cl- 0.0 35.4527 35.4527 +Cr Cr+3 0.0 51.9961 51.9961 +Cs Cs+ 0.0 132.905 132.905 +F F- 0.0 18.9984 18.9984 +Fe Fe+2 0.0 55.847 55.847 +H H+ 0.0 1.00794 1.00794 +I I- 0.0 126.904 126.904 +K K+ 0.0 39.0983 39.0983 +Li Li+ 0.0 6.941 6.941 +Mg Mg+2 0.0 24.305 24.305 +Mn Mn+2 0.0 54.938 54.938 +Mo MoO4-2 0.0 95.94 95.94 +N NO3- 0.0 14.0067 14.0067 +Na Na+ 0.0 22.9898 22.9898 +P HPO4-2 0.0 30.9738 30.9738 +S SO4-2 0.0 32.066 32.066 +Si SiO2 0.0 28.0855 28.0855 +Sr Sr+2 0.0 87.62 87.62 +B B(OH)3 0.0 10.811 10.811 +Co Co+2 0.0 58.9332 58.9332 +Cu Cu+2 0.0 63.546 63.546 +Ni Ni+2 0.0 58.6934 58.6934 +Zn Zn+2 0.0 65.39 65.39 +E e- 0.0 0 0 +O(-2) H2O 0.0 0.0 +H(1) H+ -1.0 0.0 +O(0) O2 0.0 O +H(0) H2 0.0 H +SOLUTION_SPECIES +H2O = H2O + log_k 0.0 +Al+3 = Al+3 + log_k 0.0 +Ba+2 = Ba+2 + log_k 0.0 +Br- = Br- + log_k 0.0 +HCO3- = HCO3- + log_k 0.0 +Ca+2 = Ca+2 + log_k 0.0 +Cl- = Cl- + log_k 0.0 +Cr+3 = Cr+3 + log_k 0.0 +Cs+ = Cs+ + log_k 0.0 +F- = F- + log_k 0.0 +Fe+2 = Fe+2 + log_k 0.0 +H+ = H+ + log_k 0.0 +I- = I- + log_k 0.0 +K+ = K+ + log_k 0.0 +Li+ = Li+ + log_k 0.0 +Mg+2 = Mg+2 + log_k 0.0 +Mn+2 = Mn+2 + log_k 0.0 +MoO4-2 = MoO4-2 + log_k 0.0 +NO3- = NO3- + log_k 0.0 +Na+ = Na+ + log_k 0.0 +HPO4-2 = HPO4-2 + log_k 0.0 +SO4-2 = SO4-2 + log_k 0.0 +SiO2 = SiO2 + log_k 0.0 +Sr+2 = Sr+2 + log_k 0.0 +B(OH)3 = B(OH)3 + log_k 0.0 +Co+2 = Co+2 + log_k 0.0 +Cu+2 = Cu+2 + log_k 0.0 +Ni+2 = Ni+2 + log_k 0.0 +Zn+2 = Zn+2 + log_k 0.0 +e- = e- + log_k 0.0 +Cl- + 4 H2O = ClO4- + 8 e- + 8 H+ + logk -187.717 + -analytical 98.3626 0.00897085 -57720 -38.4526 -901.663 +Cr+3 + 4 H2O = CrO4-- + 8 H+ + 3 e- + logk -74.218 + -analytical 35.5542 -0.0286437 -26783.2 -4.61493 -420.12 +Fe+2 = Fe+++ + e- + logk -13.011 + -analytical -43.28 -0.0126166 -463.442 14.3792 -7.61902 +2 e- + 2 H+ = H2 + logk -3.1047 + -analytical -44.4193 0.0008034 332.449 16.1517 5.85343 +HPO4-2 + 2 e- + 2 H+ = HPO3-- + H2O + logk -7.0529 + -analytical -44.9263 -0.000730404 -1740.87 17.7552 -26.6891 +9 H+ + SO4-2 + 8 e- = HS- + 4 H2O + logk 33.6909 + -analytical -20.9734 0.0282619 8351.27 7.37542 132.989 +Mn+2 = Mn+++ + e- + logk -25.5088 + -analytical -30.985 -0.0118433 -3758.26 8.73221 -59.0759 +Mn+2 + 4 H2O = MnO4-- + 8 H+ + 4 e- + logk -118.345 + -analytical 5.68483 -0.0404547 -36066.9 3.62834 -565.402 +10 H+ + NO3- + 8 e- = NH4+ + 3 H2O + logk 119.149 + -analytical -27.4421 0.0202152 35667 8.46459 558.747 +NO3- + 2 e- + 2 H+ = NO2- + H2O + logk 27.7671 + -analytical -18.8877 -0.000839634 8453.33 7.49673 132.138 +SO4-2 + 2 e- + 2 H+ = SO3-- + H2O + logk -3.6224 + -analytical -27.6927 -0.00236598 -285.739 10.4006 -4.10093 +7 H+ + B(OH)3 + 8 e- = BH4- + 3 H2O + logk -65.0961 + -analytical -190.98 -0.0367462 -15374.9 76.1353 -240.047 +HPO4-2 = PO4-3 + H+ + logk -12.3218 + -analytical -59.3704 -0.0321228 -524.014 23.5855 -9.61634 +2 H2O = O2 + 4 e- + 4 H+ + logk -86.0039 + -analytical 1.05677 0.00424712 -25007.3 -1.79339 -390.168 +H2O = OH- + H+ + logk -13.9951 + -analytical -67.5047 -0.0306179 -1990.13 28.0036 -32.4111 +HCO3- = CO3-2 + H+ + logk -10.3288 + -analytical -69.9531 -0.0335248 -70.9884 28.2225 -2.60731 +Al+3 + 2 H2O = AlO2- + 4 H+ + logk -22.879 +Al+3 + H2O = AlOH+2 + H+ + logk -4.957 +Al+3 + H2O = AlO+ + 2 H+ + logk -10.594 +Al+3 + 2 H2O = HAlO2 + 3 H+ + logk -16.432 +Al+3 + F- = AlF+2 + logk 7 +Al+3 + 2 F- = AlF2+ + logk 12.6 +Al+3 + 3 F- = AlF3 + logk 16.7 +Al+3 + 4 F- = AlF4- + logk 19.4 +H2O + B(OH)3 = B(OH)4- + H+ + logk -9.239 +3 B(OH)3 = B3O3(OH)4- + 2 H2O + H+ + logk -7.6188 +4 B(OH)3 = B4O5(OH)4-2 + 3 H2O + 2 H+ + logk -16.2554 +Ca+2 + B(OH)3 + H2O = CaB(OH)4+ + H+ + logk -7.5886 +Ca+2 + HCO3- = CaCO3 + H+ + logk -7.0017 + -analytical 236.013 0.0560188 -8711.9 -93.1438 -134.632 +Ca+2 + H2O = CaOH+ + H+ + logk -12.8333 + -analytical 57.5113 0.0114734 -5866.96 -21.8569 -91.3547 +Ca+2 + SO4-2 = CaSO4 + logk 1.6503 + -analytical 559.886 0.138712 -17440.1 -218.656 -279.955 +Ca+2 + Cl- = CaCl+ + logk -0.7659 + -analytical -12.4607 0.020884 1632.89 -0.00354466 26.5103 +Ca+2 + 2 Cl- = CaCl2 + logk -16.4677 + -analytical 42.4008 -0.0281976 -15074.2 0.040552 -244.698 +H+ + HCO3- = CO2 + H2O + logk 6.3447 + -analytical -10.5326 0.0217452 2521.59 0.791018 40.7333 +2 CrO4-2 + 2 H+ = Cr2O7-2 + H2O + logk 14.7024 + -analytical 153.121 0.0684345 -1593.19 -62.0073 -21.8141 +Cl- + Fe+2 = FeCl+ + logk -0.1605 + -analytical 81.4828 0.0375106 -1449.35 -35.5403 -21.0568 +Fe+2 + HCO3- = FeHCO3+ + logk 1.47 +Fe+2 + HCO3- = FeCO3 + H+ + logk -4.8788 +Fe+2 + 2 CO3-2 = Fe(CO3)2-2 + logk 7.16 +Fe+2 + H2O = FeOH+ + H+ + logk -9.51 +Fe+2 + 2 H2O = Fe(OH)2 + 2 H+ + logk -20.61 +Fe+3 + H2O = FeOH+2 + H+ + logk -2.1 +Fe+3 + 2 H2O = Fe(OH)2+ + 2 H+ + logk -6.3 +Fe+3 + 3 H2O = Fe(OH)3 + 3 H+ + logk -14.3 +Fe+3 + 4 H2O = Fe(OH)4- + 4 H+ + logk -22.3 +Fe+3 + SO4-2 = FeSO4+ + logk 4.27 +Fe+3 + 2 SO4-2 = Fe(SO4)2- + logk 6.11 +Cl- + Fe+3 = FeCl+2 + logk 1.28 +2 Cl- + Fe+3 = FeCl2+ + logk 1.16 +F- + Fe+2 = FeF+ + logk 1.4272 + -analytical 93.8061 0.0389135 -1761.64 -39.6238 -25.8207 +F- + Fe+3 = FeF+2 + logk 6.03 +Fe+3 + 2 F- = FeF2+ + logk 10.66 +H+ + SO4-2 = HSO4- + logk 1.9791 + -analytical 49.615 0.0303676 -1155.72 -21.3332 -16.3809 +H2O + SiO2 = HSiO3- + H+ + logk -9.8419 + -analytical -105.911 -0.0389391 639.77 42.6507 9.89524 +H+ + HPO4-2 = H2PO4- + logk 7.2054 + -analytical 82.1441 0.0340763 -1042.97 -32.9686 -14.8494 +HPO4-2 + 2 H+ = H3PO4 + logk 9.3751 + -analytical 183.79 0.0673192 -3778.88 -73.4595 -56.2133 +HCO3- + Mg+2 = MgCO3 + H+ + logk -7.3499 + -analytical 240.251 0.0562181 -8602.15 -95.1687 -132.792 +HCO3- + Mg+2 = MgHCO3+ + logk 1.0357 + -analytical 42.6713 0.0302909 -76.1074 -20.3612 0.594294 +H2O + Mg+2 = MgOH+ + H+ + logk -11.6825 + -analytical -1.23462 0.00322632 -3070.18 -0.445617 -47.4202 +NH4+ = NH3 + H+ + logk -9.241 + -analytical 14.5234 0.00505255 -3044.62 -6.08535 -47.4222 +Mg+2 + B(OH)3 + H2O = MgB(OH)4+ + H+ + logk -7.8397 +CrO4-2 + Ni+2 = NiCrO4 + logk 2.4 +CrO4-2 + 2 H+ = H2CrO4 + logk 6.31 +CrO4-2 + H+ = HCrO4- + logk 6.5238 + -analytical 74.9937 0.0356022 -1012.39 -30.5783 -14.1396 +Br- + Cr+3 = CrBr+2 + logk -4.7427 + -analytical 76.6031 0.0381767 -3303 -32.9854 -49.7402 +Cr+3 + H2O = CrOH+2 + H+ + logk -3.57 +Cr+3 + 2 H2O = Cr(OH)2+ + 2 H+ + logk -9.84 +Cr+3 + 3 H2O = Cr(OH)3 + 3 H+ + logk -16.19 +Cr+3 + 4 H2O = Cr(OH)4- + 4 H+ + logk -27.65 +2 Cr+3 + 6 H2O = Cr2O2(OH)4-2 + 8 H+ + logk -51.9302 +H2O + Ni+2 = NiOH+ + H+ + logk -9.5 +Ni+2 + 2 H2O = Ni(OH)2 + 2 H+ + logk -18 +Ni+2 + 3 H2O = Ni(OH)3- + 3 H+ + logk -29.7 +Ni+2 + 4 H2O = Ni(OH)4-2 + 4 H+ + logk -44.96 +Br- + Ni+2 = NiBr+ + logk -0.37 +Ni+2 + HCO3- = NiCO3 + H+ + logk -6.3288 +F- + Ni+2 = NiF+ + logk 1.12 + -analytical 102.353 0.0397234 -2187.25 -42.7229 -32.4787 +HCO3- + Ni+2 = NiHCO3+ + logk 1 +Ni+2 + HPO4-2 = NiHPO4 + logk 2.934 +Ni+2 + HPO4-2 + H+ = NiH2PO4+ + logk 8.7494 +Ni+2 + PO4-3 = NiPO4- + logk 8.374 +H+ + Ni+2 + 2 HPO4-2 = NiHP2O7- + H2O + logk 9.268 +2 HPO4-2 + Ni+2 = NiP2O7-2 + H2O + logk 3.088 +NO3- + Ni+2 = NiNO3+ + logk 0.1463 + -analytical 149.894 0.0452863 -4225.55 -60.2379 -64.462 +PITZER +-ALPHAS +Ca+2 Br- 2 12 +Ca+2 Cl- 2 12 +CaCl+ Cl- 2 12 +Ca+2 NO3- 0.87 12 +Ca+2 HSO4- 2 12 +Ca+2 I- 2 12 +Ca+2 SO4-2 1.4 12 +Cs+ Br- 2 12 +Cs+ Cl- 2 12 +Cs+ F- 2 12 +Cs+ I- 2 12 +Cs+ NO3- 2 12 +Cs+ OH- 2 12 +Cs+ SO4-2 2 12 +H+ Br- 2 12 +H+ Cl- 2 12 +H+ HSO4- 2 12 +H+ I- 2 12 +H+ NO3- 2 12 +H+ SO4-2 2 12 +K+ Br- 2 12 +K+ Cl- 2 12 +K+ CO3-2 2 12 +K+ F- 2 12 +K+ HCO3- 2 12 +K+ HPO4-2 2 12 +K+ HSO4- 2 12 +K+ I- 2 12 +K+ NO3- 2 12 +K+ OH- 2 12 +K+ SO4-2 2 12 +Li+ Br- 2 12 +Li+ Cl- 2 12 +Li+ I- 2 12 +Li+ NO3- 2 12 +Li+ OH- 2 12 +Li+ SO4-2 2 12 +Mg+2 Br- 2 12 +Mg+2 Cl- 2 12 +Mg+2 HCO3- 2 12 +Mg+2 HSO4- 2 12 +Mg+2 I- 2 12 +Mg+2 NO3- 1.55 12 +Mg+2 SO4-2 1.4 12 +Mn+2 Br- 2 12 +Mn+2 ClO4- 2 12 +MgOH+ Cl- 2 12 +Na+ AlO2- 2 12 +Na+ Br- 2 12 +Na+ Cl- 2 12 +Na+ CO3-2 2 12 +Na+ F- 2 12 +Na+ HCO3- 2 12 +Na+ HPO4-2 2 12 +Na+ H2PO4- 2 12 +Na+ HSO4- 2 12 +Na+ I- 2 12 +Na+ NO3- 1.43 12 +Na+ OH- 2 12 +Na+ SO4-2 2 12 +NH4+ Br- 2 12 +NH4+ Cl- 2 12 +NH4+ HCO3- 2 12 +NH4+ I- 2 12 +NH4+ NO3- 2 12 +NH4+ SO4-2 2 12 +Sr+2 Br- 2 12 +Sr+2 Cl- 2 12 +Sr+2 I- 2 12 +Sr+2 NO3- 2 12 +Al+3 Cl- 2 12 +Ba+2 Cl- 2 12 +Ba+2 Br- 2 12 +Ba+2 I- 2 12 +Ba+2 OH- 2 12 +Ba+2 ClO4- 2 12 +Ba+2 NO3- 2 12 +CaB(OH)4+ Cl- 2 12 +Ca+2 ClO4- 2 12 +Ca+2 MoO4-2 2 12 +Co+2 Cl- 2 12 +Co+2 ClO4- 2 12 +Co+2 Br- 2 12 +Co+2 I- 2 12 +Co+2 NO3- 2 12 +Co+2 SO4-2 1.4 12 +Cr+3 Cl- 2 12 +Cr+3 NO3- 2 12 +Na+ Cr(OH)4- 2 12 +Na+ Cr2O2(OH)4-2 2 12 +K+ Cr2O7-2 1.4 12 +Cu+2 Cl- 2 12 +Cu+2 NO3- 2 12 +Cu+2 SO4-2 1.4 12 +Cu+2 HSO4- 2 12 +Cu+2 Br- 2 12 +Cu+2 ClO4- 2 12 +Fe+2 Cl- 2 12 +H+ ClO4- 2 12 +K+ CrO4-2 2 12 +K+ PO4-3 2 12 +K+ B(OH)4- 2 12 +K+ B3O3(OH)4- 2 12 +K+ B4O5(OH)4-2 2 12 +Mn+2 Cl- 2 12 +Mn+2 SO4-2 1.4 12 +MgB(OH)4+ Cl- 2 12 +Na+ ClO4- 2 12 +Na+ B(OH)4- 2 12 +Na+ B3O3(OH)4- 2 12 +Na+ B4O5(OH)4-2 2 12 +Na+ PO4-3 2 12 +Na+ MoO4-2 2 12 +Na+ CrO4-2 2 12 +Ni+2 ClO4- 2 12 +Ni+2 Cl- 2 12 +Ni+2 Br- 2 12 +Ni+2 NO3- 2 12 +Ni+2 SO4-2 1.4 12 +Fe+2 SO4-2 1.4 12 +Fe+3 ClO4- 2 12 +Zn+2 Cl- 2 12 +Zn+2 Br- 2 12 +Zn+2 I- 2 12 +Zn+2 NO3- 2 12 +Zn+2 ClO4- 2 12 +Zn+2 SO4-2 1.4 12 +-B0 +Ca+2 Br- 0.3816 0 0 0 0 +Ca+2 Cl- 0.303822 78.1786 1.45519e-011 0.000357022 0 +CaCl+ Cl- 0.356841 22.3581 1.45519e-011 -0.000839061 0 +Ca+2 NO3- 0.125912 144.214 0.520996 -0.000477655 0 +Ca+2 HSO4- 0.2145 0 0 0 0 +Ca+2 I- 0.4379 0 0 0 0 +Ca+2 SO4-2 0.15 0 0 0 0 +Cs+ Br- 0.0255603 -34.4283 -3.63798e-012 1.06581e-014 0 +Cs+ Cl- 0.0329801 -539.761 -2.42136 0.00272852 0 +Cs+ F- 0.1306 0 0 0 0 +Cs+ I- 0.0244 0 0 0 0 +Cs+ NO3- -0.0758 0 0 0 0 +Cs+ OH- 0.15 0 0 0 0 +Cs+ SO4-2 0.0714 0 0 0 0 +H+ Br- 0.2085 0 0 0 0 +H+ Cl- 0.177001 -33.4777 -0.262215 0.000125778 0 +H+ HSO4- 0.209089 1054.54 5.95728 -0.00877592 0 +H+ I- 0.2211 0 0 0 0 +H+ NO3- 0.125585 560.12 4.91603 -0.0099509 0 +H+ SO4-2 0.0986192 -6703.44 -41.6862 0.0636777 0 +K+ Br- 0.0526049 -938.817 -4.4303 0.00524879 0 +K+ Cl- 0.0477812 -343.299 -1.38219 0.00134298 0 +K+ CO3-2 0.1288 0 0 0 0 +K+ F- 0.08089 0 0 0 0 +K+ HCO3- -0.0107 0 0 0 0 +K+ HPO4-2 0.0248 0 0 0 0 +K+ HSO4- -0.0003 0 0 0 0 +K+ I- 0.0746 0 0 0 0 +K+ NO3- -0.0816 0 0 0 0 +K+ OH- 0.1298 0 0 0 0 +K+ SO4-2 0.0555358 -1418.43 -6.74729 0.00826907 0 +Li+ Br- 0.172803 3.72529e-009 1.45519e-011 -0.000106042 0 +Li+ Cl- 0.14847 1.86265e-009 0 -0.0001546 0 +Li+ I- 0.2104 0 0 0 0 +Li+ NO3- 0.142 0 0 0 0 +Li+ OH- 0.015 0 0 0 0 +Li+ SO4-2 0.1363 0 0 0 0 +Mg+2 Br- 0.4327 0 0 0 0 +Mg+2 Cl- 0.351136 -65.5822 -0.525061 0.000447185 0 +Mg+2 HCO3- 0.033 0 0 0 0 +Mg+2 HSO4- 0.4746 0 0 0 0 +Mg+2 I- 0.4902 0 0 0 0 +Mg+2 NO3- 0.306728 0 0 0 0 +Mg+2 SO4-2 0.222818 -5688.19 -32.8401 0.0473379 0 +Mn+2 Br- 0.44655 0 0 0 0 +Mn+2 ClO4- 0.50957 0 0 0 0 +MgOH+ Cl- -0.1 0 0 0 0 +Na+ AlO2- 0.0452319 -1545.73 -8.3136 0.0110572 0 +Na+ Br- 0.0973867 -210.646 -0.395939 -0.000283574 0 +Na+ Cl- 0.0745618 -470.789 -1.85114 0.00165565 0 +Na+ CO3-2 0.0362048 1108.38 11.1986 -0.0233017 0 +Na+ F- 0.0215 0 0 0 0 +Na+ HCO3- 0.0280021 682.886 6.89959 -0.0144593 0 +Na+ HPO4-2 -0.0583 0 0 0 0 +Na+ H2PO4- -0.0533 0 0 0 0 +Na+ HSO4- 0.0734349 52.5606 0.420809 -0.000821232 0 +Na+ I- 0.1195 0 0 0 0 +Na+ NO3- -0.00129331 -482.73 -2.21016 0.00254084 0 +Na+ OH- 0.0883444 -1197.85 -6.10983 0.00743325 0 +Na+ SO4-2 0.0120596 -2189.43 -10.1448 0.0119724 0 +NH4+ Br- 0.0624 0 0 0 0 +NH4+ Cl- 0.0524921 -216.413 -0.973041 0.00106115 0 +NH4+ HCO3- -0.038 0 0 0 0 +NH4+ I- 0.057 0 0 0 0 +NH4+ NO3- -0.0154 0 0 0 0 +NH4+ SO4-2 0.0391673 313.414 2.51504 -0.0044742 0 +Sr+2 Br- 0.3311 0 0 0 0 +Sr+2 Cl- 0.2834 0 0 0 0 +Sr+2 I- 0.4013 0 0 0 0 +Sr+2 NO3- 0.1346 0 0 0 0 +Al+3 Cl- 0.6993 0 0 0 0 +Ba+2 Cl- 0.2628 0 0 0 0 +Ba+2 Br- 0.3146 0 0 0 0 +Ba+2 I- 0.4219 0 0 0 0 +Ba+2 OH- 0.1718 0 0 0 0 +Ba+2 ClO4- 0.3614 0 0 0 0 +Ba+2 NO3- -0.0323 0 0 0 0 +CaB(OH)4+ Cl- 0.12 0 0 0 0 +Ca+2 ClO4- 0.4511 0 0 0 0 +Ca+2 MoO4-2 0.2 0 0 0 0 +Co+2 Cl- 0.37351 0 0 0 0 +Co+2 ClO4- 0.50409 0 0 0 0 +Co+2 Br- 0.427 0 0 0 0 +Co+2 I- 0.5213 0 0 0 0 +Co+2 NO3- 0.3119 0 0 0 0 +Co+2 SO4-2 0.2 0 0 0 0 +Cr+3 Cl- 0.72234 0 0 0 0 +Cr+3 NO3- 0.704 0 0 0 0 +Na+ Cr(OH)4- 0.045 0 0 0 0 +Na+ Cr2O2(OH)4-2 0.41 0 0 0 0 +K+ Cr2O7-2 -2.1239 0 0 0 0 +Cu+2 Cl- 0.23052 0 0 0 0 +Cu+2 NO3- 0.28124 0 0 0 0 +Cu+2 SO4-2 0.2358 0 0 0 0 +Cu+2 HSO4- 0.4755 0 0 0 0 +Cu+2 Br- 0.41247 0 0 0 0 +Cu+2 ClO4- 0.48984 0 0 0 0 +Fe+2 Cl- 0.3359 0 0 0 0 +H+ ClO4- 0.1747 0 0 0 0 +K+ CrO4-2 0.0791 0 0 0 0 +K+ PO4-3 0.3729 0 0 0 0 +K+ B(OH)4- 0.035 0 0 0 0 +K+ B3O3(OH)4- -0.13 0 0 0 0 +K+ B4O5(OH)4-2 -0.022 0 0 0 0 +Mn+2 Cl- 0.29486 0 0 0 0 +Mn+2 SO4-2 0.201 0 0 0 0 +MgB(OH)4+ Cl- 0.16 0 0 0 0 +Na+ ClO4- 0.0554 0 0 0 0 +Na+ B(OH)4- -0.0427 0 0 0 0 +Na+ B3O3(OH)4- -0.056 0 0 0 0 +Na+ B4O5(OH)4-2 -0.11 0 0 0 0 +Na+ PO4-3 0.1781 0 0 0 0 +Na+ MoO4-2 0.116 0 0 0 0 +Na+ CrO4-2 0.0645 0 0 0 0 +Ni+2 ClO4- 0.49285 0 0 0 0 +Ni+2 Cl- 0.3499 0 0 0 0 +Ni+2 Br- 0.4451 0 0 0 0 +Ni+2 NO3- 0.30978 0 0 0 0 +Ni+2 SO4-2 0.1702 0 0 0 0 +Fe+2 SO4-2 0.2568 0 0 0 0 +Fe+3 ClO4- 1.412 0 0 0 0 +Zn+2 Cl- 0.2602 0 0 0 0 +Zn+2 Br- 0.466 0 0 0 0 +Zn+2 I- 0.4821 0 0 0 0 +Zn+2 NO3- 0.3481 0 0 0 0 +Zn+2 ClO4- 0.506 0 0 0 0 +Zn+2 SO4-2 0.1949 0 0 0 0 +-B1 +Ca+2 Br- 1.6133 0 0 0 0 +Ca+2 Cl- 1.70143 -148.325 1.16415e-010 0.00505789 0 +CaCl+ Cl- 4.81235 3865.29 4.65661e-010 0.0112332 0 +Ca+2 NO3- 0.402371 -3219.15 -8.98838 0.00373059 0 +Ca+2 HSO4- 2.5275 0 0 0 0 +Ca+2 I- 1.8068 0 0 0 0 +Ca+2 SO4-2 3 0 0 0 0 +Cs+ Br- 0.0284193 -338.671 -1.45519e-011 5.68434e-014 0 +Cs+ Cl- 0.0429 -38 1.45519e-011 0.001306 0 +Cs+ F- 0.257 0 0 0 0 +Cs+ I- 0.0262 0 0 0 0 +Cs+ NO3- -0.0669 0 0 0 0 +Cs+ OH- 0.3 0 0 0 0 +Cs+ SO4-2 1.2008 0 0 0 0 +H+ Br- 0.3477 0 0 0 0 +H+ Cl- 0.292923 3402.47 19.7936 -0.0279388 0 +H+ HSO4- 0.440922 296.437 2.37333 -0.00463168 0 +H+ I- 0.4907 0 0 0 0 +H+ NO3- 0.287782 2.71242e-007 1.58606e-009 0.0013396 0 +H+ SO4-2 0 0 0 0 0 +K+ Br- 0.235353 2489.76 12.542 -0.013415 0 +K+ Cl- 0.215511 -576.388 -2.88017 0.00464483 0 +K+ CO3-2 1.4333 0 0 0 0 +K+ F- 0.2021 0 0 0 0 +K+ HCO3- 0.0478 0 0 0 0 +K+ HPO4-2 1.2743 0 0 0 0 +K+ HSO4- 0.1735 0 0 0 0 +K+ I- 0.2517 0 0 0 0 +K+ NO3- 0.0494 0 0 0 0 +K+ OH- 0.32 0 0 0 0 +K+ SO4-2 0.796385 2067.13 2.32831e-010 0.0235793 0 +Li+ Br- 0.297759 990.383 5.60773 -0.00697051 0 +Li+ Cl- 0.307 7.45058e-009 2.91038e-011 0.000636 0 +Li+ I- 0.373 0 0 0 0 +Li+ NO3- 0.278 0 0 0 0 +Li+ OH- 0.14 0 0 0 0 +Li+ SO4-2 1.2705 0 0 0 0 +Mg+2 Br- 1.7528 0 0 0 0 +Mg+2 Cl- 1.65325 -2872.88 -23.0007 0.0494573 0 +Mg+2 HCO3- 0.8498 0 0 0 0 +Mg+2 HSO4- 1.7288 0 0 0 0 +Mg+2 I- 1.8041 0 0 0 0 +Mg+2 NO3- 1.25745 0 0 0 0 +Mg+2 SO4-2 3.37713 -23184 -139.335 0.217921 0 +Mn+2 Br- 1.34477 0 0 0 0 +Mn+2 ClO4- 2.16209 0 0 0 0 +MgOH+ Cl- 1.658 0 0 0 0 +Na+ AlO2- 0.30666 -4913.33 -28.5581 0.0416055 0 +Na+ Br- 0.297015 -6335.18 -37.5077 0.0558804 0 +Na+ Cl- 0.275241 -521.118 -2.88036 0.00471463 0 +Na+ CO3-2 1.51207 4412.51 44.5821 -0.0998912 0 +Na+ F- 0.2107 0 0 0 0 +Na+ HCO3- 0.0440052 1129.39 11.4109 -0.0244673 0 +Na+ HPO4-2 1.4655 0 0 0 0 +Na+ H2PO4- 0.0396 0 0 0 0 +Na+ HSO4- 0.299943 4698.12 26.8475 -0.0373912 0 +Na+ I- 0.3439 0 0 0 0 +Na+ NO3- 0.141301 -2759.36 -13.3527 0.0180629 0 +Na+ OH- 0.244421 1627.03 9.4825 -0.0115789 0 +Na+ SO4-2 1.11538 -12744.2 -67.1934 0.0891223 0 +NH4+ Br- 0.1947 0 0 0 0 +NH4+ Cl- 0.187339 0 -0.142555 0.00109395 0 +NH4+ HCO3- 0.07 0 0 0 0 +NH4+ I- 0.3157 0 0 0 0 +NH4+ NO3- 0.112 0 0 0 0 +NH4+ SO4-2 0.662847 37708.8 255.814 -0.422218 0 +Sr+2 Br- 1.7115 0 0 0 0 +Sr+2 Cl- 1.6256 0 0 0 0 +Sr+2 I- 1.86 0 0 0 0 +Sr+2 NO3- 1.38 0 0 0 0 +Al+3 Cl- 5.8447 0 0 0 0 +Ba+2 Cl- 1.4963 0 0 0 0 +Ba+2 Br- 1.5698 0 0 0 0 +Ba+2 I- 1.6868 0 0 0 0 +Ba+2 OH- 1.2 0 0 0 0 +Ba+2 ClO4- 1.5758 0 0 0 0 +Ba+2 NO3- 0.8025 0 0 0 0 +CaB(OH)4+ Cl- 0 0 0 0 0 +Ca+2 ClO4- 1.757 0 0 0 0 +Ca+2 MoO4-2 3.1973 0 0 0 0 +Co+2 Cl- 1.25999 0 0 0 0 +Co+2 ClO4- 1.96664 0 0 0 0 +Co+2 Br- 1.6598 0 0 0 0 +Co+2 I- 1.6725 0 0 0 0 +Co+2 NO3- 1.6905 0 0 0 0 +Co+2 SO4-2 2.7 0 0 0 0 +Cr+3 Cl- 5.5989 0 0 0 0 +Cr+3 NO3- 5.1847 0 0 0 0 +Na+ Cr(OH)4- 0.31 0 0 0 0 +Na+ Cr2O2(OH)4-2 0.7 0 0 0 0 +K+ Cr2O7-2 6.3443 0 0 0 0 +Cu+2 Cl- 2.20897 0 0 0 0 +Cu+2 NO3- 1.72906 0 0 0 0 +Cu+2 SO4-2 2.485 0 0 0 0 +Cu+2 HSO4- 2.428 0 0 0 0 +Cu+2 Br- 1.6627 0 0 0 0 +Cu+2 ClO4- 1.90361 0 0 0 0 +Fe+2 Cl- 1.5323 0 0 0 0 +H+ ClO4- 0.2931 0 0 0 0 +K+ CrO4-2 1.113 0 0 0 0 +K+ PO4-3 3.972 0 0 0 0 +K+ B(OH)4- 0.14 0 0 0 0 +K+ B3O3(OH)4- 0 0 0 0 0 +K+ B4O5(OH)4-2 0 0 0 0 0 +Mn+2 Cl- 2.01251 0 0 0 0 +Mn+2 SO4-2 2.98 0 0 0 0 +MgB(OH)4+ Cl- 0 0 0 0 0 +Na+ ClO4- 0.2755 0 0 0 0 +Na+ B(OH)4- 0.089 0 0 0 0 +Na+ B3O3(OH)4- -0.91 0 0 0 0 +Na+ B4O5(OH)4-2 -0.4 0 0 0 0 +Na+ PO4-3 3.8513 0 0 0 0 +Na+ MoO4-2 2.287 0 0 0 0 +Na+ CrO4-2 1.5974 0 0 0 0 +Ni+2 ClO4- 1.98517 0 0 0 0 +Ni+2 Cl- 1.53 0 0 0 0 +Ni+2 Br- 1.49 0 0 0 0 +Ni+2 NO3- 2.10644 0 0 0 0 +Ni+2 SO4-2 2.907 0 0 0 0 +Fe+2 SO4-2 3.063 0 0 0 0 +Fe+3 ClO4- 2.989 0 0 0 0 +Zn+2 Cl- 1.6425 0 0 0 0 +Zn+2 Br- 1.6343 0 0 0 0 +Zn+2 I- 1.9455 0 0 0 0 +Zn+2 NO3- 1.6913 0 0 0 0 +Zn+2 ClO4- 1.797 0 0 0 0 +Zn+2 SO4-2 2.883 0 0 0 0 +-B2 +Ca+2 Br- 0 0 0 0 0 +Ca+2 Cl- 0 0 0 0 0 +CaCl+ Cl- 0 0 0 0 0 +Ca+2 NO3- 0 0 0 0 0 +Ca+2 HSO4- 0 0 0 0 0 +Ca+2 I- 0 0 0 0 0 +Ca+2 SO4-2 0 0 0 0 0 +Cs+ Br- 0 0 0 0 0 +Cs+ Cl- 0 0 0 0 0 +Cs+ F- 0 0 0 0 0 +Cs+ I- 0 0 0 0 0 +Cs+ NO3- 0 0 0 0 0 +Cs+ OH- 0 0 0 0 0 +Cs+ SO4-2 0 0 0 0 0 +H+ Br- 0 0 0 0 0 +H+ Cl- 0 0 0 0 0 +H+ HSO4- 0 0 0 0 0 +H+ I- 0 0 0 0 0 +H+ NO3- 0 0 0 0 0 +H+ SO4-2 0 0 0 0 0 +K+ Br- 0 0 0 0 0 +K+ Cl- 0 0 0 0 0 +K+ CO3-2 0 0 0 0 0 +K+ F- 0 0 0 0 0 +K+ HCO3- 0 0 0 0 0 +K+ HPO4-2 0 0 0 0 0 +K+ HSO4- 0 0 0 0 0 +K+ I- 0 0 0 0 0 +K+ NO3- 0 0 0 0 0 +K+ OH- 0 0 0 0 0 +K+ SO4-2 0 0 0 0 0 +Li+ Br- 0 0 0 0 0 +Li+ Cl- 0 0 0 0 0 +Li+ I- 0 0 0 0 0 +Li+ NO3- 0 0 0 0 0 +Li+ OH- 0 0 0 0 0 +Li+ SO4-2 0 0 0 0 0 +Mg+2 Br- 0 0 0 0 0 +Mg+2 Cl- 0 0 0 0 0 +Mg+2 HCO3- 0 0 0 0 0 +Mg+2 HSO4- 0 0 0 0 0 +Mg+2 I- 0 0 0 0 0 +Mg+2 NO3- 0 0 0 0 0 +Mg+2 SO4-2 -35.2588 2.17039e+006 13998.3 -22.8323 0 +Mn+2 Br- 0 0 0 0 0 +Mn+2 ClO4- 0 0 0 0 0 +MgOH+ Cl- 0 0 0 0 0 +Na+ AlO2- 0 0 0 0 0 +Na+ Br- 0 0 0 0 0 +Na+ Cl- 0 0 0 0 0 +Na+ CO3-2 0 0 0 0 0 +Na+ F- 0 0 0 0 0 +Na+ HCO3- 0 0 0 0 0 +Na+ HPO4-2 0 0 0 0 0 +Na+ H2PO4- 0 0 0 0 0 +Na+ HSO4- 0 0 0 0 0 +Na+ I- 0 0 0 0 0 +Na+ NO3- 0 0 0 0 0 +Na+ OH- 0 0 0 0 0 +Na+ SO4-2 0 0 0 0 0 +NH4+ Br- 0 0 0 0 0 +NH4+ Cl- 0 0 0 0 0 +NH4+ HCO3- 0 0 0 0 0 +NH4+ I- 0 0 0 0 0 +NH4+ NO3- 0 0 0 0 0 +NH4+ SO4-2 0 0 0 0 0 +Sr+2 Br- 0 0 0 0 0 +Sr+2 Cl- 0 0 0 0 0 +Sr+2 I- 0 0 0 0 0 +Sr+2 NO3- 0 0 0 0 0 +Al+3 Cl- 0 0 0 0 0 +Ba+2 Cl- 0 0 0 0 0 +Ba+2 Br- 0 0 0 0 0 +Ba+2 I- 0 0 0 0 0 +Ba+2 OH- 0 0 0 0 0 +Ba+2 ClO4- 0 0 0 0 0 +Ba+2 NO3- 0 0 0 0 0 +CaB(OH)4+ Cl- 0 0 0 0 0 +Ca+2 ClO4- 0 0 0 0 0 +Ca+2 MoO4-2 -54.24 0 0 0 0 +Co+2 Cl- 0 0 0 0 0 +Co+2 ClO4- 0 0 0 0 0 +Co+2 Br- 0 0 0 0 0 +Co+2 I- 0 0 0 0 0 +Co+2 NO3- 0 0 0 0 0 +Co+2 SO4-2 -30.7 0 0 0 0 +Cr+3 Cl- 0 0 0 0 0 +Cr+3 NO3- 0 0 0 0 0 +Na+ Cr(OH)4- 0 0 0 0 0 +Na+ Cr2O2(OH)4-2 0 0 0 0 0 +K+ Cr2O7-2 25.9183 0 0 0 0 +Cu+2 Cl- 0 0 0 0 0 +Cu+2 NO3- 0 0 0 0 0 +Cu+2 SO4-2 -47.35 0 0 0 0 +Cu+2 HSO4- 0 0 0 0 0 +Cu+2 Br- 0 0 0 0 0 +Cu+2 ClO4- 0 0 0 0 0 +Fe+2 Cl- 0 0 0 0 0 +H+ ClO4- 0 0 0 0 0 +K+ CrO4-2 0 0 0 0 0 +K+ PO4-3 0 0 0 0 0 +K+ B(OH)4- 0 0 0 0 0 +K+ B3O3(OH)4- 0 0 0 0 0 +K+ B4O5(OH)4-2 0 0 0 0 0 +Mn+2 Cl- 0 0 0 0 0 +Mn+2 SO4-2 -40 0 0 0 0 +MgB(OH)4+ Cl- 0 0 0 0 0 +Na+ ClO4- 0 0 0 0 0 +Na+ B(OH)4- 0 0 0 0 0 +Na+ B3O3(OH)4- 0 0 0 0 0 +Na+ B4O5(OH)4-2 0 0 0 0 0 +Na+ PO4-3 0 0 0 0 0 +Na+ MoO4-2 0 0 0 0 0 +Na+ CrO4-2 0 0 0 0 0 +Ni+2 ClO4- 0 0 0 0 0 +Ni+2 Cl- 0 0 0 0 0 +Ni+2 Br- 0 0 0 0 0 +Ni+2 NO3- 0 0 0 0 0 +Ni+2 SO4-2 -40.06 0 0 0 0 +Fe+2 SO4-2 -42 0 0 0 0 +Fe+3 ClO4- 0 0 0 0 0 +Zn+2 Cl- 0 0 0 0 0 +Zn+2 Br- 0 0 0 0 0 +Zn+2 I- 0 0 0 0 0 +Zn+2 NO3- 0 0 0 0 0 +Zn+2 ClO4- 0 0 0 0 0 +Zn+2 SO4-2 -32.81 0 0 0 0 +-C0 +Ca+2 Br- -0.0025721 0 0 0 0 +Ca+2 Cl- 0.00117585 -2.49944 -2.27374e-013 -3.43691e-005 0 +CaCl+ Cl- -0.00677088 -1.41252 1.13687e-013 1.45678e-005 0 +Ca+2 NO3- -0.0026082 -29.6872 -0.162394 0.000220405 0 +Ca+2 HSO4- 0 0 0 0 0 +Ca+2 I- -0.00083792 0 0 0 0 +Ca+2 SO4-2 0 0 0 0 0 +Cs+ Br- 0.000949401 52.6758 0.23335 -0.0002656 0 +Cs+ Cl- -0.000183801 48.4733 0.216077 -0.000239736 0 +Cs+ F- -0.0043 0 0 0 0 +Cs+ I- -0.00365 0 0 0 0 +Cs+ NO3- 0 0 0 0 0 +Cs+ OH- 0 0 0 0 0 +Cs+ SO4-2 0.0029115 0 0 0 0 +H+ Br- 0.00152 0 0 0 0 +H+ Cl- 0.000362 -2.91038e-011 0 -3.036e-005 0 +H+ HSO4- 0 0 0 0 0 +H+ I- 0.00482 0 0 0 0 +H+ NO3- -0.00559627 -6.57638 -0.0577192 0.000110405 0 +H+ SO4-2 0.0593003 -3029.55 -16.072 0.0202175 0 +K+ Br- -1.47498e-005 255.458 1.25575 -0.00152465 0 +K+ Cl- -0.000748605 36.5328 0.148205 -0.000147225 0 +K+ CO3-2 0.00049851 0 0 0 0 +K+ F- 0.00093 0 0 0 0 +K+ HCO3- 0 0 0 0 0 +K+ HPO4-2 0.016387 0 0 0 0 +K+ HSO4- 0 0 0 0 0 +K+ I- -0.00414 0 0 0 0 +K+ NO3- 0.0066 0 0 0 0 +K+ OH- 0.0041 0 0 0 0 +K+ SO4-2 -0.0188 0 9.09495e-013 -2.66454e-015 0 +Li+ Br- 0.00476301 -92.5667 -0.462632 0.000524229 0 +Li+ Cl- 0.00370971 4.52467 0.00327989 -8.61317e-006 0 +Li+ I- 0 0 0 0 0 +Li+ NO3- -0.00551 0 0 0 0 +Li+ OH- 0 0 0 0 0 +Li+ SO4-2 -0.0039934 0 0 0 0 +Mg+2 Br- 0.0031236 0 0 0 0 +Mg+2 Cl- 0.00652611 -26.7038 -0.213795 0.000311488 0 +Mg+2 HCO3- 0 0 0 0 0 +Mg+2 HSO4- 0 0 0 0 0 +Mg+2 I- 0.0079337 0 0 0 0 +Mg+2 NO3- -0.00332923 0 0 0 0 +Mg+2 SO4-2 0.024365 1894.84 10.8446 -0.015634 0 +Mn+2 Br- -0.02269 0 0 0 0 +Mn+2 ClO4- 0.01144 0 0 0 0 +MgOH+ Cl- 0 0 0 0 0 +Na+ AlO2- -0.000303688 205.885 1.07578 -0.00143737 0 +Na+ Br- 0.000749486 -14.2959 -0.165395 0.000305237 0 +Na+ Cl- 0.00153693 48.0725 0.17468 -0.000156269 0 +Na+ CO3-2 0.0052 0 0 8.88178e-016 0 +Na+ F- 0 0 0 0 0 +Na+ HCO3- 0 0 0 0 0 +Na+ HPO4-2 0.02938 0 0 0 0 +Na+ H2PO4- 0.00795 0 0 0 0 +Na+ HSO4- -0.00462067 -5.82077e-011 -2.27374e-013 6.82142e-006 0 +Na+ I- 0.0018 0 0 0 0 +Na+ NO3- 0.000320511 46.9542 0.224387 -0.000263788 0 +Na+ OH- 0.00399944 88.2476 0.406876 -0.000475667 0 +Na+ SO4-2 0.00657125 338.749 1.48134 -0.00164185 0 +NH4+ Br- -0.00436 0 0 0 0 +NH4+ Cl- -0.00307437 7.39922 0.0230712 -1.76252e-005 0 +NH4+ HCO3- 0 0 0 0 0 +NH4+ I- -0.00308 0 0 0 0 +NH4+ NO3- -3e-005 0 0 0 0 +NH4+ SO4-2 -0.000757091 -114.019 -0.75621 0.00120647 0 +Sr+2 Br- 0.0012251 0 0 0 0 +Sr+2 Cl- -0.00089095 0 0 0 0 +Sr+2 I- 0.002657 0 0 0 0 +Sr+2 NO3- -0.019925 0 0 0 0 +Al+3 Cl- 0.00273 0 0 0 0 +Ba+2 Cl- -0.01938 0 0 0 0 +Ba+2 Br- -0.01596 0 0 0 0 +Ba+2 I- -0.01743 0 0 0 0 +Ba+2 OH- 0 0 0 0 0 +Ba+2 ClO4- -0.03126 0 0 0 0 +Ba+2 NO3- 0 0 0 0 0 +CaB(OH)4+ Cl- 0 0 0 0 0 +Ca+2 ClO4- -0.005 0 0 0 0 +Ca+2 MoO4-2 0 0 0 0 0 +Co+2 Cl- -0.01803 0 0 0 0 +Co+2 ClO4- 0.01349 0 0 0 0 +Co+2 Br- -0.00067 0 0 0 0 +Co+2 I- -0.00467 0 0 0 0 +Co+2 NO3- -0.00762 0 0 0 0 +Co+2 SO4-2 0 0 0 0 0 +Cr+3 Cl- -0.04141 0 0 0 0 +Cr+3 NO3- -0.05901 0 0 0 0 +Na+ Cr(OH)4- -0.003 0 0 0 0 +Na+ Cr2O2(OH)4-2 -0.03768 0 0 0 0 +K+ Cr2O7-2 1.6682 0 0 0 0 +Cu+2 Cl- -0.01639 0 0 0 0 +Cu+2 NO3- -0.00842 0 0 0 0 +Cu+2 SO4-2 -0.0012 0 0 0 0 +Cu+2 HSO4- 0 0 0 0 0 +Cu+2 Br- -0.04262 0 0 0 0 +Cu+2 ClO4- 0.00839 0 0 0 0 +Fe+2 Cl- -0.00861 0 0 0 0 +H+ ClO4- 0.00819 0 0 0 0 +K+ CrO4-2 -0.0012 0 0 0 0 +K+ PO4-3 -0.08679 0 0 0 0 +K+ B(OH)4- 0 0 0 0 0 +K+ B3O3(OH)4- 0 0 0 0 0 +K+ B4O5(OH)4-2 0 0 0 0 0 +Mn+2 Cl- -0.01528 0 0 0 0 +Mn+2 SO4-2 0.0182 0 0 0 0 +MgB(OH)4+ Cl- 0 0 0 0 0 +Na+ ClO4- -0.00118 0 0 0 0 +Na+ B(OH)4- 0.0114 0 0 0 0 +Na+ B3O3(OH)4- 0 0 0 0 0 +Na+ B4O5(OH)4-2 0 0 0 0 0 +Na+ PO4-3 -0.051538 0 0 0 0 +Na+ MoO4-2 0.006 0 0 0 0 +Na+ CrO4-2 0.0094 0 0 0 0 +Ni+2 ClO4- 0.01679 0 0 0 0 +Ni+2 Cl- -0.00471 0 0 0 0 +Ni+2 Br- -0.00626 0 0 0 0 +Ni+2 NO3- -0.00394 0 0 0 0 +Ni+2 SO4-2 0.0366 0 0 0 0 +Fe+2 SO4-2 0.0209 0 0 0 0 +Fe+3 ClO4- -0.462 0 0 0 0 +Zn+2 Cl- -0.08798 0 0 0 0 +Zn+2 Br- -0.10792 0 0 0 0 +Zn+2 I- -0.01427 0 0 0 0 +Zn+2 NO3- -0.01567 0 0 0 0 +Zn+2 ClO4- 0.01132 0 0 0 0 +Zn+2 SO4-2 0.029 0 0 0 0 +-THETA +Ca+2 H+ 0.092 0 0 0 0 +Ca+2 K+ 0.1156 9.31323e-010 0 1.42109e-014 0 +Ca+2 Na+ 0.05 1.86265e-009 7.27596e-012 0 0 +Ca+2 Mg+2 0.007 0 0 0 0 +Cs+ H+ -0.044 0 0 0 0 +Cs+ K+ 0 0 0 0 0 +Cs+ Li+ -0.1242 0 0 0 0 +Cs+ Na+ -0.03886 0 0 0 0 +Cs+ Mg+2 -0.078 0 0 0 0 +H+ K+ 0.005 0 0 0 0 +H+ Li+ 0.015 0 0 0 0 +H+ Mg+2 0.1 0 0 0 0 +H+ Na+ 0.036 0 0 0 0 +H+ NH4+ -0.019 0 0 0 0 +H+ Sr+2 0.0642 0 0 0 0 +K+ Li+ -0.0563 0 0 0 0 +K+ Mg+2 0 0 0 0 0 +K+ Na+ -0.00320349 14.0213 9.09495e-013 -2.66454e-015 0 +Li+ Na+ 0.0029 0 0 0 0 +Ba+2 H+ 0.0991 0 0 0 0 +Na+ Co+2 0.0382 0 0 0 0 +Ca+2 Co+2 0.1722 0 0 0 0 +Co+2 H+ 0.0829 0 0 0 0 +Co+2 K+ 0.0559 0 0 0 0 +Cr+3 K+ -0.07 0 0 0 0 +Cu+2 K+ -0.16 0 0 0 0 +Cu+2 Na+ 0.037 0 0 0 0 +Cu+2 H+ 0.084 0 0 0 0 +Mg+2 Na+ 0.07 0 0 0 0 +Na+ Mn+2 0.0907 0 0 0 0 +Ni+2 H+ 0.069 0 0 0 0 +Ni+2 K+ 0 0 0 0 0 +Ni+2 Na+ 0 0 0 0 0 +AlO2- NO3- 0.038 0 0 0 0 +AlO2- OH- 0.014 0 0 0 0 +Br- Cl- 0 0 0 0 0 +Br- OH- -0.065 0 0 0 0 +Cl- ClO4- 0.0341 0 0 0 0 +Cl- CO3-2 -0.092 0 0 0 0 +Cl- HCO3- 0.0359 0 0 0 0 +Cl- HSO4- -0.006 0 0 0 0 +Cl- NO3- 0.016 0 0 0 0 +Cl- OH- -0.05 0 0 0 0 +ClO4- OH- -0.032 0 0 0 0 +Cl- SO4-2 0.0703279 182.513 1.32573 -0.00233223 0 +ClO4- SO4-2 0.02 0 0 0 0 +CO3-2 HCO3- -0.04 0 0 0 0 +CO3-2 OH- 0.1 0 0 0 0 +CO3-2 SO4-2 0.02 0 0 0 0 +CO3-2 ClO4- 0.07 0 0 0 0 +HCO3- SO4-2 0.01 0 0 0 0 +HCO3- ClO4- 0.081 0 0 0 0 +HSO4- SO4-2 -0.116842 -3090.83 -14.002 0.0154028 0 +OH- SO4-2 -0.013 0 0 0 0 +OH- Cr(OH)4- 0.014 0 0 0 0 +Cl- CrO4-2 0.02 0 0 0 0 +Cl- Cr2O7-2 0.1 0 0 0 0 +Br- Cr2O7-2 -0.05 0 0 0 0 +Cl- MoO4-2 0.035 0 0 0 0 +SO4-2 B(OH)4- -0.012 0 0 0 0 +SO4-2 B3O3(OH)4- 0.1 0 0 0 0 +SO4-2 B4O5(OH)4-2 0.12 0 0 0 0 +Cl- B(OH)4- -0.065 0 0 0 0 +Cl- B3O3(OH)4- 0.12 0 0 0 0 +Cl- B4O5(OH)4-2 0.074 0 0 0 0 +HCO3- B3O3(OH)4- -0.1 0 0 0 0 +HCO3- B4O5(OH)4-2 -0.087 0 0 0 0 +-LAMDA +Ca+2 CO2 0.23088 554.03 2.36157 -0.00253647 0 +H+ CO2 0 0 0 0 0 +K+ CO2 0.11544 277.015 1.18078 -0.00126824 0 +Mg+2 CO2 0.23088 554.03 2.36157 -0.00253647 0 +Na+ CO2 0.11544 277.015 1.18078 -0.00126824 0 +Al+3 O2 0.3095 0 0 0 0 +Ba+2 O2 0.285 0 0 0 0 +Ca+2 O2 0.2497 0 0 0 0 +H+ O2 0.0352846 81.45 -8.09461e-011 1.21206e-013 0 +K+ O2 0.151915 199.431 7.64677e-010 -1.14341e-012 0 +Li+ O2 0.149 0 0 0 0 +Mg+2 O2 0.229806 305.513 -2.15815e-010 3.23403e-013 0 +Na+ O2 0.160236 72.8644 -0.616311 0.0018462 0 +NH4+ O2 0.0751 0 0 0 0 +H+ SiO2 0 0 0 0 0 +Mg+2 SiO2 0.0821096 -3.83318e-009 -2.32691e-011 -0.0018133 0 +Na+ SiO2 -0.0788994 -1.43923e-007 -8.63577e-010 0.000118667 0 +H+ H3PO4 0.29 0 0 0 0 +Cl- CO2 0 0 0 0 0 +SO4-2 CO2 0.0938551 640.652 2.19629 -0.00183326 0 +Cl- O2 0 0 0 0 0 +Br- O2 -0.0347 0 0 0 0 +CO3-2 O2 0.0964892 -277.074 2.03006e-010 -3.0343e-013 0 +HCO3- O2 0.0854 0 0 0 0 +I- O2 -0.0744 0 0 0 0 +NO3- O2 -0.0377 0 0 0 0 +OH- O2 0.0522254 15.571 -2.74032e-010 4.10103e-013 0 +SO4-2 O2 0.0877744 -274.085 1.32907e-009 -1.98728e-012 0 +ClO4- CO2 -0.07 0 0 0 0 +Cl- SiO2 0.142309 167.356 1.50701 -0.00321903 0 +NO3- SiO2 0.134079 6301.5 35.5262 -0.0502191 0 +SO4-2 SiO2 0.0775993 1.55517e-007 9.33172e-010 0.00055333 0 +H2PO4- H3PO4 -0.4 0 0 0 0 +CO2 CO2 -0.0114856 1172.99 5.6247 -0.00680013 0 +CaCl2 CaCl2 3.7094 5485.62 -1.01905e-010 0.0199031 0 +H3PO4 H3PO4 0.0503 0 0 0 0 +SiO2 SiO2 0 0 0 0 0 +CO2 SiO2 0 0 0 0 0 +-ZETA +Na+ Cl- CO2 -0.00756938 0 0 6.05286e-006 0 +Na+ SO4-2 CO2 -0.0105 0 0 0 0 +Ba+2 Cl- O2 0 0 0 0 0 +Ca+2 Cl- O2 -0.0169 0 0 0 0 +Ca+2 NO3- O2 0 0 0 0 0 +H+ Cl- O2 -0.0077 0 0 0 0 +K+ Br- O2 -0.00541 0 0 0 0 +K+ Cl- O2 -0.0211 0 0 0 0 +K+ OH- O2 -0.00706412 -15.075 -0.0452072 4.50985e-005 0 +K+ NO3- O2 -0.0281 0 0 0 0 +K+ SO4-2 O2 0 0 0 0 0 +Li+ Cl- O2 0 0 0 0 0 +Na+ Br- O2 -0.00909 0 0 0 0 +Na+ Cl- O2 -0.00918665 -2.739 3.25992e-011 -4.87959e-014 0 +Na+ CO3-2 O2 -0.0181 0 0 0 0 +Na+ HCO3- O2 0 0 0 0 0 +Na+ NO3- O2 -0.012 0 0 0 0 +Na+ OH- O2 -0.0125 0 0 0 0 +Na+ SO4-2 O2 -0.046 0 0 0 0 +NH4+ SO4-2 O2 0 0 0 0 0 +H+ NO3- SiO2 -0.0033 0 0 0 0 +Mg+2 Cl- SiO2 -0.0514692 1.49594e-008 8.98686e-011 0.000594133 0 +Na+ Cl- SiO2 -8.47766e-015 -1.84314e-008 -1.10633e-010 -0.0002 0 +-PSI +Ca+2 K+ Cl- -0.043189 -27.0771 -3.63798e-012 3.55271e-015 0 +Ca+2 Na+ Cl- -0.003 0 1.13687e-013 -6.66134e-016 0 +Ca+2 Na+ SO4-2 -0.012 0 4.54747e-013 -2.66454e-015 0 +K+ Mg+2 Cl- -0.022 -14.27 0 0 0 +K+ Na+ Cl- -0.00369149 -5.10213 -3.41061e-013 6.66134e-016 0 +K+ Na+ SO4-2 0.00732101 -71.6091 -0.394156 0.000606679 0 +Mg+2 Na+ Cl- -0.012 -9.51 0 0 0 +H+ Na+ HSO4- -0.0129 0 0 0 0 +H+ Na+ ClO4- -0.016 0 0 0 0 +Ca+2 Na+ ClO4- -0.02 0 0 0 0 +Na+ HCO3- ClO4- -0.019 0 0 0 0 +Cr+3 Na+ Cl- 0.01 0 0 0 0 +Cr+3 K+ Cl- -0.01 0 0 0 0 +Cs+ Na+ ClO4- 0.0026 0 0 0 0 +Cs+ Na+ Cl- -0.00135 0 0 0 0 +Cs+ Na+ SO4-2 0.0023 0 0 0 0 +Cs+ K+ Cl- -0.0013 0 0 0 0 +Cs+ K+ SO4-2 -0.0042 0 0 0 0 +Cs+ Mg+2 Cl- -0.044 0 0 0 0 +Cs+ Mg+2 SO4-2 -0.08 0 0 0 0 +Al+3 K+ Cl- -0.06 0 0 0 0 +Al+3 Mg+2 Cl- -0.024 0 0 0 0 +Ba+2 H+ Cl- -0.0081 0 0 0 0 +Na+ Co+2 Cl- -0.0056 0 0 0 0 +Ca+2 Co+2 Cl- -0.0332 0 0 0 0 +Co+2 K+ Cl- -0.0315 0 0 0 0 +Co+2 H+ Cl- 0.0075 0 0 0 0 +Cu+2 Na+ Cl- -0.0129 0 0 0 0 +Cu+2 Na+ SO4-2 -0.0235 0 0 0 0 +Cu+2 K+ Cl- -0.001 0 0 0 0 +Cu+2 K+ SO4-2 0.06 0 0 0 0 +Cu+2 H+ HSO4- -0.019 0 0 0 0 +Mg+2 H+ Cl- -0.011 0 0 0 0 +Mg+2 MgOH+ Cl- 0.028 0 0 0 0 +Na+ Mn+2 Cl- -0.019 0 0 0 0 +Ni+2 H+ Cl- 0.0044 0 0 0 0 +Ni+2 K+ Cl- -0.031 0 0 0 0 +Ni+2 K+ SO4-2 -0.069 0 0 0 0 +Ni+2 Na+ SO4-2 -0.015 0 0 0 0 +Ni+2 H+ Br- 0.06 0 0 0 0 +Ca+2 Cl- SO4-2 -0.018 -6.98492e-010 -2.72848e-012 1.77636e-015 0 +K+ Cl- SO4-2 -0.00161523 37.562 2.89901e-012 0.000284698 0 +Mg+2 Cl- SO4-2 -0.00796 32.63 0 0 0 +Na+ AlO2- NO3- 0.0066 0 0 0 0 +Na+ Cl- NO3- -0.006 0 0 0 0 +Na+ HCO3- Cl- -0.0143 0 0 0 0 +Na+ CO3-2 Cl- 0.016 0 0 0 0 +Na+ AlO2- OH- -0.0048 0 0 0 0 +Na+ Cl- OH- -0.00601 -9.93 0 0 0 +Na+ Cl- SO4-2 -0.00908855 -78.6142 -0.551999 0.000946106 0 +Na+ HSO4- SO4-2 0.0143653 258.084 1.16444 -0.00125524 0 +Na+ OH- SO4-2 -0.0091 -11.69 0 0 0 +H+ HSO4- SO4-2 0.02781 0 0 0 0 +Na+ ClO4- SO4-2 0.0014 0 0 0 0 +Na+ B(OH)4- Cl- -0.0073 0 0 0 0 +Cs+ ClO4- Cl- 0.0023 0 0 0 0 +Na+ ClO4- Cl- -0.0057 0 0 0 0 +Na+ B3O3(OH)4- Cl- -0.024 0 0 0 0 +Na+ B4O5(OH)4-2 Cl- 0.026 0 0 0 0 +Na+ OH- Cr(OH)4- -0.0048 0 0 0 0 +K+ Cl- CrO4-2 0.035 0 0 0 0 +Na+ Cl- CrO4-2 -0.004 0 0 0 0 +K+ Cl- Cr2O7-2 -0.7 0 0 0 0 +K+ Br- Cr2O7-2 -0.7 0 0 0 0 +K+ SO4-2 Cr2O7-2 -0.7 0 0 0 0 +Cu+2 Cl- SO4-2 0.01 0 0 0 0 +Na+ Cl- MoO4-2 0.0032 0 0 0 0 +-MU +CO2 CO2 CO2 0 0 0 0 0 +CaCl2 CaCl2 CaCl2 0 0 0 0 0 +H3PO4 H3PO4 H3PO4 0.0109 0 0 0 0 +SiO2 SiO2 SiO2 0 0 0 0 0 +CO2 CO2 SiO2 0 0 0 0 0 +SiO2 SiO2 CO2 0 0 0 0 0 +-ETA +PHASES +Albite + AlNaO8Si3 + 4 H+ = 1 Al+3 + 1 Na+ + 2 H2O + 3 SiO2 + logk 2.8495 + -analytical -42.136 -0.0137224 4440.75 13.7811 64.9546 +Alunite + Al3H6KO14S2 + 6 H+ = 1 K+ + 2 SO4-2 + 3 Al+3 + 6 H2O + logk -2.4018 + -analytical -681.181 -0.222279 25696 266.222 393.838 +Amesite-7A + Al2H4Mg2O9Si + 10 H+ = 1 SiO2 + 2 Al+3 + 2 Mg+2 + 7 H2O + logk 39.1427 + -analytical -414.29 -0.0953254 32230.7 151.016 499.288 +Amesite-14A + Al4H8Mg4O18Si2 + 20 H+ = 2 SiO2 + 4 Al+3 + 4 Mg+2 + 14 H2O + logk 71.5387 + -analytical -818.692 -0.189557 62349.9 298.04 965.637 +Analcime + Al0.96H2Na0.96O7Si2.04 + 3.84 H+ = 0.96 Al+3 + 0.96 Na+ + 2.04 SiO2 + 2.92 H2O + logk 6.0057 + -analytical -101.299 -0.0242806 7219.04 36.4798 109.295 +Analcime-dehy + Al0.96Na0.96O6Si2.04 + 3.84 H+ = 0.96 Al+3 + 0.96 Na+ + 1.92 H2O + 2.04 SiO2 + logk 12.3685 + -analytical -41.2622 -0.000938825 8092.99 10.7925 123.08 +Anhydrite + CaO4S = 1 Ca+2 + 1 SO4-2 + logk -4.3193 + -analytical 38.2552 -0.0229225 -2591.99 -10.9298 -42.4529 +Antarcticite + CaCl2H12O6 = 1 Ca+2 + 2 Cl- + 6 H2O + logk 4.0875 +Antigorite(am) + H4Mg3O9Si2 + 6 H+ = 2 SiO2 + 3 Mg+2 + 5 H2O + logk 34.5025 + -analytical 4.04316 0.000142474 9281.38 -0.288954 144.809 +Aragonite + CCaO3 + 1 H+ = 1 Ca+2 + 1 HCO3- + logk 1.9931 + -analytical -149.329 -0.048043 4908.59 60.2804 75.0803 +Arcanite + K2O4S = 1 SO4-2 + 2 K+ + logk -1.7916 + -analytical 14.1772 -0.0209474 -2912.86 0.0189941 -47.2865 +Artinite + CH8Mg2O8 + 3 H+ = 1 HCO3- + 2 Mg+2 + 5 H2O + logk 19.656 + -analytical -286.088 -0.0673406 15228.1 111.019 235.523 +Barite + BaO4S = 1 Ba+2 + 1 SO4-2 + logk -9.9711 + -analytical -187.464 -0.0755196 2078.68 77.9943 29.4461 +Beidellite-Mg + Al2.33H2Mg0.165O12Si3.67 + 7.32 H+ = 0.165 Mg+2 + 2.33 Al+3 + 3.67 SiO2 + 4.66 H2O + logk 4.8971 + -analytical -74.1724 -0.0371763 9405.62 23.6379 140.607 +Beidellite-Ca + Al2.33Ca0.165H2O12Si3.67 + 7.32 H+ = 0.165 Ca+2 + 2.33 Al+3 + 3.67 SiO2 + 4.66 H2O + logk 4.9352 + -analytical -71.5416 -0.0370508 9159.38 22.909 136.785 +Beidellite-K + Al2.33H2K0.33O12Si3.67 + 7.32 H+ = 0.33 K+ + 2.33 Al+3 + 3.67 SiO2 + 4.66 H2O + logk 4.6522 + -analytical -63.2861 -0.0350916 8389.36 20.2662 124.797 +Beidellite-Na + Al2.33H2Na0.33O12Si3.67 + 7.32 H+ = 0.33 Na+ + 2.33 Al+3 + 3.67 SiO2 + 4.66 H2O + logk 4.9911 + -analytical -69.0165 -0.035417 8858.86 22.1215 132.092 +Beidellite-H + Al2.33H2.33O12Si3.67 + 6.99 H+ = 2.33 Al+3 + 3.67 SiO2 + 4.66 H2O + logk 3.9773 + -analytical -62.6989 -0.0343559 8520.32 19.4903 126.89 +Bischofite + Cl2H12MgO6 = 1 Mg+2 + 2 Cl- + 6 H2O + logk 4.357 + -analytical -323.654 -0.0882897 9860.95 129.813 150.941 +Bloedite + H8MgNa2O12S2 = 1 Mg+2 + 2 Na+ + 2 SO4-2 + 4 H2O + logk -2.3469 +Boehmite + AlHO2 + 3 H+ = 1 Al+3 + 2 H2O + logk 8.6014 + -analytical -122.486 -0.0303634 9223.23 44.127 143.158 +Borax + B4H20Na2O17 + 2 H+ = 2 Na+ + 4 B(OH)3 + 5 H2O + logk 12.464 +Boric_acid + BH3O3 = 1 B(OH)3 + logk -0.0304 +KB5O8:4H2O + B5H8KO12 + 1 H+ + 3 H2O = 1 K+ + 5 B(OH)3 + logk 4.6715 +K2B4O7:4H2O + B4H8K2O11 + 2 H+ + 1 H2O = 2 K+ + 4 B(OH)3 + logk 13.9063 +NaBO2:4H2O + BH8NaO6 + 1 H+ = 1 Na+ + 1 B(OH)3 + 3 H2O + logk 9.5682 +NaB5O8:5H2O + B5H10NaO13 + 1 H+ + 2 H2O = 1 Na+ + 5 B(OH)3 + logk 5.8951 +NaBO2:NaCl:2H2O + BH4Na2O4Cl + 1 H+ = 2 Na+ + 1 B(OH)3 + 1 H2O + 1 Cl- + logk 10.8402 +Brushite + CaH5PO6 = 1 Ca+2 + 1 HPO4-2 + 2 H2O + logk -6.7334 + -analytical -262.806 -0.0876254 6120.43 105.732 92.5699 +Burkeite + CNa6O11S2 + 1 H+ = 1 HCO3- + 2 SO4-2 + 6 Na+ + logk 9.5671 +CaBr2 + CaBr2 = 1 Ca+2 + 2 Br- + logk 16.9439 + -analytical -208.916 -0.0782624 9784.9 87.4264 149.795 +Ca2Cl2(OH)2:H2O + Ca2Cl2H4O3 + 2 H+ = 2 Ca+2 + 2 Cl- + 3 H2O + logk 26.5313 +Ca4Cl2(OH)6:13H2O + Ca4Cl2H32O19 + 6 H+ = 2 Cl- + 4 Ca+2 + 19 H2O + logk 68.7343 +CaCl2 + CaCl2 = 1 Ca+2 + 2 Cl- + logk 11.942 + -analytical -210.02 -0.078342 8690.98 87.3438 132.783 +CaCl2:2H2O + CaCl2H4O2 = 1 Ca+2 + 2 Cl- + 2 H2O + logk 8.1163 +CaCl2:4H2O + CaCl2H8O4 = 1 Ca+2 + 2 Cl- + 4 H2O + logk 5.7588 +CaI2 + CaI2 = 1 Ca+2 + 2 I- + logk 21.6357 + -analytical -210.665 -0.0790864 10785.1 88.7742 165.554 +Calcite + CCaO3 + 1 H+ = 1 Ca+2 + 1 HCO3- + logk 1.8187 + -analytical 81.4192 -0.00450092 -2718.66 -27.9245 -39.666 +Ca(NO3)2 + CaN2O6 = 1 Ca+2 + 2 NO3- + logk 5.5269 + -analytical -199.442 -0.0734198 5401.66 84.3435 81.6669 +Ca(NO3)2:2H2O + CaN2O8H4 = 1 Ca+2 + 2 NO3- + 2 H2O + logk 3.4941 + -analytical -220.965 -0.0736462 4895.25 92.9336 73.7708 +Ca(NO3)2:3H2O + CaN2O9H6 = 1 Ca+2 + 2 NO3- + 3 H2O + logk 2.5539 + -analytical -234.344 -0.0737772 4935.91 97.9212 74.4102 +Ca(NO3)2:4H2O + CaN2O10H8 = 1 Ca+2 + 2 NO3- + 4 H2O + logk 1.7958 + -analytical -248.558 -0.0738614 5154.11 103.074 77.8166 +Carnallite + Cl3H12KMgO6 = 1 K+ + 1 Mg+2 + 3 Cl- + 6 H2O + logk 4.2296 + -analytical -262.031 -0.0773511 6959.18 107.466 104.27 +Carobbite + KF = 1 K+ + 1 F- + logk 4.4286 + -analytical -105.303 -0.0370254 3436.65 44.1403 52.1929 +Celadonite + AlH2KMgO12Si4 + 6 H+ = 1 Al+3 + 1 K+ + 1 Mg+2 + 4 H2O + 4 SiO2 + logk 7.8372 + -analytical -95.5787 -0.0223224 7971.9 33.6327 118.516 +Celestite + O4SSr = 1 SO4-2 + 1 Sr+2 + logk -6.632 +Chabazite + Al3.8Ca1.5Na0.2H20O34Si8.2K0.6 + 15.2 H+ = 0.2 Na+ + 1.5 Ca+2 + 3.8 Al+3 + 8.2 SiO2 + 17.6 H2O + 0.6 K+ + logk 10.3714 + -analytical -392.769 -0.0924726 25794.6 139.004 390.026 +Chamosite-7A + Al2Fe2H4O9Si + 10 H+ = 1 SiO2 + 2 Al+3 + 2 Fe+2 + 7 H2O + logk 32.6174 + -analytical -392.123 -0.0945596 29198.4 143.439 452.048 +Chloromagnesite + MgCl2 = 1 Mg+2 + 2 Cl- + logk 21.8541 + -analytical -236.465 -0.082065 13478.8 95.9947 207.374 +Clinoptilolite + Al3.45Ba0.062Ca0.761Fe0.017H21.844K0.543Mg0.124Mn0.002Na0.954O46.922Si14.533Sr0.036 + 13.868 H+ = 0.002 Mn+2 + 0.017 Fe+3 + 0.036 Sr+2 + 0.062 Ba+2 + 0.124 Mg+2 + 0.543 K+ + 0.761 Ca+2 + 0.954 Na+ + 3.45 Al+3 + 14.533 SiO2 + 17.856 H2O + logk -8.4187 + -analytical -240.855 -0.0839554 11360 88.5015 157.493 +Clinoptilolite-dehy + Al3.45Ba0.062Ca0.761Fe0.017K0.543Mg0.124Mn0.002Na0.954O36Si14.533Sr0.036 + 13.868 H+ = 0.002 Mn+2 + 0.017 Fe+3 + 0.036 Sr+2 + 0.062 Ba+2 + 0.124 Mg+2 + 0.543 K+ + 0.761 Ca+2 + 0.954 Na+ + 3.45 Al+3 + 6.934 H2O + 14.533 SiO2 + logk 27.2165 + -analytical -91.1253 -0.054599 19452.9 27.8844 283.681 +Clinoptilolite-Ca + Al3.45Ca1.7335Fe0.017H21.844O46.922Si14.533 + 13.868 H+ = 0.017 Fe+3 + 1.7335 Ca+2 + 3.45 Al+3 + 14.533 SiO2 + 17.856 H2O + logk -5.6428 + -analytical -542.13 -0.2567 16525.8 225.188 237.803 +Clinoptilolite-Cs + Al3.45Cs3.467Fe0.017H21.844O46.922Si14.533 + 13.868 H+ = 0.017 Fe+3 + 3.45 Al+3 + 3.467 Cs+ + 14.533 SiO2 + 17.856 H2O + logk -11.6912 + -analytical -379.286 -0.22275 3484.79 170.518 34.1944 +Clinoptilolite-K + Al3.45Fe0.017H21.844K3.467O46.922Si14.533 + 13.868 H+ = 0.017 Fe+3 + 3.45 Al+3 + 3.467 K+ + 14.533 SiO2 + 17.856 H2O + logk -9.5819 + -analytical -455.638 -0.237639 7364.34 198.765 95.0862 +Clinoptilolite-NH4 + Al3.45Fe0.017H35.712N3.467O46.922Si14.533 + 13.868 H+ = 0.017 Fe+3 + 3.45 Al+3 + 3.467 NH4+ + 14.533 SiO2 + 17.856 H2O + logk -9.0742 +Clinoptilolite-Na + Al3.45Fe0.017H21.844Na3.467O46.922Si14.533 + 13.868 H+ = 0.017 Fe+3 + 3.45 Al+3 + 3.467 Na+ + 14.533 SiO2 + 17.856 H2O + logk -5.7696 + -analytical -529.859 -0.241795 13475.7 222.516 190.202 +Clinoptilolite-Sr + Al3.45Fe0.017H21.844O46.922Si14.533Sr1.7335 + 13.868 H+ = 0.017 Fe+3 + 1.7335 Sr+2 + 3.45 Al+3 + 14.533 SiO2 + 17.856 H2O + logk -5.7825 + -analytical -519.963 -0.251453 15480.8 216.957 221.374 +Cristobalite(alpha) + O2Si = 1 SiO2 + logk -3.1922 + -analytical 38.7155 0.00745235 -2255.14 -14.7859 -36.3011 +Cronstedtite-7A + Fe4H4O9Si + 10 H+ = 1 SiO2 + 2 Fe+2 + 2 Fe+3 + 7 H2O + logk 17.3756 + -analytical -405.607 -0.0941652 24507.3 149.041 378.845 +Cryolite + Na3AlF6 = 3 Na+ + 1 Al+3 + 6 F- + logk -32.4002 + -analytical -772.506 -0.248156 15569.6 307.841 233.871 +Daphnite-14A + Al2Fe5H8O18Si3 + 16 H+ = 2 Al+3 + 3 SiO2 + 5 Fe+2 + 12 H2O + logk 51.6383 + -analytical -568.436 -0.135618 42817.7 208.839 660.843 +Daphnite-7A + Al2Fe5H8O18Si3 + 16 H+ = 2 Al+3 + 3 SiO2 + 5 Fe+2 + 12 H2O + logk 55.0117 + -analytical -579.983 -0.137356 44028.8 213.436 679.769 +Dawsonite + AlCH2NaO5 + 3 H+ = 1 Al+3 + 1 HCO3- + 1 Na+ + 2 H2O + logk 3.6618 +Dolomite + C2CaMgO6 + 2 H+ = 1 Ca+2 + 1 Mg+2 + 2 HCO3- + logk 2.5135 + -analytical -317.797 -0.0981791 10844.2 126.559 166.1 +Epsomite + H14MgO11S = 1 Mg+2 + 1 SO4-2 + 7 H2O + logk -1.9715 + -analytical -384.068 -0.105253 10015.3 153.504 153.124 +Erionite + Al4.2Ca0.9K1.5H26O49Si13.8Na0.9 + 16.8 H+ = 1.5 K+ + 4.2 Al+3 + 0.9 Ca+2 + 13.8 SiO2 + 21.4 H2O + 0.9 Na+ + logk -4.8296 + -analytical -259.453 -0.0619946 15676.1 88.9768 225.604 +Ferroaluminoceladonite + AlH2KFeO12Si4 + 6 H+ = 1 Al+3 + 1 K+ + 1 Fe+2 + 4 H2O + 4 SiO2 + logk 4.5745 + -analytical 9.97245 -0.0127411 2585.51 -4.19655 34.2902 +Ferroceladonite + Fe2H2KO12Si4 + 6 H+ = 1 Fe+3 + 1 K+ + 1 Fe+2 + 4 H2O + 4 SiO2 + logk -3.0464 + -analytical -14.4602 -0.0144572 921.605 5.06028 8.36248 +Fe2(MoO4)3 + Fe2Mo3O12 = 3 MoO4-2 + 2 Fe+3 + logk -38.52 +FeF3 + F3Fe = 1 Fe+3 + 3 F- + logk -19.2386 + -analytical -396.118 -0.12998 9355.66 155.261 141.448 +Fe2(SO4)3 + Fe2O12S3 = 2 Fe+3 + 3 SO4-2 + logk 0.8936 + -analytical -622.564 -0.239433 23842.5 248.433 362.754 +Fluorapatite + Ca5FO12P3 + 3 H+ = 1 F- + 3 HPO4-2 + 5 Ca+2 + logk -24.994 +Fluorite + CaF2 = 1 Ca+2 + 2 F- + logk -10.0371 + -analytical -250.379 -0.0841881 4952.95 100.542 74.3105 +Gaylussite + C2CaH10Na2O11 + 2 H+ = 1 Ca+2 + 2 HCO3- + 2 Na+ + 5 H2O + logk 11.2576 +Gibbsite + AlH3O3 + 3 H+ = 1 Al+3 + 3 H2O + logk 7.756 +Glaserite + NaK3O8S2 = 1 Na+ + 2 SO4-2 + 3 K+ + logk -3.8027 +Glauberite + CaNa2O8S2 = 1 Ca+2 + 2 Na+ + 2 SO4-2 + logk -5.1827 + -analytical -1176.03 -0.333312 32591.3 469.138 525.037 +Goethite + FeHO2 + 3 H+ = 1 Fe+3 + 2 H2O + logk 0.5347 + -analytical -155.806 -0.0368941 7422.37 57.5611 115.082 +Greenalite + Fe3H4O9Si2 + 6 H+ = 2 SiO2 + 3 Fe+2 + 5 H2O + logk 23.1624 + -analytical -187.706 -0.0423382 14992.7 69.9696 230.237 +Gypsum + CaH4O6S = 1 Ca+2 + 1 SO4-2 + 2 H2O + logk -4.5199 + -analytical 595.025 0.130898 -18425.3 -233.081 -297.408 +Halite + ClNa = 1 Cl- + 1 Na+ + logk 1.5857 + -analytical -109.351 -0.0373144 2470.11 45.9737 38.7981 +Hematite + Fe2O3 + 6 H+ = 2 Fe+3 + 3 H2O + logk 0.1086 + -analytical -270.261 -0.0637638 13829.8 98.1909 214.326 +Hemihydrate + CaHO4.5S = 1 Ca+2 + 1 SO4-2 + 0.5 H2O + logk -3.7773 + -analytical 81.5557 -0.00437956 -3026.76 -29.8462 -48.3568 +Heulandite + Al2.165Ba0.065Ca0.585H12K0.132Na0.383O24Si6.835Sr0.175 + 8.66 H+ = 0.065 Ba+2 + 0.132 K+ + 0.175 Sr+2 + 0.383 Na+ + 0.585 Ca+2 + 2.165 Al+3 + 6.835 SiO2 + 10.33 H2O + logk 3.622 + -analytical -200.737 -0.0583878 12103.4 73.142 178.964 +Hexahydrite + H12MgO10S = 1 Mg+2 + 1 SO4-2 + 6 H2O + logk -1.6712 + -analytical -364.044 -0.10209 10015.2 145.152 153.116 +Huntite + C4CaMg3O12 + 4 H+ = 1 Ca+2 + 3 Mg+2 + 4 HCO3- + logk 10.301 + -analytical -649.952 -0.196711 24813.1 256.858 380.841 +Hydroxylapatite + Ca5HO13P3 + 4 H+ = 1 H2O + 3 HPO4-2 + 5 Ca+2 + logk -3.0746 +Hydromagnesite + C4H10Mg5O18 + 6 H+ = 4 HCO3- + 5 Mg+2 + 6 H2O + logk 30.8539 + -analytical -792.808 -0.21448 36746.8 308.857 566.473 +Illite + Al2.3H2K0.6Mg0.25O12Si3.5 + 8 H+ = 0.25 Mg+2 + 0.6 K+ + 2.3 Al+3 + 3.5 SiO2 + 5 H2O + logk 8.3706 + -analytical -91.1618 -0.0407234 10315.7 31.1022 154.894 +Jarosite + Fe3H6KO14S2 + 6 H+ = 1 K+ + 2 SO4-2 + 3 Fe+3 + 6 H2O + logk -9.3706 +Jarosite-Na + Fe3H6NaO14S2 + 6 H+ = 1 Na+ + 2 SO4-2 + 3 Fe+3 + 6 H2O + logk -5.4482 +K-Feldspar + AlKO8Si3 + 4 H+ = 1 Al+3 + 1 K+ + 2 H2O + 3 SiO2 + logk -0.1903 + -analytical -39.1881 -0.0160588 2780.73 13.8944 39.2611 +K2CO3 + K2CO3 + 1 H+ = 2 K+ + 1 HCO3- + logk 15.3005 + -analytical -111.85 -0.0390095 5207.73 49.0185 79.8423 +K2CO3:1.5H2O + CH3K2O4.5 + 1 H+ = 1 HCO3- + 1.5 H2O + 2 K+ + logk 13.372 +K2O + K2O + 2 H+ = 2 K+ + 1 H2O + logk 83.9772 + -analytical -28.8514 -0.00693573 23635 14.3929 368.44 +K2Si4O9 + K2Si4O9 + 2 H+ = 2 K+ + 4 SiO2 + 1 H2O + logk 14.5138 + -analytical 131.573 0.0212645 -2762.32 -46.1627 -48.077 +K3H(SO4)2 + HK3O8S2 = 1 H+ + 2 SO4-2 + 3 K+ + logk -3.5425 +K8H4(CO3)6:3H2O + C6H10K8O21 + 2 H+ = 3 H2O + 6 HCO3- + 8 K+ + logk 27.6874 +Kainite + ClH6KMgO7S = 1 Cl- + 1 K+ + 1 Mg+2 + 1 SO4-2 + 3 H2O + logk -0.1926 +KAlCl4 + KAlCl4 = 1 K+ + 1 Al+3 + 4 Cl- + logk 34.4833 + -analytical -428.057 -0.162067 21877.4 176.765 335.659 +K2HPO4 + K2HPO4 = 2 K+ + 1 HPO4-2 + logk 3.0685 + -analytical -195.505 -0.0755722 5083.55 82.4484 76.5202 +K3AlCl6 + K3AlCl6 = 3 K+ + 1 Al+3 + 6 Cl- + logk 31.6052 + -analytical -608.409 -0.231678 22951.9 255.404 349.726 +K3AlF6 + K3AlF6 = 3 K+ + 1 Al+3 + 6 F- + logk -24.8505 + -analytical -716.589 -0.244902 14448.8 289.423 216.605 +K3PO4 + K3PO4 + 1 H+ = 3 K+ + 1 HPO4-2 + logk 13.592 + -analytical -196.721 -0.0768887 7288.22 84.361 110.711 +Kalicinite + CHKO3 = 1 HCO3- + 1 K+ + logk 0.2814 +KAl(SO4)2 + KAlS2O8 = 1 K+ + 1 Al+3 + 2 SO4-2 + logk 2.6929 + -analytical -426.225 -0.162245 14916.3 172.632 226.639 +KAl(SO4)2:3H2O + KAlS2H6O11 = 1 K+ + 1 Al+3 + 2 SO4-2 + 3 H2O + logk -1.3959 + -analytical -459.007 -0.162612 13804.4 185.779 209.3 +KAl(SO4)2:12H2O + KAlS2H24O20 = 1 K+ + 1 Al+3 + 2 SO4-2 + 12 H2O + logk -6.9137 + -analytical -931.403 -0.269691 23122.4 374.73 354.751 +Kaolinite + Al2H4O9Si2 + 6 H+ = 2 Al+3 + 2 SiO2 + 5 H2O + logk 5.9539 + -analytical -203.122 -0.0520307 13011.9 73.098 199.397 +KBr + KBr = 1 K+ + 1 Br- + logk 1.0666 + -analytical -81.8576 -0.0334454 731.358 36.5428 10.0411 +KClO4 + KClO4 = 1 K+ + 1 ClO4- + logk -1.6258 + -analytical -81.9695 -0.0313607 -523.872 36.9517 -9.30192 +KH2PO4 + KH2PO4 = 1 K+ + 1 H+ + 1 HPO4-2 + logk -7.7296 + -analytical -187.562 -0.0718743 2493.12 77.9413 36.2576 +KI + KI = 1 K+ + 1 I- + logk 1.9904 + -analytical -83.203 -0.033878 862.419 37.3349 12.1618 +Kieserite + H2MgO5S = 1 H2O + 1 Mg+2 + 1 SO4-2 + logk -0.0239 + -analytical -257.978 -0.0857205 8476.33 103.067 129.075 +KMgCl3:2H2O + Cl3H4KMgO2 = 1 K+ + 1 Mg+2 + 2 H2O + 3 Cl- + logk 13.9755 +KNaCO3:6H2O + CH12KNaO9 + 1 H+ = 1 HCO3- + 1 K+ + 1 Na+ + 6 H2O + logk 10.2233 +KOH + KOH + 1 H+ = 1 K+ + 1 H2O + logk 24.6647 + -analytical -23.3935 -0.0050498 6860.47 10.7295 106.872 +Labile_Salt + Na2Ca5S6O27H6 = 2 Na+ + 5 Ca+2 + 6 SO4-2 + 3 H2O + logk 8.4195 + -analytical 38460 10.6799 -1.03009e+006 -15428.3 -16485.5 +Lansfordite + CH10MgO8 + 1 H+ = 1 HCO3- + 1 Mg+2 + 5 H2O + logk 4.8409 +Laumontite + Al4Na0.2H16K0.2O32Si8Ca1.8 + 16 H+ = 1.8 Ca+2 + 4 Al+3 + 0.2 K+ + 8 SiO2 + 16 H2O + 0.2 Na+ + logk 14.2657 + -analytical -427.311 -0.106284 28437.6 152.618 431.152 +Leonhardtite + MgSO8H8 = 1 Mg+2 + 1 SO4-2 + 4 H2O + logk -1.0359 + -analytical -321.17 -0.095544 9319.98 128.236 142.257 +Leonite + H8K2MgO12S2 = 1 Mg+2 + 2 K+ + 2 SO4-2 + 4 H2O + logk -3.979 +Lime + CaO + 2 H+ = 1 Ca+2 + 1 H2O + logk 32.5761 + -analytical -72.6693 -0.0176566 12198.2 28.1225 189.89 +Magnesite + CMgO3 + 1 H+ = 1 HCO3- + 1 Mg+2 + logk 2.2936 + -analytical -166.64 -0.0494695 6433.95 65.501 98.7795 +Magnetite + Fe3O4 + 8 H+ = 1 Fe+2 + 2 Fe+3 + 4 H2O + logk 10.4724 + -analytical -354.562 -0.0832123 20709.8 129.461 321.141 +Maximum_Microcline + AlKO8Si3 + 4 H+ = 1 Al+3 + 1 K+ + 2 H2O + 3 SiO2 + logk -0.1903 + -analytical -22.7208 -0.0123868 2277.5 7.47861 31.3282 +Mercallite + HKO4S = 1 H+ + 1 K+ + 1 SO4-2 + logk -1.4015 +Mesolite + Al1.99Ca0.657H5.294Na0.676O12.647Si3.01 + 7.96 H+ = 0.657 Ca+2 + 0.676 Na+ + 1.99 Al+3 + 3.01 SiO2 + 6.627 H2O + logk 13.029 + -analytical -288.55 -0.0715675 17625.2 106.569 269.563 +MgBr2 + MgBr2 = 1 Mg+2 + 2 Br- + logk 27.6871 + -analytical -222.676 -0.0788434 14476 91.0389 222.884 +MgCl2:H2O + MgCl2H2O = 1 Mg+2 + 2 Cl- + 1 H2O + logk 16.1034 + -analytical -251.326 -0.0834369 12045.1 101.785 184.998 +MgCl2:2H2O + MgCl2H4O2 = 1 Mg+2 + 2 Cl- + 2 H2O + logk 12.75 + -analytical -264.212 -0.0843976 11198.5 106.901 171.793 +MgCl2:4H2O + MgCl2H8O4 = 1 Mg+2 + 2 Cl- + 4 H2O + logk 7.3158 + -analytical -292.065 -0.0863525 9957.61 117.879 152.44 +MgI2 + MgI2 = 1 Mg+2 + 2 I- + logk 34.442 + -analytical -224.276 -0.0796544 16045.5 92.3869 247.526 +MgMoO4 + MgMoO4 = 1 Mg+2 + 1 MoO4-2 + logk -0.629 + -analytical -256.115 -0.0850102 8858.39 101.467 135.248 +Mg(NO3)2 + MgN2O6 = 1 Mg+2 + 2 NO3- + logk 15.1741 + -analytical -220.129 -0.0753944 9766.18 90.9228 149.625 +MgOHCl + ClHMgO + 1 H+ = 1 Cl- + 1 H2O + 1 Mg+2 + logk 15.937 + -analytical -172.644 -0.0513614 10516.4 68.1342 162.333 +MgSO4 + MgSO4 = 1 Mg+2 + 1 SO4-2 + logk 4.8599 + -analytical -235.402 -0.0823751 9624.62 93.9569 146.985 +Minnesotaite + Fe3H2O12Si4 + 6 H+ = 3 Fe+2 + 4 H2O + 4 SiO2 + logk 15.0002 + -analytical -87.5504 -0.0243312 9515.69 31.4318 142.459 +Mirabilite + H20Na2O14S = 1 SO4-2 + 2 Na+ + 10 H2O + logk -1.2185 + -analytical 28437.8 8.55876 -735896 -11526.8 -11945 +Misenite + H6K8O28S7 = 6 H+ + 7 SO4-2 + 8 K+ + logk -10.8061 +Molysite + Cl3Fe = 1 Fe+3 + 3 Cl- + logk 13.5217 + -analytical -359.51 -0.127377 15532.8 145.021 238.032 +Montmorillonite-H + Al1.67Mg0.33O12Si4H2.33 + 5.67 H+ = 0.33 Mg+2 + 4 SiO2 + 1.67 Al+3 + 4 H2O + logk 1.4445 + -analytical -5.475 -0.0180293 4485.71 -1.15793 63.8907 +Montmorillonite-Na + Al1.67Mg0.33O12Si4H2Na0.33 + 6 H+ = 0.33 Mg+2 + 4 SiO2 + 1.67 Al+3 + 4 H2O + 0.33 Na+ + logk 2.4583 + -analytical -11.7311 -0.0190783 4822.23 1.44975 69.061 +Montmorillonite-K + Al1.67Mg0.33O12Si4H2K0.33 + 6 H+ = 0.33 Mg+2 + 4 SiO2 + 1.67 Al+3 + 4 H2O + 0.33 K+ + logk 2.1194 + -analytical -5.91544 -0.0187375 4349.68 -0.437789 61.7181 +Montmorillonite-Ca + Al1.67Mg0.33O12Si4H2Ca0.165 + 6 H+ = 0.33 Mg+2 + 4 SiO2 + 1.67 Al+3 + 4 H2O + 0.165 Ca+2 + logk 2.4024 + -analytical -14.2327 -0.0207093 5121.69 2.2288 73.7363 +Montmorillonite-Mg + Al1.67Mg0.495O12Si4H2 + 6 H+ = 0.495 Mg+2 + 4 SiO2 + 1.67 Al+3 + 4 H2O + logk 2.3643 + -analytical -16.8393 -0.0208289 5367.19 2.94824 77.5495 +Mordenite + Al0.94Ca0.2895H6.936Na0.361O15.468Si5.06 + 3.76 H+ = 0.2895 Ca+2 + 0.361 Na+ + 0.94 Al+3 + 5.06 SiO2 + 5.348 H2O + logk -4.5423 + -analytical -7.70644 -0.00723919 886.672 0.898215 7.13797 +NaBr + BrNa = 1 Br- + 1 Na+ + logk 2.9204 + -analytical -99.9637 -0.0344769 2469.74 42.3765 37.0884 +NaClO4 + NaClO4 = 1 Na+ + 1 ClO4- + logk 2.835 + -analytical -99.0835 -0.0320422 1973.68 42.3667 29.5879 +NaI + NaI = 1 I- + 1 Na+ + logk 5.1306 + -analytical -101.21 -0.0349104 2993.79 43.1159 45.3437 +NaNO2 + NaNO2 = 1 Na+ + 1 NO2- + logk 0.7016 + -analytical -104.952 -0.0349199 1925.99 44.2863 28.6574 +NaOH + NaOH + 1 H+ = 1 Na+ + 1 H2O + logk 20.905 + -analytical -45.0002 -0.006464 6987.09 17.9401 108.752 +Na2CO3:7H2O + CH14Na2O10 + 1 H+ = 1 HCO3- + 2 Na+ + 7 H2O + logk 9.8791 +Na2CrO4 + Na2CrO4 = 2 Na+ + 1 CrO4-2 + logk 3.5616 + -analytical -204.311 -0.0723054 5715.26 84.9547 86.1783 +Na2MoO4 + Na2MoO4 = 2 Na+ + 1 MoO4-2 + logk 1.2688 + -analytical -230.053 -0.074205 6098.8 94.1412 92.2479 +Na2O + Na2O + 2 H+ = 2 Na+ + 1 H2O + logk 66.8982 + -analytical -68.4307 -0.00968139 20906.7 27.514 325.733 +Na2SO4(Sol-3) + Na2O4S = 1 SO4-2 + 2 Na+ + logk -0.0439 + -analytical -214.407 -0.0737189 5228.49 88.4075 78.5437 +Na3H(SO4)2 + HNa3O8S2 = 1 H+ + 2 SO4-2 + 3 Na+ + logk -0.8143 +Kogarkoite + Na3FSO4 = 3 Na+ + 1 SO4-2 + 1 F- + logk -2.0247 + -analytical -346.388 -0.114175 8087.89 141.934 121.604 +Na4Ca(SO4)3:2H2O + CaH4Na4O14S3 = 1 Ca+2 + 2 H2O + 3 SO4-2 + 4 Na+ + logk -5.6723 +Nahcolite + CHNaO3 = 1 HCO3- + 1 Na+ + logk -0.7061 + -analytical -126.649 -0.0375254 2376.99 52.1891 35.7544 +Natrite + Na2CO3 + 1 H+ = 2 Na+ + 1 HCO3- + logk 10.984 + -analytical -153.713 -0.0421446 6497.79 62.8199 99.786 +Natron + CH20Na2O13 + 1 H+ = 1 HCO3- + 2 Na+ + 10 H2O + logk 9.5145 +Natrolite + Al2H4Na2O12Si3 + 8 H+ = 2 Al+3 + 2 Na+ + 3 SiO2 + 6 H2O + logk 17.9209 + -analytical -260.613 -0.0605425 17595.7 95.9672 269.131 +Nesquehonite + CH6MgO6 + 1 H+ = 1 HCO3- + 1 Mg+2 + 3 H2O + logk 4.9955 + -analytical -973.21 -0.199929 33093.3 374.694 527.872 +NH4Cl + NH4Cl = 1 NH4+ + 1 Cl- + logk 1.3355 + -analytical -110.674 -0.0397043 1904.08 47.4624 28.4331 +NH4ClO4 + NH4ClO4 = 1 NH4+ + 1 ClO4- + logk -0.1071 + -analytical -101.387 -0.0324767 1029.37 43.4417 14.9483 +NH4I + NH4I = 1 NH4+ + 1 I- + logk 3.4008 + -analytical -99.2224 -0.0362741 1799.99 43.3972 26.8649 +(NH4)2SO4 + N2H8SO4 = 2 NH4+ + 1 SO4-2 + logk 0.3081 + -analytical -212.438 -0.0759948 4570.8 88.9218 68.5163 +Niter + KNO3 = 1 K+ + 1 NO3- + logk -0.2344 + -analytical -76.0784 -0.0312641 -137.446 34.5969 -3.39538 +Nontronite-Mg + Al0.33Fe2H2Mg0.165O12Si3.67 + 7.32 H+ = 0.165 Mg+2 + 0.33 Al+3 + 2 Fe+3 + 3.67 SiO2 + 4.66 H2O + logk -11.1382 + -analytical -123.074 -0.0406227 5836.33 42.1746 84.9876 +Nontronite-Ca + Al0.33Ca0.165Fe2H2O12Si3.67 + 7.32 H+ = 0.165 Ca+2 + 0.33 Al+3 + 2 Fe+3 + 3.67 SiO2 + 4.66 H2O + logk -11.1001 + -analytical -120.485 -0.0405049 5591.73 41.4613 81.1889 +Nontronite-K + Al0.33Fe2H2K0.33O12Si3.67 + 7.32 H+ = 0.33 Al+3 + 0.33 K+ + 2 Fe+3 + 3.67 SiO2 + 4.66 H2O + logk -11.3831 + -analytical -112.234 -0.0385467 4822.37 38.8194 69.2107 +Nontronite-Na + Al0.33Fe2H2Na0.33O12Si3.67 + 7.32 H+ = 0.33 Al+3 + 0.33 Na+ + 2 Fe+3 + 3.67 SiO2 + 4.66 H2O + logk -11.0442 + -analytical -117.972 -0.0388729 5291.3 40.6788 76.4989 +Nontronite-H + Al0.33Fe2H2.33O12Si3.67 + 6.99 H+ = 0.33 Al+3 + 2 Fe+3 + 3.67 SiO2 + 4.66 H2O + logk -12.766 +Oxychloride-Mg + ClH11Mg2O7 + 3 H+ = 1 Cl- + 2 Mg+2 + 7 H2O + logk 26.0297 +Pentahydrite + MgSO9H10 = 1 Mg+2 + 1 SO4-2 + 5 H2O + logk -1.4274 + -analytical -342.214 -0.0987946 9568.38 136.637 146.138 +Pentasalt + K2Ca5S6O25H2 = 2 K+ + 5 Ca+2 + 6 SO4-2 + 1 H2O + logk -24.5549 + -analytical -3392.14 -0.920064 96302.2 1341.02 1516.11 +Periclase + MgO + 2 H+ = 1 H2O + 1 Mg+2 + logk 21.3354 + -analytical -88.4517 -0.0183922 10413.8 32.4644 161.938 +Phillipsite + Al3.6K0.7H25.2Na0.7O44.6Si12.4Ca1.1 + 14.4 H+ = 1.1 Ca+2 + 3.6 Al+3 + 0.7 K+ + 12.4 SiO2 + 19.8 H2O + 0.7 Na+ + logk -6.7617 + -analytical -231.538 -0.0535576 13553.9 78.7908 194.565 +Picromerite + H12K2MgO14S2 = 1 Mg+2 + 2 K+ + 2 SO4-2 + 6 H2O + logk -4.3277 +Pirssonite + C2CaH4Na2O8 + 2 H+ = 1 Ca+2 + 2 H2O + 2 HCO3- + 2 Na+ + logk 11.4354 +Polyhalite + Ca2H4K2MgO18S4 = 1 Mg+2 + 2 Ca+2 + 2 H2O + 2 K+ + 4 SO4-2 + logk -13.7441 +Portlandite + CaH2O2 + 2 H+ = 1 Ca+2 + 2 H2O + logk 22.5444 + -analytical -73.2644 -0.016935 8931.3 28.6505 138.93 +Pyrolusite + MnO2 + 4 H+ + 2 e- = 1 Mn+2 + 2 H2O + logk 42.0291 + -analytical -106.219 -0.0206254 15716.4 41.09 245.066 +Pyrophyllite + Al2H2O12Si4 + 6 H+ = 2 Al+3 + 4 H2O + 4 SiO2 + logk 0.0967 + -analytical -103.794 -0.0343486 8115.34 35.0785 120.666 +Quartz + O2Si = 1 SiO2 + logk -3.7501 + -analytical 37.4476 0.00715916 -2409.12 -14.2549 -38.712 +Ripidolite-7A + Al2Fe2H8Mg3O18Si3 + 16 H+ = 2 Al+3 + 2 Fe+2 + 3 Mg+2 + 3 SiO2 + 12 H2O + logk 62.5511 + -analytical -403.178 -0.110363 40665.9 146.332 626.677 +Ripidolite-14A + Al2Fe2H8Mg3O18Si3 + 16 H+ = 2 Al+3 + 2 Fe+2 + 3 Mg+2 + 3 SiO2 + 12 H2O + logk 59.1778 + -analytical -409.603 -0.110945 40146.1 148.341 618.583 +Saponite-H + Al0.33H2.33Mg3O12Si3.67 + 6.99 H+ = 0.33 Al+3 + 3 Mg+2 + 3.67 SiO2 + 4.66 H2O + logk 26.0453 + -analytical -102.844 -0.0289635 14319 36.122 217.242 +Saponite-Na + Al0.33H2Mg3Na0.33O12Si3.67 + 7.32 H+ = 0.33 Al+3 + 0.33 Na+ + 3 Mg+2 + 3.67 SiO2 + 4.66 H2O + logk 27.0591 + -analytical -109.019 -0.0299953 14652.3 38.6992 222.363 +Saponite-K + Al0.33H2K0.33Mg3O12Si3.67 + 7.32 H+ = 0.33 Al+3 + 0.33 K+ + 3 Mg+2 + 3.67 SiO2 + 4.66 H2O + logk 26.7202 + -analytical -103.316 -0.0296781 14184 36.8544 215.084 +Saponite-Ca + Al0.33Ca0.165H2Mg3O12Si3.67 + 7.32 H+ = 0.165 Ca+2 + 0.33 Al+3 + 3 Mg+2 + 3.67 SiO2 + 4.66 H2O + logk 27.0032 + -analytical -111.543 -0.0316298 14953.5 39.4852 227.066 +Saponite-Mg + Al0.33H2Mg3.165O12Si3.67 + 7.32 H+ = 0.33 Al+3 + 3.165 Mg+2 + 3.67 SiO2 + 4.66 H2O + logk 26.9651 + -analytical -114.362 -0.0318084 15195.8 40.3021 230.828 +Scolecite + Al2CaH6O13Si3 + 8 H+ = 1 Ca+2 + 2 Al+3 + 3 SiO2 + 7 H2O + logk 15.2772 + -analytical -306.272 -0.0778402 19236.3 113.211 294.689 +Sellaite + F2Mg = 1 Mg+2 + 2 F- + logk -9.3939 + -analytical -272.983 -0.0864825 6936.96 107.523 105.155 +Sepiolite + H14Mg4O23Si6 + 8 H+ = 4 Mg+2 + 6 SiO2 + 11 H2O + logk 30.4439 + -analytical 645.753 0.123088 -12819.1 -246.225 -212.722 +Sepiolite(am) + H14Mg4O23Si6 + 8 H+ = 4 Mg+2 + 6 SiO2 + 11 H2O + logk 37.56 +Palygorskite + H18Mg2.84O30Si7.73Al1.8 + 11.08 H+ = 2.84 Mg+2 + 7.73 SiO2 + 14.54 H2O + 1.8 Al+3 + logk 26.4078 +SiO2(am) + O2Si = 1 SiO2 + logk -2.7136 + -analytical 119.521 0.0216204 -5003.03 -45.2365 -79.6958 +Smectite-high-Fe-Mg + Al1.25Na0.1O12Ca0.025Mg1.15Fe0.7Si3.5K0.2H2 + 8 H+ = 0.025 Ca+2 + 0.1 Na+ + 0.2 Fe+3 + 0.2 K+ + 0.5 Fe+2 + 1.15 Mg+2 + 1.25 Al+3 + 3.5 SiO2 + 5 H2O + logk 17.4595 + -analytical -120.561 -0.040678 13637.1 42.1483 206.65 +Smectite-low-Fe-Mg + Al1.25Ca0.02Fe0.45H2K0.2Mg0.9Na0.15O12Si3.75 + 7 H+ = 0.02 Ca+2 + 0.15 Na+ + 0.16 Fe+3 + 0.2 K+ + 0.29 Fe+2 + 0.9 Mg+2 + 1.25 Al+3 + 3.75 SiO2 + 4.5 H2O + logk 11.1541 + -analytical -67.7478 -0.0292278 9381.15 22.6458 140.224 +Soda_Niter + NaNO3 = 1 NO3- + 1 Na+ + logk 1.0915 + -analytical -50.0994 -0.0220762 -2.0548 23.3421 -0.0171532 +Strontianite + CO3Sr + 1 H+ = 1 HCO3- + 1 Sr+2 + logk -0.3137 + -analytical -135.757 -0.0448838 3572.56 55.2929 54.1777 +SrBr2 + SrBr2 = 1 Sr+2 + 2 Br- + logk 12.8191 + -analytical -196.207 -0.0751381 8035.5 82.6175 122.422 +SrCl2 + SrCl2 = 1 Sr+2 + 2 Cl- + logk 8.1397 + -analytical -209.915 -0.0784405 7357.76 87.5837 111.899 +SrF2 + SrF2 = 1 Sr+2 + 2 F- + logk -6.5188 + -analytical -243.668 -0.0825289 5654.34 98.1007 85.1706 +SrI2 + SrI2 = 1 Sr+2 + 2 I- + logk 19.2679 + -analytical -198.847 -0.076178 9586 84.3157 146.773 +SrMoO4 + SrMoO4 = 1 Sr+2 + 1 MoO4-2 + logk -6.4216 + -analytical -227.719 -0.0812871 4822.32 92.6735 72.3052 +SrO + SrO + 2 H+ = 1 Sr+2 + 1 H2O + logk 41.9787 + -analytical -62.5567 -0.01527 14576.8 24.323 226.907 +Sr(OH)2 + SrO2H2 + 2 H+ = 1 Sr+2 + 2 H2O + logk 27.5229 + -analytical -87.6211 -0.018271 10820.7 34.0634 168.305 +Stellerite + Al4Ca2H28O50Si14 + 16 H+ = 2 Ca+2 + 4 Al+3 + 22 H2O + 14 SiO2 + logk -8.7844 + -analytical -271.187 -0.0664942 16345.6 91.7545 235.931 +Stilbite + Al2.18Ca1.019H14.66K0.006Na0.136O25.33Si6.82 + 8.72 H+ = 0.006 K+ + 0.136 Na+ + 1.019 Ca+2 + 2.18 Al+3 + 6.82 SiO2 + 11.69 H2O + logk 1.3118 + -analytical -212.595 -0.0562391 12015.4 76.8614 177.716 +Sylvite + ClK = 1 Cl- + 1 K+ + logk 0.9148 + -analytical -48.8224 -0.02655 -209.296 23.579 -4.24164 +Syngenite + CaH2K2O9S2 = 1 Ca+2 + 1 H2O + 2 K+ + 2 SO4-2 + logk -7.2618 + -analytical -854.406 -0.2438 22047.4 341.811 349.474 +Tachyhydrite + CaCl6H24Mg2O12 = 1 Ca+2 + 2 Mg+2 + 6 Cl- + 12 H2O + logk 17.3839 +Talc + H2Mg3O12Si4 + 6 H+ = 3 Mg+2 + 4 H2O + 4 SiO2 + logk 22.1646 + -analytical -121.962 -0.0258707 13192.9 43.4337 199.721 +Thenardite + Na2O4S = 1 SO4-2 + 2 Na+ + logk -0.2547 + -analytical -216.072 -0.0708355 5537.18 88.2498 90.2371 +Thermonatrite + CH2Na2O4 + 1 H+ = 1 H2O + 1 HCO3- + 2 Na+ + logk 10.7669 +Trona + C2H5Na3O8 + 1 H+ = 2 H2O + 2 HCO3- + 3 Na+ + logk 9.2948 +Trona-K + C2H5K2NaO8 + 1 H+ = 1 Na+ + 2 H2O + 2 HCO3- + 2 K+ + logk 11.5757 +Villiaumite + NaF = 1 Na+ + 1 F- + logk -0.2538 + -analytical -123.927 -0.0381649 3160.69 50.2853 47.8165 +Whitlockite + Ca3O8P2 + 2 H+ = 2 HPO4-2 + 3 Ca+2 + logk -4.2249 +Brucite + H2MgO2 + 2 H+ = 1 Mg+2 + 2 H2O + logk 17.1 +Chromium + Cr = 1 Cr+3 + 3 e- + logk 36.1367 +Eskolaite + Cr2O3 + 6 H+ = 3 H2O + 2 Cr+3 + logk 8.52 +Cr(OH)3(am) + CrO3H3 + 3 H+ = 1 Cr+3 + 3 H2O + logk 9.35 +CrCl3:6H2O + CrCl3O6H12 = 1 Cr+3 + 3 Cl- + 6 H2O + logk 5.906 +CrF3 + CrF3 = 1 Cr+3 + 3 F- + logk -9.0957 + -analytical -403.567 -0.131794 13130.8 157.472 200.362 +KFe3(CrO4)2(OH)6 + Cr2O14H6KFe3 + 6 H+ = 2 CrO4-2 + 1 K+ + 3 Fe+3 + 6 H2O + logk -18.4 +Na2CrO4:4H2O + CrNa2O8H8 = 1 CrO4-2 + 2 Na+ + 4 H2O + logk 1.01 +Tarapacaite + CrK2O4 = 1 CrO4-2 + 2 K+ + logk 0.0408 +Lopezite + Cr2K2O7 + 1 H2O = 2 CrO4-2 + 2 K+ + 2 H+ + logk -16.066 +CrO3 + CrO3 + 1 H2O = 1 CrO4-2 + 2 H+ + logk -3.2201 + -analytical -128.362 -0.062213 2041.66 55.2881 29.3875 +CuCr2O4 + Cr2CuO4 + 8 H+ = 1 Cu+2 + 2 Cr+3 + 4 H2O + logk 20.14 +Copper + Cu = 1 Cu+2 + 2 e- + logk -11.4902 + -analytical -70.1074 -0.0181655 295.75 25.4734 3.79609 +CuCl2:2H2O + Cl2H4CuO2 = 1 Cu+2 + 2 Cl- + 2 H2O + logk 1.9413 +CuSO4:5H2O + SH10CuO9 = 1 Cu+2 + 1 SO4-2 + 5 H2O + logk -2.646 +CuSO4.Na2SO4:2H2O + S2H4CuO10Na2 = 1 Cu+2 + 2 SO4-2 + 2 H2O + 2 Na+ + logk -3.917 +CuSO4.K2SO4:6H2O + S2H12CuO14K2 = 1 Cu+2 + 2 SO4-2 + 6 H2O + 2 K+ + logk -5.759 +Cu3(PO4)2 + Cu3O8P2 + 2 H+ = 2 HPO4-2 + 3 Cu+2 + logk -12.2247 +Iron + Fe = 1 Fe+2 + 2 e- + logk 16.0308 + -analytical -61.4832 -0.0180471 8161.48 22.4334 126.503 +Fe(OH)2 + FeH2O2 + 2 H+ = 1 Fe+2 + 2 H2O + logk 13.8214 + -analytical -93.2809 -0.0196505 7765.12 35.1221 120.7 +Fe(OH)3 + FeH3O3 + 3 H+ = 1 Fe+3 + 3 H2O + logk 4.1 +Siderite + CFeO3 + 1 H+ = 1 Fe+2 + 1 HCO3- + logk -0.192 + -analytical -159.192 -0.0491676 5475.43 62.7493 83.8344 +FeF2 + F2Fe = 1 Fe+2 + 2 F- + logk -1.432 + -analytical -258.724 -0.0847399 8751.77 102.309 133.523 +FeO + FeO + 2 H+ = 1 Fe+2 + 1 H2O + logk 13.5318 + -analytical -77.9861 -0.0180652 7664.02 28.7697 119.069 +FeSO4 + FeO4S = 1 Fe+2 + 1 SO4-2 + logk 1.9396 + -analytical -218.679 -0.0801956 8107.57 87.8133 123.432 +Melanterite + FeO11SH14 = 1 Fe+2 + 1 SO4-2 + 7 H2O + logk -2.209 +Ferrite-Ca + CaFe2O4 + 8 H+ = 1 Ca+2 + 2 Fe+3 + 4 H2O + logk 21.5945 + -analytical -335.289 -0.0794065 22744.2 122.952 352.968 +Ferrite-Cu + CuFe2O4 + 8 H+ = 1 Cu+2 + 2 Fe+3 + 4 H2O + logk 9.5266 + -analytical -363.028 -0.0834245 20712.7 132.523 321.231 +Ferrite-Dicalcium + Ca2Fe2O5 + 10 H+ = 2 Ca+2 + 2 Fe+3 + 5 H2O + logk 56.8114 + -analytical -412.555 -0.0984499 35896.9 152.873 557.792 +Ferrite-Mg + Fe2MgO4 + 8 H+ = 1 Mg+2 + 2 Fe+3 + 4 H2O + logk 20.9808 + -analytical -356.219 -0.0809191 24193.9 129.379 375.5 +Ferrite-Ni + NiFe2O4 + 8 H+ = 1 Ni+2 + 2 Fe+3 + 4 H2O + logk 9.7959 + -analytical -347.865 -0.083186 20253.9 127.096 313.981 +Ferrite-Zn + Fe2O4Zn + 8 H+ = 1 Zn+2 + 2 Fe+3 + 4 H2O + logk 11.7342 +Hausmannite + Mn3O4 + 8 H+ = 1 Mn+2 + 2 Mn+3 + 4 H2O + logk 11.6746 + -analytical -335.335 -0.0797562 21018.2 121.342 325.932 +Hydrozincite + C2H6O12Zn5 + 8 H+ = 2 HCO3- + 5 Zn+2 + 6 H2O + logk 30.3076 +Lawrencite + Cl2Fe = 1 Fe+2 + 2 Cl- + logk 9.0646 + -analytical -225.762 -0.0814275 9178.47 92.2536 140.307 +Cobalt + Co = 1 Co+2 + 2 e- + logk 9.5288 +Co(OH)2 + CoH2O2 + 2 H+ = 1 Co+2 + 2 H2O + logk 13.0671 + -analytical -88.2631 -0.0186574 7545.06 32.9678 117.225 +Co2SiO4 + Co2O4Si + 4 H+ = 1 SiO2 + 2 Co+2 + 2 H2O + logk 6.6808 +Co3(PO4)2 + Co3O8P2 + 2 H+ = 2 HPO4-2 + 3 Co+2 + logk -10.0123 +CoCl2 + Cl2Co = 1 Co+2 + 2 Cl- + logk 8.2904 + -analytical -221.121 -0.0801568 8799.84 90.4252 134.445 +CoCl2:2H2O + Cl2CoH4O2 = 1 Co+2 + 2 Cl- + 2 H2O + logk 4.6661 +CoCl2:6H2O + Cl2CoH12O6 = 1 Co+2 + 2 Cl- + 6 H2O + logk 2.6033 +CoCr2O4 + Cr2CoO4 + 8 H+ = 1 Co+2 + 2 Cr+3 + 4 H2O + logk 15.0442 + -analytical -345.313 -0.0828394 22711.8 124.812 352.337 +CoF2 + CoF2 = 1 Co+2 + 2 F- + logk -1.5187 + -analytical -261.172 -0.0855105 8814.7 103.271 134.511 +CoFe2O4 + CoFe2O4 + 8 H+ = 1 Co+2 + 2 Fe+3 + 4 H2O + logk 10.0104 + -analytical -359.482 -0.0847019 20752.4 131.385 321.806 +CoHPO4 + CoHO4P = 1 Co+2 + 1 HPO4-2 + logk -6.7223 +CoO + CoO + 2 H+ = 1 Co+2 + 1 H2O + logk 13.5557 + -analytical -77.6767 -0.0182812 7635.76 28.7184 118.603 +CoSO4:3Co(OH)2 + Co4H6O10S + 6 H+ = 1 SO4-2 + 4 Co+2 + 6 H2O + logk 33.2193 +CoSO4:6H2O + CoH12O10S = 1 Co+2 + 1 SO4-2 + 6 H2O + logk -2.3512 +Bieberite + CoH14O11S = 1 Co+2 + 1 SO4-2 + 7 H2O + logk -2.5051 +Malachite + CCu2H2O5 + 3 H+ = 1 HCO3- + 2 Cu+2 + 2 H2O + logk 5.9399 + -analytical -271.871 -0.0694553 11450.8 105.106 176.606 +Mg2(OH)3Cl:4H2O + H11Mg2O7Cl + 3 H+ = 2 Mg+2 + 7 H2O + 1 Cl- + logk 26 +Manganite + HMnO2 + 3 H+ = 1 Mn+3 + 2 H2O + logk 0.3485 +Manganosite + MnO + 2 H+ = 1 H2O + 1 Mn+2 + logk 18.3638 + -analytical -83.7781 -0.0184898 8817.86 31.5504 137.094 +Manganese + Mn = 1 Mn+2 + 2 e- + logk 40.3883 +Mn(OH)2(am) + H2MnO2 + 2 H+ = 1 Mn+2 + 2 H2O + logk 15.75 +Rhodochrosite + CMnO3 + 1 H+ = 1 HCO3- + 1 Mn+2 + logk 0.247 + -analytical -161.587 -0.0493345 5131.9 64.3811 78.5244 +MnCl2:4H2O + Cl2H8MnO4 = 1 Mn+2 + 2 Cl- + 4 H2O + logk 3.1961 +MnSO4 + MnO4S = 1 Mn+2 + 1 SO4-2 + logk 3.1105 + -analytical -230.662 -0.082732 8210.74 93.2946 125.022 +Molybdite + MoO3 + 1 H2O = 1 MoO4-2 + 2 H+ + logk -12.055 +Powellite + CaMoO4 = 1 Ca+2 + 1 MoO4-2 + logk -7.93 +MoO2Cl2 + Cl2O2Mo + 2 H2O = 2 Cl- + 1 MoO4-2 + 4 H+ + logk 0.5936 + -analytical -306.755 -0.128496 7342.49 129.712 109.773 +Sodium + Na = 1 Na+ + 1 e- + logk 45.8795 +Nickel + Ni = 1 Ni+2 + 2 e- + logk 7.9895 + -analytical -57.7146 -0.0180043 5937.78 20.6696 91.7918 +NiCO3 + CNiO3 + 1 H+ = 1 HCO3- + 1 Ni+2 + logk -0.8712 +NiCl2:4H2O + Cl2H8NiO4 = 1 Ni+2 + 2 Cl- + 4 H2O + logk 3.8561 +Nickelbischofite + Cl2H12NiO6 = 1 Ni+2 + 2 Cl- + 6 H2O + logk 3.062 +NiMoO4 + NiMoO4 = 1 Ni+2 + 1 MoO4-2 + logk -6.5033 + -analytical -241.401 -0.084022 6381.84 96.3854 96.6458 +Ni(OH)2 + H2NiO2 + 2 H+ = 1 Ni+2 + 2 H2O + logk 10.52 +Ni2P2O7 + Ni2O7P2 + 1 H2O = 2 HPO4-2 + 2 Ni+2 + logk -8.8991 +Ni3(PO4)2 + Ni3O8P2 + 2 H+ = 2 HPO4-2 + 3 Ni+2 + logk -6.6414 +Ni4CrO4(OH)6 + Ni4CrO10H6 + 6 H+ = 4 Ni+2 + 1 CrO4-2 + 6 H2O + logk 32.9 +NiSO4:7H2O + H14NiO11S = 1 Ni+2 + 1 SO4-2 + 7 H2O + logk -2.206 +NiCr2O4 + Cr2NiO4 + 8 H+ = 1 Ni+2 + 2 Cr+3 + 4 H2O + logk 23.6681 + -analytical -343.761 -0.0827847 25464.9 123.931 395.282 +NiF2:4H2O + F2H8NiO4 = 1 Ni+2 + 2 F- + 4 H2O + logk -4.0588 +CsClO4 + CsO4Cl = 1 Cs+ + 1 ClO4- + logk -2.406 +Witherite + BaCO3 + 1 H+ = 1 Ba+2 + 1 HCO3- + logk 1.7639 + -analytical -124.02 -0.0440044 3379.13 51.546 51.2183 +Zinc + Zn = 1 Zn+2 + 2 e- + logk 25.8015 +Zincite + OZn + 2 H+ = 1 H2O + 1 Zn+2 + logk 11.2089 + -analytical -84.4401 -0.0180238 7084.24 31.22 110.014 +Zn(BO2)2 + B2O4Zn + 2 H+ + 2 H2O = 1 Zn+2 + 2 B(OH)3 + logk 8.313 +Zn(ClO4)2:6H2O + Cl2H12O14Zn = 1 Zn+2 + 2 ClO4- + 6 H2O + logk 5.6474 +Zn(NO3)2:6H2O + H12N2O12Zn = 1 Zn+2 + 2 NO3- + 6 H2O + logk 3.4102 +Zn(OH)2(beta) + H2O2Zn + 2 H+ = 1 Zn+2 + 2 H2O + logk 11.9341 +Zn(OH)2(epsilon) + H2O2Zn + 2 H+ = 1 Zn+2 + 2 H2O + logk 11.6625 +Zn(OH)2(gamma) + H2O2Zn + 2 H+ = 1 Zn+2 + 2 H2O + logk 11.8832 +Zn2(OH)3Cl + ClH3O3Zn2 + 3 H+ = 1 Cl- + 2 Zn+2 + 3 H2O + logk 15.2921 +Zn2SO4(OH)2 + H2O6SZn2 + 2 H+ = 1 SO4-2 + 2 H2O + 2 Zn+2 + logk 7.5816 +Zn3O(SO4)2 + O9S2Zn3 + 2 H+ = 1 H2O + 2 SO4-2 + 3 Zn+2 + logk 19.1188 +Zn5(NO3)2(OH)8 + H8N2O14Zn5 + 8 H+ = 2 NO3- + 5 Zn+2 + 8 H2O + logk 42.6674 +ZnBr2:2H2O + Br2H4O2Zn = 1 Zn+2 + 2 Br- + 2 H2O + logk 5.2999 +ZnCO3:H2O + CH2O4Zn + 1 H+ = 1 H2O + 1 HCO3- + 1 Zn+2 + logk 0.1398 +ZnCl2 + Cl2Zn = 1 Zn+2 + 2 Cl- + logk 7.088 +ZnCl2(NH3)2 + Cl2H6N2Zn + 2 H+ = 1 Zn+2 + 2 Cl- + 2 NH4+ + logk 11.4864 +ZnCl2(NH3)4 + Cl2H12N4Zn + 4 H+ = 1 Zn+2 + 2 Cl- + 4 NH4+ + logk 30.2685 +ZnCl2(NH3)6 + Cl2H18N6Zn + 6 H+ = 1 Zn+2 + 2 Cl- + 6 NH4+ + logk 50.7149 +ZnCr2O4 + Cr2O4Zn + 8 H+ = 1 Zn+2 + 2 Cr+3 + 4 H2O + logk 11.8387 +ZnF2 + F2Zn = 1 Zn+2 + 2 F- + logk -0.4418 + -analytical -260.92 -0.0839315 9068.36 103.07 138.426 +ZnSO4:6H2O + H12O10SZn = 1 SO4-2 + 1 Zn+2 + 6 H2O + logk -1.6846 +ZnSO4:7H2O + H14O11SZn = 1 SO4-2 + 1 Zn+2 + 7 H2O + logk -1.8683 +ZnSO4:H2O + H2O5SZn = 1 H2O + 1 SO4-2 + 1 Zn+2 + logk -0.5383 +CH4(g) + CH4 + 3 H2O = 9 H+ + 1 HCO3- + 8 e- + logk -30.7169 + -analytical -34.1957 -0.0297767 -5519.41 12.4685 -88.4722 +CO2(g) + CO2 + 1 H2O = 1 H+ + 1 HCO3- +# logk -7.8136 + logk -7.8136 + -analytical -85.9871 -0.0304437 2071.68 32.4462 31.2334 +H2(g) + H2 = 2 e- + 2 H+ + logk -0.0001 + -analytical 5.02511 0.000229386 1524.1 -4.12472 23.4667 +H2O(g) + H2O = 1 H2O + logk 1.4999 + -analytical -21.8582 -0.000298333 2978.01 5.43886 46.4689 +HBr(g) + BrH = 1 Br- + 1 H+ + logk 8.866 + -analytical -81.3888 -0.0299616 5950.24 32.0121 91.6273 +HCl(g) + HCl = 1 H+ + 1 Cl- + logk 6.3064 + -analytical -87.702 -0.0314755 5564.12 34.2351 85.6274 +HF(g) + HF = 1 H+ + 1 F- + logk 1.2448 + -analytical -103.571 -0.0334319 5391.71 39.0716 82.8597 +HNO3(g) + HNO3 = 1 H+ + 1 NO3- + logk 6.4719 + -analytical -81.6735 -0.0271648 5470.01 31.4743 84.2668 +N2O5(g) + N2O5 + 1 H2O = 2 H+ + 2 NO3- + logk 17.9824 + -analytical -129.582 -0.0529138 9484.49 53.1421 145.821 +NO3(g) + NO3 + 1 e- = 1 NO3- + logk 39.7629 + -analytical -73.6811 -0.0274276 14838.8 29.0302 230.591 +O2(g) + O2 + 4 e- + 4 H+ = 2 H2O + logk 83.1056 + -analytical -48.8642 -0.000578392 27769.6 15.7603 433.903 +## Added by EK / Florian +Chlorite + Mg2.5Fe2.5Al2Si3O10(OH)8 + 16H+ = 3SiO2 + 2.5Fe+2 + 2.5Mg+2 + 12H2O + 2Al+3 + log_k 50.0560 + + +RATES # rates data block defines mathematical expressions for the rates of kinetic reactions + +Albite # linear regression + -start + 10 moles=0 + 20 IF ((m<=0) and (SI("Albite")<0)) then goto 200 + 30 R=8.314462 # in J*K-1*mol-1 + 40 deltaT=1/TK-1/298.15 # wird für 35°C berechnet; TK is temp in Kelvin + 50 e=2.718282 # Eulersche Zahl + ## mechanism 1 (acid) + 60 Ea=65000 # Aktivierungsenergie in J/mol => 65.0 in KJ/mol + 70 logK25=-9.87 # Reaktionskonstante für Kfs bei 25°C, pH=0, in mol/m2*s1 + 80 ny=0.457 # Reaktionsordnung + 90 mech_a=(10^logK25)*(e^(-Ea/R*deltaT))*ACT("H+")^ny ## (10^logK25)*(e^(-Ea/R*deltaT)) computes K for T=35°C; ACT=activity + ## mechanism 2 (base) + 100 Ea=71000 + 110 logK25=-16.98 + 120 ny=-0.572 + 130 mech_b=(10^logK25)*(e^(-Ea/R*deltaT))*ACT("H+")^ny + ## base term (neutral mechanism) + 140 Ea=69800 + 150 logK25=-12.04 + 160 mech_c=(10^logK25)*(e^(-Ea/R*deltaT)) + 170 rate=mech_a+mech_b+mech_c + 180 IF SI("Albite")<0 then moles=parm(1)*M*rate*(1-SR("Albite")) # dissolution + 190 IF SI("Albite")>0 then moles=parm(2)*rate*(1-SR("Albite")) # precipitation + 200 save moles*time + -end + + +Anhydrite # only data for neutral mechanism available in Palandri and Kharaka 2004 + -start + 10 moles=0 + 20 IF ((m<=0) and (SI("Anhydrite")<0)) then goto 200 + 30 R=8.314462 + 40 deltaT=1/TK-1/298.15 + 50 e=2.718282 + # base term (neutral mechanism) + 140 Ea=14.300 + 150 logK25=-3.19 + 160 mech_c=(10^logK25)*(e^(-Ea/R*deltaT)) + 170 rate=mech_c + 180 IF SI("Anhydrite")<0 then moles=parm(1)*M*rate*(1-SR("Anhydrite")) + 190 IF SI("Anhydrite")>0 then moles=parm(2)*rate*(1-SR("Anhydrite")) + 200 save moles*time + -end + +Chlorite # added by Florian Stroscher (FS), Clinochlore 14 Å. + -start + 10 moles=0 + 20 IF ((m<=0) and (SI("Chlorite")<0)) then goto 200 + 30 R=8.314462 + 40 deltaT=1/TK-1/298.15 + 50 e=2.718282 + # mechanism 1 (acid) + 60 Ea=88000 + 70 logK25=-11.11 + 80 ny=0.500 + 90 mech_a=(10^logK25)*(e^(-Ea/R*deltaT))*ACT("H+")^ny + # base term (neutral mechanism) + 140 Ea=88000 + 150 logK25=-12.52 + 160 mech_c=(10^logK25)*(e^(-Ea/R*deltaT)) + 170 rate=mech_a+mech_c + 180 IF SI("Chlorite")<0 then moles=parm(1)*M*rate*(1-SR("Chlorite")) + 190 IF SI("Chlorite")>0 then moles=parm(2)*rate*(1-SR("Chlorite")) + 200 save moles*time + -end + +Calcite + -start + 10 moles=0 + 20 IF ((m<=0) and (SI("Calcite")<0)) then goto 200 + 30 R=8.314462 + 40 deltaT=1/TK-1/298.15 + 50 e=2.718282 + # mechanism 1 (acid) + 60 Ea=14400 + 70 logK25=-0.30 + 80 ny=1 + 90 mech_a=(10^logK25)*(e^(-Ea/R*deltaT))*ACT("H+")^ny + # mechanism 2 (base) + 100 Ea=35400 + 110 logK25=-3.48 + 120 ny=1 + 130 mech_b=(10^logK25)*(e^(-Ea/R*deltaT))*ACT("H+")^ny + # base term (neutral mechanism) + 140 Ea=23500 + 150 logK25=-5.81 + 160 mech_c=(10^logK25)*(e^(-Ea/R*deltaT)) + 170 rate=mech_a+mech_b+mech_c + 180 IF SI("Calcite")<0 then moles=parm(1)*M*rate*(1-SR("Calcite")) + 190 IF SI("Calcite")>0 then moles=parm(2)*rate*(1-SR("Calcite")) + 200 save moles*time + -end + +Hematite # added by FS + -start + 10 moles=0 + 20 IF ((m<=0) and (SI("Hematite")<0)) then goto 200 + 30 R=8.314462 + 40 deltaT=1/TK-1/298.15 + 50 e=2.718282 + # mechanism 1 (acid) + 60 Ea=66200 + 70 logK25=-9.39 + 80 ny=1.000 + 90 mech_a=(10^logK25)*(e^(-Ea/R*deltaT))*ACT("H+")^ny + # base term (neutral mechanism) + 140 Ea=66200 + 150 logK25=-14.60 + 160 mech_c=(10^logK25)*(e^(-Ea/R*deltaT)) + 170 rate=mech_a+mech_c + 180 IF SI("Hematite")<0 then moles=parm(1)*M*rate*(1-SR("Hematite")) + 190 IF SI("Hematite")>0 then moles=parm(2)*rate*(1-SR("Hematite")) + 200 save moles*time + -end + +Illite # no data; instead smectite data taken + -start + 10 moles=0 + 20 IF ((m<=0) and (SI("Illite")<0)) then goto 200 + 30 R=8.314462 + 40 deltaT=1/TK-1/298.15 + 50 e=2.718282 + # mechanism 1 (acid) + 60 Ea=23600 + 70 logK25=-10.98 + 80 ny=0.340 + 90 mech_a=(10^logK25)*(e^(-Ea/R*deltaT))*ACT("H+")^ny + ## mechanism 2 (base) + 100 Ea=58900 + 110 logK25=-16.52 + 120 ny=-0.400 + 130 mech_b=(10^logK25)*(e^(-Ea/R*deltaT))*ACT("H+")^ny + ## base term (neutral mechanism) + 140 Ea=35000 + 150 logK25=-12.78 + 160 mech_c=(10^logK25)*(e^(-Ea/R*deltaT)) + 170 rate=mech_a+mech_b+mech_c + 180 IF SI("Illite")<0 then moles=parm(1)*M*rate*(1-SR("Illite")) + 190 IF SI("Illite")>0 then moles=parm(2)*rate*(1-SR("Illite")) + 200 save moles*time + -end + +K-Feldspar # (doesnt show up? - we have to take a closer look) + -start + 10 moles=0 + 20 IF ((m<=0) and (SI("K-Feldspar")<0)) then goto 200 + 30 R=8.314462 + 40 deltaT=1/TK-1/298.15 + 50 e=2.718282 + ## mechanism 1 (acid) + 60 Ea=51700 + 70 logK25=-10.06 + 80 ny=0.500 + 90 mech_a=(10^logK25)*(e^(-Ea/R*deltaT))*ACT("H+")^ny + ## mechanism 2 (base) + 100 Ea=94100 + 110 logK25=-21.20 + 120 ny=-0.823 + 130 mech_b=(10^logK25)*(e^(-Ea/R*deltaT))*ACT("H+")^ny + ## base term (neutral mechanism) + 140 Ea=38000 + 150 logK25=-12.41 + 160 mech_c=(10^logK25)*(e^(-Ea/R*deltaT)) + 170 rate=mech_a+mech_b+mech_c + 180 IF SI("K-Feldspar")<0 then moles=parm(1)*M*rate*(1-SR("K-Feldspar")) + 190 IF SI("K-Feldspar")>0 then moles=parm(2)*rate*(1-SR("K-Feldspar")) + 200 save moles*time + -end + +Kaolinite # added by FS + -start + 10 moles=0 + 20 IF ((m<=0) and (SI("Kaolinite")<0)) then goto 200 + 30 R=8.314462 + 40 deltaT=1/TK-1/298.15 + 50 e=2.718282 + # mechanism 1 (acid) + 60 Ea=65900 + 70 logK25=-11.31 + 80 ny=0.777 + 90 mech_a=(10^logK25)*(e^(-Ea/R*deltaT))*ACT("H+")^ny + ## mechanism 2 (base) + 100 Ea=17900 + 110 logK25=-17.05 + 120 ny=-0.472 + 130 mech_b=(10^logK25)*(e^(-Ea/R*deltaT))*ACT("H+")^ny + # base term (neutral mechanism) + 140 Ea=22200 + 150 logK25=-13.18 + 160 mech_c=(10^logK25)*(e^(-Ea/R*deltaT)) + 170 rate=mech_a+mech_b+mech_c + 180 IF SI("Kaolinite")<0 then moles=parm(1)*M*rate*(1-SR("Kaolinite")) + 190 IF SI("Kaolinite")>0 then moles=parm(2)*rate*(1-SR("Kaolinite")) + 200 save moles*time + -end + +Nesquehonite # added by FS + -start + 10 moles=0 + 20 IF ((m<=0) and (SI("Nesquehonite")<0)) then goto 200 + 30 R=8.314462 + 40 deltaT=1/TK-1/298.15 + 50 e=2.718282 + # mechanism 1 (acid) + 60 Ea=14400 + 70 logK25=-6.38 + 80 ny=1.000 + 90 mech_a=(10^logK25)*(e^(-Ea/R*deltaT))*ACT("H+")^ny + ## mechanism 2 (Carbonate Mechanism) + 100 Ea=62800 + 110 logK25=-3.48 + 120 ny=1.000 + 125 PCO2 = 10^SI("CO2(g)") + 130 mech_b=(10^logK25)*(e^(-Ea/R*deltaT))*PCO2^ny + # base term (neutral mechanism) + 140 Ea=23500 + 150 logK25=-9.34 + 160 mech_c=(10^logK25)*(e^(-Ea/R*deltaT)) + 170 rate=mech_a+mech_b+mech_c + 180 IF SI("Nesquehonite")<0 then moles=parm(1)*M*rate*(1-SR("Nesquehonite")) + 190 IF SI("Nesquehonite")>0 then moles=parm(2)*rate*(1-SR("Nesquehonite")) + 200 save moles*time + -end + +Siderite # added by FS, aus "Klein-2013.pdf" (Folder: "Kinetic_Ketzin_ex3"!!! + -start + 10 moles=0 + 20 IF ((m<=0) and (SI("Siderite")<0)) then goto 200 + 30 R=8.314462 + 40 deltaT=1/TK-1/298.15 + 50 e=2.718282 + # mechanism 1 (acid) + 60 Ea=48000 + 70 logK25=-3.747 + 80 ny=0.75 + 90 mech_a=(10^logK25)*(e^(-Ea/R*deltaT))*ACT("H+")^ny + # base term (neutral mechanism) + 140 Ea=62760 + 150 logK25=-8.9 + 160 mech_c=(10^logK25)*(e^(-Ea/R*deltaT)) + 170 rate=mech_a+mech_c + 180 IF SI("Siderite")<0 then moles=parm(1)*M*rate*(1-SR("Siderite")) + 190 IF SI("Siderite")>0 then moles=parm(2)*rate*(1-SR("Siderite")) + 200 save moles*time + -end + +END + diff --git a/src/parallel_r_library.R b/src/parallel_r_library.R new file mode 100644 index 000000000..1b1f1c035 --- /dev/null +++ b/src/parallel_r_library.R @@ -0,0 +1,43 @@ +distribute_work_packages <- function(len, package_size) +{ + ## Check if work_package is a divisor of grid length and act + ## accordingly + if ((len %% package_size) == 0) { + n_packages <- len/package_size + } else { + n_packages <- floor(len/package_size) + 1 + } + + ids <- rep(1:n_packages, times=package_size, each = 1)[1:len] + return(ids) +} + +compute_wp_sizes <- function(ids) +{ + as.integer(table(ids)) +} + +shuffle_field <- function(data, send_ord) +{ + shf <- data[send_ord,] + return(shf) +} + +unshuffle_field <- function(data, send_ord) +{ + data[send_ord,] <- data + rownames(data) <- NULL + return(data) +} + +stat_wp_sizes <- function(sizes) +{ + res <- as.data.frame(table(sizes)) + if (nrow(res)>1) { + msgm("Chosen work_package_size is not a divisor of grid length. ") + colnames(res) <- c("Size", "N") + print(res) + } else { + msgm("All work packages of length ", sizes[1]) + } +} diff --git a/src/r_utils.cpp b/src/r_utils.cpp new file mode 100644 index 000000000..eafa33fa6 --- /dev/null +++ b/src/r_utils.cpp @@ -0,0 +1,44 @@ +#include "r_utils.h" + +/* This function converts a pure double dataframe into a double array. + buffer <- double array, needs to be allocated before + df <- reference to input dataframe +*/ +void convert_R_Dataframe_2_C_buffer(double* buffer, Rcpp::DataFrame &df) +{ + size_t rowCount = df.nrow(); + size_t colCount = df.ncol(); + + for (size_t i = 0; i < rowCount; i++) + { + for (size_t j = 0; j < colCount; j++) + { + /* Access column vector j and extract value of line i */ + Rcpp::DoubleVector col = df[j]; + buffer[i * colCount + j] = col[i]; + } + } +} + +/* This function converts a double array into a double dataframe. + buffer <- input double array + df <- reference to output dataframe, needs to be of fitting size, structure will be taken from it +*/ +void convert_C_buffer_2_R_Dataframe(double* buffer, Rcpp::DataFrame &df) +{ + size_t rowCount = df.nrow(); + size_t colCount = df.ncol(); + + for (size_t i = 0; i < rowCount; i++) + { + for (size_t j = 0; j < colCount; j++) + { + /* Access column vector j and extract value of line i */ + Rcpp::DoubleVector col = df[j]; + col[i] = buffer[i * colCount + j]; + } + } +} + + + diff --git a/src/r_utils.h b/src/r_utils.h new file mode 100644 index 000000000..95000e97f --- /dev/null +++ b/src/r_utils.h @@ -0,0 +1,6 @@ +#pragma once +#include + +/*Functions*/ +void convert_R_Dataframe_2_C_buffer(double* buffer, Rcpp::DataFrame &df); +void convert_C_buffer_2_R_Dataframe(double* buffer, Rcpp::DataFrame &df); \ No newline at end of file diff --git a/src/worker.cpp b/src/worker.cpp new file mode 100644 index 000000000..880b71f51 --- /dev/null +++ b/src/worker.cpp @@ -0,0 +1,277 @@ +#include "worker.h" +#include "dht_wrapper.h" +#include "global_buffer.h" +#include "r_utils.h" +#include +#include + +void worker_function(RInside& R) +{ + int world_rank; + MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); + MPI_Status probe_status; + int count; + + int local_work_package_size; + int iteration; + double dt, current_sim_time; + + double idle_a, idle_b; + double cummul_idle = 0.f; + + double dht_get_start=0, dht_get_end=0; + double dht_fill_start=0, dht_fill_end=0; + double phreeqc_time_start=0, phreeqc_time_end=0; + int phreeqc_count = 0; + + //timing[0] -> phreeqc + //timing[1] -> dht_get + //timing[2] -> dht_fill + double timing[3]; + timing[0] = 0.0; + timing[1] = 0.0; + timing[2] = 0.0; + + //dht_perf[0] -> hits + //dht_perf[1] -> miss + //dht_perf[2] -> collisions + uint64_t dht_perf[3]; + + if (dht_enabled) + { + dht_flags.resize(work_package_size, true); //set size + dht_flags.assign(work_package_size, true); //assign all elements to true (default) + dht_hits = 0; + dht_miss = 0; + dht_collision = 0; + + + // MDL: This code has now been moved to kin.cpp + // /*Load significance vector from R setup file (or set default)*/ + // bool signif_vector_exists = R.parseEval("exists('signif_vector')"); + // if (signif_vector_exists) + // { + // dht_significant_digits_vector = as>(R["signif_vector"]); + // } else + // { + // dht_significant_digits_vector.assign(dht_object->key_size / sizeof(double), dht_significant_digits); + // } + + // /*Load property type vector from R setup file (or set default)*/ + // bool prop_type_vector_exists = R.parseEval("exists('prop_type')"); + // if (prop_type_vector_exists) + // { + // prop_type_vector = as>(R["prop_type"]); + // } else + // { + // prop_type_vector.assign(dht_object->key_size / sizeof(double), "normal"); + // } + } + + //initialization of helper variables + iteration = 0; + dt = 0; + current_sim_time = 0; + local_work_package_size = 0; + + /*worker loop*/ + while(1) + { + /*Wait for Message*/ + idle_a = MPI_Wtime(); + MPI_Probe(0, MPI_ANY_TAG, MPI_COMM_WORLD, &probe_status); + idle_b = MPI_Wtime(); + + if (probe_status.MPI_TAG == TAG_WORK) + { /* do work */ + + cummul_idle += idle_b - idle_a; + + /* get number of doubles sent */ + MPI_Get_count(&probe_status, MPI_DOUBLE, &count); + + /* receive */ + MPI_Recv(mpi_buffer, count, MPI_DOUBLE, 0, TAG_WORK, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + + //decrement count of work_package by BUFFER_OFFSET + count -= BUFFER_OFFSET; + //check for changes on all additional variables given by the 'header' of mpi_buffer + if (mpi_buffer[count] != local_work_package_size) { //work_package_size + local_work_package_size = mpi_buffer[count]; + R["work_package_size"] = local_work_package_size; + R.parseEvalQ("mysetup$work_package_size <- work_package_size"); + } + if (mpi_buffer[count+1] != iteration) { //current iteration of simulation + iteration = mpi_buffer[count+1]; + R["iter"] = iteration; + R.parseEvalQ("mysetup$iter <- iter"); + } + if (mpi_buffer[count+2] != dt) { //current timestep size + dt = mpi_buffer[count+2]; + R["dt"] = dt; + R.parseEvalQ("mysetup$dt <- dt"); + } + if (mpi_buffer[count+3] != current_sim_time) { //current simulation time ('age' of simulation) + current_sim_time = mpi_buffer[count+3]; + R["simulation_time"] = current_sim_time; + R.parseEvalQ("mysetup$simulation_time <- simulation_time"); + } + /* 4th double value is currently a placeholder */ + // if (mpi_buffer[count+4] != placeholder) { + // placeholder = mpi_buffer[count+4]; + // R["mysetup$placeholder"] = placeholder; + // } + + /* get df with right structure to fill in work package */ + R.parseEvalQ("tmp2 <- head(mysetup$state_C, work_package_size)"); + // R.parseEval("print(rownames(tmp2)[1:5])"); + // R.parseEval("print(head(tmp2, 2))"); + // R.parseEvalQ("tmp2$id <- as.double(rownames(tmp2))"); + + Rcpp::DataFrame buffer = R.parseEval("tmp2"); + + if (dht_enabled) + { + // DEBUG + // cout << "RANK " << world_rank << " start checking DHT\n"; + + //resize helper vector dht_flags of work_package_size changes + if ((int) dht_flags.size() != local_work_package_size) { + dht_flags.resize(local_work_package_size, true); //set size + dht_flags.assign(local_work_package_size, true); //assign all elements to true (default) + } + + dht_get_start = MPI_Wtime(); + check_dht(R, local_work_package_size, dht_flags, mpi_buffer); + dht_get_end = MPI_Wtime(); + + //DEBUG + //cout << "RANK " << world_rank << " checking DHT complete \n"; + + R["dht_flags"] = as(wrap(dht_flags)); + //R.parseEvalQ("print(head(dht_flags))"); + } + + /* work */ + convert_C_buffer_2_R_Dataframe(mpi_buffer, buffer); + R["work_package_full"] = buffer; + //R["work_package"] = buffer; + + //DEBUG + //R.parseEvalQ("print(head(work_package_full))"); + //R.parseEvalQ("print( c(length(dht_flags), nrow(work_package_full)) )"); + + if (dht_enabled) + { + R.parseEvalQ("work_package <- work_package_full[dht_flags,]"); + } else { + R.parseEvalQ("work_package <- work_package_full"); + } + + //DEBUG + // R.parseEvalQ("print(head(work_package),2)"); + + // R.parseEvalQ("rownames(work_package) <- work_package$id"); + // R.parseEval("print(paste('id %in% colnames(work_package)', 'id' %in% colnames(work_package)"); + // R.parseEvalQ("id_store <- rownames(work_package)"); //"[, ncol(work_package)]"); + // R.parseEvalQ("work_package$id <- NULL"); + R.parseEvalQ("work_package <- as.matrix(work_package)"); + + unsigned int nrows = R.parseEval("nrow(work_package)"); + + if (nrows > 0) + { + /*Single Line error Workaround*/ + if (nrows <=1) + { + //duplicate line to enable correct simmulation + R.parseEvalQ("work_package <- work_package[rep(1:nrow(work_package), times = 2), ]"); + } + + phreeqc_count++; + + phreeqc_time_start = MPI_Wtime(); + // MDL + // R.parseEvalQ("print('Work_package:\n'); print(head(work_package , 2)); cat('RCpp: worker_function:', local_rank, ' \n')"); + R.parseEvalQ("result <- as.data.frame(slave_chemistry(setup=mysetup, data = work_package))"); + phreeqc_time_end = MPI_Wtime(); + // R.parseEvalQ("result$id <- id_store"); + } else + { + //cout << "Work-Package is empty, skipping phreeqc!" << endl; + } + + + if (dht_enabled) + { + R.parseEvalQ("result_full <- work_package_full"); + if (nrows > 0) + R.parseEvalQ("result_full[dht_flags,] <- result"); + } else { + R.parseEvalQ("result_full <- result"); + } + + Rcpp::DataFrame result = R.parseEval("result_full"); + convert_R_Dataframe_2_C_buffer(mpi_buffer_results, result); + + /* send results to master */ + MPI_Request send_req; + MPI_Isend(mpi_buffer_results, count, MPI_DOUBLE, 0, TAG_WORK, MPI_COMM_WORLD, &send_req); + + if (dht_enabled) + { + dht_fill_start = MPI_Wtime(); + fill_dht(R, local_work_package_size, dht_flags, mpi_buffer, mpi_buffer_results); + dht_fill_end = MPI_Wtime(); + + timing[1] += dht_get_end - dht_get_start; + timing[2] += dht_fill_end - dht_fill_start; + } + + timing[0] += phreeqc_time_end - phreeqc_time_start; + + MPI_Wait(&send_req,MPI_STATUS_IGNORE); + + } else if (probe_status.MPI_TAG == TAG_FINISH) + { /* recv and die */ + /* before death, submit profiling/timings to master*/ + + MPI_Recv(NULL, 0, MPI_DOUBLE, 0, TAG_FINISH, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + + //timings + MPI_Send(timing, 3, MPI_DOUBLE, 0, TAG_TIMING, MPI_COMM_WORLD); + + MPI_Send(&phreeqc_count, 1, MPI_INT, 0, TAG_TIMING, MPI_COMM_WORLD); + MPI_Send(&cummul_idle, 1, MPI_DOUBLE, 0, TAG_TIMING, MPI_COMM_WORLD); + + if(dht_enabled) + { + //dht_perf + dht_perf[0] = dht_hits; + dht_perf[1] = dht_miss; + dht_perf[2] = dht_collision; + MPI_Send(dht_perf, 3, MPI_UNSIGNED_LONG_LONG, 0, TAG_DHT_PERF, MPI_COMM_WORLD); + } + + break; + + } else if ((probe_status.MPI_TAG == TAG_DHT_STATS)) { + MPI_Recv(NULL, 0, MPI_DOUBLE, 0, TAG_DHT_STATS, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + print_statistics(); + MPI_Barrier(MPI_COMM_WORLD); + } else if ((probe_status.MPI_TAG == TAG_DHT_STORE)) { + char* outdir; + MPI_Get_count(&probe_status, MPI_CHAR, &count); + outdir = (char *) calloc(count + 1, sizeof(char)); + MPI_Recv(outdir, count, MPI_CHAR, 0, TAG_DHT_STORE, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + int res = table_to_file((char *) outdir); + if (res != DHT_SUCCESS) { + if (world_rank == 2) cerr << "CPP: Worker: Error in writing current state of DHT to file (TAG_DHT_STORE)" << endl; + } else { + if (world_rank == 2) cout << "CPP: Worker: Successfully written DHT to file " << outdir << endl; + } + free(outdir); + MPI_Barrier(MPI_COMM_WORLD); + } + } +} diff --git a/src/worker.h b/src/worker.h new file mode 100644 index 000000000..221a1f7a3 --- /dev/null +++ b/src/worker.h @@ -0,0 +1,17 @@ +#pragma once +#include + +using namespace std; +using namespace Rcpp; + +/*Functions*/ +void worker_function(RInside &R); + + +/*Globals*/ +#define TAG_WORK 42 +#define TAG_FINISH 43 +#define TAG_TIMING 44 +#define TAG_DHT_PERF 45 +#define TAG_DHT_STATS 46 +#define TAG_DHT_STORE 47 \ No newline at end of file