From 724ba7ee08211497f9c781c821e4ecb7ec6a02d5 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 3 Aug 2022 17:17:16 +0100 Subject: [PATCH 001/244] Add io aggregator buffer --- src/cs_limits.h | 11 ++++++++- src/io_aggr_buf.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++ src/io_aggr_buf.h | 31 ++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 src/io_aggr_buf.c create mode 100644 src/io_aggr_buf.h diff --git a/src/cs_limits.h b/src/cs_limits.h index 9a0ab98e5..f4087ce47 100644 --- a/src/cs_limits.h +++ b/src/cs_limits.h @@ -2,7 +2,7 @@ * * cs_limits.h * - * A container (only) for a cubiodal region. + * A container for a cubiodal region. * *****************************************************************************/ @@ -20,4 +20,13 @@ struct cs_limits_s { int kmax; }; +static inline int cs_limits_size(cs_limits_t lim) { + + int szx = 1 + lim.imax - lim.imin; + int szy = 1 + lim.jmax - lim.jmin; + int szz = 1 + lim.kmax - lim.kmin; + + return szx*szy*szz; +} + #endif diff --git a/src/io_aggr_buf.c b/src/io_aggr_buf.c new file mode 100644 index 000000000..9282c1f69 --- /dev/null +++ b/src/io_aggr_buf.c @@ -0,0 +1,61 @@ +/***************************************************************************** + * + * io_aggr_buf.c + * + * Temporary buffer for lattice quantity i/o. A minimal container. + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include + +#include "io_aggr_buf.h" + +/***************************************************************************** + * + * io_aggr_buf_create + * + *****************************************************************************/ + +int io_aggr_buf_create(size_t lsz, cs_limits_t lim, io_aggr_buf_t * aggr) { + + assert(aggr); + assert(cs_limits_size(lim) > 0); /* No zero-size buffers */ + + *aggr = (io_aggr_buf_t) {0}; + + aggr->szelement = lsz; + aggr->szbuf = lsz*sizeof(char)*cs_limits_size(lim); + aggr->lim = lim; + + aggr->buf = (char *) malloc(aggr->szbuf*sizeof(char)); + assert(aggr->buf); + + return 0; +} + +/***************************************************************************** + * + * io_aggr_buf_free + * + *****************************************************************************/ + +int io_aggr_buf_free(io_aggr_buf_t * aggr) { + + assert(aggr); + assert(aggr->buf); + + free(aggr->buf); + + *aggr = (io_aggr_buf_t) {0}; + + return 0; +} + diff --git a/src/io_aggr_buf.h b/src/io_aggr_buf.h new file mode 100644 index 000000000..11def4634 --- /dev/null +++ b/src/io_aggr_buf.h @@ -0,0 +1,31 @@ +/***************************************************************************** + * + * io_aggr_buf.h + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#ifndef LUDWIG_IO_AGGR_BUF_H +#define LUDWIG_IO_AGGR_BUF_H + +#include "cs_limits.h" + +typedef struct io_aggr_buf_s io_aggr_buf_t; + +struct io_aggr_buf_s { + size_t szelement; /* element sz in bytes */ + size_t szbuf; /* total sz */ + cs_limits_t lim; /* 3-d limits of buffer */ + char * buf; +}; + +int io_aggr_buf_create(size_t lsz, cs_limits_t lim, io_aggr_buf_t * aggr); +int io_aggr_buf_free(io_aggr_buf_t * aggr); + +#endif From 6ee44588429a40fd88352ee78a56748c777fabf7 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 3 Aug 2022 17:18:10 +0100 Subject: [PATCH 002/244] Add io buffer element --- src/field.c | 77 +++++++++++++++++++++++++++++ src/field.h | 8 ++- tests/unit/test_field.c | 105 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+), 1 deletion(-) diff --git a/src/field.c b/src/field.c index 5c517f8a7..406fe5e55 100644 --- a/src/field.c +++ b/src/field.c @@ -951,6 +951,83 @@ static int field_write(FILE * fp, int index, void * self) { return 0; } +int field_write_buf(field_t * field, int index, char * buf) { + + /* const field_t * field = (const field_t *) self;*/ + double array[NQAB] = {0}; + + assert(field); + + field_scalar_array(field, index, array); + memcpy(buf, array, field->nf*sizeof(double)); + + return 0; +} + +int field_read_buf(field_t * field, int index, const char * buf) { + + double array[NQAB] = {0}; + + assert(field); + assert(buf); + + memcpy(array, buf, field->nf*sizeof(double)); + field_scalar_array_set(field, index, array); + + return 0; +} + +int field_write_buf_ascii(field_t * field, int index, char * buf) { + + /* const field_t * field = (const field_t *) self;*/ + double array[NQAB] = {0}; + + int nbyte = 23; + int ifail = 0; + + assert(field); + assert(buf); + + field_scalar_array(field, index, array); + + /* We will overwrite any `\0` coming from sprintf() */ + /* Use tmp with +1 to allow for the \0 */ + + for (int n = 0; n < field->nf; n++) { + char tmp[nbyte + 1] = {0}; + int np = snprintf(tmp, nbyte + 1, " %22.15e", array[n]); + if (np != nbyte) ifail = 1; + memcpy(buf + n*nbyte, tmp, nbyte*sizeof(char)); + if (n == field->nf - 1) { + np = snprintf(tmp, 2, "\n"); + if (np != 1) ifail = 2; + memcpy(buf + field->nf*nbyte, tmp, sizeof(char)); + } + } + + return ifail; +} + +int field_read_buf_ascii(field_t * field, int index, const char * buf) { + + double array[NQAB] = {0}; + + int nbyte = 23; + int ifail = 0; + + assert(field); + assert(buf); + + for (int n = 0; n < field->nf; n++) { + int nr = sscanf(buf + n*nbyte, "%le", array + n); + if (nr != 1) ifail = 1; + } + + field_scalar_array_set(field, index, array); + + return ifail; +} + /***************************************************************************** * * field_write_ascii diff --git a/src/field.h b/src/field.h index 8333b2f61..d305162e7 100644 --- a/src/field.h +++ b/src/field.h @@ -5,7 +5,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2012-2021 The University of Edinburgh + * (c) 2012-2022 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -104,4 +104,10 @@ __host__ __device__ int field_scalar_array(field_t * obj, int index, __host__ __device__ int field_scalar_array_set(field_t * obj, int index, const double * array); + +int field_read_buf(field_t * field, int index, const char * buf); +int field_read_buf_ascii(field_t * field, int index, const char * buf); +int field_write_buf(field_t * field, int index, char * buf); +int field_write_buf_ascii(field_t * field, int index, char * buf); + #endif diff --git a/tests/unit/test_field.c b/tests/unit/test_field.c index 2b405556d..dc5b46036 100644 --- a/tests/unit/test_field.c +++ b/tests/unit/test_field.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "pe.h" @@ -37,6 +38,8 @@ static int test_field_halo(cs_t * cs, field_t * phi); int do_test_device1(pe_t * pe); int test_field_halo_create(pe_t * pe); +int test_field_write_buf(pe_t * pe); +int test_field_write_buf_ascii(pe_t * pe); __global__ void do_test_field_kernel1(field_t * phi); @@ -65,6 +68,10 @@ int test_field_suite(void) { test_field_halo_create(pe); + /* Experimental ... */ + test_field_write_buf(pe); + test_field_write_buf_ascii(pe); + pe_info(pe, "PASS ./unit/test_field\n"); pe_free(pe); @@ -485,3 +492,101 @@ int test_field_halo_create(pe_t * pe) { return 0; } + +/***************************************************************************** + * + * test_field_write_buf + * + * It is convenient to test field_read_buf() at the same time. + * + *****************************************************************************/ + +int test_field_write_buf(pe_t * pe) { + + int nf = 3; /* Test field */ + + cs_t * cs = NULL; + field_t * field = NULL; + field_options_t options = field_options_ndata_nhalo(nf, 1); + + assert(pe); + + cs_create(pe, &cs); + cs_init(cs); + field_create(pe, cs, NULL, "test_write_buf", &options, &field); + + { + double array[nf] = {1.0, 2.0, 3.0}; + char buf[nf*sizeof(double)] = {0}; + int index = cs_index(cs, 2, 3, 4); + + field_scalar_array_set(field, index, array); + field_write_buf(field, index, buf); + + { + double val[nf] = {0}; + + field_read_buf(field, index + 1, buf); + field_scalar_array(field, index + 1, val); + + assert(fabs(val[0] - array[0]) < DBL_EPSILON); + assert(fabs(val[1] - array[1]) < DBL_EPSILON); + assert(fabs(val[2] - array[2]) < DBL_EPSILON); + } + } + + field_free(field); + cs_free(cs); + + return 0; +} + +/***************************************************************************** + * + * test_field_write_buf_ascii + * + * It is convenient to test field_read_buf_ascii() at the same time. + * + *****************************************************************************/ + +int test_field_write_buf_ascii(pe_t * pe) { + + int nf = 5; /* Test field */ + + cs_t * cs = NULL; + field_t * field = NULL; + field_options_t options = field_options_ndata_nhalo(nf, 1); + + cs_create(pe, &cs); + cs_init(cs); + field_create(pe, cs, NULL, "test_field_write_buf_ascii", &options, &field); + + { + double array[nf] = {1.0, 3.0, 2.0, -4.0, -5.0}; + char buf[BUFSIZ] = {0}; + int index = cs_index(cs, 1, 2, 3); + + field_scalar_array_set(field, index, array); + field_write_buf_ascii(field, index, buf); + assert(strnlen(buf, BUFSIZ) == (23*nf + 1)*sizeof(char)); + + /* Put the values back in a different location and check */ + + { + double val[nf] = {0}; + field_read_buf_ascii(field, index + 1, buf); + field_scalar_array(field, index + 1, val); + + assert((val[0] - array[0]) < DBL_EPSILON); + assert((val[1] - array[1]) < DBL_EPSILON); + assert((val[2] - array[2]) < DBL_EPSILON); + assert((val[3] - array[3]) < DBL_EPSILON); + assert((val[4] - array[4]) < DBL_EPSILON); + } + } + + field_free(field); + cs_free(cs); + + return 0; +} From b1dc41d3df4da3431a912065f9e43ea338aa1d95 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 4 Aug 2022 09:03:23 +0100 Subject: [PATCH 003/244] Add missing size_t definition --- src/io_aggr_buf.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/io_aggr_buf.h b/src/io_aggr_buf.h index 11def4634..a407f6654 100644 --- a/src/io_aggr_buf.h +++ b/src/io_aggr_buf.h @@ -14,6 +14,7 @@ #ifndef LUDWIG_IO_AGGR_BUF_H #define LUDWIG_IO_AGGR_BUF_H +#include #include "cs_limits.h" typedef struct io_aggr_buf_s io_aggr_buf_t; From b83093e84cacd8cc8b6129d1482265728c3eda1b Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 4 Aug 2022 09:04:34 +0100 Subject: [PATCH 004/244] Fix variable size initialisations error --- src/field.c | 11 +++++------ tests/unit/test_field.c | 10 +++++----- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/field.c b/src/field.c index 406fe5e55..3e106df0f 100644 --- a/src/field.c +++ b/src/field.c @@ -979,10 +979,9 @@ int field_read_buf(field_t * field, int index, const char * buf) { int field_write_buf_ascii(field_t * field, int index, char * buf) { - /* const field_t * field = (const field_t *) self;*/ - double array[NQAB] = {0}; + const int nbyte = 23; - int nbyte = 23; + double array[NQAB] = {0}; int ifail = 0; assert(field); @@ -994,7 +993,7 @@ int field_write_buf_ascii(field_t * field, int index, char * buf) { /* Use tmp with +1 to allow for the \0 */ for (int n = 0; n < field->nf; n++) { - char tmp[nbyte + 1] = {0}; + char tmp[BUFSIZ] = {0}; int np = snprintf(tmp, nbyte + 1, " %22.15e", array[n]); if (np != nbyte) ifail = 1; memcpy(buf + n*nbyte, tmp, nbyte*sizeof(char)); @@ -1010,9 +1009,9 @@ int field_write_buf_ascii(field_t * field, int index, char * buf) { int field_read_buf_ascii(field_t * field, int index, const char * buf) { - double array[NQAB] = {0}; + const int nbyte = 23; - int nbyte = 23; + double array[NQAB] = {0}; int ifail = 0; assert(field); diff --git a/tests/unit/test_field.c b/tests/unit/test_field.c index dc5b46036..820a9f469 100644 --- a/tests/unit/test_field.c +++ b/tests/unit/test_field.c @@ -516,15 +516,15 @@ int test_field_write_buf(pe_t * pe) { field_create(pe, cs, NULL, "test_write_buf", &options, &field); { - double array[nf] = {1.0, 2.0, 3.0}; - char buf[nf*sizeof(double)] = {0}; + double array[3] = {1.0, 2.0, 3.0}; + char buf[3*sizeof(double)] = {0}; int index = cs_index(cs, 2, 3, 4); field_scalar_array_set(field, index, array); field_write_buf(field, index, buf); { - double val[nf] = {0}; + double val[3] = {0}; field_read_buf(field, index + 1, buf); field_scalar_array(field, index + 1, val); @@ -562,7 +562,7 @@ int test_field_write_buf_ascii(pe_t * pe) { field_create(pe, cs, NULL, "test_field_write_buf_ascii", &options, &field); { - double array[nf] = {1.0, 3.0, 2.0, -4.0, -5.0}; + double array[5] = {1.0, 3.0, 2.0, -4.0, -5.0}; char buf[BUFSIZ] = {0}; int index = cs_index(cs, 1, 2, 3); @@ -573,7 +573,7 @@ int test_field_write_buf_ascii(pe_t * pe) { /* Put the values back in a different location and check */ { - double val[nf] = {0}; + double val[5] = {0}; field_read_buf_ascii(field, index + 1, buf); field_scalar_array(field, index + 1, val); From 7b34260ed51944a18937961468dc306e244ce8e6 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 5 Aug 2022 14:27:25 +0100 Subject: [PATCH 005/244] Add cs_limits_t functions --- src/cs_limits.h | 125 ++++++++++++++++++++++ tests/unit/test_cs_limits.c | 200 ++++++++++++++++++++++++++++++++++++ tests/unit/tests.c | 1 + tests/unit/tests.h | 1 + 4 files changed, 327 insertions(+) create mode 100644 tests/unit/test_cs_limits.c diff --git a/src/cs_limits.h b/src/cs_limits.h index f4087ce47..e9f3d3fa7 100644 --- a/src/cs_limits.h +++ b/src/cs_limits.h @@ -4,6 +4,20 @@ * * A container for a cubiodal region. * + * One may think of this as being associated with a flat address + * space starting at zero, and organised C-style (k running fastest). + * + * We allow conversion of a flattened 1-d index iflat to 3-d (ic, jc, kc) + * and vice-versa. + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * *****************************************************************************/ #ifndef LUDWIG_CS_LIMITS_H @@ -20,8 +34,20 @@ struct cs_limits_s { int kmax; }; +/***************************************************************************** + * + * cs_limits_size + * + * Return size, or volume, of region. + * + *****************************************************************************/ + static inline int cs_limits_size(cs_limits_t lim) { + assert(lim.imin <= lim.imax); + assert(lim.jmin <= lim.jmax); + assert(lim.kmin <= lim.kmax); + int szx = 1 + lim.imax - lim.imin; int szy = 1 + lim.jmax - lim.jmin; int szz = 1 + lim.kmax - lim.kmin; @@ -29,4 +55,103 @@ static inline int cs_limits_size(cs_limits_t lim) { return szx*szy*szz; } +/***************************************************************************** + * + * cs_limits_ic + * + * x-coordinate from flat index. + * + *****************************************************************************/ + +static inline int cs_limits_ic(cs_limits_t lim, int iflat) { + + assert(0 <= iflat && iflat < cs_limits_size(lim)); + + int ny = 1 + lim.jmax - lim.jmin; + int nz = 1 + lim.kmax - lim.kmin; + int strz = 1; + int stry = strz*nz; + int strx = stry*ny; + + int ic = lim.imin + iflat/strx; + + assert(lim.imin <= ic && ic <= lim.imax); + + return ic; +} + +/***************************************************************************** + * + * cs_limits_jc + * + * y-coordinate from flat index. + * + *****************************************************************************/ + +static inline int cs_limits_jc(cs_limits_t lim, int iflat) { + + assert(0 <= iflat && iflat < cs_limits_size(lim)); + + int ny = 1 + lim.jmax - lim.jmin; + int nz = 1 + lim.kmax - lim.kmin; + int strz = 1; + int stry = strz*nz; + int strx = stry*ny; + + int jc = lim.jmin + (iflat % strx)/stry; + + assert(lim.jmin <= jc && jc <= lim.jmax); + + return jc; +} + +/***************************************************************************** + * + * cs_limits_kc + * + * z-coordinate from flat index. + * + *****************************************************************************/ + +static inline int cs_limits_kc(cs_limits_t lim, int iflat) { + + assert(0 <= iflat && iflat < cs_limits_size(lim)); + + int nz = 1 + lim.kmax - lim.kmin; + int strz = 1; + int stry = strz*nz; + + int kc = lim.kmin + (iflat % stry)/strz; + + assert(lim.kmin <= kc && kc <= lim.kmax); + + return kc; +} + +/***************************************************************************** + * + * cs_limits_index + * + * Flat index from (ic, jc, kc) triple. + * + *****************************************************************************/ + +static inline int cs_limits_index(cs_limits_t lim, int ic, int jc, int kc) { + + int iflat = -1; + + int ny = 1 + lim.jmax - lim.jmin; + int nz = 1 + lim.kmax - lim.kmin; + + assert(lim.imin <= ic && ic <= lim.imax); + assert(lim.jmin <= jc && jc <= lim.jmax); + assert(lim.kmin <= kc && kc <= lim.kmax); + + iflat = (ic - lim.imin)*ny*nz + (jc - lim.jmin)*nz + (kc - lim.kmin); + + assert(0 <= iflat && iflat < cs_limits_size(lim)); + + return iflat; +} + #endif diff --git a/tests/unit/test_cs_limits.c b/tests/unit/test_cs_limits.c new file mode 100644 index 000000000..6ca82805c --- /dev/null +++ b/tests/unit/test_cs_limits.c @@ -0,0 +1,200 @@ +/***************************************************************************** + * + * test_cs_limits.c + * + * Some basic sanity checks. + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include + +#include "pe.h" +#include "cs_limits.h" + +int test_cs_limits(int imin, int imax, int jmin, int jmax, int kmin, int kmax); +int test_cs_limits_size(cs_limits_t lim); +int test_cs_limits_ic(cs_limits_t lim); +int test_cs_limits_jc(cs_limits_t lim); +int test_cs_limits_kc(cs_limits_t lim); +int test_cs_limits_index(cs_limits_t lim); + +/***************************************************************************** + * + * test_cs_limits_suite + * + *****************************************************************************/ + +int test_cs_limits_suite(void) { + + pe_t * pe = NULL; + + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + /* A selection of (imin, imax, jmin, jmax, kmin, kmax) */ + test_cs_limits( 1, 16, 1, 1, 1, 1); + test_cs_limits( 1, 16, 1, 8, 1, 4); + test_cs_limits(-1, 18, 0, 9, 1, 1); + test_cs_limits( 1, 1, 0, 0, 1, 16); + test_cs_limits(-3, -1, 1, 1, 2, 16); + + pe_info(pe, "PASS ./unit/test_cs_limits\n"); + + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_cs_limits + * + *****************************************************************************/ + +int test_cs_limits(int imin, int imax, int jmin, int jmax, int kmin, int kmax) { + + cs_limits_t lim = {imin, imax, jmin, jmax, kmin, kmax}; + + test_cs_limits_size(lim); + test_cs_limits_kc(lim); + test_cs_limits_jc(lim); + test_cs_limits_ic(lim); + test_cs_limits_index(lim); + + return 0; +} +/***************************************************************************** + * + * test_cs_limits_size + * + *****************************************************************************/ + +int test_cs_limits_size(cs_limits_t lim) { + + int ifail = 0; + + int nx = 1 + lim.imax - lim.imin; + int ny = 1 + lim.jmax - lim.jmin; + int nz = 1 + lim.kmax - lim.kmin; + + assert(nx*ny*nz == cs_limits_size(lim)); + if (nx*ny*nz != cs_limits_size(lim)) ifail += 1; + + return ifail; +} + +/***************************************************************************** + * + * test_cs_limits_ic + * + *****************************************************************************/ + +int test_cs_limits_ic(cs_limits_t lim) { + + int ifail = 0; + + { + /* Flat index zero must be imin. */ + int ic = cs_limits_ic(lim, 0); + assert(ic == lim.imin); + if (ic != lim.imin) ifail += 1; + } + + { + /* Modular arithmetic check ... topmost flat index must be imax ... */ + + int ic = cs_limits_ic(lim, cs_limits_size(lim) - 1); + assert(ic == lim.imax); + if (ic != lim.imax) ifail += 1; + } + + return ifail; +} + +/***************************************************************************** + * + * test_cs_limits_jc + * + *****************************************************************************/ + +int test_cs_limits_jc(cs_limits_t lim) { + + int ifail = 0; + + { + /* Flat index 0 must be jmin. */ + int jc = cs_limits_jc(lim, 0); + assert(jc == lim.jmin); + if (jc != lim.jmin) ifail += 1; + } + + { + /* Modular arithmetic ...*/ + int jc = cs_limits_jc(lim, cs_limits_size(lim) - 1); + assert(jc == lim.jmax); + if (jc != lim.jmax) ifail += 1; + } + + return ifail; +} + +/***************************************************************************** + * + * test_cs_limits_kc + * + *****************************************************************************/ + +int test_cs_limits_kc(cs_limits_t lim) { + + int ifail = 0; + + { + /* Flat index 0 must be kmin */ + int kc = cs_limits_kc(lim, 0); + assert(kc == lim.kmin); + if (kc != lim.kmin) ifail += 1; + } + + { + /* Modular arithemtic check ... */ + int kc = cs_limits_kc(lim, cs_limits_size(lim) - 1); + assert(kc == lim.kmax); + if (kc != lim.kmax) ifail += 1; + } + + return ifail; +} + +/***************************************************************************** + * + * test_cs_limits_index + * + *****************************************************************************/ + +int test_cs_limits_index(cs_limits_t lim) { + + int ifail = 0; + + { + /* Flat index 0 is (imin, jmin, kmin) */ + int iflat = cs_limits_index(lim, lim.imin, lim.jmin, lim.kmin); + assert(iflat == 0); + if (iflat != 0) ifail += 1; + } + + { + /* Flat index for (imax, jmax, kmax) ... */ + int iflat = cs_limits_index(lim, lim.imax, lim.jmax, lim.kmax); + assert(iflat == cs_limits_size(lim) - 1); + if (iflat != cs_limits_size(lim) - 1) ifail += 1; + } + + return ifail; +} diff --git a/tests/unit/tests.c b/tests/unit/tests.c index dce322015..6eb2169b7 100644 --- a/tests/unit/tests.c +++ b/tests/unit/tests.c @@ -48,6 +48,7 @@ __host__ int tests_create() { test_pe_suite(); test_coords_suite(); + test_cs_limits_suite(); test_kernel_suite(); test_gradient_d3q27_suite(); diff --git a/tests/unit/tests.h b/tests/unit/tests.h index 60176c829..39bf5a2ad 100644 --- a/tests/unit/tests.h +++ b/tests/unit/tests.h @@ -38,6 +38,7 @@ int test_colloid_suite(void); int test_colloids_info_suite(void); int test_colloids_halo_suite(void); int test_coords_suite(void); +int test_cs_limits_suite(void); int test_ewald_suite(void); int test_fe_null_suite(void); int test_fe_electro_suite(void); From dcbe17e254ad6bbe03a5e8918fb907f751a4c7a3 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 5 Aug 2022 15:03:31 +0100 Subject: [PATCH 006/244] io_aggr_buf_t prototype, test thereof --- tests/unit/test_io_aggr_buf.c | 76 +++++++++++++++++++++++++++++++++++ tests/unit/tests.c | 1 + tests/unit/tests.h | 1 + 3 files changed, 78 insertions(+) create mode 100644 tests/unit/test_io_aggr_buf.c diff --git a/tests/unit/test_io_aggr_buf.c b/tests/unit/test_io_aggr_buf.c new file mode 100644 index 000000000..502001e72 --- /dev/null +++ b/tests/unit/test_io_aggr_buf.c @@ -0,0 +1,76 @@ +/***************************************************************************** + * + * test_io_aggr_buf.c + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include + +#include "pe.h" +#include "io_aggr_buf.h" + +int test_io_aggr_buf_create(void); + +/***************************************************************************** + * + * test_io_aggr_buf_suite + * + *****************************************************************************/ + +int test_io_aggr_buf_suite(void) { + + pe_t * pe = NULL; + + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + /* If the size of the struct has changed, tests need to be changed... */ + assert(sizeof(io_aggr_buf_t) == 48); + + test_io_aggr_buf_create(); + + pe_info(pe, "PASS ./unit/testr_io_aggr_buf\n"); + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_io_aggr_buf_create + * + *****************************************************************************/ + +int test_io_aggr_buf_create(void) { + + int ifail = 0; + + { + /* Create and free. */ + size_t lsz = 99; + cs_limits_t lim = {-2, 18, 1, 8, 1, 4}; + io_aggr_buf_t aggr = {0}; + + io_aggr_buf_create(lsz, lim, &aggr); + + assert(aggr.szelement == lsz); + assert(aggr.szbuf == lsz*cs_limits_size(lim)); + assert(aggr.lim.imin == lim.imin); /* Assume sufficient */ + assert(aggr.buf); + + io_aggr_buf_free(&aggr); + assert(aggr.szelement == 0); + assert(aggr.szbuf == 0); + assert(aggr.lim.imin == 0); + assert(aggr.buf == NULL); + } + + return ifail; +} diff --git a/tests/unit/tests.c b/tests/unit/tests.c index 6eb2169b7..429216d0a 100644 --- a/tests/unit/tests.c +++ b/tests/unit/tests.c @@ -73,6 +73,7 @@ __host__ int tests_create() { test_field_grad_suite(); test_halo_suite(); test_hydro_suite(); + test_io_aggr_buf_suite(); test_io_suite(); test_io_options_suite(); test_io_options_rt_suite(); diff --git a/tests/unit/tests.h b/tests/unit/tests.h index 39bf5a2ad..f7aa2279b 100644 --- a/tests/unit/tests.h +++ b/tests/unit/tests.h @@ -52,6 +52,7 @@ int test_field_grad_suite(void); int test_gradient_d3q27_suite(void); int test_halo_suite(void); int test_hydro_suite(void); +int test_io_aggr_buf_suite(void); int test_io_suite(void); int test_io_options_suite(void); int test_io_options_rt_suite(void); From ddc57b60886fa77fa83620390c6e3cc2f4dfe2ee Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 5 Aug 2022 17:44:00 +0100 Subject: [PATCH 007/244] minimal mpiio in progress --- mpi_s/mpi.h | 46 +++++++- mpi_s/mpi_serial.c | 287 ++++++++++++++++++++++++++++++++++++++++++++- mpi_s/mpi_tests.c | 57 +++++++++ 3 files changed, 386 insertions(+), 4 deletions(-) diff --git a/mpi_s/mpi.h b/mpi_s/mpi.h index 1ac0425c9..1354d3fb8 100644 --- a/mpi_s/mpi.h +++ b/mpi_s/mpi.h @@ -9,6 +9,8 @@ * * From an idea appearing in LAMMPS. * + * (c) 2022 The University of Edinburgh + * * Kevin Stratford (kevin@epcc.ed.ac.uk) * *****************************************************************************/ @@ -31,6 +33,8 @@ typedef MPI_Handle MPI_Datatype; typedef MPI_Handle MPI_Request; typedef MPI_Handle MPI_Op; typedef MPI_Handle MPI_Errhandler; +typedef MPI_Handle MPI_File; +typedef MPI_Handle MPI_Info; typedef struct { int MPI_SOURCE; @@ -41,9 +45,11 @@ typedef struct { #define MPI_STATUSES_IGNORE ((MPI_Status *) 0) /* MPI_Aint is a signed integer. Prefer intmax_t over intptr_t as - the latter is optional in the standard. */ + the latter is optional in the standard. */ +/* MPI_Offset is a large integer. */ typedef intmax_t MPI_Aint; +typedef intmax_t MPI_Offset; /* Defined constants (see Annex A.2) */ @@ -102,6 +108,8 @@ enum reserved_communicators{MPI_COMM_WORLD, MPI_COMM_SELF}; #define MPI_REQUEST_NULL -4 #define MPI_OP_NULL -5 #define MPI_ERRHANDLER_NULL -6 +#define MPI_FILE_NULL -7 +#define MPI_INFO_NULL -8 /* Special values */ @@ -114,6 +122,22 @@ enum reserved_communicators{MPI_COMM_WORLD, MPI_COMM_SELF}; #define MPI_THREAD_SERIALIZED 3 #define MPI_THREAD_MULTIPLE 4 +/* MPI_ORDER */ + +enum mpi_order_enum {MPI_ORDER_C, MPI_ORDER_FORTRAN}; + +/* MPI File amodes (bitmask) */ + +#define MPI_MODE_RDONLY 1 +#define MPI_MODE_RDWR 2 +#define MPI_MODE_WRONLY 4 +#define MPI_MODE_CREATE 8 +#define MPI_MODE_EXCL 16 +#define MPI_MODE_DELETE_ON_CLOSE 32 +#define MPI_MODE_UNIQUE_OPEN 64 +#define MPI_MODE_SEQUENTIAL 128 +#define MPI_MODE_APPEND 256 + /* Interface */ int MPI_Barrier(MPI_Comm comm); @@ -227,6 +251,26 @@ int MPI_Type_create_resized(MPI_Datatype oldtype, MPI_Aint ub, MPI_Aint extent, MPI_Datatype * newtype); int MPI_Type_get_extent(MPI_Datatype handle, MPI_Aint * lb, MPI_Aint *extent); +/* MPI IO related */ + +int MPI_File_open(MPI_Comm comm, const char * filename, int amode, + MPI_Info info, MPI_File * fh); +int MPI_File_close(MPI_File * fh); +int MPI_File_delete(const char * filename, MPI_Info info); +int MPI_Type_create_subarray(int ndims, const int * array_of_sizes, + const int * array_of_subsizes, + const int * array_of_starts, + int order, + MPI_Datatype oldtype, + MPI_Datatype * newtype); +int MPI_File_set_view(MPI_File fh, MPI_Offset disp, MPI_Datatype etype, + MPI_Datatype filetype, const char * datarep, + MPI_Info info); +int MPI_File_read_all(MPI_File fh, void * buf, int count, + MPI_Datatype datatype, MPI_Status * status); +int MPI_File_write_all(MPI_File fh, const void * buf, int count, + MPI_Datatype datatype, MPI_Status * status); + #ifdef __cplusplus } #endif diff --git a/mpi_s/mpi_serial.c b/mpi_s/mpi_serial.c index 04ea6e3e9..c29600a72 100644 --- a/mpi_s/mpi_serial.c +++ b/mpi_s/mpi_serial.c @@ -4,18 +4,21 @@ * * Library to replace MPI in serial. * - * From an idea appearing in, for example, LAMMPS. + * From an idea appearing in, for example, LAMMPS, and elsewhere. * - * Point-to-point communications: will terminate. + * Point-to-point communications: may terminate. * Collective communications: copy for basic datatypes * Groups, Contexts, Comunicators: mostly no-operations * Process Topologies: no operations * Environmental inquiry: mostly operational + * MPI-IO: basic operations + * * * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2021 The University of Edinburgh + * (c) 2021-2022 The University of Edinburgh + * * Kevin Stratford (kevin@epcc.ed.ac.uk) * *****************************************************************************/ @@ -42,12 +45,14 @@ #define MAX_CART_COMM 16 #define MAX_USER_DT 32 +#define MAX_USER_FILE 16 /* We are not going to deal with all possible data types; encode * what we have ... */ enum dt_flavour {DT_NOT_IMPLEMENTED = 0, DT_CONTIGUOUS, DT_VECTOR, DT_STRUCT}; typedef struct internal_data_type_s data_t; +typedef struct internal_file_view_s file_t; struct internal_data_type_s { MPI_Datatype handle; /* User space handle [in suitable range] */ @@ -56,6 +61,13 @@ struct internal_data_type_s { int flavour; /* Contiguous types only at present */ }; +struct internal_file_view_s { + FILE * fp; /* file pointer */ + MPI_Offset disp; /* e.g., from MPI_File_set_view() */ + MPI_Datatype etype; + MPI_Datatype filetype; +}; + typedef struct mpi_info_s mpi_info_t; struct mpi_info_s { @@ -66,6 +78,8 @@ struct mpi_info_s { data_t dt[MAX_USER_DT]; /* Internal information per data type */ int ndatatypelast; /* Current free list extent */ int dtfreelist[MAX_USER_DT]; /* Free list */ + + FILE * filelist[MAX_USER_FILE]; /* Open file handle -> pointer table */ }; static mpi_info_t * mpi_info = NULL; @@ -79,6 +93,9 @@ static int mpi_data_type_add(mpi_info_t * ctxt, const data_t * dt, static int mpi_data_type_free(mpi_info_t * ctxt, MPI_Datatype * handle); static int mpi_data_type_handle(mpi_info_t * ctxt, MPI_Datatype handle); +static MPI_File mpi_file_handle_retain(mpi_info_t * ctxt, FILE * fp); +static FILE * mpi_file_handle_release(mpi_info_t * ctxt, MPI_File handle); + /***************************************************************************** * * MPI_Barrier @@ -141,6 +158,10 @@ int MPI_Init(int * argc, char *** argv) { mpi_info->ndatatype = 0; mpi_info->ndatatypelast = 0; + for (int ih = 0; ih < MAX_USER_FILE; ih++) { + mpi_info->filelist[ih] = NULL; + } + return MPI_SUCCESS; } @@ -1248,6 +1269,208 @@ int MPI_Type_get_extent(MPI_Datatype datatype, MPI_Aint * lb, return MPI_SUCCESS; } +/***************************************************************************** + * + * MPI_File_open + * + *****************************************************************************/ + +int MPI_File_open(MPI_Comm comm, const char * filename, int amode, + MPI_Info info, MPI_File * fh) { + + int flags = 0; + FILE * fp = NULL; + const char * fdmode = NULL; + + assert(mpi_is_valid_comm(comm)); + assert(filename); + assert(fh); + + /* Exactly one of RDONLY, WRONLY, or RDWR must be present. */ + /* RDONLY => no CREATE or EXCL. */ + /* RDWR => no SEQUENTIAL */ + + { + int have_rdonly = (amode & MPI_MODE_RDONLY) ? 1 : 0; + int have_wronly = (amode & MPI_MODE_WRONLY) ? 2 : 0; + int have_rdwr = (amode & MPI_MODE_RDWR) ? 4 : 0; + + int have_create = (amode & MPI_MODE_CREATE); + int have_excl = (amode & MPI_MODE_EXCL); + int have_append = (amode & MPI_MODE_APPEND); + + switch (have_rdonly + have_wronly + have_rdwr) { + case (1): + /* Read only */ + fdmode = "r"; + /* + flags = O_RDONLY; + */ + if (have_create) printf("No create with RDONLY\n"); + if (have_excl) printf("No excl with RDONLY\n"); + break; + case (2): + /* Write only */ + fdmode = "w"; + /* + flags = O_WRONLY; + if (have_create) flags = flags | O_CREAT; + if (have_excl) flags = flags | O_EXCL; + if (have_append) flags = flags | O_APPEND; + */ + if (have_append) fdmode = "a"; + break; + case (4): + /* Read write */ + fdmode = "r+"; + /* + flags = O_RDWR; + if (have_create) flags = flags | O_CREAT; + if (have_excl) flags = flags | O_EXCL; + if (have_append) flags = flags | O_APPEND; + */ + if (have_append) fdmode = "a+"; + break; + default: + printf("Please specify exactly one of MPI_MODE_RDONLY, MPI_MODE_WRONLY, " + "or MPI_MODE_RDWR\n"); + } + } + + assert(flags == 0); /* Do something with flags */ + + fp = fopen(filename, fdmode); + + if (fp == NULL) { + printf("MPI_File_open: attempt to open %s mode %s failed\n", filename, + fdmode); + exit(0); + } + + *fh = mpi_file_handle_retain(mpi_info, fp); + + return MPI_SUCCESS; +} + +/***************************************************************************** + * + * MPI_File_close + * + *****************************************************************************/ + +int MPI_File_close(MPI_File * fh) { + + assert(fh); + + { + FILE * fp = NULL; + MPI_File handle = *fh; + + fp = mpi_file_handle_release(mpi_info, handle); + + fclose(fp); + *fh = MPI_FILE_NULL; + } + + return MPI_SUCCESS; +} + +/***************************************************************************** + * + * MPI_File_delete + * + *****************************************************************************/ + +int MPI_File_delete(const char * filename, MPI_Info info) { + + assert(filename); + assert(0); + + return MPI_SUCCESS; +} + +/***************************************************************************** + * + * MPI_Type_create_subarray + * + *****************************************************************************/ + +int MPI_Type_create_subarray(int ndims, const int * array_of_sizes, + const int * array_of_subsizes, + const int * array_of_starts, + int order, + MPI_Datatype oldtype, + MPI_Datatype * newtype) { + assert(array_of_sizes); + assert(array_of_subsizes); + assert(array_of_starts); + assert(order == MPI_ORDER_C || order == MPI_ORDER_FORTRAN); + assert(newtype); + + return MPI_SUCCESS; +} + +/***************************************************************************** + * + * MPI_File_set_view + * + *****************************************************************************/ + +int MPI_File_set_view(MPI_File fh, MPI_Offset disp, MPI_Datatype etype, + MPI_Datatype filetype, const char * datarep, + MPI_Info info) { + + /* datarep may be 'NULL' => defaults to "native" */ + + return MPI_SUCCESS; +} + +/***************************************************************************** + * + * MPI_File_read_all + * + *****************************************************************************/ + +int MPI_File_read_all(MPI_File fh, void * buf, int count, + MPI_Datatype datatype, MPI_Status * status) { + assert(buf); + assert(status); + + return MPI_SUCCESS; +} + +/***************************************************************************** + * + * MPI_File_write_all + * + *****************************************************************************/ + +int MPI_File_write_all(MPI_File fh, const void * buf, int count, + MPI_Datatype datatype, MPI_Status * status) { + + assert(0 < fh && fh < MAX_USER_FILE); + assert(buf); + assert(status); + + /* Translate to a simple fwrite() */ + + FILE * fp = mpi_info->filelist[fh]; + size_t size = mpi_sizeof(datatype); + size_t nitems = count; + + assert(fp); /* Replace with check on fh */ + + fwrite(buf, size, nitems, fp); + + if (ferror(fp)) { + perror("perror: "); + printf("MPI_File_write_all() failed\n"); + exit(0); + } + + return MPI_SUCCESS; +} + #endif /* _DO_NOT_INCLUDE_MPI2_INTERFACE */ /***************************************************************************** @@ -1348,3 +1571,61 @@ static int mpi_data_type_handle(mpi_info_t * ctxt, MPI_Datatype handle) { return index; } + +/***************************************************************************** + * + * mpi_file_handle_retain + * + * Implementation of file open. + * Success returns a valie MPI_FIle handle. + * + *****************************************************************************/ + +static MPI_File mpi_file_handle_retain(mpi_info_t * mpi, FILE * fp) { + + int handle = MPI_FILE_NULL; + + assert(mpi); + assert(fp); + + for (int ih = 1; ih < MAX_USER_FILE; ih++) { + if (mpi->filelist[ih] == NULL) { + handle = ih; + break; + } + } + + if (handle == MPI_FILE_NULL) { + printf("Run out of MPI file handles\n"); + exit(0); + } + + /* Record the pointer against the handle */ + mpi->filelist[handle] = fp; + + return handle; +} + +/***************************************************************************** + * + * mpi_file_handle_release + * + *****************************************************************************/ + +static FILE * mpi_file_handle_release(mpi_info_t * mpi, MPI_File handle) { + + FILE * fp = NULL; + + assert(mpi); + assert(1 <= handle && handle < MAX_USER_FILE); + + if (mpi->filelist[handle] == NULL) { + printf("Attempt to release NULL mpi file handle"); + exit(0); + } + + fp = mpi->filelist[handle]; + mpi->filelist[handle] = NULL; + + return fp; +} diff --git a/mpi_s/mpi_tests.c b/mpi_s/mpi_tests.c index 1d11b8c26..1b6fb9ace 100644 --- a/mpi_s/mpi_tests.c +++ b/mpi_s/mpi_tests.c @@ -10,6 +10,8 @@ #include #include +#include + #include "mpi.h" static MPI_Comm comm_ = MPI_COMM_WORLD; @@ -21,6 +23,7 @@ static int test_mpi_allgather(void); static int test_mpi_type_contiguous(void); static int test_mpi_type_create_struct(void); static int test_mpi_op_create(void); +static int test_mpi_file_open(void); int main (int argc, char ** argv) { @@ -41,6 +44,8 @@ int main (int argc, char ** argv) { test_mpi_type_create_struct(); test_mpi_op_create(); + test_mpi_file_open(); + ireturn = MPI_Finalize(); assert(ireturn == MPI_SUCCESS); @@ -335,3 +340,55 @@ static int test_mpi_op_create(void) { return MPI_SUCCESS; } + +/***************************************************************************** + * + * test_mpi_file_open + * + *****************************************************************************/ + +int test_mpi_file_open(void) { + + MPI_Comm comm = MPI_COMM_WORLD; + MPI_Info info = MPI_INFO_NULL; + + { + /* fopen "r". We must have an existing file. */ + MPI_File fh = MPI_FILE_NULL; + MPI_File_open(comm, "/dev/null", MPI_MODE_RDONLY, info, &fh); + assert(fh != MPI_FILE_NULL); + MPI_File_close(&fh); + assert(fh == MPI_FILE_NULL); + } + + { + /* fopen "w" */ + MPI_File fh = MPI_FILE_NULL; + MPI_File_open(comm, "zw.dat", MPI_MODE_WRONLY+MPI_MODE_CREATE, info, &fh); + assert(fh != MPI_FILE_NULL); + MPI_File_close(&fh); + assert(fh == MPI_FILE_NULL); + unlink("zw.dat"); + } + + { + /* fopen "a" */ + MPI_File fh = MPI_FILE_NULL; + MPI_File_open(comm, "z.dat", MPI_MODE_WRONLY+MPI_MODE_APPEND, info, &fh); + assert(fh != MPI_FILE_NULL); + MPI_File_close(&fh); + assert(fh == MPI_FILE_NULL); + } + + { + /* fopen "r+" */ + MPI_File fh = MPI_FILE_NULL; + MPI_File_open(comm, "z.dat", MPI_MODE_RDWR, info, &fh); + assert(fh != MPI_FILE_NULL); + MPI_File_close(&fh); + assert(fh == MPI_FILE_NULL); + unlink("z.dat"); + } + + return 0; +} From 4e34bef859c2e54b1ce599addb9ddaad4a56189f Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 5 Aug 2022 17:44:43 +0100 Subject: [PATCH 008/244] Proto type aggregator for field io --- src/field.c | 113 +++++++++++++++++++++++++++- src/field.h | 9 ++- src/io_aggr.h | 28 +++++++ tests/unit/test_field.c | 160 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 307 insertions(+), 3 deletions(-) create mode 100644 src/io_aggr.h diff --git a/src/field.c b/src/field.c index 3e106df0f..b29bdcf32 100644 --- a/src/field.c +++ b/src/field.c @@ -98,6 +98,14 @@ __host__ int field_create(pe_t * pe, cs_t * cs, lees_edw_t * le, field_halo_create(obj, &obj->h); obj->opts = *opts; + /* I/O aggregator */ + { + obj->aggr.asc_etype = MPI_CHAR; + obj->aggr.bin_etype = MPI_DOUBLE; + obj->aggr.asc_esize = 23*sizeof(char); /* 23 to be rationalised */ + obj->aggr.bin_esize = obj->opts.ndata*sizeof(double); + } + if (obj->opts.haloverbose) field_halo_info(obj); *pobj = obj; @@ -951,9 +959,16 @@ static int field_write(FILE * fp, int index, void * self) { return 0; } +/***************************************************************************** + * + * field_write_buf + * + * Per-lattice site binary write. + * + *****************************************************************************/ + int field_write_buf(field_t * field, int index, char * buf) { - /* const field_t * field = (const field_t *) self;*/ double array[NQAB] = {0}; assert(field); @@ -964,6 +979,14 @@ int field_write_buf(field_t * field, int index, char * buf) { return 0; } +/***************************************************************************** + * + * field_read_buf + * + * Per lattice site read (binary) + * + *****************************************************************************/ + int field_read_buf(field_t * field, int index, const char * buf) { double array[NQAB] = {0}; @@ -977,6 +1000,14 @@ int field_read_buf(field_t * field, int index, const char * buf) { return 0; } +/***************************************************************************** + * + * field_write_buf_ascii + * + * Per lattice site write (binary). + * + *****************************************************************************/ + int field_write_buf_ascii(field_t * field, int index, char * buf) { const int nbyte = 23; @@ -1007,6 +1038,14 @@ int field_write_buf_ascii(field_t * field, int index, char * buf) { return ifail; } +/***************************************************************************** + * + * field_read_buf_ascii + * + * Per lattice site read (ascii). + * + *****************************************************************************/ + int field_read_buf_ascii(field_t * field, int index, const char * buf) { const int nbyte = 23; @@ -1027,6 +1066,78 @@ int field_read_buf_ascii(field_t * field, int index, const char * buf) { return ifail; } +/***************************************************************************** + * + * field_io_aggr_pack + * + * Aggregator for packing the field to io_aggr_buf_t. + * + *****************************************************************************/ + +int field_io_aggr_pack(field_t * field, io_aggr_buf_t aggr) { + + assert(field); + assert(aggr.buf); + + #pragma omp parallel + { + int iasc = field->opts.iodata.output.iorformat == IO_RECORD_ASCII; + int ibin = field->opts.iodata.output.iorformat == IO_RECORD_BINARY; + + #pragma omp for + for (int ib = 0; ib < cs_limits_size(aggr.lim); ib++) { + int ic = cs_limits_ic(aggr.lim, ib); + int jc = cs_limits_jc(aggr.lim, ib); + int kc = cs_limits_kc(aggr.lim, ib); + + /* Read/write data for (ic,jc,kc) */ + int index = cs_index(field->cs, ic, jc, kc); + int offset = ib*aggr.szelement; + if (iasc) field_write_buf_ascii(field, index, aggr.buf + offset); + if (ibin) field_write_buf(field, index, aggr.buf + offset); + } + } + + return 0; +} + +/***************************************************************************** + * + * field_io_aggr_unpack + * + * Aggregator for the upack (read) stage. + * + *****************************************************************************/ + +int field_io_aggr_unpack(field_t * field, io_aggr_buf_t aggr) { + + assert(field); + assert(aggr.buf); + + #pragma omp parallel + { + int iasc = field->opts.iodata.input.iorformat == IO_RECORD_ASCII; + int ibin = field->opts.iodata.input.iorformat == IO_RECORD_BINARY; + assert(iasc ^ ibin); /* one or other */ + + #pragma omp for + for (int ib = 0; ib < cs_limits_size(aggr.lim); ib++) { + int ic = cs_limits_ic(aggr.lim, ib); + int jc = cs_limits_jc(aggr.lim, ib); + int kc = cs_limits_kc(aggr.lim, ib); + + /* Read/write data for (ic,jc,kc) */ + int index = cs_index(field->cs, ic, jc, kc); + int offset = ib*aggr.szelement; + if (iasc) field_read_buf_ascii(field, index, aggr.buf + offset); + if (ibin) field_read_buf(field, index, aggr.buf + offset); + } + } + + return 0; +} + + /***************************************************************************** * * field_write_ascii diff --git a/src/field.h b/src/field.h index d305162e7..bb1b33db7 100644 --- a/src/field.h +++ b/src/field.h @@ -22,7 +22,9 @@ #include "pe.h" #include "coords.h" -#include "io_harness.h" +#include "io_aggr.h" /* I/O data aggregation */ +#include "io_aggr_buf.h" /* Aggregation buffer */ +#include "io_harness.h" /* To be removed in favour of io_aggr_t */ #include "leesedwards.h" #include "halo_swap.h" #include "field_options.h" @@ -61,7 +63,8 @@ struct field_s { pe_t * pe; /* Parallel environment */ cs_t * cs; /* Coordinate system */ lees_edw_t * le; /* Lees-Edwards */ - io_info_t * info; /* I/O Handler */ + io_aggr_t aggr; /* I/O aggregator information */ + io_info_t * info; /* I/O Handler (to be removed) */ halo_swap_t * halo; /* Halo swap driver object */ field_halo_t h; /* Host halo */ field_options_t opts; /* Options */ @@ -109,5 +112,7 @@ int field_read_buf(field_t * field, int index, const char * buf); int field_read_buf_ascii(field_t * field, int index, const char * buf); int field_write_buf(field_t * field, int index, char * buf); int field_write_buf_ascii(field_t * field, int index, char * buf); +int field_io_aggr_pack(field_t * field, io_aggr_buf_t aggr); +int field_io_aggr_unpack(field_t * field, io_aggr_buf_t aggr); #endif diff --git a/src/io_aggr.h b/src/io_aggr.h new file mode 100644 index 000000000..f6e4215fd --- /dev/null +++ b/src/io_aggr.h @@ -0,0 +1,28 @@ +/***************************************************************************** + * + * io_aggr.h + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#ifndef LUDWIG_IO_AGGR_H +#define LUDWIG_IO_AGGR_H + +#include + +typedef struct io_aggr_s io_aggr_t; + +struct io_aggr_s { + MPI_Datatype bin_etype; /* Data type for element (binary) */ + MPI_Datatype asc_etype; /* Data type for element (ascii) usu. MPI_CHAR */ + size_t bin_esize; /* number bytes per lattice site */ + size_t asc_esize; /* fixed character size per line of output */ +}; + +#endif diff --git a/tests/unit/test_field.c b/tests/unit/test_field.c index 820a9f469..d827af35e 100644 --- a/tests/unit/test_field.c +++ b/tests/unit/test_field.c @@ -40,6 +40,11 @@ int do_test_device1(pe_t * pe); int test_field_halo_create(pe_t * pe); int test_field_write_buf(pe_t * pe); int test_field_write_buf_ascii(pe_t * pe); +int test_field_io_aggr_pack(pe_t * pe); + +int util_field_data_check(field_t * field); +int util_field_data_check_set(field_t * field); + __global__ void do_test_field_kernel1(field_t * phi); @@ -71,6 +76,7 @@ int test_field_suite(void) { /* Experimental ... */ test_field_write_buf(pe); test_field_write_buf_ascii(pe); + test_field_io_aggr_pack(pe); pe_info(pe, "PASS ./unit/test_field\n"); pe_free(pe); @@ -590,3 +596,157 @@ int test_field_write_buf_ascii(pe_t * pe) { return 0; } + +/***************************************************************************** + * + * test_field_io_aggr_pack + * + *****************************************************************************/ + +int test_field_io_aggr_pack(pe_t * pe) { + + int nf = 5; /* Test field */ + + cs_t * cs = NULL; + field_t * field = NULL; + field_options_t options = field_options_ndata_nhalo(nf, 1); + + assert(pe); + + cs_create(pe, &cs); + cs_init(cs); + field_create(pe, cs, NULL, "test_field_io_aggr_pack", &options, &field); + + /* This should be elsewhere as part of test_field_create() */ + { + /* Note one can use == with pre-defined data types */ + assert(field->aggr.asc_etype == MPI_CHAR); + assert(field->aggr.bin_etype == MPI_DOUBLE); + assert(field->aggr.asc_esize == 23*sizeof(char)); + assert(field->aggr.bin_esize == field->nf*sizeof(double)); + } + + { + int nlocal[3] = {0}; + cs_nlocal(cs, nlocal); + { + cs_limits_t lim = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; + io_aggr_buf_t buf = {0}; + + io_aggr_buf_create(field->aggr.bin_esize, lim, &buf); + + util_field_data_check_set(field); + field_io_aggr_pack(field, buf); + + /* Are the values in the buffer correct? */ + /* Clear existing values and unpack. */ + + memset(field->data, 0, field->nsites*field->nf*sizeof(double)); + + field_io_aggr_unpack(field, buf); + util_field_data_check(field); + + io_aggr_buf_free(&buf); + } + } + + field_free(field); + cs_free(cs); + + return 0; +} + +/***************************************************************************** + * + * field_unique_value + * + * Set a unique value based on global position. + * + *****************************************************************************/ + +int64_t field_unique_value(field_t * f, int ic, int jc, int kc, int n) { + + int64_t ival = INT64_MIN; + + int ntotal[3] = {0}; + int nlocal[3] = {0}; + int noffset[3] = {0}; + + assert(f); + + cs_ntotal(f->cs, ntotal); + cs_nlocal_offset(f->cs, noffset); + cs_nlocal(f->cs, nlocal); + + { + int strz = 1; + int stry = strz*ntotal[Z]; + int strx = stry*ntotal[Y]; + int nstr = strx*f->nf; + int ix = noffset[X] + ic; + int iy = noffset[Y] + jc; + int iz = noffset[Z] + kc; + ival = nstr*n + strx*ix + stry*iy + strz*iz; + } + + return ival; +} + +/***************************************************************************** + * + * util_field_data_check_set + * + *****************************************************************************/ + +int util_field_data_check_set(field_t * field) { + + int nlocal[3] = {0}; + + assert(field); + + cs_nlocal(field->cs, nlocal); + + for (int ic = 1; ic <= nlocal[X]; ic++) { + for (int jc = 1; jc <= nlocal[Y]; jc++) { + for (int kc = 1; kc <= nlocal[Z]; kc++) { + int index = cs_index(field->cs, ic, jc, kc); + for (int n = 0; n < field->nf; n++) { + int faddr = addr_rank1(field->nsites, field->nf, index, n); + field->data[faddr] = 1.0*field_unique_value(field, ic, jc, kc, n); + } + } + } + } + + return 0; +} + +/***************************************************************************** + * + * util_field_data_check + * + *****************************************************************************/ + +int util_field_data_check(field_t * field) { + + int nlocal[3] = {0}; + + assert(field); + + cs_nlocal(field->cs, nlocal); + + for (int ic = 1; ic <= nlocal[X]; ic++) { + for (int jc = 1; jc <= nlocal[Y]; jc++) { + for (int kc = 1; kc <= nlocal[Z]; kc++) { + int index = cs_index(field->cs, ic, jc, kc); + for (int n = 0; n < field->nf; n++) { + int faddr = addr_rank1(field->nsites, field->nf, index, n); + double fval = 1.0*field_unique_value(field, ic, jc, kc, n); + assert(fabs(field->data[faddr] - fval) < DBL_EPSILON); + } + } + } + } + + return 0; +} From b5b399ef4e5e5f5d2f4a4c19561a18e6d71f9573 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 10 Aug 2022 16:29:42 +0100 Subject: [PATCH 009/244] File set view and get view working --- mpi_s/mpi.h | 4 ++ mpi_s/mpi_serial.c | 153 +++++++++++++++++++++++++++++++++++---------- mpi_s/mpi_tests.c | 81 ++++++++++++++++++++++++ 3 files changed, 205 insertions(+), 33 deletions(-) diff --git a/mpi_s/mpi.h b/mpi_s/mpi.h index 1354d3fb8..0f9f6f451 100644 --- a/mpi_s/mpi.h +++ b/mpi_s/mpi.h @@ -138,6 +138,8 @@ enum mpi_order_enum {MPI_ORDER_C, MPI_ORDER_FORTRAN}; #define MPI_MODE_SEQUENTIAL 128 #define MPI_MODE_APPEND 256 +#define MPI_MAX_DATAREP_STRING 128 /* E.g., "native" */ + /* Interface */ int MPI_Barrier(MPI_Comm comm); @@ -263,6 +265,8 @@ int MPI_Type_create_subarray(int ndims, const int * array_of_sizes, int order, MPI_Datatype oldtype, MPI_Datatype * newtype); +int MPI_File_get_view(MPI_File fh, MPI_Offset * disp, MPI_Datatype * etype, + MPI_Datatype * filetype, char * datarep); int MPI_File_set_view(MPI_File fh, MPI_Offset disp, MPI_Datatype etype, MPI_Datatype filetype, const char * datarep, MPI_Info info); diff --git a/mpi_s/mpi_serial.c b/mpi_s/mpi_serial.c index c29600a72..0de115efc 100644 --- a/mpi_s/mpi_serial.c +++ b/mpi_s/mpi_serial.c @@ -66,6 +66,7 @@ struct internal_file_view_s { MPI_Offset disp; /* e.g., from MPI_File_set_view() */ MPI_Datatype etype; MPI_Datatype filetype; + char datarep[MPI_MAX_DATAREP_STRING]; }; typedef struct mpi_info_s mpi_info_t; @@ -79,7 +80,7 @@ struct mpi_info_s { int ndatatypelast; /* Current free list extent */ int dtfreelist[MAX_USER_DT]; /* Free list */ - FILE * filelist[MAX_USER_FILE]; /* Open file handle -> pointer table */ + file_t filelist[MAX_USER_FILE]; /* MPI_File information for open files */ }; static mpi_info_t * mpi_info = NULL; @@ -95,6 +96,7 @@ static int mpi_data_type_handle(mpi_info_t * ctxt, MPI_Datatype handle); static MPI_File mpi_file_handle_retain(mpi_info_t * ctxt, FILE * fp); static FILE * mpi_file_handle_release(mpi_info_t * ctxt, MPI_File handle); +static FILE * mpi_file_handle_to_fp(mpi_info_t * info, MPI_File handle); /***************************************************************************** * @@ -159,7 +161,11 @@ int MPI_Init(int * argc, char *** argv) { mpi_info->ndatatypelast = 0; for (int ih = 0; ih < MAX_USER_FILE; ih++) { - mpi_info->filelist[ih] = NULL; + mpi_info->filelist[ih].fp = NULL; + mpi_info->filelist[ih].disp = 0; + mpi_info->filelist[ih].etype = MPI_BYTE; + mpi_info->filelist[ih].filetype = MPI_BYTE; + strncpy(mpi_info->filelist[ih].datarep, "native", MPI_MAX_DATAREP_STRING); } return MPI_SUCCESS; @@ -1360,14 +1366,17 @@ int MPI_File_open(MPI_Comm comm, const char * filename, int amode, int MPI_File_close(MPI_File * fh) { - assert(fh); + FILE * fp = NULL; - { - FILE * fp = NULL; - MPI_File handle = *fh; + assert(fh); - fp = mpi_file_handle_release(mpi_info, handle); + fp = mpi_file_handle_release(mpi_info, *fh); + if (fp == NULL) { + printf("MPI_File_close: invalid file handle\n"); + exit(0); + } + else { fclose(fp); *fh = MPI_FILE_NULL; } @@ -1410,6 +1419,39 @@ int MPI_Type_create_subarray(int ndims, const int * array_of_sizes, return MPI_SUCCESS; } +/***************************************************************************** + * + * MPI_File_get_view + * + *****************************************************************************/ + +int MPI_File_get_view(MPI_File fh, MPI_Offset * disp, MPI_Datatype * etype, + MPI_Datatype * filetype, char * datarep) { + + FILE * fp = NULL; + + assert(disp); + assert(etype); + assert(filetype); + assert(datarep); + assert(mpi_info); + + fp = mpi_file_handle_to_fp(mpi_info, fh); + + if (fp == NULL) { + printf("MPI_File_get_view: invalid file handle\n"); + exit(0); + } + else { + file_t * file = &mpi_info->filelist[fh]; + *disp = file->disp; + *etype = file->etype; + *filetype = file->filetype; + strncpy(datarep, file->datarep, MPI_MAX_DATAREP_STRING-1); + } + + return MPI_SUCCESS; +} /***************************************************************************** * * MPI_File_set_view @@ -1420,7 +1462,26 @@ int MPI_File_set_view(MPI_File fh, MPI_Offset disp, MPI_Datatype etype, MPI_Datatype filetype, const char * datarep, MPI_Info info) { - /* datarep may be 'NULL' => defaults to "native" */ + FILE * fp = NULL; + + assert(datarep); + assert(mpi_info); + + fp = mpi_file_handle_to_fp(mpi_info, fh); + + if (fp == NULL) { + printf("MPI_File_set_view: invalid file handle\n"); + exit(0); + } + else { + file_t * file = &mpi_info->filelist[fh]; + file->disp = disp; + file->etype = etype; + file->filetype = filetype; + /* Could demand "native" ... */ + strncpy(file->datarep, datarep, MPI_MAX_DATAREP_STRING-1); + /* info is currently discarded */ + } return MPI_SUCCESS; } @@ -1448,24 +1509,31 @@ int MPI_File_read_all(MPI_File fh, void * buf, int count, int MPI_File_write_all(MPI_File fh, const void * buf, int count, MPI_Datatype datatype, MPI_Status * status) { - assert(0 < fh && fh < MAX_USER_FILE); + FILE * fp = NULL; + assert(buf); assert(status); - /* Translate to a simple fwrite() */ + fp = mpi_file_handle_to_fp(mpi_info, fh); + + if (fp == NULL) { + printf("MPI_File_write_all: invalid_file handle"); + exit(0); + } + else { - FILE * fp = mpi_info->filelist[fh]; - size_t size = mpi_sizeof(datatype); - size_t nitems = count; + /* Translate to a simple fwrite() */ - assert(fp); /* Replace with check on fh */ + size_t size = mpi_sizeof(datatype); + size_t nitems = count; - fwrite(buf, size, nitems, fp); + fwrite(buf, size, nitems, fp); - if (ferror(fp)) { - perror("perror: "); - printf("MPI_File_write_all() failed\n"); - exit(0); + if (ferror(fp)) { + perror("perror: "); + printf("MPI_File_write_all() file operation failed\n"); + exit(0); + } } return MPI_SUCCESS; @@ -1577,55 +1645,74 @@ static int mpi_data_type_handle(mpi_info_t * ctxt, MPI_Datatype handle) { * mpi_file_handle_retain * * Implementation of file open. - * Success returns a valie MPI_FIle handle. + * Success returns a valid MPI_FIle handle. * *****************************************************************************/ static MPI_File mpi_file_handle_retain(mpi_info_t * mpi, FILE * fp) { - int handle = MPI_FILE_NULL; + MPI_File fh = MPI_FILE_NULL; assert(mpi); assert(fp); for (int ih = 1; ih < MAX_USER_FILE; ih++) { - if (mpi->filelist[ih] == NULL) { - handle = ih; + if (mpi->filelist[ih].fp == NULL) { + fh = ih; break; } } - if (handle == MPI_FILE_NULL) { + if (fh == MPI_FILE_NULL) { printf("Run out of MPI file handles\n"); exit(0); } /* Record the pointer against the handle */ - mpi->filelist[handle] = fp; + mpi->filelist[fh].fp = fp; - return handle; + return fh; } /***************************************************************************** * * mpi_file_handle_release * + * Release handle, and return the file pointer. + * *****************************************************************************/ -static FILE * mpi_file_handle_release(mpi_info_t * mpi, MPI_File handle) { +static FILE * mpi_file_handle_release(mpi_info_t * mpi, MPI_File fh) { FILE * fp = NULL; assert(mpi); - assert(1 <= handle && handle < MAX_USER_FILE); - if (mpi->filelist[handle] == NULL) { - printf("Attempt to release NULL mpi file handle"); - exit(0); + if (1 <= fh && fh < MAX_USER_FILE) { + fp = mpi->filelist[fh].fp; + mpi->filelist[fh].fp = NULL; /* Release */ } - fp = mpi->filelist[handle]; - mpi->filelist[handle] = NULL; + return fp; +} + +/***************************************************************************** + * + * mpi_file_handle_to_fp + * + * Valid handles return relevant FILE * fp (or NULL). + * + *****************************************************************************/ + +static FILE * mpi_file_handle_to_fp(mpi_info_t * mpi, MPI_File fh) { + + FILE * fp = NULL; + + assert(mpi); + + if (1 <= fh && fh < MAX_USER_FILE) { + fp = mpi->filelist[fh].fp; + } return fp; } diff --git a/mpi_s/mpi_tests.c b/mpi_s/mpi_tests.c index 1b6fb9ace..2d2e54876 100644 --- a/mpi_s/mpi_tests.c +++ b/mpi_s/mpi_tests.c @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -24,6 +25,8 @@ static int test_mpi_type_contiguous(void); static int test_mpi_type_create_struct(void); static int test_mpi_op_create(void); static int test_mpi_file_open(void); +static int test_mpi_file_get_view(void); +static int test_mpi_file_set_view(void); int main (int argc, char ** argv) { @@ -45,6 +48,8 @@ int main (int argc, char ** argv) { test_mpi_op_create(); test_mpi_file_open(); + test_mpi_file_get_view(); + test_mpi_file_set_view(); ireturn = MPI_Finalize(); assert(ireturn == MPI_SUCCESS); @@ -392,3 +397,79 @@ int test_mpi_file_open(void) { return 0; } + +/***************************************************************************** + * + * test_mpi_file_get_view + * + *****************************************************************************/ + +static int test_mpi_file_get_view(void) { + + MPI_File fh = MPI_FILE_NULL; + MPI_Comm comm = MPI_COMM_WORLD; + MPI_Info info = MPI_INFO_NULL; + + MPI_File_open(comm, "mpif.dat", MPI_MODE_WRONLY+MPI_MODE_CREATE, info, &fh); + + { + MPI_Offset disp = 1; + MPI_Datatype etype = MPI_DATATYPE_NULL; + MPI_Datatype filetype = MPI_DATATYPE_NULL; + char datarep[MPI_MAX_DATAREP_STRING] = {0}; + + MPI_File_get_view(fh, &disp, &etype, &filetype, datarep); + assert(disp == 0); + assert(etype == MPI_BYTE); + assert(filetype == MPI_BYTE); + assert(strncmp(datarep, "native", MPI_MAX_DATAREP_STRING-1) == 0); + } + + MPI_File_close(&fh); + unlink("mpif.dat"); + + return 0; +} + +/***************************************************************************** + * + * test_mpi_file_set_view + * + *****************************************************************************/ + +int test_mpi_file_set_view(void) { + + + MPI_File fh = MPI_FILE_NULL; + MPI_Comm comm = MPI_COMM_WORLD; + MPI_Info info = MPI_INFO_NULL; + + MPI_File_open(comm, "mpif.dat", MPI_MODE_WRONLY+MPI_MODE_CREATE, info, &fh); + + { + /* Thge values here aren't really meaningful, but they will do ... */ + MPI_Offset disp = 1; + MPI_Datatype etype = MPI_DOUBLE; + MPI_Datatype filetype = MPI_INT; + + MPI_File_set_view(fh, disp, etype, filetype, "native", info); + } + + { + MPI_Offset disp = 0; + MPI_Datatype etype = MPI_DATATYPE_NULL; + MPI_Datatype filetype = MPI_DATATYPE_NULL; + char datarep[MPI_MAX_DATAREP_STRING] = {0}; + + MPI_File_get_view(fh, &disp, &etype, &filetype, datarep); + assert(disp == 1); + assert(etype == MPI_DOUBLE); + assert(filetype == MPI_INT); + assert(strncmp(datarep, "native", MPI_MAX_DATAREP_STRING-1) == 0); + } + + MPI_File_close(&fh); + unlink("mpif.dat"); + + return 0; +} From 2d2e26460fefee096f2cd9963c7bcceae9498f56 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 23 Aug 2022 19:16:46 +0100 Subject: [PATCH 010/244] MPI File stubs for write all and read all working --- mpi_s/mpi_serial.c | 47 +++++++++++++++- mpi_s/mpi_tests.c | 137 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 180 insertions(+), 4 deletions(-) diff --git a/mpi_s/mpi_serial.c b/mpi_s/mpi_serial.c index 0de115efc..24c6415ec 100644 --- a/mpi_s/mpi_serial.c +++ b/mpi_s/mpi_serial.c @@ -49,7 +49,8 @@ /* We are not going to deal with all possible data types; encode * what we have ... */ -enum dt_flavour {DT_NOT_IMPLEMENTED = 0, DT_CONTIGUOUS, DT_VECTOR, DT_STRUCT}; +enum dt_flavour {DT_NOT_IMPLEMENTED = 0, DT_CONTIGUOUS, DT_VECTOR, DT_STRUCT, + DT_SUBARRAY}; typedef struct internal_data_type_s data_t; typedef struct internal_file_view_s file_t; @@ -1410,12 +1411,30 @@ int MPI_Type_create_subarray(int ndims, const int * array_of_sizes, int order, MPI_Datatype oldtype, MPI_Datatype * newtype) { + + int nelements = 0; + + assert(ndims == 2 || ndims == 3); /* We accept this is not general */ assert(array_of_sizes); assert(array_of_subsizes); assert(array_of_starts); assert(order == MPI_ORDER_C || order == MPI_ORDER_FORTRAN); assert(newtype); + /* Assume this is a contiguous block of elements of oldtype */ + nelements = array_of_sizes[0]*array_of_sizes[1]; + if (ndims == 3) nelements *= array_of_sizes[2]; + + { + data_t dt = {0}; + + dt.handle = MPI_DATATYPE_NULL; + dt.bytes = mpi_sizeof(oldtype)*nelements; + dt.commit = 0; + dt.flavour = DT_SUBARRAY; + + mpi_data_type_add(mpi_info, &dt, newtype); + } return MPI_SUCCESS; } @@ -1494,8 +1513,31 @@ int MPI_File_set_view(MPI_File fh, MPI_Offset disp, MPI_Datatype etype, int MPI_File_read_all(MPI_File fh, void * buf, int count, MPI_Datatype datatype, MPI_Status * status) { + FILE * fp = NULL; + assert(buf); - assert(status); + + fp = mpi_file_handle_to_fp(mpi_info, fh); + + if (fp == NULL) { + printf("MPI_File_read_all: invalid_file handle\n"); + exit(0); + } + else { + + /* Translate to a simple fread() */ + + size_t size = mpi_sizeof(datatype); + size_t nitems = count; + + fread(buf, size, nitems, fp); + + if (ferror(fp)) { + perror("perror: "); + printf("MPI_File_read_all() file operation failed\n"); + exit(0); + } + } return MPI_SUCCESS; } @@ -1512,7 +1554,6 @@ int MPI_File_write_all(MPI_File fh, const void * buf, int count, FILE * fp = NULL; assert(buf); - assert(status); fp = mpi_file_handle_to_fp(mpi_info, fh); diff --git a/mpi_s/mpi_tests.c b/mpi_s/mpi_tests.c index 2d2e54876..7bdce316b 100644 --- a/mpi_s/mpi_tests.c +++ b/mpi_s/mpi_tests.c @@ -27,6 +27,8 @@ static int test_mpi_op_create(void); static int test_mpi_file_open(void); static int test_mpi_file_get_view(void); static int test_mpi_file_set_view(void); +static int test_mpi_type_create_subarray(void); +static int test_mpi_file_write_all(void); int main (int argc, char ** argv) { @@ -50,6 +52,8 @@ int main (int argc, char ** argv) { test_mpi_file_open(); test_mpi_file_get_view(); test_mpi_file_set_view(); + test_mpi_type_create_subarray(); + test_mpi_file_write_all(); ireturn = MPI_Finalize(); assert(ireturn == MPI_SUCCESS); @@ -447,7 +451,7 @@ int test_mpi_file_set_view(void) { MPI_File_open(comm, "mpif.dat", MPI_MODE_WRONLY+MPI_MODE_CREATE, info, &fh); { - /* Thge values here aren't really meaningful, but they will do ... */ + /* The values here aren't really meaningful, but they will do ... */ MPI_Offset disp = 1; MPI_Datatype etype = MPI_DOUBLE; MPI_Datatype filetype = MPI_INT; @@ -473,3 +477,134 @@ int test_mpi_file_set_view(void) { return 0; } + +/***************************************************************************** + * + * test_mpi_type_create_subarray + * + *****************************************************************************/ + +int test_mpi_type_create_subarray(void) { + + { + /* two dimensional (contiguous) */ + int ndims = 2; + int sizes[2] = {128, 64}; + int subsizes[2] = {128, 64}; + int starts[2] = {0, 0}; + MPI_Datatype oldtype = MPI_CHAR; + MPI_Datatype newtype = MPI_DATATYPE_NULL; + + MPI_Type_create_subarray(ndims, sizes, subsizes, starts, MPI_ORDER_C, + oldtype, &newtype); + MPI_Type_commit(&newtype); + assert(newtype != MPI_DATATYPE_NULL); + MPI_Type_free(&newtype); + } + + { + /* three dimensional (contiguous) */ + int ndims = 3; + int sizes[3] = {16, 8, 4}; + int subsizes[3] = {16, 8, 4}; + int starts[3] = {0, 0}; + MPI_Datatype oldtype = MPI_DOUBLE; + MPI_Datatype newtype = MPI_DATATYPE_NULL; + + MPI_Type_create_subarray(ndims, sizes, subsizes, starts, MPI_ORDER_C, + oldtype, &newtype); + MPI_Type_commit(&newtype); + assert(newtype != MPI_DATATYPE_NULL); + MPI_Type_free(&newtype); + } + + return 0; +} + +/***************************************************************************** + * + * test_mpi_file_write_all + * + * It is convenient to test MPI_File_read_all() at the same time. + * + *****************************************************************************/ + +int test_mpi_file_write_all(void) { + +#define NX 23 +#define NY 12 + + int ifail = 0; /* return value */ + + const char * filename = "mpi-file-write-all.dat"; + MPI_Comm comm = MPI_COMM_WORLD; + MPI_Info info = MPI_INFO_NULL; + + /* Some very synthetic data */ + int ndims = 2; + int sizes[2] = {NX, NY}; + int subsizes[2] = {NX, NY}; + int starts[2] = {0, 0}; + + /* Create a subarray type */ + + MPI_Datatype etype = MPI_DOUBLE; + MPI_Datatype filetype = MPI_DATATYPE_NULL; + + MPI_Type_create_subarray(ndims, sizes, subsizes, starts, MPI_ORDER_C, + etype, &filetype); + MPI_Type_commit(&filetype); + + { + /* Write */ + + MPI_File fh = MPI_FILE_NULL; + MPI_Offset disp = 0; + + int count = 1; + double wbuf[NX*NY] = {0}; + + /* Some test values */ + for (int id = 0; id < NX*NY; id++) { + wbuf[id] = 1.0*id; + } + + MPI_File_open(comm, filename, MPI_MODE_WRONLY+MPI_MODE_CREATE, info, &fh); + + /* Set the view */ + /* As this is serial the datetype is the filetype */ + + MPI_File_set_view(fh, disp, etype, filetype, "native", info); + + MPI_File_write_all(fh, wbuf, count, filetype, MPI_STATUS_IGNORE); + MPI_File_close(&fh); + } + + + { + /* Re-read the same file */ + MPI_File fh = MPI_FILE_NULL; + MPI_Offset disp = 0; + + int count = 1; + double rbuf[NX*NY] = {0}; + + MPI_File_open(comm, filename, MPI_MODE_RDONLY, info, &fh); + + /* Set the view */ + MPI_File_set_view(fh, disp, etype, filetype, "native", info); + + MPI_File_read_all(fh, rbuf, count, filetype, MPI_STATUS_IGNORE); + MPI_File_close(&fh); + + for (int id = 0; id < NX*NY; id++) { + assert(fabs(rbuf[id] - 1.0*id) < DBL_EPSILON); + if (fabs(rbuf[id] - 1.0*id) > DBL_EPSILON) ifail += 1; + } + } + + MPI_Type_free(&filetype); + unlink(filename); + + return ifail; +} From 2325e031fa1beed46b7768f4776cca87750aaebc Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 7 Oct 2022 11:44:56 +0100 Subject: [PATCH 011/244] Add int32_t, int64_t datatypes --- mpi_s/mpi.h | 4 +++- mpi_s/mpi_serial.c | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/mpi_s/mpi.h b/mpi_s/mpi.h index 0f9f6f451..717589ee7 100644 --- a/mpi_s/mpi.h +++ b/mpi_s/mpi.h @@ -81,7 +81,9 @@ enum elementary_datatypes {MPI_CHAR = -11, MPI_DOUBLE = -20, MPI_LONG_DOUBLE = -21, MPI_BYTE = -22, - MPI_PACKED = -23}; + MPI_PACKED = -23, + MPI_INT32_T = -24, + MPI_INT64_T = -25}; enum collective_operations {MPI_MAX, MPI_MIN, diff --git a/mpi_s/mpi_serial.c b/mpi_s/mpi_serial.c index 24c6415ec..6477c09b2 100644 --- a/mpi_s/mpi_serial.c +++ b/mpi_s/mpi_serial.c @@ -1058,10 +1058,17 @@ static int mpi_sizeof(MPI_Datatype type) { break; case MPI_LONG_DOUBLE: size = sizeof(double); + assert(sizeof(double) == sizeof(long double)); break; case MPI_BYTE: size = sizeof(char); break; + case MPI_INT32_T: + size = sizeof(int32_t); + break; + case MPI_INT64_T: + size = sizeof(int64_t); + break; case MPI_PACKED: printf("MPI_PACKED not implemented\n"); default: From 0b827dbd6b79126aff394bf7c3fc274007901e38 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 7 Oct 2022 13:27:28 +0100 Subject: [PATCH 012/244] Reduce complexity --- src/field.c | 11 +++++------ src/field.h | 3 ++- src/io_aggr.h | 6 ++---- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/field.c b/src/field.c index b29bdcf32..08514e6fe 100644 --- a/src/field.c +++ b/src/field.c @@ -99,12 +99,10 @@ __host__ int field_create(pe_t * pe, cs_t * cs, lees_edw_t * le, obj->opts = *opts; /* I/O aggregator */ - { - obj->aggr.asc_etype = MPI_CHAR; - obj->aggr.bin_etype = MPI_DOUBLE; - obj->aggr.asc_esize = 23*sizeof(char); /* 23 to be rationalised */ - obj->aggr.bin_esize = obj->opts.ndata*sizeof(double); - } + obj->aggr_asc.etype = MPI_CHAR; + obj->aggr_asc.esize = 23*sizeof(char); + obj->aggr_bin.etype = MPI_DOUBLE; + obj->aggr_bin.esize = obj->opts.ndata*sizeof(double); if (obj->opts.haloverbose) field_halo_info(obj); @@ -1083,6 +1081,7 @@ int field_io_aggr_pack(field_t * field, io_aggr_buf_t aggr) { { int iasc = field->opts.iodata.output.iorformat == IO_RECORD_ASCII; int ibin = field->opts.iodata.output.iorformat == IO_RECORD_BINARY; + assert(iasc ^ ibin); /* one or other */ #pragma omp for for (int ib = 0; ib < cs_limits_size(aggr.lim); ib++) { diff --git a/src/field.h b/src/field.h index bb1b33db7..23c751b19 100644 --- a/src/field.h +++ b/src/field.h @@ -63,7 +63,8 @@ struct field_s { pe_t * pe; /* Parallel environment */ cs_t * cs; /* Coordinate system */ lees_edw_t * le; /* Lees-Edwards */ - io_aggr_t aggr; /* I/O aggregator information */ + io_aggr_t aggr_asc; /* I/O aggregator information */ + io_aggr_t aggr_bin; /* Binary */ io_info_t * info; /* I/O Handler (to be removed) */ halo_swap_t * halo; /* Halo swap driver object */ field_halo_t h; /* Host halo */ diff --git a/src/io_aggr.h b/src/io_aggr.h index f6e4215fd..59f9e727b 100644 --- a/src/io_aggr.h +++ b/src/io_aggr.h @@ -19,10 +19,8 @@ typedef struct io_aggr_s io_aggr_t; struct io_aggr_s { - MPI_Datatype bin_etype; /* Data type for element (binary) */ - MPI_Datatype asc_etype; /* Data type for element (ascii) usu. MPI_CHAR */ - size_t bin_esize; /* number bytes per lattice site */ - size_t asc_esize; /* fixed character size per line of output */ + MPI_Datatype etype; /* Data type for element */ + size_t esize; /* Number bytes per element */ }; #endif From 1b168d471a45214c478c3a455ff4a295e8262834 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 7 Oct 2022 13:30:14 +0100 Subject: [PATCH 013/244] First attempt mpio aggregator --- src/io_aggr_buf_mpio.c | 145 +++++++++++++++++++++++++++++++++++++++++ src/io_aggr_buf_mpio.h | 25 +++++++ 2 files changed, 170 insertions(+) create mode 100644 src/io_aggr_buf_mpio.c create mode 100644 src/io_aggr_buf_mpio.h diff --git a/src/io_aggr_buf_mpio.c b/src/io_aggr_buf_mpio.c new file mode 100644 index 000000000..cdc9cd568 --- /dev/null +++ b/src/io_aggr_buf_mpio.c @@ -0,0 +1,145 @@ +/***************************************************************************** + * + * io_aggr_buf_mpio.c + * + * Read/write aggregated data buffers using MPI/IO. + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include + +#include "io_aggr_buf_mpio.h" + +/***************************************************************************** + * + * io_aggr_buf_write + * + * Generalise to case where separate write_all in comm. + * + *****************************************************************************/ + +int io_aggr_mpio_write(pe_t * pe, cs_t * cs, const char * filename, + const io_aggr_buf_t * buf) { + assert(pe); + assert(cs); + assert(filename); + assert(buf); + + int ndims = 3; /* nz may be 1; ie., 2-dimensional system */ + int sizes[3] = {0}; /* ie., ntotal */ + int subsizes[3] = {0}; /* ie., nlocal */ + int starts[3] = {0}; /* ie., local offset */ + int zero3[3] = {0}; + + MPI_Comm comm = MPI_COMM_NULL; + MPI_Datatype etype = MPI_DATATYPE_NULL; /* element description */ + MPI_Datatype array = MPI_DATATYPE_NULL; /* local array description */ + MPI_Datatype filetype = MPI_DATATYPE_NULL; /* global description */ + + cs_cart_comm(cs, &comm); + cs_ntotal(cs, sizes); + cs_nlocal(cs, subsizes); + cs_nlocal_offset(cs, starts); + + /* Element type (multiple of MPI_CHAR), and file type */ + + MPI_Type_contiguous(buf->szelement, MPI_CHAR, &etype); + MPI_Type_create_subarray(ndims, subsizes, subsizes, zero3, MPI_ORDER_C, + etype, &array); + MPI_Type_create_subarray(ndims, sizes, subsizes, starts, MPI_ORDER_C, + etype, &filetype); + + MPI_Type_commit(&etype); + MPI_Type_commit(&array); + MPI_Type_commit(&filetype); + + { + MPI_File fh = MPI_FILE_NULL; + MPI_Info info = MPI_INFO_NULL; /* MUST BE SUPPLIED SOMEHOW */ + MPI_Offset disp = 0; + + int count = 1; + + MPI_File_open(comm, filename, MPI_MODE_WRONLY+MPI_MODE_CREATE, info, &fh); + MPI_File_set_view(fh, disp, etype, filetype, "native", info); + MPI_File_write_all(fh, buf->buf, count, array, MPI_STATUS_IGNORE); + MPI_File_close(&fh); + } + + MPI_Type_free(&filetype); + MPI_Type_free(&array); + MPI_Type_free(&etype); + + return 0; +} + +/***************************************************************************** + * + * io_aggr_mpio_read + * + * SAME except write_all is read_all and mode! + * + *****************************************************************************/ + +int io_aggr_mpio_read(pe_t * pe, cs_t * cs, const char * filename, + io_aggr_buf_t * buf) { + assert(pe); + assert(cs); + assert(filename); + assert(buf); + + int ndims = 3; /* nz may be 1; ie., 2-dimensional system */ + int sizes[3] = {0}; /* ie., ntotal */ + int subsizes[3] = {0}; /* ie., nlocal */ + int starts[3] = {0}; /* ie., local offset */ + int zero[3] = {0}; + + MPI_Comm comm = MPI_COMM_NULL; + MPI_Datatype etype = MPI_DATATYPE_NULL; + MPI_Datatype array = MPI_DATATYPE_NULL; + MPI_Datatype filetype = MPI_DATATYPE_NULL; + + cs_cart_comm(cs, &comm); + cs_ntotal(cs, sizes); + cs_nlocal(cs, subsizes); + cs_nlocal_offset(cs, starts); + + /* Element type (multiple of MPI_CHAR), and file type */ + + MPI_Type_contiguous(buf->szelement, MPI_CHAR, &etype); + MPI_Type_create_subarray(ndims, subsizes, subsizes, zero, MPI_ORDER_C, + etype, &array); + MPI_Type_create_subarray(ndims, sizes, subsizes, starts, MPI_ORDER_C, + etype, &filetype); + + MPI_Type_commit(&etype); + MPI_Type_commit(&array); + MPI_Type_commit(&filetype); + + { + MPI_File fh = MPI_FILE_NULL; + MPI_Info info = MPI_INFO_NULL; /* MUST BE SUPPLIED SOMEHOW */ + MPI_Offset disp = 0; + + int count = 1; + + MPI_File_open(comm, filename, MPI_MODE_RDONLY, info, &fh); + MPI_File_set_view(fh, disp, etype, filetype, "native", info); + MPI_File_read_all(fh, buf->buf, count, array, MPI_STATUS_IGNORE); + MPI_File_close(&fh); + } + + MPI_Type_free(&filetype); + MPI_Type_free(&array); + MPI_Type_free(&etype); + + return 0; +} diff --git a/src/io_aggr_buf_mpio.h b/src/io_aggr_buf_mpio.h new file mode 100644 index 000000000..e31218799 --- /dev/null +++ b/src/io_aggr_buf_mpio.h @@ -0,0 +1,25 @@ +/***************************************************************************** + * + * io_aggr_mpio.h + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#ifndef LUDWIG_IO_AGGR_MPIO_H +#define LUDWIG_IO_AGGR_MPIO_H + +#include "pe.h" +#include "coords.h" +#include "io_aggr_buf.h" + +int io_aggr_mpio_write(pe_t * pe, cs_t * cs, const char * filename, + const io_aggr_buf_t * buf); +int io_aggr_mpio_read(pe_t * pe, cs_t * cs, const char * filename, + io_aggr_buf_t * buf); +#endif From 9eba47cb8ea9df4a9ed10ef7ecba808e7693830f Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 7 Oct 2022 13:31:10 +0100 Subject: [PATCH 014/244] Add mpio aggregator test --- tests/unit/test_io_aggr_mpio.c | 347 +++++++++++++++++++++++++++++++++ tests/unit/tests.c | 1 + tests/unit/tests.h | 1 + 3 files changed, 349 insertions(+) create mode 100644 tests/unit/test_io_aggr_mpio.c diff --git a/tests/unit/test_io_aggr_mpio.c b/tests/unit/test_io_aggr_mpio.c new file mode 100644 index 000000000..4ba8816b1 --- /dev/null +++ b/tests/unit/test_io_aggr_mpio.c @@ -0,0 +1,347 @@ +/***************************************************************************** + * + * test_io_aggr_mpio.c + * + * The MPI / IO aggregtor (with a mock aggregator). + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Contributing authors: + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include +#include + +#include "pe.h" +#include "coords.h" +#include "io_aggr.h" +#include "io_aggr_buf_mpio.h" + +int test_io_aggr_mpio_write(pe_t * pe, cs_t * cs, io_aggr_t aggr, + const char * filename); +int test_io_aggr_mpio_read(pe_t * pe, cs_t * cs, io_aggr_t aggr, + const char * filename); + +int test_io_aggr_buf_pack_asc(cs_t * cs, io_aggr_buf_t buf); +int test_io_aggr_buf_pack_bin(cs_t * cs, io_aggr_buf_t buf); +int test_io_aggr_buf_unpack_asc(cs_t * cs, io_aggr_buf_t buf); +int test_io_aggr_buf_unpack_bin(cs_t * cs, io_aggr_buf_t buf); + +/***************************************************************************** + * + * test_io_aggr_mpio_suite + * + *****************************************************************************/ + +int test_io_aggr_mpio_suite(void) { + + pe_t * pe = NULL; + cs_t * cs = NULL; + + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + cs_create(pe, &cs); + + { + /* I want a smallish size, but allow for a meaningful decomposition */ + int ntotal[3] = {16, 8, 4}; + cs_ntotal_set(cs, ntotal); + } + + cs_init(cs); + + /* ASCII: write then read */ + { + io_aggr_t aggr = {.etype = MPI_CHAR, .esize = 28*sizeof(char)}; + const char * filename = "io-aggr-mpio-asc.dat"; + + test_io_aggr_mpio_write(pe, cs, aggr, filename); + test_io_aggr_mpio_read(pe, cs, aggr, filename); + + MPI_Barrier(MPI_COMM_WORLD); + if (pe_mpi_rank(pe) == 0) remove(filename); + } + + /* Binary: write thne read */ + { + io_aggr_t aggr = {.etype = MPI_INT64_T, .esize = sizeof(int64_t)}; + const char * filename = "io-aggr-mpio-bin.dat"; + + test_io_aggr_mpio_write(pe, cs, aggr, filename); + test_io_aggr_mpio_read(pe, cs, aggr, filename); + + MPI_Barrier(MPI_COMM_WORLD); + if (pe_mpi_rank(pe) == 0) remove(filename); + } + + pe_info(pe, "PASS ./unit/test_io_aggr_mpio\n"); + cs_free(cs); + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_io_aggr_mpio_write + * + *****************************************************************************/ + +int test_io_aggr_mpio_write(pe_t * pe, cs_t * cs, io_aggr_t aggr, + const char * filename) { + + assert(pe); + assert(cs); + assert(filename); + + int nlocal[3] = {0}; + cs_nlocal(cs, nlocal); + + /* Aggregator buffer */ + + { + cs_limits_t lim = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; + io_aggr_buf_t buf = {0}; + + io_aggr_buf_create(aggr.esize, lim, &buf); + + if (aggr.etype == MPI_CHAR) test_io_aggr_buf_pack_asc(cs, buf); + if (aggr.etype == MPI_INT64_T) test_io_aggr_buf_pack_bin(cs, buf); + + io_aggr_mpio_write(pe, cs, filename, &buf); + + io_aggr_buf_free(&buf); + } + + return 0; +} + +/***************************************************************************** + * + * test_io_aggr_mpio_read + * + *****************************************************************************/ + +int test_io_aggr_mpio_read(pe_t * pe, cs_t * cs, io_aggr_t aggr, + const char * filename) { + + assert(pe); + assert(cs); + assert(filename); + + int nlocal[3] = {0}; + cs_nlocal(cs, nlocal); + + { + /* Read and check */ + cs_limits_t lim = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; + io_aggr_buf_t buf = {0}; + + io_aggr_buf_create(aggr.esize, lim ,&buf); + + io_aggr_mpio_read(pe, cs, filename, &buf); + + if (aggr.etype == MPI_CHAR) test_io_aggr_buf_unpack_asc(cs, buf); + if (aggr.etype == MPI_INT64_T) test_io_aggr_buf_unpack_bin(cs, buf); + + io_aggr_buf_free(&buf); + } + + return 0; +} + +/***************************************************************************** + * + * test_unique_value + * + *****************************************************************************/ + +int64_t test_unique_value(cs_t * cs, int ic, int jc, int kc) { + + int64_t ival = -1; + int ntotal[3] = {0}; + int nlocal[3] = {0}; + int offset[3] = {0}; + + cs_ntotal(cs, ntotal); + cs_nlocal(cs, nlocal); + cs_nlocal_offset(cs, offset); + + { + int ix = offset[X] + ic - 1; + int iy = offset[Y] + jc - 1; + int iz = offset[Z] + kc - 1; + + ival = ntotal[Z]*ntotal[Y]*ix + ntotal[Z]*iy + iz; + } + + return ival; +} + +/***************************************************************************** + * + * test_io_aggr_buf_pack_asc + * + *****************************************************************************/ + +int test_io_aggr_buf_pack_asc(cs_t * cs, io_aggr_buf_t buf) { + + assert(cs); + assert(buf.buf); + + int ib = 0; + int ntotal[3] = {0}; + int nlocal[3] = {0}; + int offset[3] = {0}; + + cs_ntotal(cs, ntotal); + cs_nlocal(cs, nlocal); + cs_nlocal_offset(cs, offset); + + for (int ic = 1; ic <= nlocal[X]; ic++) { + for (int jc = 1; jc <= nlocal[Y]; jc++) { + for (int kc = 1; kc <= nlocal[Z]; kc++) { + int64_t ival = test_unique_value(cs, ic, jc, kc); + { + /* Add 1 <= label <= ntotal for each dimension */ + int ix = offset[X] + ic; + int iy = offset[Y] + jc; + int iz = offset[Z] + kc; + int nc = -1; + char cline[BUFSIZ] = {0}; + nc = sprintf(cline, "%4d %4d %4d %12lld\n", ix, iy, iz, ival); + assert(nc == buf.szelement); + memcpy(buf.buf + ib*buf.szelement, cline, buf.szelement); + } + ib += 1; + } + } + } + + assert(ib == nlocal[X]*nlocal[Y]*nlocal[Z]); + + return 0; +} + +/***************************************************************************** + * + * test_io_aggr_buf_unpack_asc + * + *****************************************************************************/ + +int test_io_aggr_buf_unpack_asc(cs_t * cs, io_aggr_buf_t buf) { + + assert(cs); + assert(buf.buf); + + int ib = 0; + int ntotal[3] = {0}; + int nlocal[3] = {0}; + int offset[3] = {0}; + + cs_ntotal(cs, ntotal); + cs_nlocal(cs, nlocal); + cs_nlocal_offset(cs, offset); + + for (int ic = 1; ic <= nlocal[X]; ic++) { + for (int jc = 1; jc <= nlocal[Y]; jc++) { + for (int kc = 1; kc <= nlocal[Z]; kc++) { + int64_t ival = test_unique_value(cs, ic, jc, kc); + { + /* Add 1 <= label <= ntotal for each dimension */ + int ix = offset[X] + ic; + int iy = offset[Y] + jc; + int iz = offset[Z] + kc; + int ixread = -1; + int iyread = -1; + int izread = -1; + int64_t ivalread = -1; + int nc = sscanf(buf.buf + ib*buf.szelement, "%4d %4d %4d %12lld", + &ixread, &iyread, &izread, &ivalread); + + assert(nc == 4); + assert(ixread == ix); + assert(iyread == iy); + assert(izread == iz); + assert(ivalread == ival); + } + ib += 1; + } + } + } + + assert(ib == nlocal[X]*nlocal[Y]*nlocal[Z]); + + return 0; +} + + +/***************************************************************************** + * + * test_io_aggr_buf_pack_bin + * + *****************************************************************************/ + +int test_io_aggr_buf_pack_bin(cs_t * cs, io_aggr_buf_t buf) { + + assert(cs); + assert(buf.buf); + + int ib = 0; + int nlocal[3] = {0}; + + cs_nlocal(cs, nlocal); + + for (int ic = 1; ic <= nlocal[X]; ic++) { + for (int jc = 1; jc <= nlocal[Y]; jc++) { + for (int kc = 1; kc <= nlocal[Z]; kc++) { + int64_t ival = test_unique_value(cs, ic, jc, kc); + memcpy(buf.buf + ib*sizeof(int64_t), &ival, sizeof(int64_t)); + ib += 1; + } + } + } + + assert(ib == nlocal[X]*nlocal[Y]*nlocal[Z]); + + return 0; +} + +/***************************************************************************** + * + * test_io_aggr_buf_unpack_bin + * + *****************************************************************************/ + +int test_io_aggr_buf_unpack_bin(cs_t * cs, io_aggr_buf_t buf) { + + assert(cs); + assert(buf.buf); + + int ib = 0; + int nlocal[3] = {0}; + + cs_nlocal(cs, nlocal); + + for (int ic = 1; ic <= nlocal[X]; ic++) { + for (int jc = 1; jc <= nlocal[Y]; jc++) { + for (int kc = 1; kc <= nlocal[Z]; kc++) { + int64_t ival = test_unique_value(cs, ic, jc, kc); + int64_t iread = -1; + memcpy(&iread, buf.buf + ib*sizeof(int64_t), sizeof(int64_t)); + assert(ival == iread); + ib += 1; + } + } + } + + assert(ib == nlocal[X]*nlocal[Y]*nlocal[Z]); + + return 0; +} diff --git a/tests/unit/tests.c b/tests/unit/tests.c index 429216d0a..efffa4a1f 100644 --- a/tests/unit/tests.c +++ b/tests/unit/tests.c @@ -74,6 +74,7 @@ __host__ int tests_create() { test_halo_suite(); test_hydro_suite(); test_io_aggr_buf_suite(); + test_io_aggr_mpio_suite(); test_io_suite(); test_io_options_suite(); test_io_options_rt_suite(); diff --git a/tests/unit/tests.h b/tests/unit/tests.h index f7aa2279b..606699d40 100644 --- a/tests/unit/tests.h +++ b/tests/unit/tests.h @@ -53,6 +53,7 @@ int test_gradient_d3q27_suite(void); int test_halo_suite(void); int test_hydro_suite(void); int test_io_aggr_buf_suite(void); +int test_io_aggr_mpio_suite(void); int test_io_suite(void); int test_io_options_suite(void); int test_io_options_rt_suite(void); From e9b2f287b0ab3a6dbfe6afbe6952f896b9453692 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 7 Oct 2022 13:31:57 +0100 Subject: [PATCH 015/244] Refactor aggregator stuff --- tests/unit/test_field.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/unit/test_field.c b/tests/unit/test_field.c index d827af35e..8ede05118 100644 --- a/tests/unit/test_field.c +++ b/tests/unit/test_field.c @@ -599,7 +599,9 @@ int test_field_write_buf_ascii(pe_t * pe) { /***************************************************************************** * - * test_field_io_aggr_pack + * test_field_io_aggr_pack + * + * It is convenient to test field_io_aggr_unpack() at te same time. * *****************************************************************************/ @@ -620,10 +622,10 @@ int test_field_io_aggr_pack(pe_t * pe) { /* This should be elsewhere as part of test_field_create() */ { /* Note one can use == with pre-defined data types */ - assert(field->aggr.asc_etype == MPI_CHAR); - assert(field->aggr.bin_etype == MPI_DOUBLE); - assert(field->aggr.asc_esize == 23*sizeof(char)); - assert(field->aggr.bin_esize == field->nf*sizeof(double)); + assert(field->aggr_asc.etype == MPI_CHAR); + assert(field->aggr_bin.etype == MPI_DOUBLE); + assert(field->aggr_asc.esize == 23*sizeof(char)); + assert(field->aggr_bin.esize == field->nf*sizeof(double)); } { @@ -633,7 +635,7 @@ int test_field_io_aggr_pack(pe_t * pe) { cs_limits_t lim = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; io_aggr_buf_t buf = {0}; - io_aggr_buf_create(field->aggr.bin_esize, lim, &buf); + io_aggr_buf_create(field->aggr_bin.esize, lim, &buf); util_field_data_check_set(field); field_io_aggr_pack(field, buf); From 5caf7fdf9d0035171ba2774a1ec2ffba247da2e1 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 7 Oct 2022 13:32:16 +0100 Subject: [PATCH 016/244] Fix for nproc > 4 --- tests/unit/test_psi_sor.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/tests/unit/test_psi_sor.c b/tests/unit/test_psi_sor.c index 11e146f82..55b4edc00 100644 --- a/tests/unit/test_psi_sor.c +++ b/tests/unit/test_psi_sor.c @@ -53,10 +53,21 @@ int test_psi_sor_suite(void) { pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); - test_psi_sor_poisson(pe); - test_psi_sor_vare_poisson(pe); + { + int mpisz = pe_mpi_size(pe); + + if (mpisz > 4) { + /* It's really just a 1-d problem, so no large deompcositions */ + pe_info(pe, "SKIP ./unit/test_psi_sor\n"); + } + else { + test_psi_sor_poisson(pe); + test_psi_sor_vare_poisson(pe); + + pe_info(pe, "PASS ./unit/test_psi_sor\n"); + } + } - pe_info(pe, "PASS ./unit/test_psi_sor\n"); pe_free(pe); return 0; @@ -83,8 +94,16 @@ int test_psi_sor_poisson(pe_t * pe) { assert(pe); cs_create(pe, &cs); - cs_nhalo_set(cs, 1); - cs_ntotal_set(cs, ntotal); + { + /* We need to control the decomposition (not in z, please) */ + int ndims = 3; + int dims[3] = {0,0,1}; + + MPI_Dims_create(pe_mpi_size(pe), ndims, dims); + cs_nhalo_set(cs, 1); + cs_ntotal_set(cs, ntotal); + cs_decomposition_set(cs, dims); + } cs_init(cs); psi_create(pe, cs, 2, &psi); @@ -101,6 +120,7 @@ int test_psi_sor_poisson(pe_t * pe) { psi_halo_rho(psi); /* Time step is -1 for no output. */ + psi_sor_poisson(psi, -1); test_charge1_exact(psi, fepsilon_constant); From cb3b38712ecfff6a3ef943e735d3de4cc9b8e50a Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 7 Oct 2022 14:45:06 +0100 Subject: [PATCH 017/244] Do not ignore return values --- mpi_s/mpi_serial.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mpi_s/mpi_serial.c b/mpi_s/mpi_serial.c index 6477c09b2..0617f8003 100644 --- a/mpi_s/mpi_serial.c +++ b/mpi_s/mpi_serial.c @@ -1536,14 +1536,14 @@ int MPI_File_read_all(MPI_File fh, void * buf, int count, size_t size = mpi_sizeof(datatype); size_t nitems = count; - - fread(buf, size, nitems, fp); + size_t nr = fread(buf, size, nitems, fp); if (ferror(fp)) { perror("perror: "); printf("MPI_File_read_all() file operation failed\n"); exit(0); } + assert(nr == nitems); } return MPI_SUCCESS; @@ -1574,14 +1574,14 @@ int MPI_File_write_all(MPI_File fh, const void * buf, int count, size_t size = mpi_sizeof(datatype); size_t nitems = count; - - fwrite(buf, size, nitems, fp); + size_t nw = fwrite(buf, size, nitems, fp); if (ferror(fp)) { perror("perror: "); printf("MPI_File_write_all() file operation failed\n"); exit(0); } + assert(nw == nitems); } return MPI_SUCCESS; From 591f006777d0a635302301b05a23d0dea89286dc Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 7 Oct 2022 14:45:26 +0100 Subject: [PATCH 018/244] Fix warnings for floating point equality --- mpi_s/mpi_tests.c | 70 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 10 deletions(-) diff --git a/mpi_s/mpi_tests.c b/mpi_s/mpi_tests.c index 7bdce316b..055a004e0 100644 --- a/mpi_s/mpi_tests.c +++ b/mpi_s/mpi_tests.c @@ -2,6 +2,10 @@ * * mpi_tests * + * Tests for the serial stubs + * + * (c) 2022 The University of Edinburgh + * *****************************************************************************/ #include @@ -30,6 +34,52 @@ static int test_mpi_file_set_view(void); static int test_mpi_type_create_subarray(void); static int test_mpi_file_write_all(void); +/* Utilities */ + +void util_printf_bits(size_t size, void const * data) { + + unsigned char * bytes = (unsigned char *) data; + + for (size_t ibyte = size; ibyte-- > 0; ) { + for (int ibit = 7; ibit >= 0; ibit--) { + unsigned char bit = (bytes[ibyte] >> ibit) & 1; + printf("%u", bit); + } + } + + printf("\n"); + + return; +} + +static int util_bits_same(size_t size, const void * data1, const void * data2) { + + int bsame = 1; + unsigned char * b1 = (unsigned char *) data1; + unsigned char * b2 = (unsigned char *) data2; + + assert(b1); + assert(b2); + + for (size_t ibyte = size; ibyte-- > 0; ) { + for (int ibit = 7; ibit >= 0; ibit--) { + unsigned char bit1 = (b1[ibyte] >> ibit) & 1; + unsigned char bit2 = (b2[ibyte] >> ibit) & 1; + if (bit1 != bit2) bsame = 0; + } + } + + return bsame; +} + +static int util_double_same(double d1, double d2) { + + double a = d1; + double b = d2; + + return util_bits_same(sizeof(double), &a, &b); +} + int main (int argc, char ** argv) { int ireturn; @@ -114,8 +164,8 @@ static int test_mpi_allreduce(void) { dsend = 1.0; drecv = 0.0; ireturn = MPI_Allreduce(&dsend, &drecv, 1, MPI_DOUBLE, MPI_SUM, comm_); assert(ireturn == MPI_SUCCESS); - assert(dsend == 1.0); /* Exactly */ - assert(drecv == dsend); /* Exactly */ + assert(util_double_same(dsend, 1.0)); /* Exactly */ + assert(util_double_same(drecv, dsend)); /* Exactly */ isend[0] = -1; isend[1] = 0; @@ -149,8 +199,8 @@ static int test_mpi_reduce(void) { dsend = 1.0; drecv = 0.0; ireturn = MPI_Reduce(&dsend, &drecv, 1, MPI_DOUBLE, MPI_SUM, 0, comm_); assert(ireturn == MPI_SUCCESS); - assert(dsend == 1.0); /* Exactly */ - assert(drecv == dsend); /* Exactly */ + assert(util_double_same(dsend, 1.0)); /* Exactly */ + assert(util_double_same(drecv, dsend)); /* Exactly */ isend[0] = -1; isend[1] = 0; @@ -178,10 +228,10 @@ static int test_mpi_reduce(void) { ireturn = MPI_Reduce(dvsend, dvrecv, 2, MPI_DOUBLE, MPI_SUM, 0, comm_); assert(ireturn == MPI_SUCCESS); - assert(dvsend[0] == -1.0); /* All should be exact. */ - assert(dvsend[1] == +1.5); - assert(dvrecv[0] == dvsend[0]); - assert(dvrecv[1] == dvsend[1]); + assert(util_double_same(dvsend[0], -1.0)); /* All should be exact. */ + assert(util_double_same(dvsend[1], +1.5)); + assert(util_double_same(dvrecv[0], dvsend[0])); + assert(util_double_same(dvrecv[1], dvsend[1])); free(dvsend); free(dvrecv); @@ -301,8 +351,8 @@ int test_mpi_type_create_struct(void) { test_t recv = {.a = 0, .b = 0.0}; MPI_Reduce(&send, &recv, 1, dt, MPI_SUM, 0, MPI_COMM_WORLD); - assert(send.a == recv.a); - assert(fabs(send.b - recv.b) < DBL_EPSILON); + assert(send.a == recv.a); /* integer */ + assert(fabs(send.b - recv.b) < DBL_EPSILON); /* double */ } MPI_Type_free(&dt); From 5f7dc7586180eaa5e96c826029687f2e02ee4765 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 7 Oct 2022 15:12:56 +0100 Subject: [PATCH 019/244] Attempt to fix warnings --- tests/unit/test_io_aggr_mpio.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_io_aggr_mpio.c b/tests/unit/test_io_aggr_mpio.c index 4ba8816b1..585275247 100644 --- a/tests/unit/test_io_aggr_mpio.c +++ b/tests/unit/test_io_aggr_mpio.c @@ -16,6 +16,7 @@ *****************************************************************************/ #include +#include #include #include "pe.h" @@ -213,8 +214,8 @@ int test_io_aggr_buf_pack_asc(cs_t * cs, io_aggr_buf_t buf) { int ix = offset[X] + ic; int iy = offset[Y] + jc; int iz = offset[Z] + kc; - int nc = -1; char cline[BUFSIZ] = {0}; + size_t nc = 0; /* int returned but need to compare to size_t */ nc = sprintf(cline, "%4d %4d %4d %12lld\n", ix, iy, iz, ival); assert(nc == buf.szelement); memcpy(buf.buf + ib*buf.szelement, cline, buf.szelement); @@ -262,7 +263,8 @@ int test_io_aggr_buf_unpack_asc(cs_t * cs, io_aggr_buf_t buf) { int iyread = -1; int izread = -1; int64_t ivalread = -1; - int nc = sscanf(buf.buf + ib*buf.szelement, "%4d %4d %4d %12lld", + /* Note int64_t requires portable format */ + int nc = sscanf(buf.buf + ib*buf.szelement, "%4d %4d %4d %" SCNd64, &ixread, &iyread, &izread, &ivalread); assert(nc == 4); From 6fa8d6f651ae0fcc5164a280a7fe2f7d43d8943d Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 7 Oct 2022 15:20:10 +0100 Subject: [PATCH 020/244] Fix format --- tests/unit/test_io_aggr_mpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_io_aggr_mpio.c b/tests/unit/test_io_aggr_mpio.c index 585275247..89c882835 100644 --- a/tests/unit/test_io_aggr_mpio.c +++ b/tests/unit/test_io_aggr_mpio.c @@ -216,7 +216,7 @@ int test_io_aggr_buf_pack_asc(cs_t * cs, io_aggr_buf_t buf) { int iz = offset[Z] + kc; char cline[BUFSIZ] = {0}; size_t nc = 0; /* int returned but need to compare to size_t */ - nc = sprintf(cline, "%4d %4d %4d %12lld\n", ix, iy, iz, ival); + nc = sprintf(cline, "%4d %4d %4d %12" PRId64 "\n", ix, iy, iz, ival); assert(nc == buf.szelement); memcpy(buf.buf + ib*buf.szelement, cline, buf.szelement); } From b66f59370e1bb378afcb7a0eb30e9e1c4eaa2647 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 7 Oct 2022 15:41:11 +0100 Subject: [PATCH 021/244] Remove commented-out code --- mpi_s/mpi_serial.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/mpi_s/mpi_serial.c b/mpi_s/mpi_serial.c index 0617f8003..1cce46b21 100644 --- a/mpi_s/mpi_serial.c +++ b/mpi_s/mpi_serial.c @@ -1317,32 +1317,17 @@ int MPI_File_open(MPI_Comm comm, const char * filename, int amode, case (1): /* Read only */ fdmode = "r"; - /* - flags = O_RDONLY; - */ if (have_create) printf("No create with RDONLY\n"); if (have_excl) printf("No excl with RDONLY\n"); break; case (2): /* Write only */ fdmode = "w"; - /* - flags = O_WRONLY; - if (have_create) flags = flags | O_CREAT; - if (have_excl) flags = flags | O_EXCL; - if (have_append) flags = flags | O_APPEND; - */ if (have_append) fdmode = "a"; break; case (4): /* Read write */ fdmode = "r+"; - /* - flags = O_RDWR; - if (have_create) flags = flags | O_CREAT; - if (have_excl) flags = flags | O_EXCL; - if (have_append) flags = flags | O_APPEND; - */ if (have_append) fdmode = "a+"; break; default: @@ -1401,7 +1386,8 @@ int MPI_File_close(MPI_File * fh) { int MPI_File_delete(const char * filename, MPI_Info info) { assert(filename); - assert(0); + + remove(filename); return MPI_SUCCESS; } From 48ae7b0c45615892421ab31b4501ca51fa936803 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 7 Oct 2022 15:41:34 +0100 Subject: [PATCH 022/244] Fix conversion --- tests/unit/test_field.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_field.c b/tests/unit/test_field.c index 8ede05118..2f6dcd3e8 100644 --- a/tests/unit/test_field.c +++ b/tests/unit/test_field.c @@ -643,7 +643,7 @@ int test_field_io_aggr_pack(pe_t * pe) { /* Are the values in the buffer correct? */ /* Clear existing values and unpack. */ - memset(field->data, 0, field->nsites*field->nf*sizeof(double)); + memset(field->data, 0, sizeof(double)*field->nsites*field->nf); field_io_aggr_unpack(field, buf); util_field_data_check(field); From b2753299c7e9aa07d1aa1eb1bcc36b92e5a2e71f Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 7 Oct 2022 16:10:37 +0100 Subject: [PATCH 023/244] Fix scanf and fopen alerts --- util/extract.c | 26 ++++++++++++++------------ util/polarizer.c | 23 +++++++++++++++-------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/util/extract.c b/util/extract.c index 080cd6473..5f1fe2f62 100644 --- a/util/extract.c +++ b/util/extract.c @@ -58,7 +58,8 @@ #include #include -#include "../src/util.h" +#include "util.h" +#include "util_fopen.h" #define MAXNTOTAL 1024 /* Maximum linear system size */ @@ -294,7 +295,7 @@ int extract_driver(const char * filename, metadata_v1_t * meta, int version) { } else { sprintf(io_data, "q-%8.8d", ntime); - fp_data = fopen(io_data, "w+b"); + fp_data = util_fopen(io_data, "w+b"); if (fp_data == NULL) { printf("fopen(%s) failed\n", io_data); exit(-1); @@ -335,7 +336,7 @@ int extract_driver(const char * filename, metadata_v1_t * meta, int version) { if (output_vtk_ == 1 || output_vtk_ == 2) { strcat(io_data, suf); - fp_data = fopen(io_data, "w+b"); + fp_data = util_fopen(io_data, "w+b"); if (fp_data == NULL) printf("fopen(%s) failed\n", io_data); printf("\nWriting result to %s\n", io_data); @@ -355,7 +356,7 @@ int extract_driver(const char * filename, metadata_v1_t * meta, int version) { } else { - fp_data = fopen(io_data, "w+b"); + fp_data = util_fopen(io_data, "w+b"); if (fp_data == NULL) printf("fopen(%s) failed\n", io_data); printf("\nWriting result to %s\n", io_data); @@ -408,7 +409,7 @@ int read_version2(int ntime, metadata_v1_t * meta, double * datasection) { meta->nio, n); printf("-> %s\n", io_data); - fp_data = fopen(io_data, "r+b"); + fp_data = util_fopen(io_data, "r+b"); if (fp_data == NULL) printf("fopen(%s) failed\n", io_data); /* Read data file based on offsets recorded in the metadata, @@ -469,7 +470,7 @@ int read_version1(int ntime, metadata_v1_t * meta, double * datasection) { meta->stub, meta->nio, n); printf("Reading metadata file ... %s ", io_metadata); - fp_metadata = fopen(io_metadata, "r"); + fp_metadata = util_fopen(io_metadata, "r"); if (fp_metadata == NULL) printf("fopen(%s) failed\n", io_metadata); for (p = 0; p < 12; p++) { @@ -484,7 +485,7 @@ int read_version1(int ntime, metadata_v1_t * meta, double * datasection) { ntime, meta->nio, n); printf("-> %s\n", io_data); - fp_data = fopen(io_data, "r+b"); + fp_data = util_fopen(io_data, "r+b"); if (fp_data == NULL) printf("fopen(%s) failed\n", io_data); /* Read data file based on offsets recorded in the metadata, @@ -535,7 +536,7 @@ void read_meta_data_file(const char * filename, metadata_v1_t * meta) { assert(filename); assert(meta); - fp_meta = fopen(filename, "r"); + fp_meta = util_fopen(filename, "r"); if (fp_meta == NULL) { printf("fopen(%s) failed\n", filename); exit(-1); @@ -656,7 +657,8 @@ int read_data_file_name(const char * filename) { tmp = strchr(filename, '-'); if (tmp) { - sscanf(tmp+1, "%d.", &ntime); + int ns = sscanf(tmp+1, "%d.", &ntime); + if (ns != 1) printf("Could not determine time from %s\n", filename); } assert (ntime >= 0); @@ -1079,7 +1081,7 @@ int write_qab_vtk(int ntime, int ntarget[3], double * datasection) { /* Scalar order */ if (output_lcs_) { sprintf(io_data, "lcs-%8.8d.vtk", ntime); - fp_data = fopen(io_data, "w"); + fp_data = util_fopen(io_data, "w"); if (fp_data == NULL) { printf("fopen(%s) failed\n", io_data); @@ -1099,7 +1101,7 @@ int write_qab_vtk(int ntime, int ntarget[3], double * datasection) { if (output_lcd_) { sprintf(io_data, "lcd-%8.8d.vtk", ntime); - fp_data = fopen(io_data, "w"); + fp_data = util_fopen(io_data, "w"); if (fp_data == NULL) { printf("fopen(%s) failed\n", io_data); @@ -1119,7 +1121,7 @@ int write_qab_vtk(int ntime, int ntarget[3], double * datasection) { if (output_lcx_) { sprintf(io_data, "lcb-%8.8d.vtk", ntime); - fp_data = fopen(io_data, "w"); + fp_data = util_fopen(io_data, "w"); if (fp_data == NULL) { printf("fopen(%s) failed\n", io_data); diff --git a/util/polarizer.c b/util/polarizer.c index fcc890025..46a9c3bbd 100644 --- a/util/polarizer.c +++ b/util/polarizer.c @@ -43,6 +43,8 @@ #include #include +#include "util_fopen.h" + #define Pi 3.141592653589793 #define NLAMBDA 3 @@ -146,7 +148,7 @@ int main(int argc, char* argv[]){ /* take system dimensions from vtk-header */ sprintf(filename, "lcd-%8.8d.vtk", sys.its); - dirinput = fopen(filename, "r"); + dirinput = util_fopen(filename, "r"); if (!dirinput) { printf("Cannot open director input file %s\n", argv[1]); @@ -349,7 +351,7 @@ void read_data(int argc, char** argv, const options_t * opts, char * pl = NULL; sprintf(filename, "lcd-%8.8d.vtk", sys->its); - dirinput = fopen(filename, "r"); + dirinput = util_fopen(filename, "r"); if (!dirinput) { printf("Cannot open director input file %s\n", argv[1]); @@ -374,10 +376,15 @@ void read_data(int argc, char** argv, const options_t * opts, double diry = 0.0; double dirz = 0.0; - sscanf(line, "%le %le %le", &dirx, &diry, &dirz); - sys->dir[i][j][k][0] = dirx; - sys->dir[i][j][k][1] = diry; - sys->dir[i][j][k][2] = dirz; + int ns = sscanf(line, "%le %le %le", &dirx, &diry, &dirz); + if (ns == 3) { + sys->dir[i][j][k][0] = dirx; + sys->dir[i][j][k][1] = diry; + sys->dir[i][j][k][2] = dirz; + } + else { + printf("Incorrect dirx diry dirz\n"); + } } } } @@ -411,7 +418,7 @@ void read_data(int argc, char** argv, const options_t * opts, int Lzsop = -1; sprintf(filename, "lcs-%8.8d.vtk", sys->its); - sopinput = fopen(filename, "r"); + sopinput = util_fopen(filename, "r"); if (!sopinput) { printf("Cannot open scalar order parameter input file %s\n", argv[2]); @@ -700,7 +707,7 @@ void output(int argc, char ** argv, const options_t * opts, system_t * sys) { if (opts->raydir == 1) sprintf(outfile, "polar-y-%8.8d.vtk", sys->its); if (opts->raydir == 2) sprintf(outfile, "polar-z-%8.8d.vtk", sys->its); - polaroutput = fopen(outfile, "w"); + polaroutput = util_fopen(outfile, "w"); assert(polaroutput); if (opts->raydir == 0) { From 5b8454b6a6783374bb2441ab14b349195c30a880 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 7 Oct 2022 16:11:32 +0100 Subject: [PATCH 024/244] Fix scanf alert --- tests/unit/test_io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_io.c b/tests/unit/test_io.c index 449d6fc93..0d6ae590f 100644 --- a/tests/unit/test_io.c +++ b/tests/unit/test_io.c @@ -256,8 +256,8 @@ int test_io_read3(FILE * fp, int index, void * self) { n = fscanf(fp, "%d\n", &indata); - test_assert(n == 1); - test_assert(indata == data->iref*index); + assert(n == 1); + if (n == 1) assert(indata == data->iref*index); return n; } From 2c123e63cbbefe414823af2543d15ad7828947a6 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 7 Oct 2022 16:45:30 +0100 Subject: [PATCH 025/244] Fix alert --- src/colloid_io.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/colloid_io.c b/src/colloid_io.c index 9681902c6..782fe19de 100644 --- a/src/colloid_io.c +++ b/src/colloid_io.c @@ -22,6 +22,7 @@ #include "pe.h" #include "coords.h" #include "colloid_io.h" +#include "util_fopen.h" struct colloid_io_s { int n_io; /* Number of parallel IO group */ @@ -320,7 +321,7 @@ int colloid_io_write(colloid_io_t * cio, const char * filename) { if (cio->rank == 0) { - fp_state = fopen(filename_io, "w"); + fp_state = util_fopen(filename_io, "w"); if (fp_state == NULL) { pe_fatal(cio->pe, "Failed to open %s\n", filename_io); } @@ -450,7 +451,7 @@ int colloid_io_read(colloid_io_t * cio, const char * filename) { /* Open the file and read the information */ - fp_state = fopen(filename_io, "r"); + fp_state = util_fopen(filename_io, "r"); if (fp_state == NULL) pe_fatal(cio->pe, "Failed to open %s\n", filename_io); cio->f_header_read(fp_state, &ngroup); From 394abfae4b4d34c286ddfb94ceaee303a2dfdc38 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 7 Oct 2022 16:57:47 +0100 Subject: [PATCH 026/244] Try fix again --- util/extract.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/util/extract.c b/util/extract.c index 5f1fe2f62..c0656be57 100644 --- a/util/extract.c +++ b/util/extract.c @@ -658,7 +658,10 @@ int read_data_file_name(const char * filename) { tmp = strchr(filename, '-'); if (tmp) { int ns = sscanf(tmp+1, "%d.", &ntime); - if (ns != 1) printf("Could not determine time from %s\n", filename); + if (ns < 1) { + printf("Could not determine time from %s\n", filename); + ntime = -1; + } } assert (ntime >= 0); From 8e74b36d92b6f1e5e0f46e4dc390e5f0994cf834 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 7 Oct 2022 17:15:19 +0100 Subject: [PATCH 027/244] Try again --- util/extract.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/util/extract.c b/util/extract.c index c0656be57..e4e885f3a 100644 --- a/util/extract.c +++ b/util/extract.c @@ -652,20 +652,24 @@ void read_meta_data_file(const char * filename, metadata_v1_t * meta) { int read_data_file_name(const char * filename) { - int ntime = -1; - const char * tmp; + const char * tmp = NULL; + + assert(filename); tmp = strchr(filename, '-'); if (tmp) { + int ntime = -1; int ns = sscanf(tmp+1, "%d.", &ntime); if (ns < 1) { printf("Could not determine time from %s\n", filename); - ntime = -1; + assert(0); + } + else { + return ntime; } } - assert (ntime >= 0); - return ntime; + return -1; } /**************************************************************************** From ffa3a2469963572410775b71b019c3bf5a0ec475 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 10 Oct 2022 17:43:25 +0100 Subject: [PATCH 028/244] Attempt to sanitise filename --- util/extract.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/extract.c b/util/extract.c index e4e885f3a..c698cf799 100644 --- a/util/extract.c +++ b/util/extract.c @@ -53,6 +53,7 @@ ****************************************************************************/ #include +#include #include #include #include @@ -469,6 +470,7 @@ int read_version1(int ntime, metadata_v1_t * meta, double * datasection) { snprintf(io_metadata, sizeof(io_metadata), "%s.%3.3d-%3.3d.meta", meta->stub, meta->nio, n); printf("Reading metadata file ... %s ", io_metadata); + assert(isalpha(io_metadata[0])); fp_metadata = util_fopen(io_metadata, "r"); if (fp_metadata == NULL) printf("fopen(%s) failed\n", io_metadata); From 628b4f6756b5300ac200845b962581c3f05811a9 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 12 Oct 2022 14:54:57 +0100 Subject: [PATCH 029/244] Undo ineffective change --- util/extract.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/util/extract.c b/util/extract.c index c698cf799..e4e885f3a 100644 --- a/util/extract.c +++ b/util/extract.c @@ -53,7 +53,6 @@ ****************************************************************************/ #include -#include #include #include #include @@ -470,7 +469,6 @@ int read_version1(int ntime, metadata_v1_t * meta, double * datasection) { snprintf(io_metadata, sizeof(io_metadata), "%s.%3.3d-%3.3d.meta", meta->stub, meta->nio, n); printf("Reading metadata file ... %s ", io_metadata); - assert(isalpha(io_metadata[0])); fp_metadata = util_fopen(io_metadata, "r"); if (fp_metadata == NULL) printf("fopen(%s) failed\n", io_metadata); From 8674579cb433483e12ff22df5d659235bbe0444b Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 12 Oct 2022 15:55:54 +0100 Subject: [PATCH 030/244] Add spearate fatal message routine --- src/runtime.c | 47 +++++++++++++++++++++++++++++++++++++---------- src/runtime.h | 3 +++ 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/runtime.c b/src/runtime.c index 3706fcbab..52059a092 100644 --- a/src/runtime.c +++ b/src/runtime.c @@ -875,24 +875,50 @@ int rt_key_required(rt_t * rt, const char * key, rt_enum_t level) { int rt_vinfo(rt_t * rt, rt_enum_t lv, const char * fmt, ...) { - int rank; - assert(rt); - rank = pe_mpi_rank(rt->pe); + if (lv >= RT_INFO) { - if (lv >= RT_INFO && rank == 0) { + int rank = pe_mpi_rank(rt->pe); - va_list args; + if (rank == 0) { + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + } + } - va_start(args, fmt); - vprintf(fmt, args); - va_end(args); + return 0; +} - if (lv == RT_FATAL) { +/***************************************************************************** + * + * rt_fatal + * + * A convenience: only really fatal if level == RT_FATAL. + * Allows non-fatal testing. + * + *****************************************************************************/ + +int rt_fatal(rt_t * rt, rt_enum_t level, const char * format, ...) { + + assert(rt); + + if (level >= RT_INFO) { + + int rank = pe_mpi_rank(rt->pe); + + if (rank == 0) { + va_list args; + va_start(args, format); + vprintf(format, args); + va_end(args); + } + + if (level >= RT_FATAL) { MPI_Comm comm = MPI_COMM_NULL; pe_mpi_comm(rt->pe, &comm); - printf("Please check input and try again.\n"); MPI_Abort(comm, 0); } } @@ -900,6 +926,7 @@ int rt_vinfo(rt_t * rt, rt_enum_t lv, const char * fmt, ...) { return 0; } + /***************************************************************************** * * rt_report_unused_keys diff --git a/src/runtime.h b/src/runtime.h index 1bab495e2..0fe3b76ae 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -19,6 +19,7 @@ #include "pe.h" +typedef enum {RT_KEY_OK = 0, RT_KEY_MISSING, RT_KEY_INVALID} rt_err_t; typedef enum {RT_NONE, RT_INFO, RT_FATAL} rt_enum_t; typedef struct rt_s rt_t; @@ -43,5 +44,7 @@ int rt_key_present(rt_t * rt, const char * key); int rt_key_required(rt_t * rt, const char * key, rt_enum_t level); int rt_report_unused_keys(rt_t * rt, rt_enum_t level); int rt_vinfo(rt_t * rt, rt_enum_t lv, const char * fmt, ...); +int rt_fatal(rt_t * rt, rt_enum_t lv, const char * fmt, ...); + #endif From 7a9419356286cf08c728814d05578932bb16d0a8 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 12 Oct 2022 15:56:58 +0100 Subject: [PATCH 031/244] Refactor to replace pe by msg level --- src/io_options_rt.c | 144 +++++++++++++++++++++++++++----------------- src/io_options_rt.h | 11 ++-- 2 files changed, 95 insertions(+), 60 deletions(-) diff --git a/src/io_options_rt.c b/src/io_options_rt.c index 93abcb0af..2275bb39c 100644 --- a/src/io_options_rt.c +++ b/src/io_options_rt.c @@ -8,7 +8,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2020 The University of Edinburgh + * (c) 2020-2022 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -35,44 +35,37 @@ * *****************************************************************************/ -__host__ int io_options_rt(pe_t * pe, rt_t * rt, const char * keystub, +__host__ int io_options_rt(rt_t * rt, rt_enum_t lv, const char * keystub, io_options_t * options) { char key[BUFSIZ] = {0}; - io_mode_enum_t mode = IO_MODE_INVALID; - io_record_format_enum_t iorformat = IO_RECORD_INVALID; - io_options_t opts = io_options_default(); - assert(pe); assert(rt); assert(keystub); assert(options); - sprintf(key, "%s_io_mode", keystub); - io_options_rt_mode(pe, rt, key, &mode); - if (io_options_mode_valid(mode)) opts.mode = mode; - - sprintf(key, "%s_io_format", keystub); - io_options_rt_record_format(pe, rt, key, &iorformat); - if (io_options_record_format_valid(iorformat)) opts.iorformat = iorformat; + { + int ierr = io_options_rt_mode(rt, lv, key, &options->mode); - sprintf(key, "%s_io_report", keystub); - opts.report = rt_switch(rt, key); + /* Force metadata to be consistent with the mode */ - /* Force metadata to be consistent with the mode */ + if (ierr == RT_KEY_OK && options->mode == IO_MODE_SINGLE) { + /* only one choice at the moment */ + options->metadata_version = IO_METADATA_SINGLE_V1; + } - if (opts.mode == IO_MODE_SINGLE) { - /* only one choice at the moment */ - opts.metadata_version = IO_METADATA_SINGLE_V1; + if (ierr == RT_KEY_OK && options->mode == IO_MODE_MULTIPLE) { + /* only one choice again */ + options->metadata_version = IO_METADATA_MULTI_V1; + } } - if (opts.mode == IO_MODE_MULTIPLE) { - /* only one choice again */ - opts.metadata_version = IO_METADATA_MULTI_V1; - } + sprintf(key, "%s_io_format", keystub); + io_options_rt_record_format(rt, lv, key, &options->iorformat); - *options = opts; + sprintf(key, "%s_io_report", keystub); + io_options_rt_report(rt, lv, key, &options->report); return 0; } @@ -81,36 +74,46 @@ __host__ int io_options_rt(pe_t * pe, rt_t * rt, const char * keystub, * * io_options_rt_mode * + * If a valid key/value is present, record a mode and return RT_KEY_OK. + * *****************************************************************************/ -__host__ int io_options_rt_mode(pe_t * pe, rt_t * rt, const char * key, +__host__ int io_options_rt_mode(rt_t * rt, rt_enum_t lv, const char * key, io_mode_enum_t * mode) { + + int ifail = RT_KEY_MISSING; int key_present = 0; char value[BUFSIZ] = {0}; - io_mode_enum_t user_mode = IO_MODE_INVALID; - assert(pe); assert(rt); assert(key); assert(mode); key_present = rt_string_parameter(rt, key, value, BUFSIZ); - util_str_tolower(value, strlen(value)); - - if (strcmp(value, "single") == 0) user_mode = IO_MODE_SINGLE; - if (strcmp(value, "multiple") == 0) user_mode = IO_MODE_MULTIPLE; - if (key_present && io_options_mode_valid(user_mode) == 0) { - pe_info(pe, "I/O mode key present but value not recognised\n"); - pe_info(pe, "key: %s\n", key); - pe_info(pe, "value: %s\n", value); - pe_info(pe, "Should be either 'single' or 'multiple'\n"); - pe_fatal(pe, "Please check the input file and try again!\n"); + if (key_present) { + io_mode_enum_t user_mode = IO_MODE_INVALID; + + util_str_tolower(value, strlen(value)); + + if (strcmp(value, "single") == 0) user_mode = IO_MODE_SINGLE; + if (strcmp(value, "multiple") == 0) user_mode = IO_MODE_MULTIPLE; + + if (io_options_mode_valid(user_mode) == 1) { + *mode = user_mode; + ifail = RT_KEY_OK; + } + else { + rt_vinfo(rt, lv, "I/O mode key present but value not recognised\n"); + rt_vinfo(rt, lv, "key: %s\n", key); + rt_vinfo(rt, lv, "value: %s\n", value); + rt_vinfo(rt, lv, "Should be either 'single' or 'multiple'\n"); + rt_fatal(rt, lv, "Please check the input file and try again!\n"); + ifail = RT_KEY_INVALID; + } } - *mode = user_mode; - - return 0; + return ifail; } /***************************************************************************** @@ -121,33 +124,64 @@ __host__ int io_options_rt_mode(pe_t * pe, rt_t * rt, const char * key, * *****************************************************************************/ -__host__ int io_options_rt_record_format(pe_t * pe, rt_t * rt, +__host__ int io_options_rt_record_format(rt_t * rt, rt_enum_t lv, const char * key, io_record_format_enum_t * iorformat) { + int ifail = RT_KEY_MISSING; int key_present = 0; char value[BUFSIZ] = {0}; - io_record_format_enum_t user_iorformat = IO_RECORD_INVALID; - assert(pe); assert(rt); assert(key); assert(iorformat); key_present = rt_string_parameter(rt, key, value, BUFSIZ); - util_str_tolower(value, strlen(value)); - - if (strcmp(value, "ascii") == 0) user_iorformat = IO_RECORD_ASCII; - if (strcmp(value, "binary") == 0) user_iorformat = IO_RECORD_BINARY; - if (key_present && io_options_record_format_valid(user_iorformat) == 0) { - pe_info(pe, "I/O record format key present but value not recognised\n"); - pe_info(pe, "key: %s\n", key); - pe_info(pe, "value: %s\n", value); - pe_info(pe, "Should be either 'ascii' or 'binary'\n"); - pe_fatal(pe, "Please check the input file and try again!\n"); + if (key_present) { + io_record_format_enum_t user_iorformat = IO_RECORD_INVALID; + + util_str_tolower(value, strlen(value)); + + if (strcmp(value, "ascii") == 0) user_iorformat = IO_RECORD_ASCII; + if (strcmp(value, "binary") == 0) user_iorformat = IO_RECORD_BINARY; + + if (io_options_record_format_valid(user_iorformat) == 1) { + ifail = RT_KEY_OK; + *iorformat = user_iorformat; + } + else { + ifail = RT_KEY_INVALID; + rt_vinfo(rt, lv, "I/O record format present but value not recognised\n"); + rt_vinfo(rt, lv, "key: %s\n", key); + rt_vinfo(rt, lv, "value: %s\n", value); + rt_vinfo(rt, lv, "Should be either 'ascii' or 'binary'\n"); + rt_fatal(rt, lv, "Please check the input file and try again!\n"); + } } - *iorformat = user_iorformat; + return ifail; +} - return 0; +/***************************************************************************** + * + * io_options_rt_report + * + *****************************************************************************/ + +__host__ int io_options_rt_report(rt_t * rt, rt_enum_t lv, const char * key, + int * report) { + + int ifail = RT_KEY_MISSING; + int key_present = -1; + + /* A switch is never badly formatted: it's just false or true */ + + key_present = rt_key_present(rt, key); + + if (key_present) { + ifail = RT_KEY_OK; + *report = rt_switch(rt, key); + } + + return ifail; } diff --git a/src/io_options_rt.h b/src/io_options_rt.h index 1ff8784b8..7f7d1af66 100644 --- a/src/io_options_rt.h +++ b/src/io_options_rt.h @@ -8,7 +8,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2020 The University of Edinburgh + * (c) 2020-2022 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -18,15 +18,16 @@ #ifndef LUDWIG_IO_OPTIONS_RT_H #define LUDWIG_IO_OPTIONS_RT_H -#include "pe.h" #include "runtime.h" #include "io_options.h" -__host__ int io_options_rt(pe_t * pe, rt_t * rt, const char * keystub, +__host__ int io_options_rt(rt_t * rt, rt_enum_t lv, const char * keystub, io_options_t * opts); -__host__ int io_options_rt_mode(pe_t * pe, rt_t * rt, const char * key, +__host__ int io_options_rt_mode(rt_t * rt, rt_enum_t lv, const char * key, io_mode_enum_t * mode); -__host__ int io_options_rt_record_format(pe_t * pe, rt_t * rt, +__host__ int io_options_rt_record_format(rt_t * rt, rt_enum_t lv, const char * key, io_record_format_enum_t * options); +__host__ int io_options_rt_report(rt_t * rt, rt_enum_t lv, const char * key, + int * report); #endif From 3b96c66fee407691e49342428a36ac4726df1822 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 12 Oct 2022 15:57:14 +0100 Subject: [PATCH 032/244] Bring into use --- src/io_info_args_rt.c | 137 ++++++++++++++++++++++++++++-------------- src/io_info_args_rt.h | 11 ++-- 2 files changed, 100 insertions(+), 48 deletions(-) diff --git a/src/io_info_args_rt.c b/src/io_info_args_rt.c index 2b6cd7548..ea585a337 100644 --- a/src/io_info_args_rt.c +++ b/src/io_info_args_rt.c @@ -6,7 +6,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2020 The University of Edinburgh + * (c) 2020-2022 The University of Edinburgh * * Contribuiting authors: * Kevin Stratford @@ -16,7 +16,6 @@ #include #include -#include "pe.h" #include "runtime.h" #include "io_info_args_rt.h" @@ -29,78 +28,128 @@ * *****************************************************************************/ -__host__ int io_info_args_rt(pe_t * pe, rt_t * rt, const char * stub, +__host__ int io_info_args_rt(rt_t * rt, rt_enum_t lv, const char * stub, io_info_rw_enum_t rw, io_info_args_t * args) { - int iogrid[3] = {0}; - int sz = 3*sizeof(int); - char stubi[BUFSIZ] = {0}; - char stubo[BUFSIZ] = {0}; - - assert(pe); assert(rt); assert(stub); assert(args); - assert(strlen(stub) < BUFSIZ/2); - assert(0); /* io_options_rt() must not overwrite args */ - - sprintf(stubi, "%s_input", stub); - sprintf(stubo, "%s_output", stub); /* User defaults */ /* Specific options for this info type */ - io_options_rt(pe, rt, "default", &args->input); - io_options_rt(pe, rt, "default", &args->output); - io_options_rt(pe, rt, "default_input", &args->input); - io_options_rt(pe, rt, "default_output", &args->output); - io_options_rt(pe, rt, stubi, &args->input); - io_options_rt(pe, rt, stubo, &args->output); + if (rw == IO_INFO_READ_WRITE || rw == IO_INFO_READ_ONLY) { + io_info_args_rt_input(rt, lv, stub, args); + } + + if (rw == IO_INFO_READ_WRITE || rw == IO_INFO_WRITE_ONLY) { + io_info_args_rt_output(rt, lv, stub, args); + } /* Now the io grid (shared between input and output) */ - io_info_args_rt_iogrid(pe, rt, "default_io_grid", iogrid); - if (io_info_args_iogrid_valid(iogrid)) memcpy(args->grid, iogrid, sz); + { + char key[BUFSIZ] = {0}; - sprintf(stubi, "%s_io_grid", stub); - io_info_args_rt_iogrid(pe, rt, stubi, iogrid); - if (io_info_args_iogrid_valid(iogrid)) memcpy(args->grid, iogrid, sz); + sprintf(key, "%s_io_grid", stub); + io_info_args_rt_iogrid(rt, lv, "default_io_grid", args->grid); + io_info_args_rt_iogrid(rt, lv, key, args->grid); + } return 0; } /***************************************************************************** * - * io_info_args_rt_iogrid + * io_info_args_rt_input * - * Could return an error flag here if an invalid grid. + *****************************************************************************/ + +__host__ int io_info_args_rt_input(rt_t * rt, rt_enum_t lv, const char * stub, + io_info_args_t * args) { + + char stub_input[BUFSIZ] = {0}; + + assert(rt); + assert(stub); + assert(args); + + assert(strlen(stub) < BUFSIZ/2); + + sprintf(stub_input, "%s_input", stub); + + /* This is in order of increasing precedence ... */ + io_options_rt(rt, lv, "default", &args->input); + io_options_rt(rt, lv, "default_input", &args->input); + io_options_rt(rt, lv, stub, &args->input); + io_options_rt(rt, lv, stub_input, &args->input); + + return 0; +} + +/***************************************************************************** + * + * io_info_args_rt_output * *****************************************************************************/ -__host__ int io_info_args_rt_iogrid(pe_t * pe, rt_t * rt, const char * key, - int iogrid[3]) { +__host__ int io_info_args_rt_output(rt_t * rt, rt_enum_t lv, const char * stub, + io_info_args_t * args) { - int key_present = 0; - int user_grid[3] = {0}; + char stub_output[BUFSIZ] = {0}; - assert(pe); assert(rt); - assert(key); + assert(stub); + assert(args); - key_present = rt_int_parameter_vector(rt, key, user_grid); + assert(strlen(stub) < BUFSIZ/2); - if (key_present && io_info_args_iogrid_valid(user_grid) == 0) { - pe_info(pe, "I/O grid key present but invalid\n"); - pe_info(pe, "key: %s\n", key); - pe_info(pe, "value: %d %d %d\n", user_grid[0], user_grid[1], user_grid[2]); - pe_info(pe, "Must be greater than zero in each dimension\n"); - pe_fatal(pe, "Please check the input file and try again!\n"); - } + sprintf(stub_output, "%s_output", stub); - iogrid[0] = user_grid[0]; - iogrid[1] = user_grid[1]; - iogrid[2] = user_grid[2]; + /* In order of increasing precedence .. */ + io_options_rt(rt, lv, "default", &args->output); + io_options_rt(rt, lv, "default_output", &args->output); + io_options_rt(rt, lv, stub, &args->output); + io_options_rt(rt, lv, stub_output, &args->output); return 0; } + +/***************************************************************************** + * + * io_info_args_rt_iogrid + * + *****************************************************************************/ + +__host__ int io_info_args_rt_iogrid(rt_t * rt, rt_enum_t lv, const char * key, + int grid[3]) { + + int key_present = 0; + int iogrid[3] = {0}; /* invalid */ + int ifail = -1; /* Return -1 for no key; +1 for invalid key; 0 ok */ + + assert(rt); + assert(key); + + key_present = rt_int_parameter_vector(rt, key, iogrid); + + if (key_present) { + if (io_info_args_iogrid_valid(iogrid) == 1) { + grid[0] = iogrid[0]; + grid[1] = iogrid[1]; + grid[2] = iogrid[2]; + ifail = 0; + } + else { + rt_vinfo(rt, lv, "I/O grid key present but invalid\n"); + rt_vinfo(rt, lv, "key: %s\n", key); + rt_vinfo(rt, lv, "value: %d %d %d\n", iogrid[0], iogrid[1], iogrid[2]); + rt_vinfo(rt, lv, "Must be greater than zero in each dimension\n"); + rt_fatal(rt, lv, "Please check the input file and try again!\n"); + ifail = +1; + } + } + + return ifail; +} diff --git a/src/io_info_args_rt.h b/src/io_info_args_rt.h index 6ccf49bc7..3d76e1bbd 100644 --- a/src/io_info_args_rt.h +++ b/src/io_info_args_rt.h @@ -6,7 +6,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2020 The University of Edinburgh + * (c) 2020-2022 The University of Edinburgh * * Contribuiting authors: * Kevin Stratford @@ -16,13 +16,16 @@ #ifndef LUDWIG_IO_INFO_ARGS_RT_H #define LUDWIG_IO_INFO_ARGS_RT_H -#include "pe.h" #include "runtime.h" #include "io_info_args.h" -__host__ int io_info_args_rt(pe_t * pe, rt_t * rt, const char * name, +__host__ int io_info_args_rt(rt_t * rt, rt_enum_t lv, const char * name, io_info_rw_enum_t rw, io_info_args_t * args); -__host__ int io_info_args_rt_iogrid(pe_t * pe, rt_t * rt, const char * key, +__host__ int io_info_args_rt_input(rt_t * rt, rt_enum_t lv, const char * stub, + io_info_args_t * args); +__host__ int io_info_args_rt_output(rt_t * rt, rt_enum_t lv, const char * stub, + io_info_args_t * args); +__host__ int io_info_args_rt_iogrid(rt_t * rt, rt_enum_t lv, const char * key, int iogrid[3]); #endif From 1b5869bc01b5291f8ce998f297e5c8433d2250b1 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 12 Oct 2022 15:57:21 +0100 Subject: [PATCH 033/244] Refactor to replace pe by msg level --- src/distribution_rt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/distribution_rt.c b/src/distribution_rt.c index e6de2b750..71aedc819 100644 --- a/src/distribution_rt.c +++ b/src/distribution_rt.c @@ -113,11 +113,11 @@ int lb_run_time(pe_t * pe, cs_t * cs, rt_t * rt, lb_t ** lb) { io_info_args_t lb_info = io_info_args_default(); io_info_args_t rho_info = io_info_args_default(); - io_info_args_rt(pe, rt, "lb", IO_INFO_READ_WRITE, &lb_info); + io_info_args_rt(rt, RT_FATAL, "lb", IO_INFO_READ_WRITE, &lb_info); /* density is output only */ - io_info_args_rt(pe, rt, "rho", IO_INFO_WRITE_ONLY, &rho_info); + io_info_args_rt(rt, RT_FATAL, "rho", IO_INFO_WRITE_ONLY, &rho_info); } From 129c370612ba0d1b4619d794d5d0e6b4928e21be Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 12 Oct 2022 15:58:02 +0100 Subject: [PATCH 034/244] New test --- tests/unit/test_io_info_args.c | 93 ++++++++++++++++ tests/unit/test_io_info_args_rt.c | 171 ++++++++++++++++++++++++++++++ tests/unit/tests.c | 4 +- tests/unit/tests.h | 4 +- 4 files changed, 270 insertions(+), 2 deletions(-) create mode 100644 tests/unit/test_io_info_args.c create mode 100644 tests/unit/test_io_info_args_rt.c diff --git a/tests/unit/test_io_info_args.c b/tests/unit/test_io_info_args.c new file mode 100644 index 000000000..6678a11c4 --- /dev/null +++ b/tests/unit/test_io_info_args.c @@ -0,0 +1,93 @@ +/***************************************************************************** + * + * test_io_info_args.c + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include + +#include "pe.h" +#include "io_info_args.h" + +int test_io_info_args_default(void); +int test_io_info_args_iogrid_valid(void); + +/***************************************************************************** + * + * test_io_info_args_suite + * + *****************************************************************************/ + +int test_io_info_args_suite(void) { + + pe_t * pe = NULL; + + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + test_io_info_args_default(); + test_io_info_args_iogrid_valid(); + + pe_info(pe, "%-10s %s\n", "PASS **", __FILE__); + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_io_info_args_default + * + *****************************************************************************/ + +int test_io_info_args_default(void) { + + int ifail = 0; + io_info_args_t args = io_info_args_default(); + + /* If the size of the structs changes, the tests need updating */ + assert(sizeof(io_options_t) == 20); + assert(sizeof(io_info_args_t) == (2*sizeof(io_options_t) + 4*sizeof(int))); + + assert(args.input.mode == io_mode_default()); + assert(args.output.mode == io_mode_default()); + assert(args.grid[0] == 1); + assert(args.grid[1] == 1); + assert(args.grid[2] == 1); + assert(args.nfreq == 100000); + + return ifail; +} + +/***************************************************************************** + * + * test_io_info_args_iogrid_valid + * + *****************************************************************************/ + +int test_io_info_args_iogrid_valid(void) { + + int ifail = 0; + + /* Wrong. Zero not allowed. */ + { + int iogrid[3] = {0, 1, 1}; + assert(io_info_args_iogrid_valid(iogrid) == 0); + if (io_info_args_iogrid_valid(iogrid)) ifail = -1; + } + + /* Right */ + { + int iogrid[3] = {1, 1, 1}; + assert(io_info_args_iogrid_valid(iogrid) == 1); + if (io_info_args_iogrid_valid(iogrid) == 0) ifail = -1; + } + + return ifail; +} diff --git a/tests/unit/test_io_info_args_rt.c b/tests/unit/test_io_info_args_rt.c new file mode 100644 index 000000000..65f6d0bc1 --- /dev/null +++ b/tests/unit/test_io_info_args_rt.c @@ -0,0 +1,171 @@ +/***************************************************************************** + * + * test_io_info_args_rt.c + * + * Edinburgh Soft Matter and Statistical Physics Groups and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include + +#include "io_info_args_rt.h" + +int test_io_info_args_rt(pe_t * pe); +int test_io_info_args_rt_input(pe_t * pe); +int test_io_info_args_rt_output(pe_t * pe); +int test_io_info_args_rt_iogrid(pe_t * pe); + +/***************************************************************************** + * + * test_io_info_args_rt_suite + * + *****************************************************************************/ + +int test_io_info_args_rt_suite(void) { + + pe_t * pe = NULL; + + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + test_io_info_args_rt_iogrid(pe); + /* PENDING DEBUG test_io_info_args_rt_input(pe); */ + test_io_info_args_rt(pe); + + pe_info(pe, "%10s %s\n", "PASS **", __FILE__); + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_io_info_args_rt + * + *****************************************************************************/ + +int test_io_info_args_rt(pe_t * pe) { + + int ifail = 0; + io_info_args_t args = io_info_args_default(); + rt_t * rt = NULL; + + rt_create(pe, &rt); + + io_info_args_rt(rt, RT_FATAL, "stub", IO_INFO_READ_WRITE, &args); + + rt_free(rt); + + return ifail; +} + +/***************************************************************************** + * + * test_io_info_args_rt_input + * + *****************************************************************************/ + +int test_io_info_args_rt_input(pe_t * pe) { + + int ifail = 0; + rt_t * rt = NULL; + + rt_create(pe, &rt); + + /* No keys are present: default */ + { + io_info_args_t args = io_info_args_default(); + + io_info_args_rt_input(rt, RT_FATAL, "stub", &args); + assert(args.input.mode == io_mode_default()); + assert(args.input.iorformat == io_record_format_default()); + assert(args.input.metadata_version == io_metadata_version_default()); + assert(args.input.report == 0); + } + + rt_add_key_value(rt, "default_io_mode", "multiple"); + rt_add_key_value(rt, "default_io_format", "ascii"); + rt_add_key_value(rt, "default_io_report", "yes"); + + /* Default keys only are present: check */ + { + io_info_args_t args = io_info_args_default(); + + io_info_args_rt_input(rt, RT_FATAL, "nostub", &args); + assert(args.input.mode == IO_MODE_MULTIPLE); + assert(args.input.iorformat == IO_RECORD_ASCII); + assert(args.input.metadata_version == IO_METADATA_MULTI_V1); + assert(args.input.report == 1); + } + + /* Specific key stub: different from default keys */ + + rt_add_key_value(rt, "phi_input_io_mode", "single"); + rt_add_key_value(rt, "phi_input_io_format", "BINARY"); + rt_add_key_value(rt, "phi_input_io_report", "no"); + + { + io_info_args_t args = io_info_args_default(); + + io_info_args_rt_input(rt, RT_FATAL, "phi", &args); + assert(args.input.mode == IO_MODE_SINGLE); + assert(args.input.iorformat == IO_RECORD_BINARY); + assert(args.input.metadata_version == IO_METADATA_SINGLE_V1); + assert(args.input.report == 0); + } + + rt_free(rt); + + return ifail; +} + +/***************************************************************************** + * + * test_io_info_args_rt_iogrid + * + *****************************************************************************/ + +int test_io_info_args_rt_iogrid(pe_t * pe) { + + int ierr = 0; + rt_t * rt = NULL; + + rt_create(pe, &rt); + rt_add_key_value(rt, "iogrid_right", "2_3_4"); + rt_add_key_value(rt, "iogrid_wrong", "0_0_0"); + + /* No key at all. iogrid arg should be unchanged */ + { + int iogrid[3] = {2, 3, 4}; + int ifail = io_info_args_rt_iogrid(rt, RT_FATAL, "iogrid_none", iogrid); + assert(ifail == -1); + assert(iogrid[0] == 2); + assert(iogrid[1] == 3); + assert(iogrid[2] == 4); + } + + /* Right */ + { + int iogrid[3] = {0}; + int ifail = io_info_args_rt_iogrid(rt, RT_FATAL, "iogrid_right", iogrid); + assert(ifail == 0); + assert(iogrid[0] == 2); + assert(iogrid[1] == 3); + assert(iogrid[2] == 4); + } + + /* Wrong. Key present but invalid. RT_NONE for no fatal error. */ + { + int grid[3] = {0}; + int ifail = io_info_args_rt_iogrid(rt, RT_NONE, "iogrid_wrong", grid); + assert (ifail == +1); + } + + rt_free(rt); + + return ierr; +} diff --git a/tests/unit/tests.c b/tests/unit/tests.c index efffa4a1f..d187a1d51 100644 --- a/tests/unit/tests.c +++ b/tests/unit/tests.c @@ -75,9 +75,11 @@ __host__ int tests_create() { test_hydro_suite(); test_io_aggr_buf_suite(); test_io_aggr_mpio_suite(); - test_io_suite(); test_io_options_suite(); test_io_options_rt_suite(); + test_io_info_args_suite(); + test_io_info_args_rt_suite(); + test_io_suite(); test_lb_d2q9_suite(); test_lb_d3q15_suite(); test_lb_d3q19_suite(); diff --git a/tests/unit/tests.h b/tests/unit/tests.h index 606699d40..1ea655745 100644 --- a/tests/unit/tests.h +++ b/tests/unit/tests.h @@ -54,9 +54,11 @@ int test_halo_suite(void); int test_hydro_suite(void); int test_io_aggr_buf_suite(void); int test_io_aggr_mpio_suite(void); -int test_io_suite(void); +int test_io_info_args_suite(void); +int test_io_info_args_rt_suite(void); int test_io_options_suite(void); int test_io_options_rt_suite(void); +int test_io_suite(void); int test_lb_d2q9_suite(void); int test_lb_d3q15_suite(void); int test_lb_d3q19_suite(void); From a40a070cfd32039eb5171a75a9442f8369adbc51 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 12 Oct 2022 15:58:32 +0100 Subject: [PATCH 035/244] Various refactoring in progress --- tests/unit/test_io_options.c | 3 ++ tests/unit/test_io_options_rt.c | 65 +++++++++++++++++++++++++++++---- 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/tests/unit/test_io_options.c b/tests/unit/test_io_options.c index 567ebc1f7..3c7595bb2 100644 --- a/tests/unit/test_io_options.c +++ b/tests/unit/test_io_options.c @@ -154,6 +154,9 @@ __host__ int test_io_options_default(void) { io_options_t opts = io_options_default(); + /* If entries are changed in the struct, the tests should be updated... */ + assert(sizeof(io_options_t) == 20); + assert(io_options_mode_valid(opts.mode)); assert(io_options_record_format_valid(opts.iorformat)); assert(io_options_metadata_version_valid(&opts)); diff --git a/tests/unit/test_io_options_rt.c b/tests/unit/test_io_options_rt.c index f02b71a82..9ed64f983 100644 --- a/tests/unit/test_io_options_rt.c +++ b/tests/unit/test_io_options_rt.c @@ -21,6 +21,7 @@ __host__ int test_io_options_rt_mode(pe_t * pe); __host__ int test_io_options_rt_rformat(pe_t * pe); +__host__ int test_io_options_rt_report(pe_t * pe); __host__ int test_io_options_rt_default(pe_t * pe); __host__ int test_io_options_rt(pe_t * pe); @@ -38,8 +39,9 @@ __host__ int test_io_options_rt_suite(void) { test_io_options_rt_mode(pe); test_io_options_rt_rformat(pe); + /* PENDING DEBUG test_io_options_rt_report(pe); */ test_io_options_rt_default(pe); - test_io_options_rt(pe); + /* DITTO test_io_options_rt(pe);*/ pe_info(pe, "PASS ./unit/test_io_options_rt\n"); @@ -65,10 +67,10 @@ __host__ int test_io_options_rt_mode(pe_t * pe) { rt_add_key_value(rt, "example_input_io_mode", "SINGLE"); rt_add_key_value(rt, "example_output_io_mode", "MULTIPLE"); - io_options_rt_mode(pe, rt, "example_input_io_mode", &mode); + io_options_rt_mode(rt, RT_NONE, "example_input_io_mode", &mode); assert(mode == IO_MODE_SINGLE); - io_options_rt_mode(pe, rt, "example_output_io_mode", &mode); + io_options_rt_mode(rt, RT_NONE, "example_output_io_mode", &mode); assert(mode == IO_MODE_MULTIPLE); rt_free(rt); @@ -94,10 +96,10 @@ __host__ int test_io_options_rt_rformat(pe_t * pe) { rt_add_key_value(rt, "lb_input_io_format", "ASCII"); rt_add_key_value(rt, "lb_output_io_format", "BINARY"); - io_options_rt_record_format(pe, rt, "lb_input_io_format", &iorformat); + io_options_rt_record_format(rt, RT_NONE, "lb_input_io_format", &iorformat); assert(iorformat == IO_RECORD_ASCII); - io_options_rt_record_format(pe, rt, "lb_output_io_format", &iorformat); + io_options_rt_record_format(rt, RT_NONE, "lb_output_io_format", &iorformat); assert(iorformat == IO_RECORD_BINARY); rt_free(rt); @@ -105,6 +107,52 @@ __host__ int test_io_options_rt_rformat(pe_t * pe) { return 0; } +/***************************************************************************** + * + * test_io_options_rt_report + * + *****************************************************************************/ + +__host__ int test_io_options_rt_report(pe_t * pe) { + + int ifail = 0; + rt_t * rt = NULL; + + assert(pe); + + rt_create(pe, &rt); + rt_add_key_value(rt, "default_io_report", "no"); + rt_add_key_value(rt, "phi_io_report", "yes"); + + /* Not present */ + { + int irep = -1; + int iret = io_options_rt_report(rt, RT_FATAL, "not_present", &irep); + assert(iret == RT_KEY_MISSING); + assert(irep == -1); + } + + /* No */ + { + int irep = -1; + int iret = io_options_rt_report(rt, RT_FATAL, "default_io_report", &irep); + assert(iret == RT_KEY_OK); + assert(irep == 0); + } + + /* Yes */ + { + int irep = -1; + int iret = io_options_rt_report(rt, RT_FATAL, "default_io_report", &irep); + assert(iret == RT_KEY_OK); + assert(irep == 1); + } + + rt_free(rt); + + return ifail; +} + /***************************************************************************** * * test_io_options_rt_default @@ -123,7 +171,7 @@ __host__ int test_io_options_rt_default(pe_t * pe) { rt_create(pe, &rt); - io_options_rt(pe, rt, "default", &opts); + io_options_rt(rt, RT_FATAL, "default", &opts); assert(opts.mode == defs.mode); assert(opts.iorformat == defs.iorformat); @@ -156,15 +204,16 @@ __host__ int test_io_options_rt(pe_t * pe) { rt_add_key_value(rt, "default_io_mode", "multiple"); rt_add_key_value(rt, "default_io_format", "ascii"); - rt_add_key_value(rt, "default_io_report", "yes"); + rt_add_key_value(rt, "default_io_report", "yes"); - io_options_rt(pe, rt, "default", &opts); + io_options_rt(rt, RT_FATAL, "default", &opts); assert(opts.mode == IO_MODE_MULTIPLE); assert(opts.iorformat == IO_RECORD_ASCII); assert(opts.metadata_version == IO_METADATA_MULTI_V1); assert(opts.report == 1); assert(opts.asynchronous == 0); + rt_free(rt); return 0; From 59e9e43aba661b340f1914f03f0b0f271f5852f9 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 12 Oct 2022 15:59:17 +0100 Subject: [PATCH 036/244] Correct typo --- tests/unit/test_io_aggr_mpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_io_aggr_mpio.c b/tests/unit/test_io_aggr_mpio.c index 89c882835..06bd8a080 100644 --- a/tests/unit/test_io_aggr_mpio.c +++ b/tests/unit/test_io_aggr_mpio.c @@ -81,7 +81,7 @@ int test_io_aggr_mpio_suite(void) { if (pe_mpi_rank(pe) == 0) remove(filename); } - pe_info(pe, "PASS ./unit/test_io_aggr_mpio\n"); + pe_info(pe, "PASS ./unit/test_io_aggr_mpio\n"); cs_free(cs); pe_free(pe); From ba7497168743097825d0e40b38d741c4a49a1d9e Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 12 Oct 2022 16:57:47 +0100 Subject: [PATCH 037/244] Bug fix in rt_key_present() and test --- src/runtime.c | 2 +- tests/unit/test_runtime.c | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/runtime.c b/src/runtime.c index 52059a092..0fdec15f7 100644 --- a/src/runtime.c +++ b/src/runtime.c @@ -779,7 +779,7 @@ int rt_key_present(rt_t * rt, const char * key) { assert(rt); - for ( ; pair; pair = pair->next) { + for (pair = rt->keylist; pair; pair = pair->next) { if (strncmp(key, pair->key, NKEY_LENGTH) == 0) { present = 1; break; diff --git a/tests/unit/test_runtime.c b/tests/unit/test_runtime.c index f868e8324..d2835b472 100644 --- a/tests/unit/test_runtime.c +++ b/tests/unit/test_runtime.c @@ -25,6 +25,7 @@ int test_rt_general(pe_t * pe); int test_rt_nvector(pe_t * pe); +int test_rt_key_present(pe_t * pe); /***************************************************************************** * @@ -40,6 +41,7 @@ int test_rt_suite(void) { test_rt_general(pe); test_rt_nvector(pe); + test_rt_key_present(pe); pe_info(pe, "PASS ./unit/test_runtime\n"); pe_free(pe); @@ -276,3 +278,28 @@ int test_rt_nvector(pe_t * pe) { return key_ret; } + +/***************************************************************************** + * + * test_rt_key_present + * + *****************************************************************************/ + +int test_rt_key_present(pe_t * pe) { + + int ifail = 0; + rt_t * rt = NULL; + + rt_create(pe, &rt); + rt_add_key_value(rt, "present", "and_correct"); + + ifail = rt_key_present(rt, "present"); + assert(ifail == 1); + + ifail = rt_key_present(rt, "no_present"); + assert(ifail == 0); + + rt_free(rt); + + return ifail; +} From 0e50b69f6edece819657c58bdd1ce8d9351896e2 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 12 Oct 2022 17:03:10 +0100 Subject: [PATCH 038/244] Repair tests --- tests/unit/test_io_info_args_rt.c | 2 +- tests/unit/test_io_options_rt.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/test_io_info_args_rt.c b/tests/unit/test_io_info_args_rt.c index 65f6d0bc1..27e00d599 100644 --- a/tests/unit/test_io_info_args_rt.c +++ b/tests/unit/test_io_info_args_rt.c @@ -33,7 +33,7 @@ int test_io_info_args_rt_suite(void) { pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); test_io_info_args_rt_iogrid(pe); - /* PENDING DEBUG test_io_info_args_rt_input(pe); */ + test_io_info_args_rt_input(pe); test_io_info_args_rt(pe); pe_info(pe, "%10s %s\n", "PASS **", __FILE__); diff --git a/tests/unit/test_io_options_rt.c b/tests/unit/test_io_options_rt.c index 9ed64f983..bafa4b710 100644 --- a/tests/unit/test_io_options_rt.c +++ b/tests/unit/test_io_options_rt.c @@ -39,9 +39,9 @@ __host__ int test_io_options_rt_suite(void) { test_io_options_rt_mode(pe); test_io_options_rt_rformat(pe); - /* PENDING DEBUG test_io_options_rt_report(pe); */ + test_io_options_rt_report(pe); test_io_options_rt_default(pe); - /* DITTO test_io_options_rt(pe);*/ + test_io_options_rt(pe); pe_info(pe, "PASS ./unit/test_io_options_rt\n"); @@ -143,7 +143,7 @@ __host__ int test_io_options_rt_report(pe_t * pe) { /* Yes */ { int irep = -1; - int iret = io_options_rt_report(rt, RT_FATAL, "default_io_report", &irep); + int iret = io_options_rt_report(rt, RT_FATAL, "phi_io_report", &irep); assert(iret == RT_KEY_OK); assert(irep == 1); } From d50c8a1189751ddf73405e11d7705954cbc3bbce Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 12 Oct 2022 17:03:32 +0100 Subject: [PATCH 039/244] Update comments --- src/io_options_rt.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/io_options_rt.c b/src/io_options_rt.c index 2275bb39c..68c833339 100644 --- a/src/io_options_rt.c +++ b/src/io_options_rt.c @@ -166,6 +166,9 @@ __host__ int io_options_rt_record_format(rt_t * rt, rt_enum_t lv, * * io_options_rt_report * + * Update report accordingly if the switch "key" is present. + * Return RT_KEY_OK or RT_KEY_MISSING. + * *****************************************************************************/ __host__ int io_options_rt_report(rt_t * rt, rt_enum_t lv, const char * key, @@ -174,7 +177,7 @@ __host__ int io_options_rt_report(rt_t * rt, rt_enum_t lv, const char * key, int ifail = RT_KEY_MISSING; int key_present = -1; - /* A switch is never badly formatted: it's just false or true */ + /* A switch is just false or true, so we have to look more carefully */ key_present = rt_key_present(rt, key); From e42b16fbd97a0cf03dd6c52abcdad52428371084 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 14 Oct 2022 15:42:36 +0100 Subject: [PATCH 040/244] Remove compiler warnings at -DNDEBUG --- mpi_s/mpi_serial.c | 13 +++++++++---- mpi_s/mpi_tests.c | 28 ++++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/mpi_s/mpi_serial.c b/mpi_s/mpi_serial.c index 1cce46b21..b86363583 100644 --- a/mpi_s/mpi_serial.c +++ b/mpi_s/mpi_serial.c @@ -1292,7 +1292,6 @@ int MPI_Type_get_extent(MPI_Datatype datatype, MPI_Aint * lb, int MPI_File_open(MPI_Comm comm, const char * filename, int amode, MPI_Info info, MPI_File * fh) { - int flags = 0; FILE * fp = NULL; const char * fdmode = NULL; @@ -1336,7 +1335,7 @@ int MPI_File_open(MPI_Comm comm, const char * filename, int amode, } } - assert(flags == 0); /* Do something with flags */ + /* Open the file and record the handle */ fp = fopen(filename, fdmode); @@ -1524,12 +1523,15 @@ int MPI_File_read_all(MPI_File fh, void * buf, int count, size_t nitems = count; size_t nr = fread(buf, size, nitems, fp); + if (nr != nitems) { + printf("MPI_File_read_all(): incorrect number of items in fread()\n"); + } + if (ferror(fp)) { perror("perror: "); printf("MPI_File_read_all() file operation failed\n"); exit(0); } - assert(nr == nitems); } return MPI_SUCCESS; @@ -1562,12 +1564,15 @@ int MPI_File_write_all(MPI_File fh, const void * buf, int count, size_t nitems = count; size_t nw = fwrite(buf, size, nitems, fp); + if (nw != nitems) { + printf("MPI_File_write_all(): incorrect number of items in fwrite()\n"); + } + if (ferror(fp)) { perror("perror: "); printf("MPI_File_write_all() file operation failed\n"); exit(0); } - assert(nw == nitems); } return MPI_SUCCESS; diff --git a/mpi_s/mpi_tests.c b/mpi_s/mpi_tests.c index 055a004e0..c741b0ca0 100644 --- a/mpi_s/mpi_tests.c +++ b/mpi_s/mpi_tests.c @@ -36,6 +36,14 @@ static int test_mpi_file_write_all(void); /* Utilities */ +/***************************************************************************** + * + * util_printf_bits + * + * Convenience to print a set of bits as 0 or 1. + * + *****************************************************************************/ + void util_printf_bits(size_t size, void const * data) { unsigned char * bytes = (unsigned char *) data; @@ -52,7 +60,15 @@ void util_printf_bits(size_t size, void const * data) { return; } -static int util_bits_same(size_t size, const void * data1, const void * data2) { +/***************************************************************************** + * + * util_bits_same + * + * Return 1 if two bits arrays are the same. + * + *****************************************************************************/ + +int util_bits_same(size_t size, const void * data1, const void * data2) { int bsame = 1; unsigned char * b1 = (unsigned char *) data1; @@ -72,7 +88,15 @@ static int util_bits_same(size_t size, const void * data1, const void * data2) { return bsame; } -static int util_double_same(double d1, double d2) { +/***************************************************************************** + * + * util_double_same + * + * Return 1 if two doubles are bitwise the same. + * + *****************************************************************************/ + +int util_double_same(double d1, double d2) { double a = d1; double b = d2; From deb7d73c41d705bcb985f40e691e920e4d96a5d1 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 14 Oct 2022 15:47:51 +0100 Subject: [PATCH 041/244] Complete output tests --- tests/unit/test_io_info_args_rt.c | 98 ++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 3 deletions(-) diff --git a/tests/unit/test_io_info_args_rt.c b/tests/unit/test_io_info_args_rt.c index 27e00d599..7ac8d6891 100644 --- a/tests/unit/test_io_info_args_rt.c +++ b/tests/unit/test_io_info_args_rt.c @@ -2,6 +2,7 @@ * * test_io_info_args_rt.c * + * * Edinburgh Soft Matter and Statistical Physics Groups and * Edinburgh Parallel Computing Centre * @@ -33,10 +34,11 @@ int test_io_info_args_rt_suite(void) { pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); test_io_info_args_rt_iogrid(pe); + test_io_info_args_rt_output(pe); test_io_info_args_rt_input(pe); test_io_info_args_rt(pe); - pe_info(pe, "%10s %s\n", "PASS **", __FILE__); + pe_info(pe, "%-9s %s\n", "PASS", __FILE__); pe_free(pe); return 0; @@ -46,17 +48,45 @@ int test_io_info_args_rt_suite(void) { * * test_io_info_args_rt * + * We will assume the parts are individually ok, so just check the result + * of the rw argument. + * *****************************************************************************/ int test_io_info_args_rt(pe_t * pe) { int ifail = 0; - io_info_args_t args = io_info_args_default(); rt_t * rt = NULL; rt_create(pe, &rt); - io_info_args_rt(rt, RT_FATAL, "stub", IO_INFO_READ_WRITE, &args); + /* Test values are non-defaults. */ + rt_add_key_value(rt, "test_input_io_report", "yes"); + rt_add_key_value(rt, "test_output_io_report", "yes"); + + /* Read only */ + { + io_info_args_t args = io_info_args_default(); + io_info_args_rt(rt, RT_FATAL, "test", IO_INFO_READ_ONLY, &args); + assert(args.input.report == 1); + assert(args.output.report == 0); + } + + /* Write only */ + { + io_info_args_t args = io_info_args_default(); + io_info_args_rt(rt, RT_FATAL, "test", IO_INFO_WRITE_ONLY, &args); + assert(args.input.report == 0); + assert(args.output.report == 1); + } + + /* Read write */ + { + io_info_args_t args = io_info_args_default(); + io_info_args_rt(rt, RT_FATAL, "test", IO_INFO_READ_WRITE, &args); + assert(args.input.report == 1); + assert(args.output.report == 1); + } rt_free(rt); @@ -123,6 +153,65 @@ int test_io_info_args_rt_input(pe_t * pe) { return ifail; } +/***************************************************************************** + * + * test_io_info_args_rt_output + * + *****************************************************************************/ + +int test_io_info_args_rt_output(pe_t * pe) { + + int ifail = 0; + rt_t * rt = NULL; + + assert(pe); + + rt_create(pe, &rt); + + /* No keys are present: default */ + { + io_info_args_t args = io_info_args_default(); + + io_info_args_rt_output(rt, RT_FATAL, "q", &args); + assert(args.output.mode == io_mode_default()); + assert(args.output.iorformat == io_record_format_default()); + assert(args.output.metadata_version == io_metadata_version_default()); + assert(args.output.report == 0); + } + + /* Explicit default */ + rt_add_key_value(rt, "default_io_mode", "multiple"); + rt_add_key_value(rt, "default_io_format", "ascii"); + rt_add_key_value(rt, "default_io_report", "yes"); + + { + io_info_args_t args = io_info_args_default(); + + io_info_args_rt_output(rt, RT_FATAL, "q", &args); + assert(args.output.mode == IO_MODE_MULTIPLE); + assert(args.output.iorformat == IO_RECORD_ASCII); + assert(args.output.report == 1); + } + + /* output stub */ + rt_add_key_value(rt, "q_output_io_mode", "single"); + rt_add_key_value(rt, "q_output_io_format", "ascii"); + rt_add_key_value(rt, "q_output_io_report", "yes"); + + { + io_info_args_t args = {0}; + + io_info_args_rt_output(rt, RT_FATAL, "q", &args); + assert(args.output.mode == IO_MODE_SINGLE); + assert(args.output.iorformat == IO_RECORD_ASCII); + assert(args.output.report == 1); + } + + rt_free(rt); + + return ifail; +} + /***************************************************************************** * * test_io_info_args_rt_iogrid @@ -146,6 +235,7 @@ int test_io_info_args_rt_iogrid(pe_t * pe) { assert(iogrid[0] == 2); assert(iogrid[1] == 3); assert(iogrid[2] == 4); + ierr = 1 + ifail; } /* Right */ @@ -156,6 +246,7 @@ int test_io_info_args_rt_iogrid(pe_t * pe) { assert(iogrid[0] == 2); assert(iogrid[1] == 3); assert(iogrid[2] == 4); + ierr = ifail; } /* Wrong. Key present but invalid. RT_NONE for no fatal error. */ @@ -163,6 +254,7 @@ int test_io_info_args_rt_iogrid(pe_t * pe) { int grid[3] = {0}; int ifail = io_info_args_rt_iogrid(rt, RT_NONE, "iogrid_wrong", grid); assert (ifail == +1); + ierr = (1 - ifail); } rt_free(rt); From 187f65d642a3b244300a9c328ed6f05771cb3567 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 14 Oct 2022 15:48:45 +0100 Subject: [PATCH 042/244] Remove compiler warnings at -DNDEBUG --- tests/unit/test_field.c | 4 +++- tests/unit/test_io.c | 2 +- tests/unit/test_io_aggr_mpio.c | 31 ++++++++++++++++++++++--------- tests/unit/test_io_info_args.c | 6 ++++-- tests/unit/test_io_options_rt.c | 8 ++++++++ tests/unit/test_lb_model.c | 5 ++++- tests/unit/test_lc_anchoring.c | 26 ++++++++++++++++++++------ tests/unit/test_psi_sor.c | 6 ++++-- 8 files changed, 66 insertions(+), 22 deletions(-) diff --git a/tests/unit/test_field.c b/tests/unit/test_field.c index 2f6dcd3e8..4712dc46d 100644 --- a/tests/unit/test_field.c +++ b/tests/unit/test_field.c @@ -731,6 +731,7 @@ int util_field_data_check_set(field_t * field) { int util_field_data_check(field_t * field) { + int ifail = 0; int nlocal[3] = {0}; assert(field); @@ -745,10 +746,11 @@ int util_field_data_check(field_t * field) { int faddr = addr_rank1(field->nsites, field->nf, index, n); double fval = 1.0*field_unique_value(field, ic, jc, kc, n); assert(fabs(field->data[faddr] - fval) < DBL_EPSILON); + if (fabs(field->data[faddr] - fval) > DBL_EPSILON) ifail += 1; } } } } - return 0; + return ifail; } diff --git a/tests/unit/test_io.c b/tests/unit/test_io.c index 0d6ae590f..3e666d7c4 100644 --- a/tests/unit/test_io.c +++ b/tests/unit/test_io.c @@ -257,7 +257,7 @@ int test_io_read3(FILE * fp, int index, void * self) { n = fscanf(fp, "%d\n", &indata); assert(n == 1); - if (n == 1) assert(indata == data->iref*index); + if (n == 1) test_assert(indata == data->iref*index); return n; } diff --git a/tests/unit/test_io_aggr_mpio.c b/tests/unit/test_io_aggr_mpio.c index 06bd8a080..fd27b88d1 100644 --- a/tests/unit/test_io_aggr_mpio.c +++ b/tests/unit/test_io_aggr_mpio.c @@ -193,14 +193,16 @@ int64_t test_unique_value(cs_t * cs, int ic, int jc, int kc) { int test_io_aggr_buf_pack_asc(cs_t * cs, io_aggr_buf_t buf) { - assert(cs); - assert(buf.buf); + int ifail = 0; int ib = 0; int ntotal[3] = {0}; int nlocal[3] = {0}; int offset[3] = {0}; + assert(cs); + assert(buf.buf); + cs_ntotal(cs, ntotal); cs_nlocal(cs, nlocal); cs_nlocal_offset(cs, offset); @@ -218,6 +220,7 @@ int test_io_aggr_buf_pack_asc(cs_t * cs, io_aggr_buf_t buf) { size_t nc = 0; /* int returned but need to compare to size_t */ nc = sprintf(cline, "%4d %4d %4d %12" PRId64 "\n", ix, iy, iz, ival); assert(nc == buf.szelement); + if (nc != buf.szelement) ifail += 1; memcpy(buf.buf + ib*buf.szelement, cline, buf.szelement); } ib += 1; @@ -227,7 +230,7 @@ int test_io_aggr_buf_pack_asc(cs_t * cs, io_aggr_buf_t buf) { assert(ib == nlocal[X]*nlocal[Y]*nlocal[Z]); - return 0; + return ifail; } /***************************************************************************** @@ -238,14 +241,16 @@ int test_io_aggr_buf_pack_asc(cs_t * cs, io_aggr_buf_t buf) { int test_io_aggr_buf_unpack_asc(cs_t * cs, io_aggr_buf_t buf) { - assert(cs); - assert(buf.buf); + int ifail = 0; int ib = 0; int ntotal[3] = {0}; int nlocal[3] = {0}; int offset[3] = {0}; + assert(cs); + assert(buf.buf); + cs_ntotal(cs, ntotal); cs_nlocal(cs, nlocal); cs_nlocal_offset(cs, offset); @@ -272,6 +277,11 @@ int test_io_aggr_buf_unpack_asc(cs_t * cs, io_aggr_buf_t buf) { assert(iyread == iy); assert(izread == iz); assert(ivalread == ival); + if (nc != 4) ifail += 1; + if (iz != izread) ifail += 1; + if (iy != iyread) ifail += 1; + if (ix != ixread) ifail =+ 1; + if (ivalread != ival) ifail += 1; } ib += 1; } @@ -280,7 +290,7 @@ int test_io_aggr_buf_unpack_asc(cs_t * cs, io_aggr_buf_t buf) { assert(ib == nlocal[X]*nlocal[Y]*nlocal[Z]); - return 0; + return ifail; } @@ -323,12 +333,14 @@ int test_io_aggr_buf_pack_bin(cs_t * cs, io_aggr_buf_t buf) { int test_io_aggr_buf_unpack_bin(cs_t * cs, io_aggr_buf_t buf) { - assert(cs); - assert(buf.buf); + int ifail = 0; int ib = 0; int nlocal[3] = {0}; + assert(cs); + assert(buf.buf); + cs_nlocal(cs, nlocal); for (int ic = 1; ic <= nlocal[X]; ic++) { @@ -338,6 +350,7 @@ int test_io_aggr_buf_unpack_bin(cs_t * cs, io_aggr_buf_t buf) { int64_t iread = -1; memcpy(&iread, buf.buf + ib*sizeof(int64_t), sizeof(int64_t)); assert(ival == iread); + if (ival != iread) ifail += 1; ib += 1; } } @@ -345,5 +358,5 @@ int test_io_aggr_buf_unpack_bin(cs_t * cs, io_aggr_buf_t buf) { assert(ib == nlocal[X]*nlocal[Y]*nlocal[Z]); - return 0; + return ifail; } diff --git a/tests/unit/test_io_info_args.c b/tests/unit/test_io_info_args.c index 6678a11c4..c39e982bf 100644 --- a/tests/unit/test_io_info_args.c +++ b/tests/unit/test_io_info_args.c @@ -34,7 +34,7 @@ int test_io_info_args_suite(void) { test_io_info_args_default(); test_io_info_args_iogrid_valid(); - pe_info(pe, "%-10s %s\n", "PASS **", __FILE__); + pe_info(pe, "%-9s %s\n", "PASS", __FILE__); pe_free(pe); return 0; @@ -51,7 +51,7 @@ int test_io_info_args_default(void) { int ifail = 0; io_info_args_t args = io_info_args_default(); - /* If the size of the structs changes, the tests need updating */ + /* If the size of the struct changes, the tests need updating */ assert(sizeof(io_options_t) == 20); assert(sizeof(io_info_args_t) == (2*sizeof(io_options_t) + 4*sizeof(int))); @@ -62,6 +62,8 @@ int test_io_info_args_default(void) { assert(args.grid[2] == 1); assert(args.nfreq == 100000); + if (args.input.mode != 0) ifail += 1; + return ifail; } diff --git a/tests/unit/test_io_options_rt.c b/tests/unit/test_io_options_rt.c index bafa4b710..a2202989b 100644 --- a/tests/unit/test_io_options_rt.c +++ b/tests/unit/test_io_options_rt.c @@ -111,6 +111,8 @@ __host__ int test_io_options_rt_rformat(pe_t * pe) { * * test_io_options_rt_report * + * Test the report switch. + * *****************************************************************************/ __host__ int test_io_options_rt_report(pe_t * pe) { @@ -130,6 +132,8 @@ __host__ int test_io_options_rt_report(pe_t * pe) { int iret = io_options_rt_report(rt, RT_FATAL, "not_present", &irep); assert(iret == RT_KEY_MISSING); assert(irep == -1); + if (iret != RT_KEY_MISSING) ifail += 1; + if (irep != -1) ifail += 1; } /* No */ @@ -138,6 +142,8 @@ __host__ int test_io_options_rt_report(pe_t * pe) { int iret = io_options_rt_report(rt, RT_FATAL, "default_io_report", &irep); assert(iret == RT_KEY_OK); assert(irep == 0); + if (iret != RT_KEY_OK) ifail += 1; + if (irep != 0) ifail += 1; } /* Yes */ @@ -146,6 +152,8 @@ __host__ int test_io_options_rt_report(pe_t * pe) { int iret = io_options_rt_report(rt, RT_FATAL, "phi_io_report", &irep); assert(iret == RT_KEY_OK); assert(irep == 1); + if (iret != RT_KEY_OK) ifail += 1; + if (irep != 1) ifail += 1; } rt_free(rt); diff --git a/tests/unit/test_lb_model.c b/tests/unit/test_lb_model.c index a3cd88dd4..25a445bb8 100644 --- a/tests/unit/test_lb_model.c +++ b/tests/unit/test_lb_model.c @@ -330,6 +330,8 @@ int test_lb_model_ma(const lb_model_t * model) { int test_lb_model_hydrodynamic_modes(const lb_model_t * model) { + int ifail = 0; + assert(model); /* The hydrodynamic modes are always the same independent of model @@ -360,11 +362,12 @@ int test_lb_model_hydrodynamic_modes(const lb_model_t * model) { double dij = (i == j); double sij = model->cv[p][i]*model->cv[p][j] - cs2*dij; assert(fabs(model->ma[k][p] - sij) < DBL_EPSILON); + if (fabs(model->ma[k][p] - sij) > DBL_EPSILON) ifail += 1; k += 1; } } } } - return 0; + return ifail; } diff --git a/tests/unit/test_lc_anchoring.c b/tests/unit/test_lc_anchoring.c index 3ff0e0fc1..59957ca13 100644 --- a/tests/unit/test_lc_anchoring.c +++ b/tests/unit/test_lc_anchoring.c @@ -111,7 +111,7 @@ int test_lc_anchoring_type_from_enum(void) { name = lc_anchoring_type_from_enum(LC_ANCHORING_FIXED); assert(strcmp(name, "fixed") == 0); - return 0; + return strcmp(name, "fixed"); } /***************************************************************************** @@ -218,6 +218,8 @@ int test_lc_anchoring_fixed_q0() { int test_lc_anchoring_fixed_ct(void) { + int ifail = 0; + double a0 = 2.0; double kappa1 = 0.01; double q0 = 0.0; @@ -238,11 +240,12 @@ int test_lc_anchoring_fixed_ct(void) { for (int ib = 0; ib < 3; ib++) { double ct0 = -anchor.w1*(qs[ia][ib] - qfix[ia][ib]); assert(fabs(ct0 - ct[ia][ib]) < DBL_EPSILON); + if (fabs(ct0 - ct[ia][ib]) > DBL_EPSILON) ifail += 1; } } } - return 0; + return ifail; } /***************************************************************************** @@ -285,6 +288,8 @@ int test_lc_anchoring_normal_q0(void) { int test_lc_anchoring_normal_ct(void) { + int ifail = 0; + double a0 = 2.0; double kappa1 = 0.01; double q0 = 0.0; @@ -305,11 +310,12 @@ int test_lc_anchoring_normal_ct(void) { for (int ib = 0; ib < 3; ib++) { double ct0 = -anchor.w1*(qs[ia][ib] - qnormal[ia][ib]); assert(fabs(ct0 - ct[ia][ib]) < DBL_EPSILON); + if (fabs(ct0 - ct[ia][ib]) > DBL_EPSILON) ifail += 1; } } } - return 0; + return ifail; } /***************************************************************************** @@ -320,6 +326,8 @@ int test_lc_anchoring_normal_ct(void) { int test_lc_anchoring_planar_qtilde(void) { + int ifail = 0; + double a0 = 3.0; double qs[3][3] = {{1.0,2.0,3.0}, {4.0,5.0,6.0}, {7.0,8.0,9.0}}; double qtilde[3][3] = {0}; @@ -329,11 +337,13 @@ int test_lc_anchoring_planar_qtilde(void) { for (int ia = 0; ia < 3; ia++) { for (int ib = 0; ib < 3; ib++) { double dab = 1.0*(ia == ib); - assert(fabs(qtilde[ia][ib] - (qs[ia][ib] + 0.5*a0*dab)) < DBL_EPSILON); + double diff = fabs(qtilde[ia][ib] - (qs[ia][ib] + 0.5*a0*dab)); + assert(fabs(diff) < DBL_EPSILON); + if (fabs(diff) > DBL_EPSILON) ifail += 1; } } - return 0; + return ifail; } /***************************************************************************** @@ -346,6 +356,8 @@ int test_lc_anchoring_planar_qtilde(void) { int test_lc_anchoring_planar_ct(void) { + int ifail = 0; + double a0 = 2.0; double kappa1 = 0.0; double q0 = 0.0; @@ -371,6 +383,7 @@ int test_lc_anchoring_planar_ct(void) { for (int ib = 0; ib < 3; ib++) { double fe = -anchor.w1*(qtilde[ia][ib] - qtperp[ia][ib]); assert(fabs(fe - ct[ia][ib]) < DBL_EPSILON); + if (fabs(fe - ct[ia][ib]) > DBL_EPSILON) ifail += 1; } } } @@ -399,11 +412,12 @@ int test_lc_anchoring_planar_ct(void) { for (int ib = 0; ib < 3; ib++) { double fe = -2.0*anchor.w2*(qt2 - 2.25*a0*a0)*qtilde[ia][ib]; assert(fabs(fe - ct[ia][ib]) < DBL_EPSILON); + if (fabs(fe - ct[ia][ib]) > DBL_EPSILON) ifail += 1; } } } - return 0; + return ifail; } /***************************************************************************** diff --git a/tests/unit/test_psi_sor.c b/tests/unit/test_psi_sor.c index 55b4edc00..39bc9a02d 100644 --- a/tests/unit/test_psi_sor.c +++ b/tests/unit/test_psi_sor.c @@ -328,7 +328,7 @@ static int test_charge1_exact(psi_t * obj, f_vare_t fepsilon) { int k, kp1, km1, index; int nlocal[3]; int nz; - int ifail; + int ifail = 0; double * epsilon = NULL; /* 1-d e = e(z) from fepsilon */ double eph; /* epsilon(k + 1/2) */ @@ -411,6 +411,7 @@ static int test_charge1_exact(psi_t * obj, f_vare_t fepsilon) { if (k == 0) psi0 = psi; assert(fabs(b[k] - (psi - psi0)) < tolerance); + if (fabs(b[k] - (psi - psi0)) > tolerance) ifail += 1; /* Extra check on the differencing terms */ @@ -433,6 +434,7 @@ static int test_charge1_exact(psi_t * obj, f_vare_t fepsilon) { rhodiff = -(emh*psim1 - (emh + eph)*psi + eph*psip1); assert(fabs(rho0 - rhodiff) < tolerance); + if (fabs(rho0 - rhodiff) > tolerance) ifail += 1; rhotot += rho0; } } @@ -444,7 +446,7 @@ static int test_charge1_exact(psi_t * obj, f_vare_t fepsilon) { free(a); free(epsilon); - return 0; + return ifail; } /***************************************************************************** From 7e7bdeb38ccb10f726a093e1b4992900849ec825 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 14 Oct 2022 15:49:32 +0100 Subject: [PATCH 043/244] Remove compiler warnings at -DNDEBUG --- util/polarizer.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util/polarizer.c b/util/polarizer.c index 46a9c3bbd..a4d9cba3f 100644 --- a/util/polarizer.c +++ b/util/polarizer.c @@ -362,7 +362,7 @@ void read_data(int argc, char** argv, const options_t * opts, /* skip vtk header lines */ for (int skip = 0; skip < 9; skip++) { pl = fgets(line, BUFSIZ, dirinput); - assert(pl); + if (pl == NULL) printf("Error in header\n"); } for (int k = 0; k < sys->Lz; k++) { @@ -427,11 +427,11 @@ void read_data(int argc, char** argv, const options_t * opts, /* skip header vtk lines to size ... */ for (int skip = 0; skip < 4; skip++) { pl = fgets(line, BUFSIZ, sopinput); - assert(pl); + if (pl == NULL) printf("Error in vtk header\n"); } /* take system dimensions from vtk-header */ nread = fscanf(sopinput,"%s %d %d %d", dummy, &Lxsop, &Lysop, &Lzsop); - assert(nread == 4); + if (nread != 4) printf("Bad dimensions in header\n"); /* skip rest header lines */ for (int skip = 5; skip < 11; skip++) { From dfb2e0ca2d5c75cb58f4cbd9596bd3ea1ce009e7 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 14 Oct 2022 16:00:49 +0100 Subject: [PATCH 044/244] Fix initialisation --- tests/unit/test_io_info_args_rt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_io_info_args_rt.c b/tests/unit/test_io_info_args_rt.c index 7ac8d6891..a11f92ee0 100644 --- a/tests/unit/test_io_info_args_rt.c +++ b/tests/unit/test_io_info_args_rt.c @@ -199,7 +199,7 @@ int test_io_info_args_rt_output(pe_t * pe) { rt_add_key_value(rt, "q_output_io_report", "yes"); { - io_info_args_t args = {0}; + io_info_args_t args = io_info_args_default(); io_info_args_rt_output(rt, RT_FATAL, "q", &args); assert(args.output.mode == IO_MODE_SINGLE); From abc24356620fbc0ba7930a9b7e6d25bc53bd9736 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 14 Oct 2022 16:24:05 +0100 Subject: [PATCH 045/244] Fix alert attempt --- util/polarizer.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/util/polarizer.c b/util/polarizer.c index a4d9cba3f..cec83f816 100644 --- a/util/polarizer.c +++ b/util/polarizer.c @@ -431,7 +431,11 @@ void read_data(int argc, char** argv, const options_t * opts, } /* take system dimensions from vtk-header */ nread = fscanf(sopinput,"%s %d %d %d", dummy, &Lxsop, &Lysop, &Lzsop); - if (nread != 4) printf("Bad dimensions in header\n"); + assert(nread == 4); + if (nread != 4) { + printf("Bad dimensions in header\n"); + exit(-1); + } /* skip rest header lines */ for (int skip = 5; skip < 11; skip++) { From ccea6295c4b588cb1fc9075e9ccf8dac53131dcf Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 18 Oct 2022 16:47:10 +0100 Subject: [PATCH 046/244] Patch key_present bug --- src/runtime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime.c b/src/runtime.c index 3706fcbab..83d6313d7 100644 --- a/src/runtime.c +++ b/src/runtime.c @@ -779,7 +779,7 @@ int rt_key_present(rt_t * rt, const char * key) { assert(rt); - for ( ; pair; pair = pair->next) { + for (pair = rt->keylist; pair; pair = pair->next) { if (strncmp(key, pair->key, NKEY_LENGTH) == 0) { present = 1; break; From bd100f15952a3c1c8ff7407fe7bc2f9aefd197c0 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 18 Oct 2022 16:49:42 +0100 Subject: [PATCH 047/244] Add version with global correction --- src/phi_grad_mu.c | 329 ++++++++++++++++++++++++++++++++++++++++++++++ src/phi_grad_mu.h | 4 +- 2 files changed, 332 insertions(+), 1 deletion(-) diff --git a/src/phi_grad_mu.c b/src/phi_grad_mu.c index cef830422..80efd30e0 100644 --- a/src/phi_grad_mu.c +++ b/src/phi_grad_mu.c @@ -32,6 +32,14 @@ __global__ void phi_grad_mu_solid_kernel(kernel_ctxt_t * ktx, field_t * phi, __global__ void phi_grad_mu_external_kernel(kernel_ctxt_t * ktx, field_t * phi, double3 grad_mu, hydro_t * hydro); +__global__ void phi_grad_mu_correction_kernel(kernel_ctxt_t * ktx, + field_t * phi, fe_t * fe, + hydro_t * hydro, map_t * map, + double * fcorrect); + +__global__ void phi_grad_mu_force_kernel(kernel_ctxt_t * ktx, hydro_t * hydro, + map_t * map, double3 fcorrect); + /***************************************************************************** * * phi_grad_mu_fluid @@ -180,6 +188,97 @@ __host__ int phi_grad_mu_external(cs_t * cs, field_t * phi, hydro_t * hydro) { return 0; } +/***************************************************************************** + * + * phi_grad_mu_correction + * + * Driver for a general case which allows for arbitrary solid, and + * also provides an option for a correction to any deviation from + * \sum f_i /= 0 in the global picture. + * + * The correction is a two-stage process in which any non-zero f_i + * is evenly distributed between all available fluid nodes. + * + *****************************************************************************/ + +__host__ int phi_grad_mu_correction(cs_t * cs, field_t * phi, fe_t * fe, + hydro_t * hydro, map_t * map, int opt) { + + int nlocal[3] = {0}; + dim3 nblk, ntpb; + + size_t sz = 4*sizeof(double); + double * flocal_d = NULL; /* Local contribution to correction */ + double ftotal[4] = {0}; /* (volume, fx, fy, fz) */ + + fe_t * fe_target = NULL; + kernel_info_t limits = {0}; + kernel_ctxt_t * ctxt = NULL; + + assert(cs); + assert(phi); + assert(hydro); + assert(map); + + cs_nlocal(cs, nlocal); + fe->func->target(fe, &fe_target); + + limits.imin = 1; limits.imax = nlocal[X]; + limits.jmin = 1; limits.jmax = nlocal[Y]; + limits.kmin = 1; limits.kmax = nlocal[Z]; + + /* Allocate and initialise the accumulator */ + tdpAssert(tdpMalloc((void **) &flocal_d, sz)); + tdpAssert(tdpMemset(flocal_d, 0, sz)); + + kernel_ctxt_create(cs, 1, limits, &ctxt); + kernel_ctxt_launch_param(ctxt, &nblk, &ntpb); + + tdpLaunchKernel(phi_grad_mu_correction_kernel, nblk, ntpb, 0, 0, + ctxt->target, phi->target, fe_target, hydro->target, + map->target, flocal_d); + + tdpAssert(tdpPeekAtLastError()); + tdpAssert(tdpDeviceSynchronize()); + + /* Accumulate the total (all ranks) */ + + if (opt == 0) { + /* No correction */ + } + else { + + /* Compute and apply the global correction */ + /* The volume ftotal[0] should be an integer number of fluid sites, + * but is accumulated as a double ... */ + + MPI_Comm comm = MPI_COMM_NULL; + + cs_cart_comm(cs, &comm); + tdpAssert(tdpMemcpy(ftotal, flocal_d, sz, tdpMemcpyDeviceToHost)); + MPI_Allreduce(MPI_IN_PLACE, ftotal, 4, MPI_DOUBLE, MPI_SUM, comm); + + if (ftotal[0] > 0.0) { + ftotal[1] = -ftotal[1]/ftotal[0]; + ftotal[2] = -ftotal[2]/ftotal[0]; + ftotal[3] = -ftotal[3]/ftotal[0]; + } + + { + double3 f = {ftotal[1], ftotal[2], ftotal[3]}; + tdpLaunchKernel(phi_grad_mu_force_kernel, nblk, ntpb, 0, 0, + ctxt->target, hydro->target, map->target, f); + tdpAssert(tdpPeekAtLastError()); + tdpAssert(tdpDeviceSynchronize()); + } + } + + tdpAssert(tdpFree(&flocal_d)); + kernel_ctxt_free(ctxt); + + return 0; +} + /***************************************************************************** * * phi_grad_mu_fluid_kernel @@ -280,6 +379,10 @@ __global__ void phi_grad_mu_fluid_kernel(kernel_ctxt_t * ktx, field_t * phi, * chemical potentials. The force only involves the first two * chemical potentials, so loops involving nf are relevant. * + * Note this case is aimed at walls, so it is assumed that all + * fluid sites in the domain are MAP_FLUID, and there is a check + * against MAP_BOUNDARY only to identify solid sites... + * *****************************************************************************/ __global__ void phi_grad_mu_solid_kernel(kernel_ctxt_t * ktx, field_t * field, @@ -452,3 +555,229 @@ __global__ void phi_grad_mu_external_kernel(kernel_ctxt_t * ktx, field_t * phi, return; } + +/***************************************************************************** + * + * phi_grad_mu_solid_correction_kernel + * + * Work out the force at each fluid site as before. + * This is a general version which allows general solid objects. + * + * Here we accumulate the force over all sites (and the current number + * of fluid sites) to admit a global correction if the sum isn't zero. + * + *****************************************************************************/ + +__global__ void phi_grad_mu_correction_kernel(kernel_ctxt_t * ktx, + field_t * field, + fe_t * fe, hydro_t * hydro, + map_t * map, double * fcorrect) { + int kiterations; + int kindex; + int tid = threadIdx.x; + + __shared__ double fv[TARGET_PAD*TARGET_MAX_THREADS_PER_BLOCK]; + __shared__ double fx[TARGET_PAD*TARGET_MAX_THREADS_PER_BLOCK]; + __shared__ double fy[TARGET_PAD*TARGET_MAX_THREADS_PER_BLOCK]; + __shared__ double fz[TARGET_PAD*TARGET_MAX_THREADS_PER_BLOCK]; + + assert(ktx); + assert(field); + assert(hydro); + assert(field->nf <= NVECTOR); + + fv[TARGET_PAD*tid] = 0.0; + fx[TARGET_PAD*tid] = 0.0; + fy[TARGET_PAD*tid] = 0.0; + fz[TARGET_PAD*tid] = 0.0; + + kiterations = kernel_iterations(ktx); + + for_simt_parallel(kindex, kiterations, 1) { + + int ic = kernel_coords_ic(ktx, kindex); + int jc = kernel_coords_jc(ktx, kindex); + int kc = kernel_coords_kc(ktx, kindex); + int index0 = kernel_coords_index(ktx, ic, jc, kc); + int map0 = -1; + + map_status(map, index0, &map0); + + if (map0 == MAP_FLUID) { + + double force[3] = {0}; + double phi[NVECTOR] = {0}; + double mu[NVECTOR+1] = {0}; + + field_scalar_array(field, index0, phi); + fe->func->mu(fe, index0, mu); + + /* x-direction */ + { + int indexm1 = kernel_coords_index(ktx, ic-1, jc, kc); + int indexp1 = kernel_coords_index(ktx, ic+1, jc, kc); + int mapm1 = MAP_FLUID; + int mapp1 = MAP_FLUID; + + double mum1[NVECTOR+1] = {0}; + double mup1[NVECTOR+1] = {0}; + + fe->func->mu(fe, indexm1, mum1); + fe->func->mu(fe, indexp1, mup1); + + map_status(map, indexm1, &mapm1); + map_status(map, indexp1, &mapp1); + + if (mapm1 != MAP_FLUID) { + for (int n1 = 0; n1 < field->nf; n1++) { + mum1[n1] = mu[n1]; + } + } + if (mapp1 != MAP_FLUID) { + for (int n1 = 0; n1 < field->nf; n1++) { + mup1[n1] = mu[n1]; + } + } + + for (int n1 = 0; n1 < field->nf; n1++) { + force[X] -= phi[n1]*0.5*(mup1[n1] - mum1[n1]); + } + } + + /* y-direction */ + { + int indexm1 = kernel_coords_index(ktx, ic, jc-1, kc); + int indexp1 = kernel_coords_index(ktx, ic, jc+1, kc); + int mapm1 = MAP_FLUID; + int mapp1 = MAP_FLUID; + + double mum1[NVECTOR+1] = {0}; + double mup1[NVECTOR+1] = {0}; + + fe->func->mu(fe, indexm1, mum1); + fe->func->mu(fe, indexp1, mup1); + + map_status(map, indexm1, &mapm1); + map_status(map, indexp1, &mapp1); + + if (mapm1 != MAP_FLUID) { + for (int n1 =0; n1 < field->nf; n1++) { + mum1[n1] = mu[n1]; + } + } + if (mapp1 != MAP_FLUID) { + for (int n1 = 0; n1 < field->nf; n1++) { + mup1[n1] = mu[n1]; + } + } + + for (int n1 = 0; n1 < field->nf; n1++) { + force[Y] -= phi[n1]*0.5*(mup1[n1] - mum1[n1]); + } + } + + /* z-direction */ + { + int indexm1 = kernel_coords_index(ktx, ic, jc, kc-1); + int indexp1 = kernel_coords_index(ktx, ic, jc, kc+1); + int mapm1 = MAP_FLUID; + int mapp1 = MAP_FLUID; + + double mum1[NVECTOR+1] = {0}; + double mup1[NVECTOR+1] = {0}; + + fe->func->mu(fe, indexm1, mum1); + fe->func->mu(fe, indexp1, mup1); + + map_status(map, indexm1, &mapm1); + map_status(map, indexp1, &mapp1); + + if (mapm1 != MAP_FLUID) { + for (int n1 = 0; n1 < field->nf; n1++) { + mum1[n1] = mu[n1]; + } + } + if (mapp1 != MAP_FLUID) { + for (int n1 = 0; n1 < field->nf; n1++) { + mup1[n1] = mu[n1]; + } + } + + for (int n1 = 0; n1 < field->nf; n1++) { + force[Z] -= phi[n1]*0.5*(mup1[n1] - mum1[n1]); + } + } + + /* Store the force on lattice */ + + hydro_f_local_add(hydro, index0, force); + + fv[TARGET_PAD*tid] += 1.0; + fx[TARGET_PAD*tid] += force[X]; + fy[TARGET_PAD*tid] += force[Y]; + fz[TARGET_PAD*tid] += force[Z]; + } + } + + /* Accumulate the totals */ + + __syncthreads(); + + if (tid == 0) { + double fvb = 0.0; + double fxb = 0.0; + double fyb = 0.0; + double fzb = 0.0; + + for (int it = 0; it < blockDim.x; it++) { + fvb += fv[TARGET_PAD*it]; + fxb += fx[TARGET_PAD*it]; + fyb += fy[TARGET_PAD*it]; + fzb += fz[TARGET_PAD*it]; + } + tdpAtomicAddDouble(fcorrect, fvb); /* Volume */ + tdpAtomicAddDouble(fcorrect + 1 + X, fxb); /* force[X] */ + tdpAtomicAddDouble(fcorrect + 1 + Y, fyb); /* force[Y] */ + tdpAtomicAddDouble(fcorrect + 1 + Z, fzb); /* force[Z] */ + } + + return; +} + +/***************************************************************************** + * + * phi_grad_mu_force_kernel + * + * Apply a uniform correction to local force at fluid sites. + * + *****************************************************************************/ + +__global__ void phi_grad_mu_force_kernel(kernel_ctxt_t * ktx, hydro_t * hydro, + map_t * map, double3 fcorrect) { + int kiterations; + int kindex; + + assert(ktx); + assert(hydro); + assert(map); + + kiterations = kernel_iterations(ktx); + + for_simt_parallel(kindex, kiterations, 1) { + + int ic = kernel_coords_ic(ktx, kindex); + int jc = kernel_coords_jc(ktx, kindex); + int kc = kernel_coords_kc(ktx, kindex); + int index = kernel_coords_index(ktx, ic, jc, kc); + int map0 = -1; + + map_status(map, index, &map0); + + if (map0 == MAP_FLUID) { + double force[3] = {fcorrect.x, fcorrect.y, fcorrect.z}; + hydro_f_local_add(hydro, index, force); + } + } + + return; +} diff --git a/src/phi_grad_mu.h b/src/phi_grad_mu.h index 4b96e7f3f..4720dc87b 100644 --- a/src/phi_grad_mu.h +++ b/src/phi_grad_mu.h @@ -5,7 +5,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2021 Thge University of Edinburgh + * (c) 2021-2022 Thge University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -27,4 +27,6 @@ __host__ int phi_grad_mu_solid(cs_t * cs, field_t * phi, fe_t * fe, hydro_t * hydro, map_t * map); __host__ int phi_grad_mu_external(cs_t * cs, field_t * phi, hydro_t * hydro); +__host__ int phi_grad_mu_correction(cs_t * cs, field_t * phi, fe_t * fe, + hydro_t * hydro, map_t * map, int opt); #endif From 4ec3d59b4c2e78bea8b727b0fb8871624e3b63f9 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 18 Oct 2022 16:51:19 +0100 Subject: [PATCH 048/244] Refactor force method input --- src/ludwig.c | 203 +++++++++++++++++++++----------- src/phi_force.c | 13 +- src/phi_force_colloid.c | 4 +- src/phi_force_stress.c | 18 +-- src/phi_force_stress.h | 6 +- src/stats_colloid_force_split.c | 2 +- 6 files changed, 160 insertions(+), 86 deletions(-) diff --git a/src/ludwig.c b/src/ludwig.c index d84d895c6..3578a4b2c 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -69,6 +69,8 @@ #include "fe_electro.h" #include "fe_electro_symmetric.h" +#include "fe_force_method_rt.h" + /* Dynamics */ #include "cahn_hilliard.h" #include "phi_cahn_hilliard.h" @@ -1282,12 +1284,11 @@ int free_energy_init_rt(ludwig_t * ludwig) { coords_init_rt(pe, rt, cs); lees_edw_create(pe, cs, info, &le); lees_edw_info(le); - pth_create(pe, cs, PTH_METHOD_NO_FORCE, &ludwig->pth); + pth_create(pe, cs, FE_FORCE_METHOD_NO_FORCE, &ludwig->pth); } else if (strcmp(description, "symmetric") == 0 || strcmp(description, "symmetric_noise") == 0) { - int use_stress_relaxation; phi_ch_info_t ch_options = {0}; fe_symm_t * fe = NULL; @@ -1346,22 +1347,28 @@ int free_energy_init_rt(ludwig_t * ludwig) { /* Force */ - use_stress_relaxation = rt_switch(rt, "fe_use_stress_relaxation"); - fe->super.use_stress_relaxation = use_stress_relaxation; + { + fe_force_method_enum_t method = fe_force_method_default(); + + fe_force_method_rt_messages(rt, RT_INFO); + fe_force_method_rt(rt, RT_FATAL, &method); + + /* The following are supported */ + switch (method) { + case FE_FORCE_METHOD_STRESS_DIVERGENCE: + case FE_FORCE_METHOD_PHI_GRADMU: + case FE_FORCE_METHOD_PHI_GRADMU_CORRECTION: + break; + case FE_FORCE_METHOD_RELAXATION_SYMM: + fe->super.use_stress_relaxation = 1; + break; + default: + pe_fatal(pe, "symmetric free energy force_method not available\n"); + } - if (fe->super.use_stress_relaxation) { - pe_info(pe, "\n"); - pe_info(pe, "Force calculation\n"); - pe_info(pe, "Symmetric stress via collision relaxation\n"); - pth_create(pe, cs, PTH_METHOD_STRESS_ONLY, &ludwig->pth); - } - else { - p = 1; /* Default is to use divergence method */ - rt_int_parameter(rt, "fd_force_divergence", &p); + pth_create(pe, cs, method, &ludwig->pth); pe_info(pe, "Force calculation: %s\n", - (p == 0) ? "phi grad mu method" : "divergence method"); - if (p == 0) pth_create(pe, cs, PTH_METHOD_GRADMU, &ludwig->pth); - if (p == 1) pth_create(pe, cs, PTH_METHOD_DIVERGENCE, &ludwig->pth); + fe_force_method_to_string(method)); } ludwig->fe_symm = fe; @@ -1402,7 +1409,8 @@ int free_energy_init_rt(ludwig_t * ludwig) { pe_info(pe, "Mobility M = %12.5e\n", value); /* No explicit force is relevant */ - pth_create(pe, cs, PTH_METHOD_NO_FORCE, &ludwig->pth); + fe_force_method_rt_messages(rt, RT_INFO); + pth_create(pe, cs, FE_FORCE_METHOD_NO_FORCE, &ludwig->pth); ludwig->fe_symm = fe; ludwig->fe = (fe_t *) fe; @@ -1442,16 +1450,27 @@ int free_energy_init_rt(ludwig_t * ludwig) { physics_mobility_set(ludwig->phys, value); pe_info(pe, "Mobility M = %12.5e\n", value); - p = 1; - rt_int_parameter(rt, "fd_force_divergence", &p); - pe_info(pe, "Force caluclation: %s\n", - (p == 0) ? "phi grad mu method" : "divergence method"); - if (p == 0) { - pth_create(pe, cs, PTH_METHOD_GRADMU, &ludwig->pth); - } - else { - pth_create(pe, cs, PTH_METHOD_DIVERGENCE, &ludwig->pth); + { + fe_force_method_enum_t method = fe_force_method_default(); + + fe_force_method_rt_messages(rt, RT_INFO); + fe_force_method_rt(rt, RT_FATAL, &method); + + /* The following are supported */ + switch (method) { + case FE_FORCE_METHOD_STRESS_DIVERGENCE: + case FE_FORCE_METHOD_PHI_GRADMU: + case FE_FORCE_METHOD_PHI_GRADMU_CORRECTION: + break; + default: + pe_fatal(pe, "brazovskii: force_method not available\n"); + } + + pth_create(pe, cs, method, &ludwig->pth); + pe_info(pe, "Force calculation: %s\n", + fe_force_method_to_string(method)); } + ludwig->fe_braz = fe; ludwig->fe = (fe_t *) fe; } @@ -1503,10 +1522,25 @@ int free_energy_init_rt(ludwig_t * ludwig) { /* Coupling between momentum and free energy */ /* Hydrodynamics sector (move to hydro_rt?) */ - - n = rt_switch(rt, "hydrodynamics"); { - int method = (n == 0) ? PTH_METHOD_NO_FORCE : PTH_METHOD_GRADMU; + fe_force_method_enum_t method = fe_force_method_default(); + + fe_force_method_rt_messages(rt, RT_INFO); + fe_force_method_rt(rt, RT_FATAL, &method); + + /* The following are possible (if not tested) */ + switch (method) { + case FE_FORCE_METHOD_STRESS_DIVERGENCE: + case FE_FORCE_METHOD_PHI_GRADMU: + case FE_FORCE_METHOD_PHI_GRADMU_CORRECTION: + break; + case FE_FORCE_METHOD_RELAXATION_SYMM: + fe->super.use_stress_relaxation = 1; + break; + default: + pe_fatal(pe, "surfactant free energy force_method not available\n"); + } + pth_create(pe, cs, method, &ludwig->pth); } @@ -1565,17 +1599,29 @@ int free_energy_init_rt(ludwig_t * ludwig) { ch_info(ludwig->ch); /* Coupling between momentum and free energy */ - /* Default method for ternary free energy: gradmu */ - p = 0; + /* Default method for ternary free energy: phi_gradmu */ + { + fe_force_method_enum_t method = FE_FORCE_METHOD_PHI_GRADMU; + + fe_force_method_rt_messages(rt, RT_INFO); + fe_force_method_rt(rt, RT_FATAL, &method); + + /* The following are supported */ + switch (method) { + case FE_FORCE_METHOD_STRESS_DIVERGENCE: + case FE_FORCE_METHOD_PHI_GRADMU: + case FE_FORCE_METHOD_PHI_GRADMU_CORRECTION: + break; + case FE_FORCE_METHOD_RELAXATION_SYMM: + fe->super.use_stress_relaxation = 1; + break; + default: + pe_fatal(pe, "ternary free energy: force_method not available\n"); + } - rt_int_parameter(rt, "fd_force_divergence", &p); - pe_info(pe, "Force calculation: %s\n", - (p == 0) ? "phi grad mu method" : "divergence method"); - if (p == 0) { - pth_create(pe, cs, PTH_METHOD_GRADMU, &ludwig->pth); - } - else { - pth_create(pe, cs, PTH_METHOD_DIVERGENCE, &ludwig->pth); + pth_create(pe, cs, method, &ludwig->pth); + pe_info(pe, "Force calculation: %s\n", + fe_force_method_to_string(method)); } ludwig->fe_ternary = fe; @@ -1584,7 +1630,6 @@ int free_energy_init_rt(ludwig_t * ludwig) { else if (strcmp(description, "lc_blue_phase") == 0) { fe_lc_t * fe = NULL; - int use_stress_relaxation = 0; /* Liquid crystal (always finite difference). */ @@ -1619,23 +1664,36 @@ int free_energy_init_rt(ludwig_t * ludwig) { beris_edw_create(pe, cs, le, &ludwig->be); blue_phase_init_rt(pe, rt, fe, ludwig->be); - use_stress_relaxation = rt_switch(rt, "fe_use_stress_relaxation"); - fe->super.use_stress_relaxation = use_stress_relaxation; - if (fe->super.use_stress_relaxation) { - pe_info(pe, "\n"); - pe_info(pe, "Split symmetric/antisymmetric stress relaxation/force\n"); - tdpMemcpy(&fe->target->super.use_stress_relaxation, - &use_stress_relaxation, sizeof(int), tdpMemcpyHostToDevice); + { + fe_force_method_enum_t method = fe_force_method_default(); + + fe_force_method_rt_messages(rt, RT_INFO); + fe_force_method_rt(rt, RT_FATAL, &method); + + /* The following are supported */ + switch (method) { + case FE_FORCE_METHOD_STRESS_DIVERGENCE: + break; + case FE_FORCE_METHOD_RELAXATION_ANTI: + fe->super.use_stress_relaxation = 1; + tdpAssert(tdpMemcpy(&fe->target->super.use_stress_relaxation, + &fe->super.use_stress_relaxation, sizeof(int), + tdpMemcpyHostToDevice)); + break; + default: + pe_fatal(pe, "liquid crystal: force_method not available\n"); + } + + pth_create(pe, cs, method, &ludwig->pth); } + /* Order parameter fluctuations */ p = 0; rt_int_parameter(rt, "lc_noise", &p); noise_present_set(ludwig->noise_rho, NOISE_QAB, p); pe_info(pe, "LC fluctuations: = %s\n", (p == 0) ? "off" : "on"); - pth_create(pe, cs, PTH_METHOD_DIVERGENCE, &ludwig->pth); - - /* Not very elegant, but here ... */ + /* Assign anchoring information (both gradient possibilities) */ grad_lc_anch_create(pe, cs, NULL, NULL, NULL, fe, NULL); grad_s7_anchoring_create(pe, cs, NULL, fe, NULL); @@ -1678,7 +1736,7 @@ int free_energy_init_rt(ludwig_t * ludwig) { leslie_ericksen_swim_set(value); pe_info(pe, "Self-advection parameter = %12.5e\n", value); - pth_create(pe, cs, PTH_METHOD_DIVERGENCE, &ludwig->pth); + pth_create(pe, cs, FE_FORCE_METHOD_STRESS_DIVERGENCE, &ludwig->pth); } else if(strcmp(description, "lc_droplet") == 0) { @@ -1686,7 +1744,6 @@ int free_energy_init_rt(ludwig_t * ludwig) { fe_symm_t * symm = NULL; fe_lc_t * lc = NULL; fe_lc_droplet_t * fe = NULL; - int use_stress_relaxation = 0; /* liquid crystal droplet */ pe_info(pe, "\n"); @@ -1735,15 +1792,6 @@ int free_energy_init_rt(ludwig_t * ludwig) { physics_mobility_set(ludwig->phys, value); pe_info(pe, "Mobility M = %12.5e\n", value); - /* Force */ - - p = 1; /* Default is to use divergence method */ - rt_int_parameter(rt, "fd_force_divergence", &p); - pe_info(pe, "Force calculation: %s\n", - (p == 0) ? "phi grad mu method" : "divergence method"); - assert(p != 0); /* Grad mu method not implemented! */ - pth_create(pe, cs, PTH_METHOD_DIVERGENCE, &ludwig->pth); - /* Liquid crystal part */ nhalo = 2; /* Required for stress diveregnce. */ ngrad = 2; /* (\nabla^2) required */ @@ -1773,13 +1821,34 @@ int free_energy_init_rt(ludwig_t * ludwig) { fe_lc_droplet_create(pe, cs, lc, symm, &fe); fe_lc_droplet_run_time(pe, rt, fe); - use_stress_relaxation = rt_switch(rt, "fe_use_stress_relaxation"); - fe->super.use_stress_relaxation = use_stress_relaxation; - if (fe->super.use_stress_relaxation) { + /* Force */ + + { + fe_force_method_enum_t method = fe_force_method_default(); + + fe_force_method_rt_messages(rt, RT_INFO); + fe_force_method_rt(rt, RT_FATAL, &method); + + /* The following are supported */ + switch (method) { + case FE_FORCE_METHOD_STRESS_DIVERGENCE: + break; + case FE_FORCE_METHOD_RELAXATION_ANTI: + fe->super.use_stress_relaxation = 1; + tdpAssert(tdpMemcpy(&fe->target->super.use_stress_relaxation, + &fe->super.use_stress_relaxation, sizeof(int), + tdpMemcpyHostToDevice)); + break; + default: + pe_fatal(pe, "liquid crystal droplet: force_method not available\n"); + } + + pth_create(pe, cs, method, &ludwig->pth); + pe_info(pe, "\n"); - pe_info(pe, "Split symmetric/antisymmetric stress relaxation/force\n"); - tdpMemcpy(&fe->target->super.use_stress_relaxation, - &use_stress_relaxation, sizeof(int), tdpMemcpyHostToDevice); + pe_info(pe, "Coupled free energy\n"); + pe_info(pe, "Force calculation: %s\n", + fe_force_method_to_string(method)); } p = rt_switch(rt, "lc_noise"); diff --git a/src/phi_force.c b/src/phi_force.c index ac64b580c..23ec6a7d0 100644 --- a/src/phi_force.c +++ b/src/phi_force.c @@ -81,7 +81,7 @@ __host__ int phi_force_calculation(pe_t * pe, cs_t * cs, lees_edw_t * le, if (pth == NULL) return 0; if (hydro == NULL) return 0; - if (pth->method == PTH_METHOD_NO_FORCE) return 0; + if (pth->method == FE_FORCE_METHOD_NO_FORCE) return 0; wall_is_pm(wall, &is_pm); @@ -96,7 +96,8 @@ __host__ int phi_force_calculation(pe_t * pe, cs_t * cs, lees_edw_t * le, } else { switch (pth->method) { - case PTH_METHOD_DIVERGENCE: + case FE_FORCE_METHOD_STRESS_DIVERGENCE: + case FE_FORCE_METHOD_RELAXATION_ANTI: pth_stress_compute(pth, fe); if (wall_present(wall) || is_pm) { pth_force_fluid_wall_driver(pth, hydro, map, wall); @@ -105,7 +106,7 @@ __host__ int phi_force_calculation(pe_t * pe, cs_t * cs, lees_edw_t * le, pth_force_fluid_driver(pth, hydro); } break; - case PTH_METHOD_GRADMU: + case FE_FORCE_METHOD_PHI_GRADMU: if (wall_present(wall) || is_pm) { phi_grad_mu_solid(cs, phi, fe, hydro, map); @@ -117,7 +118,11 @@ __host__ int phi_force_calculation(pe_t * pe, cs_t * cs, lees_edw_t * le, phi_grad_mu_external(cs, phi, hydro); } break; - case PTH_METHOD_STRESS_ONLY: + case FE_FORCE_METHOD_PHI_GRADMU_CORRECTION: + phi_grad_mu_correction(cs, phi, fe, hydro, map, 0); + break; + case FE_FORCE_METHOD_RELAXATION_SYMM: + assert(0); /* HAS THIS EVER BEEN TESTED? */ pth_stress_compute(pth, fe); break; default: diff --git a/src/phi_force_colloid.c b/src/phi_force_colloid.c index 4f4a93afa..d2b0e5ac9 100644 --- a/src/phi_force_colloid.c +++ b/src/phi_force_colloid.c @@ -46,7 +46,7 @@ * Kevin Stratford (kevin@epcc.ed.ac.uk) * Alan Gray (alang@epcc.ed.ac.uk) provided device implementations. * - * (c) 2010-2021 The University of Edinburgh + * (c) 2010-2022 The University of Edinburgh * *****************************************************************************/ @@ -95,7 +95,7 @@ __host__ int pth_force_colloid(pth_t * pth, fe_t * fe, colloids_info_t * cinfo, if (hydro == NULL && ncolloid == 0) return 0; - if (pth->method == PTH_METHOD_DIVERGENCE) { + if (pth->method == FE_FORCE_METHOD_STRESS_DIVERGENCE) { pth_stress_compute(pth, fe); pth_force_driver(pth, cinfo, hydro, map, wall, model); } diff --git a/src/phi_force_stress.c b/src/phi_force_stress.c index 163329960..1b54dcd34 100644 --- a/src/phi_force_stress.c +++ b/src/phi_force_stress.c @@ -9,7 +9,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2012-2018 The University of Edinburgh + * (c) 2012-2022 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -56,12 +56,11 @@ __host__ int pth_create(pe_t * pe, cs_t * cs, int method, pth_t ** pobj) { obj->method = method; cs_nsites(cs, &obj->nsites); - /* If memory required */ + /* malloc() here in all cases on host (even if not required). */ - if (method == PTH_METHOD_DIVERGENCE) { - obj->str = (double *) calloc(3*3*obj->nsites, sizeof(double)); - if (obj->str == NULL) pe_fatal(pe, "calloc(pth->str) failed\n"); - } + obj->str = (double *) malloc(3*3*obj->nsites*sizeof(double)); + assert(obj->str); + if (obj->str == NULL) pe_fatal(pe, "malloc(pth->str) failed\n"); /* Allocate target memory, or alias */ @@ -71,13 +70,16 @@ __host__ int pth_create(pe_t * pe, cs_t * cs, int method, pth_t ** pobj) { obj->target = obj; } else { + /* Allocate data only if really required */ + int imem = (method == FE_FORCE_METHOD_STRESS_DIVERGENCE) + || (method == FE_FORCE_METHOD_RELAXATION_ANTI); tdpMalloc((void **) &obj->target, sizeof(pth_t)); tdpMemset(obj->target, 0, sizeof(pth_t)); tdpMemcpy(&obj->target->nsites, &obj->nsites, sizeof(int), tdpMemcpyHostToDevice); - if (method == PTH_METHOD_DIVERGENCE) { + if (imem) { tdpMalloc((void **) &tmp, 3*3*obj->nsites*sizeof(double)); tdpMemcpy(&obj->target->str, &tmp, sizeof(double *), tdpMemcpyHostToDevice); @@ -197,7 +199,7 @@ __host__ int pth_stress_compute(pth_t * pth, fe_t * fe) { * do nothing. */ if (fe->func->str_anti != NULL) { tdpLaunchKernel(pth_kernel_a_v, nblk, ntpb, 0, 0, - ctxt->target, pth->target, fe_target); + ctxt->target, pth->target, fe_target); } } else { diff --git a/src/phi_force_stress.h b/src/phi_force_stress.h index 37bbb5c40..04f8571e9 100644 --- a/src/phi_force_stress.h +++ b/src/phi_force_stress.h @@ -7,7 +7,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2012-2019 The University of Edinburgh + * (c) 2012-2022 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -20,9 +20,7 @@ #include "pe.h" #include "coords.h" #include "free_energy.h" - -enum {PTH_METHOD_NO_FORCE, PTH_METHOD_DIVERGENCE, PTH_METHOD_GRADMU, - PTH_METHOD_STRESS_ONLY}; +#include "fe_force_method.h" typedef struct pth_s pth_t; diff --git a/src/stats_colloid_force_split.c b/src/stats_colloid_force_split.c index 58b2c5369..d0d6f1a59 100644 --- a/src/stats_colloid_force_split.c +++ b/src/stats_colloid_force_split.c @@ -64,7 +64,7 @@ int stats_colloid_force_split_update(colloids_info_t * cinfo, fe_t * fe) { pe = cinfo->pe; cs = cinfo->cs; - pth_create(pe, cs, PTH_METHOD_DIVERGENCE, &pth); + pth_create(pe, cs, FE_FORCE_METHOD_STRESS_DIVERGENCE, &pth); /* Total stress */ stat_stress_compute(pth, fe, fe_lc_stress); From 6bab89f0792360011a699456007e54f89049fbdd Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 18 Oct 2022 16:52:13 +0100 Subject: [PATCH 049/244] Refactor force method input --- tests/unit/test_fe_force_method.c | 148 +++++++++++++++++++++++ tests/unit/test_fe_force_method_rt.c | 172 +++++++++++++++++++++++++++ tests/unit/tests.c | 2 + tests/unit/tests.h | 2 + 4 files changed, 324 insertions(+) create mode 100644 tests/unit/test_fe_force_method.c create mode 100644 tests/unit/test_fe_force_method_rt.c diff --git a/tests/unit/test_fe_force_method.c b/tests/unit/test_fe_force_method.c new file mode 100644 index 000000000..be55bcaa0 --- /dev/null +++ b/tests/unit/test_fe_force_method.c @@ -0,0 +1,148 @@ +/***************************************************************************** + * + * test_fe_force_method.c + * + * + * (c) 2022 The University of Edinburgh + * + * Contributing author: + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include +#include + +#include "pe.h" +#include "fe_force_method.h" + +int test_fe_force_method_default(void); +int test_fe_force_method_to_enum(void); +int test_fe_force_method_to_string(void); + +/***************************************************************************** + * + * test_fe_force_method_suite + * + *****************************************************************************/ + +int test_fe_force_method_suite(void) { + + pe_t * pe = NULL; + + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + /* If the implementation has changed, the tests need to change. */ + assert(FE_FORCE_METHOD_MAX == 7); + + test_fe_force_method_default(); + test_fe_force_method_to_enum(); + test_fe_force_method_to_string(); + + pe_info(pe, "%-9s %s\n", "PASS", __FILE__); + + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_fe_force_method_default + * + *****************************************************************************/ + +int test_fe_force_method_default(void) { + + fe_force_method_enum_t method = fe_force_method_default(); + + assert(method == FE_FORCE_METHOD_STRESS_DIVERGENCE); + + return (method == FE_FORCE_METHOD_STRESS_DIVERGENCE); +} + +/***************************************************************************** + * + * test_fe_force_method_to_enum + * + *****************************************************************************/ + +int test_fe_force_method_to_enum(void) { + + int ifail = 0; + fe_force_method_enum_t method = FE_FORCE_METHOD_INVALID; + + method = fe_force_method_to_enum("stress_divergence"); + assert(method == FE_FORCE_METHOD_STRESS_DIVERGENCE); + + method = fe_force_method_to_enum("phi_gradmu"); + assert(method == FE_FORCE_METHOD_PHI_GRADMU); + + method = fe_force_method_to_enum("phi_gradmu_correction"); + assert(method == FE_FORCE_METHOD_PHI_GRADMU_CORRECTION); + + method = fe_force_method_to_enum("relaxation_symmetric"); + assert(method == FE_FORCE_METHOD_RELAXATION_SYMM); + + method = fe_force_method_to_enum("relaxation_antisymmetric"); + assert(method == FE_FORCE_METHOD_RELAXATION_ANTI); + if (method != FE_FORCE_METHOD_RELAXATION_ANTI) ifail = -1; + + return ifail; +} + +/***************************************************************************** + * + * test_fe_force_method_to_string + * + *****************************************************************************/ + +int test_fe_force_method_to_string(void) { + + int ifail = 0; + + + { + fe_force_method_enum_t method = FE_FORCE_METHOD_NO_FORCE; + const char * s = fe_force_method_to_string(method); + ifail = strcmp(s, "no_force"); + assert(ifail == 0); + } + + { + fe_force_method_enum_t method = FE_FORCE_METHOD_STRESS_DIVERGENCE; + const char * s = fe_force_method_to_string(method); + ifail = strcmp(s, "stress_divergence"); + assert(ifail == 0); + } + + { + fe_force_method_enum_t method = FE_FORCE_METHOD_PHI_GRADMU; + const char * s = fe_force_method_to_string(method); + ifail = strcmp(s, "phi_gradmu"); + assert(ifail == 0); + } + + { + fe_force_method_enum_t method = FE_FORCE_METHOD_PHI_GRADMU_CORRECTION; + const char * s = fe_force_method_to_string(method); + ifail = strcmp(s, "phi_gradmu_correction"); + assert(ifail == 0); + } + + { + fe_force_method_enum_t method = FE_FORCE_METHOD_RELAXATION_SYMM; + const char * s = fe_force_method_to_string(method); + ifail = strcmp(s, "relaxation_symmetric"); + assert(ifail == 0); + } + + { + fe_force_method_enum_t method = FE_FORCE_METHOD_RELAXATION_ANTI; + const char * s = fe_force_method_to_string(method); + ifail = strcmp(s, "relaxation_antisymmetric"); + assert(ifail == 0); + } + + return ifail; +} diff --git a/tests/unit/test_fe_force_method_rt.c b/tests/unit/test_fe_force_method_rt.c new file mode 100644 index 000000000..96871f502 --- /dev/null +++ b/tests/unit/test_fe_force_method_rt.c @@ -0,0 +1,172 @@ +/***************************************************************************** + * + * test_fe_force_method_rt.c + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Contributing author: + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include + +#include "fe_force_method_rt.h" + +int test_fe_force_method_rt(pe_t * pe, fe_force_method_enum_t method); +int test_fe_force_method_rt_messages(pe_t * pe); + +/***************************************************************************** + * + * test_fe_force_method_rt_suite + * + *****************************************************************************/ + +int test_fe_force_method_rt_suite(void) { + + pe_t * pe = NULL; + + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + test_fe_force_method_rt(pe, FE_FORCE_METHOD_STRESS_DIVERGENCE); + test_fe_force_method_rt(pe, FE_FORCE_METHOD_PHI_GRADMU); + test_fe_force_method_rt(pe, FE_FORCE_METHOD_PHI_GRADMU_CORRECTION); + test_fe_force_method_rt(pe, FE_FORCE_METHOD_RELAXATION_SYMM); + test_fe_force_method_rt(pe, FE_FORCE_METHOD_RELAXATION_ANTI); + + test_fe_force_method_rt_messages(pe); + + pe_info(pe, "%-9s %s\n", "PASS", __FILE__); + + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_fe_force_method_rt + * + *****************************************************************************/ + +int test_fe_force_method_rt(pe_t * pe, fe_force_method_enum_t method) { + + int ifail = 0; + rt_t * rt = NULL; + + assert(pe); + + rt_create(pe, &rt); + + /* No keys present */ + { + fe_force_method_enum_t imethod = fe_force_method_default(); + int iret = fe_force_method_rt(rt, RT_NONE, &imethod); + assert(iret == 0); + assert(imethod == fe_force_method_default()); + if (iret != 0) ifail += 1; + } + + switch (method) { + case (FE_FORCE_METHOD_STRESS_DIVERGENCE): + rt_add_key_value(rt, "fe_force_method", "stress_divergence"); + { + fe_force_method_enum_t imethod = FE_FORCE_METHOD_INVALID; + int iret = fe_force_method_rt(rt, RT_NONE, &imethod); + assert(iret == 1); + assert(imethod == FE_FORCE_METHOD_STRESS_DIVERGENCE); + if (iret != 1) ifail += 1; + } + break; + case (FE_FORCE_METHOD_PHI_GRADMU): + rt_add_key_value(rt, "fe_force_method", "phi_gradmu"); + { + fe_force_method_enum_t imethod = FE_FORCE_METHOD_INVALID; + int iret = fe_force_method_rt(rt, RT_NONE, &imethod); + assert(iret == 1); + assert(imethod == FE_FORCE_METHOD_PHI_GRADMU); + if (iret != 1) ifail += 1; + } + break; + case (FE_FORCE_METHOD_PHI_GRADMU_CORRECTION): + rt_add_key_value(rt, "fe_force_method", "phi_gradmu_correction"); + { + fe_force_method_enum_t imethod = FE_FORCE_METHOD_INVALID; + int iret = fe_force_method_rt(rt, RT_NONE, &imethod); + assert(iret == 1); + assert(imethod == FE_FORCE_METHOD_PHI_GRADMU_CORRECTION); + if (iret != 1) ifail += 1; + } + break; + case (FE_FORCE_METHOD_RELAXATION_SYMM): + rt_add_key_value(rt, "fe_force_method", "relaxation_symmetric"); + { + fe_force_method_enum_t imethod = FE_FORCE_METHOD_INVALID; + int iret = fe_force_method_rt(rt, RT_NONE, &imethod); + assert(iret == 1); + assert(imethod == FE_FORCE_METHOD_RELAXATION_SYMM); + if (iret != 1) ifail += 1; + } + break; + case (FE_FORCE_METHOD_RELAXATION_ANTI): + rt_add_key_value(rt, "fe_force_method", "relaxation_antisymmetric"); + { + fe_force_method_enum_t imethod = FE_FORCE_METHOD_INVALID; + int iret = fe_force_method_rt(rt, RT_NONE, &imethod); + assert(iret == 1); + assert(imethod == FE_FORCE_METHOD_RELAXATION_ANTI); + if (iret != 1) ifail += 1; + } + break; + default: + /* Nothing. */ + ; + } + + rt_free(rt); + + return ifail; +} + +/***************************************************************************** + * + * test_fe_force_method_rt_messages + * + *****************************************************************************/ + +int test_fe_force_method_rt_messages(pe_t * pe) { + + int ifail = -1; + rt_t * rt = NULL; + + assert(pe); + + rt_create(pe, &rt); + + ifail = fe_force_method_rt_messages(rt, RT_NONE); + assert(ifail == 0); + + { + int ierr = 0; + rt_add_key_value(rt, "fd_force_divergence", "0"); + ierr = fe_force_method_rt_messages(rt, RT_NONE); + assert(ierr == -1); + if (ierr != -1) ifail += 1; + } + + { + int ierr = 0; + rt_add_key_value(rt, "fe_use_stress_relaxation", "yes"); + ierr = fe_force_method_rt_messages(rt, RT_NONE); + assert(ierr == -2); + if (ierr != -2) ifail += 1; + } + + rt_free(rt); + + return ifail; +} diff --git a/tests/unit/tests.c b/tests/unit/tests.c index dce322015..c1d0e1178 100644 --- a/tests/unit/tests.c +++ b/tests/unit/tests.c @@ -68,6 +68,8 @@ __host__ int tests_create() { test_fe_electro_suite(); test_fe_electro_symm_suite(); test_fe_lc_droplet_suite(); + test_fe_force_method_suite(); + test_fe_force_method_rt_suite(); test_field_suite(); test_field_grad_suite(); test_halo_suite(); diff --git a/tests/unit/tests.h b/tests/unit/tests.h index 60176c829..edd557b03 100644 --- a/tests/unit/tests.h +++ b/tests/unit/tests.h @@ -46,6 +46,8 @@ int test_fe_lc_droplet_suite(void); int test_fe_surfactant1_suite(void); int test_fe_symmetric_suite(void); int test_fe_ternary_suite(void); +int test_fe_force_method_suite(void); +int test_fe_force_method_rt_suite(void); int test_field_suite(void); int test_field_grad_suite(void); int test_gradient_d3q27_suite(void); From c184858e4ad8357b2adee6cdf34af2a8feaebf9e Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 18 Oct 2022 16:53:36 +0100 Subject: [PATCH 050/244] Comestic changes for force input --- tests/regression/d3q19-short/serial-bond-c01.log | 2 +- tests/regression/d3q19-short/serial-bond-c02.log | 2 +- tests/regression/d3q19-short/serial-drop-lc1.inp | 1 - tests/regression/d3q19-short/serial-drop-lc1.log | 4 +++- tests/regression/d3q19-short/serial-drop-lc2.inp | 1 - tests/regression/d3q19-short/serial-drop-lc2.log | 4 +++- tests/regression/d3q19-short/serial-drop-lc3.inp | 2 +- tests/regression/d3q19-short/serial-drop-lc3.log | 4 ++-- tests/regression/d3q19-short/serial-drop-lc4.inp | 2 +- tests/regression/d3q19-short/serial-drop-lc4.log | 4 ++-- tests/regression/d3q19-short/serial-drop-lc5.inp | 6 +++--- tests/regression/d3q19-short/serial-drop-lc5.log | 4 ++-- tests/regression/d3q19-short/serial-le2d-fd1.log | 2 +- tests/regression/d3q19-short/serial-le2d-fd2.log | 2 +- tests/regression/d3q19-short/serial-le3d-st1.log | 2 +- tests/regression/d3q19-short/serial-le3d-st2.log | 2 +- tests/regression/d3q19-short/serial-le3d-st3.log | 2 +- tests/regression/d3q19-short/serial-le3d-st4.log | 2 +- tests/regression/d3q19-short/serial-le3d-st5.log | 2 +- tests/regression/d3q19-short/serial-le3d-st6.log | 2 +- tests/regression/d3q19-short/serial-le3d-st7.log | 2 +- tests/regression/d3q19-short/serial-le3d-st8.log | 2 +- tests/regression/d3q19-short/serial-muex-st1.inp | 2 +- tests/regression/d3q19-short/serial-muex-st1.log | 2 +- tests/regression/d3q19-short/serial-spin-c02.log | 2 +- tests/regression/d3q19-short/serial-spin-fd1.log | 2 +- tests/regression/d3q19-short/serial-spin-fd2.log | 2 +- tests/regression/d3q19-short/serial-spin-n01.log | 2 +- tests/regression/d3q19-short/serial-spin-n02.log | 2 +- tests/regression/d3q19-short/serial-symm-dr1.log | 2 +- tests/regression/d3q19-short/serial-symm-dr2.log | 2 +- tests/regression/d3q19-short/serial-symm-pat.log | 2 +- tests/regression/d3q19-short/serial-tern-st1.log | 2 +- tests/regression/d3q19-short/serial-tern-st2.log | 2 +- tests/regression/d3q19-short/serial-tern-st3.log | 2 +- tests/regression/d3q19-short/serial-tern-st4.log | 2 +- tests/regression/d3q19-short/serial-tern-st5.inp | 1 - tests/regression/d3q19-short/serial-tern-st5.log | 2 +- 38 files changed, 44 insertions(+), 43 deletions(-) diff --git a/tests/regression/d3q19-short/serial-bond-c01.log b/tests/regression/d3q19-short/serial-bond-c01.log index fd54e1163..f27a71941 100644 --- a/tests/regression/d3q19-short/serial-bond-c01.log +++ b/tests/regression/d3q19-short/serial-bond-c01.log @@ -36,7 +36,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.50000e-01 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-bond-c02.log b/tests/regression/d3q19-short/serial-bond-c02.log index 6ed3c4c08..4cc8eae19 100644 --- a/tests/regression/d3q19-short/serial-bond-c02.log +++ b/tests/regression/d3q19-short/serial-bond-c02.log @@ -35,7 +35,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.50000e-01 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-drop-lc1.inp b/tests/regression/d3q19-short/serial-drop-lc1.inp index 5fe226adb..e7455b8e6 100644 --- a/tests/regression/d3q19-short/serial-drop-lc1.inp +++ b/tests/regression/d3q19-short/serial-drop-lc1.inp @@ -40,7 +40,6 @@ free_energy lc_droplet fd_advection_scheme_order 3 fd_gradient_calculation 3d_7pt_fluid -fd_force_divergence 1 ############################################################################### # diff --git a/tests/regression/d3q19-short/serial-drop-lc1.log b/tests/regression/d3q19-short/serial-drop-lc1.log index 2a4f29b9f..ab1ef1176 100644 --- a/tests/regression/d3q19-short/serial-drop-lc1.log +++ b/tests/regression/d3q19-short/serial-drop-lc1.log @@ -31,7 +31,6 @@ Interfacial width = 1.30231e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 5.00000e-02 -Force calculation: divergence method Free energy details ------------------- @@ -61,6 +60,9 @@ Isotropic/LC control gamma0 = 2.58600e+00 Isotropic/LC control delta = 2.50000e-01 Anchoring parameter W = -5.00000e-02 +Coupled free energy +Force calculation: stress_divergence + System properties ---------------- Mean fluid density: 1.00000e+00 diff --git a/tests/regression/d3q19-short/serial-drop-lc2.inp b/tests/regression/d3q19-short/serial-drop-lc2.inp index b1ab9693b..00c457f71 100644 --- a/tests/regression/d3q19-short/serial-drop-lc2.inp +++ b/tests/regression/d3q19-short/serial-drop-lc2.inp @@ -38,7 +38,6 @@ free_energy lc_droplet fd_advection_scheme_order 3 fd_gradient_calculation 3d_7pt_fluid -fd_force_divergence 1 ############################################################################### # diff --git a/tests/regression/d3q19-short/serial-drop-lc2.log b/tests/regression/d3q19-short/serial-drop-lc2.log index 14fa3ff70..4fe70c7b3 100644 --- a/tests/regression/d3q19-short/serial-drop-lc2.log +++ b/tests/regression/d3q19-short/serial-drop-lc2.log @@ -31,7 +31,6 @@ Interfacial width = 1.30231e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 5.00000e-02 -Force calculation: divergence method Free energy details ------------------- @@ -61,6 +60,9 @@ Isotropic/LC control gamma0 = 2.58600e+00 Isotropic/LC control delta = 2.50000e-01 Anchoring parameter W = -3.00000e-02 +Coupled free energy +Force calculation: stress_divergence + System properties ---------------- Mean fluid density: 1.00000e+00 diff --git a/tests/regression/d3q19-short/serial-drop-lc3.inp b/tests/regression/d3q19-short/serial-drop-lc3.inp index 1aa09d753..0207799ed 100644 --- a/tests/regression/d3q19-short/serial-drop-lc3.inp +++ b/tests/regression/d3q19-short/serial-drop-lc3.inp @@ -130,7 +130,7 @@ temperature 0.0000096 ############################################################################### free_energy lc_droplet -fe_use_stress_relaxation yes +fe_force_method relaxation_antisymmetric fd_advection_scheme_order 3 fd_gradient_calculation 3d_7pt_fluid diff --git a/tests/regression/d3q19-short/serial-drop-lc3.log b/tests/regression/d3q19-short/serial-drop-lc3.log index ecacb8fc6..f3be8f39e 100644 --- a/tests/regression/d3q19-short/serial-drop-lc3.log +++ b/tests/regression/d3q19-short/serial-drop-lc3.log @@ -36,7 +36,6 @@ Interfacial width = 1.30231e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 5.00000e+00 -Force calculation: divergence method Free energy details ------------------- @@ -66,7 +65,8 @@ Isotropic/LC control gamma0 = 2.58600e+00 Isotropic/LC control delta = 2.50000e-01 Anchoring parameter W = 0.00000e+00 -Split symmetric/antisymmetric stress relaxation/force +Coupled free energy +Force calculation: relaxation_antisymmetric System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-drop-lc4.inp b/tests/regression/d3q19-short/serial-drop-lc4.inp index 8fc62e6aa..f7f6e26fe 100644 --- a/tests/regression/d3q19-short/serial-drop-lc4.inp +++ b/tests/regression/d3q19-short/serial-drop-lc4.inp @@ -66,7 +66,7 @@ temperature 0.0000096 ############################################################################### free_energy lc_droplet -fe_use_stress_relaxation yes +fe_force_method relaxation_antisymmetric fd_advection_scheme_order 3 fd_gradient_calculation 3d_7pt_fluid diff --git a/tests/regression/d3q19-short/serial-drop-lc4.log b/tests/regression/d3q19-short/serial-drop-lc4.log index 70485701b..b37726504 100644 --- a/tests/regression/d3q19-short/serial-drop-lc4.log +++ b/tests/regression/d3q19-short/serial-drop-lc4.log @@ -30,7 +30,6 @@ Interfacial width = 1.30231e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 5.00000e+00 -Force calculation: divergence method Free energy details ------------------- @@ -60,7 +59,8 @@ Isotropic/LC control gamma0 = 2.58600e+00 Isotropic/LC control delta = 2.50000e-01 Anchoring parameter W = 0.00000e+00 -Split symmetric/antisymmetric stress relaxation/force +Coupled free energy +Force calculation: relaxation_antisymmetric System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-drop-lc5.inp b/tests/regression/d3q19-short/serial-drop-lc5.inp index fafacb6e9..8d7f665cf 100644 --- a/tests/regression/d3q19-short/serial-drop-lc5.inp +++ b/tests/regression/d3q19-short/serial-drop-lc5.inp @@ -16,7 +16,7 @@ # # Compositional order # -# The composition is 80:20 liquid crystal: i 1sotropic +# The composition is 80:20 liquid crystal: isotropic # The reported mean phi should be around 0.6. # # Symmetric free energy @@ -31,7 +31,7 @@ # so \sigma \approx 5.4e-02 and \xi_0 \approx 1.3 # Cahn Hilliard equation mobility M = 0.05 # -# Liquid crystel free energy +# Liquid crystal free energy # # lc_a0 0.05 # lc_gamma 3.086 @@ -78,7 +78,7 @@ temperature 0.0000096 ############################################################################### free_energy lc_droplet -fe_use_stress_relaxation yes +fe_force_method relaxation_antisymmetric fd_advection_scheme_order 3 fd_gradient_calculation 3d_7pt_fluid diff --git a/tests/regression/d3q19-short/serial-drop-lc5.log b/tests/regression/d3q19-short/serial-drop-lc5.log index 09b9a2388..118e61a95 100644 --- a/tests/regression/d3q19-short/serial-drop-lc5.log +++ b/tests/regression/d3q19-short/serial-drop-lc5.log @@ -30,7 +30,6 @@ Interfacial width = 1.30231e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 5.00000e-02 -Force calculation: divergence method Free energy details ------------------- @@ -60,7 +59,8 @@ Isotropic/LC control gamma0 = 2.58600e+00 Isotropic/LC control delta = 2.50000e-01 Anchoring parameter W = 0.00000e+00 -Split symmetric/antisymmetric stress relaxation/force +Coupled free energy +Force calculation: relaxation_antisymmetric System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-le2d-fd1.log b/tests/regression/d3q19-short/serial-le2d-fd1.log index 1fcd21aa5..236fd67cf 100644 --- a/tests/regression/d3q19-short/serial-le2d-fd1.log +++ b/tests/regression/d3q19-short/serial-le2d-fd1.log @@ -37,7 +37,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.50000e-01 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-le2d-fd2.log b/tests/regression/d3q19-short/serial-le2d-fd2.log index 434f73cba..740972ac4 100644 --- a/tests/regression/d3q19-short/serial-le2d-fd2.log +++ b/tests/regression/d3q19-short/serial-le2d-fd2.log @@ -37,7 +37,7 @@ Amplitude = 1.28418e+00 Using Cahn-Hilliard solver: Mobility M = 2.50000e-01 -Force caluclation: divergence method +Force calculation: stress_divergence System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-le3d-st1.log b/tests/regression/d3q19-short/serial-le3d-st1.log index 9495344b6..156a97cee 100644 --- a/tests/regression/d3q19-short/serial-le3d-st1.log +++ b/tests/regression/d3q19-short/serial-le3d-st1.log @@ -37,7 +37,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.50000e-01 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-le3d-st2.log b/tests/regression/d3q19-short/serial-le3d-st2.log index c750b6e5e..5392e86df 100644 --- a/tests/regression/d3q19-short/serial-le3d-st2.log +++ b/tests/regression/d3q19-short/serial-le3d-st2.log @@ -37,7 +37,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.50000e-01 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-le3d-st3.log b/tests/regression/d3q19-short/serial-le3d-st3.log index 3d4ac8493..9f4aacfc9 100644 --- a/tests/regression/d3q19-short/serial-le3d-st3.log +++ b/tests/regression/d3q19-short/serial-le3d-st3.log @@ -37,7 +37,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.50000e-01 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-le3d-st4.log b/tests/regression/d3q19-short/serial-le3d-st4.log index 9a0b95863..b791ad620 100644 --- a/tests/regression/d3q19-short/serial-le3d-st4.log +++ b/tests/regression/d3q19-short/serial-le3d-st4.log @@ -37,7 +37,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.50000e-01 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-le3d-st5.log b/tests/regression/d3q19-short/serial-le3d-st5.log index 26d4cfd3b..8ab73a5a2 100644 --- a/tests/regression/d3q19-short/serial-le3d-st5.log +++ b/tests/regression/d3q19-short/serial-le3d-st5.log @@ -37,7 +37,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.50000e-01 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-le3d-st6.log b/tests/regression/d3q19-short/serial-le3d-st6.log index 9dc35fc07..de509d613 100644 --- a/tests/regression/d3q19-short/serial-le3d-st6.log +++ b/tests/regression/d3q19-short/serial-le3d-st6.log @@ -37,7 +37,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.50000e-01 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-le3d-st7.log b/tests/regression/d3q19-short/serial-le3d-st7.log index c74fa4509..3fec43165 100644 --- a/tests/regression/d3q19-short/serial-le3d-st7.log +++ b/tests/regression/d3q19-short/serial-le3d-st7.log @@ -42,7 +42,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.50000e-01 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-le3d-st8.log b/tests/regression/d3q19-short/serial-le3d-st8.log index 2a7afb620..5438ce313 100644 --- a/tests/regression/d3q19-short/serial-le3d-st8.log +++ b/tests/regression/d3q19-short/serial-le3d-st8.log @@ -37,7 +37,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.50000e-01 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-muex-st1.inp b/tests/regression/d3q19-short/serial-muex-st1.inp index 268454892..17f536d04 100644 --- a/tests/regression/d3q19-short/serial-muex-st1.inp +++ b/tests/regression/d3q19-short/serial-muex-st1.inp @@ -41,6 +41,7 @@ ghost_modes off ############################################################################### free_energy symmetric +fe_force_method phi_gradmu A -0.00625 B 0.00625 @@ -54,7 +55,6 @@ grad_mu 0.00001_0.00002_0.00003 fd_gradient_calculation 3d_27pt_fluid fd_advection_scheme_order 1 -fd_force_divergence 0 # External chemical potential requires grad_mu method at the moment diff --git a/tests/regression/d3q19-short/serial-muex-st1.log b/tests/regression/d3q19-short/serial-muex-st1.log index d544e9578..c112cf6e1 100644 --- a/tests/regression/d3q19-short/serial-muex-st1.log +++ b/tests/regression/d3q19-short/serial-muex-st1.log @@ -37,7 +37,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.25000e+00 Order parameter noise = off -Force calculation: phi grad mu method +Force calculation: phi_gradmu System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-spin-c02.log b/tests/regression/d3q19-short/serial-spin-c02.log index bae6c7e0e..f09b58472 100644 --- a/tests/regression/d3q19-short/serial-spin-c02.log +++ b/tests/regression/d3q19-short/serial-spin-c02.log @@ -35,7 +35,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 4.50000e-01 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-spin-fd1.log b/tests/regression/d3q19-short/serial-spin-fd1.log index d0a24498a..998c2e65c 100644 --- a/tests/regression/d3q19-short/serial-spin-fd1.log +++ b/tests/regression/d3q19-short/serial-spin-fd1.log @@ -35,7 +35,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.25000e+00 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-spin-fd2.log b/tests/regression/d3q19-short/serial-spin-fd2.log index b51e96d63..65c08e475 100644 --- a/tests/regression/d3q19-short/serial-spin-fd2.log +++ b/tests/regression/d3q19-short/serial-spin-fd2.log @@ -35,7 +35,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.25000e+00 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-spin-n01.log b/tests/regression/d3q19-short/serial-spin-n01.log index 7d8e9b8cd..7a3bb4188 100644 --- a/tests/regression/d3q19-short/serial-spin-n01.log +++ b/tests/regression/d3q19-short/serial-spin-n01.log @@ -35,7 +35,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.25000e+00 Order parameter noise = on -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-spin-n02.log b/tests/regression/d3q19-short/serial-spin-n02.log index a8147dc8e..0e78eac82 100644 --- a/tests/regression/d3q19-short/serial-spin-n02.log +++ b/tests/regression/d3q19-short/serial-spin-n02.log @@ -35,7 +35,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.25000e+00 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-symm-dr1.log b/tests/regression/d3q19-short/serial-symm-dr1.log index 9eb09fae2..f4297f6eb 100644 --- a/tests/regression/d3q19-short/serial-symm-dr1.log +++ b/tests/regression/d3q19-short/serial-symm-dr1.log @@ -35,7 +35,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.25000e+00 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-symm-dr2.log b/tests/regression/d3q19-short/serial-symm-dr2.log index 7afd17608..49add1a36 100644 --- a/tests/regression/d3q19-short/serial-symm-dr2.log +++ b/tests/regression/d3q19-short/serial-symm-dr2.log @@ -35,7 +35,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.25000e+00 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-symm-pat.log b/tests/regression/d3q19-short/serial-symm-pat.log index 9b4a82f43..f7f3b5e13 100644 --- a/tests/regression/d3q19-short/serial-symm-pat.log +++ b/tests/regression/d3q19-short/serial-symm-pat.log @@ -35,7 +35,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.25000e+00 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-tern-st1.log b/tests/regression/d3q19-short/serial-tern-st1.log index feb125024..47340da8e 100644 --- a/tests/regression/d3q19-short/serial-tern-st1.log +++ b/tests/regression/d3q19-short/serial-tern-st1.log @@ -42,7 +42,7 @@ Using Cahn-Hilliard solver: Number of fields = 2 Mobility (phi) = 1.50000e-01 Mobility (psi) = 1.00000e-01 -Force calculation: phi grad mu method +Force calculation: phi_gradmu System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-tern-st2.log b/tests/regression/d3q19-short/serial-tern-st2.log index c1cf4f0ba..ae18debee 100644 --- a/tests/regression/d3q19-short/serial-tern-st2.log +++ b/tests/regression/d3q19-short/serial-tern-st2.log @@ -42,7 +42,7 @@ Using Cahn-Hilliard solver: Number of fields = 2 Mobility (phi) = 1.50000e-01 Mobility (psi) = 1.00000e-01 -Force calculation: phi grad mu method +Force calculation: phi_gradmu System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-tern-st3.log b/tests/regression/d3q19-short/serial-tern-st3.log index c6f81ec0a..4fde8b2a7 100644 --- a/tests/regression/d3q19-short/serial-tern-st3.log +++ b/tests/regression/d3q19-short/serial-tern-st3.log @@ -42,7 +42,7 @@ Using Cahn-Hilliard solver: Number of fields = 2 Mobility (phi) = 1.50000e-01 Mobility (psi) = 1.00000e-01 -Force calculation: phi grad mu method +Force calculation: phi_gradmu System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-tern-st4.log b/tests/regression/d3q19-short/serial-tern-st4.log index 577a73dfd..1cd719979 100644 --- a/tests/regression/d3q19-short/serial-tern-st4.log +++ b/tests/regression/d3q19-short/serial-tern-st4.log @@ -50,7 +50,7 @@ Using Cahn-Hilliard solver: Number of fields = 2 Mobility (phi) = 1.00000e+00 Mobility (psi) = 1.00000e+00 -Force calculation: phi grad mu method +Force calculation: phi_gradmu System properties ---------------- diff --git a/tests/regression/d3q19-short/serial-tern-st5.inp b/tests/regression/d3q19-short/serial-tern-st5.inp index 97f2dccda..6dbd2412a 100644 --- a/tests/regression/d3q19-short/serial-tern-st5.inp +++ b/tests/regression/d3q19-short/serial-tern-st5.inp @@ -59,7 +59,6 @@ ternary_initialisation 2d_tee hydrodynamics yes fd_gradient_calculation 2d_ternary_solid -fd_force_divergence 0 ############################################################################### # diff --git a/tests/regression/d3q19-short/serial-tern-st5.log b/tests/regression/d3q19-short/serial-tern-st5.log index 8fc93fd7c..2dbc96508 100644 --- a/tests/regression/d3q19-short/serial-tern-st5.log +++ b/tests/regression/d3q19-short/serial-tern-st5.log @@ -49,7 +49,7 @@ Using Cahn-Hilliard solver: Number of fields = 2 Mobility (phi) = 1.00000e+00 Mobility (psi) = 1.00000e+00 -Force calculation: phi grad mu method +Force calculation: phi_gradmu System properties ---------------- From a4c8cca8a171adde4ece792407a481cd5e6bdc4c Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 18 Oct 2022 17:13:12 +0100 Subject: [PATCH 051/244] Typo correction --- tests/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Makefile b/tests/Makefile index a576b09b1..15b5d0526 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -47,7 +47,7 @@ d3q19-io: $(MAKE) -C regression/d3q19-io d3q19-elec: - $(Make) -C regression/d3q19-elec + $(MAKE) -C regression/d3q19-elec # GPU target From 60847a07855eb15e3113f0dd35be9e5ae2b67929 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 18 Oct 2022 17:19:17 +0100 Subject: [PATCH 052/244] Force method infrastructure --- src/fe_force_method.c | 100 ++++++++++++++++++++++++++++++++++++ src/fe_force_method.h | 35 +++++++++++++ src/fe_force_method_rt.c | 106 +++++++++++++++++++++++++++++++++++++++ src/fe_force_method_rt.h | 25 +++++++++ 4 files changed, 266 insertions(+) create mode 100644 src/fe_force_method.c create mode 100644 src/fe_force_method.h create mode 100644 src/fe_force_method_rt.c create mode 100644 src/fe_force_method_rt.h diff --git a/src/fe_force_method.c b/src/fe_force_method.c new file mode 100644 index 000000000..1d255742b --- /dev/null +++ b/src/fe_force_method.c @@ -0,0 +1,100 @@ +/***************************************************************************** + * + * fe_force_method.c + * + * Utility to describe the implementation of the force from the free + * energy sector. + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Contributing author: + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include +#include + +#include "fe_force_method.h" + +/***************************************************************************** + * + * fe_force_method_default + * + *****************************************************************************/ + +fe_force_method_enum_t fe_force_method_default(void) { + + return FE_FORCE_METHOD_STRESS_DIVERGENCE; +} + +/***************************************************************************** + * + * fe_force_method_to_enum + * + *****************************************************************************/ + +fe_force_method_enum_t fe_force_method_to_enum(const char * method) { + + fe_force_method_enum_t imethod = FE_FORCE_METHOD_INVALID; + + assert(method); + + if (strcmp(method, "stress_divergence") == 0) { + imethod = FE_FORCE_METHOD_STRESS_DIVERGENCE; + } + else if (strcmp(method, "phi_gradmu") == 0) { + imethod = FE_FORCE_METHOD_PHI_GRADMU; + } + else if (strcmp(method, "phi_gradmu_correction") == 0) { + imethod = FE_FORCE_METHOD_PHI_GRADMU_CORRECTION; + } + else if (strcmp(method, "relaxation_symmetric") == 0) { + imethod = FE_FORCE_METHOD_RELAXATION_SYMM; + } + else if (strcmp(method, "relaxation_antisymmetric") == 0) { + imethod = FE_FORCE_METHOD_RELAXATION_ANTI; + } + + return imethod; +} + +/***************************************************************************** + * + * fe_force_method_to_string + * + *****************************************************************************/ + +const char * fe_force_method_to_string(fe_force_method_enum_t method) { + + const char * mstring = "Invalid"; + + switch (method) { + case (FE_FORCE_METHOD_NO_FORCE): + mstring = "no_force"; + break; + case (FE_FORCE_METHOD_STRESS_DIVERGENCE): + mstring = "stress_divergence"; + break; + case (FE_FORCE_METHOD_PHI_GRADMU): + mstring = "phi_gradmu"; + break; + case (FE_FORCE_METHOD_PHI_GRADMU_CORRECTION): + mstring = "phi_gradmu_correction"; + break; + case (FE_FORCE_METHOD_RELAXATION_SYMM): + mstring = "relaxation_symmetric"; + break; + case (FE_FORCE_METHOD_RELAXATION_ANTI): + mstring = "relaxation_antisymmetric"; + break; + default: + mstring = "Not found"; + } + + return mstring; +} diff --git a/src/fe_force_method.h b/src/fe_force_method.h new file mode 100644 index 000000000..15f5c0870 --- /dev/null +++ b/src/fe_force_method.h @@ -0,0 +1,35 @@ +/***************************************************************************** + * + * fe_force_method.h + * + * For implementation of force from free energy sector. + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Contributing author: + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#ifndef LUDWIG_FE_FORCE_METHOD_H +#define LUDWIG_FE_FORCE_METHOD_H + +typedef enum { + FE_FORCE_METHOD_INVALID, + FE_FORCE_METHOD_NO_FORCE, /* Usually no hydrodynamics */ + FE_FORCE_METHOD_STRESS_DIVERGENCE, /* Current default */ + FE_FORCE_METHOD_PHI_GRADMU, /* Simple phi grad mu version */ + FE_FORCE_METHOD_PHI_GRADMU_CORRECTION, /* Version with conservation */ + FE_FORCE_METHOD_RELAXATION_SYMM, /* Via LB collision */ + FE_FORCE_METHOD_RELAXATION_ANTI, /* Antisymmetric case */ + FE_FORCE_METHOD_MAX +} fe_force_method_enum_t; + +fe_force_method_enum_t fe_force_method_default(void); +fe_force_method_enum_t fe_force_method_to_enum(const char * string); +const char * fe_force_method_to_string(fe_force_method_enum_t method); + +#endif diff --git a/src/fe_force_method_rt.c b/src/fe_force_method_rt.c new file mode 100644 index 000000000..88502a299 --- /dev/null +++ b/src/fe_force_method_rt.c @@ -0,0 +1,106 @@ +/***************************************************************************** + * + * fe_force_method_rt + * + * Map the input onto the method to implement the thermodynamic force + * on the fluid (and potentially colloids). + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Contributing authors: + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include +#include + +#include "fe_force_method_rt.h" + +/***************************************************************************** + * + * fe_force_method_rt + * + * Return one of phi_force_stress enum type depending on key + * "force_method" + * + * If no valid value is present, the input method is unchanged. + * + *****************************************************************************/ + +int fe_force_method_rt(rt_t * rt, rt_enum_t lv, + fe_force_method_enum_t * method) { + + int key_present = 0; + + assert(rt); + assert(method); + + key_present = rt_key_present(rt, "fe_force_method"); + + if (key_present) { + char value[BUFSIZ] = {0}; + fe_force_method_enum_t imethod = FE_FORCE_METHOD_INVALID; + + rt_string_parameter(rt, "fe_force_method", value, BUFSIZ); + imethod = fe_force_method_to_enum(value); + + if (imethod != FE_FORCE_METHOD_INVALID) { + *method = imethod; + } + else { + rt_vinfo(rt, lv, "Input file: fe_force_method %s\n", value); + rt_vinfo(rt, lv, "Input file: not recognised\n"); + /* PENDING UPDATE TO ft_fatal() */ + rt_vinfo(rt, lv, "Please check and try again\n"); + } + } + + return key_present; +} + +/***************************************************************************** + * + * fe_force_methid_rt_messages + * + * Messages for various conditions which are always in force. + * Returns 0 if no messages were issued. + * + *****************************************************************************/ + +int fe_force_method_rt_messages(rt_t * rt, rt_enum_t lv) { + + int ifail = 0; + assert(rt); + + /* V 0.19.0 */ + /* fd_force_divergence is now replaced by fe_force_method */ + + if (rt_key_present(rt, "fd_force_divergence")) { + + rt_vinfo(rt, lv, "Input file contains key: fd_force_divergence\n"); + rt_vinfo(rt, lv, "This should be replaced by \"fe_force_method\"\n"); + rt_vinfo(rt, lv, "See https://ludwig.epcc.ed.ac.uk/inputs/force.html\n"); + /* PENDING REPLACEMENT BY rt_fatal() */ + rt_vinfo(rt, lv, "Please check and try again\n"); + ifail = -1; + } + + /* V 0.19.0 */ + /* fe_use_stress_relaxation also replaced by fe_force_method */ + + if (rt_key_present(rt, "fe_use_stress_relaxation")) { + rt_vinfo(rt, lv, "Input file contains key: fe_use_stress_relaxation\n"); + rt_vinfo(rt, lv, "This should be replaced by \"fe_force_method\"\n"); + rt_vinfo(rt, lv, "See https://ludwig.epcc.ed.ac.uk/inputs/force.html\n"); + /* PENDING REPLACEMENT BY rt_fatal() */ + rt_vinfo(rt, lv, "Please check and try again\n"); + ifail = -2; + } + + return ifail; +} diff --git a/src/fe_force_method_rt.h b/src/fe_force_method_rt.h new file mode 100644 index 000000000..4e20ee0cf --- /dev/null +++ b/src/fe_force_method_rt.h @@ -0,0 +1,25 @@ +/***************************************************************************** + * + * fe_force_method_rt.h + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Contributing author: + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#ifndef FE_LUDWIG_FE_FORCE_METHOD_RT +#define FE_LUDWIG_FE_FORCE_METHOD_RT + +#include "runtime.h" +#include "fe_force_method.h" + +int fe_force_method_rt(rt_t * rt, rt_enum_t lv, fe_force_method_enum_t * meth); +int fe_force_method_rt_messages(rt_t * rt, rt_enum_t lv); + +#endif From 48bb1252ca73ff829b1510526926aadefe4d0672 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 18 Oct 2022 17:20:04 +0100 Subject: [PATCH 053/244] Add force input changes --- CHANGES.md | 9 +++++++++ version.h | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index f83e484d4..bf68716a4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,15 @@ ### Changes +version 0.19.0 + +- Input associated with the choice of force arising from the free energy + sector has been made more general. Specifically, input keys + `fd_force_divergence` and `fe_use_stress_relaxation` are replaced. + - For scalar order parameters an extra version of the "phi_gradmu" + approach is avaialble" "phi_gradmu_correction". + - See https://ludwig.epcc.ed.ac.uk/inputs/force.html for details. + version 0.18.0 - Added a lubrication correction offset to allow an option for keeping diff --git a/version.h b/version.h index e50d861e8..791a2701c 100644 --- a/version.h +++ b/version.h @@ -13,7 +13,7 @@ #define LUDWIG_VERSION_H #define LUDWIG_MAJOR_VERSION 0 -#define LUDWIG_MINOR_VERSION 18 +#define LUDWIG_MINOR_VERSION 19 #define LUDWIG_PATCH_VERSION 0 #endif From 89a6a29169e5be656f568ba7df32167f521259f4 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 18 Oct 2022 19:09:17 +0100 Subject: [PATCH 054/244] Fix fsancf return value alert --- tests/unit/test_io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_io.c b/tests/unit/test_io.c index 449d6fc93..3e666d7c4 100644 --- a/tests/unit/test_io.c +++ b/tests/unit/test_io.c @@ -256,8 +256,8 @@ int test_io_read3(FILE * fp, int index, void * self) { n = fscanf(fp, "%d\n", &indata); - test_assert(n == 1); - test_assert(indata == data->iref*index); + assert(n == 1); + if (n == 1) test_assert(indata == data->iref*index); return n; } From 8965e7b4a90c25db995136d19878a7810ab92439 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 18 Oct 2022 19:20:23 +0100 Subject: [PATCH 055/244] Fix sscanf alert --- util/polarizer.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/util/polarizer.c b/util/polarizer.c index fcc890025..f188d35bd 100644 --- a/util/polarizer.c +++ b/util/polarizer.c @@ -374,10 +374,15 @@ void read_data(int argc, char** argv, const options_t * opts, double diry = 0.0; double dirz = 0.0; - sscanf(line, "%le %le %le", &dirx, &diry, &dirz); - sys->dir[i][j][k][0] = dirx; - sys->dir[i][j][k][1] = diry; - sys->dir[i][j][k][2] = dirz; + int ns = sscanf(line, "%le %le %le", &dirx, &diry, &dirz); + if (ns == 3) { + sys->dir[i][j][k][0] = dirx; + sys->dir[i][j][k][1] = diry; + sys->dir[i][j][k][2] = dirz; + } + else { + printf("Incorrect dirx diry dirz\n"); + } } } } From 422325652df77365727f052248386709d4735c24 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 18 Oct 2022 19:29:55 +0100 Subject: [PATCH 056/244] Fix sscanf alert --- util/extract.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/util/extract.c b/util/extract.c index bde76c8f5..b3dbfab44 100644 --- a/util/extract.c +++ b/util/extract.c @@ -651,16 +651,21 @@ void read_meta_data_file(const char * filename, metadata_v1_t * meta) { int read_data_file_name(const char * filename) { - int ntime = -1; - const char * tmp; + const char * tmp = NULL; tmp = strchr(filename, '-'); if (tmp) { - sscanf(tmp+1, "%d.", &ntime); + int ntime = -1; + int ns = sscanf(tmp+1, "%d.", &ntime); + if (ns < 1) { + printf("Could not determine time from %s\n", filename); + } + else { + return ntime; + } } - assert (ntime >= 0); - return ntime; + return -1; } /**************************************************************************** From 80fb2d1fd1d5ab8d52b9c0fee4b621a09dabc8ba Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 19 Oct 2022 11:51:04 +0100 Subject: [PATCH 057/244] Add missing no_force --- src/fe_force_method.c | 5 ++++- tests/unit/test_fe_force_method.c | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/fe_force_method.c b/src/fe_force_method.c index 1d255742b..e6742045f 100644 --- a/src/fe_force_method.c +++ b/src/fe_force_method.c @@ -44,7 +44,10 @@ fe_force_method_enum_t fe_force_method_to_enum(const char * method) { assert(method); - if (strcmp(method, "stress_divergence") == 0) { + if (strcmp(method, "no_force") == 0) { + imethod = FE_FORCE_METHOD_NO_FORCE; + } + else if (strcmp(method, "stress_divergence") == 0) { imethod = FE_FORCE_METHOD_STRESS_DIVERGENCE; } else if (strcmp(method, "phi_gradmu") == 0) { diff --git a/tests/unit/test_fe_force_method.c b/tests/unit/test_fe_force_method.c index be55bcaa0..2f5df37a7 100644 --- a/tests/unit/test_fe_force_method.c +++ b/tests/unit/test_fe_force_method.c @@ -72,6 +72,9 @@ int test_fe_force_method_to_enum(void) { int ifail = 0; fe_force_method_enum_t method = FE_FORCE_METHOD_INVALID; + method = fe_force_method_to_enum("no_force"); + assert(method == FE_FORCE_METHOD_NO_FORCE); + method = fe_force_method_to_enum("stress_divergence"); assert(method == FE_FORCE_METHOD_STRESS_DIVERGENCE); From 4aef40f9cac84811a2910be01f38d787ee0a797a Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 19 Oct 2022 11:51:17 +0100 Subject: [PATCH 058/244] Bug fix --- src/phi_grad_mu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/phi_grad_mu.c b/src/phi_grad_mu.c index 80efd30e0..01a9c1073 100644 --- a/src/phi_grad_mu.c +++ b/src/phi_grad_mu.c @@ -209,7 +209,6 @@ __host__ int phi_grad_mu_correction(cs_t * cs, field_t * phi, fe_t * fe, size_t sz = 4*sizeof(double); double * flocal_d = NULL; /* Local contribution to correction */ - double ftotal[4] = {0}; /* (volume, fx, fy, fz) */ fe_t * fe_target = NULL; kernel_info_t limits = {0}; @@ -252,6 +251,7 @@ __host__ int phi_grad_mu_correction(cs_t * cs, field_t * phi, fe_t * fe, /* The volume ftotal[0] should be an integer number of fluid sites, * but is accumulated as a double ... */ + double ftotal[4] = {0}; /* (volume, fx, fy, fz) */ MPI_Comm comm = MPI_COMM_NULL; cs_cart_comm(cs, &comm); @@ -273,7 +273,7 @@ __host__ int phi_grad_mu_correction(cs_t * cs, field_t * phi, fe_t * fe, } } - tdpAssert(tdpFree(&flocal_d)); + tdpAssert(tdpFree(flocal_d)); kernel_ctxt_free(ctxt); return 0; From 75731fdd5b279ea0b01049a780ab7826e2914a5e Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 19 Oct 2022 18:45:12 +0100 Subject: [PATCH 059/244] Fix one alert --- util/extract.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/util/extract.c b/util/extract.c index b3dbfab44..87905b9ff 100644 --- a/util/extract.c +++ b/util/extract.c @@ -58,7 +58,8 @@ #include #include -#include "../src/util.h" +#include "util.h" +#include "util_fopen.h" #define MAXNTOTAL 1024 /* Maximum linear system size */ @@ -294,7 +295,7 @@ int extract_driver(const char * filename, metadata_v1_t * meta, int version) { } else { sprintf(io_data, "q-%8.8d", ntime); - fp_data = fopen(io_data, "w+b"); + fp_data = util_fopen(io_data, "w+b"); if (fp_data == NULL) { printf("fopen(%s) failed\n", io_data); exit(-1); From 6f49d9ad66a3063fb082bd04e7fc474d28d2d739 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 19 Oct 2022 18:58:31 +0100 Subject: [PATCH 060/244] Should create extra alert --- util/extract.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/util/extract.c b/util/extract.c index 87905b9ff..2a2420a50 100644 --- a/util/extract.c +++ b/util/extract.c @@ -332,11 +332,13 @@ int extract_driver(const char * filename, metadata_v1_t * meta, int version) { FILENAME_MAX/2 - strnlen(meta->stub, FILENAME_MAX/2-1) - 1); snprintf(io_data, sizeof(io_data), "%s-%8.8d", tmp, ntime); } + /* KEVIN remove code above with a constant string from allow list */ + /* e.g., sprintf(io_data, "phi-%s-%8.8d.%s", ntime, suffix) */ if (output_vtk_ == 1 || output_vtk_ == 2) { strcat(io_data, suf); - fp_data = fopen(io_data, "w+b"); + fp_data = util_fopen(io_data, "w+b"); if (fp_data == NULL) printf("fopen(%s) failed\n", io_data); printf("\nWriting result to %s\n", io_data); From 7a59d97173f287ffd746c4ce6f2ad31c13183daa Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 19 Oct 2022 19:07:29 +0100 Subject: [PATCH 061/244] One fix --- util/extract.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/util/extract.c b/util/extract.c index 2a2420a50..f6c9c7ec3 100644 --- a/util/extract.c +++ b/util/extract.c @@ -332,8 +332,6 @@ int extract_driver(const char * filename, metadata_v1_t * meta, int version) { FILENAME_MAX/2 - strnlen(meta->stub, FILENAME_MAX/2-1) - 1); snprintf(io_data, sizeof(io_data), "%s-%8.8d", tmp, ntime); } - /* KEVIN remove code above with a constant string from allow list */ - /* e.g., sprintf(io_data, "phi-%s-%8.8d.%s", ntime, suffix) */ if (output_vtk_ == 1 || output_vtk_ == 2) { @@ -358,7 +356,7 @@ int extract_driver(const char * filename, metadata_v1_t * meta, int version) { } else { - fp_data = fopen(io_data, "w+b"); + fp_data = util_fopen(io_data, "w+b"); if (fp_data == NULL) printf("fopen(%s) failed\n", io_data); printf("\nWriting result to %s\n", io_data); From a4b008c717c7742789002c0420807b2dc58da62a Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 19 Oct 2022 19:16:30 +0100 Subject: [PATCH 062/244] Replace fopen() --- util/extract.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/extract.c b/util/extract.c index f6c9c7ec3..08e1dd9a1 100644 --- a/util/extract.c +++ b/util/extract.c @@ -409,7 +409,7 @@ int read_version2(int ntime, metadata_v1_t * meta, double * datasection) { meta->nio, n); printf("-> %s\n", io_data); - fp_data = fopen(io_data, "r+b"); + fp_data = util_fopen(io_data, "r+b"); if (fp_data == NULL) printf("fopen(%s) failed\n", io_data); /* Read data file based on offsets recorded in the metadata, From 119a1f1d988d7ae82cafd36feeaacfd6ca5eed58 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 19 Oct 2022 19:24:37 +0100 Subject: [PATCH 063/244] Replace fopen() --- util/extract.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/extract.c b/util/extract.c index 08e1dd9a1..5f75a758d 100644 --- a/util/extract.c +++ b/util/extract.c @@ -470,7 +470,7 @@ int read_version1(int ntime, metadata_v1_t * meta, double * datasection) { meta->stub, meta->nio, n); printf("Reading metadata file ... %s ", io_metadata); - fp_metadata = fopen(io_metadata, "r"); + fp_metadata = util_fopen(io_metadata, "r"); if (fp_metadata == NULL) printf("fopen(%s) failed\n", io_metadata); for (p = 0; p < 12; p++) { From 1a0e146f9f78d819f8e0e7982fa2d825b6245ab8 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 19 Oct 2022 19:35:26 +0100 Subject: [PATCH 064/244] Replace fopen() --- util/extract.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/extract.c b/util/extract.c index 5f75a758d..1584c8028 100644 --- a/util/extract.c +++ b/util/extract.c @@ -485,7 +485,7 @@ int read_version1(int ntime, metadata_v1_t * meta, double * datasection) { ntime, meta->nio, n); printf("-> %s\n", io_data); - fp_data = fopen(io_data, "r+b"); + fp_data = util_fopen(io_data, "r+b"); if (fp_data == NULL) printf("fopen(%s) failed\n", io_data); /* Read data file based on offsets recorded in the metadata, From a222ff5cda899ee367497a2a57fdb4e6df690d25 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 19 Oct 2022 19:45:13 +0100 Subject: [PATCH 065/244] Replace fopen() --- util/extract.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/extract.c b/util/extract.c index 1584c8028..30abf3b00 100644 --- a/util/extract.c +++ b/util/extract.c @@ -536,7 +536,7 @@ void read_meta_data_file(const char * filename, metadata_v1_t * meta) { assert(filename); assert(meta); - fp_meta = fopen(filename, "r"); + fp_meta = util_fopen(filename, "r"); if (fp_meta == NULL) { printf("fopen(%s) failed\n", filename); exit(-1); From 834a26076f614adc74129c095cd42524a3995b09 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 19 Oct 2022 19:55:37 +0100 Subject: [PATCH 066/244] Replace one fopen() --- util/extract.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/extract.c b/util/extract.c index 30abf3b00..9f0ada9c8 100644 --- a/util/extract.c +++ b/util/extract.c @@ -1085,7 +1085,7 @@ int write_qab_vtk(int ntime, int ntarget[3], double * datasection) { /* Scalar order */ if (output_lcs_) { sprintf(io_data, "lcs-%8.8d.vtk", ntime); - fp_data = fopen(io_data, "w"); + fp_data = util_fopen(io_data, "w"); if (fp_data == NULL) { printf("fopen(%s) failed\n", io_data); From 7289de97e3ead59addf8b451e071796abec14a89 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 19 Oct 2022 20:04:36 +0100 Subject: [PATCH 067/244] Pending comment --- util/extract.c | 1 + 1 file changed, 1 insertion(+) diff --git a/util/extract.c b/util/extract.c index 9f0ada9c8..2caa1f46f 100644 --- a/util/extract.c +++ b/util/extract.c @@ -202,6 +202,7 @@ int main(int argc, char ** argv) { exit(EXIT_FAILURE); } + /* PENDING KEVIN uncontrolled path name */ read_meta_data_file(argv[optind], &metadata); extract_driver(argv[optind+1], &metadata, version); From 3293fc37959ec6536068f08ca34d16e00b71feb5 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 19 Oct 2022 20:17:54 +0100 Subject: [PATCH 068/244] Remaining fopen() replaced --- util/extract.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/extract.c b/util/extract.c index 2caa1f46f..c1a23e301 100644 --- a/util/extract.c +++ b/util/extract.c @@ -1106,7 +1106,7 @@ int write_qab_vtk(int ntime, int ntarget[3], double * datasection) { if (output_lcd_) { sprintf(io_data, "lcd-%8.8d.vtk", ntime); - fp_data = fopen(io_data, "w"); + fp_data = util_fopen(io_data, "w"); if (fp_data == NULL) { printf("fopen(%s) failed\n", io_data); @@ -1126,7 +1126,7 @@ int write_qab_vtk(int ntime, int ntarget[3], double * datasection) { if (output_lcx_) { sprintf(io_data, "lcb-%8.8d.vtk", ntime); - fp_data = fopen(io_data, "w"); + fp_data = util_fopen(io_data, "w"); if (fp_data == NULL) { printf("fopen(%s) failed\n", io_data); From d288c4d8e9e935d19fde131938ebebd1e590ab19 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 20 Oct 2022 16:41:00 +0100 Subject: [PATCH 069/244] Update input for electrokinetic free energies --- src/ludwig.c | 70 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 18 deletions(-) diff --git a/src/ludwig.c b/src/ludwig.c index 3578a4b2c..f73fd53ba 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -1863,18 +1863,35 @@ int free_energy_init_rt(ludwig_t * ludwig) { else if(strcmp(description, "fe_electro") == 0) { fe_electro_t * fe = NULL; + fe_force_method_enum_t method = fe_force_method_default(); + int psi_method = PSI_FORCE_NONE; + nk = 2; /* Number of charge densities always 2 for now */ + nhalo = 2; /* Default to stress divergence (see force). */ /* Single fluid electrokinetic free energy */ - /* Default method is divergence of stress tensor */ - p = 1; - nhalo = 2; - rt_int_parameter(rt, "fd_force_divergence", &p); + /* Force */ + + { + fe_force_method_rt_messages(rt, RT_INFO); + fe_force_method_rt(rt, RT_FATAL, &method); - if (p == 0) nhalo = 1; - if (p == 1) nhalo = 2; + /* The following are supported */ + switch (method) { + case FE_FORCE_METHOD_PHI_GRADMU_CORRECTION: + nhalo = 1; + psi_method = PSI_FORCE_GRADMU; + break; + case FE_FORCE_METHOD_STRESS_DIVERGENCE: + nhalo = 2; + psi_method = PSI_FORCE_DIVERGENCE; + break; + default: + pe_fatal(pe, "electrokinetic: force_method not available\n"); + } + } cs_nhalo_set(cs, nhalo); coords_init_rt(pe, rt, cs); @@ -1891,11 +1908,10 @@ int free_energy_init_rt(ludwig_t * ludwig) { psi_create(pe, cs, nk, &ludwig->psi); psi_rt_init_param(pe, rt, ludwig->psi); + psi_force_method_set(ludwig->psi, psi_method); - pe_info(pe, "Force calculation: %s\n", - (p == 0) ? "psi grad mu method" : "Divergence method"); - if (p == 0) psi_force_method_set(ludwig->psi, PSI_FORCE_GRADMU); - if (p == 1) psi_force_method_set(ludwig->psi, PSI_FORCE_DIVERGENCE); + pe_info(pe, "Force calculation: %s\n", + fe_force_method_to_string(method)); /* Create FE objects and set function pointers */ fe_electro_create(pe, ludwig->psi, &fe); @@ -1964,14 +1980,6 @@ int free_energy_init_rt(ludwig_t * ludwig) { fe_electro_create(pe, ludwig->psi, &fe_elec); - /* Default method is divergence of stress tensor */ - p = 1; - rt_int_parameter(rt, "fd_force_divergence", &p); - pe_info(pe, "Force calculation: %s\n", - (p == 0) ? "psi grad mu method" : "Divergence method"); - if (p == 0) psi_force_method_set(ludwig->psi, PSI_FORCE_GRADMU); - if (p == 1) psi_force_method_set(ludwig->psi, PSI_FORCE_DIVERGENCE); - /* Coupling part */ pe_info(pe, "\n"); @@ -2019,6 +2027,32 @@ int free_energy_init_rt(ludwig_t * ludwig) { (e1 == e2) ? "uniform" : "heterogeneous"); if (e1 != e2) ludwig->epsilon = (f_vare_t) fe_es_var_epsilon; + /* Force */ + + { + fe_force_method_enum_t method = fe_force_method_default(); + + fe_force_method_rt_messages(rt, RT_INFO); + fe_force_method_rt(rt, RT_FATAL, &method); + + /* The following are supported */ + switch (method) { + case FE_FORCE_METHOD_PHI_GRADMU_CORRECTION: + psi_force_method_set(ludwig->psi, PSI_FORCE_GRADMU); + break; + case FE_FORCE_METHOD_STRESS_DIVERGENCE: + psi_force_method_set(ludwig->psi, PSI_FORCE_DIVERGENCE); + break; + default: + pe_fatal(pe, "electrosymmetric: force_method not available\n"); + } + + pe_info(pe, "\n"); + pe_info(pe, "Coupled free energy\n"); + pe_info(pe, "Force calculation: %s\n", + fe_force_method_to_string(method)); + } + ludwig->fe_symm = fe_symm; ludwig->fe = (fe_t *) fes; From 9ab5e0a8f04046c81c9f16f5f019ad70c1a67bf2 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 20 Oct 2022 16:43:07 +0100 Subject: [PATCH 070/244] Update input keys for force --- tests/regression/d3q19-elec/serial-elec-do1.inp | 2 -- tests/regression/d3q19-elec/serial-elec-do2.inp | 3 +-- tests/regression/d3q19-elec/serial-elec-do3.inp | 2 -- tests/regression/d3q19-elec/serial-elec-dr1.inp | 2 -- tests/regression/d3q19-elec/serial-elec-dr2.inp | 3 +-- tests/regression/d3q19-elec/serial-elec-eo1.inp | 3 +-- tests/regression/d3q19-elec/serial-elec-eo2.inp | 4 ++-- tests/regression/d3q19-elec/serial-elec-ep1.inp | 2 -- tests/regression/d3q19-elec/serial-elec-ep2.inp | 4 ++-- tests/regression/d3q19-elec/serial-elec-gc1.inp | 4 ++-- tests/regression/d3q19-elec/serial-elec-gc2.inp | 4 ++-- tests/regression/d3q19-elec/serial-elec-rr1.inp | 3 ++- tests/regression/d3q19-elec/serial-elec-rr2.inp | 2 +- tests/regression/d3q19-elec/serial-elec-rr3.inp | 2 +- tests/regression/d3q19-elec/serial-elec-rr4.inp | 2 +- tests/regression/d3q19-elec/serial-rest-ec1.inp | 3 ++- tests/regression/d3q19-elec/serial-rest-ec2.inp | 2 +- 17 files changed, 19 insertions(+), 28 deletions(-) diff --git a/tests/regression/d3q19-elec/serial-elec-do1.inp b/tests/regression/d3q19-elec/serial-elec-do1.inp index b954c4026..263911c3a 100644 --- a/tests/regression/d3q19-elec/serial-elec-do1.inp +++ b/tests/regression/d3q19-elec/serial-elec-do1.inp @@ -23,7 +23,6 @@ N_cycles 50 size 4_4_256 grid 1_1_1 periodicity 1_1_1 -reduced_halo no ############################################################################## # @@ -49,7 +48,6 @@ free_energy fe_electro_symmetric fd_advection_scheme_order 3 fd_gradient_calculation 3d_7pt_fluid -fd_force_divergence 1 ############################################################################### # diff --git a/tests/regression/d3q19-elec/serial-elec-do2.inp b/tests/regression/d3q19-elec/serial-elec-do2.inp index f7875b529..5c24d8afb 100644 --- a/tests/regression/d3q19-elec/serial-elec-do2.inp +++ b/tests/regression/d3q19-elec/serial-elec-do2.inp @@ -23,7 +23,6 @@ N_cycles 50 size 4_4_256 grid 1_1_1 periodicity 1_1_1 -reduced_halo no ############################################################################## # @@ -46,10 +45,10 @@ temperature 3.3333e-4 ############################################################################### free_energy fe_electro_symmetric +fe_force_method phi_gradmu_correction fd_advection_scheme_order 3 fd_gradient_calculation 3d_7pt_fluid -fd_force_divergence 0 ############################################################################### # diff --git a/tests/regression/d3q19-elec/serial-elec-do3.inp b/tests/regression/d3q19-elec/serial-elec-do3.inp index ef882b9d2..00e1a55df 100644 --- a/tests/regression/d3q19-elec/serial-elec-do3.inp +++ b/tests/regression/d3q19-elec/serial-elec-do3.inp @@ -22,7 +22,6 @@ N_cycles 50 size 4_4_256 grid 1_1_1 periodicity 1_1_1 -reduced_halo no ############################################################################## # @@ -48,7 +47,6 @@ free_energy fe_electro_symmetric fd_advection_scheme_order 3 fd_gradient_calculation 3d_7pt_fluid -fd_force_divergence 1 ############################################################################### # diff --git a/tests/regression/d3q19-elec/serial-elec-dr1.inp b/tests/regression/d3q19-elec/serial-elec-dr1.inp index b6f5d6d7d..e9475a3fc 100644 --- a/tests/regression/d3q19-elec/serial-elec-dr1.inp +++ b/tests/regression/d3q19-elec/serial-elec-dr1.inp @@ -23,7 +23,6 @@ N_cycles 100 size 64_64_2 grid 1_1_1 periodicity 1_1_1 -reduced_halo no ############################################################################## # @@ -49,7 +48,6 @@ free_energy fe_electro_symmetric fd_advection_scheme_order 3 fd_gradient_calculation 3d_7pt_fluid -fd_force_divergence 1 ############################################################################### # diff --git a/tests/regression/d3q19-elec/serial-elec-dr2.inp b/tests/regression/d3q19-elec/serial-elec-dr2.inp index bdbd39463..a42050de1 100644 --- a/tests/regression/d3q19-elec/serial-elec-dr2.inp +++ b/tests/regression/d3q19-elec/serial-elec-dr2.inp @@ -23,7 +23,6 @@ N_cycles 100 size 64_64_2 grid 1_1_1 periodicity 1_1_1 -reduced_halo no ############################################################################## # @@ -46,10 +45,10 @@ temperature 3.3333e-4 ############################################################################### free_energy fe_electro_symmetric +fe_force_method phi_gradmu_correction fd_advection_scheme_order 3 fd_gradient_calculation 3d_7pt_fluid -fd_force_divergence 0 ############################################################################### # diff --git a/tests/regression/d3q19-elec/serial-elec-eo1.inp b/tests/regression/d3q19-elec/serial-elec-eo1.inp index b614fbbe7..3221cb63e 100644 --- a/tests/regression/d3q19-elec/serial-elec-eo1.inp +++ b/tests/regression/d3q19-elec/serial-elec-eo1.inp @@ -16,7 +16,6 @@ N_cycles 1000 size 64_2_2 grid 8_1_1 periodicity 0_1_1 -reduced_halo no ############################################################################## # @@ -49,9 +48,9 @@ electric_e0 0.0_1.0e-6_0.0 ############################################################################### free_energy fe_electro +fe_force_method phi_gradmu_correction fd_advection_scheme_order 3 -fd_force_divergence 0 ############################################################################### # diff --git a/tests/regression/d3q19-elec/serial-elec-eo2.inp b/tests/regression/d3q19-elec/serial-elec-eo2.inp index 889b1af07..f1a8c541a 100644 --- a/tests/regression/d3q19-elec/serial-elec-eo2.inp +++ b/tests/regression/d3q19-elec/serial-elec-eo2.inp @@ -16,7 +16,6 @@ N_cycles 100 size 64_4_4 grid 1_1_1 periodicity 0_1_1 -reduced_halo no ############################################################################## # @@ -47,9 +46,10 @@ electric_e0 0.0_3.0e-2_0.0 ############################################################################### free_energy fe_electro +fe_force_method phi_gradmu_correction fd_advection_scheme_order 3 -fd_force_divergence 0 + ############################################################################### # diff --git a/tests/regression/d3q19-elec/serial-elec-ep1.inp b/tests/regression/d3q19-elec/serial-elec-ep1.inp index 951252d27..bea800561 100644 --- a/tests/regression/d3q19-elec/serial-elec-ep1.inp +++ b/tests/regression/d3q19-elec/serial-elec-ep1.inp @@ -23,7 +23,6 @@ N_cycles 100 size 32_32_32 grid 1_1_1 periodicity 1_1_1 -reduced_halo no ############################################################################## # @@ -48,7 +47,6 @@ temperature 3.3333e-4 free_energy fe_electro fd_advection_scheme_order 3 -fd_force_divergence 1 ############################################################################### # diff --git a/tests/regression/d3q19-elec/serial-elec-ep2.inp b/tests/regression/d3q19-elec/serial-elec-ep2.inp index ee3fe9f3f..bfae7fdf2 100644 --- a/tests/regression/d3q19-elec/serial-elec-ep2.inp +++ b/tests/regression/d3q19-elec/serial-elec-ep2.inp @@ -23,7 +23,6 @@ N_cycles 100 size 32_32_32 grid 1_1_1 periodicity 1_1_1 -reduced_halo no ############################################################################## # @@ -46,9 +45,10 @@ temperature 3.3333e-4 ############################################################################### free_energy fe_electro +fe_force_method phi_gradmu_correction fd_advection_scheme_order 3 -fd_force_divergence 0 + ############################################################################### # diff --git a/tests/regression/d3q19-elec/serial-elec-gc1.inp b/tests/regression/d3q19-elec/serial-elec-gc1.inp index 87dcd6e0e..7cdc37819 100644 --- a/tests/regression/d3q19-elec/serial-elec-gc1.inp +++ b/tests/regression/d3q19-elec/serial-elec-gc1.inp @@ -26,7 +26,7 @@ N_cycles 1000 size 64_4_4 grid 1_1_2 periodicity 1_1_1 -reduced_halo yes +lb_halo_scheme lb_halo_openmp_reduced ############################################################################## # @@ -48,9 +48,9 @@ temperature 3.33333333333333333e-5 ############################################################################### free_energy fe_electro +fe_force_method phi_gradmu_correction fd_advection_scheme_order 3 -fd_force_divergence 0 ############################################################################### # diff --git a/tests/regression/d3q19-elec/serial-elec-gc2.inp b/tests/regression/d3q19-elec/serial-elec-gc2.inp index 763ad4bf8..3b86e9416 100644 --- a/tests/regression/d3q19-elec/serial-elec-gc2.inp +++ b/tests/regression/d3q19-elec/serial-elec-gc2.inp @@ -28,7 +28,7 @@ N_cycles 100 size 64_4_4 grid 1_1_2 periodicity 1_1_1 -reduced_halo yes +lb_halo_scheme lb_halo_openmp_reduced ############################################################################## # @@ -50,9 +50,9 @@ temperature 3.33333333333333333e-5 ############################################################################### free_energy fe_electro +fe_force_method phi_gradmu_correction fd_advection_scheme_order 3 -fd_force_divergence 0 ############################################################################### # diff --git a/tests/regression/d3q19-elec/serial-elec-rr1.inp b/tests/regression/d3q19-elec/serial-elec-rr1.inp index bb9f78b94..91b55d2d1 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr1.inp +++ b/tests/regression/d3q19-elec/serial-elec-rr1.inp @@ -27,9 +27,10 @@ isothermal_fluctuations off temperature 0.00001 free_energy fe_electro +fe_force_method phi_gradmu_correction fd_advection_scheme_order 2 -fd_force_divergence 0 + ############################################################################### # diff --git a/tests/regression/d3q19-elec/serial-elec-rr2.inp b/tests/regression/d3q19-elec/serial-elec-rr2.inp index 785c4734d..8b2a7d6d5 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr2.inp +++ b/tests/regression/d3q19-elec/serial-elec-rr2.inp @@ -27,9 +27,9 @@ isothermal_fluctuations off temperature 0.00001 free_energy fe_electro +fe_force_method phi_gradmu_correction fd_advection_scheme_order 2 -fd_force_divergence 0 ############################################################################### # diff --git a/tests/regression/d3q19-elec/serial-elec-rr3.inp b/tests/regression/d3q19-elec/serial-elec-rr3.inp index 0e58ca5d2..0afd0c030 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr3.inp +++ b/tests/regression/d3q19-elec/serial-elec-rr3.inp @@ -26,9 +26,9 @@ isothermal_fluctuations off temperature 0.00001 free_energy fe_electro +fe_force_method phi_gradmu_correction fd_advection_scheme_order 2 -fd_force_divergence 0 ############################################################################### # diff --git a/tests/regression/d3q19-elec/serial-elec-rr4.inp b/tests/regression/d3q19-elec/serial-elec-rr4.inp index ac77ebccc..94776dfbd 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr4.inp +++ b/tests/regression/d3q19-elec/serial-elec-rr4.inp @@ -26,9 +26,9 @@ isothermal_fluctuations off temperature 0.00001 free_energy fe_electro +fe_force_method phi_gradmu_correction fd_advection_scheme_order 2 -fd_force_divergence 0 ############################################################################### # diff --git a/tests/regression/d3q19-elec/serial-rest-ec1.inp b/tests/regression/d3q19-elec/serial-rest-ec1.inp index f88888cb5..8658d52e6 100644 --- a/tests/regression/d3q19-elec/serial-rest-ec1.inp +++ b/tests/regression/d3q19-elec/serial-rest-ec1.inp @@ -24,9 +24,10 @@ isothermal_fluctuations off temperature 0.00001 free_energy fe_electro +fe_force_method phi_gradmu_correction fd_advection_scheme_order 2 -fd_force_divergence 0 + ############################################################################### # diff --git a/tests/regression/d3q19-elec/serial-rest-ec2.inp b/tests/regression/d3q19-elec/serial-rest-ec2.inp index 6028db447..b2949427b 100644 --- a/tests/regression/d3q19-elec/serial-rest-ec2.inp +++ b/tests/regression/d3q19-elec/serial-rest-ec2.inp @@ -24,9 +24,9 @@ isothermal_fluctuations off temperature 0.00001 free_energy fe_electro +fe_force_method phi_gradmu_correction fd_advection_scheme_order 2 -fd_force_divergence 0 ############################################################################### # From 771e6371dbc7a164b888fa58e7fa32e1abcb87b5 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 20 Oct 2022 16:43:53 +0100 Subject: [PATCH 071/244] Cosmetic updates --- tests/regression/d3q19-elec/serial-elec-do1.log | 6 ++++-- tests/regression/d3q19-elec/serial-elec-do2.log | 6 ++++-- tests/regression/d3q19-elec/serial-elec-do3.log | 6 ++++-- tests/regression/d3q19-elec/serial-elec-dr1.log | 6 ++++-- tests/regression/d3q19-elec/serial-elec-dr2.log | 6 ++++-- tests/regression/d3q19-elec/serial-elec-eo1.log | 4 ++-- tests/regression/d3q19-elec/serial-elec-eo2.log | 4 ++-- tests/regression/d3q19-elec/serial-elec-ep1.log | 4 ++-- tests/regression/d3q19-elec/serial-elec-ep2.log | 4 ++-- tests/regression/d3q19-elec/serial-elec-gc1.log | 4 ++-- tests/regression/d3q19-elec/serial-elec-gc2.log | 4 ++-- tests/regression/d3q19-elec/serial-elec-rr1.log | 4 ++-- tests/regression/d3q19-elec/serial-elec-rr2.log | 4 ++-- tests/regression/d3q19-elec/serial-elec-rr3.log | 4 ++-- tests/regression/d3q19-elec/serial-elec-rr4.log | 4 ++-- tests/regression/d3q19-elec/serial-rest-ec1.log | 2 +- tests/regression/d3q19-elec/serial-rest-ec2.log | 2 +- 17 files changed, 42 insertions(+), 32 deletions(-) diff --git a/tests/regression/d3q19-elec/serial-elec-do1.log b/tests/regression/d3q19-elec/serial-elec-do1.log index b60366e34..7dc44473c 100644 --- a/tests/regression/d3q19-elec/serial-elec-do1.log +++ b/tests/regression/d3q19-elec/serial-elec-do1.log @@ -54,7 +54,6 @@ Absolute tolerance: 1.0000000e-07 Max. no. of iterations: 10000 I/O decomposition: 1 1 1 I/O format: BINARY -Force calculation: Divergence method Coupling part ------------- @@ -66,6 +65,9 @@ Solvation dmu species 0: 4.0000000e+00 Solvation dmu species 1: -4.0000000e+00 Poisson solver: uniform +Coupled free energy +Force calculation: stress_divergence + System properties ---------------- Mean fluid density: 6.00000e+00 @@ -82,7 +84,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 diff --git a/tests/regression/d3q19-elec/serial-elec-do2.log b/tests/regression/d3q19-elec/serial-elec-do2.log index 959b07446..f786c9299 100644 --- a/tests/regression/d3q19-elec/serial-elec-do2.log +++ b/tests/regression/d3q19-elec/serial-elec-do2.log @@ -54,7 +54,6 @@ Absolute tolerance: 1.0000000e-07 Max. no. of iterations: 10000 I/O decomposition: 1 1 1 I/O format: BINARY -Force calculation: psi grad mu method Coupling part ------------- @@ -66,6 +65,9 @@ Solvation dmu species 0: 4.0000000e+00 Solvation dmu species 1: -4.0000000e+00 Poisson solver: uniform +Coupled free energy +Force calculation: phi_gradmu_correction + System properties ---------------- Mean fluid density: 6.00000e+00 @@ -82,7 +84,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 diff --git a/tests/regression/d3q19-elec/serial-elec-do3.log b/tests/regression/d3q19-elec/serial-elec-do3.log index a93258f15..c67762ca5 100644 --- a/tests/regression/d3q19-elec/serial-elec-do3.log +++ b/tests/regression/d3q19-elec/serial-elec-do3.log @@ -54,7 +54,6 @@ Absolute tolerance: 1.0000000e-07 Max. no. of iterations: 10000 I/O decomposition: 1 1 1 I/O format: BINARY -Force calculation: Divergence method Coupling part ------------- @@ -66,6 +65,9 @@ Solvation dmu species 0: 5.0000000e+00 Solvation dmu species 1: -3.0000000e+00 Poisson solver: uniform +Coupled free energy +Force calculation: stress_divergence + System properties ---------------- Mean fluid density: 6.00000e+00 @@ -82,7 +84,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 diff --git a/tests/regression/d3q19-elec/serial-elec-dr1.log b/tests/regression/d3q19-elec/serial-elec-dr1.log index 03f9b8e5c..98f40d07b 100644 --- a/tests/regression/d3q19-elec/serial-elec-dr1.log +++ b/tests/regression/d3q19-elec/serial-elec-dr1.log @@ -54,7 +54,6 @@ Absolute tolerance: 1.0000000e-07 Max. no. of iterations: 2000 I/O decomposition: 1 1 1 I/O format: BINARY -Force calculation: Divergence method Coupling part ------------- @@ -66,6 +65,9 @@ Solvation dmu species 0: 0.0000000e+00 Solvation dmu species 1: 0.0000000e+00 Poisson solver: heterogeneous +Coupled free energy +Force calculation: stress_divergence + System properties ---------------- Mean fluid density: 6.00000e+00 @@ -82,7 +84,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 diff --git a/tests/regression/d3q19-elec/serial-elec-dr2.log b/tests/regression/d3q19-elec/serial-elec-dr2.log index ff1dbce48..8abcb0788 100644 --- a/tests/regression/d3q19-elec/serial-elec-dr2.log +++ b/tests/regression/d3q19-elec/serial-elec-dr2.log @@ -54,7 +54,6 @@ Absolute tolerance: 1.0000000e-07 Max. no. of iterations: 2000 I/O decomposition: 1 1 1 I/O format: BINARY -Force calculation: psi grad mu method Coupling part ------------- @@ -66,6 +65,9 @@ Solvation dmu species 0: 0.0000000e+00 Solvation dmu species 1: 0.0000000e+00 Poisson solver: heterogeneous +Coupled free energy +Force calculation: phi_gradmu_correction + System properties ---------------- Mean fluid density: 6.00000e+00 @@ -82,7 +84,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 diff --git a/tests/regression/d3q19-elec/serial-elec-eo1.log b/tests/regression/d3q19-elec/serial-elec-eo1.log index 4685cd2c8..bd4ed9ee6 100644 --- a/tests/regression/d3q19-elec/serial-elec-eo1.log +++ b/tests/regression/d3q19-elec/serial-elec-eo1.log @@ -38,7 +38,7 @@ Absolute tolerance: 1.1920929e-09 Max. no. of iterations: 10000 I/O decomposition: 1 1 1 I/O format: ASCII -Force calculation: psi grad mu method +Force calculation: phi_gradmu_correction System properties ---------------- @@ -56,7 +56,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 diff --git a/tests/regression/d3q19-elec/serial-elec-eo2.log b/tests/regression/d3q19-elec/serial-elec-eo2.log index a8bac85f4..cc29cd10f 100644 --- a/tests/regression/d3q19-elec/serial-elec-eo2.log +++ b/tests/regression/d3q19-elec/serial-elec-eo2.log @@ -38,7 +38,7 @@ Absolute tolerance: 1.0000000e-09 Max. no. of iterations: 2000 I/O decomposition: 1 1 1 I/O format: BINARY -Force calculation: psi grad mu method +Force calculation: phi_gradmu_correction System properties ---------------- @@ -56,7 +56,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 diff --git a/tests/regression/d3q19-elec/serial-elec-ep1.log b/tests/regression/d3q19-elec/serial-elec-ep1.log index f85e9e161..f2eb0049a 100644 --- a/tests/regression/d3q19-elec/serial-elec-ep1.log +++ b/tests/regression/d3q19-elec/serial-elec-ep1.log @@ -38,7 +38,7 @@ Absolute tolerance: 1.0000000e-15 Max. no. of iterations: 5000 I/O decomposition: 1 1 1 I/O format: BINARY -Force calculation: Divergence method +Force calculation: stress_divergence System properties ---------------- @@ -56,7 +56,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 diff --git a/tests/regression/d3q19-elec/serial-elec-ep2.log b/tests/regression/d3q19-elec/serial-elec-ep2.log index 61d352f48..708519c9b 100644 --- a/tests/regression/d3q19-elec/serial-elec-ep2.log +++ b/tests/regression/d3q19-elec/serial-elec-ep2.log @@ -38,7 +38,7 @@ Absolute tolerance: 1.0000000e-15 Max. no. of iterations: 5000 I/O decomposition: 1 1 1 I/O format: BINARY -Force calculation: psi grad mu method +Force calculation: phi_gradmu_correction System properties ---------------- @@ -56,7 +56,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 diff --git a/tests/regression/d3q19-elec/serial-elec-gc1.log b/tests/regression/d3q19-elec/serial-elec-gc1.log index 70bb7fd3c..da36116ea 100644 --- a/tests/regression/d3q19-elec/serial-elec-gc1.log +++ b/tests/regression/d3q19-elec/serial-elec-gc1.log @@ -38,7 +38,7 @@ Absolute tolerance: 1.1920929e-09 Max. no. of iterations: 10000 I/O decomposition: 1 1 1 I/O format: BINARY -Force calculation: psi grad mu method +Force calculation: phi_gradmu_correction System properties ---------------- @@ -56,7 +56,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: reduced +Halo type: lb_halo_openmp_reduced (host) Input format: binary Output format: binary I/O grid: 1 1 1 diff --git a/tests/regression/d3q19-elec/serial-elec-gc2.log b/tests/regression/d3q19-elec/serial-elec-gc2.log index 142b482e7..905248069 100644 --- a/tests/regression/d3q19-elec/serial-elec-gc2.log +++ b/tests/regression/d3q19-elec/serial-elec-gc2.log @@ -45,7 +45,7 @@ Absolute tolerance: 1.1920929e-09 Max. no. of iterations: 10000 I/O decomposition: 1 1 1 I/O format: BINARY -Force calculation: psi grad mu method +Force calculation: phi_gradmu_correction System properties ---------------- @@ -63,7 +63,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: reduced +Halo type: lb_halo_openmp_reduced (host) Input format: binary Output format: binary I/O grid: 1 1 1 diff --git a/tests/regression/d3q19-elec/serial-elec-rr1.log b/tests/regression/d3q19-elec/serial-elec-rr1.log index a3ab5db0f..72115f699 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr1.log +++ b/tests/regression/d3q19-elec/serial-elec-rr1.log @@ -38,7 +38,7 @@ Absolute tolerance: 1.1920929e-09 Max. no. of iterations: 10000 I/O decomposition: 1 1 1 I/O format: BINARY -Force calculation: psi grad mu method +Force calculation: phi_gradmu_correction System properties ---------------- @@ -56,7 +56,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 diff --git a/tests/regression/d3q19-elec/serial-elec-rr2.log b/tests/regression/d3q19-elec/serial-elec-rr2.log index a4cbfbc23..5a59df0e6 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr2.log +++ b/tests/regression/d3q19-elec/serial-elec-rr2.log @@ -38,7 +38,7 @@ Absolute tolerance: 1.1920929e-09 Max. no. of iterations: 10000 I/O decomposition: 1 1 1 I/O format: BINARY -Force calculation: psi grad mu method +Force calculation: phi_gradmu_correction System properties ---------------- @@ -56,7 +56,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 diff --git a/tests/regression/d3q19-elec/serial-elec-rr3.log b/tests/regression/d3q19-elec/serial-elec-rr3.log index 8224c7a31..bee9c984e 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr3.log +++ b/tests/regression/d3q19-elec/serial-elec-rr3.log @@ -38,7 +38,7 @@ Absolute tolerance: 1.1920929e-09 Max. no. of iterations: 10000 I/O decomposition: 1 1 1 I/O format: BINARY -Force calculation: psi grad mu method +Force calculation: phi_gradmu_correction System properties ---------------- @@ -56,7 +56,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 diff --git a/tests/regression/d3q19-elec/serial-elec-rr4.log b/tests/regression/d3q19-elec/serial-elec-rr4.log index e9a7cb07e..81f572dd5 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr4.log +++ b/tests/regression/d3q19-elec/serial-elec-rr4.log @@ -38,7 +38,7 @@ Absolute tolerance: 1.1920929e-09 Max. no. of iterations: 10000 I/O decomposition: 1 1 1 I/O format: BINARY -Force calculation: psi grad mu method +Force calculation: phi_gradmu_correction System properties ---------------- @@ -56,7 +56,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 diff --git a/tests/regression/d3q19-elec/serial-rest-ec1.log b/tests/regression/d3q19-elec/serial-rest-ec1.log index 7f26f5745..685b61162 100644 --- a/tests/regression/d3q19-elec/serial-rest-ec1.log +++ b/tests/regression/d3q19-elec/serial-rest-ec1.log @@ -38,7 +38,7 @@ Absolute tolerance: 1.1920929e-09 Max. no. of iterations: 10000 I/O decomposition: 1 1 1 I/O format: BINARY -Force calculation: psi grad mu method +Force calculation: phi_gradmu_correction System properties ---------------- diff --git a/tests/regression/d3q19-elec/serial-rest-ec2.log b/tests/regression/d3q19-elec/serial-rest-ec2.log index e837911ff..2f77527ef 100644 --- a/tests/regression/d3q19-elec/serial-rest-ec2.log +++ b/tests/regression/d3q19-elec/serial-rest-ec2.log @@ -38,7 +38,7 @@ Absolute tolerance: 1.1920929e-09 Max. no. of iterations: 10000 I/O decomposition: 1 1 1 I/O format: BINARY -Force calculation: psi grad mu method +Force calculation: phi_gradmu_correction System properties ---------------- From ebaed4af3883fb68e57fe3e6da49b57044adaa95 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 24 Oct 2022 14:57:11 +0100 Subject: [PATCH 072/244] Add comment --- src/phi_grad_mu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/phi_grad_mu.c b/src/phi_grad_mu.c index 01a9c1073..1050ff105 100644 --- a/src/phi_grad_mu.c +++ b/src/phi_grad_mu.c @@ -607,7 +607,7 @@ __global__ void phi_grad_mu_correction_kernel(kernel_ctxt_t * ktx, double force[3] = {0}; double phi[NVECTOR] = {0}; - double mu[NVECTOR+1] = {0}; + double mu[NVECTOR+1] = {0}; /* allow ternary with extra chemical pot. */ field_scalar_array(field, index0, phi); fe->func->mu(fe, index0, mu); From 6b83e004ae9ad9c169196e768c17ea012caacd32 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 24 Oct 2022 14:59:41 +0100 Subject: [PATCH 073/244] Add comments --- src/phi_force.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/phi_force.c b/src/phi_force.c index 23ec6a7d0..5f7065e50 100644 --- a/src/phi_force.c +++ b/src/phi_force.c @@ -119,10 +119,12 @@ __host__ int phi_force_calculation(pe_t * pe, cs_t * cs, lees_edw_t * le, } break; case FE_FORCE_METHOD_PHI_GRADMU_CORRECTION: - phi_grad_mu_correction(cs, phi, fe, hydro, map, 0); + /* The "1" here indicates it's always a correction, but an option + * could be added to switch it off. */ + phi_grad_mu_correction(cs, phi, fe, hydro, map, 1); break; case FE_FORCE_METHOD_RELAXATION_SYMM: - assert(0); /* HAS THIS EVER BEEN TESTED? */ + assert(0); /* NOT TESTED */ pth_stress_compute(pth, fe); break; default: From c0996c031e0f7d2207fd5b4e2d270b50052fbbc0 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 24 Oct 2022 15:00:05 +0100 Subject: [PATCH 074/244] Allow no_force method --- src/ludwig.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ludwig.c b/src/ludwig.c index 3578a4b2c..9623d1968 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -1355,6 +1355,7 @@ int free_energy_init_rt(ludwig_t * ludwig) { /* The following are supported */ switch (method) { + case FE_FORCE_METHOD_NO_FORCE: case FE_FORCE_METHOD_STRESS_DIVERGENCE: case FE_FORCE_METHOD_PHI_GRADMU: case FE_FORCE_METHOD_PHI_GRADMU_CORRECTION: From b243f645c433a5ee15ff0099b2c2d25f2690e680 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 24 Oct 2022 16:29:36 +0100 Subject: [PATCH 075/244] Bit utilities --- src/util_bits.c | 87 ++++++++++++++++++++++++++++++ src/util_bits.h | 23 ++++++++ tests/unit/test_util_bits.c | 104 ++++++++++++++++++++++++++++++++++++ tests/unit/tests.c | 1 + tests/unit/tests.h | 1 + 5 files changed, 216 insertions(+) create mode 100644 src/util_bits.c create mode 100644 src/util_bits.h create mode 100644 tests/unit/test_util_bits.c diff --git a/src/util_bits.c b/src/util_bits.c new file mode 100644 index 000000000..f01ad0726 --- /dev/null +++ b/src/util_bits.c @@ -0,0 +1,87 @@ +/***************************************************************************** + * + * util_bits.c + * + * Various bit/byte manipulations + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Center + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include +#include + +/***************************************************************************** + * + * util_bits_same + * + * Return 1 if two bits arrays are the same. + * + *****************************************************************************/ + +int util_bits_same(size_t size, const void * data1, const void * data2) { + + int bsame = 1; + unsigned char * b1 = (unsigned char *) data1; + unsigned char * b2 = (unsigned char *) data2; + + assert(b1); + assert(b2); + + for (size_t ibyte = size; ibyte-- > 0; ) { + for (int ibit = 7; ibit >= 0; ibit--) { + unsigned char bit1 = (b1[ibyte] >> ibit) & 1; + unsigned char bit2 = (b2[ibyte] >> ibit) & 1; + if (bit1 != bit2) bsame = 0; + } + } + + return bsame; +} + +/***************************************************************************** + * + * util_double_same + * + * Return 1 if two doubles are bitwise the same; zero if not. + * + *****************************************************************************/ + +int util_double_same(double d1, double d2) { + + double a = d1; + double b = d2; + + return util_bits_same(sizeof(double), &a, &b); +} + +/***************************************************************************** + * + * util_printf_bits + * + * Convenience to print a set of bits as 0 or 1. + * + *****************************************************************************/ + +int util_printf_bits(size_t size, const void * data) { + + unsigned char * bytes = (unsigned char *) data; + + if (data == NULL) return -1; + + for (size_t ibyte = size; ibyte-- > 0; ) { + for (int ibit = 7; ibit >= 0; ibit--) { + unsigned char bit = (bytes[ibyte] >> ibit) & 1; + printf("%u", bit); + } + } + + printf("\n"); + + return 0; +} diff --git a/src/util_bits.h b/src/util_bits.h new file mode 100644 index 000000000..cf48e5a02 --- /dev/null +++ b/src/util_bits.h @@ -0,0 +1,23 @@ +/***************************************************************************** + * + * util_bits.h + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#ifndef LUDWIG_UTIL_BITS_H +#define LUDWIG_UTIL_BITS_H + +#include + +int util_bits_same(size_t size, const void * data1, const void * data2); +int util_double_same(double d1, double d2); +int util_printf_bits(size_t size, const void * data); + +#endif diff --git a/tests/unit/test_util_bits.c b/tests/unit/test_util_bits.c new file mode 100644 index 000000000..46376f65f --- /dev/null +++ b/tests/unit/test_util_bits.c @@ -0,0 +1,104 @@ +/***************************************************************************** + * + * test_util_bits.c + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include + +#include "pe.h" +#include "util_bits.h" + +int test_util_bits_same(void); +int test_util_double_same(void); + +/***************************************************************************** + * + * test_util_bits_suite + * + *****************************************************************************/ + +int test_util_bits_suite(void) { + + pe_t * pe = NULL; + + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + test_util_bits_same(); + test_util_double_same(); + + pe_info(pe, "%-9s %s\n", "PASS", __FILE__); + + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_util_bits_same + * + *****************************************************************************/ + +int test_util_bits_same(void) { + + int ifail = 0; + + { + int i1 = 1; + int i2 = 1; + ifail = util_bits_same(sizeof(int), &i1, &i2); + assert(ifail == 1); + } + + { + int i1 = 2; + int i2 = -2; + ifail = util_bits_same(sizeof(int), &i1, &i2); + assert(ifail == 0); + } + + return ifail; +} + +/***************************************************************************** + * + * test_util_double_same + * + *****************************************************************************/ + +int test_util_double_same(void) { + + int ifail = 0; + + { + double a1 = 1.0; + double a2 = a1; + ifail = util_double_same(a1, a2); + assert(ifail == 1); + } + + { + double a1 = +0.0; + double a2 = -0.0; + ifail = util_double_same(a1, a2); + assert(ifail == 0); + } + + { + double a1 = 0.0; + double a2 = 2.0; + ifail = util_double_same(a1, a2); + assert(ifail == 0); + } + + return ifail; +} diff --git a/tests/unit/tests.c b/tests/unit/tests.c index dce322015..eea28b743 100644 --- a/tests/unit/tests.c +++ b/tests/unit/tests.c @@ -109,6 +109,7 @@ __host__ int tests_create() { test_rt_suite(); test_timer_suite(); test_util_suite(); + test_util_bits_suite(); test_util_fopen_suite(); test_util_sum_suite(); test_visc_arrhenius_suite(); diff --git a/tests/unit/tests.h b/tests/unit/tests.h index 60176c829..56b913e7a 100644 --- a/tests/unit/tests.h +++ b/tests/unit/tests.h @@ -90,6 +90,7 @@ int test_random_suite(void); int test_rt_suite(void); int test_timer_suite(void); int test_util_suite(void); +int test_util_bits_suite(void); int test_util_fopen_suite(void); int test_util_sum_suite(void); int test_visc_arrhenius_suite(void); From 229a73292ca61e759c46b5e44453420fbc8fd164 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 24 Oct 2022 17:26:43 +0100 Subject: [PATCH 076/244] Remove floating point comparison --- src/ludwig.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/ludwig.c b/src/ludwig.c index 113fdbccb..509f190f9 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -31,6 +31,7 @@ #include "leesedwards_rt.h" #include "control.h" #include "util.h" +#include "util_bits.h" #include "model_le.h" #include "bbl.h" @@ -2024,9 +2025,13 @@ int free_energy_init_rt(ludwig_t * ludwig) { /* f_vare_t function */ /* If permittivities really not the same number... */ - pe_info(pe, "Poisson solver: %15s\n", - (e1 == e2) ? "uniform" : "heterogeneous"); - if (e1 != e2) ludwig->epsilon = (f_vare_t) fe_es_var_epsilon; + if (util_double_same(e1, e2)) { + pe_info(pe, "Poisson solver: %15s\n", "uniform"); + } + else { + pe_info(pe, "Poisson solver: %15s\n", "heterogeneous"); + ludwig->epsilon = (f_vare_t) fe_es_var_epsilon; + } /* Force */ From f47025d1b32a5b5e8429926fec0388b715102a10 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 25 Oct 2022 14:47:27 +0100 Subject: [PATCH 077/244] Spelling check --- src/phi_grad_mu.c | 2 +- src/phi_grad_mu.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/phi_grad_mu.c b/src/phi_grad_mu.c index 1050ff105..3f6073940 100644 --- a/src/phi_grad_mu.c +++ b/src/phi_grad_mu.c @@ -283,7 +283,7 @@ __host__ int phi_grad_mu_correction(cs_t * cs, field_t * phi, fe_t * fe, * * phi_grad_mu_fluid_kernel * - * Accumulate -phi grad mu to local force at poistion (i). + * Accumulate -phi grad mu to local force at position (i). * * f_x(i) = -0.5*phi(i)*(mu(i+1) - mu(i-1)) etc * diff --git a/src/phi_grad_mu.h b/src/phi_grad_mu.h index 4720dc87b..6357bfac0 100644 --- a/src/phi_grad_mu.h +++ b/src/phi_grad_mu.h @@ -5,7 +5,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2021-2022 Thge University of Edinburgh + * (c) 2021-2022 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) From 688812314ebf4d420d11a30274c71c4fa052843a Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 27 Oct 2022 16:56:19 +0100 Subject: [PATCH 078/244] Add MPI_Comm_compare() --- mpi_s/mpi.h | 8 ++++++++ mpi_s/mpi_serial.c | 27 ++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/mpi_s/mpi.h b/mpi_s/mpi.h index 717589ee7..2972a54b9 100644 --- a/mpi_s/mpi.h +++ b/mpi_s/mpi.h @@ -102,6 +102,13 @@ enum collective_operations {MPI_MAX, enum reserved_communicators{MPI_COMM_WORLD, MPI_COMM_SELF}; +/* results of group comparisons */ + +#define MPI_UNEQUAL 0 +#define MPI_SIMILAR 1 +#define MPI_CONGRUENT 2 +#define MPI_IDENT 3 + /* NULL handles */ #define MPI_GROUP_NULL -1 @@ -150,6 +157,7 @@ int MPI_Bcast(void * buffer, int count, MPI_Datatype datatype, int root, int MPI_Comm_rank(MPI_Comm comm, int * rank); int MPI_Comm_size(MPI_Comm comm, int * size); int MPI_Comm_group(MPI_Comm comm, MPI_Group * grp); +int MPI_Comm_compare(MPI_Comm comm1, MPI_Comm comm2, int * result); int MPI_Send(void * buf, int count, MPI_Datatype type, int dest, int tag, MPI_Comm comm); diff --git a/mpi_s/mpi_serial.c b/mpi_s/mpi_serial.c index b86363583..5d4debbf3 100644 --- a/mpi_s/mpi_serial.c +++ b/mpi_s/mpi_serial.c @@ -274,6 +274,29 @@ int MPI_Comm_size(MPI_Comm comm, int * size) { return MPI_SUCCESS; } +/***************************************************************************** + * + * MPI_Comm_compare + * + * This is not quite right at the moment, as Cartesian communicators + * and non-Cartesian communictors need to treated on an equal basis. + * Specifically MPI_Comm_split() does note return a unique value. + * + *****************************************************************************/ + +int MPI_Comm_compare(MPI_Comm comm1, MPI_Comm comm2, int * result) { + + assert(result); + + *result = MPI_UNEQUAL; + if (mpi_is_valid_comm(comm1) && mpi_is_valid_comm(comm2)) { + *result = MPI_CONGRUENT; + if (comm1 == comm2) *result = MPI_IDENT; + } + + return 0; +} + /***************************************************************************** * * MPI_Abort @@ -631,7 +654,9 @@ int MPI_Comm_split(MPI_Comm comm, int colour, int key, MPI_Comm * newcomm) { assert(mpi_is_valid_comm(comm)); assert(newcomm); - *newcomm = comm; + /* Allow that a split Cartesian communicator is different */ + /* See MPI_Comm_compare() */ + *newcomm = MPI_COMM_WORLD; return MPI_SUCCESS; } From 6203e414a57658c4e9e623e001d0eca9a16be66d Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 27 Oct 2022 20:48:10 +0100 Subject: [PATCH 079/244] Validate stub name --- util/extract.c | 57 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/util/extract.c b/util/extract.c index c1a23e301..38de2bb08 100644 --- a/util/extract.c +++ b/util/extract.c @@ -71,7 +71,7 @@ typedef enum vtk_enum {VTK_SCALARS, VTK_VECTORS} vtk_enum_t; typedef struct metadata_v1_s metadata_v1_t; struct metadata_v1_s { - char stub[BUFSIZ/2]; /* file stub */ + const char * stub; /* file stub name */ int nrbyte; /* byte per record */ int is_bigendean; /* flag */ int npe; /* comm sz */ @@ -145,6 +145,8 @@ void le_unroll(metadata_v1_t * meta, double *); int lc_transform_q5(int * nlocal, double * datasection); int lc_compute_scalar_ops(double q[3][3], double qs[5]); +const char * file_stub_valid(const char * input); + /***************************************************************************** * * main @@ -327,12 +329,7 @@ int extract_driver(const char * filename, metadata_v1_t * meta, int version) { /* Write a single file with the final section */ - { - char tmp[FILENAME_MAX/2] = {0}; /* Avoid potential buffer overflow */ - strncpy(tmp, meta->stub, - FILENAME_MAX/2 - strnlen(meta->stub, FILENAME_MAX/2-1) - 1); - snprintf(io_data, sizeof(io_data), "%s-%8.8d", tmp, ntime); - } + snprintf(io_data, sizeof(io_data), "%s-%8.8d", meta->stub, ntime); if (output_vtk_ == 1 || output_vtk_ == 2) { @@ -530,6 +527,7 @@ void read_meta_data_file(const char * filename, metadata_v1_t * meta) { int nrbyte; int ifail; char tmp[FILENAME_MAX]; + char buf[BUFSIZ] = {0}; char * p; FILE * fp_meta; const int ncharoffset = 33; @@ -545,12 +543,18 @@ void read_meta_data_file(const char * filename, metadata_v1_t * meta) { p = fgets(tmp, FILENAME_MAX, fp_meta); assert(p); - ifail = sscanf(tmp+ncharoffset, "%s\n", meta->stub); + ifail = sscanf(tmp+ncharoffset, "%s\n", buf); if (ifail != 1) { printf("Meta data stub not read correctly\n"); exit(-1); } + meta->stub = file_stub_valid(buf); + if (meta->stub == NULL) { + printf("Meta data stub not recognised: %s\n", buf); + } printf("Read stub: %s\n", meta->stub); + + /* Data description */ p = fgets(tmp, FILENAME_MAX, fp_meta); assert(p); @@ -1393,3 +1397,40 @@ int lc_compute_scalar_ops(double q[3][3], double qs[5]) { return ifail; } + +/***************************************************************************** + * + * file_stub_valid + * + * Return a recognised name, or NULL if the input is not recognised. + * + *****************************************************************************/ + +const char * file_stub_valid(const char * input) { + + const char * output = NULL; + + if (strcmp(input, "phi") == 0) { + output = "phi"; /* scalar order parameter(s) */ + } + else if (strcmp(input, "p") == 0) { + output = "p"; /* vector order parameter */ + } + else if (strcmp(input, "q") == 0) { + output = "q"; /* tensor order parameter */ + } + else if (strcmp(input, "psi") == 0) { + output = "psi"; /* charge */ + } + else if (strcmp(input, "dist") == 0) { + output = "dist"; /* distributions */ + } + else if (strcmp(input, "rho") == 0) { + output = "rho"; /* desnity */ + } + else if (strcmp(input, "vel") == 0) { + output = "vel"; + } + + return output; +} From 7c101efcbbd97a87f4e0809e32dd8a7e78463382 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 27 Oct 2022 21:02:31 +0100 Subject: [PATCH 080/244] Add check on number of groups --- util/extract.c | 1 + 1 file changed, 1 insertion(+) diff --git a/util/extract.c b/util/extract.c index 38de2bb08..425510d55 100644 --- a/util/extract.c +++ b/util/extract.c @@ -624,6 +624,7 @@ void read_meta_data_file(const char * filename, metadata_v1_t * meta) { assert(p); ifail = sscanf(tmp+ncharoffset, "%d", &meta->nio); assert(ifail == 1); + assert(0 < meta->nio && meta->nio < 1000); printf("Number of I/O groups: %d\n", meta->nio); /* I/O decomposition */ p = fgets(tmp, FILENAME_MAX, fp_meta); From 4585d2299e3c48b3f3d339e2b63f557ce7c17e71 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 27 Oct 2022 21:23:37 +0100 Subject: [PATCH 081/244] Check number of groups again --- util/extract.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/util/extract.c b/util/extract.c index 425510d55..b097381f0 100644 --- a/util/extract.c +++ b/util/extract.c @@ -622,10 +622,14 @@ void read_meta_data_file(const char * filename, metadata_v1_t * meta) { /* Number of I/O groups */ p = fgets(tmp, FILENAME_MAX, fp_meta); assert(p); - ifail = sscanf(tmp+ncharoffset, "%d", &meta->nio); - assert(ifail == 1); - assert(0 < meta->nio && meta->nio < 1000); + + meta->nio = atoi(tmp + ncharoffset); + if (1 > meta->nio || meta->nio > 999) { + printf("Invalid number of i/o groups %d\n", meta->nio); + exit(-1); + } printf("Number of I/O groups: %d\n", meta->nio); + /* I/O decomposition */ p = fgets(tmp, FILENAME_MAX, fp_meta); if (p == NULL) printf("Not reached last line correctly\n"); From 824b983151468d6f9a948eb1ceee02af20ede962 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 27 Oct 2022 21:31:09 +0100 Subject: [PATCH 082/244] Try again --- util/extract.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/util/extract.c b/util/extract.c index b097381f0..c0bff5b23 100644 --- a/util/extract.c +++ b/util/extract.c @@ -623,12 +623,17 @@ void read_meta_data_file(const char * filename, metadata_v1_t * meta) { p = fgets(tmp, FILENAME_MAX, fp_meta); assert(p); - meta->nio = atoi(tmp + ncharoffset); - if (1 > meta->nio || meta->nio > 999) { - printf("Invalid number of i/o groups %d\n", meta->nio); - exit(-1); + { + int nio = atoi(tmp + ncharoffset); + if (1 > nio || nio > 999) { + printf("Invalid number of i/o groups %d\n", meta->nio); + exit(-1); + } + else { + meta->nio = nio; + } + printf("Number of I/O groups: %d\n", meta->nio); } - printf("Number of I/O groups: %d\n", meta->nio); /* I/O decomposition */ p = fgets(tmp, FILENAME_MAX, fp_meta); From 36fe8f5ffcf1e2fb0f5c51733383a03692decfd9 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sun, 30 Oct 2022 19:08:15 +0000 Subject: [PATCH 083/244] Filter command line --- util/extract.c | 44 +++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/util/extract.c b/util/extract.c index c0bff5b23..63ef30e48 100644 --- a/util/extract.c +++ b/util/extract.c @@ -204,8 +204,22 @@ int main(int argc, char ** argv) { exit(EXIT_FAILURE); } - /* PENDING KEVIN uncontrolled path name */ - read_meta_data_file(argv[optind], &metadata); + { + /* Must have a recognised metadata quantity */ + const char * stub = file_stub_valid(argv[optind]); + if (stub == NULL) { + printf("Unrecognised metadata file %s\n", argv[optind]); + exit(-1); + } + else { + char buf[FILENAME_MAX] = {0}; + char * filename = buf; + size_t len = strlen(stub); + strcat(filename, stub); + strncat(filename + len, argv[optind] + len, FILENAME_MAX-len-1); + read_meta_data_file(filename, &metadata); + } + } extract_driver(argv[optind+1], &metadata, version); @@ -1420,26 +1434,26 @@ const char * file_stub_valid(const char * input) { const char * output = NULL; - if (strcmp(input, "phi") == 0) { + if (strncmp(input, "phi", 3) == 0) { output = "phi"; /* scalar order parameter(s) */ } - else if (strcmp(input, "p") == 0) { - output = "p"; /* vector order parameter */ - } - else if (strcmp(input, "q") == 0) { - output = "q"; /* tensor order parameter */ - } - else if (strcmp(input, "psi") == 0) { + else if (strncmp(input, "psi", 3) == 0) { output = "psi"; /* charge */ } - else if (strcmp(input, "dist") == 0) { + else if (strncmp(input, "dist", 4) == 0) { output = "dist"; /* distributions */ } - else if (strcmp(input, "rho") == 0) { - output = "rho"; /* desnity */ + else if (strncmp(input, "rho", 3) == 0) { + output = "rho"; /* density */ + } + else if (strncmp(input, "vel", 3) == 0) { + output = "vel"; /* velocity field */ } - else if (strcmp(input, "vel") == 0) { - output = "vel"; + else if (strncmp(input, "p", 1) == 0) { + output = "p"; /* vector order parameter */ + } + else if (strncmp(input, "q", 1) == 0) { + output = "q"; /* tensor order parameter */ } return output; From e4abc417c7c34e120eb9821ea02db088d90036b3 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sun, 30 Oct 2022 19:45:47 +0000 Subject: [PATCH 084/244] Try again --- util/extract.c | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/util/extract.c b/util/extract.c index 63ef30e48..dfd3b6e82 100644 --- a/util/extract.c +++ b/util/extract.c @@ -146,6 +146,8 @@ int lc_transform_q5(int * nlocal, double * datasection); int lc_compute_scalar_ops(double q[3][3], double qs[5]); const char * file_stub_valid(const char * input); +int file_get_file_nfile(const char * filename); +int file_get_file_index(const char * filename); /***************************************************************************** * @@ -214,9 +216,9 @@ int main(int argc, char ** argv) { else { char buf[FILENAME_MAX] = {0}; char * filename = buf; - size_t len = strlen(stub); - strcat(filename, stub); - strncat(filename + len, argv[optind] + len, FILENAME_MAX-len-1); + int ifile = file_get_file_index(argv[optind]); + int nfile = file_get_file_nfile(argv[optind]); + sprintf(filename, "%s.%3.3d-%3.3d.meta", stub, ifile, nfile); read_meta_data_file(filename, &metadata); } } @@ -1458,3 +1460,33 @@ const char * file_stub_valid(const char * input) { return output; } + +int file_get_file_nfile(const char * filename) { + + int nfile = 0; + char dash = '-'; + const char * str1 = strchr(filename, dash); + + if (str1 != NULL) { + char buf[BUFSIZ] = {0}; + strncpy(buf, str1 + 1, 3); + nfile = atoi(buf); + } + + return nfile; +} + +int file_get_file_index(const char * filename) { + + int ifile = 0; + char dot = '.'; + const char * str1 = strchr(filename, dot); /* E.g., phi.001-001.meta */ + + if (str1 != NULL) { + char buf[BUFSIZ] = {0}; + strncpy(buf, str1 + 1, 3); + ifile = atoi(buf); + } + + return ifile; +} From f672b7f88359cfe9baa5628bcb29a764e59b6bd8 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 31 Oct 2022 16:37:01 +0000 Subject: [PATCH 085/244] Add io_cart_sub_t --- src/io_cart_sub.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++ src/io_cart_sub.h | 41 ++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 src/io_cart_sub.c create mode 100644 src/io_cart_sub.h diff --git a/src/io_cart_sub.c b/src/io_cart_sub.c new file mode 100644 index 000000000..36a9fc66f --- /dev/null +++ b/src/io_cart_sub.c @@ -0,0 +1,154 @@ +/***************************************************************************** + * + * io_cart_sub.c + * + * A 3-dimensional Cartesian partitioning of the existing global + * Cartesian communictor into blocks for the purpose of i/o to + * file. One file for each Cartesian sub-block. + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include + +#include "coords_s.h" +#include "io_cart_sub.h" + +/***************************************************************************** + * + * io_cart_sub_create + * + * The requested iogrid[3] must exactly devide the existing Cartesian + * communicator. + * + * Returns 0 on success with a newly created communicator. + * + *****************************************************************************/ + +int io_cart_sub_create(cs_t * cs, int iogrid[3], io_cart_sub_t * iosub) { + + assert(cs); + assert(iosub); + + *iosub = (io_cart_sub_t) {0}; + + /* Check we can make a decomposition ... */ + + for (int i = 0; i < 3; i++) { + if (cs->param->mpi_cartsz[i] % iogrid[i] != 0) goto err; + } + + cs_cart_comm(cs, &iosub->parent); + + /* Some integer arithmetic to form blocks */ + + for (int i = 0; i < 3; i++) { + int isz = cs->param->mpi_cartsz[i]; + int icoord = cs->param->mpi_cartcoords[i]; + int ioffset = icoord / (isz/iogrid[i]); + + iosub->size[i] = iogrid[i]; + iosub->coords[i] = iogrid[i]*icoord/isz; + + /* Offset and size, local size allowing for non-uniform decomposition */ + + iosub->ntotal[i] = cs->param->ntotal[i]; + iosub->nlocal[i] = 0; + iosub->offset[i] = cs->listnoffset[i][ioffset]; + + for (int j = ioffset; j < ioffset + (isz/iogrid[i]); j++) { + iosub->nlocal[i] += cs->listnlocal[i][j]; + } + } + + /* We can now split the communicator from the coordinates */ + + iosub->nfile = iogrid[X]*iogrid[Y]*iogrid[Z]; + iosub->index = iosub->coords[X] + + iosub->coords[Y]*iogrid[X] + + iosub->coords[Z]*iogrid[X]*iogrid[Y]; + { + int rank = -1; + MPI_Comm_rank(iosub->parent, &rank); + MPI_Comm_split(iosub->parent, iosub->index, rank, &iosub->comm); + } + + return 0; + + err: + return -1; +} + +/***************************************************************************** + * + * io_cart_sub_free + * + *****************************************************************************/ + +int io_cart_sub_free(io_cart_sub_t * iosub) { + + assert(iosub); + + MPI_Comm_free(&iosub->comm); + + *iosub = (io_cart_sub_t) {0}; + iosub->comm = MPI_COMM_NULL; + + return 0; +} + +/***************************************************************************** + * + * io_cart_sub_printf + * + *****************************************************************************/ + +int io_cart_sub_printf(const io_cart_sub_t * iosub) { + + int sz = -1; + int rank = -1; + char msg[BUFSIZ] = {0}; + + assert(iosub); + + /* Commnuication is in the Cartesian communicator */ + + MPI_Comm_size(iosub->parent, &sz); + MPI_Comm_rank(iosub->parent, &rank); + + + { + int coords[3] = {0}; /* Cartesian communicator coordinates */ + int iorank = -1; + MPI_Comm_rank(iosub->comm, &iorank); + MPI_Cart_coords(iosub->parent, rank, 3, coords); + sprintf(msg, "%4d %3d %3d %3d %2d %4d %3d %3d %3d %4d %4d %4d", + rank, coords[X], coords[Y], coords[Z], + iosub->index, iorank, + iosub->coords[X], iosub->coords[Y], iosub->coords[Z], + iosub->offset[X], iosub->offset[Y], iosub->offset[Z]); + } + + if (rank > 0) { + MPI_Send(msg, BUFSIZ, MPI_CHAR, 0, 271022, iosub->parent); + } + else { + printf("Cartesian io_cart_sub\n"); + printf("rank x y z file rank x y z ox oy oz\n"); + printf("%s\n", msg); + for (int ir = 1; ir < sz; ir++) { + MPI_Recv(msg, BUFSIZ, MPI_CHAR, ir, 271022, iosub->parent, + MPI_STATUS_IGNORE); + printf("%s\n", msg); + } + } + + return 0; +} diff --git a/src/io_cart_sub.h b/src/io_cart_sub.h new file mode 100644 index 000000000..6f0db7463 --- /dev/null +++ b/src/io_cart_sub.h @@ -0,0 +1,41 @@ +/***************************************************************************** + * + * io_cart_sub.h + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#ifndef LUDWIG_IO_CART_SUB_H +#define LUDWIG_IO_CART_SUB_H + +#include "coords.h" + +typedef struct io_cart_sub_s io_cart_sub_t; + +struct io_cart_sub_s { + + MPI_Comm parent; /* Global Cartesian communictor */ + MPI_Comm comm; /* MPI communicator for this 3d subsection */ + int size[3]; /* Global I/O Grid or topology */ + int coords[3]; /* Cartesian position of this rank in group */ + + int ntotal[3]; /* Total sites (all files) */ + int nlocal[3]; /* Local sites (this file) */ + int offset[3]; /* Offset (this file) */ + + int nfile; /* Total number of files in decomposition */ + int index; /* Index of this group {0, 1, ..., nfile-1} */ +}; + +int io_cart_sub_create(cs_t * cs, int iogrid[3], io_cart_sub_t * iosub); +int io_cart_sub_free(io_cart_sub_t * iosub); +int io_cart_sub_printf(const io_cart_sub_t * iosub); + +#endif From 1d7218c1c244a3356b1b9b7ada9d81e5b87f2559 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 31 Oct 2022 16:37:33 +0000 Subject: [PATCH 086/244] Add io_cart_sub_t test --- tests/unit/test_io_cart_sub.c | 219 ++++++++++++++++++++++++++++++++++ tests/unit/tests.c | 1 + tests/unit/tests.h | 1 + 3 files changed, 221 insertions(+) create mode 100644 tests/unit/test_io_cart_sub.c diff --git a/tests/unit/test_io_cart_sub.c b/tests/unit/test_io_cart_sub.c new file mode 100644 index 000000000..d2e026a88 --- /dev/null +++ b/tests/unit/test_io_cart_sub.c @@ -0,0 +1,219 @@ +/***************************************************************************** + * + * test_io_cart_sub.c + * + * This is one place where it would be useful to test MPI decompositions + * of significant extent in three dimensions. + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford(kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include +#include + +#include "pe.h" +#include "io_cart_sub.h" + +int test_io_cart_sub_create(pe_t * pe); +int test_io_cart_sub_util_pass(pe_t * pe, int ntotal[3], int iogrid[3]); + +/***************************************************************************** + * + * test_io_cart_sub_suite + * + *****************************************************************************/ + +int test_io_cart_sub_suite(void) { + + int sz = -1; + pe_t * pe = NULL; + + MPI_Comm_size(MPI_COMM_WORLD, &sz); + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + /* "Serial": only one case */ + + test_io_cart_sub_create(pe); + + /* Small: MPI_Comm_size() <= 8 */ + + if (sz >= 2) { + { + int ntotal[3] = {8, 8, 8}; + int iogrid[3] = {2, 1, 1}; + test_io_cart_sub_util_pass(pe, ntotal, iogrid); + } + } + + if (sz >= 4) { + /* We expect MPI_Dims_create() to be the decomposition in cs_t */ + { + int ntotal[3] = {8, 8, 8}; + int iogrid[3] = {2, 2, 1}; + test_io_cart_sub_util_pass(pe, ntotal, iogrid); + } + } + if (sz >= 8) { + { + int ntotal[3] = {8, 8, 8}; + int iogrid[3] = {2, 2, 2}; + test_io_cart_sub_util_pass(pe, ntotal, iogrid); + } + } + + pe_info(pe, "%-9s %s\n", "PASS", __FILE__); + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_io_cart_sub_create + * + * The simple case of one file: iogrid = {1,1,1} + * + *****************************************************************************/ + +int test_io_cart_sub_create(pe_t * pe) { + + cs_t * cs = NULL; + + assert(pe); + + /* Default system */ + cs_create(pe, &cs); + cs_init(cs); + + { + int iogrid[3] = {1, 1, 1}; + io_cart_sub_t iosub = {0}; + + io_cart_sub_create(cs, iogrid, &iosub); + + { + MPI_Comm comm = MPI_COMM_NULL; + int myresult = MPI_UNEQUAL; + + cs_cart_comm(cs, &comm); + MPI_Comm_compare(comm, iosub.parent, &myresult); + assert(myresult == MPI_IDENT); + + MPI_Comm_compare(comm, iosub.comm, &myresult); + printf("cart io %d %d result %d\n", comm, iosub.comm, myresult); + assert(myresult == MPI_CONGRUENT); + } + + assert(iosub.size[X] == iogrid[X]); + assert(iosub.size[Y] == iogrid[Y]); + assert(iosub.size[Z] == iogrid[Z]); + assert(iosub.coords[X] == 0); + assert(iosub.coords[Y] == 0); + assert(iosub.coords[Z] == 0); + + { + int ntotal[3] = {0}; + cs_ntotal(cs, ntotal); + assert(iosub.ntotal[X] == ntotal[X]); + assert(iosub.ntotal[Y] == ntotal[Y]); + assert(iosub.ntotal[Z] == ntotal[Z]); + assert(iosub.nlocal[X] == ntotal[X]); /* All one file */ + assert(iosub.nlocal[Y] == ntotal[Y]); + assert(iosub.nlocal[Z] == ntotal[Z]); + assert(iosub.offset[X] == 0); + assert(iosub.offset[Y] == 0); + assert(iosub.offset[Z] == 0); + } + + assert(iosub.nfile == 1); + assert(iosub.index == 0); + + io_cart_sub_free(&iosub); + assert(iosub.comm == MPI_COMM_NULL); + } + + { + /* Here's one that must fail. */ + int iogrid[3] = {INT_MAX, 1, 1}; + io_cart_sub_t iosub = {0}; + + assert(io_cart_sub_create(cs, iogrid, &iosub) != 0); + + } + cs_free(cs); + + return 0; +} + +/***************************************************************************** + * + * test_io_cart_sub_util_pass + * + *****************************************************************************/ + +int test_io_cart_sub_util_pass(pe_t * pe, int ntotal[3], int iogrid[3]) { + + cs_t * cs = NULL; + + assert(pe); + + cs_create(pe, &cs); + cs_ntotal_set(cs, ntotal); + cs_init(cs); + + { + int ifail = 0; + io_cart_sub_t iosub = {0}; + + ifail = io_cart_sub_create(cs, iogrid, &iosub); + assert(ifail == 0); + + { + /* Communicators must be different */ + MPI_Comm comm = MPI_COMM_NULL; + int myresult = MPI_UNEQUAL; + + cs_cart_comm(cs, &comm); + MPI_Comm_compare(comm, iosub.parent, &myresult); + assert(myresult == MPI_IDENT); + + MPI_Comm_compare(comm, iosub.comm, &myresult); + assert(myresult == MPI_UNEQUAL); + } + + /* size == iogrid */ + assert(iosub.size[X] == iogrid[X]); + assert(iosub.size[Y] == iogrid[Y]); + assert(iosub.size[Z] == iogrid[Z]); + + /* One should be able to reconstruct the coords from + * the index and the offset */ + + /* ntotal[] is fixed */ + assert(iosub.ntotal[X] == ntotal[X]); + assert(iosub.ntotal[Y] == ntotal[Y]); + assert(iosub.ntotal[Z] == ntotal[Z]); + /* PENDING NLOCAL */ + /* PENDING OFFSET */ + + { + /* Don't care about exactly what the index is ... */ + int nfile = iogrid[X]*iogrid[Y]*iogrid[Z]; + assert(iosub.nfile == nfile); + assert(0 <= iosub.index && iosub.index < nfile); + } + + io_cart_sub_free(&iosub); + } + + cs_free(cs); + + return 0; +} diff --git a/tests/unit/tests.c b/tests/unit/tests.c index d187a1d51..3348221c3 100644 --- a/tests/unit/tests.c +++ b/tests/unit/tests.c @@ -75,6 +75,7 @@ __host__ int tests_create() { test_hydro_suite(); test_io_aggr_buf_suite(); test_io_aggr_mpio_suite(); + test_io_cart_sub_suite(); test_io_options_suite(); test_io_options_rt_suite(); test_io_info_args_suite(); diff --git a/tests/unit/tests.h b/tests/unit/tests.h index 1ea655745..fbcbafacb 100644 --- a/tests/unit/tests.h +++ b/tests/unit/tests.h @@ -54,6 +54,7 @@ int test_halo_suite(void); int test_hydro_suite(void); int test_io_aggr_buf_suite(void); int test_io_aggr_mpio_suite(void); +int test_io_cart_sub_suite(void); int test_io_info_args_suite(void); int test_io_info_args_rt_suite(void); int test_io_options_suite(void); From b77e1412819dc291f42c1f35f51d7cfcb82e2445 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 31 Oct 2022 16:51:03 +0000 Subject: [PATCH 087/244] Add cJSON from Dave Gamble and io_element_t --- src/io_element.c | 144 +++ src/io_element.h | 45 + src/util_cJSON.c | 3119 ++++++++++++++++++++++++++++++++++++++++++++++ src/util_cJSON.h | 300 +++++ 4 files changed, 3608 insertions(+) create mode 100644 src/io_element.c create mode 100644 src/io_element.h create mode 100644 src/util_cJSON.c create mode 100644 src/util_cJSON.h diff --git a/src/io_element.c b/src/io_element.c new file mode 100644 index 000000000..cf7684ab1 --- /dev/null +++ b/src/io_element.c @@ -0,0 +1,144 @@ +/***************************************************************************** + * + * io_element.c + * + * Provides a container for i/o record description. + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include +#include + +#include "io_element.h" + +/***************************************************************************** + * + * io_endian_from_string + * + *****************************************************************************/ + +io_endian_enum_t io_endian_from_string(const char * str) { + + io_endian_enum_t endianness = IO_ENDIAN_UNKNOWN; + + if (strcmp(str, "BIG_ENDIAN") == 0) endianness = IO_ENDIAN_BIG_ENDIAN; + if (strcmp(str, "LITTLE_ENDIAN") == 0) endianness = IO_ENDIAN_LITTLE_ENDIAN; + + return endianness; +} + +/***************************************************************************** + * + * io_endian_to_string + * + *****************************************************************************/ + +const char * io_endian_to_string(io_endian_enum_t endianness) { + + const char * str = NULL; + + switch (endianness) { + case IO_ENDIAN_LITTLE_ENDIAN: + str = "LITTLE_ENDIAN"; + break; + case IO_ENDIAN_BIG_ENDIAN: + str = "BIG_ENDIAN"; + break; + default: + str = "UNKNOWN"; + } + + return str; +} + +/***************************************************************************** + * + * io_element_null + * + * In particular, MPI_DATATYPE_NULL should be correct. + * + *****************************************************************************/ + +io_element_t io_element_null(void) { + + io_element_t element = {.datatype = MPI_DATATYPE_NULL, + .datasize = 0, + .count = 0, + .endian = IO_ENDIAN_UNKNOWN}; + + return element; +} + +/***************************************************************************** + * + * io_element_to_json + * + * Caller responisble for releasing resources. + * + *****************************************************************************/ + +int io_element_to_json(const io_element_t * element, cJSON * json) { + + int ifail = 0; + + assert(element); + + if (json != NULL) { + ifail = -1; + } + else { + json = cJSON_CreateObject(); + + cJSON_AddStringToObject(json, "MPI_Datatype", "MPI_DOUBLE TBC"); + cJSON_AddNumberToObject(json, "Datasize (bytes)", element->datasize); + cJSON_AddNumberToObject(json, "Count", element->count); + cJSON_AddStringToObject(json, "Endianness", + io_endian_to_string(element->endian)); + } + + return ifail; +} + +/***************************************************************************** + * + * io_element_from_json + * + *****************************************************************************/ + +int io_element_from_json(const cJSON * json, io_element_t * element) { + + int ifail = 0; + + assert(element); + + if (json == NULL) { + ifail = -1; + } + else { + cJSON * datatype = cJSON_GetObjectItem(json, "MPI_Datatype"); + cJSON * datasize = cJSON_GetObjectItem(json, "Datasize (bytes)"); + cJSON * count = cJSON_GetObjectItem(json, "Count"); + cJSON * endianness = cJSON_GetObjectItem(json, "Endianness"); + + if (datatype) { + /* Another string to be converted to MPI_Datatype */ + element->datatype = MPI_DOUBLE; /* TBC */ + } + if (datasize) element->datasize = cJSON_GetNumberValue(datasize); + if (count) element->count = cJSON_GetNumberValue(count); + + if (endianness) { + char * str = {0}; + str = cJSON_GetStringValue(endianness); + element->endian = io_endian_from_string(str); + } + } + + return ifail; +} diff --git a/src/io_element.h b/src/io_element.h new file mode 100644 index 000000000..88be2ae7b --- /dev/null +++ b/src/io_element.h @@ -0,0 +1,45 @@ +/***************************************************************************** + * + * io_element.h + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#ifndef LUDWIG_IO_ELEMENT_H +#define LUDWIG_IO_ELEMENT_H + +#include "mpi.h" +#include "util_cjson.h" + +/* Utility */ + +typedef enum { + IO_ENDIAN_UNKNOWN, + IO_ENDIAN_LITTLE_ENDIAN, + IO_ENDIAN_BIG_ENDIAN +} io_endian_enum_t; + +io_endian_enum_t io_endian_from_string(const char * str); +const char * io_endian_to_string(io_endian_enum_t endian); + + +typedef struct io_element_s io_element_t; + +struct io_element_s { + MPI_Datatype datatype; /* Fundamental datatype */ + size_t datasize; /* sizeof(datatype) */ + int count; /* Items per record (scalar, vector, ...) */ + io_endian_enum_t endian; /* Big, little. */ +}; + +io_element_t io_element_null(void); +int io_element_from_json(const cJSON * json, io_element_t * element); +int io_element_to_json(const io_element_t * element, cJSON * json); + +#endif diff --git a/src/util_cJSON.c b/src/util_cJSON.c new file mode 100644 index 000000000..89054b96d --- /dev/null +++ b/src/util_cJSON.c @@ -0,0 +1,3119 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "util_cJSON.h" + +/* define our own boolean type */ +#ifdef true +#undef true +#endif +#define true ((cJSON_bool)1) + +#ifdef false +#undef false +#endif +#define false ((cJSON_bool)0) + +/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ +#ifndef isinf +#define isinf(d) (isnan((d - d)) && !isnan(d)) +#endif +#ifndef isnan +#define isnan(d) (d != d) +#endif + +#ifndef NAN +#ifdef _WIN32 +#define NAN sqrt(-1.0) +#else +#define NAN 0.0/0.0 +#endif +#endif + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) +{ + return (const char*) (global_error.json + global_error.position); +} + +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) +{ + if (!cJSON_IsString(item)) + { + return NULL; + } + + return item->valuestring; +} + +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) +{ + if (!cJSON_IsNumber(item)) + { + return (double) NAN; + } + + return item->valuedouble; +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 15) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) +{ + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks +{ + void *(CJSON_CDECL *allocate)(size_t size); + void (CJSON_CDECL *deallocate)(void *pointer); + void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ +static void * CJSON_CDECL internal_malloc(size_t size) +{ + return malloc(size); +} +static void CJSON_CDECL internal_free(void *pointer) +{ + free(pointer); +} +static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) +{ + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +/* strlen of character literals resolved at compile time */ +#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) + +static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +{ + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ +#ifdef ENABLE_LOCALES + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) +{ + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) +{ + char *copy = NULL; + /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ + if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) + { + return NULL; + } + if (strlen(valuestring) <= strlen(object->valuestring)) + { + strcpy(object->valuestring, valuestring); + return object->valuestring; + } + copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); + if (copy == NULL) + { + return NULL; + } + if (object->valuestring != NULL) + { + cJSON_free(object->valuestring); + } + object->valuestring = copy; + + return copy; +} + +typedef struct +{ + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) + { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) + { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else + { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + + memcpy(newbuffer, p->buffer, p->offset + 1); + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* securely comparison of floating-point variables */ +static cJSON_bool compare_double(double a, double b) +{ + double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + return (fabs(a - b) <= maxVal * DBL_EPSILON); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test = 0.0; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if (isnan(d) || isinf(d)) + { + length = sprintf((char*)number_buffer, "null"); + } + else if(d == (double)item->valueint) + { + length = sprintf((char*)number_buffer, "%d", item->valueint); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) + { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char*)number_buffer, "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occurred */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) +{ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +{ + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) + { + return false; + } + + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + if (cannot_access_at_index(buffer, 0)) + { + return buffer; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) + { + return NULL; + } + + if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) + { + buffer->offset += 3; + } + + return buffer; +} + +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + size_t buffer_length; + + if (NULL == value) + { + return NULL; + } + + /* Adding null character size due to require_null_terminated. */ + buffer_length = strlen(value) + sizeof(""); + + return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL || 0 == buffer_length) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = buffer_length; + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) +{ + return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); +} + +#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); + if (printed == NULL) { + goto fail; + } + buffer->buffer = NULL; + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + } + + if (printed != NULL) + { + hooks->deallocate(printed); + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if ((length < 0) || (buffer == NULL)) + { + return false; + } + + p.buffer = (unsigned char*)buffer; + p.length = (size_t)length; + p.offset = 0; + p.noalloc = true; + p.format = format; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) + { + return false; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + if (head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { + return false; + } + update_offset(output_buffer); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* failed to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + if (head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) + { + if (output_buffer->format) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) + { + return 0; + } + + child = array->child; + + while(child != NULL) + { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + if ((current_element == NULL) || (current_element->string == NULL)) { + return NULL; + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) +{ + cJSON *reference = NULL; + if (item == NULL) + { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) + { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL) || (array == item)) + { + return false; + } + + child = array->child; + /* + * To find the last item in array quickly, we use prev in array + */ + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + item->prev = item; + item->next = NULL; + } + else + { + /* append to the end */ + if (child->prev) + { + suffix_object(child->prev, item); + array->child->prev = item; + } + } + + return true; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + return add_item_to_array(array, item); +} + +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) +{ + return (void*)string; +} +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + + +static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) +{ + char *new_key = NULL; + int new_type = cJSON_Invalid; + + if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) + { + return false; + } + + if (constant_key) + { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } + else + { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if (new_key == NULL) + { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + return add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + return add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) + { + return false; + } + + return add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { + return false; + } + + return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) +{ + cJSON *null = cJSON_CreateNull(); + if (add_item_to_object(object, name, null, &global_hooks, false)) + { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) +{ + cJSON *true_item = cJSON_CreateTrue(); + if (add_item_to_object(object, name, true_item, &global_hooks, false)) + { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) +{ + cJSON *false_item = cJSON_CreateFalse(); + if (add_item_to_object(object, name, false_item, &global_hooks, false)) + { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) +{ + cJSON *bool_item = cJSON_CreateBool(boolean); + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) + { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) +{ + cJSON *number_item = cJSON_CreateNumber(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) + { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) +{ + cJSON *string_item = cJSON_CreateString(string); + if (add_item_to_object(object, name, string_item, &global_hooks, false)) + { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) +{ + cJSON *raw_item = cJSON_CreateRaw(raw); + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) + { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) +{ + cJSON *object_item = cJSON_CreateObject(); + if (add_item_to_object(object, name, object_item, &global_hooks, false)) + { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) +{ + cJSON *array = cJSON_CreateArray(); + if (add_item_to_object(object, name, array, &global_hooks, false)) + { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL)) + { + return NULL; + } + + if (item != parent->child) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + else if (item->next == NULL) + { + /* last element */ + parent->child->prev = item->prev; + } + + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0) + { + return false; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + return add_item_to_array(array, newitem); + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (replacement == NULL) || (item == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (parent->child == item) + { + if (parent->child->prev == parent->child) + { + replacement->prev = replacement; + } + parent->child = replacement; + } + else + { /* + * To find the last item in array quickly, we use prev in array. + * We can't modify the last item's next pointer where this item was the parent's child + */ + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (replacement->next == NULL) + { + parent->child->prev = replacement; + } + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return false; + } + + return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if (replacement->string == NULL) + { + return false; + } + + replacement->type &= ~cJSON_StringIsConst; + + return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + return replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + return replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = boolean ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) + { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + if (newitem && newitem->child) + { + newitem->child->prev = newchild; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; +} + +static void skip_oneline_comment(char **input) +{ + *input += static_strlen("//"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if ((*input)[0] == '\n') { + *input += static_strlen("\n"); + return; + } + } +} + +static void skip_multiline_comment(char **input) +{ + *input += static_strlen("/*"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if (((*input)[0] == '*') && ((*input)[1] == '/')) + { + *input += static_strlen("*/"); + return; + } + } +} + +static void minify_string(char **input, char **output) { + (*output)[0] = (*input)[0]; + *input += static_strlen("\""); + *output += static_strlen("\""); + + + for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { + (*output)[0] = (*input)[0]; + + if ((*input)[0] == '\"') { + (*output)[0] = '\"'; + *input += static_strlen("\""); + *output += static_strlen("\""); + return; + } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { + (*output)[1] = (*input)[1]; + *input += static_strlen("\""); + *output += static_strlen("\""); + } + } +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + char *into = json; + + if (json == NULL) + { + return; + } + + while (json[0] != '\0') + { + switch (json[0]) + { + case ' ': + case '\t': + case '\r': + case '\n': + json++; + break; + + case '/': + if (json[1] == '/') + { + skip_oneline_comment(&json); + } + else if (json[1] == '*') + { + skip_multiline_comment(&json); + } else { + json++; + } + break; + + case '\"': + minify_string(&json, (char**)&into); + break; + + default: + into[0] = json[0]; + json++; + into++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (compare_double(a->valuedouble, b->valuedouble)) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); +} diff --git a/src/util_cJSON.h b/src/util_cJSON.h new file mode 100644 index 000000000..95a9cf69d --- /dev/null +++ b/src/util_cJSON.h @@ -0,0 +1,300 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 15 + +#include + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; +} cJSON; + +typedef struct cJSON_Hooks +{ + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ + void *(CJSON_CDECL *malloc_fn)(size_t sz); + void (CJSON_CDECL *free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* Check item type and return its value */ +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); +/* Create an object/array that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); + +/* These utilities create an Array of count items. + * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detach items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will + * need to be released. With recurse!=0, it will duplicate any children connected to the item. + * The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); + +/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. + * The input pointer json cannot point to a read-only address area, such as a string constant, + * but should point to a readable and writable address area. */ +CJSON_PUBLIC(void) cJSON_Minify(char *json); + +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) +/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); + +/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/ +#define cJSON_SetBoolValue(object, boolValue) ( \ + (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \ + (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \ + cJSON_Invalid\ +) + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + +#ifdef __cplusplus +} +#endif + +#endif From 2c1acae86c8a9f5320ae7579a474fca482ab0141 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 31 Oct 2022 16:52:03 +0000 Subject: [PATCH 088/244] Add io_element_t test --- tests/unit/test_io_element.c | 170 +++++++++++++++++++++++++++++++++++ tests/unit/tests.c | 1 + tests/unit/tests.h | 1 + 3 files changed, 172 insertions(+) create mode 100644 tests/unit/test_io_element.c diff --git a/tests/unit/test_io_element.c b/tests/unit/test_io_element.c new file mode 100644 index 000000000..646087402 --- /dev/null +++ b/tests/unit/test_io_element.c @@ -0,0 +1,170 @@ +/***************************************************************************** + * + * test_io_element.c + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include +#include + +#include "pe.h" +#include "io_element.h" + +int test_io_endian_from_string(void); +int test_io_endian_to_string(void); +int test_io_element_null(void); +int test_io_element_from_json(void); +int test_io_element_to_json(void); + +/***************************************************************************** + * + * test_io_element_suite + * + *****************************************************************************/ + +int test_io_element_suite(void) { + + pe_t * pe = NULL; + + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + test_io_endian_from_string(); + test_io_endian_to_string(); + + test_io_element_null(); + test_io_element_from_json(); + test_io_element_to_json(); + + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_io_endian_from_string + * + *****************************************************************************/ + +int test_io_endian_from_string(void) { + + { + const char * str = "LITTLE_ENDIAN"; + io_endian_enum_t endian = io_endian_from_string(str); + assert(endian == IO_ENDIAN_LITTLE_ENDIAN); + } + + { + const char * str = "BIG_ENDIAN"; + io_endian_enum_t endian = io_endian_from_string(str); + assert(endian == IO_ENDIAN_BIG_ENDIAN); + } + + { + const char * str = "RUBBISH"; + io_endian_enum_t endian = io_endian_from_string(str); + assert(endian == IO_ENDIAN_UNKNOWN); + } + + return 0; +} + +/***************************************************************************** + * + * test_io_endian_to_string + * + *****************************************************************************/ + +int test_io_endian_to_string(void) { + + { + io_endian_enum_t endian = IO_ENDIAN_UNKNOWN; + const char * str = io_endian_to_string(endian); + assert(strcmp(str, "UNKNOWN") == 0); + } + + { + io_endian_enum_t endian = IO_ENDIAN_LITTLE_ENDIAN; + const char * str = io_endian_to_string(endian); + assert(strcmp(str, "LITTLE_ENDIAN") == 0); + } + + { + io_endian_enum_t endian = IO_ENDIAN_BIG_ENDIAN; + const char * str = io_endian_to_string(endian); + assert(strcmp(str, "BIG_ENDIAN") == 0); + } + + return 0; +} + +/***************************************************************************** + * + * test_io_element_null + * + *****************************************************************************/ + +int test_io_element_null(void) { + + io_element_t element = io_element_null(); + + assert(element.datatype == MPI_DATATYPE_NULL); + assert(element.datasize == 0); + assert(element.count == 0); + assert(element.endian == IO_ENDIAN_UNKNOWN); + + return 0; +} + +/***************************************************************************** + * + * test_io_element_from_json + * + *****************************************************************************/ + +int test_io_element_from_json(void) { + + int ifail = 0; + + { + /* Null JSON object is a fail */ + cJSON * json = NULL; + io_element_t element = io_element_null(); + int ifail = io_element_from_json(json, &element); + assert(ifail == -1); + } + + return ifail; +} + +/***************************************************************************** + * + * test_io_element_to_json + * + *****************************************************************************/ + +int test_io_element_to_json(void) { + + int ifail = 0; + + { + io_element_t element = {.datatype = MPI_DOUBLE, + .datasize = 1, + .count = 3, + .endian = IO_ENDIAN_LITTLE_ENDIAN}; + cJSON * json = NULL; + ifail = io_element_to_json(&element, json); + + cJSON_Delete(json); + } + + return ifail; +} diff --git a/tests/unit/tests.c b/tests/unit/tests.c index 3348221c3..578d7d44e 100644 --- a/tests/unit/tests.c +++ b/tests/unit/tests.c @@ -76,6 +76,7 @@ __host__ int tests_create() { test_io_aggr_buf_suite(); test_io_aggr_mpio_suite(); test_io_cart_sub_suite(); + test_io_element_suite(); test_io_options_suite(); test_io_options_rt_suite(); test_io_info_args_suite(); diff --git a/tests/unit/tests.h b/tests/unit/tests.h index fbcbafacb..b029b24cc 100644 --- a/tests/unit/tests.h +++ b/tests/unit/tests.h @@ -55,6 +55,7 @@ int test_hydro_suite(void); int test_io_aggr_buf_suite(void); int test_io_aggr_mpio_suite(void); int test_io_cart_sub_suite(void); +int test_io_element_suite(void); int test_io_info_args_suite(void); int test_io_info_args_rt_suite(void); int test_io_options_suite(void); From 5bd8471ba4c4ac10e5d2376d32cbfcbc291c1a0f Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 31 Oct 2022 17:07:10 +0000 Subject: [PATCH 089/244] Correct case of cJSON include --- src/io_element.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io_element.h b/src/io_element.h index 88be2ae7b..81f958f25 100644 --- a/src/io_element.h +++ b/src/io_element.h @@ -15,7 +15,7 @@ #define LUDWIG_IO_ELEMENT_H #include "mpi.h" -#include "util_cjson.h" +#include "util_cJSON.h" /* Utility */ From ec432847ea80adb2661de3f393ad2f0ba9ca5987 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 31 Oct 2022 17:22:47 +0000 Subject: [PATCH 090/244] Correction --- tests/unit/test_io_cart_sub.c | 1 - tests/unit/test_io_element.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/unit/test_io_cart_sub.c b/tests/unit/test_io_cart_sub.c index d2e026a88..f68b58c25 100644 --- a/tests/unit/test_io_cart_sub.c +++ b/tests/unit/test_io_cart_sub.c @@ -107,7 +107,6 @@ int test_io_cart_sub_create(pe_t * pe) { assert(myresult == MPI_IDENT); MPI_Comm_compare(comm, iosub.comm, &myresult); - printf("cart io %d %d result %d\n", comm, iosub.comm, myresult); assert(myresult == MPI_CONGRUENT); } diff --git a/tests/unit/test_io_element.c b/tests/unit/test_io_element.c index 646087402..9af0dafa9 100644 --- a/tests/unit/test_io_element.c +++ b/tests/unit/test_io_element.c @@ -138,7 +138,7 @@ int test_io_element_from_json(void) { /* Null JSON object is a fail */ cJSON * json = NULL; io_element_t element = io_element_null(); - int ifail = io_element_from_json(json, &element); + ifail = io_element_from_json(json, &element); assert(ifail == -1); } From df5408064ef871f3584b8bf260889bf5b618d097 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 31 Oct 2022 17:37:53 +0000 Subject: [PATCH 091/244] Use int and remove FIXME comment --- src/util_cJSON.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/util_cJSON.c b/src/util_cJSON.c index 89054b96d..3559281bf 100644 --- a/src/util_cJSON.c +++ b/src/util_cJSON.c @@ -1828,7 +1828,7 @@ static cJSON_bool print_object(const cJSON * const item, printbuffer * const out CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) { cJSON *child = NULL; - size_t size = 0; + int size = 0; if (array == NULL) { @@ -1843,9 +1843,7 @@ CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) child = child->next; } - /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ - - return (int)size; + return size; } static cJSON* get_array_item(const cJSON *array, size_t index) From fa9a8cb978aae462132b2c0450731a8e3ef54c86 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 1 Nov 2022 13:15:54 +0000 Subject: [PATCH 092/244] Add util for io quantities --- src/util_io.c | 154 ++++++++++++++++++++++++++++++++++++++ src/util_io.h | 15 ++++ tests/unit/test_util_io.c | 99 ++++++++++++++++++++++++ tests/unit/tests.c | 1 + tests/unit/tests.h | 1 + 5 files changed, 270 insertions(+) create mode 100644 src/util_io.c create mode 100644 src/util_io.h create mode 100644 tests/unit/test_util_io.c diff --git a/src/util_io.c b/src/util_io.c new file mode 100644 index 000000000..22db1d519 --- /dev/null +++ b/src/util_io.c @@ -0,0 +1,154 @@ +/***************************************************************************** + * + * util_io.c + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include +#include + +#include "util_io.h" + +/***************************************************************************** + * + * util_io_string_to_mpi_datatype + * + * An unrecognised string will return MPI_TYPE_NULL. + * + *****************************************************************************/ + +MPI_Datatype util_io_string_to_mpi_datatype(const char * str) { + + MPI_Datatype dt = MPI_DATATYPE_NULL; + + if (str == NULL) { + dt = MPI_DATATYPE_NULL; + } + else if (strcmp(str, "MPI_CHAR") == 0) { + dt = MPI_CHAR; + } + else if (strcmp(str, "MPI_SHORT") == 0) { + dt = MPI_SHORT; + } + else if (strcmp(str, "MPI_INT") == 0) { + dt = MPI_INT; + } + else if (strcmp(str, "MPI_LONG") == 0) { + dt = MPI_LONG; + } + else if (strcmp(str, "MPI_UNSIGNED") == 0) { + dt = MPI_UNSIGNED; + } + else if (strcmp(str, "MPI_UNSIGNED_CHAR") == 0) { + dt = MPI_UNSIGNED_CHAR; + } + else if (strcmp(str, "MPI_UNSIGNED_SHORT") == 0) { + dt = MPI_UNSIGNED_SHORT; + } + else if (strcmp(str, "MPI_UNSIGNED_LONG") == 0) { + dt = MPI_UNSIGNED_LONG; + } + else if (strcmp(str, "MPI_FLOAT") == 0) { + dt = MPI_DOUBLE; + } + else if (strcmp(str, "MPI_DOUBLE") == 0) { + dt = MPI_DOUBLE; + } + else if (strcmp(str, "MPI_LONG_DOUBLE") == 0) { + dt = MPI_LONG_DOUBLE; + } + else if (strcmp(str, "MPI_BYTE") == 0) { + dt = MPI_BYTE; + } + else if (strcmp(str, "MPI_INT32_T") == 0) { + dt = MPI_INT32_T; + } + else if (strcmp(str, "MPI_INT64_T") == 0) { + dt = MPI_INT64_T; + } + else if (strcmp(str, "MPI_PACKED") == 0) { + dt = MPI_PACKED; + } + + return dt; +} + +/***************************************************************************** + * + * util_io_mpi_datatype_to_string + * + * The string is exactly what one would expect for an intrinsic + * data type name. User defined types will (and unrecognised + * intrinsic types) will return MPI_TYPE_NULL. + * + *****************************************************************************/ + +const char * util_io_mpi_datatype_to_string(MPI_Datatype dt) { + + const char * str = NULL; + + if (dt == MPI_DATATYPE_NULL) { + str = "MPI_DATATYPE_NULL"; + } + else if (dt == MPI_CHAR) { + str = "MPI_CHAR"; + } + else if (dt == MPI_SHORT) { + str = "MPI_SHORT"; + } + else if (dt == MPI_INT) { + str = "MPI_INT"; + } + else if (dt == MPI_LONG) { + str = "MPI_LONG"; + } + else if (dt == MPI_UNSIGNED) { + str = "MPI_UNSIGNED"; + } + else if (dt == MPI_UNSIGNED_CHAR) { + str = "MPI_UNSIGNED_CHAR"; + } + else if (dt == MPI_UNSIGNED_SHORT) { + str = "MPI_UNSIGNED_SHORT"; + } + else if (dt == MPI_UNSIGNED_LONG) { + str = "MPI_UNSIGNED_LONG"; + } + else if (dt == MPI_FLOAT) { + str = "MPI_FLOAT"; + } + else if (dt == MPI_DOUBLE) { + str = "MPI_DOUBLE"; + } + else if (dt == MPI_LONG_DOUBLE) { + str = "MPI_LONG_DOUBLE"; + } + else if (dt == MPI_BYTE) { + str = "MPI_BYTE"; + } + else if (dt == MPI_INT32_T) { + str = "MPI_INT32_T"; + } + else if (dt == MPI_INT64_T) { + str = "MPI_INT64_T"; + } + else if (dt == MPI_PACKED) { + str = "MPI_PACKED"; + } + else { + /* Unrecognised/Not included yet. */ + str = "MPI_DATATYPE_NULL"; + } + + assert(str != NULL); + + return str; +} diff --git a/src/util_io.h b/src/util_io.h new file mode 100644 index 000000000..bc79fb73b --- /dev/null +++ b/src/util_io.h @@ -0,0 +1,15 @@ +/***************************************************************************** + * + * util_io.h + * + *****************************************************************************/ + +#ifndef LUDWIG_UTIL_IO_H +#define LUDWIG_UTIL_IO_H + +#include + +MPI_Datatype util_io_string_to_mpi_datatype(const char * str); +const char * util_io_mpi_datatype_to_string(MPI_Datatype dt); + +#endif diff --git a/tests/unit/test_util_io.c b/tests/unit/test_util_io.c new file mode 100644 index 000000000..f9b63c656 --- /dev/null +++ b/tests/unit/test_util_io.c @@ -0,0 +1,99 @@ +/***************************************************************************** + * + * test_util_io.c + * + * + *****************************************************************************/ + +#include +#include + +#include "pe.h" +#include "util_io.h" + +int test_util_io_string_to_mpi_datatype(void); +int test_util_io_mpi_datatype_to_string(void); + +/***************************************************************************** + * + * test_util_io_suite + * + *****************************************************************************/ + +int test_util_io_suite(void) { + + pe_t * pe = NULL; + + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + test_util_io_string_to_mpi_datatype(); + test_util_io_mpi_datatype_to_string(); + + pe_info(pe, "%-9s %s\n", "PASS", __FILE__); + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_util_io_string_to_mpi_datatype + * + *****************************************************************************/ + +int test_util_io_string_to_mpi_datatype(void) { + + { + MPI_Datatype dt = util_io_string_to_mpi_datatype(NULL); + assert(dt == MPI_DATATYPE_NULL); + } + + { + MPI_Datatype dt = util_io_string_to_mpi_datatype("MPI_DOUBLE"); + assert(dt == MPI_DOUBLE); + } + + { + MPI_Datatype dt = util_io_string_to_mpi_datatype("MPI_INT"); + assert(dt == MPI_INT); + } + + /* An unrecognised value -> MPI_DATATYPE_NULL */ + { + MPI_Datatype dt = util_io_string_to_mpi_datatype("RUBBISH"); + assert(dt == MPI_DATATYPE_NULL); + } + + return 0; +} + +/***************************************************************************** + * + * test_util_io_mpi_datatype_to_string + * + *****************************************************************************/ + +int test_util_io_mpi_datatype_to_string(void) { + + int ifail = 0; + + { + const char * str = util_io_mpi_datatype_to_string(MPI_DATATYPE_NULL); + ifail += strcmp(str, "MPI_DATATYPE_NULL"); + assert(ifail == 0); + } + + { + const char * str = util_io_mpi_datatype_to_string(MPI_DOUBLE); + ifail += strcmp(str, "MPI_DOUBLE"); + assert(ifail == 0); + } + + { + const char * str = util_io_mpi_datatype_to_string(MPI_INT); + ifail += strcmp(str, "MPI_INT"); + assert(ifail == 0); + } + + return ifail; +} diff --git a/tests/unit/tests.c b/tests/unit/tests.c index 578d7d44e..3bdbe8aa3 100644 --- a/tests/unit/tests.c +++ b/tests/unit/tests.c @@ -117,6 +117,7 @@ __host__ int tests_create() { test_timer_suite(); test_util_suite(); test_util_fopen_suite(); + test_util_io_suite(); test_util_sum_suite(); test_visc_arrhenius_suite(); test_wall_suite(); diff --git a/tests/unit/tests.h b/tests/unit/tests.h index b029b24cc..83c9a9a76 100644 --- a/tests/unit/tests.h +++ b/tests/unit/tests.h @@ -98,6 +98,7 @@ int test_rt_suite(void); int test_timer_suite(void); int test_util_suite(void); int test_util_fopen_suite(void); +int test_util_io_suite(void); int test_util_sum_suite(void); int test_visc_arrhenius_suite(void); int test_wall_suite(void); From 5c5d4666342f6a1c22b340d106d7327ee2752678 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 1 Nov 2022 13:16:58 +0000 Subject: [PATCH 093/244] Complete io_element_t --- src/io_element.c | 35 ++++++++++++--------- src/io_element.h | 4 ++- tests/unit/test_io_element.c | 61 ++++++++++++++++++++++++++++++++++-- 3 files changed, 82 insertions(+), 18 deletions(-) diff --git a/src/io_element.c b/src/io_element.c index cf7684ab1..7b5be0644 100644 --- a/src/io_element.c +++ b/src/io_element.c @@ -83,23 +83,25 @@ io_element_t io_element_null(void) { * *****************************************************************************/ -int io_element_to_json(const io_element_t * element, cJSON * json) { +int io_element_to_json(const io_element_t * element, cJSON ** json) { int ifail = 0; assert(element); - if (json != NULL) { + if (json == NULL || *json != NULL) { ifail = -1; } else { - json = cJSON_CreateObject(); + cJSON * myjson = cJSON_CreateObject(); - cJSON_AddStringToObject(json, "MPI_Datatype", "MPI_DOUBLE TBC"); - cJSON_AddNumberToObject(json, "Datasize (bytes)", element->datasize); - cJSON_AddNumberToObject(json, "Count", element->count); - cJSON_AddStringToObject(json, "Endianness", + cJSON_AddStringToObject(myjson, "MPI_Datatype", + util_io_mpi_datatype_to_string(element->datatype)); + cJSON_AddNumberToObject(myjson, "Size (bytes)", element->datasize); + cJSON_AddNumberToObject(myjson, "Count", element->count); + cJSON_AddStringToObject(myjson, "Endianness", io_endian_to_string(element->endian)); + *json = myjson; } return ifail; @@ -115,29 +117,32 @@ int io_element_from_json(const cJSON * json, io_element_t * element) { int ifail = 0; - assert(element); - - if (json == NULL) { + if (json == NULL || element == NULL) { ifail = -1; } else { cJSON * datatype = cJSON_GetObjectItem(json, "MPI_Datatype"); - cJSON * datasize = cJSON_GetObjectItem(json, "Datasize (bytes)"); + cJSON * datasize = cJSON_GetObjectItem(json, "Size (bytes)"); cJSON * count = cJSON_GetObjectItem(json, "Count"); cJSON * endianness = cJSON_GetObjectItem(json, "Endianness"); if (datatype) { - /* Another string to be converted to MPI_Datatype */ - element->datatype = MPI_DOUBLE; /* TBC */ + char * str = cJSON_GetStringValue(datatype); + element->datatype = util_io_string_to_mpi_datatype(str); } + if (datasize) element->datasize = cJSON_GetNumberValue(datasize); if (count) element->count = cJSON_GetNumberValue(count); if (endianness) { - char * str = {0}; - str = cJSON_GetStringValue(endianness); + char * str = cJSON_GetStringValue(endianness); element->endian = io_endian_from_string(str); } + /* Indicate what failed, if anything */ + if (!datatype) ifail += 1; + if (!datasize) ifail += 2; + if (!count) ifail += 4; + if (!endianness) ifail += 8; } return ifail; diff --git a/src/io_element.h b/src/io_element.h index 81f958f25..fb462a3ef 100644 --- a/src/io_element.h +++ b/src/io_element.h @@ -15,6 +15,7 @@ #define LUDWIG_IO_ELEMENT_H #include "mpi.h" +#include "util_io.h" #include "util_cJSON.h" /* Utility */ @@ -28,6 +29,7 @@ typedef enum { io_endian_enum_t io_endian_from_string(const char * str); const char * io_endian_to_string(io_endian_enum_t endian); +/* I/O element */ typedef struct io_element_s io_element_t; @@ -40,6 +42,6 @@ struct io_element_s { io_element_t io_element_null(void); int io_element_from_json(const cJSON * json, io_element_t * element); -int io_element_to_json(const io_element_t * element, cJSON * json); +int io_element_to_json(const io_element_t * element, cJSON ** json); #endif diff --git a/tests/unit/test_io_element.c b/tests/unit/test_io_element.c index 9af0dafa9..c21a0e177 100644 --- a/tests/unit/test_io_element.c +++ b/tests/unit/test_io_element.c @@ -142,6 +142,26 @@ int test_io_element_from_json(void) { assert(ifail == -1); } + { + /* Typical example */ + io_element_t element = io_element_null(); + const char * jstr = "{\"MPI_Datatype\": \"MPI_INT\"," + "\"Size (bytes)\": 4," + "\"Count\": 1," + "\"Endianness\": \"BIG_ENDIAN\"}"; + + cJSON * json = cJSON_Parse(jstr); + assert(json); + ifail = io_element_from_json(json, &element); + assert(ifail == 0); + assert(element.datatype == MPI_INT); + assert(element.datasize == 4); + assert(element.count == 1); + assert(element.endian == IO_ENDIAN_BIG_ENDIAN); + + cJSON_Delete(json); + } + return ifail; } @@ -156,12 +176,49 @@ int test_io_element_to_json(void) { int ifail = 0; { + /* A typical example */ io_element_t element = {.datatype = MPI_DOUBLE, - .datasize = 1, + .datasize = sizeof(double), .count = 3, .endian = IO_ENDIAN_LITTLE_ENDIAN}; cJSON * json = NULL; - ifail = io_element_to_json(&element, json); + ifail = io_element_to_json(&element, &json); + assert(ifail == 0); + + { + /* Datatype */ + MPI_Datatype dt = MPI_DATATYPE_NULL; + cJSON * jsondt = cJSON_GetObjectItemCaseSensitive(json, "MPI_Datatype"); + assert(jsondt); + dt = util_io_string_to_mpi_datatype(cJSON_GetStringValue(jsondt)); + assert(dt == MPI_DOUBLE); + } + { + /* Datasize */ + size_t sz = 0; + cJSON * jsonsz = cJSON_GetObjectItemCaseSensitive(json, "Size (bytes)"); + assert(jsonsz); + assert(cJSON_IsNumber(jsonsz)); + sz = cJSON_GetNumberValue(jsonsz); + assert(sz == sizeof(double)); + } + { + /* Count */ + int count = -1; + cJSON * jsonct = cJSON_GetObjectItemCaseSensitive(json, "Count"); + assert(jsonct); + assert(cJSON_IsNumber(jsonct)); + count = cJSON_GetNumberValue(jsonct); + assert(count == 3); + } + { + io_endian_enum_t endian = IO_ENDIAN_UNKNOWN; + cJSON * jsonend = cJSON_GetObjectItemCaseSensitive(json, "Endianness"); + assert(jsonend); + assert(cJSON_IsString(jsonend)); + endian = io_endian_from_string(cJSON_GetStringValue(jsonend)); + assert(endian == IO_ENDIAN_LITTLE_ENDIAN); + } cJSON_Delete(json); } From 0c998dc5f6e6cb7f4bdb7fd93c4b411524f32a7c Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 4 Nov 2022 16:30:28 +0000 Subject: [PATCH 094/244] Cosmetic update --- src/util_cJSON.c | 49 ++++++++++++++++++++---------------------------- src/util_io.c | 4 ++++ src/util_io.h | 7 +++++++ 3 files changed, 31 insertions(+), 29 deletions(-) diff --git a/src/util_cJSON.c b/src/util_cJSON.c index 3559281bf..c27c75501 100644 --- a/src/util_cJSON.c +++ b/src/util_cJSON.c @@ -3020,21 +3020,18 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons return true; case cJSON_Number: - if (compare_double(a->valuedouble, b->valuedouble)) - { - return true; + if (compare_double(a->valuedouble, b->valuedouble)) { + return true; } return false; case cJSON_String: case cJSON_Raw: - if ((a->valuestring == NULL) || (b->valuestring == NULL)) - { - return false; + if ((a->valuestring == NULL) || (b->valuestring == NULL)) { + return false; } - if (strcmp(a->valuestring, b->valuestring) == 0) - { - return true; + if (strcmp(a->valuestring, b->valuestring) == 0) { + return true; } return false; @@ -3044,15 +3041,13 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons cJSON *a_element = a->child; cJSON *b_element = b->child; - for (; (a_element != NULL) && (b_element != NULL);) - { - if (!cJSON_Compare(a_element, b_element, case_sensitive)) - { - return false; - } + for (; (a_element != NULL) && (b_element != NULL);) { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) { + return false; + } - a_element = a_element->next; - b_element = b_element->next; + a_element = a_element->next; + b_element = b_element->next; } /* one of the arrays is longer than the other */ @@ -3071,14 +3066,12 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons { /* TODO This has O(n^2) runtime, which is horrible! */ b_element = get_object_item(b, a_element->string, case_sensitive); - if (b_element == NULL) - { - return false; + if (b_element == NULL) { + return false; } - if (!cJSON_Compare(a_element, b_element, case_sensitive)) - { - return false; + if (!cJSON_Compare(a_element, b_element, case_sensitive)) { + return false; } } @@ -3087,14 +3080,12 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons cJSON_ArrayForEach(b_element, b) { a_element = get_object_item(a, b_element->string, case_sensitive); - if (a_element == NULL) - { - return false; + if (a_element == NULL) { + return false; } - if (!cJSON_Compare(b_element, a_element, case_sensitive)) - { - return false; + if (!cJSON_Compare(b_element, a_element, case_sensitive)) { + return false; } } diff --git a/src/util_io.c b/src/util_io.c index 22db1d519..e8493a118 100644 --- a/src/util_io.c +++ b/src/util_io.c @@ -77,6 +77,10 @@ MPI_Datatype util_io_string_to_mpi_datatype(const char * str) { else if (strcmp(str, "MPI_PACKED") == 0) { dt = MPI_PACKED; } + else { + /* Not strictly necessary, but include anyway... */ + dt = MPI_DATATYPE_NULL; + } return dt; } diff --git a/src/util_io.h b/src/util_io.h index bc79fb73b..c8b59b584 100644 --- a/src/util_io.h +++ b/src/util_io.h @@ -2,6 +2,13 @@ * * util_io.h * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * *****************************************************************************/ #ifndef LUDWIG_UTIL_IO_H From accc30fb0ebb0bca16ffb02f8194233bc943cb5b Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 4 Nov 2022 16:31:00 +0000 Subject: [PATCH 095/244] Refactor small container --- src/io_aggr.h | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 src/io_aggr.h diff --git a/src/io_aggr.h b/src/io_aggr.h deleted file mode 100644 index 59f9e727b..000000000 --- a/src/io_aggr.h +++ /dev/null @@ -1,26 +0,0 @@ -/***************************************************************************** - * - * io_aggr.h - * - * Edinburgh Soft Matter and Statistical Physics Group and - * Edinburgh Parallel Computing Centre - * - * (c) 2022 The University of Edinburgh - * - * Kevin Stratford (kevin@epcc.ed.ac.uk) - * - *****************************************************************************/ - -#ifndef LUDWIG_IO_AGGR_H -#define LUDWIG_IO_AGGR_H - -#include - -typedef struct io_aggr_s io_aggr_t; - -struct io_aggr_s { - MPI_Datatype etype; /* Data type for element */ - size_t esize; /* Number bytes per element */ -}; - -#endif From 87339b5aa23e3eaa1b761161d58f536b8e20a69c Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 4 Nov 2022 16:31:18 +0000 Subject: [PATCH 096/244] Add endianness --- src/io_element.c | 20 ++++++++++++++++++++ src/io_element.h | 1 + 2 files changed, 21 insertions(+) diff --git a/src/io_element.c b/src/io_element.c index 7b5be0644..15dd7fec4 100644 --- a/src/io_element.c +++ b/src/io_element.c @@ -8,6 +8,8 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * + * (c) 2022 The University of Edinburgh + * * Kevin Stratford (kevin@epcc.ed.ac.uk) * *****************************************************************************/ @@ -17,6 +19,24 @@ #include "io_element.h" +/***************************************************************************** + * + * io_endianness + * + *****************************************************************************/ + +io_endian_enum_t io_endianness(void) { + + io_endian_enum_t endian = IO_ENDIAN_LITTLE_ENDIAN; /* usually */ + + { + const int p = 1; + if (*(char *) &p == 0) endian = IO_ENDIAN_BIG_ENDIAN; + } + + return endian; +} + /***************************************************************************** * * io_endian_from_string diff --git a/src/io_element.h b/src/io_element.h index fb462a3ef..c2c8cbf23 100644 --- a/src/io_element.h +++ b/src/io_element.h @@ -26,6 +26,7 @@ typedef enum { IO_ENDIAN_BIG_ENDIAN } io_endian_enum_t; +io_endian_enum_t io_endianness(void); io_endian_enum_t io_endian_from_string(const char * str); const char * io_endian_to_string(io_endian_enum_t endian); From 07fc7efc548d71001d929577e019b8216ecd2fa3 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 4 Nov 2022 16:37:58 +0000 Subject: [PATCH 097/244] Refactor to element type --- src/field.c | 19 ++++++++++++----- src/field.h | 6 +++--- src/io_aggr_buf.c | 12 ++++++----- src/io_aggr_buf.h | 18 +++++++++------- tests/unit/test_field.c | 21 ++++++++++++------ tests/unit/test_io_aggr_buf.c | 19 ++++++++++++----- tests/unit/test_io_aggr_mpio.c | 39 +++++++++++++++++++--------------- tests/unit/test_io_element.c | 2 ++ tests/unit/test_util_io.c | 6 ++++++ 9 files changed, 93 insertions(+), 49 deletions(-) diff --git a/src/field.c b/src/field.c index 6fb7e02a8..87b76981e 100644 --- a/src/field.c +++ b/src/field.c @@ -100,11 +100,20 @@ __host__ int field_create(pe_t * pe, cs_t * cs, lees_edw_t * le, field_init(obj, opts->nhcomm, le); field_halo_create(obj, &obj->h); - /* I/O aggregator */ - obj->aggr_asc.etype = MPI_CHAR; - obj->aggr_asc.esize = 23*sizeof(char); - obj->aggr_bin.etype = MPI_DOUBLE; - obj->aggr_bin.esize = obj->opts.ndata*sizeof(double); + /* I/O single record information */ + + { + io_element_t elasc = {.datatype = MPI_CHAR, + .datasize = sizeof(char), + .count = 1 + 23*obj->opts.ndata, + .endian = io_endianness()}; + io_element_t elbin = {.datatype = MPI_DOUBLE, + .datasize = sizeof(double), + .count = obj->opts.ndata, + .endian = io_endianness()}; + obj->ascii = elasc; + obj->binary = elbin; + } if (obj->opts.haloverbose) field_halo_info(obj); diff --git a/src/field.h b/src/field.h index 23c751b19..1a7b5a2b5 100644 --- a/src/field.h +++ b/src/field.h @@ -22,7 +22,7 @@ #include "pe.h" #include "coords.h" -#include "io_aggr.h" /* I/O data aggregation */ +#include "io_element.h" #include "io_aggr_buf.h" /* Aggregation buffer */ #include "io_harness.h" /* To be removed in favour of io_aggr_t */ #include "leesedwards.h" @@ -63,8 +63,8 @@ struct field_s { pe_t * pe; /* Parallel environment */ cs_t * cs; /* Coordinate system */ lees_edw_t * le; /* Lees-Edwards */ - io_aggr_t aggr_asc; /* I/O aggregator information */ - io_aggr_t aggr_bin; /* Binary */ + io_element_t ascii; /* I/O record information (ascii) */ + io_element_t binary; /* Binary */ io_info_t * info; /* I/O Handler (to be removed) */ halo_swap_t * halo; /* Halo swap driver object */ field_halo_t h; /* Host halo */ diff --git a/src/io_aggr_buf.c b/src/io_aggr_buf.c index 9282c1f69..816d81cf2 100644 --- a/src/io_aggr_buf.c +++ b/src/io_aggr_buf.c @@ -1,6 +1,6 @@ /***************************************************************************** * - * io_aggr_buf.c + * io_aggregator.c * * Temporary buffer for lattice quantity i/o. A minimal container. * @@ -24,18 +24,20 @@ * *****************************************************************************/ -int io_aggr_buf_create(size_t lsz, cs_limits_t lim, io_aggr_buf_t * aggr) { +int io_aggr_buf_create(io_element_t e, cs_limits_t lim, io_aggr_buf_t * aggr) { assert(aggr); - assert(cs_limits_size(lim) > 0); /* No zero-size buffers */ *aggr = (io_aggr_buf_t) {0}; - aggr->szelement = lsz; - aggr->szbuf = lsz*sizeof(char)*cs_limits_size(lim); + aggr->element = e; + aggr->szelement = e.count*e.datasize; + aggr->szbuf = aggr->szelement*cs_limits_size(lim); aggr->lim = lim; aggr->buf = (char *) malloc(aggr->szbuf*sizeof(char)); + + assert(aggr->szbuf > 0); /* No zero size buffers */ assert(aggr->buf); return 0; diff --git a/src/io_aggr_buf.h b/src/io_aggr_buf.h index a407f6654..1442d2fe1 100644 --- a/src/io_aggr_buf.h +++ b/src/io_aggr_buf.h @@ -1,6 +1,6 @@ /***************************************************************************** * - * io_aggr_buf.h + * io_aggregator.h * * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre @@ -11,22 +11,24 @@ * *****************************************************************************/ -#ifndef LUDWIG_IO_AGGR_BUF_H -#define LUDWIG_IO_AGGR_BUF_H +#ifndef LUDWIG_IO_AGGREGATOR_H +#define LUDWIG_IO_AGGREGATOR_H #include #include "cs_limits.h" +#include "io_element.h" typedef struct io_aggr_buf_s io_aggr_buf_t; struct io_aggr_buf_s { - size_t szelement; /* element sz in bytes */ - size_t szbuf; /* total sz */ - cs_limits_t lim; /* 3-d limits of buffer */ - char * buf; + io_element_t element; /* Element information */ + cs_limits_t lim; /* 3-d limits of buffer */ + size_t szelement; /* bytes per record */ + size_t szbuf; /* total size of buffer (bytes) */ + char * buf; /* Storage space */ }; -int io_aggr_buf_create(size_t lsz, cs_limits_t lim, io_aggr_buf_t * aggr); +int io_aggr_buf_create(io_element_t el, cs_limits_t lim, io_aggr_buf_t * aggr); int io_aggr_buf_free(io_aggr_buf_t * aggr); #endif diff --git a/tests/unit/test_field.c b/tests/unit/test_field.c index 4712dc46d..2ce0edc78 100644 --- a/tests/unit/test_field.c +++ b/tests/unit/test_field.c @@ -601,7 +601,7 @@ int test_field_write_buf_ascii(pe_t * pe) { * * test_field_io_aggr_pack * - * It is convenient to test field_io_aggr_unpack() at te same time. + * It is convenient to test field_io_aggr_unpack() at the same time. * *****************************************************************************/ @@ -622,20 +622,27 @@ int test_field_io_aggr_pack(pe_t * pe) { /* This should be elsewhere as part of test_field_create() */ { /* Note one can use == with pre-defined data types */ - assert(field->aggr_asc.etype == MPI_CHAR); - assert(field->aggr_bin.etype == MPI_DOUBLE); - assert(field->aggr_asc.esize == 23*sizeof(char)); - assert(field->aggr_bin.esize == field->nf*sizeof(double)); + + assert(field->ascii.datatype == MPI_CHAR); + assert(field->ascii.datasize == sizeof(char)); + assert(field->ascii.count == 1 + 23*nf); + assert(field->ascii.endian == io_endianness()); + + assert(field->binary.datatype == MPI_DOUBLE); + assert(field->binary.datasize == sizeof(double)); + assert(field->binary.count == nf); + assert(field->binary.endian == io_endianness()); } { + /* Default is binary */ int nlocal[3] = {0}; cs_nlocal(cs, nlocal); { cs_limits_t lim = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; io_aggr_buf_t buf = {0}; - io_aggr_buf_create(field->aggr_bin.esize, lim, &buf); + io_aggr_buf_create(field->binary, lim, &buf); util_field_data_check_set(field); field_io_aggr_pack(field, buf); @@ -652,6 +659,8 @@ int test_field_io_aggr_pack(pe_t * pe) { } } + /* Repeat for ASCII */ + field_free(field); cs_free(cs); diff --git a/tests/unit/test_io_aggr_buf.c b/tests/unit/test_io_aggr_buf.c index 502001e72..e0a4155d1 100644 --- a/tests/unit/test_io_aggr_buf.c +++ b/tests/unit/test_io_aggr_buf.c @@ -32,7 +32,8 @@ int test_io_aggr_buf_suite(void) { pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); /* If the size of the struct has changed, tests need to be changed... */ - assert(sizeof(io_aggr_buf_t) == 48); + + assert(sizeof(io_aggr_buf_t) == 72); test_io_aggr_buf_create(); @@ -54,14 +55,22 @@ int test_io_aggr_buf_create(void) { { /* Create and free. */ - size_t lsz = 99; + io_element_t element = {.datatype = MPI_DOUBLE, + .datasize = sizeof(double), + .count = 3, + .endian = io_endianness()}; cs_limits_t lim = {-2, 18, 1, 8, 1, 4}; io_aggr_buf_t aggr = {0}; - io_aggr_buf_create(lsz, lim, &aggr); + io_aggr_buf_create(element, lim, &aggr); + + assert(aggr.element.datatype == MPI_DOUBLE); + assert(aggr.element.datasize == sizeof(double)); + assert(aggr.element.count == 3); + assert(aggr.element.endian == io_endianness()); - assert(aggr.szelement == lsz); - assert(aggr.szbuf == lsz*cs_limits_size(lim)); + assert(aggr.szelement == element.datasize*element.count); + assert(aggr.szbuf == aggr.szelement*cs_limits_size(lim)); assert(aggr.lim.imin == lim.imin); /* Assume sufficient */ assert(aggr.buf); diff --git a/tests/unit/test_io_aggr_mpio.c b/tests/unit/test_io_aggr_mpio.c index fd27b88d1..eac7e980b 100644 --- a/tests/unit/test_io_aggr_mpio.c +++ b/tests/unit/test_io_aggr_mpio.c @@ -21,12 +21,11 @@ #include "pe.h" #include "coords.h" -#include "io_aggr.h" #include "io_aggr_buf_mpio.h" -int test_io_aggr_mpio_write(pe_t * pe, cs_t * cs, io_aggr_t aggr, +int test_io_aggr_mpio_write(pe_t * pe, cs_t * cs, io_element_t el, const char * filename); -int test_io_aggr_mpio_read(pe_t * pe, cs_t * cs, io_aggr_t aggr, +int test_io_aggr_mpio_read(pe_t * pe, cs_t * cs, io_element_t el, const char * filename); int test_io_aggr_buf_pack_asc(cs_t * cs, io_aggr_buf_t buf); @@ -59,11 +58,14 @@ int test_io_aggr_mpio_suite(void) { /* ASCII: write then read */ { - io_aggr_t aggr = {.etype = MPI_CHAR, .esize = 28*sizeof(char)}; + io_element_t element = {.datatype = MPI_CHAR, + .datasize = sizeof(char), + .count = 28, + .endian = io_endianness()}; const char * filename = "io-aggr-mpio-asc.dat"; - test_io_aggr_mpio_write(pe, cs, aggr, filename); - test_io_aggr_mpio_read(pe, cs, aggr, filename); + test_io_aggr_mpio_write(pe, cs, element, filename); + test_io_aggr_mpio_read(pe, cs, element, filename); MPI_Barrier(MPI_COMM_WORLD); if (pe_mpi_rank(pe) == 0) remove(filename); @@ -71,11 +73,14 @@ int test_io_aggr_mpio_suite(void) { /* Binary: write thne read */ { - io_aggr_t aggr = {.etype = MPI_INT64_T, .esize = sizeof(int64_t)}; + io_element_t element = {.datatype = MPI_INT64_T, + .datasize = sizeof(int64_t), + .count = 1, + .endian = io_endianness()}; const char * filename = "io-aggr-mpio-bin.dat"; - test_io_aggr_mpio_write(pe, cs, aggr, filename); - test_io_aggr_mpio_read(pe, cs, aggr, filename); + test_io_aggr_mpio_write(pe, cs, element, filename); + test_io_aggr_mpio_read(pe, cs, element, filename); MPI_Barrier(MPI_COMM_WORLD); if (pe_mpi_rank(pe) == 0) remove(filename); @@ -94,7 +99,7 @@ int test_io_aggr_mpio_suite(void) { * *****************************************************************************/ -int test_io_aggr_mpio_write(pe_t * pe, cs_t * cs, io_aggr_t aggr, +int test_io_aggr_mpio_write(pe_t * pe, cs_t * cs, io_element_t element, const char * filename) { assert(pe); @@ -110,10 +115,10 @@ int test_io_aggr_mpio_write(pe_t * pe, cs_t * cs, io_aggr_t aggr, cs_limits_t lim = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; io_aggr_buf_t buf = {0}; - io_aggr_buf_create(aggr.esize, lim, &buf); + io_aggr_buf_create(element, lim, &buf); - if (aggr.etype == MPI_CHAR) test_io_aggr_buf_pack_asc(cs, buf); - if (aggr.etype == MPI_INT64_T) test_io_aggr_buf_pack_bin(cs, buf); + if (element.datatype == MPI_CHAR) test_io_aggr_buf_pack_asc(cs, buf); + if (element.datatype == MPI_INT64_T) test_io_aggr_buf_pack_bin(cs, buf); io_aggr_mpio_write(pe, cs, filename, &buf); @@ -129,7 +134,7 @@ int test_io_aggr_mpio_write(pe_t * pe, cs_t * cs, io_aggr_t aggr, * *****************************************************************************/ -int test_io_aggr_mpio_read(pe_t * pe, cs_t * cs, io_aggr_t aggr, +int test_io_aggr_mpio_read(pe_t * pe, cs_t * cs, io_element_t element, const char * filename) { assert(pe); @@ -144,12 +149,12 @@ int test_io_aggr_mpio_read(pe_t * pe, cs_t * cs, io_aggr_t aggr, cs_limits_t lim = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; io_aggr_buf_t buf = {0}; - io_aggr_buf_create(aggr.esize, lim ,&buf); + io_aggr_buf_create(element, lim ,&buf); io_aggr_mpio_read(pe, cs, filename, &buf); - if (aggr.etype == MPI_CHAR) test_io_aggr_buf_unpack_asc(cs, buf); - if (aggr.etype == MPI_INT64_T) test_io_aggr_buf_unpack_bin(cs, buf); + if (element.datatype == MPI_CHAR) test_io_aggr_buf_unpack_asc(cs, buf); + if (element.datatype == MPI_INT64_T) test_io_aggr_buf_unpack_bin(cs, buf); io_aggr_buf_free(&buf); } diff --git a/tests/unit/test_io_element.c b/tests/unit/test_io_element.c index c21a0e177..5a708eb71 100644 --- a/tests/unit/test_io_element.c +++ b/tests/unit/test_io_element.c @@ -43,6 +43,7 @@ int test_io_element_suite(void) { test_io_element_from_json(); test_io_element_to_json(); + pe_info(pe, "%-9s %s\n", "PASS", __FILE__); pe_free(pe); return 0; @@ -140,6 +141,7 @@ int test_io_element_from_json(void) { io_element_t element = io_element_null(); ifail = io_element_from_json(json, &element); assert(ifail == -1); + assert(json == NULL); /* Nothing to release */ } { diff --git a/tests/unit/test_util_io.c b/tests/unit/test_util_io.c index f9b63c656..e769d3443 100644 --- a/tests/unit/test_util_io.c +++ b/tests/unit/test_util_io.c @@ -2,6 +2,12 @@ * * test_util_io.c * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2002 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) * *****************************************************************************/ From 6962c728cc20ec217ae8f280a16d14bd8de44246 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 4 Nov 2022 17:37:34 +0000 Subject: [PATCH 098/244] Refactor names: largely cosmetic --- src/field.c | 38 ++++++++++++----------- src/field.h | 4 +-- src/io_aggr_buf.c | 13 ++++---- src/io_aggr_buf.h | 9 +++--- src/io_aggr_buf_mpio.c | 6 ++-- src/io_aggr_buf_mpio.h | 4 +-- tests/unit/test_field.c | 10 +++--- tests/unit/test_io_aggr_buf.c | 20 ++++++------ tests/unit/test_io_aggr_mpio.c | 56 +++++++++++++++++----------------- 9 files changed, 82 insertions(+), 78 deletions(-) diff --git a/src/field.c b/src/field.c index 87b76981e..d7be50f7f 100644 --- a/src/field.c +++ b/src/field.c @@ -1146,10 +1146,11 @@ int field_read_buf_ascii(field_t * field, int index, const char * buf) { * *****************************************************************************/ -int field_io_aggr_pack(field_t * field, io_aggr_buf_t aggr) { +int field_io_aggr_pack(field_t * field, io_aggregator_t * aggr) { assert(field); - assert(aggr.buf); + assert(aggr); + assert(aggr->buf); #pragma omp parallel { @@ -1158,16 +1159,16 @@ int field_io_aggr_pack(field_t * field, io_aggr_buf_t aggr) { assert(iasc ^ ibin); /* one or other */ #pragma omp for - for (int ib = 0; ib < cs_limits_size(aggr.lim); ib++) { - int ic = cs_limits_ic(aggr.lim, ib); - int jc = cs_limits_jc(aggr.lim, ib); - int kc = cs_limits_kc(aggr.lim, ib); + for (int ib = 0; ib < cs_limits_size(aggr->lim); ib++) { + int ic = cs_limits_ic(aggr->lim, ib); + int jc = cs_limits_jc(aggr->lim, ib); + int kc = cs_limits_kc(aggr->lim, ib); /* Read/write data for (ic,jc,kc) */ int index = cs_index(field->cs, ic, jc, kc); - int offset = ib*aggr.szelement; - if (iasc) field_write_buf_ascii(field, index, aggr.buf + offset); - if (ibin) field_write_buf(field, index, aggr.buf + offset); + int offset = ib*aggr->szelement; + if (iasc) field_write_buf_ascii(field, index, aggr->buf + offset); + if (ibin) field_write_buf(field, index, aggr->buf + offset); } } @@ -1182,10 +1183,11 @@ int field_io_aggr_pack(field_t * field, io_aggr_buf_t aggr) { * *****************************************************************************/ -int field_io_aggr_unpack(field_t * field, io_aggr_buf_t aggr) { +int field_io_aggr_unpack(field_t * field, const io_aggregator_t * aggr) { assert(field); - assert(aggr.buf); + assert(aggr); + assert(aggr->buf); #pragma omp parallel { @@ -1194,16 +1196,16 @@ int field_io_aggr_unpack(field_t * field, io_aggr_buf_t aggr) { assert(iasc ^ ibin); /* one or other */ #pragma omp for - for (int ib = 0; ib < cs_limits_size(aggr.lim); ib++) { - int ic = cs_limits_ic(aggr.lim, ib); - int jc = cs_limits_jc(aggr.lim, ib); - int kc = cs_limits_kc(aggr.lim, ib); + for (int ib = 0; ib < cs_limits_size(aggr->lim); ib++) { + int ic = cs_limits_ic(aggr->lim, ib); + int jc = cs_limits_jc(aggr->lim, ib); + int kc = cs_limits_kc(aggr->lim, ib); /* Read/write data for (ic,jc,kc) */ int index = cs_index(field->cs, ic, jc, kc); - int offset = ib*aggr.szelement; - if (iasc) field_read_buf_ascii(field, index, aggr.buf + offset); - if (ibin) field_read_buf(field, index, aggr.buf + offset); + int offset = ib*aggr->szelement; + if (iasc) field_read_buf_ascii(field, index, aggr->buf + offset); + if (ibin) field_read_buf(field, index, aggr->buf + offset); } } diff --git a/src/field.h b/src/field.h index 1a7b5a2b5..ddece911a 100644 --- a/src/field.h +++ b/src/field.h @@ -113,7 +113,7 @@ int field_read_buf(field_t * field, int index, const char * buf); int field_read_buf_ascii(field_t * field, int index, const char * buf); int field_write_buf(field_t * field, int index, char * buf); int field_write_buf_ascii(field_t * field, int index, char * buf); -int field_io_aggr_pack(field_t * field, io_aggr_buf_t aggr); -int field_io_aggr_unpack(field_t * field, io_aggr_buf_t aggr); +int field_io_aggr_pack(field_t * field, io_aggregator_t * aggr); +int field_io_aggr_unpack(field_t * field, const io_aggregator_t * aggr); #endif diff --git a/src/io_aggr_buf.c b/src/io_aggr_buf.c index 816d81cf2..a0db299af 100644 --- a/src/io_aggr_buf.c +++ b/src/io_aggr_buf.c @@ -20,15 +20,16 @@ /***************************************************************************** * - * io_aggr_buf_create + * io_aggregator_create * *****************************************************************************/ -int io_aggr_buf_create(io_element_t e, cs_limits_t lim, io_aggr_buf_t * aggr) { +int io_aggregator_create(io_element_t e, cs_limits_t lim, + io_aggregator_t * aggr) { assert(aggr); - *aggr = (io_aggr_buf_t) {0}; + *aggr = (io_aggregator_t) {0}; aggr->element = e; aggr->szelement = e.count*e.datasize; @@ -45,18 +46,18 @@ int io_aggr_buf_create(io_element_t e, cs_limits_t lim, io_aggr_buf_t * aggr) { /***************************************************************************** * - * io_aggr_buf_free + * io_aggregator_free * *****************************************************************************/ -int io_aggr_buf_free(io_aggr_buf_t * aggr) { +int io_aggregator_free(io_aggregator_t * aggr) { assert(aggr); assert(aggr->buf); free(aggr->buf); - *aggr = (io_aggr_buf_t) {0}; + *aggr = (io_aggregator_t) {0}; return 0; } diff --git a/src/io_aggr_buf.h b/src/io_aggr_buf.h index 1442d2fe1..3156dfd5f 100644 --- a/src/io_aggr_buf.h +++ b/src/io_aggr_buf.h @@ -18,9 +18,9 @@ #include "cs_limits.h" #include "io_element.h" -typedef struct io_aggr_buf_s io_aggr_buf_t; +typedef struct io_aggregator_s io_aggregator_t; -struct io_aggr_buf_s { +struct io_aggregator_s { io_element_t element; /* Element information */ cs_limits_t lim; /* 3-d limits of buffer */ size_t szelement; /* bytes per record */ @@ -28,7 +28,8 @@ struct io_aggr_buf_s { char * buf; /* Storage space */ }; -int io_aggr_buf_create(io_element_t el, cs_limits_t lim, io_aggr_buf_t * aggr); -int io_aggr_buf_free(io_aggr_buf_t * aggr); +int io_aggregator_create(io_element_t el, cs_limits_t lim, + io_aggregator_t * aggr); +int io_aggregator_free(io_aggregator_t * aggr); #endif diff --git a/src/io_aggr_buf_mpio.c b/src/io_aggr_buf_mpio.c index cdc9cd568..5b4860de4 100644 --- a/src/io_aggr_buf_mpio.c +++ b/src/io_aggr_buf_mpio.c @@ -1,6 +1,6 @@ /***************************************************************************** * - * io_aggr_buf_mpio.c + * io_impl_mpio.c * * Read/write aggregated data buffers using MPI/IO. * @@ -27,7 +27,7 @@ *****************************************************************************/ int io_aggr_mpio_write(pe_t * pe, cs_t * cs, const char * filename, - const io_aggr_buf_t * buf) { + const io_aggregator_t * buf) { assert(pe); assert(cs); assert(filename); @@ -90,7 +90,7 @@ int io_aggr_mpio_write(pe_t * pe, cs_t * cs, const char * filename, *****************************************************************************/ int io_aggr_mpio_read(pe_t * pe, cs_t * cs, const char * filename, - io_aggr_buf_t * buf) { + io_aggregator_t * buf) { assert(pe); assert(cs); assert(filename); diff --git a/src/io_aggr_buf_mpio.h b/src/io_aggr_buf_mpio.h index e31218799..3d26332d3 100644 --- a/src/io_aggr_buf_mpio.h +++ b/src/io_aggr_buf_mpio.h @@ -19,7 +19,7 @@ #include "io_aggr_buf.h" int io_aggr_mpio_write(pe_t * pe, cs_t * cs, const char * filename, - const io_aggr_buf_t * buf); + const io_aggregator_t * buf); int io_aggr_mpio_read(pe_t * pe, cs_t * cs, const char * filename, - io_aggr_buf_t * buf); + io_aggregator_t * buf); #endif diff --git a/tests/unit/test_field.c b/tests/unit/test_field.c index 2ce0edc78..3fccf77e9 100644 --- a/tests/unit/test_field.c +++ b/tests/unit/test_field.c @@ -640,22 +640,22 @@ int test_field_io_aggr_pack(pe_t * pe) { cs_nlocal(cs, nlocal); { cs_limits_t lim = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; - io_aggr_buf_t buf = {0}; + io_aggregator_t buf = {0}; - io_aggr_buf_create(field->binary, lim, &buf); + io_aggregator_create(field->binary, lim, &buf); util_field_data_check_set(field); - field_io_aggr_pack(field, buf); + field_io_aggr_pack(field, &buf); /* Are the values in the buffer correct? */ /* Clear existing values and unpack. */ memset(field->data, 0, sizeof(double)*field->nsites*field->nf); - field_io_aggr_unpack(field, buf); + field_io_aggr_unpack(field, &buf); util_field_data_check(field); - io_aggr_buf_free(&buf); + io_aggregator_free(&buf); } } diff --git a/tests/unit/test_io_aggr_buf.c b/tests/unit/test_io_aggr_buf.c index e0a4155d1..258014fde 100644 --- a/tests/unit/test_io_aggr_buf.c +++ b/tests/unit/test_io_aggr_buf.c @@ -1,6 +1,6 @@ /***************************************************************************** * - * test_io_aggr_buf.c + * test_io_aggregator.c * * * Edinburgh Soft Matter and Statistical Physics Group and @@ -17,7 +17,7 @@ #include "pe.h" #include "io_aggr_buf.h" -int test_io_aggr_buf_create(void); +int test_io_aggregator_create(void); /***************************************************************************** * @@ -33,11 +33,11 @@ int test_io_aggr_buf_suite(void) { /* If the size of the struct has changed, tests need to be changed... */ - assert(sizeof(io_aggr_buf_t) == 72); + assert(sizeof(io_aggregator_t) == 72); - test_io_aggr_buf_create(); + test_io_aggregator_create(); - pe_info(pe, "PASS ./unit/testr_io_aggr_buf\n"); + pe_info(pe, "%-9s %s\n", "PASS", __FILE__); pe_free(pe); return 0; @@ -45,11 +45,11 @@ int test_io_aggr_buf_suite(void) { /***************************************************************************** * - * test_io_aggr_buf_create + * test_io_aggregator_create * *****************************************************************************/ -int test_io_aggr_buf_create(void) { +int test_io_aggregator_create(void) { int ifail = 0; @@ -60,9 +60,9 @@ int test_io_aggr_buf_create(void) { .count = 3, .endian = io_endianness()}; cs_limits_t lim = {-2, 18, 1, 8, 1, 4}; - io_aggr_buf_t aggr = {0}; + io_aggregator_t aggr = {0}; - io_aggr_buf_create(element, lim, &aggr); + io_aggregator_create(element, lim, &aggr); assert(aggr.element.datatype == MPI_DOUBLE); assert(aggr.element.datasize == sizeof(double)); @@ -74,7 +74,7 @@ int test_io_aggr_buf_create(void) { assert(aggr.lim.imin == lim.imin); /* Assume sufficient */ assert(aggr.buf); - io_aggr_buf_free(&aggr); + io_aggregator_free(&aggr); assert(aggr.szelement == 0); assert(aggr.szbuf == 0); assert(aggr.lim.imin == 0); diff --git a/tests/unit/test_io_aggr_mpio.c b/tests/unit/test_io_aggr_mpio.c index eac7e980b..ff4b594c4 100644 --- a/tests/unit/test_io_aggr_mpio.c +++ b/tests/unit/test_io_aggr_mpio.c @@ -28,10 +28,10 @@ int test_io_aggr_mpio_write(pe_t * pe, cs_t * cs, io_element_t el, int test_io_aggr_mpio_read(pe_t * pe, cs_t * cs, io_element_t el, const char * filename); -int test_io_aggr_buf_pack_asc(cs_t * cs, io_aggr_buf_t buf); -int test_io_aggr_buf_pack_bin(cs_t * cs, io_aggr_buf_t buf); -int test_io_aggr_buf_unpack_asc(cs_t * cs, io_aggr_buf_t buf); -int test_io_aggr_buf_unpack_bin(cs_t * cs, io_aggr_buf_t buf); +int test_io_aggr_buf_pack_asc(cs_t * cs, io_aggregator_t * buf); +int test_io_aggr_buf_pack_bin(cs_t * cs, io_aggregator_t * buf); +int test_io_aggr_buf_unpack_asc(cs_t * cs, const io_aggregator_t * buf); +int test_io_aggr_buf_unpack_bin(cs_t * cs, const io_aggregator_t * buf); /***************************************************************************** * @@ -113,16 +113,16 @@ int test_io_aggr_mpio_write(pe_t * pe, cs_t * cs, io_element_t element, { cs_limits_t lim = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; - io_aggr_buf_t buf = {0}; + io_aggregator_t buf = {0}; - io_aggr_buf_create(element, lim, &buf); + io_aggregator_create(element, lim, &buf); - if (element.datatype == MPI_CHAR) test_io_aggr_buf_pack_asc(cs, buf); - if (element.datatype == MPI_INT64_T) test_io_aggr_buf_pack_bin(cs, buf); + if (element.datatype == MPI_CHAR) test_io_aggr_buf_pack_asc(cs, &buf); + if (element.datatype == MPI_INT64_T) test_io_aggr_buf_pack_bin(cs, &buf); io_aggr_mpio_write(pe, cs, filename, &buf); - io_aggr_buf_free(&buf); + io_aggregator_free(&buf); } return 0; @@ -147,16 +147,16 @@ int test_io_aggr_mpio_read(pe_t * pe, cs_t * cs, io_element_t element, { /* Read and check */ cs_limits_t lim = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; - io_aggr_buf_t buf = {0}; + io_aggregator_t buf = {0}; - io_aggr_buf_create(element, lim ,&buf); + io_aggregator_create(element, lim ,&buf); io_aggr_mpio_read(pe, cs, filename, &buf); - if (element.datatype == MPI_CHAR) test_io_aggr_buf_unpack_asc(cs, buf); - if (element.datatype == MPI_INT64_T) test_io_aggr_buf_unpack_bin(cs, buf); + if (element.datatype == MPI_CHAR) test_io_aggr_buf_unpack_asc(cs, &buf); + if (element.datatype == MPI_INT64_T) test_io_aggr_buf_unpack_bin(cs, &buf); - io_aggr_buf_free(&buf); + io_aggregator_free(&buf); } return 0; @@ -196,7 +196,7 @@ int64_t test_unique_value(cs_t * cs, int ic, int jc, int kc) { * *****************************************************************************/ -int test_io_aggr_buf_pack_asc(cs_t * cs, io_aggr_buf_t buf) { +int test_io_aggr_buf_pack_asc(cs_t * cs, io_aggregator_t * buf) { int ifail = 0; @@ -206,7 +206,7 @@ int test_io_aggr_buf_pack_asc(cs_t * cs, io_aggr_buf_t buf) { int offset[3] = {0}; assert(cs); - assert(buf.buf); + assert(buf); cs_ntotal(cs, ntotal); cs_nlocal(cs, nlocal); @@ -224,9 +224,9 @@ int test_io_aggr_buf_pack_asc(cs_t * cs, io_aggr_buf_t buf) { char cline[BUFSIZ] = {0}; size_t nc = 0; /* int returned but need to compare to size_t */ nc = sprintf(cline, "%4d %4d %4d %12" PRId64 "\n", ix, iy, iz, ival); - assert(nc == buf.szelement); - if (nc != buf.szelement) ifail += 1; - memcpy(buf.buf + ib*buf.szelement, cline, buf.szelement); + assert(nc == buf->szelement); + if (nc != buf->szelement) ifail += 1; + memcpy(buf->buf + ib*buf->szelement, cline, buf->szelement); } ib += 1; } @@ -244,7 +244,7 @@ int test_io_aggr_buf_pack_asc(cs_t * cs, io_aggr_buf_t buf) { * *****************************************************************************/ -int test_io_aggr_buf_unpack_asc(cs_t * cs, io_aggr_buf_t buf) { +int test_io_aggr_buf_unpack_asc(cs_t * cs, const io_aggregator_t * buf) { int ifail = 0; @@ -254,7 +254,7 @@ int test_io_aggr_buf_unpack_asc(cs_t * cs, io_aggr_buf_t buf) { int offset[3] = {0}; assert(cs); - assert(buf.buf); + assert(buf->buf); cs_ntotal(cs, ntotal); cs_nlocal(cs, nlocal); @@ -274,7 +274,7 @@ int test_io_aggr_buf_unpack_asc(cs_t * cs, io_aggr_buf_t buf) { int izread = -1; int64_t ivalread = -1; /* Note int64_t requires portable format */ - int nc = sscanf(buf.buf + ib*buf.szelement, "%4d %4d %4d %" SCNd64, + int nc = sscanf(buf->buf + ib*buf->szelement, "%4d %4d %4d %" SCNd64, &ixread, &iyread, &izread, &ivalread); assert(nc == 4); @@ -305,10 +305,10 @@ int test_io_aggr_buf_unpack_asc(cs_t * cs, io_aggr_buf_t buf) { * *****************************************************************************/ -int test_io_aggr_buf_pack_bin(cs_t * cs, io_aggr_buf_t buf) { +int test_io_aggr_buf_pack_bin(cs_t * cs, io_aggregator_t * buf) { assert(cs); - assert(buf.buf); + assert(buf->buf); int ib = 0; int nlocal[3] = {0}; @@ -319,7 +319,7 @@ int test_io_aggr_buf_pack_bin(cs_t * cs, io_aggr_buf_t buf) { for (int jc = 1; jc <= nlocal[Y]; jc++) { for (int kc = 1; kc <= nlocal[Z]; kc++) { int64_t ival = test_unique_value(cs, ic, jc, kc); - memcpy(buf.buf + ib*sizeof(int64_t), &ival, sizeof(int64_t)); + memcpy(buf->buf + ib*sizeof(int64_t), &ival, sizeof(int64_t)); ib += 1; } } @@ -336,7 +336,7 @@ int test_io_aggr_buf_pack_bin(cs_t * cs, io_aggr_buf_t buf) { * *****************************************************************************/ -int test_io_aggr_buf_unpack_bin(cs_t * cs, io_aggr_buf_t buf) { +int test_io_aggr_buf_unpack_bin(cs_t * cs, const io_aggregator_t * buf) { int ifail = 0; @@ -344,7 +344,7 @@ int test_io_aggr_buf_unpack_bin(cs_t * cs, io_aggr_buf_t buf) { int nlocal[3] = {0}; assert(cs); - assert(buf.buf); + assert(buf->buf); cs_nlocal(cs, nlocal); @@ -353,7 +353,7 @@ int test_io_aggr_buf_unpack_bin(cs_t * cs, io_aggr_buf_t buf) { for (int kc = 1; kc <= nlocal[Z]; kc++) { int64_t ival = test_unique_value(cs, ic, jc, kc); int64_t iread = -1; - memcpy(&iread, buf.buf + ib*sizeof(int64_t), sizeof(int64_t)); + memcpy(&iread, buf->buf + ib*sizeof(int64_t), sizeof(int64_t)); assert(ival == iread); if (ival != iread) ifail += 1; ib += 1; From 804a435a9517e063d71bcd2c06ec26083cc456ff Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sun, 6 Nov 2022 12:40:41 +0000 Subject: [PATCH 099/244] File extensions the right way around --- util/extract.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/util/extract.c b/util/extract.c index dfd3b6e82..23df1be1b 100644 --- a/util/extract.c +++ b/util/extract.c @@ -218,7 +218,7 @@ int main(int argc, char ** argv) { char * filename = buf; int ifile = file_get_file_index(argv[optind]); int nfile = file_get_file_nfile(argv[optind]); - sprintf(filename, "%s.%3.3d-%3.3d.meta", stub, ifile, nfile); + sprintf(filename, "%s.%3.3d-%3.3d.meta", stub, nfile, ifile); read_meta_data_file(filename, &metadata); } } @@ -252,7 +252,7 @@ int extract_driver(const char * filename, metadata_v1_t * meta, int version) { /* Work out parallel local file size */ - assert(io_size[0] == 1); + assert(io_size[0] == 1); /* No x-decompositions WILL FAIL */ switch (version) { case 1: @@ -1461,11 +1461,22 @@ const char * file_stub_valid(const char * input) { return output; } +/***************************************************************************** + * + * file_get_file_nfile + * + * Metadata encoded in a set of parallel, decomposed, filenames e.g., + * phi-metadata.002-001 + * phi-metadata.002-002 + * would return nfile = 2. + * + *****************************************************************************/ + int file_get_file_nfile(const char * filename) { int nfile = 0; - char dash = '-'; - const char * str1 = strchr(filename, dash); + char dot = '.'; + const char * str1 = strchr(filename, dot); /* First "." */ if (str1 != NULL) { char buf[BUFSIZ] = {0}; @@ -1476,17 +1487,25 @@ int file_get_file_nfile(const char * filename) { return nfile; } +/***************************************************************************** + * + * file_get_file_index + * + * Likewise, returns the index (see file_get_file_nfile above). + * + *****************************************************************************/ + int file_get_file_index(const char * filename) { int ifile = 0; - char dot = '.'; - const char * str1 = strchr(filename, dot); /* E.g., phi.001-001.meta */ + char dash = '-'; + const char * str1 = strrchr(filename, dash); /* Last "-" */ if (str1 != NULL) { char buf[BUFSIZ] = {0}; strncpy(buf, str1 + 1, 3); ifile = atoi(buf); } - + return ifile; } From 3c2991fa4dd38634a69751e6de21bdfe5031fd79 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sun, 6 Nov 2022 12:43:14 +0000 Subject: [PATCH 100/244] Update i/o for alerts --- util/extract_colloids.c | 83 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 7 deletions(-) diff --git a/util/extract_colloids.c b/util/extract_colloids.c index de4e65782..8c8a01cd8 100644 --- a/util/extract_colloids.c +++ b/util/extract_colloids.c @@ -21,7 +21,7 @@ * * 1st argument is the file name stub (in front of the last dot), * 2nd argument is the number of parallel files (as set with XXX_io_grid), - * 3rd argyment is the (single) ouput file name. + * 3rd argument is the (single) ouput file name. * * If you have a set of files, try (eg. here with 4 parallel output files), * @@ -32,16 +32,19 @@ * Edinburgh Parallel Computing Centre * * Kevin Stratford (kevin@epcc.ed.ac.uk) - * (c) 2012-2019 The University of Edinburgh + * (c) 2012-2022 The University of Edinburgh * *****************************************************************************/ #include +#include #include #include +#include #include #include "colloid.h" +#include "util_fopen.h" #define NX 32 #define NY 32 @@ -60,6 +63,7 @@ static const char * formate4end_ = "%14.6e, %14.6e, %14.6e, %14.6e\n"; void colloids_to_csv_header(FILE * fp); void colloids_to_csv_header_with_m(FILE * fp); void colloids_to_csv_header_with_v(FILE * fp); +int util_io_posix_filename(const char * input, char * buf, size_t bufsz); int main(int argc, char ** argv) { @@ -78,7 +82,7 @@ int main(int argc, char ** argv) { FILE * fp_csv = NULL; char filename[FILENAME_MAX]; - if (argc < 3) { + if (argc < 4) { printf("Usage: %s \n", argv[0]); exit(0); @@ -87,10 +91,22 @@ int main(int argc, char ** argv) { nfile = atoi(argv[2]); printf("Number of files: %d\n", nfile); - /* Open csv file */ - fp_csv = fopen(argv[3], "w"); + /* Open csv file (check input first) */ + { + char csv_filename[BUFSIZ] = {0}; + int ireturn = util_io_posix_filename(argv[3], csv_filename, BUFSIZ); + if (ireturn == 0) { + fp_csv = util_fopen(csv_filename, "w"); + } + else { + printf("Please use output filename with allowed characters\n"); + printf("[A-Z a-z 0-9 - _ .] only\n"); + exit(-1); + } + } + if (fp_csv == NULL) { - printf("fopen(%s) failed\n", argv[2]); + printf("fopen(%s) failed\n", argv[3]); exit(0); } @@ -104,7 +120,7 @@ int main(int argc, char ** argv) { snprintf(filename, sizeof(filename), "%s.%3.3d-%3.3d", argv[1], nfile, nf); printf("Filename: %s\n", filename); - fp_colloids = fopen(filename, "r"); + fp_colloids = util_fopen(filename, "r"); if (fp_colloids == NULL) { @@ -304,3 +320,56 @@ void colloids_to_csv_header_with_v(FILE * fp) { return; } + +/***************************************************************************** + * + * util_io_posix_filename + * + * A posix "fully portable filename" (not a path) has allowed characters: + * A-Z, a-z, 0-9, ".", "-", and "_" + * The first character must not be a hyphen. + * + * This returns a positive value if a replacement is required, + * a negative value if the supplied buffer is too small, + * or zero on a succcessful copy with no replacements. + * + * The output buffer contains the input with any duff characters replaced + * by "_". + * + *****************************************************************************/ + +int util_io_posix_filename(const char * input, char * buf, size_t bufsz) { + + int ifail = 0; + size_t len = strnlen(input, FILENAME_MAX-1); + const char replacement_character = '_'; + + if (bufsz <= len) { + /* would be truncated */ + ifail = -1; + } + else { + /* Copy, but replace anything that's not posix */ + for (size_t i = 0; i < len; i++) { + char c = input[i]; + if (i == 0 && c == '-') { + /* Replace */ + buf[i] = replacement_character; + ifail += 1; + } + else if (isalnum(c) || c == '_' || c == '-' || c == '.') { + /* ok */ + buf[i] = input[i]; + } + else { + /* Replace */ + buf[i] = replacement_character; + ifail += 1; + } + } + /* Terminate */ + buf[len] = '\0'; + } + + return ifail; +} From 1fd9a1e070230bbb375a63977cfc7d077416a969 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sun, 6 Nov 2022 13:51:23 +0000 Subject: [PATCH 101/244] Parse file name on input --- util/extract_colloids.c | 136 ++++++++++++++++++---------------------- 1 file changed, 61 insertions(+), 75 deletions(-) diff --git a/util/extract_colloids.c b/util/extract_colloids.c index 8c8a01cd8..889c6aae4 100644 --- a/util/extract_colloids.c +++ b/util/extract_colloids.c @@ -15,29 +15,26 @@ * * $ make extract_colloids * - * $ ./a.out + * $ ./extract_colloids * - * where the - * - * 1st argument is the file name stub (in front of the last dot), - * 2nd argument is the number of parallel files (as set with XXX_io_grid), - * 3rd argument is the (single) ouput file name. + * The file name must be of the form "config.cds00020000.002-001"; + * if there are more than one file (from parallel i/o), any + * individual file will do, e.g., the first one. * - * If you have a set of files, try (eg. here with 4 parallel output files), + * The corresponding output will be a single file with combined information: + * colloids-00020000.csv for the example above. * - * $ for f in config.cds*004-001; do g=`echo $f | sed s/.004-001//`; \ - * echo $g; ~/ludwig/trunk/util/extract_colloids $g 4 $g.csv; done * * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * Kevin Stratford (kevin@epcc.ed.ac.uk) * (c) 2012-2022 The University of Edinburgh * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * *****************************************************************************/ #include -#include #include #include #include @@ -63,7 +60,8 @@ static const char * formate4end_ = "%14.6e, %14.6e, %14.6e, %14.6e\n"; void colloids_to_csv_header(FILE * fp); void colloids_to_csv_header_with_m(FILE * fp); void colloids_to_csv_header_with_v(FILE * fp); -int util_io_posix_filename(const char * input, char * buf, size_t bufsz); +int file_name_to_ntime(const char * filename); +int file_name_to_nfile(const char * filename); int main(int argc, char ** argv) { @@ -71,6 +69,7 @@ int main(int argc, char ** argv) { int nf, nfile; int ncolloid; int nread; + int ntime = 0; int ncount = 0; double normv; @@ -80,30 +79,24 @@ int main(int argc, char ** argv) { FILE * fp_colloids = NULL; FILE * fp_csv = NULL; - char filename[FILENAME_MAX]; + char csv_filename[BUFSIZ] = {0}; - if (argc < 4) { - printf("Usage: %s \n", - argv[0]); + if (argc < 2) { + printf("Usage: %s \n", argv[0]); exit(0); } - nfile = atoi(argv[2]); + /* Check the file name. */ + ntime = file_name_to_ntime(argv[1]); + nfile = file_name_to_nfile(argv[1]); + printf("Time step: %d\n", ntime); printf("Number of files: %d\n", nfile); - /* Open csv file (check input first) */ - { - char csv_filename[BUFSIZ] = {0}; - int ireturn = util_io_posix_filename(argv[3], csv_filename, BUFSIZ); - if (ireturn == 0) { - fp_csv = util_fopen(csv_filename, "w"); - } - else { - printf("Please use output filename with allowed characters\n"); - printf("[A-Z a-z 0-9 - _ .] only\n"); - exit(-1); - } - } + /* Open csv file (output) */ + + sprintf(csv_filename, "colloids-%8.8d.csv", ntime); + fp_csv = util_fopen(csv_filename, "w"); + if (fp_csv == NULL) { printf("fopen(%s) failed\n", argv[3]); @@ -115,17 +108,19 @@ int main(int argc, char ** argv) { for (nf = 1; nf <= nfile; nf++) { + char filename[BUFSIZ] = {0}; + /* We expect extensions 00n-001 00n-002 ... 00n-00n */ - snprintf(filename, sizeof(filename), "%s.%3.3d-%3.3d", argv[1], nfile, nf); + sprintf(filename, "config.cds%8.8d.%3.3d-%3.3d", ntime, nfile, nf); printf("Filename: %s\n", filename); fp_colloids = util_fopen(filename, "r"); if (fp_colloids == NULL) { - printf("fopen(%s) failed\n", filename); - exit(0); + printf("fopen(%s) failed\n", filename); + exit(0); } if (iread_ascii) { @@ -172,11 +167,11 @@ int main(int argc, char ** argv) { /* Finish colloid coordinate output */ fclose(fp_csv); if (include_ref) { - printf("Wrote %d actual colloids + 3 reference colloids in header\n", - ncount); + printf("Wrote %d actual colloids + 3 reference colloids in header to %s\n", + ncount, csv_filename); } else { - printf("Wrote %d colloids\n", ncount); + printf("Wrote %d colloids to %s\n", ncount, csv_filename); } /* Finish */ @@ -323,53 +318,44 @@ void colloids_to_csv_header_with_v(FILE * fp) { /***************************************************************************** * - * util_io_posix_filename + * file_name_to_nfile * - * A posix "fully portable filename" (not a path) has allowed characters: - * A-Z, a-z, 0-9, ".", "-", and "_" - * The first character must not be a hyphen. + * The file name must be of the form "config.cds00020000.004-001". * - * This returns a positive value if a replacement is required, - * a negative value if the supplied buffer is too small, - * or zero on a succcessful copy with no replacements. + *****************************************************************************/ + +int file_name_to_nfile(const char * filename) { + + int nfile = 0; + const char * ext = strrchr(filename, '.'); /* last dot */ + + if (ext != NULL) { + char buf[BUFSIZ] = {0}; + strncpy(buf, ext + 1, 3); + nfile = atoi(buf); + } + + return nfile; +} + +/***************************************************************************** + * + * file_name_to_ntime * - * The output buffer contains the input with any duff characters replaced - * by "_". + * The file name must be of the form "config.cds00020000.004-001". * *****************************************************************************/ -int util_io_posix_filename(const char * input, char * buf, size_t bufsz) { +int file_name_to_ntime(const char * filename) { - int ifail = 0; - size_t len = strnlen(input, FILENAME_MAX-1); - const char replacement_character = '_'; + int ntime = -1; + const char * tmp = strchr(filename, 's'); /* Must be "config.cds" */ - if (bufsz <= len) { - /* would be truncated */ - ifail = -1; - } - else { - /* Copy, but replace anything that's not posix */ - for (size_t i = 0; i < len; i++) { - char c = input[i]; - if (i == 0 && c == '-') { - /* Replace */ - buf[i] = replacement_character; - ifail += 1; - } - else if (isalnum(c) || c == '_' || c == '-' || c == '.') { - /* ok */ - buf[i] = input[i]; - } - else { - /* Replace */ - buf[i] = replacement_character; - ifail += 1; - } - } - /* Terminate */ - buf[len] = '\0'; + if (tmp) { + char buf[BUFSIZ] = {0}; + strncpy(buf, tmp + 1, 8); + ntime = atoi(buf); } - return ifail; + return ntime; } From 540f46c5e9f8ed01fa986b07a047798ce8032c47 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sun, 6 Nov 2022 17:45:57 +0000 Subject: [PATCH 102/244] Check time step --- util/extract_colloids.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/util/extract_colloids.c b/util/extract_colloids.c index 889c6aae4..6ed07e4da 100644 --- a/util/extract_colloids.c +++ b/util/extract_colloids.c @@ -357,5 +357,10 @@ int file_name_to_ntime(const char * filename) { ntime = atoi(buf); } + if (0 > ntime || ntime >= 1000*1000*1000) { + printf("Could not parse a time step from file name %s\n", filename); + exit(-1); + } + return ntime; } From 3595c8dc533fe8eea025d85b7d8c069edd29058a Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sun, 6 Nov 2022 19:37:09 +0000 Subject: [PATCH 103/244] Cosmetic update --- src/util.c | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/src/util.c b/src/util.c index 39d780be8..07710e5d4 100644 --- a/src/util.c +++ b/src/util.c @@ -1039,15 +1039,13 @@ int util_svd(int m, int n, double ** a, double * w, double ** v) { __host__ int util_matrix_invert(int n, double ** a) { - int i, j, k, ia, ib; - int irow, icol; + int irow = -1; + int icol = -1; int * indexcol = NULL; int * indexrow = NULL; int * ipivot = NULL; - double rpivot, tmp; - assert(a); indexcol = (int*) calloc(n, sizeof(int)); @@ -1069,18 +1067,16 @@ __host__ int util_matrix_invert(int n, double ** a) { return -3; } - icol = -1; - irow = -1; - - for (j = 0; j < n; j++) { + /* Begin the Gaussian elimination */ + for (int j = 0; j < n; j++) { ipivot[j] = -1; } - for (i = 0; i < n; i++) { - tmp = 0.0; - for (j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + double tmp = 0.0; + for (int j = 0; j < n; j++) { if (ipivot[j] != 0) { - for (k = 0; k < n; k++) { + for (int k = 0; k < n; k++) { if (ipivot[k] == -1) { if (fabs(a[j][k]) >= tmp) { @@ -1099,13 +1095,14 @@ __host__ int util_matrix_invert(int n, double ** a) { ipivot[icol] += 1; if (irow != icol) { - for (ia = 0; ia < n; ia++) { - tmp = a[irow][ia]; + for (int ia = 0; ia < n; ia++) { + double tmp = a[irow][ia]; a[irow][ia] = a[icol][ia]; a[icol][ia] = tmp; } } + /* Check the pivot element is not zero ... */ indexrow[i] = irow; indexcol[i] = icol; @@ -1116,18 +1113,20 @@ __host__ int util_matrix_invert(int n, double ** a) { return -1; } - rpivot = 1.0/a[icol][icol]; - a[icol][icol] = 1.0; + { + double rpivot = 1.0/a[icol][icol]; + a[icol][icol] = 1.0; - for (ia = 0; ia < n; ia++) { - a[icol][ia] *= rpivot; + for (int ia = 0; ia < n; ia++) { + a[icol][ia] *= rpivot; + } } - for (ia = 0; ia < n; ia++) { + for (int ia = 0; ia < n; ia++) { if (ia != icol) { - tmp = a[ia][icol]; + double tmp = a[ia][icol]; a[ia][icol] = 0.0; - for (ib = 0; ib < n; ib++) { + for (int ib = 0; ib < n; ib++) { a[ia][ib] -= a[icol][ib]*tmp; } } @@ -1136,10 +1135,10 @@ __host__ int util_matrix_invert(int n, double ** a) { /* Recover the inverse. */ - for (i = n - 1; i >= 0; i--) { + for (int i = n - 1; i >= 0; i--) { if (indexrow[i] != indexcol[i]) { - for (j = 0; j < n; j++) { - tmp = a[j][indexrow[i]]; + for (int j = 0; j < n; j++) { + double tmp = a[j][indexrow[i]]; a[j][indexrow[i]] = a[j][indexcol[i]]; a[j][indexcol[i]] = tmp; } From fad89dc9f5d2b52abbb4c409f737f9e1a78b8615 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sun, 6 Nov 2022 19:37:32 +0000 Subject: [PATCH 104/244] Retire LGTM.com --- .lgtm.yml | 6 ------ CHANGES.md | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 .lgtm.yml diff --git a/.lgtm.yml b/.lgtm.yml deleted file mode 100644 index afc6f98be..000000000 --- a/.lgtm.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -extraction: - cpp: - configure: - command: - - "cp config/travis-gcc.mk config.mk" diff --git a/CHANGES.md b/CHANGES.md index f83e484d4..3ce9c7c9c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,12 @@ ### Changes +version 0.19.0 +- The extract_colloids.c utility has been updated so that it takes + only one comand line argument. +- LTGM.com analysis has been retired as the service is closing. The + two outstanding recommendations are covered by CodeQL notes. + version 0.18.0 - Added a lubrication correction offset to allow an option for keeping From 2663d8cb92c715d1f2727d458f3a6ef83861d22d Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sun, 6 Nov 2022 20:01:57 +0000 Subject: [PATCH 105/244] Clear shadow declarations --- src/util.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/util.c b/src/util.c index 07710e5d4..b57808001 100644 --- a/src/util.c +++ b/src/util.c @@ -1096,7 +1096,7 @@ __host__ int util_matrix_invert(int n, double ** a) { if (irow != icol) { for (int ia = 0; ia < n; ia++) { - double tmp = a[irow][ia]; + tmp = a[irow][ia]; a[irow][ia] = a[icol][ia]; a[icol][ia] = tmp; } @@ -1124,13 +1124,14 @@ __host__ int util_matrix_invert(int n, double ** a) { for (int ia = 0; ia < n; ia++) { if (ia != icol) { - double tmp = a[ia][icol]; + tmp = a[ia][icol]; a[ia][icol] = 0.0; for (int ib = 0; ib < n; ib++) { a[ia][ib] -= a[icol][ib]*tmp; } } } + /* .. outer loop .. */ } /* Recover the inverse. */ From 5d467aeba87cdb83de4947c0a8afabe49b285cb8 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 7 Nov 2022 09:22:29 +0000 Subject: [PATCH 106/244] File name change to reflect contents --- src/field.h | 4 ++-- src/io_aggr_buf_mpio.h | 2 +- src/{io_aggr_buf.c => io_aggregator.c} | 2 +- src/{io_aggr_buf.h => io_aggregator.h} | 0 tests/unit/{test_io_aggr_buf.c => test_io_aggregator.c} | 6 +++--- tests/unit/tests.c | 2 +- tests/unit/tests.h | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) rename src/{io_aggr_buf.c => io_aggregator.c} (98%) rename src/{io_aggr_buf.h => io_aggregator.h} (100%) rename tests/unit/{test_io_aggr_buf.c => test_io_aggregator.c} (95%) diff --git a/src/field.h b/src/field.h index ddece911a..f5fa7867c 100644 --- a/src/field.h +++ b/src/field.h @@ -23,8 +23,8 @@ #include "pe.h" #include "coords.h" #include "io_element.h" -#include "io_aggr_buf.h" /* Aggregation buffer */ -#include "io_harness.h" /* To be removed in favour of io_aggr_t */ +#include "io_aggregator.h" /* Aggregation buffer */ +#include "io_harness.h" /* To be removed in favour of refactored io */ #include "leesedwards.h" #include "halo_swap.h" #include "field_options.h" diff --git a/src/io_aggr_buf_mpio.h b/src/io_aggr_buf_mpio.h index 3d26332d3..c719ea00f 100644 --- a/src/io_aggr_buf_mpio.h +++ b/src/io_aggr_buf_mpio.h @@ -16,7 +16,7 @@ #include "pe.h" #include "coords.h" -#include "io_aggr_buf.h" +#include "io_aggregator.h" int io_aggr_mpio_write(pe_t * pe, cs_t * cs, const char * filename, const io_aggregator_t * buf); diff --git a/src/io_aggr_buf.c b/src/io_aggregator.c similarity index 98% rename from src/io_aggr_buf.c rename to src/io_aggregator.c index a0db299af..804030488 100644 --- a/src/io_aggr_buf.c +++ b/src/io_aggregator.c @@ -16,7 +16,7 @@ #include -#include "io_aggr_buf.h" +#include "io_aggregator.h" /***************************************************************************** * diff --git a/src/io_aggr_buf.h b/src/io_aggregator.h similarity index 100% rename from src/io_aggr_buf.h rename to src/io_aggregator.h diff --git a/tests/unit/test_io_aggr_buf.c b/tests/unit/test_io_aggregator.c similarity index 95% rename from tests/unit/test_io_aggr_buf.c rename to tests/unit/test_io_aggregator.c index 258014fde..fc4246637 100644 --- a/tests/unit/test_io_aggr_buf.c +++ b/tests/unit/test_io_aggregator.c @@ -15,17 +15,17 @@ #include #include "pe.h" -#include "io_aggr_buf.h" +#include "io_aggregator.h" int test_io_aggregator_create(void); /***************************************************************************** * - * test_io_aggr_buf_suite + * test_io_aggregator_suite * *****************************************************************************/ -int test_io_aggr_buf_suite(void) { +int test_io_aggregator_suite(void) { pe_t * pe = NULL; diff --git a/tests/unit/tests.c b/tests/unit/tests.c index 3bdbe8aa3..5f51f74da 100644 --- a/tests/unit/tests.c +++ b/tests/unit/tests.c @@ -73,7 +73,7 @@ __host__ int tests_create() { test_field_grad_suite(); test_halo_suite(); test_hydro_suite(); - test_io_aggr_buf_suite(); + test_io_aggregator_suite(); test_io_aggr_mpio_suite(); test_io_cart_sub_suite(); test_io_element_suite(); diff --git a/tests/unit/tests.h b/tests/unit/tests.h index 83c9a9a76..6342892b2 100644 --- a/tests/unit/tests.h +++ b/tests/unit/tests.h @@ -52,7 +52,7 @@ int test_field_grad_suite(void); int test_gradient_d3q27_suite(void); int test_halo_suite(void); int test_hydro_suite(void); -int test_io_aggr_buf_suite(void); +int test_io_aggregator_suite(void); int test_io_aggr_mpio_suite(void); int test_io_cart_sub_suite(void); int test_io_element_suite(void); From 98b8d3066768cf3b67af51d6878da22ae5362db1 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 9 Nov 2022 20:17:02 +0000 Subject: [PATCH 107/244] Add some comments --- src/gradient_3d_27pt_solid.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/gradient_3d_27pt_solid.c b/src/gradient_3d_27pt_solid.c index 7e3f98030..abba14aac 100644 --- a/src/gradient_3d_27pt_solid.c +++ b/src/gradient_3d_27pt_solid.c @@ -318,6 +318,12 @@ __global__ void grad_3d_27pt_solid_kernel(kernel_ctxt_t * ktx, * * grad_3d_27pt_solid_dab * + * This routine clearly carries a couple of health warnings: + * 1. There's no solid; + * 2. One order parameter only. + * + * It needs to be reconsidered, if required. + * *****************************************************************************/ __host__ int grad_3d_27pt_solid_dab(field_grad_t * df) { @@ -356,6 +362,7 @@ __host__ int grad_3d_27pt_solid_dab(field_grad_t * df) { index = cs_index(cs, ic, jc, kc); + /* d_xx */ dab[addr_rank1(nsites, NSYMM, index, XX)] = 0.2* (+ 1.0*field[addr_rank0(nsites, index + xs)] + 1.0*field[addr_rank0(nsites, index - xs)] @@ -373,6 +380,7 @@ __host__ int grad_3d_27pt_solid_dab(field_grad_t * df) { + 1.0*field[addr_rank0(nsites, index - xs - 1)] - 2.0*field[addr_rank0(nsites, index - 1)]); + /* d_xy */ dab[addr_rank1(nsites, NSYMM, index, XY)] = r12* (+ field[addr_rank0(nsites, index + xs + ys)] - field[addr_rank0(nsites, index + xs - ys)] @@ -387,6 +395,7 @@ __host__ int grad_3d_27pt_solid_dab(field_grad_t * df) { - field[addr_rank0(nsites, index - xs + ys - 1)] + field[addr_rank0(nsites, index - xs - ys - 1)]); + /* d_xz */ dab[addr_rank1(nsites, NSYMM, index, XZ)] = r12* (+ field[addr_rank0(nsites, index + xs + 1)] - field[addr_rank0(nsites, index + xs - 1)] @@ -401,6 +410,7 @@ __host__ int grad_3d_27pt_solid_dab(field_grad_t * df) { - field[addr_rank0(nsites, index - xs - ys + 1)] + field[addr_rank0(nsites, index - xs - ys - 1)]); + /* d_yy */ dab[addr_rank1(nsites, NSYMM, index, YY)] = 0.2* (+ 1.0*field[addr_rank0(nsites, index + ys)] + 1.0*field[addr_rank0(nsites, index - ys)] @@ -419,6 +429,7 @@ __host__ int grad_3d_27pt_solid_dab(field_grad_t * df) { - 2.0*field[addr_rank0(nsites, index - 1 )]); + /* d_yz */ dab[addr_rank1(nsites, NSYMM, index, YZ)] = r12* (+ field[addr_rank0(nsites, index + ys + 1)] - field[addr_rank0(nsites, index + ys - 1)] @@ -433,6 +444,7 @@ __host__ int grad_3d_27pt_solid_dab(field_grad_t * df) { - field[addr_rank0(nsites, index - xs - ys + 1)] + field[addr_rank0(nsites, index - xs - ys - 1)]); + /* d_zz */ dab[addr_rank1(nsites, NSYMM, index, ZZ)] = 0.2* (+ 1.0*field[addr_rank0(nsites, index + 1)] + 1.0*field[addr_rank0(nsites, index - 1)] From 624729625f97e07986dfacf2648d0fcd7ceb09e1 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 12 Nov 2022 12:54:59 +0000 Subject: [PATCH 108/244] Add comments --- src/collision.c | 45 +++++++++++++++++++++++++++++---------------- src/fe_lc_stats.c | 11 ++++++++++- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/collision.c b/src/collision.c index e4530e3cc..52d7edb00 100644 --- a/src/collision.c +++ b/src/collision.c @@ -13,7 +13,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2011-2021 The University of Edinburgh + * (c) 2011-2022 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -2866,6 +2866,14 @@ __device__ void d3q19_mode2f_chunk(double* mode, double* fchunk) { } +/***************************************************************************** + * + * d3q19_mode2f_phi + * + * Explicit projection of modes back to distributions for binary + * case. + * + *****************************************************************************/ __device__ void d3q19_mode2f_phi(double jdotc[NSIMDVL], double sphidotq[NSIMDVL], @@ -2878,6 +2886,7 @@ __device__ void d3q19_mode2f_phi(double jdotc[NSIMDVL], LB_RCS2_DOUBLE(rcs2); const double r2rcs4 = (9.0/2.0); + /* cv[p = 0] = {0,0,0} */ for_simd_v(iv, NSIMDVL) { jdotc[iv] = 0.0; sphidotq[iv] = 0.0;} for_simd_v(iv, NSIMDVL) sphidotq[iv] += sphi[0][0][iv]*-3.3333333333333331e-01; @@ -2889,6 +2898,7 @@ __device__ void d3q19_mode2f_phi(double jdotc[NSIMDVL], = w0*(jdotc[iv]*rcs2 + sphidotq[iv]*r2rcs4) + phi[iv]; + /* cv[p = 1] = {1,1,0} */ for_simd_v(iv, NSIMDVL) { jdotc[iv] = 0.0; sphidotq[iv] = 0.0;} for_simd_v(iv, NSIMDVL) jdotc[iv] += jphi[X][iv]; @@ -2904,6 +2914,7 @@ __device__ void d3q19_mode2f_phi(double jdotc[NSIMDVL], = w2*(jdotc[iv]*rcs2 + sphidotq[iv]*r2rcs4); + /* cv[p = 2] = {1,0,1} */ for_simd_v(iv, NSIMDVL) { jdotc[iv] = 0.0; sphidotq[iv] = 0.0;} for_simd_v(iv, NSIMDVL) jdotc[iv] += jphi[X][iv]; @@ -2918,7 +2929,7 @@ __device__ void d3q19_mode2f_phi(double jdotc[NSIMDVL], f[ LB_ADDR(_lbp.nsite, NDIST, NVEL, baseIndex+iv, LB_PHI, 2) ] = w2*(jdotc[iv]*rcs2 + sphidotq[iv]*r2rcs4); - + /* cv[p = 3] = {1,0,0} */ for_simd_v(iv, NSIMDVL) { jdotc[iv] = 0.0; sphidotq[iv] = 0.0;} for_simd_v(iv, NSIMDVL) jdotc[iv] += jphi[X][iv]; @@ -2930,9 +2941,9 @@ __device__ void d3q19_mode2f_phi(double jdotc[NSIMDVL], f[ LB_ADDR(_lbp.nsite, NDIST, NVEL, baseIndex+iv, LB_PHI, 3) ] = w1*(jdotc[iv]*rcs2 + sphidotq[iv]*r2rcs4); + /* cv[p = 4] = {1,0,-1} */ - for_simd_v(iv, NSIMDVL) { jdotc[iv] = 0.0; sphidotq[iv] = 0.0;} - + for_simd_v(iv, NSIMDVL) { jdotc[iv] = 0.0; sphidotq[iv] = 0.0;} for_simd_v(iv, NSIMDVL) jdotc[iv] += jphi[X][iv]; for_simd_v(iv, NSIMDVL) jdotc[iv] -= jphi[Z][iv]; for_simd_v(iv, NSIMDVL) sphidotq[iv] += sphi[0][0][iv]*6.6666666666666663e-01; @@ -2946,6 +2957,7 @@ __device__ void d3q19_mode2f_phi(double jdotc[NSIMDVL], = w2*(jdotc[iv]*rcs2 + sphidotq[iv]*r2rcs4); + /* cv[p = 5] = {1,-1,0} */ for_simd_v(iv, NSIMDVL) { jdotc[iv] = 0.0; sphidotq[iv] = 0.0;} for_simd_v(iv, NSIMDVL) jdotc[iv] += jphi[X][iv]; @@ -2961,6 +2973,7 @@ __device__ void d3q19_mode2f_phi(double jdotc[NSIMDVL], = w2*(jdotc[iv]*rcs2 + sphidotq[iv]*r2rcs4); + /* cv[p = 6] = {0,1,1} */ for_simd_v(iv, NSIMDVL) { jdotc[iv] = 0.0; sphidotq[iv] = 0.0;} for_simd_v(iv, NSIMDVL) jdotc[iv] += jphi[Y][iv]; @@ -2975,7 +2988,7 @@ __device__ void d3q19_mode2f_phi(double jdotc[NSIMDVL], f[ LB_ADDR(_lbp.nsite, NDIST, NVEL, baseIndex+iv, LB_PHI, 6) ] = w2*(jdotc[iv]*rcs2 + sphidotq[iv]*r2rcs4); - + /* cv[p = 7] = {0,1,0} */ for_simd_v(iv, NSIMDVL) { jdotc[iv] = 0.0; sphidotq[iv] = 0.0;} for_simd_v(iv, NSIMDVL) jdotc[iv] += jphi[Y][iv]; @@ -2987,7 +3000,7 @@ __device__ void d3q19_mode2f_phi(double jdotc[NSIMDVL], f[ LB_ADDR(_lbp.nsite, NDIST, NVEL, baseIndex+iv, LB_PHI, 7) ] = w1*(jdotc[iv]*rcs2 + sphidotq[iv]*r2rcs4); - + /* cv[p = 8] = {0,1,-1} */ for_simd_v(iv, NSIMDVL) { jdotc[iv] = 0.0; sphidotq[iv] = 0.0;} for_simd_v(iv, NSIMDVL) jdotc[iv] += jphi[Y][iv]; @@ -3002,7 +3015,7 @@ __device__ void d3q19_mode2f_phi(double jdotc[NSIMDVL], f[ LB_ADDR(_lbp.nsite, NDIST, NVEL, baseIndex+iv, LB_PHI, 8) ] = w2*(jdotc[iv]*rcs2 + sphidotq[iv]*r2rcs4); - + /* cv[p = 9] = {0,0,1} */ for_simd_v(iv, NSIMDVL) { jdotc[iv] = 0.0; sphidotq[iv] = 0.0;} for_simd_v(iv, NSIMDVL) jdotc[iv] += jphi[Z][iv]; @@ -3014,7 +3027,7 @@ __device__ void d3q19_mode2f_phi(double jdotc[NSIMDVL], f[ LB_ADDR(_lbp.nsite, NDIST, NVEL, baseIndex+iv, LB_PHI, 9) ] = w1*(jdotc[iv]*rcs2 + sphidotq[iv]*r2rcs4); - + /* cv[p = 10] = {0,0,-1} */ for_simd_v(iv, NSIMDVL) { jdotc[iv] = 0.0; sphidotq[iv] = 0.0;} for_simd_v(iv, NSIMDVL) jdotc[iv] -= jphi[Z][iv]; @@ -3026,7 +3039,7 @@ __device__ void d3q19_mode2f_phi(double jdotc[NSIMDVL], f[ LB_ADDR(_lbp.nsite, NDIST, NVEL, baseIndex+iv, LB_PHI, 10) ] = w1*(jdotc[iv]*rcs2 + sphidotq[iv]*r2rcs4); - + /* cv[p = 11] = {0,-1,1} */ for_simd_v(iv, NSIMDVL) { jdotc[iv] = 0.0; sphidotq[iv] = 0.0;} for_simd_v(iv, NSIMDVL) jdotc[iv] -= jphi[Y][iv]; @@ -3041,7 +3054,7 @@ __device__ void d3q19_mode2f_phi(double jdotc[NSIMDVL], f[ LB_ADDR(_lbp.nsite, NDIST, NVEL, baseIndex+iv, LB_PHI, 11) ] = w2*(jdotc[iv]*rcs2 + sphidotq[iv]*r2rcs4); - + /* cv[p = 12] = {0,-1,0} */ for_simd_v(iv, NSIMDVL) { jdotc[iv] = 0.0; sphidotq[iv] = 0.0;} for_simd_v(iv, NSIMDVL) jdotc[iv] -= jphi[Y][iv]; @@ -3053,7 +3066,7 @@ __device__ void d3q19_mode2f_phi(double jdotc[NSIMDVL], f[ LB_ADDR(_lbp.nsite, NDIST, NVEL, baseIndex+iv, LB_PHI, 12) ] = w1*(jdotc[iv]*rcs2 + sphidotq[iv]*r2rcs4); - + /* cv[p = 13] = {0,-1,-1} */ for_simd_v(iv, NSIMDVL) { jdotc[iv] = 0.0; sphidotq[iv] = 0.0;} for_simd_v(iv, NSIMDVL) jdotc[iv] -= jphi[Y][iv]; @@ -3068,7 +3081,7 @@ __device__ void d3q19_mode2f_phi(double jdotc[NSIMDVL], f[ LB_ADDR(_lbp.nsite, NDIST, NVEL, baseIndex+iv, LB_PHI, 13) ] = w2*(jdotc[iv]*rcs2 + sphidotq[iv]*r2rcs4); - + /* cv[p = 14] = {-1,1,0} */ for_simd_v(iv, NSIMDVL) { jdotc[iv] = 0.0; sphidotq[iv] = 0.0;} for_simd_v(iv, NSIMDVL) jdotc[iv] -= jphi[X][iv]; @@ -3083,7 +3096,7 @@ __device__ void d3q19_mode2f_phi(double jdotc[NSIMDVL], f[ LB_ADDR(_lbp.nsite, NDIST, NVEL, baseIndex+iv, LB_PHI, 14) ] = w2*(jdotc[iv]*rcs2 + sphidotq[iv]*r2rcs4); - + /* cv[p = 15] = {-1,0,1} */ for_simd_v(iv, NSIMDVL) { jdotc[iv] = 0.0; sphidotq[iv] = 0.0;} for_simd_v(iv, NSIMDVL) jdotc[iv] -= jphi[X][iv]; @@ -3098,7 +3111,7 @@ __device__ void d3q19_mode2f_phi(double jdotc[NSIMDVL], f[ LB_ADDR(_lbp.nsite, NDIST, NVEL, baseIndex+iv, LB_PHI, 15) ] = w2*(jdotc[iv]*rcs2 + sphidotq[iv]*r2rcs4); - + /* cv[p = 16] = {-1,0,0} */ for_simd_v(iv, NSIMDVL) { jdotc[iv] = 0.0; sphidotq[iv] = 0.0;} for_simd_v(iv, NSIMDVL) jdotc[iv] -= jphi[X][iv]; @@ -3110,7 +3123,7 @@ __device__ void d3q19_mode2f_phi(double jdotc[NSIMDVL], f[ LB_ADDR(_lbp.nsite, NDIST, NVEL, baseIndex+iv, LB_PHI, 16) ] = w1*(jdotc[iv]*rcs2 + sphidotq[iv]*r2rcs4); - + /* cv[p = 17] = {-1,0,-1} */ for_simd_v(iv, NSIMDVL) { jdotc[iv] = 0.0; sphidotq[iv] = 0.0;} for_simd_v(iv, NSIMDVL) jdotc[iv] -= jphi[X][iv]; @@ -3125,7 +3138,7 @@ __device__ void d3q19_mode2f_phi(double jdotc[NSIMDVL], f[ LB_ADDR(_lbp.nsite, NDIST, NVEL, baseIndex+iv, LB_PHI, 17) ] = w2*(jdotc[iv]*rcs2 + sphidotq[iv]*r2rcs4); - + /* cv[p = 18] = {1,1,0} */ for_simd_v(iv, NSIMDVL) { jdotc[iv] = 0.0; sphidotq[iv] = 0.0;} for_simd_v(iv, NSIMDVL) jdotc[iv] -= jphi[X][iv]; diff --git a/src/fe_lc_stats.c b/src/fe_lc_stats.c index 28e07b4bd..f7ebb3cf8 100644 --- a/src/fe_lc_stats.c +++ b/src/fe_lc_stats.c @@ -8,7 +8,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2017-2020 The University of Edinburgh + * (c) 2017-2022 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -420,11 +420,13 @@ static int fe_lc_colloid(fe_lc_t * fe, cs_t * cs, colloids_info_t * cinfo, map_status(map, index, &status); if (status != MAP_FLUID) continue; + /* This site is fluid. Look at six nearest neighbours... */ field_tensor(fe->q, index, qs); nhat[Y] = 0; nhat[Z] = 0; + /* Surface in direction of (ic+1,jc,kc) */ index1 = cs_index(cs, ic+1, jc, kc); map_status(map, index1, &status); @@ -436,6 +438,7 @@ static int fe_lc_colloid(fe_lc_t * fe, cs_t * cs, colloids_info_t * cinfo, fs[1] += 1.0; } + /* Surface in direction of (ic-1,jc,kc) */ index1 = cs_index(cs, ic-1, jc, kc); map_status(map, index1, &status); @@ -450,6 +453,7 @@ static int fe_lc_colloid(fe_lc_t * fe, cs_t * cs, colloids_info_t * cinfo, nhat[X] = 0; nhat[Z] = 0; + /* Surface in direction of (ic, jc+1,kc) */ index1 = cs_index(cs, ic, jc+1, kc); map_status(map, index1, &status); @@ -461,6 +465,7 @@ static int fe_lc_colloid(fe_lc_t * fe, cs_t * cs, colloids_info_t * cinfo, fs[1] += 1.0; } + /* Surface in direction of (ic,jc-1,kc) */ index1 = cs_index(cs, ic, jc-1, kc); map_status(map, index1, &status); @@ -475,6 +480,7 @@ static int fe_lc_colloid(fe_lc_t * fe, cs_t * cs, colloids_info_t * cinfo, nhat[X] = 0; nhat[Y] = 0; + /* Suface in direction of (ic,jc,kc+1) */ index1 = cs_index(cs, ic, jc, kc+1); map_status(map, index1, &status); @@ -486,6 +492,7 @@ static int fe_lc_colloid(fe_lc_t * fe, cs_t * cs, colloids_info_t * cinfo, fs[1] += 1.0; } + /* Surface in direction of (ic,jc,kc-1) */ index1 = cs_index(cs, ic, jc, kc-1); map_status(map, index1, &status); @@ -528,6 +535,8 @@ static int fe_lc_colloid(fe_lc_t * fe, cs_t * cs, colloids_info_t * cinfo, * gradient calculation currently in gradient_3d_7pt_solid which * needs to be refactored. That is: colloids_q_boundary(). * + * E.g., Can we use lc_anchoring.h? + * *****************************************************************************/ __host__ int blue_phase_fs(fe_lc_param_t * feparam, const double dn[3], From c6eac4f2a8f4c056f5fd33015b9032b1b6bf1408 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 12 Nov 2022 12:55:36 +0000 Subject: [PATCH 109/244] Code improvement --- src/ewald.c | 130 +++++++++++++++++++--------------------- tests/unit/test_ewald.c | 3 +- 2 files changed, 65 insertions(+), 68 deletions(-) diff --git a/src/ewald.c b/src/ewald.c index 567959b1f..59b33e38e 100644 --- a/src/ewald.c +++ b/src/ewald.c @@ -10,7 +10,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2007-2020 The University of Edinburgh. + * (c) 2007-2022 The University of Edinburgh. * * Contributing authors: * Grace Kim @@ -336,27 +336,20 @@ int ewald_fourier_space_energy(ewald_t * ewald, double * ef) { * For each k, for the Fourier space sum, we need * sinx_ = \sum_i u_i.k sin(k.r_i) i.e., S(k) * cosx_ = \sum_i u_i.k cos(k.r_i) i.e., C(k) + * where \sum_i is the sum over the dipoles. * *****************************************************************************/ static int ewald_sum_sin_cos_terms(ewald_t * ewald) { - double k[3], ksq; double fkx, fky, fkz; - int kx, ky, kz, kn = 0; - int ic, jc, kc; - int ncell[3]; - double ltot[3]; - double * subsin; - double * subcos; PI_DOUBLE(pi); - MPI_Comm comm; + colloid_t * pc = NULL; assert(ewald); cs_ltot(ewald->cs, ltot); - colloids_info_ncell(ewald->cinfo, ncell); fkx = 2.0*pi/ltot[X]; fky = 2.0*pi/ltot[Y]; @@ -364,88 +357,90 @@ static int ewald_sum_sin_cos_terms(ewald_t * ewald) { /* Comupte S(k) and C(k) from sum over particles */ - for (kn = 0; kn < nktot_; kn++) { + for (int kn = 0; kn < nktot_; kn++) { sinx_[kn] = 0.0; cosx_[kn] = 0.0; } - for (ic = 1; ic <= ncell[X]; ic++) { - for (jc = 1; jc <= ncell[Y]; jc++) { - for (kc = 1; kc <= ncell[Z]; kc++) { - - colloid_t * p_colloid; + colloids_info_local_head(ewald->cinfo, &pc); - colloids_info_cell_list_head(ewald->cinfo, ic, jc, kc, &p_colloid); + for ( ; pc; pc = pc->nextlocal) { - while (p_colloid != NULL) { + int kn = 0; - if (p_colloid->s.type == COLLOID_TYPE_SUBGRID) continue; + if (pc->s.type == COLLOID_TYPE_SUBGRID) continue; - kn = 0; - ewald_set_kr_table(ewald, p_colloid->s.r); + ewald_set_kr_table(ewald, pc->s.r); - for (kz = 0; kz <= nk_[Z]; kz++) { - for (ky = -nk_[Y]; ky <= nk_[Y]; ky++) { - for (kx = -nk_[X]; kx <= nk_[X]; kx++) { - double udotk, kdotr; - double skr[3], ckr[3]; + /* Loop over wavevectors */ + for (int kz = 0; kz <= nk_[Z]; kz++) { + for (int ky = -nk_[Y]; ky <= nk_[Y]; ky++) { + for (int kx = -nk_[X]; kx <= nk_[X]; kx++) { + double k[3], ksq; + double udotk, kdotr; + double skr[3], ckr[3]; - k[X] = fkx*kx; - k[Y] = fky*ky; - k[Z] = fkz*kz; - ksq = k[X]*k[X] + k[Y]*k[Y] + k[Z]*k[Z]; + k[X] = fkx*kx; + k[Y] = fky*ky; + k[Z] = fkz*kz; + ksq = k[X]*k[X] + k[Y]*k[Y] + k[Z]*k[Z]; - if (ksq <= 0.0 || ksq > kmax_) continue; + if (ksq <= 0.0 || ksq > kmax_) continue; + assert(kn < nktot_); - skr[X] = sinkr_[3*abs(kx) + X]; - skr[Y] = sinkr_[3*abs(ky) + Y]; - skr[Z] = sinkr_[3*kz + Z]; - ckr[X] = coskr_[3*abs(kx) + X]; - ckr[Y] = coskr_[3*abs(ky) + Y]; - ckr[Z] = coskr_[3*kz + Z]; + skr[X] = sinkr_[3*abs(kx) + X]; + skr[Y] = sinkr_[3*abs(ky) + Y]; + skr[Z] = sinkr_[3*kz + Z]; + ckr[X] = coskr_[3*abs(kx) + X]; + ckr[Y] = coskr_[3*abs(ky) + Y]; + ckr[Z] = coskr_[3*kz + Z]; - if (kx < 0) skr[X] = -skr[X]; - if (ky < 0) skr[Y] = -skr[Y]; + if (kx < 0) skr[X] = -skr[X]; + if (ky < 0) skr[Y] = -skr[Y]; - udotk = dot_product(p_colloid->s.s, k); + udotk = dot_product(pc->s.s, k); - kdotr = skr[X]*ckr[Y]*ckr[Z] + ckr[X]*skr[Y]*ckr[Z] - + ckr[X]*ckr[Y]*skr[Z] - skr[X]*skr[Y]*skr[Z]; - sinx_[kn] += udotk*kdotr; + kdotr = skr[X]*ckr[Y]*ckr[Z] + ckr[X]*skr[Y]*ckr[Z] + + ckr[X]*ckr[Y]*skr[Z] - skr[X]*skr[Y]*skr[Z]; + sinx_[kn] += udotk*kdotr; - kdotr = ckr[X]*ckr[Y]*ckr[Z] - ckr[X]*skr[Y]*skr[Z] - - skr[X]*ckr[Y]*skr[Z] - skr[X]*skr[Y]*ckr[Z]; - cosx_[kn] += udotk*kdotr; + kdotr = ckr[X]*ckr[Y]*ckr[Z] - ckr[X]*skr[Y]*skr[Z] + - skr[X]*ckr[Y]*skr[Z] - skr[X]*skr[Y]*ckr[Z]; + cosx_[kn] += udotk*kdotr; - kn++; - } - } - } - p_colloid = p_colloid->next; + kn++; } - /* Next cell */ } } + /* Next colloid */ } - subsin = (double *) calloc(nktot_, sizeof(double)); - subcos = (double *) calloc(nktot_, sizeof(double)); - assert(subsin); - assert(subcos); - if (subsin == NULL) pe_fatal(ewald->pe, "calloc(subsin) failed\n"); - if (subcos == NULL) pe_fatal(ewald->pe, "calloc(subcos) failed\n"); + { + double * subsin = NULL; /* Local contribution to sum of sin() */ + double * subcos = NULL; /* Local contribution to sum of cos() */ + MPI_Comm comm = MPI_COMM_NULL; - for (kn = 0; kn < nktot_; kn++) { - subsin[kn] = sinx_[kn]; - subcos[kn] = cosx_[kn]; - } + cs_cart_comm(ewald->cs, &comm); + + subsin = (double *) calloc(nktot_, sizeof(double)); + subcos = (double *) calloc(nktot_, sizeof(double)); + assert(subsin); + assert(subcos); + if (subsin == NULL) pe_fatal(ewald->pe, "calloc(subsin) failed\n"); + if (subcos == NULL) pe_fatal(ewald->pe, "calloc(subcos) failed\n"); - cs_cart_comm(ewald->cs, &comm); - MPI_Allreduce(subsin, sinx_, nktot_, MPI_DOUBLE, MPI_SUM, comm); - MPI_Allreduce(subcos, cosx_, nktot_, MPI_DOUBLE, MPI_SUM, comm); + for (int kn = 0; kn < nktot_; kn++) { + subsin[kn] = sinx_[kn]; + subcos[kn] = cosx_[kn]; + } - free(subsin); - free(subcos); + /* Could be reformed into a single reduction .. */ + MPI_Allreduce(subsin, sinx_, nktot_, MPI_DOUBLE, MPI_SUM, comm); + MPI_Allreduce(subcos, cosx_, nktot_, MPI_DOUBLE, MPI_SUM, comm); + + free(subsin); + free(subcos); + } return 0; } @@ -833,6 +828,7 @@ static int ewald_set_kr_table(ewald_t * ewald, double r[3]) { cs_ltot(ewald->cs, ltot); + /* k = 0 and k = 1 */ for (i = 0; i < 3; i++) { sinkr_[3*0 + i] = 0.0; coskr_[3*0 + i] = 1.0; diff --git a/tests/unit/test_ewald.c b/tests/unit/test_ewald.c index 24caa149a..47ff72ba6 100644 --- a/tests/unit/test_ewald.c +++ b/tests/unit/test_ewald.c @@ -10,7 +10,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2010-2017 The University of Edinburgh + * (c) 2010-2022 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -102,6 +102,7 @@ int test_ewald_suite(void) { assert(p_c1 != NULL); assert(p_c2 != NULL); colloids_info_ntotal_set(cinfo); + colloids_info_list_local_build(cinfo); /* First colloid .... */ From 68843a546d39aee141bba5d1fd471aabdebb4bbc Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 12 Nov 2022 17:06:15 +0000 Subject: [PATCH 110/244] Remove commented-out code --- src/brownian.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/brownian.c b/src/brownian.c index a8d3d6396..ac7a67c25 100644 --- a/src/brownian.c +++ b/src/brownian.c @@ -90,8 +90,7 @@ void do_brownian_dynamics() { /* Set random numbers for each particle */ brownian_set_random(); colloids_halo_state(); - /* brownian_step_ermak_buckholz();*/ - brownian_step_no_inertia(); + brownian_step_no_inertia(); /* Or Ermak & Buckholz routine */ /* diagnostics */ From b4627e0ca632321ad10ade9d0b6c48e0552a552a Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 12 Nov 2022 17:29:37 +0000 Subject: [PATCH 111/244] Update command line parsing --- util/coll_squ_subgrid_init.c | 13 +++++++++---- util/colloid_init.c | 15 ++++++++++----- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/util/coll_squ_subgrid_init.c b/util/coll_squ_subgrid_init.c index 22c2765f5..b1613df34 100644 --- a/util/coll_squ_subgrid_init.c +++ b/util/coll_squ_subgrid_init.c @@ -93,19 +93,24 @@ int main(int argc, char ** argv) { MPI_Init(&argc, &argv); - for (optind = 1; optind < argc && argv[optind][0] == '-'; optind++) { + if ((argc-1) % 2 != 0) { + printf("Usage: %s [-n Monte-Carlo-moves] [-v colume-fraction]\n", argv[0]); + exit(EXIT_FAILURE); + } + + for (optind = 1; optind < argc && argv[optind][0] == '-'; optind += 2) { switch (argv[optind][1]) { case 'n': - mc = atoi(argv[++optind]); + mc = atoi(argv[optind+1]); printf("%s: option -n sets mc = %d\n", argv[0], mc); break; case 'v': - vf = atof(argv[++optind]); + vf = atof(argv[optind+1]); printf("%s: option -v sets vf = %f\n", argv[0], vf); break; default: fprintf(stderr, "Unrecognised option: %s\n", argv[optind]); - fprintf(stderr, "Usage: %s [-ahv]\n", argv[0]); + fprintf(stderr, "Usage: %s [-nv]\n", argv[0]); exit(EXIT_FAILURE); } } diff --git a/util/colloid_init.c b/util/colloid_init.c index 3aee96827..eb284da37 100644 --- a/util/colloid_init.c +++ b/util/colloid_init.c @@ -26,7 +26,7 @@ * Edinburgh Parallel Computing Centre * * Kevin Stratford (kevin@epcc.ed.ac.uk) - * (c) 2012-2020 The University of Edinburgh + * (c) 2012-2022 The University of Edinburgh * *****************************************************************************/ @@ -100,18 +100,23 @@ int main(int argc, char ** argv) { /* Check the command line, then parse the meta data information, * and sort out the data file name */ - for (optind = 1; optind < argc && argv[optind][0] == '-'; optind++) { + if ((argc-1) % 2 != 0) { + printf("Usage: %s [-a a0] [-h ah] [-v volume-fraction]\n", argv[0]); + exit(EXIT_FAILURE); + } + + for (optind = 1; optind < argc && argv[optind][0] == '-'; optind += 2) { switch (argv[optind][1]) { case 'a': - a0 = atof(argv[++optind]); + a0 = atof(argv[optind+1]); printf("%s: option -a sets a0 = %f\n", argv[0], a0); break; case'h': - ah = atof(argv[++optind]); + ah = atof(argv[optind+1]); printf("%s: option -h sets ah = %f\n", argv[0], ah); break; case 'v': - vf = atof(argv[++optind]); + vf = atof(argv[optind+1]); printf("%s: option -v sets vf = %f\n", argv[0], vf); break; default: From 912a2849545419a8394e3f388ffe49f78642e25a Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 12 Nov 2022 17:41:24 +0000 Subject: [PATCH 112/244] Fix alert on fopen --- util/coll_squ_subgrid_init.c | 3 ++- util/colloid_init.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/util/coll_squ_subgrid_init.c b/util/coll_squ_subgrid_init.c index b1613df34..07d635def 100644 --- a/util/coll_squ_subgrid_init.c +++ b/util/coll_squ_subgrid_init.c @@ -36,6 +36,7 @@ #include "../src/coords.h" #include "../src/colloid.h" #include "../src/util.h" +#include "../src/util_fopen.h" enum format {ASCII, BINARY}; #define NTRYMAX 10000 @@ -582,7 +583,7 @@ void colloid_init_write_file(const int nc, const colloid_state_t * pc, const char * filename = "config.cds.init.001-001"; FILE * fp; - fp = fopen(filename, "w"); + fp = util_fopen(filename, "w"); if (fp == NULL) { printf("Could not open %s\n", filename); exit(0); diff --git a/util/colloid_init.c b/util/colloid_init.c index eb284da37..26845167d 100644 --- a/util/colloid_init.c +++ b/util/colloid_init.c @@ -39,6 +39,7 @@ #include "../src/pe.h" #include "../src/coords.h" #include "../src/util.h" +#include "../src/util_fopen.h" #include "../src/ran.h" enum format {ASCII, BINARY}; @@ -573,7 +574,7 @@ void colloid_init_write_file(const int nc, const colloid_state_t * pc, const char * filename = "config.cds.init.001-001"; FILE * fp; - fp = fopen(filename, "w"); + fp = util_fopen(filename, "w"); if (fp == NULL) { printf("Could not open %s\n", filename); exit(0); From 0f287ed5111e8b43fdd1bdfcaadbd69bd2b36951 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 14 Nov 2022 08:51:25 +0000 Subject: [PATCH 113/244] Merge pth_s.h with phi_force_stress.h --- src/phi_force.c | 5 +++-- src/phi_force_colloid.c | 2 +- src/phi_force_stress.c | 1 - src/phi_force_stress.h | 9 +++++++++ src/pth_s.h | 19 ------------------- src/stats_colloid_force_split.c | 4 ++-- 6 files changed, 15 insertions(+), 25 deletions(-) delete mode 100644 src/pth_s.h diff --git a/src/phi_force.c b/src/phi_force.c index 5f7065e50..20e4a6691 100644 --- a/src/phi_force.c +++ b/src/phi_force.c @@ -31,9 +31,9 @@ #include "kernel.h" #include "hydro.h" -#include "pth_s.h" #include "timer.h" #include "phi_force.h" +#include "phi_force_stress.h" #include "phi_force_colloid.h" #include "phi_grad_mu.h" #include "physics.h" @@ -79,7 +79,8 @@ __host__ int phi_force_calculation(pe_t * pe, cs_t * cs, lees_edw_t * le, int is_pm; int nplanes = 0; - if (pth == NULL) return 0; + assert(pth); + if (hydro == NULL) return 0; if (pth->method == FE_FORCE_METHOD_NO_FORCE) return 0; diff --git a/src/phi_force_colloid.c b/src/phi_force_colloid.c index d2b0e5ac9..e5491066a 100644 --- a/src/phi_force_colloid.c +++ b/src/phi_force_colloid.c @@ -59,7 +59,7 @@ #include "kernel.h" #include "wall.h" #include "colloids.h" -#include "pth_s.h" +#include "phi_force_stress.h" #include "phi_force_colloid.h" #include "timer.h" diff --git a/src/phi_force_stress.c b/src/phi_force_stress.c index 1b54dcd34..57ad9b5ae 100644 --- a/src/phi_force_stress.c +++ b/src/phi_force_stress.c @@ -23,7 +23,6 @@ #include "coords.h" #include "timer.h" #include "kernel.h" -#include "pth_s.h" #include "phi_force_stress.h" __global__ void pth_kernel(kernel_ctxt_t * ktx, pth_t * pth, fe_t * fe); diff --git a/src/phi_force_stress.h b/src/phi_force_stress.h index 04f8571e9..d94b56e42 100644 --- a/src/phi_force_stress.h +++ b/src/phi_force_stress.h @@ -24,6 +24,15 @@ typedef struct pth_s pth_t; +struct pth_s { + pe_t * pe; /* Parallel environment */ + cs_t * cs; /* Coordinate system */ + int method; /* Method for force computation */ + int nsites; /* Number of sites allocated */ + double * str; /* Stress may be antisymmetric */ + pth_t * target; /* Target memory */ +}; + __host__ int pth_create(pe_t * pe, cs_t * cs, int method, pth_t ** pth); __host__ int pth_free(pth_t * pth); __host__ int pth_memcpy(pth_t * pth, tdpMemcpyKind flag); diff --git a/src/pth_s.h b/src/pth_s.h deleted file mode 100644 index b2b8cc40a..000000000 --- a/src/pth_s.h +++ /dev/null @@ -1,19 +0,0 @@ -/***************************************************************************** - * - *****************************************************************************/ - -#ifndef PTH_S_H -#define PTH_S_H - -#include "phi_force_stress.h" - -struct pth_s { - pe_t * pe; /* Parallel environment */ - cs_t * cs; /* Coordinate system */ - int method; /* Method for force computation */ - int nsites; /* Number of sites allocated */ - double * str; /* Stress may be antisymmetric */ - pth_t * target; /* Target memory */ -}; - -#endif diff --git a/src/stats_colloid_force_split.c b/src/stats_colloid_force_split.c index d0d6f1a59..50e84c519 100644 --- a/src/stats_colloid_force_split.c +++ b/src/stats_colloid_force_split.c @@ -21,11 +21,11 @@ #include #include "pe.h" -#include "pth_s.h" #include "lb_data.h" #include "blue_phase.h" #include "colloid_sums.h" #include "phi_force_stress.h" +#include "util_fopen.h" #include "stats_colloid_force_split.h" static int switch_me_on_ = 0; @@ -151,7 +151,7 @@ int stats_colloid_force_split_output(colloids_info_t * cinfo, int timestep) { FILE * fp = NULL; sprintf(filename, "colloid-diag-%8.8d.dat", timestep); - fp = fopen(filename, "w"); + fp = util_fopen(filename, "w"); if (fp == NULL) { pe_fatal(pe, "Failed to open diagnostic file %s\n", filename); } From f71d1654ba663a09b4bdf8980a583a9d159ff3b8 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 14 Nov 2022 08:57:58 +0000 Subject: [PATCH 114/244] Allow phi grad mu for colloids --- src/ludwig.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/ludwig.c b/src/ludwig.c index 509f190f9..f22ac667a 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -755,9 +755,17 @@ void ludwig_run(const char * inputfile) { } else { + if (ludwig->pth->method == FE_FORCE_METHOD_STRESS_DIVERGENCE) { pth_force_colloid(ludwig->pth, ludwig->fe, ludwig->collinfo, ludwig->hydro, ludwig->map, ludwig->wall, &ludwig->lb->model); + } + else { + /* Allow case with colloids using PHI_GRADMU_CORRECTION */ + phi_force_calculation(ludwig->pe, ludwig->cs, ludwig->le, + ludwig->wall, ludwig->pth, ludwig->fe, + ludwig->map, ludwig->phi, ludwig->hydro); + } } } From 0834c397ff291dd5810be1e268b2b62bd90e7757 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 18 Nov 2022 15:16:19 +0000 Subject: [PATCH 115/244] Add missing include --- src/cs_limits.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cs_limits.h b/src/cs_limits.h index e9f3d3fa7..de5895433 100644 --- a/src/cs_limits.h +++ b/src/cs_limits.h @@ -23,6 +23,8 @@ #ifndef LUDWIG_CS_LIMITS_H #define LUDWIG_CS_LIMITS_H +#include + typedef struct cs_limits_s cs_limits_t; struct cs_limits_s { From e0456ba83684876ff9015c8cc7738d229d6e200b Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 18 Nov 2022 15:17:10 +0000 Subject: [PATCH 116/244] Removed --- src/io_cart_sub.c | 154 ---------------------------------------------- src/io_cart_sub.h | 41 ------------ 2 files changed, 195 deletions(-) delete mode 100644 src/io_cart_sub.c delete mode 100644 src/io_cart_sub.h diff --git a/src/io_cart_sub.c b/src/io_cart_sub.c deleted file mode 100644 index 36a9fc66f..000000000 --- a/src/io_cart_sub.c +++ /dev/null @@ -1,154 +0,0 @@ -/***************************************************************************** - * - * io_cart_sub.c - * - * A 3-dimensional Cartesian partitioning of the existing global - * Cartesian communictor into blocks for the purpose of i/o to - * file. One file for each Cartesian sub-block. - * - * - * Edinburgh Soft Matter and Statistical Physics Group and - * Edinburgh Parallel Computing Centre - * - * (c) 2022 The University of Edinburgh - * - * Kevin Stratford (kevin@epcc.ed.ac.uk) - * - *****************************************************************************/ - -#include - -#include "coords_s.h" -#include "io_cart_sub.h" - -/***************************************************************************** - * - * io_cart_sub_create - * - * The requested iogrid[3] must exactly devide the existing Cartesian - * communicator. - * - * Returns 0 on success with a newly created communicator. - * - *****************************************************************************/ - -int io_cart_sub_create(cs_t * cs, int iogrid[3], io_cart_sub_t * iosub) { - - assert(cs); - assert(iosub); - - *iosub = (io_cart_sub_t) {0}; - - /* Check we can make a decomposition ... */ - - for (int i = 0; i < 3; i++) { - if (cs->param->mpi_cartsz[i] % iogrid[i] != 0) goto err; - } - - cs_cart_comm(cs, &iosub->parent); - - /* Some integer arithmetic to form blocks */ - - for (int i = 0; i < 3; i++) { - int isz = cs->param->mpi_cartsz[i]; - int icoord = cs->param->mpi_cartcoords[i]; - int ioffset = icoord / (isz/iogrid[i]); - - iosub->size[i] = iogrid[i]; - iosub->coords[i] = iogrid[i]*icoord/isz; - - /* Offset and size, local size allowing for non-uniform decomposition */ - - iosub->ntotal[i] = cs->param->ntotal[i]; - iosub->nlocal[i] = 0; - iosub->offset[i] = cs->listnoffset[i][ioffset]; - - for (int j = ioffset; j < ioffset + (isz/iogrid[i]); j++) { - iosub->nlocal[i] += cs->listnlocal[i][j]; - } - } - - /* We can now split the communicator from the coordinates */ - - iosub->nfile = iogrid[X]*iogrid[Y]*iogrid[Z]; - iosub->index = iosub->coords[X] - + iosub->coords[Y]*iogrid[X] - + iosub->coords[Z]*iogrid[X]*iogrid[Y]; - { - int rank = -1; - MPI_Comm_rank(iosub->parent, &rank); - MPI_Comm_split(iosub->parent, iosub->index, rank, &iosub->comm); - } - - return 0; - - err: - return -1; -} - -/***************************************************************************** - * - * io_cart_sub_free - * - *****************************************************************************/ - -int io_cart_sub_free(io_cart_sub_t * iosub) { - - assert(iosub); - - MPI_Comm_free(&iosub->comm); - - *iosub = (io_cart_sub_t) {0}; - iosub->comm = MPI_COMM_NULL; - - return 0; -} - -/***************************************************************************** - * - * io_cart_sub_printf - * - *****************************************************************************/ - -int io_cart_sub_printf(const io_cart_sub_t * iosub) { - - int sz = -1; - int rank = -1; - char msg[BUFSIZ] = {0}; - - assert(iosub); - - /* Commnuication is in the Cartesian communicator */ - - MPI_Comm_size(iosub->parent, &sz); - MPI_Comm_rank(iosub->parent, &rank); - - - { - int coords[3] = {0}; /* Cartesian communicator coordinates */ - int iorank = -1; - MPI_Comm_rank(iosub->comm, &iorank); - MPI_Cart_coords(iosub->parent, rank, 3, coords); - sprintf(msg, "%4d %3d %3d %3d %2d %4d %3d %3d %3d %4d %4d %4d", - rank, coords[X], coords[Y], coords[Z], - iosub->index, iorank, - iosub->coords[X], iosub->coords[Y], iosub->coords[Z], - iosub->offset[X], iosub->offset[Y], iosub->offset[Z]); - } - - if (rank > 0) { - MPI_Send(msg, BUFSIZ, MPI_CHAR, 0, 271022, iosub->parent); - } - else { - printf("Cartesian io_cart_sub\n"); - printf("rank x y z file rank x y z ox oy oz\n"); - printf("%s\n", msg); - for (int ir = 1; ir < sz; ir++) { - MPI_Recv(msg, BUFSIZ, MPI_CHAR, ir, 271022, iosub->parent, - MPI_STATUS_IGNORE); - printf("%s\n", msg); - } - } - - return 0; -} diff --git a/src/io_cart_sub.h b/src/io_cart_sub.h deleted file mode 100644 index 6f0db7463..000000000 --- a/src/io_cart_sub.h +++ /dev/null @@ -1,41 +0,0 @@ -/***************************************************************************** - * - * io_cart_sub.h - * - * - * Edinburgh Soft Matter and Statistical Physics Group and - * Edinburgh Parallel Computing Centre - * - * (c) 2022 The University of Edinburgh - * - * Kevin Stratford (kevin@epcc.ed.ac.uk) - * - *****************************************************************************/ - -#ifndef LUDWIG_IO_CART_SUB_H -#define LUDWIG_IO_CART_SUB_H - -#include "coords.h" - -typedef struct io_cart_sub_s io_cart_sub_t; - -struct io_cart_sub_s { - - MPI_Comm parent; /* Global Cartesian communictor */ - MPI_Comm comm; /* MPI communicator for this 3d subsection */ - int size[3]; /* Global I/O Grid or topology */ - int coords[3]; /* Cartesian position of this rank in group */ - - int ntotal[3]; /* Total sites (all files) */ - int nlocal[3]; /* Local sites (this file) */ - int offset[3]; /* Offset (this file) */ - - int nfile; /* Total number of files in decomposition */ - int index; /* Index of this group {0, 1, ..., nfile-1} */ -}; - -int io_cart_sub_create(cs_t * cs, int iogrid[3], io_cart_sub_t * iosub); -int io_cart_sub_free(io_cart_sub_t * iosub); -int io_cart_sub_printf(const io_cart_sub_t * iosub); - -#endif From 17ea17c6fb43acf36259211e7bb17a3c5eef330b Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 18 Nov 2022 15:17:56 +0000 Subject: [PATCH 117/244] Now io_impl_mpio --- src/io_aggr_buf_mpio.c | 145 ----------------------------------------- src/io_aggr_buf_mpio.h | 25 ------- 2 files changed, 170 deletions(-) delete mode 100644 src/io_aggr_buf_mpio.c delete mode 100644 src/io_aggr_buf_mpio.h diff --git a/src/io_aggr_buf_mpio.c b/src/io_aggr_buf_mpio.c deleted file mode 100644 index 5b4860de4..000000000 --- a/src/io_aggr_buf_mpio.c +++ /dev/null @@ -1,145 +0,0 @@ -/***************************************************************************** - * - * io_impl_mpio.c - * - * Read/write aggregated data buffers using MPI/IO. - * - * - * Edinburgh Soft Matter and Statistical Physics Group and - * Edinburgh Parallel Computing Centre - * - * (c) 2022 The University of Edinburgh - * - * Kevin Stratford (kevin@epcc.ed.ac.uk) - * - *****************************************************************************/ - -#include - -#include "io_aggr_buf_mpio.h" - -/***************************************************************************** - * - * io_aggr_buf_write - * - * Generalise to case where separate write_all in comm. - * - *****************************************************************************/ - -int io_aggr_mpio_write(pe_t * pe, cs_t * cs, const char * filename, - const io_aggregator_t * buf) { - assert(pe); - assert(cs); - assert(filename); - assert(buf); - - int ndims = 3; /* nz may be 1; ie., 2-dimensional system */ - int sizes[3] = {0}; /* ie., ntotal */ - int subsizes[3] = {0}; /* ie., nlocal */ - int starts[3] = {0}; /* ie., local offset */ - int zero3[3] = {0}; - - MPI_Comm comm = MPI_COMM_NULL; - MPI_Datatype etype = MPI_DATATYPE_NULL; /* element description */ - MPI_Datatype array = MPI_DATATYPE_NULL; /* local array description */ - MPI_Datatype filetype = MPI_DATATYPE_NULL; /* global description */ - - cs_cart_comm(cs, &comm); - cs_ntotal(cs, sizes); - cs_nlocal(cs, subsizes); - cs_nlocal_offset(cs, starts); - - /* Element type (multiple of MPI_CHAR), and file type */ - - MPI_Type_contiguous(buf->szelement, MPI_CHAR, &etype); - MPI_Type_create_subarray(ndims, subsizes, subsizes, zero3, MPI_ORDER_C, - etype, &array); - MPI_Type_create_subarray(ndims, sizes, subsizes, starts, MPI_ORDER_C, - etype, &filetype); - - MPI_Type_commit(&etype); - MPI_Type_commit(&array); - MPI_Type_commit(&filetype); - - { - MPI_File fh = MPI_FILE_NULL; - MPI_Info info = MPI_INFO_NULL; /* MUST BE SUPPLIED SOMEHOW */ - MPI_Offset disp = 0; - - int count = 1; - - MPI_File_open(comm, filename, MPI_MODE_WRONLY+MPI_MODE_CREATE, info, &fh); - MPI_File_set_view(fh, disp, etype, filetype, "native", info); - MPI_File_write_all(fh, buf->buf, count, array, MPI_STATUS_IGNORE); - MPI_File_close(&fh); - } - - MPI_Type_free(&filetype); - MPI_Type_free(&array); - MPI_Type_free(&etype); - - return 0; -} - -/***************************************************************************** - * - * io_aggr_mpio_read - * - * SAME except write_all is read_all and mode! - * - *****************************************************************************/ - -int io_aggr_mpio_read(pe_t * pe, cs_t * cs, const char * filename, - io_aggregator_t * buf) { - assert(pe); - assert(cs); - assert(filename); - assert(buf); - - int ndims = 3; /* nz may be 1; ie., 2-dimensional system */ - int sizes[3] = {0}; /* ie., ntotal */ - int subsizes[3] = {0}; /* ie., nlocal */ - int starts[3] = {0}; /* ie., local offset */ - int zero[3] = {0}; - - MPI_Comm comm = MPI_COMM_NULL; - MPI_Datatype etype = MPI_DATATYPE_NULL; - MPI_Datatype array = MPI_DATATYPE_NULL; - MPI_Datatype filetype = MPI_DATATYPE_NULL; - - cs_cart_comm(cs, &comm); - cs_ntotal(cs, sizes); - cs_nlocal(cs, subsizes); - cs_nlocal_offset(cs, starts); - - /* Element type (multiple of MPI_CHAR), and file type */ - - MPI_Type_contiguous(buf->szelement, MPI_CHAR, &etype); - MPI_Type_create_subarray(ndims, subsizes, subsizes, zero, MPI_ORDER_C, - etype, &array); - MPI_Type_create_subarray(ndims, sizes, subsizes, starts, MPI_ORDER_C, - etype, &filetype); - - MPI_Type_commit(&etype); - MPI_Type_commit(&array); - MPI_Type_commit(&filetype); - - { - MPI_File fh = MPI_FILE_NULL; - MPI_Info info = MPI_INFO_NULL; /* MUST BE SUPPLIED SOMEHOW */ - MPI_Offset disp = 0; - - int count = 1; - - MPI_File_open(comm, filename, MPI_MODE_RDONLY, info, &fh); - MPI_File_set_view(fh, disp, etype, filetype, "native", info); - MPI_File_read_all(fh, buf->buf, count, array, MPI_STATUS_IGNORE); - MPI_File_close(&fh); - } - - MPI_Type_free(&filetype); - MPI_Type_free(&array); - MPI_Type_free(&etype); - - return 0; -} diff --git a/src/io_aggr_buf_mpio.h b/src/io_aggr_buf_mpio.h deleted file mode 100644 index c719ea00f..000000000 --- a/src/io_aggr_buf_mpio.h +++ /dev/null @@ -1,25 +0,0 @@ -/***************************************************************************** - * - * io_aggr_mpio.h - * - * Edinburgh Soft Matter and Statistical Physics Group and - * Edinburgh Parallel Computing Centre - * - * (c) 2022 The University of Edinburgh - * - * Kevin Stratford (kevin@epcc.ed.ac.uk) - * - *****************************************************************************/ - -#ifndef LUDWIG_IO_AGGR_MPIO_H -#define LUDWIG_IO_AGGR_MPIO_H - -#include "pe.h" -#include "coords.h" -#include "io_aggregator.h" - -int io_aggr_mpio_write(pe_t * pe, cs_t * cs, const char * filename, - const io_aggregator_t * buf); -int io_aggr_mpio_read(pe_t * pe, cs_t * cs, const char * filename, - io_aggregator_t * buf); -#endif From 9a22065f8b981047e17df19986c633124c826da9 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 18 Nov 2022 15:18:56 +0000 Subject: [PATCH 118/244] Helper functions --- src/util_json.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/util_json.h | 21 +++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/util_json.c create mode 100644 src/util_json.h diff --git a/src/util_json.c b/src/util_json.c new file mode 100644 index 000000000..a63b1abd2 --- /dev/null +++ b/src/util_json.c @@ -0,0 +1,45 @@ +/***************************************************************************** + * + * util_json.c + * + * A couple of useful additions to cJSON.c + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + + +#include +#include + +#include "util_json.h" + +/***************************************************************************** + * + * util_json_to_int_array + * + * The number of elements successfully identified is returned. + * + *****************************************************************************/ + +int util_json_to_int_array(const cJSON * const json, int * array, int sz) { + + int icount = 0; + + if (cJSON_IsArray(json) && cJSON_GetArraySize(json) == sz) { + cJSON * element = NULL; + cJSON_ArrayForEach(element, json) { + if (cJSON_IsNumber(element)) { + array[icount++] = cJSON_GetNumberValue(element); + } + } + } + + return icount; +} diff --git a/src/util_json.h b/src/util_json.h new file mode 100644 index 000000000..fa97392a9 --- /dev/null +++ b/src/util_json.h @@ -0,0 +1,21 @@ +/***************************************************************************** + * + * util_json.h + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#ifndef LUDWIG_UTIL_JSON_H +#define LUDWIG_UTIL_JSON_H + +#include "util_cJSON.h" + +int util_json_to_int_array(const cJSON * const json, int * array, int sz); + +#endif From a169c30fd92c118440acc280ab6842fca7f8f954 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 18 Nov 2022 15:20:05 +0000 Subject: [PATCH 119/244] Add io implementation abstraction --- src/io_impl.c | 37 +++++++ src/io_impl.h | 62 +++++++++++ src/io_impl_mpio.c | 255 +++++++++++++++++++++++++++++++++++++++++++++ src/io_impl_mpio.h | 45 ++++++++ src/io_subfile.c | 205 ++++++++++++++++++++++++++++++++++++ src/io_subfile.h | 47 +++++++++ 6 files changed, 651 insertions(+) create mode 100644 src/io_impl.c create mode 100644 src/io_impl.h create mode 100644 src/io_impl_mpio.c create mode 100644 src/io_impl_mpio.h create mode 100644 src/io_subfile.c create mode 100644 src/io_subfile.h diff --git a/src/io_impl.c b/src/io_impl.c new file mode 100644 index 000000000..85cb65df1 --- /dev/null +++ b/src/io_impl.c @@ -0,0 +1,37 @@ +/***************************************************************************** + * + * io_impl.c + * + * A factory method to choose a real implementation. + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include "io_impl.h" +#include "io_impl_mpio.h" + +int io_impl_create(const io_metadata_t * metadata, io_impl_t ** io) { + + *io = NULL; + + switch (metadata->options.mode) { + case IO_MODE_MPIIO: + { + io_impl_mpio_t * mpio = NULL; + io_impl_mpio_create(metadata, &mpio); + *io = (io_impl_t *) mpio; + } + break; + default: + ; + } + + return 0; +} diff --git a/src/io_impl.h b/src/io_impl.h new file mode 100644 index 000000000..7b36941d9 --- /dev/null +++ b/src/io_impl.h @@ -0,0 +1,62 @@ +/***************************************************************************** + * + * io_impl.h + * + * Abstraction of i/o implementations. + * + * An implementation is expected to support synchronous i/o, + * but may not support asynchronous i/o. In addtion, only + * asynchronous write is considered at the moment. + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#ifndef LUDWIG_IO_IMPL_H +#define LUDWIG_IO_IMPL_H + +#include "io_metadata.h" +#include "io_aggregator.h" + +typedef struct io_impl_vt_s io_impl_vt_t; +typedef struct io_impl_s io_impl_t; + +/* General */ + +typedef int (* io_impl_free_ft) (io_impl_t ** io); + +/* Synchronous implmentations */ + +typedef int (* io_impl_read_ft) (io_impl_t * io, const char * filename); +typedef int (* io_impl_write_ft) (io_impl_t * io, const char * filename); + +/* Asynchronous implementation may also supply, for writing ... */ + +typedef int (* io_impl_write_begin_ft) (io_impl_t * io, const char * filename); +typedef int (* io_impl_write_end_ft) (io_impl_t * io); + +struct io_impl_vt_s { + io_impl_free_ft free; /* Destructor */ + io_impl_read_ft read; /* Synchronous read */ + io_impl_write_ft write; /* Synchronous write */ + + io_impl_write_begin_ft write_begin; /* Asynchronous start */ + io_impl_write_end_ft write_end; /* Asynchronous end */ +}; + +struct io_impl_s { + const io_impl_vt_t * impl; /* Implementation */ + io_aggregator_t * aggr; /* Implementation has an aggregator */ +}; + +/* Factory method to instantiate a concrete object ... */ + +int io_impl_create(const io_metadata_t * metadata, io_impl_t ** io); + +#endif diff --git a/src/io_impl_mpio.c b/src/io_impl_mpio.c new file mode 100644 index 000000000..7ed895a1f --- /dev/null +++ b/src/io_impl_mpio.c @@ -0,0 +1,255 @@ +/***************************************************************************** + * + * io_impl_mpio.c + * + * Read/write aggregated data buffers using MPI/IO. + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include + +#include "io_impl_mpio.h" + +/* Function table */ +static io_impl_vt_t vt_ = { + (io_impl_free_ft) io_impl_mpio_free, + (io_impl_read_ft) io_impl_mpio_read, + (io_impl_write_ft) io_impl_mpio_write, + (io_impl_write_begin_ft) io_impl_mpio_write_begin, + (io_impl_write_end_ft) io_impl_mpio_write_end +}; + +static int io_impl_mpio_types_create(io_impl_mpio_t * io); + +/***************************************************************************** + * + * io_impl_mpio_create + * + *****************************************************************************/ + +int io_impl_mpio_create(const io_metadata_t * metadata, + io_impl_mpio_t ** io) { + + io_impl_mpio_t * mpio = NULL; + + mpio = (io_impl_mpio_t *) calloc(1, sizeof(io_impl_mpio_t)); + if (mpio == NULL) goto err; + + io_impl_mpio_initialise(metadata, mpio); + *io = mpio; + + return 0; + + err: + return -1; +} + +/***************************************************************************** + * + * io_impl_mpio_free + * + *****************************************************************************/ + +int io_impl_mpio_free(io_impl_mpio_t ** io) { + + assert(io); + assert(*io); + + io_impl_mpio_finalise(*io); + free(*io); + *io = NULL; + + return 0; +} + +/***************************************************************************** + * + * io_impl_mpio_initialise + * + *****************************************************************************/ + +int io_impl_mpio_initialise(const io_metadata_t * metadata, + io_impl_mpio_t * io) { + assert(metadata); + assert(io); + + *io = (io_impl_mpio_t) {0}; + + io->super.impl = &vt_; + io_aggregator_create(metadata->element, metadata->limits, &io->super.aggr); + /* ALLOW FAILURE? */ + + io->metadata = metadata; + + io->fh = MPI_FILE_NULL; + io_impl_mpio_types_create(io); + + return 0; +} + +/***************************************************************************** + * + * io_impl_mpio_finalise + * + *****************************************************************************/ + +int io_impl_mpio_finalise(io_impl_mpio_t * io) { + + assert(io); + + MPI_Type_free(&io->file); + MPI_Type_free(&io->array); + MPI_Type_free(&io->element); + io_aggregator_free(&io->super.aggr); + + *io = (io_impl_mpio_t) {0}; + + return 0; +} + +/***************************************************************************** + * + * io_impl_mpio_types_create + * + *****************************************************************************/ + +static int io_impl_mpio_types_create(io_impl_mpio_t * io) { + + const io_element_t * element = &io->metadata->element; + const io_subfile_t * subfile = &io->metadata->subfile; + + int nlocal[3] = {0}; + int offset[3] = {0}; + + cs_nlocal(io->metadata->cs, nlocal); + cs_nlocal_offset(io->metadata->cs, offset); + + MPI_Type_contiguous(element->count, element->datatype, &io->element); + MPI_Type_commit(&io->element); + + { + int zero3[3] = {0}; /* No halo */ + int starts[3] = {0}; /* Local offset in the file */ + starts[X] = offset[X] - subfile->offset[X]; + starts[Y] = offset[Y] - subfile->offset[Y]; + starts[Z] = offset[Z] - subfile->offset[Z]; + + /* Local array with no halo, and the file structure */ + MPI_Type_create_subarray(subfile->ndims, nlocal, nlocal, zero3, + MPI_ORDER_C, io->element, &io->array); + MPI_Type_create_subarray(subfile->ndims, subfile->sizes, nlocal, starts, + MPI_ORDER_C, io->element, &io->file); + } + + MPI_Type_commit(&io->array); + MPI_Type_commit(&io->file); + + return 0; +} + +/***************************************************************************** + * + * io_impl_mpio_write + * + * Synchronous MPI_File_write_all() per communicator. + * + *****************************************************************************/ + +int io_impl_mpio_write(io_impl_mpio_t * io, const char * filename) { + + assert(io); + assert(filename); + + { + MPI_Comm comm = io->metadata->comm; + MPI_Info info = MPI_INFO_NULL; /* PENDING from io_options_t */ + MPI_Offset disp = 0; + int count = 1; + + MPI_File_open(comm, filename, MPI_MODE_WRONLY + MPI_MODE_CREATE, info, + &io->fh); + MPI_File_set_view(io->fh, disp, io->element, io->file, "native", info); + MPI_File_write_all(io->fh, io->super.aggr->buf, count, io->array, + &io->status); + MPI_File_close(&io->fh); + } + + return 0; +} + +/***************************************************************************** + * + * io_impl_mpio_read + * + *****************************************************************************/ + +int io_impl_mpio_read(io_impl_mpio_t * io, const char * filename) { + + assert(io); + assert(filename); + + { + MPI_Comm comm = io->metadata->comm; + MPI_Info info = MPI_INFO_NULL; /* PENDING an io_option_t */ + MPI_Offset disp = 0; + int count = 1; + + MPI_File_open(comm, filename, MPI_MODE_RDONLY, info, &io->fh); + MPI_File_set_view(io->fh, disp, io->element, io->file, "native", info); + MPI_File_read_all(io->fh, io->super.aggr->buf, count, io->array, + &io->status); + MPI_File_close(&io->fh); + } + + return 0; +} + +/***************************************************************************** + * + * io_impl_mpio_write_begin + * + *****************************************************************************/ + +int io_impl_mpio_write_begin(io_impl_mpio_t * io, const char * filename) { + + assert(io); + assert(filename); + + { + MPI_Comm comm = io->metadata->comm; + MPI_Info info = MPI_INFO_NULL; /* PENDING from io_options_t */ + MPI_Offset disp = 0; + int count = 1; + + MPI_File_open(comm, filename, MPI_MODE_WRONLY + MPI_MODE_CREATE, info, + &io->fh); + MPI_File_set_view(io->fh, disp, io->element, io->file, "native", info); + MPI_File_write_all_begin(io->fh, io->super.aggr->buf, count, io->array); + } + + return 0; +} + + +/***************************************************************************** + * + * io_impl_mpio_write_end + * + *****************************************************************************/ + +int io_impl_mpio_write_end(io_impl_mpio_t * io) { + + assert(io); + + MPI_File_write_all_end(io->fh, io->super.aggr->buf, &io->status); + + return 0; +} diff --git a/src/io_impl_mpio.h b/src/io_impl_mpio.h new file mode 100644 index 000000000..1e44e2ef5 --- /dev/null +++ b/src/io_impl_mpio.h @@ -0,0 +1,45 @@ +/***************************************************************************** + * + * io_impl_mpio.h + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#ifndef LUDWIG_IO_IMPL_MPIO_H +#define LUDWIG_IO_IMPL_MPIO_H + +#include "io_impl.h" +#include "io_metadata.h" + +typedef struct io_impl_mpio_s io_impl_mpio_t; + +struct io_impl_mpio_s { + io_impl_t super; /* superclass block */ + const io_metadata_t * metadata; /* options, element type, ... */ + + /* MPIO implementation state ... */ + MPI_File fh; /* file handle */ + MPI_Status status; /* last status */ + MPI_Datatype element; /* element type */ + MPI_Datatype array; /* subarray type */ + MPI_Datatype file; /* file type */ +}; + +int io_impl_mpio_create(const io_metadata_t * meta, io_impl_mpio_t ** io); +int io_impl_mpio_free(io_impl_mpio_t ** io); + +int io_impl_mpio_initialise(const io_metadata_t * meta, io_impl_mpio_t * io); +int io_impl_mpio_finalise(io_impl_mpio_t * io); + +int io_impl_mpio_write(io_impl_mpio_t * io, const char * filename); +int io_impl_mpio_read(io_impl_mpio_t * io, const char * filename); +int io_impl_mpio_write_begin(io_impl_mpio_t * io, const char * filename); +int io_impl_mpio_write_end(io_impl_mpio_t * io); + +#endif diff --git a/src/io_subfile.c b/src/io_subfile.c new file mode 100644 index 000000000..aee384ece --- /dev/null +++ b/src/io_subfile.c @@ -0,0 +1,205 @@ +/***************************************************************************** + * + * io_subfile.c + * + * File decomposition description. + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include +#include + +#include "coords_s.h" +#include "io_subfile.h" + +/***************************************************************************** + * + * io_subfile_default + * + * Return decomposition with iogrid {1,1,1}, which cannot fail. + * + *****************************************************************************/ + +io_subfile_t io_subfile_default(cs_t * cs) { + + io_subfile_t subfile = {0}; + int iogrid[3] = {1, 1, 1}; + + io_subfile_create(cs, iogrid, &subfile); + + return subfile; +} + +/***************************************************************************** + * + * io_subfile_create + * + * Returns zero on success. Non-zero indicates no decompsoition is + * available. + * + *****************************************************************************/ + +int io_subfile_create(cs_t * cs, const int iogrid[3], io_subfile_t * subfile) { + + MPI_Comm parent = MPI_COMM_NULL; + + assert(cs); + assert(subfile); + + /* Check we can make a decomposition ... */ + + for (int i = 0; i < 3; i++) { + if (cs->param->mpi_cartsz[i] % iogrid[i] != 0) goto err; + } + + /* Note ndims is fixed at the moment ...*/ + + subfile->ndims = 3; + cs_cart_comm(cs, &parent); + + for (int i = 0; i < 3; i++) { + /* Some integer arithmetic to form blocks */ + int isz = cs->param->mpi_cartsz[i]; + int icoord = cs->param->mpi_cartcoords[i]; + int ioffset = icoord / (isz/iogrid[i]); + + subfile->iosize[i] = iogrid[i]; + subfile->coords[i] = iogrid[i]*icoord/isz; + subfile->offset[i] = cs->listnoffset[i][ioffset]; + + /* sizes must be accumulated allowing for non-uniform decomposition */ + subfile->sizes[i] = 0; + for (int j = ioffset; j < ioffset + (isz/iogrid[i]); j++) { + subfile->sizes[i] += cs->listnlocal[i][j]; + } + } + + /* Index/nfile */ + + subfile->nfile = iogrid[X]*iogrid[Y]*iogrid[Z]; + subfile->index = subfile->coords[X] + + subfile->coords[Y]*iogrid[X] + + subfile->coords[Z]*iogrid[X]*iogrid[Y]; + return 0; + + err: + return -1; +} + +/***************************************************************************** + * + * io_subfile_to_json + * + *****************************************************************************/ + +int io_subfile_to_json(const io_subfile_t * subfile, cJSON ** json) { + + int ifail = 0; + + if (json == NULL || *json != NULL) { + ifail = -1; + } + else { + + cJSON * myjson = cJSON_CreateObject(); + cJSON * iosize = cJSON_CreateIntArray(subfile->iosize, 3); + cJSON * coords = cJSON_CreateIntArray(subfile->coords, 3); + cJSON * offset = cJSON_CreateIntArray(subfile->offset, 3); + cJSON * sizes = cJSON_CreateIntArray(subfile->sizes, 3); + + cJSON_AddNumberToObject(myjson, "Number of files", subfile->nfile); + cJSON_AddNumberToObject(myjson, "File index", subfile->index); + cJSON_AddItemToObject(myjson, "Topology", iosize); + cJSON_AddItemToObject(myjson, "Coordinate", coords); + + cJSON_AddNumberToObject(myjson, "Data ndims", subfile->ndims); + cJSON_AddItemToObject(myjson, "File size (sites)", sizes); + cJSON_AddItemToObject(myjson, "File offset (sites)", offset); + + *json = myjson; + } + + return ifail; +} + +/***************************************************************************** + * + * io_subfile_from_json + * + * Return zero indicates success. + * + *****************************************************************************/ + +int io_subfile_from_json(const cJSON * json, io_subfile_t * subfile) { + + int ifail = 0; + + if (json == NULL || subfile == NULL) { + ifail = -1; + } + else { + cJSON * nfile = cJSON_GetObjectItemCaseSensitive(json, "Number of files"); + cJSON * index = cJSON_GetObjectItemCaseSensitive(json, "File index"); + cJSON * ndims = cJSON_GetObjectItemCaseSensitive(json, "Data ndims"); + cJSON * iosz = cJSON_GetObjectItemCaseSensitive(json, "Topology"); + cJSON * coord = cJSON_GetObjectItemCaseSensitive(json, "Coordinate"); + cJSON * sizes = cJSON_GetObjectItemCaseSensitive(json, "File size (sites)"); + cJSON * offst = cJSON_GetObjectItemCaseSensitive(json, "File offset (sites)"); + + if (nfile) subfile->nfile = cJSON_GetNumberValue(nfile); + if (index) subfile->index = cJSON_GetNumberValue(index); + if (ndims) subfile->ndims = cJSON_GetNumberValue(ndims); + + if (nfile == NULL) ifail += 1; + if (index == NULL) ifail += 2; + if (ndims == NULL) ifail += 4; + + if (3 != util_json_to_int_array(iosz, subfile->iosize, 3)) ifail += 8; + if (3 != util_json_to_int_array(coord, subfile->coords, 3)) ifail += 16; + if (3 != util_json_to_int_array(sizes, subfile->sizes, 3)) ifail += 32; + if (3 != util_json_to_int_array(offst, subfile->offset, 3)) ifail += 64; + } + + return ifail; +} + +/***************************************************************************** + * + * io_subfile_name + * + * Return a standardised data file name e.g., "stub-tttttttt.iii-nnn" + * being read as timestep t, part iii of nnn parts. + * + * Parts are counted in natural numbers 1, 2, 3... which is (1+index). + * + * bufsz is the maximum length of filename. + * + *****************************************************************************/ + +int io_subfile_name(const io_subfile_t * subfile, const char * stub, int it, + char * filename, size_t bufsz) { + + int ifail = -1; + + assert(subfile); + assert(stub); + assert(filename); + + assert(0 <= it && it < 1000*1000*1000); /* Format error */ + + if (bufsz > strlen(stub) + 8) { + sprintf(filename, "%s-%9.9d.%3.3d-%3.3d", stub, it, 1 + subfile->index, + subfile->nfile); + ifail = 0; + } + + return ifail; +} diff --git a/src/io_subfile.h b/src/io_subfile.h new file mode 100644 index 000000000..5c57676b3 --- /dev/null +++ b/src/io_subfile.h @@ -0,0 +1,47 @@ +/***************************************************************************** + * + * io_subfile.h + * + * Description of i/o decomposition of the whole system into Cartesian + * blocks with one block per file. + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#ifndef LUDWIG_IO_SUBFILE_H +#define LUDWIG_IO_SUBFILE_H + +#include "coords.h" +#include "util_json.h" + +typedef struct io_subfile_s io_subfile_t; + +struct io_subfile_s { + + /* Data shared between all ranks in the current block/file */ + + int nfile; /* Number of blocks/files */ + int index; /* Index {0 .. nfile-1} */ + int iosize[3]; /* 3-d (Cartesian) decomposition extents */ + int coords[3]; /* Carteisan position in decomposition */ + + int ndims; /* System dimensions (always 3 here) */ + int sizes[3]; /* Total size of block/file in lattice sites */ + int offset[3]; /* Offset of block/file in system (sites) */ +}; + +io_subfile_t io_subfile_default(cs_t * cs); +int io_subfile_create(cs_t * cs, const int iogrid[3], io_subfile_t * subfile); +int io_subfile_to_json(const io_subfile_t * subfile, cJSON ** json); +int io_subfile_from_json(const cJSON * json, io_subfile_t * subfile); +int io_subfile_name(const io_subfile_t * subfile, const char * stub, int it, + char * filename, size_t bufsz); + +#endif From 013bfab1aa33d3f4d753e632b6529108cb436f8f Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 18 Nov 2022 15:20:36 +0000 Subject: [PATCH 120/244] Add metadata container --- src/io_aggregator.c | 66 ++++++++++++++++++++++++++++---- src/io_aggregator.h | 8 +++- src/io_metadata.c | 91 +++++++++++++++++++++++++++++++++++++++++++++ src/io_metadata.h | 44 ++++++++++++++++++++++ 4 files changed, 200 insertions(+), 9 deletions(-) create mode 100644 src/io_metadata.c create mode 100644 src/io_metadata.h diff --git a/src/io_aggregator.c b/src/io_aggregator.c index 804030488..ab3c1ea94 100644 --- a/src/io_aggregator.c +++ b/src/io_aggregator.c @@ -22,10 +22,57 @@ * * io_aggregator_create * + * Allocate memory and initialise. + * + *****************************************************************************/ + +int io_aggregator_create(io_element_t el, cs_limits_t lim, + io_aggregator_t ** aggr) { + + io_aggregator_t * newaggr = NULL; + + assert(aggr); + + newaggr = (io_aggregator_t *) calloc(1, sizeof(io_aggregator_t)); + if (newaggr == NULL) goto err; + + if (0 != io_aggregator_initialise(el, lim, newaggr)) goto err; + + *aggr = newaggr; + + return 0; + + err: + if (newaggr) free(newaggr); + return -1; +} + +/***************************************************************************** + * + * io_aggregator_free + * + *****************************************************************************/ + +int io_aggregator_free(io_aggregator_t ** aggr) { + + assert(aggr); + + io_aggregator_finalise(*aggr); + free(*aggr); + + *aggr = NULL; + + return 0; +} + +/***************************************************************************** + * + * io_aggregator_initialise + * *****************************************************************************/ -int io_aggregator_create(io_element_t e, cs_limits_t lim, - io_aggregator_t * aggr) { +int io_aggregator_initialise(io_element_t e, cs_limits_t lim, + io_aggregator_t * aggr) { assert(aggr); @@ -36,21 +83,27 @@ int io_aggregator_create(io_element_t e, cs_limits_t lim, aggr->szbuf = aggr->szelement*cs_limits_size(lim); aggr->lim = lim; + if (aggr->szbuf == 0) goto err; + aggr->buf = (char *) malloc(aggr->szbuf*sizeof(char)); - assert(aggr->szbuf > 0); /* No zero size buffers */ - assert(aggr->buf); + if (aggr->buf == NULL) goto err; return 0; + + err: + + *aggr = (io_aggregator_t) {0}; + return -1; } /***************************************************************************** * - * io_aggregator_free + * io_aggregator_finalise * *****************************************************************************/ -int io_aggregator_free(io_aggregator_t * aggr) { +int io_aggregator_finalise(io_aggregator_t * aggr) { assert(aggr); assert(aggr->buf); @@ -61,4 +114,3 @@ int io_aggregator_free(io_aggregator_t * aggr) { return 0; } - diff --git a/src/io_aggregator.h b/src/io_aggregator.h index 3156dfd5f..83d6e6f6a 100644 --- a/src/io_aggregator.h +++ b/src/io_aggregator.h @@ -29,7 +29,11 @@ struct io_aggregator_s { }; int io_aggregator_create(io_element_t el, cs_limits_t lim, - io_aggregator_t * aggr); -int io_aggregator_free(io_aggregator_t * aggr); + io_aggregator_t ** aggr); +int io_aggregator_free(io_aggregator_t ** aggr); + +int io_aggregator_initialise(io_element_t el, cs_limits_t lim, + io_aggregator_t * aggr); +int io_aggregator_finalise(io_aggregator_t * aggr); #endif diff --git a/src/io_metadata.c b/src/io_metadata.c new file mode 100644 index 000000000..801bc4f3e --- /dev/null +++ b/src/io_metadata.c @@ -0,0 +1,91 @@ +/***************************************************************************** + * + * io_metadata.c + * + * Lattice quantity i/o metadata. + * + * Aggregate information on i/o, that is the run time options, the data + * element type, and the lattice structure, and provide a communicator. + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include +#include + +#include "io_metadata.h" + +/***************************************************************************** + * + * io_metadata_create + * + * Generate once only per run as the cost of the MPI_Comm_split() + * should not be repeated. + * + *****************************************************************************/ + +int io_metadata_create(cs_t * cs, + const io_options_t * options, + const io_element_t * element, + io_metadata_t * meta) { + assert(cs); + assert(options); + assert(element); + assert(meta); + + *meta = (io_metadata_t) {0}; + + meta->cs = cs; + cs_cart_comm(cs, &meta->parent); + + { + /* Store the limits as a convenience */ + int nlocal[3] = {0}; + cs_nlocal(cs, nlocal); + meta->limits.imin = 1; meta->limits.imax = nlocal[X]; + meta->limits.jmin = 1; meta->limits.jmax = nlocal[Y]; + meta->limits.kmin = 1; meta->limits.kmax = nlocal[Z]; + } + + meta->options = *options; + meta->element = *element; + + { + /* Must have a decomposition... */ + int ifail = io_subfile_create(cs, options->iogrid, &meta->subfile); + if (ifail != 0) return -1; + } + + /* Split communicator for the file. */ + /* One could revisit assigning the rank ... */ + { + int rank = -1; + MPI_Comm_rank(meta->parent, &rank); + MPI_Comm_split(meta->parent, meta->subfile.index, rank, &meta->comm); + } + + return 0; +} + +/***************************************************************************** + * + * io_metadata_free + * + *****************************************************************************/ + +int io_metadata_free(io_metadata_t * meta) { + + assert(meta); + + MPI_Comm_free(&meta->comm); + *meta = (io_metadata_t) {0}; + + return 0; +} diff --git a/src/io_metadata.h b/src/io_metadata.h new file mode 100644 index 000000000..43e96df98 --- /dev/null +++ b/src/io_metadata.h @@ -0,0 +1,44 @@ +/***************************************************************************** + * + * io_metadata.h + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#ifndef LUDWIG_IO_METADATA_H +#define LUDWIG_IO_METADATA_H + +#include "coords.h" +#include "cs_limits.h" +#include "io_options.h" +#include "io_element.h" +#include "io_subfile.h" + +typedef struct io_metadata_s io_metadata_t; + +struct io_metadata_s { + + cs_t * cs; /* Keep a reference to coordinates */ + cs_limits_t limits; /* Always local size with no halo */ + MPI_Comm parent; /* Cartesian communicator */ + MPI_Comm comm; /* Cartesian sub-communicator */ + + io_options_t options; + io_element_t element; + io_subfile_t subfile; + +}; + +int io_metadata_create(cs_t * cs, + const io_options_t * options, + const io_element_t * element, + io_metadata_t * metadata); +int io_metadata_free(io_metadata_t * metadata); + +#endif From b078dc45c8add77ad9e2cfc3f628f08c55736696 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 18 Nov 2022 15:21:48 +0000 Subject: [PATCH 121/244] Add mpio options --- src/io_info_args_rt.c | 7 ++++ src/io_options.c | 96 +++++++++++++++++++++++++++++++++++++++++-- src/io_options.h | 19 +++++++-- src/io_options_rt.c | 19 ++++----- 4 files changed, 124 insertions(+), 17 deletions(-) diff --git a/src/io_info_args_rt.c b/src/io_info_args_rt.c index ea585a337..f601718fb 100644 --- a/src/io_info_args_rt.c +++ b/src/io_info_args_rt.c @@ -57,6 +57,13 @@ __host__ int io_info_args_rt(rt_t * rt, rt_enum_t lv, const char * stub, io_info_args_rt_iogrid(rt, lv, key, args->grid); } + args->input.iogrid[0] = args->grid[0]; + args->input.iogrid[1] = args->grid[1]; + args->input.iogrid[2] = args->grid[2]; + args->output.iogrid[0] = args->grid[0]; + args->output.iogrid[1] = args->grid[1]; + args->output.iogrid[2] = args->grid[2]; + return 0; } diff --git a/src/io_options.c b/src/io_options.c index cf7e66cf1..dd07daea7 100644 --- a/src/io_options.c +++ b/src/io_options.c @@ -8,7 +8,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2020 The University of Edinburgh + * (c) 2020-2022 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -23,9 +23,17 @@ #define IO_MODE_DEFAULT() IO_MODE_SINGLE #define IO_RECORD_FORMAT_DEFAULT() IO_RECORD_BINARY #define IO_METADATA_VERSION_DEFAULT() IO_METADATA_SINGLE_V1 +#define IO_REPORT_DEFAULT() 0 +#define IO_ASYNCHRONOUS_DEFAULT() 0 +#define IO_COMPRESSION_LEVL_DEFAULT() 0 +#define IO_GRID_DEFAULT() {1, 1, 1} #define IO_OPTIONS_DEFAULT() {IO_MODE_DEFAULT(), \ IO_RECORD_FORMAT_DEFAULT(), \ - IO_METADATA_VERSION_DEFAULT(), 0, 0} + IO_METADATA_VERSION_DEFAULT(),\ + IO_REPORT_DEFAULT(), \ + IO_ASYNCHRONOUS_DEFAULT(), \ + IO_COMPRESSION_LEVL_DEFAULT(), \ + IO_GRID_DEFAULT()} /***************************************************************************** * @@ -75,7 +83,6 @@ __host__ io_options_t io_options_default(void) { } - /***************************************************************************** * * io_options_valid @@ -109,6 +116,7 @@ __host__ int io_options_mode_valid(io_mode_enum_t mode) { valid += (mode == IO_MODE_SINGLE); valid += (mode == IO_MODE_MULTIPLE); + valid += (mode == IO_MODE_MPIIO); return valid; } @@ -158,9 +166,91 @@ __host__ int io_options_metadata_version_valid(const io_options_t * options) { valid = (options->mode == IO_MODE_MULTIPLE); break; + case IO_METADATA_V2: + valid = (options->mode == IO_MODE_MPIIO); + break; + default: ; } return valid; } + +/***************************************************************************** + * + * io_options_with_mode + * + * Return a default for the given mode + * + *****************************************************************************/ + +__host__ io_options_t io_options_with_mode(io_mode_enum_t mode) { + + io_options_t options = io_options_default(); + + switch (mode) { + case IO_MODE_SINGLE: + options.mode = IO_MODE_SINGLE; + options.iorformat = IO_RECORD_BINARY; + options.metadata_version = IO_METADATA_SINGLE_V1; + /* otherwise defaults */ + break; + case IO_MODE_MULTIPLE: + options.mode = IO_MODE_MULTIPLE; + options.iorformat = IO_RECORD_BINARY; + options.metadata_version = IO_METADATA_MULTI_V1; + /* otherwise defaults */ + break; + case IO_MODE_MPIIO: + options.mode = IO_MODE_MPIIO; + options.iorformat = IO_RECORD_BINARY; + options.metadata_version = IO_METADATA_V2; + options.report = 1; + options.asynchronous = 0; + options.compression_levl = 0; + break; + default: + /* User error ... */ + options.mode = IO_MODE_INVALID; + } + + return options; +} + +/***************************************************************************** + * + * io_options_with_format + * + * Actually with mode and format ... + * + *****************************************************************************/ + +__host__ io_options_t io_options_with_format(io_mode_enum_t mode, + io_record_format_enum_t iorf) { + + io_options_t options = io_options_with_mode(mode); + + options.iorformat = iorf; + + return options; +} + +/***************************************************************************** + * + * io_options_with_iogrid + * + *****************************************************************************/ + +__host__ io_options_t io_options_with_iogrid(io_mode_enum_t mode, + io_record_format_enum_t iorf, + int iogrid[3]) { + + io_options_t options = io_options_with_format(mode, iorf); + + options.iogrid[0] = iogrid[0]; + options.iogrid[1] = iogrid[1]; + options.iogrid[2] = iogrid[2]; + + return options; +} diff --git a/src/io_options.h b/src/io_options.h index 3b4d860d9..d841e8a62 100644 --- a/src/io_options.h +++ b/src/io_options.h @@ -27,9 +27,13 @@ * data written as if in serial. * IO_MODE_MULTIPLE: one or more files with decomposition dependent order; * output must be post-processed to recover serial order. + * + * IO_MODE_ANSI ANSI implementation PENDING + * IO_MODE_MPIO MPIO-IO implementation */ -enum io_mode_enum {IO_MODE_INVALID, IO_MODE_SINGLE, IO_MODE_MULTIPLE}; +enum io_mode_enum {IO_MODE_INVALID, IO_MODE_SINGLE, IO_MODE_MULTIPLE, + IO_MODE_MPIIO}; /* Record formats: */ @@ -41,7 +45,8 @@ enum io_record_format_enum {IO_RECORD_INVALID, enum io_metadata_version_enum {IO_METADATA_INVALID, IO_METADATA_SINGLE_V1, - IO_METADATA_MULTI_V1}; + IO_METADATA_MULTI_V1, + IO_METADATA_V2}; /* Options container type */ @@ -54,7 +59,9 @@ struct io_options_s { io_record_format_enum_t iorformat; /* Record format ascii/binary */ io_metadata_version_enum_t metadata_version; /* Metadata version no. */ int report; /* Switch reporting on/off */ - int asynchronous; /* Not implemented */ + int asynchronous; /* Asynchronous i/o */ + int compression_levl; /* Compression 0-9 */ + int iogrid[3]; /* i/o decomposition */ }; typedef struct io_options_s io_options_t; @@ -63,6 +70,12 @@ __host__ io_mode_enum_t io_mode_default(void); __host__ io_record_format_enum_t io_record_format_default(void); __host__ io_metadata_version_enum_t io_metadata_version_default(void); __host__ io_options_t io_options_default(void); +__host__ io_options_t io_options_with_mode(io_mode_enum_t mode); +__host__ io_options_t io_options_with_format(io_mode_enum_t mode, + io_record_format_enum_t iorf); +__host__ io_options_t io_options_with_iogrid(io_mode_enum_t mode, + io_record_format_enum_t iorf, + int iogrid[3]); __host__ int io_options_valid(const io_options_t * options); __host__ int io_options_mode_valid(io_mode_enum_t mode); diff --git a/src/io_options_rt.c b/src/io_options_rt.c index 68c833339..fce16b3f2 100644 --- a/src/io_options_rt.c +++ b/src/io_options_rt.c @@ -46,21 +46,17 @@ __host__ int io_options_rt(rt_t * rt, rt_enum_t lv, const char * keystub, sprintf(key, "%s_io_mode", keystub); { - int ierr = io_options_rt_mode(rt, lv, key, &options->mode); + io_mode_enum_t mode = IO_MODE_INVALID; + int ierr = io_options_rt_mode(rt, lv, key, &mode); - /* Force metadata to be consistent with the mode */ + /* For a valid mode, the default options are selected. */ - if (ierr == RT_KEY_OK && options->mode == IO_MODE_SINGLE) { - /* only one choice at the moment */ - options->metadata_version = IO_METADATA_SINGLE_V1; - } - - if (ierr == RT_KEY_OK && options->mode == IO_MODE_MULTIPLE) { - /* only one choice again */ - options->metadata_version = IO_METADATA_MULTI_V1; + if (ierr == RT_KEY_OK) { + *options = io_options_with_mode(mode); } } + /* Allow default format and report to be updated */ sprintf(key, "%s_io_format", keystub); io_options_rt_record_format(rt, lv, key, &options->iorformat); @@ -98,6 +94,7 @@ __host__ int io_options_rt_mode(rt_t * rt, rt_enum_t lv, const char * key, if (strcmp(value, "single") == 0) user_mode = IO_MODE_SINGLE; if (strcmp(value, "multiple") == 0) user_mode = IO_MODE_MULTIPLE; + if (strcmp(value, "mpiio") == 0) user_mode = IO_MODE_MPIIO; if (io_options_mode_valid(user_mode) == 1) { *mode = user_mode; @@ -107,7 +104,7 @@ __host__ int io_options_rt_mode(rt_t * rt, rt_enum_t lv, const char * key, rt_vinfo(rt, lv, "I/O mode key present but value not recognised\n"); rt_vinfo(rt, lv, "key: %s\n", key); rt_vinfo(rt, lv, "value: %s\n", value); - rt_vinfo(rt, lv, "Should be either 'single' or 'multiple'\n"); + rt_vinfo(rt, lv, "Should be either 'single', 'multiple' or 'mpiio'\n"); rt_fatal(rt, lv, "Please check the input file and try again!\n"); ifail = RT_KEY_INVALID; } From a81fe8f625f3b3f9b5799de2160e2f3ab0250679 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 18 Nov 2022 15:34:27 +0000 Subject: [PATCH 122/244] Add MPI_Type_size and others --- mpi_s/mpi.h | 4 ++++ mpi_s/mpi_serial.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/mpi_s/mpi.h b/mpi_s/mpi.h index 2972a54b9..55c3d3ff8 100644 --- a/mpi_s/mpi.h +++ b/mpi_s/mpi.h @@ -262,6 +262,7 @@ int MPI_Type_create_struct(int count, int * arry_of_blocklens, int MPI_Type_create_resized(MPI_Datatype oldtype, MPI_Aint ub, MPI_Aint extent, MPI_Datatype * newtype); int MPI_Type_get_extent(MPI_Datatype handle, MPI_Aint * lb, MPI_Aint *extent); +int MPI_Type_size(MPI_Datatype handle, int * sz); /* MPI IO related */ @@ -284,6 +285,9 @@ int MPI_File_read_all(MPI_File fh, void * buf, int count, MPI_Datatype datatype, MPI_Status * status); int MPI_File_write_all(MPI_File fh, const void * buf, int count, MPI_Datatype datatype, MPI_Status * status); +int MPI_File_write_all_begin(MPI_File fh, const void * buf, int count, + MPI_Datatype datatype); +int MPI_File_write_all_end(MPI_File fh, const void * buf, MPI_Status * status); #ifdef __cplusplus } diff --git a/mpi_s/mpi_serial.c b/mpi_s/mpi_serial.c index 5d4debbf3..1ecafd888 100644 --- a/mpi_s/mpi_serial.c +++ b/mpi_s/mpi_serial.c @@ -1308,6 +1308,21 @@ int MPI_Type_get_extent(MPI_Datatype datatype, MPI_Aint * lb, return MPI_SUCCESS; } +/***************************************************************************** + * + * MPI_Type_size + * + *****************************************************************************/ + +int MPI_Type_size(MPI_Datatype datatype, int * sz) { + + assert(sz); + + *sz = mpi_sizeof(datatype); + + return 0; +} + /***************************************************************************** * * MPI_File_open @@ -1603,6 +1618,38 @@ int MPI_File_write_all(MPI_File fh, const void * buf, int count, return MPI_SUCCESS; } +/***************************************************************************** + * + * MPI_File_write_all_begin + * + *****************************************************************************/ + +int MPI_File_write_all_begin(MPI_File fh, const void * buf, int count, + MPI_Datatype datatype) { + + /* We are going to do it here and throw away the status */ + + MPI_Status status = {0}; + + MPI_File_write_all(fh, buf, count, datatype, &status); + + return 0; +} + +/***************************************************************************** + * + * MPI_File_write_all_end + * + *****************************************************************************/ + +int MPI_File_write_all_end(MPI_File fh, const void * buf, MPI_Status * status) { + + /* A real implementation returns the number of bytes written in the + * status object. */ + + return 0; +} + #endif /* _DO_NOT_INCLUDE_MPI2_INTERFACE */ /***************************************************************************** From d087341c59cce2cbfaff200e33f5094f7ceb7fb4 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 18 Nov 2022 15:35:05 +0000 Subject: [PATCH 123/244] Removed --- tests/unit/test_io_aggr_mpio.c | 367 --------------------------------- tests/unit/test_io_cart_sub.c | 218 -------------------- 2 files changed, 585 deletions(-) delete mode 100644 tests/unit/test_io_aggr_mpio.c delete mode 100644 tests/unit/test_io_cart_sub.c diff --git a/tests/unit/test_io_aggr_mpio.c b/tests/unit/test_io_aggr_mpio.c deleted file mode 100644 index ff4b594c4..000000000 --- a/tests/unit/test_io_aggr_mpio.c +++ /dev/null @@ -1,367 +0,0 @@ -/***************************************************************************** - * - * test_io_aggr_mpio.c - * - * The MPI / IO aggregtor (with a mock aggregator). - * - * - * Edinburgh Soft Matter and Statistical Physics Group and - * Edinburgh Parallel Computing Centre - * - * (c) 2022 The University of Edinburgh - * - * Contributing authors: - * Kevin Stratford (kevin@epcc.ed.ac.uk) - * - *****************************************************************************/ - -#include -#include -#include - -#include "pe.h" -#include "coords.h" -#include "io_aggr_buf_mpio.h" - -int test_io_aggr_mpio_write(pe_t * pe, cs_t * cs, io_element_t el, - const char * filename); -int test_io_aggr_mpio_read(pe_t * pe, cs_t * cs, io_element_t el, - const char * filename); - -int test_io_aggr_buf_pack_asc(cs_t * cs, io_aggregator_t * buf); -int test_io_aggr_buf_pack_bin(cs_t * cs, io_aggregator_t * buf); -int test_io_aggr_buf_unpack_asc(cs_t * cs, const io_aggregator_t * buf); -int test_io_aggr_buf_unpack_bin(cs_t * cs, const io_aggregator_t * buf); - -/***************************************************************************** - * - * test_io_aggr_mpio_suite - * - *****************************************************************************/ - -int test_io_aggr_mpio_suite(void) { - - pe_t * pe = NULL; - cs_t * cs = NULL; - - pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); - - cs_create(pe, &cs); - - { - /* I want a smallish size, but allow for a meaningful decomposition */ - int ntotal[3] = {16, 8, 4}; - cs_ntotal_set(cs, ntotal); - } - - cs_init(cs); - - /* ASCII: write then read */ - { - io_element_t element = {.datatype = MPI_CHAR, - .datasize = sizeof(char), - .count = 28, - .endian = io_endianness()}; - const char * filename = "io-aggr-mpio-asc.dat"; - - test_io_aggr_mpio_write(pe, cs, element, filename); - test_io_aggr_mpio_read(pe, cs, element, filename); - - MPI_Barrier(MPI_COMM_WORLD); - if (pe_mpi_rank(pe) == 0) remove(filename); - } - - /* Binary: write thne read */ - { - io_element_t element = {.datatype = MPI_INT64_T, - .datasize = sizeof(int64_t), - .count = 1, - .endian = io_endianness()}; - const char * filename = "io-aggr-mpio-bin.dat"; - - test_io_aggr_mpio_write(pe, cs, element, filename); - test_io_aggr_mpio_read(pe, cs, element, filename); - - MPI_Barrier(MPI_COMM_WORLD); - if (pe_mpi_rank(pe) == 0) remove(filename); - } - - pe_info(pe, "PASS ./unit/test_io_aggr_mpio\n"); - cs_free(cs); - pe_free(pe); - - return 0; -} - -/***************************************************************************** - * - * test_io_aggr_mpio_write - * - *****************************************************************************/ - -int test_io_aggr_mpio_write(pe_t * pe, cs_t * cs, io_element_t element, - const char * filename) { - - assert(pe); - assert(cs); - assert(filename); - - int nlocal[3] = {0}; - cs_nlocal(cs, nlocal); - - /* Aggregator buffer */ - - { - cs_limits_t lim = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; - io_aggregator_t buf = {0}; - - io_aggregator_create(element, lim, &buf); - - if (element.datatype == MPI_CHAR) test_io_aggr_buf_pack_asc(cs, &buf); - if (element.datatype == MPI_INT64_T) test_io_aggr_buf_pack_bin(cs, &buf); - - io_aggr_mpio_write(pe, cs, filename, &buf); - - io_aggregator_free(&buf); - } - - return 0; -} - -/***************************************************************************** - * - * test_io_aggr_mpio_read - * - *****************************************************************************/ - -int test_io_aggr_mpio_read(pe_t * pe, cs_t * cs, io_element_t element, - const char * filename) { - - assert(pe); - assert(cs); - assert(filename); - - int nlocal[3] = {0}; - cs_nlocal(cs, nlocal); - - { - /* Read and check */ - cs_limits_t lim = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; - io_aggregator_t buf = {0}; - - io_aggregator_create(element, lim ,&buf); - - io_aggr_mpio_read(pe, cs, filename, &buf); - - if (element.datatype == MPI_CHAR) test_io_aggr_buf_unpack_asc(cs, &buf); - if (element.datatype == MPI_INT64_T) test_io_aggr_buf_unpack_bin(cs, &buf); - - io_aggregator_free(&buf); - } - - return 0; -} - -/***************************************************************************** - * - * test_unique_value - * - *****************************************************************************/ - -int64_t test_unique_value(cs_t * cs, int ic, int jc, int kc) { - - int64_t ival = -1; - int ntotal[3] = {0}; - int nlocal[3] = {0}; - int offset[3] = {0}; - - cs_ntotal(cs, ntotal); - cs_nlocal(cs, nlocal); - cs_nlocal_offset(cs, offset); - - { - int ix = offset[X] + ic - 1; - int iy = offset[Y] + jc - 1; - int iz = offset[Z] + kc - 1; - - ival = ntotal[Z]*ntotal[Y]*ix + ntotal[Z]*iy + iz; - } - - return ival; -} - -/***************************************************************************** - * - * test_io_aggr_buf_pack_asc - * - *****************************************************************************/ - -int test_io_aggr_buf_pack_asc(cs_t * cs, io_aggregator_t * buf) { - - int ifail = 0; - - int ib = 0; - int ntotal[3] = {0}; - int nlocal[3] = {0}; - int offset[3] = {0}; - - assert(cs); - assert(buf); - - cs_ntotal(cs, ntotal); - cs_nlocal(cs, nlocal); - cs_nlocal_offset(cs, offset); - - for (int ic = 1; ic <= nlocal[X]; ic++) { - for (int jc = 1; jc <= nlocal[Y]; jc++) { - for (int kc = 1; kc <= nlocal[Z]; kc++) { - int64_t ival = test_unique_value(cs, ic, jc, kc); - { - /* Add 1 <= label <= ntotal for each dimension */ - int ix = offset[X] + ic; - int iy = offset[Y] + jc; - int iz = offset[Z] + kc; - char cline[BUFSIZ] = {0}; - size_t nc = 0; /* int returned but need to compare to size_t */ - nc = sprintf(cline, "%4d %4d %4d %12" PRId64 "\n", ix, iy, iz, ival); - assert(nc == buf->szelement); - if (nc != buf->szelement) ifail += 1; - memcpy(buf->buf + ib*buf->szelement, cline, buf->szelement); - } - ib += 1; - } - } - } - - assert(ib == nlocal[X]*nlocal[Y]*nlocal[Z]); - - return ifail; -} - -/***************************************************************************** - * - * test_io_aggr_buf_unpack_asc - * - *****************************************************************************/ - -int test_io_aggr_buf_unpack_asc(cs_t * cs, const io_aggregator_t * buf) { - - int ifail = 0; - - int ib = 0; - int ntotal[3] = {0}; - int nlocal[3] = {0}; - int offset[3] = {0}; - - assert(cs); - assert(buf->buf); - - cs_ntotal(cs, ntotal); - cs_nlocal(cs, nlocal); - cs_nlocal_offset(cs, offset); - - for (int ic = 1; ic <= nlocal[X]; ic++) { - for (int jc = 1; jc <= nlocal[Y]; jc++) { - for (int kc = 1; kc <= nlocal[Z]; kc++) { - int64_t ival = test_unique_value(cs, ic, jc, kc); - { - /* Add 1 <= label <= ntotal for each dimension */ - int ix = offset[X] + ic; - int iy = offset[Y] + jc; - int iz = offset[Z] + kc; - int ixread = -1; - int iyread = -1; - int izread = -1; - int64_t ivalread = -1; - /* Note int64_t requires portable format */ - int nc = sscanf(buf->buf + ib*buf->szelement, "%4d %4d %4d %" SCNd64, - &ixread, &iyread, &izread, &ivalread); - - assert(nc == 4); - assert(ixread == ix); - assert(iyread == iy); - assert(izread == iz); - assert(ivalread == ival); - if (nc != 4) ifail += 1; - if (iz != izread) ifail += 1; - if (iy != iyread) ifail += 1; - if (ix != ixread) ifail =+ 1; - if (ivalread != ival) ifail += 1; - } - ib += 1; - } - } - } - - assert(ib == nlocal[X]*nlocal[Y]*nlocal[Z]); - - return ifail; -} - - -/***************************************************************************** - * - * test_io_aggr_buf_pack_bin - * - *****************************************************************************/ - -int test_io_aggr_buf_pack_bin(cs_t * cs, io_aggregator_t * buf) { - - assert(cs); - assert(buf->buf); - - int ib = 0; - int nlocal[3] = {0}; - - cs_nlocal(cs, nlocal); - - for (int ic = 1; ic <= nlocal[X]; ic++) { - for (int jc = 1; jc <= nlocal[Y]; jc++) { - for (int kc = 1; kc <= nlocal[Z]; kc++) { - int64_t ival = test_unique_value(cs, ic, jc, kc); - memcpy(buf->buf + ib*sizeof(int64_t), &ival, sizeof(int64_t)); - ib += 1; - } - } - } - - assert(ib == nlocal[X]*nlocal[Y]*nlocal[Z]); - - return 0; -} - -/***************************************************************************** - * - * test_io_aggr_buf_unpack_bin - * - *****************************************************************************/ - -int test_io_aggr_buf_unpack_bin(cs_t * cs, const io_aggregator_t * buf) { - - int ifail = 0; - - int ib = 0; - int nlocal[3] = {0}; - - assert(cs); - assert(buf->buf); - - cs_nlocal(cs, nlocal); - - for (int ic = 1; ic <= nlocal[X]; ic++) { - for (int jc = 1; jc <= nlocal[Y]; jc++) { - for (int kc = 1; kc <= nlocal[Z]; kc++) { - int64_t ival = test_unique_value(cs, ic, jc, kc); - int64_t iread = -1; - memcpy(&iread, buf->buf + ib*sizeof(int64_t), sizeof(int64_t)); - assert(ival == iread); - if (ival != iread) ifail += 1; - ib += 1; - } - } - } - - assert(ib == nlocal[X]*nlocal[Y]*nlocal[Z]); - - return ifail; -} diff --git a/tests/unit/test_io_cart_sub.c b/tests/unit/test_io_cart_sub.c deleted file mode 100644 index f68b58c25..000000000 --- a/tests/unit/test_io_cart_sub.c +++ /dev/null @@ -1,218 +0,0 @@ -/***************************************************************************** - * - * test_io_cart_sub.c - * - * This is one place where it would be useful to test MPI decompositions - * of significant extent in three dimensions. - * - * - * Edinburgh Soft Matter and Statistical Physics Group and - * Edinburgh Parallel Computing Centre - * - * (c) 2022 The University of Edinburgh - * - * Kevin Stratford(kevin@epcc.ed.ac.uk) - * - *****************************************************************************/ - -#include -#include - -#include "pe.h" -#include "io_cart_sub.h" - -int test_io_cart_sub_create(pe_t * pe); -int test_io_cart_sub_util_pass(pe_t * pe, int ntotal[3], int iogrid[3]); - -/***************************************************************************** - * - * test_io_cart_sub_suite - * - *****************************************************************************/ - -int test_io_cart_sub_suite(void) { - - int sz = -1; - pe_t * pe = NULL; - - MPI_Comm_size(MPI_COMM_WORLD, &sz); - pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); - - /* "Serial": only one case */ - - test_io_cart_sub_create(pe); - - /* Small: MPI_Comm_size() <= 8 */ - - if (sz >= 2) { - { - int ntotal[3] = {8, 8, 8}; - int iogrid[3] = {2, 1, 1}; - test_io_cart_sub_util_pass(pe, ntotal, iogrid); - } - } - - if (sz >= 4) { - /* We expect MPI_Dims_create() to be the decomposition in cs_t */ - { - int ntotal[3] = {8, 8, 8}; - int iogrid[3] = {2, 2, 1}; - test_io_cart_sub_util_pass(pe, ntotal, iogrid); - } - } - if (sz >= 8) { - { - int ntotal[3] = {8, 8, 8}; - int iogrid[3] = {2, 2, 2}; - test_io_cart_sub_util_pass(pe, ntotal, iogrid); - } - } - - pe_info(pe, "%-9s %s\n", "PASS", __FILE__); - pe_free(pe); - - return 0; -} - -/***************************************************************************** - * - * test_io_cart_sub_create - * - * The simple case of one file: iogrid = {1,1,1} - * - *****************************************************************************/ - -int test_io_cart_sub_create(pe_t * pe) { - - cs_t * cs = NULL; - - assert(pe); - - /* Default system */ - cs_create(pe, &cs); - cs_init(cs); - - { - int iogrid[3] = {1, 1, 1}; - io_cart_sub_t iosub = {0}; - - io_cart_sub_create(cs, iogrid, &iosub); - - { - MPI_Comm comm = MPI_COMM_NULL; - int myresult = MPI_UNEQUAL; - - cs_cart_comm(cs, &comm); - MPI_Comm_compare(comm, iosub.parent, &myresult); - assert(myresult == MPI_IDENT); - - MPI_Comm_compare(comm, iosub.comm, &myresult); - assert(myresult == MPI_CONGRUENT); - } - - assert(iosub.size[X] == iogrid[X]); - assert(iosub.size[Y] == iogrid[Y]); - assert(iosub.size[Z] == iogrid[Z]); - assert(iosub.coords[X] == 0); - assert(iosub.coords[Y] == 0); - assert(iosub.coords[Z] == 0); - - { - int ntotal[3] = {0}; - cs_ntotal(cs, ntotal); - assert(iosub.ntotal[X] == ntotal[X]); - assert(iosub.ntotal[Y] == ntotal[Y]); - assert(iosub.ntotal[Z] == ntotal[Z]); - assert(iosub.nlocal[X] == ntotal[X]); /* All one file */ - assert(iosub.nlocal[Y] == ntotal[Y]); - assert(iosub.nlocal[Z] == ntotal[Z]); - assert(iosub.offset[X] == 0); - assert(iosub.offset[Y] == 0); - assert(iosub.offset[Z] == 0); - } - - assert(iosub.nfile == 1); - assert(iosub.index == 0); - - io_cart_sub_free(&iosub); - assert(iosub.comm == MPI_COMM_NULL); - } - - { - /* Here's one that must fail. */ - int iogrid[3] = {INT_MAX, 1, 1}; - io_cart_sub_t iosub = {0}; - - assert(io_cart_sub_create(cs, iogrid, &iosub) != 0); - - } - cs_free(cs); - - return 0; -} - -/***************************************************************************** - * - * test_io_cart_sub_util_pass - * - *****************************************************************************/ - -int test_io_cart_sub_util_pass(pe_t * pe, int ntotal[3], int iogrid[3]) { - - cs_t * cs = NULL; - - assert(pe); - - cs_create(pe, &cs); - cs_ntotal_set(cs, ntotal); - cs_init(cs); - - { - int ifail = 0; - io_cart_sub_t iosub = {0}; - - ifail = io_cart_sub_create(cs, iogrid, &iosub); - assert(ifail == 0); - - { - /* Communicators must be different */ - MPI_Comm comm = MPI_COMM_NULL; - int myresult = MPI_UNEQUAL; - - cs_cart_comm(cs, &comm); - MPI_Comm_compare(comm, iosub.parent, &myresult); - assert(myresult == MPI_IDENT); - - MPI_Comm_compare(comm, iosub.comm, &myresult); - assert(myresult == MPI_UNEQUAL); - } - - /* size == iogrid */ - assert(iosub.size[X] == iogrid[X]); - assert(iosub.size[Y] == iogrid[Y]); - assert(iosub.size[Z] == iogrid[Z]); - - /* One should be able to reconstruct the coords from - * the index and the offset */ - - /* ntotal[] is fixed */ - assert(iosub.ntotal[X] == ntotal[X]); - assert(iosub.ntotal[Y] == ntotal[Y]); - assert(iosub.ntotal[Z] == ntotal[Z]); - /* PENDING NLOCAL */ - /* PENDING OFFSET */ - - { - /* Don't care about exactly what the index is ... */ - int nfile = iogrid[X]*iogrid[Y]*iogrid[Z]; - assert(iosub.nfile == nfile); - assert(0 <= iosub.index && iosub.index < nfile); - } - - io_cart_sub_free(&iosub); - } - - cs_free(cs); - - return 0; -} From 62fd0aa7d358ce4efc8ba87f64bcc5df1bf529da Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 18 Nov 2022 15:35:30 +0000 Subject: [PATCH 124/244] Updated tests for io metadata and implmentation --- tests/unit/test_field.c | 4 +- tests/unit/test_io_aggregator.c | 56 +++- tests/unit/test_io_element.c | 26 +- tests/unit/test_io_impl_mpio.c | 552 ++++++++++++++++++++++++++++++++ tests/unit/test_io_info_args.c | 2 +- tests/unit/test_io_metadata.c | 100 ++++++ tests/unit/test_io_options.c | 137 +++++++- tests/unit/test_io_options_rt.c | 48 ++- tests/unit/test_io_subfile.c | 378 ++++++++++++++++++++++ tests/unit/test_util_io.c | 8 +- tests/unit/tests.c | 4 +- tests/unit/tests.h | 4 +- 12 files changed, 1287 insertions(+), 32 deletions(-) create mode 100644 tests/unit/test_io_impl_mpio.c create mode 100644 tests/unit/test_io_metadata.c create mode 100644 tests/unit/test_io_subfile.c diff --git a/tests/unit/test_field.c b/tests/unit/test_field.c index 3fccf77e9..2a5e1ed92 100644 --- a/tests/unit/test_field.c +++ b/tests/unit/test_field.c @@ -642,7 +642,7 @@ int test_field_io_aggr_pack(pe_t * pe) { cs_limits_t lim = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; io_aggregator_t buf = {0}; - io_aggregator_create(field->binary, lim, &buf); + io_aggregator_initialise(field->binary, lim, &buf); util_field_data_check_set(field); field_io_aggr_pack(field, &buf); @@ -655,7 +655,7 @@ int test_field_io_aggr_pack(pe_t * pe) { field_io_aggr_unpack(field, &buf); util_field_data_check(field); - io_aggregator_free(&buf); + io_aggregator_finalise(&buf); } } diff --git a/tests/unit/test_io_aggregator.c b/tests/unit/test_io_aggregator.c index fc4246637..cfda4fe07 100644 --- a/tests/unit/test_io_aggregator.c +++ b/tests/unit/test_io_aggregator.c @@ -18,6 +18,7 @@ #include "io_aggregator.h" int test_io_aggregator_create(void); +int test_io_aggregator_initialise(void); /***************************************************************************** * @@ -36,6 +37,7 @@ int test_io_aggregator_suite(void) { assert(sizeof(io_aggregator_t) == 72); test_io_aggregator_create(); + test_io_aggregator_initialise(); pe_info(pe, "%-9s %s\n", "PASS", __FILE__); pe_free(pe); @@ -45,11 +47,11 @@ int test_io_aggregator_suite(void) { /***************************************************************************** * - * test_io_aggregator_create + * test_io_aggregator_initialise * *****************************************************************************/ -int test_io_aggregator_create(void) { +int test_io_aggregator_initialise(void) { int ifail = 0; @@ -62,7 +64,7 @@ int test_io_aggregator_create(void) { cs_limits_t lim = {-2, 18, 1, 8, 1, 4}; io_aggregator_t aggr = {0}; - io_aggregator_create(element, lim, &aggr); + io_aggregator_initialise(element, lim, &aggr); assert(aggr.element.datatype == MPI_DOUBLE); assert(aggr.element.datasize == sizeof(double)); @@ -74,7 +76,8 @@ int test_io_aggregator_create(void) { assert(aggr.lim.imin == lim.imin); /* Assume sufficient */ assert(aggr.buf); - io_aggregator_free(&aggr); + io_aggregator_finalise(&aggr); + assert(aggr.szelement == 0); assert(aggr.szbuf == 0); assert(aggr.lim.imin == 0); @@ -83,3 +86,48 @@ int test_io_aggregator_create(void) { return ifail; } + +/***************************************************************************** + * + * test_io_aggregator_create + * + *****************************************************************************/ + +int test_io_aggregator_create(void) { + + int ifail = 0; + + { + /* Bad size (e.g., via element.count = 0) */ + io_element_t element = {.datatype = MPI_DOUBLE, + .datasize = sizeof(double), + .count = 0, + .endian = io_endianness()}; + cs_limits_t limits = {0}; + io_aggregator_t * aggr = NULL; + + ifail = io_aggregator_create(element, limits, &aggr); + assert(ifail != 0); + assert(aggr == NULL); + } + + { + /* Good */ + io_element_t element = {.datatype = MPI_DOUBLE, + .datasize = sizeof(double), + .count = 1, + .endian = io_endianness()}; + cs_limits_t limits = {1, 8, 1, 4, 1, 2}; + io_aggregator_t * aggr = NULL; + + ifail = io_aggregator_create(element, limits, &aggr); + assert(ifail == 0); + assert(aggr); + assert(aggr->buf); + + io_aggregator_free(&aggr); + assert(aggr == NULL); + } + + return ifail; +} diff --git a/tests/unit/test_io_element.c b/tests/unit/test_io_element.c index 5a708eb71..3588d3397 100644 --- a/tests/unit/test_io_element.c +++ b/tests/unit/test_io_element.c @@ -57,25 +57,30 @@ int test_io_element_suite(void) { int test_io_endian_from_string(void) { + int ifail = 0; + { const char * str = "LITTLE_ENDIAN"; io_endian_enum_t endian = io_endian_from_string(str); assert(endian == IO_ENDIAN_LITTLE_ENDIAN); + if (endian != IO_ENDIAN_LITTLE_ENDIAN) ifail = -1; } { const char * str = "BIG_ENDIAN"; io_endian_enum_t endian = io_endian_from_string(str); assert(endian == IO_ENDIAN_BIG_ENDIAN); + if (endian != IO_ENDIAN_BIG_ENDIAN) ifail = -2; } { const char * str = "RUBBISH"; io_endian_enum_t endian = io_endian_from_string(str); assert(endian == IO_ENDIAN_UNKNOWN); + if (endian != IO_ENDIAN_UNKNOWN) ifail = -4; } - return 0; + return ifail; } /***************************************************************************** @@ -86,25 +91,30 @@ int test_io_endian_from_string(void) { int test_io_endian_to_string(void) { + int ifail = 0; + { io_endian_enum_t endian = IO_ENDIAN_UNKNOWN; const char * str = io_endian_to_string(endian); - assert(strcmp(str, "UNKNOWN") == 0); + ifail = strcmp(str, "UNKNOWN"); + assert(ifail == 0); } { io_endian_enum_t endian = IO_ENDIAN_LITTLE_ENDIAN; const char * str = io_endian_to_string(endian); - assert(strcmp(str, "LITTLE_ENDIAN") == 0); + ifail = strcmp(str, "LITTLE_ENDIAN"); + assert(ifail == 0); } { io_endian_enum_t endian = IO_ENDIAN_BIG_ENDIAN; const char * str = io_endian_to_string(endian); - assert(strcmp(str, "BIG_ENDIAN") == 0); + ifail = strcmp(str, "BIG_ENDIAN"); + assert(ifail == 0); } - return 0; + return ifail; } /***************************************************************************** @@ -122,7 +132,7 @@ int test_io_element_null(void) { assert(element.count == 0); assert(element.endian == IO_ENDIAN_UNKNOWN); - return 0; + return element.count; } /***************************************************************************** @@ -194,6 +204,7 @@ int test_io_element_to_json(void) { assert(jsondt); dt = util_io_string_to_mpi_datatype(cJSON_GetStringValue(jsondt)); assert(dt == MPI_DOUBLE); + if (dt != MPI_DOUBLE) ifail = -1; } { /* Datasize */ @@ -203,6 +214,7 @@ int test_io_element_to_json(void) { assert(cJSON_IsNumber(jsonsz)); sz = cJSON_GetNumberValue(jsonsz); assert(sz == sizeof(double)); + if (sz != sizeof(double)) ifail = -2; } { /* Count */ @@ -212,6 +224,7 @@ int test_io_element_to_json(void) { assert(cJSON_IsNumber(jsonct)); count = cJSON_GetNumberValue(jsonct); assert(count == 3); + if (count != 3) ifail = -4; } { io_endian_enum_t endian = IO_ENDIAN_UNKNOWN; @@ -220,6 +233,7 @@ int test_io_element_to_json(void) { assert(cJSON_IsString(jsonend)); endian = io_endian_from_string(cJSON_GetStringValue(jsonend)); assert(endian == IO_ENDIAN_LITTLE_ENDIAN); + if (endian != IO_ENDIAN_LITTLE_ENDIAN) ifail = -8; } cJSON_Delete(json); diff --git a/tests/unit/test_io_impl_mpio.c b/tests/unit/test_io_impl_mpio.c new file mode 100644 index 000000000..bd2b60d88 --- /dev/null +++ b/tests/unit/test_io_impl_mpio.c @@ -0,0 +1,552 @@ +/***************************************************************************** + * + * test_io_impl_mpio.c + * + * The MPI / IO implementation (with a mock aggregator). + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Contributing authors: + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include +#include +#include + +#include "pe.h" +#include "coords.h" +#include "io_impl_mpio.h" + +int test_io_impl_mpio_create(cs_t * cs, const io_metadata_t * metadata); +int test_io_impl_mpio_initialise(cs_t * cs, const io_metadata_t * metadata); + +int test_io_impl_mpio_write(cs_t * cs, const io_metadata_t * metadata, + const char * filename); +int test_io_impl_mpio_read(cs_t * cs, const io_metadata_t * metadata, + const char * filename); +int test_io_impl_mpio_write_begin(io_impl_mpio_t ** io, + const io_metadata_t * meta, + const char * filename); +int test_io_impl_mpio_write_end(io_impl_mpio_t ** io); + +static int test_buf_pack_asc(cs_t * cs, io_aggregator_t * buf); +static int test_buf_pack_bin(cs_t * cs, io_aggregator_t * buf); +static int test_buf_unpack_asc(cs_t * cs, const io_aggregator_t * buf); +static int test_buf_unpack_bin(cs_t * cs, const io_aggregator_t * buf); + +/***************************************************************************** + * + * test_io_impl_mpio_suite + * + *****************************************************************************/ + +int test_io_impl_mpio_suite(void) { + + /* I want a smallish size, but allow for a meaningful decomposition */ + int ntotal[3] = {16, 8, 4}; + + pe_t * pe = NULL; + cs_t * cs = NULL; + + /* Describe test_buf_pack_asc() and test_buf_pack_bin() */ + io_mode_enum_t mode = IO_MODE_MPIIO; + io_element_t element_asc = {.datatype = MPI_CHAR, + .datasize = sizeof(char), + .count = 28, + .endian = io_endianness()}; + io_element_t element_bin = {.datatype = MPI_INT64_T, + .datasize = sizeof(int64_t), + .count = 1, + .endian = io_endianness()}; + + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + cs_create(pe, &cs); + cs_ntotal_set(cs, ntotal); + cs_init(cs); + + /* ASCII: write then read */ + { + io_options_t opts = io_options_with_format(mode, IO_RECORD_ASCII); + io_metadata_t metadata = {0}; + const char * filename = "io-impl-mpio-asc.dat"; + const char * afilename = "io-impl-mpio-async-asc.dat"; + + io_metadata_create(cs, &opts, &element_asc, &metadata); + + test_io_impl_mpio_create(cs, &metadata); + test_io_impl_mpio_initialise(cs, &metadata); + test_io_impl_mpio_write(cs, &metadata, filename); + test_io_impl_mpio_read(cs, &metadata, filename); + + /* Asynchronous version ... just write ... */ + /* Something of a smoke test at the moment ... */ + { + io_impl_mpio_t * io = NULL; + test_io_impl_mpio_write_begin(&io, &metadata, afilename); + test_io_impl_mpio_write_end(&io); + } + + io_metadata_free(&metadata); + + MPI_Barrier(MPI_COMM_WORLD); + if (pe_mpi_rank(pe) == 0) remove(filename); + if (pe_mpi_rank(pe) == 0) remove(afilename); + } + + /* Binary: write then read */ + { + io_options_t opts = io_options_with_format(mode, IO_RECORD_BINARY); + io_metadata_t metadata = {0}; + const char * filename = "io-impl-mpio-bin.dat"; + + io_metadata_create(cs, &opts, &element_bin, &metadata); + + test_io_impl_mpio_create(cs, &metadata); + test_io_impl_mpio_initialise(cs, &metadata); + test_io_impl_mpio_write(cs, &metadata, filename); + test_io_impl_mpio_read(cs, &metadata, filename); + + io_metadata_free(&metadata); + + MPI_Barrier(MPI_COMM_WORLD); + if (pe_mpi_rank(pe) == 0) remove(filename); + } + + /* Multiple file iogrid = {2, 1, 1} */ + + if (pe_mpi_size(pe) > 1) { + + io_record_format_enum_t ior = IO_RECORD_ASCII; + int iosize[3] = {2, 1, 1}; + io_options_t opts = io_options_with_iogrid(mode, ior, iosize); + io_metadata_t metadata = {0}; + + const char * filestub = "io-impl-mpio"; + char filename[BUFSIZ] = {0}; + + io_metadata_create(cs, &opts, &element_asc, &metadata); + io_subfile_name(&metadata.subfile, filestub, 0, filename, BUFSIZ); + + test_io_impl_mpio_create(cs, &metadata); + test_io_impl_mpio_initialise(cs, &metadata); + test_io_impl_mpio_write(cs, &metadata, filename); + test_io_impl_mpio_read(cs, &metadata, filename); + + MPI_Barrier(metadata.comm); + { + /* Clean up */ + int rank = -1; + MPI_Comm_rank(metadata.comm, &rank); + if (rank == 0) remove(filename); + } + + io_metadata_free(&metadata); + } + + pe_info(pe, "%-9s %s\n", "PASS", __FILE__); + cs_free(cs); + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_io_impl_mpio_create + * + *****************************************************************************/ + +int test_io_impl_mpio_create(cs_t * cs, const io_metadata_t * meta) { + + int ifail = 0; + io_impl_mpio_t * io = NULL; + + ifail = io_impl_mpio_create(meta, &io); + assert(ifail == 0); + assert(io); + + assert(io->super.impl); + assert(io->super.aggr); + assert(io->super.aggr->buf); + + /* Just check we have retained the pointers. + * Substantial checks are covered in initialise() test */ + + assert(io->metadata == meta); + + io_impl_mpio_free(&io); + assert(io == NULL); + + return ifail; +} + +/***************************************************************************** + * + * test_io_impl_mpio_initialise + * + *****************************************************************************/ + +int test_io_impl_mpio_initialise(cs_t * cs, const io_metadata_t * meta) { + + int ifail = 0; + io_impl_mpio_t io = {0}; + + assert(cs); + assert(meta); + + io_impl_mpio_initialise(meta, &io); + + assert(io.super.impl); + assert(io.super.aggr); + assert(io.super.aggr->buf); + + assert(io.metadata == meta); + assert(io.fh == MPI_FILE_NULL); + + /* Portability may be questioned if padding is present... */ + { + /* Element size (bytes) */ + size_t esz = meta->element.datasize*meta->element.count; + int mpisz = -1; + MPI_Type_size(io.element, &mpisz); + if ((size_t) mpisz != esz) ifail = -1; + assert(ifail == 0); + } + + { + /* Local size of array (bytes) */ + int mpisz = -1; + int lsize = cs_limits_size(meta->limits); + size_t asz = lsize*meta->element.datasize*meta->element.count; + MPI_Type_size(io.array, &mpisz); + if ((size_t) mpisz != asz) ifail = -2; + assert(ifail == 0); + } + + { + /* Size of file data type (bytes) ... is actually the same as + * the local size ... */ + int mpisz = -1; + int lsize = cs_limits_size(meta->limits); + size_t fsz = lsize*meta->element.datasize*meta->element.count; + MPI_Type_size(io.file, &mpisz); + if ((size_t) mpisz != fsz) ifail = -4; + assert(ifail == 0); + } + + io_impl_mpio_finalise(&io); + + assert(io.super.aggr == NULL); + assert(io.metadata == NULL); + + return ifail; +} + +/***************************************************************************** + * + * test_io_impl_mpio_write + * + *****************************************************************************/ + +int test_io_impl_mpio_write(cs_t * cs, const io_metadata_t * meta, + const char * filename) { + + io_impl_mpio_t io = {0}; + + assert(cs); + assert(meta); + assert(filename); + + io_impl_mpio_initialise(meta, &io); + + { + io_aggregator_t * aggr = io.super.aggr; + if (meta->element.datatype == MPI_CHAR) test_buf_pack_asc(cs, aggr); + if (meta->element.datatype == MPI_INT64_T) test_buf_pack_bin(cs, aggr); + } + + io_impl_mpio_write(&io, filename); + + io_impl_mpio_finalise(&io); + + return 0; +} + +/***************************************************************************** + * + * test_io_impl_mpio_read + * + *****************************************************************************/ + +int test_io_impl_mpio_read(cs_t * cs, const io_metadata_t * meta, + const char * filename) { + + io_impl_mpio_t io = {0}; + + assert(cs); + assert(filename); + + /* Read and check. The unpack does the check. */ + + io_impl_mpio_initialise(meta, &io); + + io_impl_mpio_read(&io, filename); + + { + io_aggregator_t * aggr = io.super.aggr; + if (meta->element.datatype == MPI_CHAR) test_buf_unpack_asc(cs, aggr); + if (meta->element.datatype == MPI_INT64_T) test_buf_unpack_bin(cs, aggr); + } + + io_impl_mpio_finalise(&io); + + return 0; +} + +/***************************************************************************** + * + * test_io_impl_mpio_write_begin + * + *****************************************************************************/ + +int test_io_impl_mpio_write_begin(io_impl_mpio_t ** io, + const io_metadata_t * meta, + const char * filename) { + assert(io); + assert(meta); + + io_impl_mpio_create(meta, io); + + io_impl_mpio_write_begin(*io, filename); + + return 0; +} + +/***************************************************************************** + * + * test_io_impl_mpio_write_end + * + *****************************************************************************/ + +int test_io_impl_mpio_write_end(io_impl_mpio_t ** io) { + + assert(io); + + io_impl_mpio_write_end(*io); + io_impl_mpio_free(io); + + assert(*io == NULL); + + return 0; +} + +/***************************************************************************** + * + * test_unique_value + * + *****************************************************************************/ + +static int64_t test_unique_value(cs_t * cs, int ic, int jc, int kc) { + + int64_t ival = -1; + int ntotal[3] = {0}; + int nlocal[3] = {0}; + int offset[3] = {0}; + + cs_ntotal(cs, ntotal); + cs_nlocal(cs, nlocal); + cs_nlocal_offset(cs, offset); + + { + int ix = offset[X] + ic - 1; + int iy = offset[Y] + jc - 1; + int iz = offset[Z] + kc - 1; + + ival = ntotal[Z]*ntotal[Y]*ix + ntotal[Z]*iy + iz; + } + + return ival; +} + +/***************************************************************************** + * + * test_buf_pack_asc + * + *****************************************************************************/ + +static int test_buf_pack_asc(cs_t * cs, io_aggregator_t * buf) { + + int ifail = 0; + + int ib = 0; + int ntotal[3] = {0}; + int nlocal[3] = {0}; + int offset[3] = {0}; + + assert(cs); + assert(buf); + + cs_ntotal(cs, ntotal); + cs_nlocal(cs, nlocal); + cs_nlocal_offset(cs, offset); + + for (int ic = 1; ic <= nlocal[X]; ic++) { + for (int jc = 1; jc <= nlocal[Y]; jc++) { + for (int kc = 1; kc <= nlocal[Z]; kc++) { + int64_t ival = test_unique_value(cs, ic, jc, kc); + { + /* Add 1 <= label <= ntotal for each dimension */ + int ix = offset[X] + ic; + int iy = offset[Y] + jc; + int iz = offset[Z] + kc; + char cline[BUFSIZ] = {0}; + size_t nc = 0; /* int returned but need to compare to size_t */ + nc = sprintf(cline, "%4d %4d %4d %12" PRId64 "\n", ix, iy, iz, ival); + assert(nc == buf->szelement); + if (nc != buf->szelement) ifail += 1; + memcpy(buf->buf + ib*buf->szelement, cline, buf->szelement); + } + ib += 1; + } + } + } + + assert(ib == nlocal[X]*nlocal[Y]*nlocal[Z]); + + return ifail; +} + +/***************************************************************************** + * + * test_buf_unpack_asc + * + *****************************************************************************/ + +static int test_buf_unpack_asc(cs_t * cs, const io_aggregator_t * buf) { + + int ifail = 0; + + int ib = 0; + int ntotal[3] = {0}; + int nlocal[3] = {0}; + int offset[3] = {0}; + + assert(cs); + assert(buf->buf); + + cs_ntotal(cs, ntotal); + cs_nlocal(cs, nlocal); + cs_nlocal_offset(cs, offset); + + for (int ic = 1; ic <= nlocal[X]; ic++) { + for (int jc = 1; jc <= nlocal[Y]; jc++) { + for (int kc = 1; kc <= nlocal[Z]; kc++) { + int64_t ival = test_unique_value(cs, ic, jc, kc); + { + /* Add 1 <= label <= ntotal for each dimension */ + int ix = offset[X] + ic; + int iy = offset[Y] + jc; + int iz = offset[Z] + kc; + int ixread = -1; + int iyread = -1; + int izread = -1; + int64_t ivalread = -1; + /* Note int64_t requires portable format */ + int nc = sscanf(buf->buf + ib*buf->szelement, "%4d %4d %4d %" SCNd64, + &ixread, &iyread, &izread, &ivalread); + + assert(nc == 4); + assert(ixread == ix); + assert(iyread == iy); + assert(izread == iz); + assert(ivalread == ival); + if (nc != 4) ifail += 1; + if (iz != izread) ifail += 1; + if (iy != iyread) ifail += 1; + if (ix != ixread) ifail =+ 1; + if (ivalread != ival) ifail += 1; + } + ib += 1; + } + } + } + + assert(ib == nlocal[X]*nlocal[Y]*nlocal[Z]); + + return ifail; +} + + +/***************************************************************************** + * + * test_buf_pack_bin + * + *****************************************************************************/ + +static int test_buf_pack_bin(cs_t * cs, io_aggregator_t * buf) { + + assert(cs); + assert(buf->buf); + + int ib = 0; + int nlocal[3] = {0}; + + cs_nlocal(cs, nlocal); + + for (int ic = 1; ic <= nlocal[X]; ic++) { + for (int jc = 1; jc <= nlocal[Y]; jc++) { + for (int kc = 1; kc <= nlocal[Z]; kc++) { + int64_t ival = test_unique_value(cs, ic, jc, kc); + memcpy(buf->buf + ib*sizeof(int64_t), &ival, sizeof(int64_t)); + ib += 1; + } + } + } + + assert(ib == nlocal[X]*nlocal[Y]*nlocal[Z]); + + return 0; +} + +/***************************************************************************** + * + * test_buf_unpack_bin + * + *****************************************************************************/ + +static int test_buf_unpack_bin(cs_t * cs, const io_aggregator_t * buf) { + + int ifail = 0; + + int ib = 0; + int nlocal[3] = {0}; + + assert(cs); + assert(buf->buf); + + cs_nlocal(cs, nlocal); + + for (int ic = 1; ic <= nlocal[X]; ic++) { + for (int jc = 1; jc <= nlocal[Y]; jc++) { + for (int kc = 1; kc <= nlocal[Z]; kc++) { + int64_t ival = test_unique_value(cs, ic, jc, kc); + int64_t iread = -1; + memcpy(&iread, buf->buf + ib*sizeof(int64_t), sizeof(int64_t)); + assert(ival == iread); + if (ival != iread) ifail += 1; + ib += 1; + } + } + } + + assert(ib == nlocal[X]*nlocal[Y]*nlocal[Z]); + + return ifail; +} diff --git a/tests/unit/test_io_info_args.c b/tests/unit/test_io_info_args.c index c39e982bf..1a205507a 100644 --- a/tests/unit/test_io_info_args.c +++ b/tests/unit/test_io_info_args.c @@ -52,7 +52,7 @@ int test_io_info_args_default(void) { io_info_args_t args = io_info_args_default(); /* If the size of the struct changes, the tests need updating */ - assert(sizeof(io_options_t) == 20); + assert(sizeof(io_info_args_t) == (2*sizeof(io_options_t) + 4*sizeof(int))); assert(args.input.mode == io_mode_default()); diff --git a/tests/unit/test_io_metadata.c b/tests/unit/test_io_metadata.c new file mode 100644 index 000000000..df798a62b --- /dev/null +++ b/tests/unit/test_io_metadata.c @@ -0,0 +1,100 @@ +/***************************************************************************** + * + * test_io_metadata.c + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include + +#include "io_metadata.h" + +int test_io_metadata_create(cs_t * cs); + +/***************************************************************************** + * + * test_io_metadata_suite + * + *****************************************************************************/ + +int test_io_metadata_suite(void) { + + pe_t * pe = NULL; + cs_t * cs = NULL; + + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + cs_create(pe, &cs); + cs_init(cs); + + test_io_metadata_create(cs); + + pe_info(pe, "%-9s %s\n", "PASS", __FILE__); + cs_free(cs); + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_io_metadata_create + * + *****************************************************************************/ + +int test_io_metadata_create(cs_t * cs) { + + int ifail = 0; + io_metadata_t metadata = {0}; + io_element_t element = {0}; + io_options_t options = io_options_default(); + + ifail = io_metadata_create(cs, &options, &element, &metadata); + assert(ifail == 0); /* Bad decomposition */ + + /* Really want a proper compare method for components */ + assert(metadata.options.iogrid[X] == options.iogrid[X]); + assert(metadata.options.iogrid[Y] == options.iogrid[Y]); + assert(metadata.options.iogrid[Z] == options.iogrid[Z]); + + assert(metadata.subfile.iosize[X] == options.iogrid[X]); + assert(metadata.subfile.iosize[Y] == options.iogrid[Y]); + assert(metadata.subfile.iosize[Z] == options.iogrid[Z]); + + { + /* Check limits */ + int nlocal[3] = {0}; + cs_nlocal(cs, nlocal); + assert(metadata.limits.imin == 1); + assert(metadata.limits.jmin == 1); + assert(metadata.limits.kmin == 1); + assert(metadata.limits.imax == nlocal[X]); + assert(metadata.limits.jmax == nlocal[Y]); + assert(metadata.limits.kmax == nlocal[Z]); + } + + { + /* Parent communicator should be the Carteisan communicator */ + /* and the communicator is congruent with it */ + MPI_Comm comm = MPI_COMM_NULL; + int myresult = MPI_UNEQUAL; + + cs_cart_comm(cs, &comm); + MPI_Comm_compare(comm, metadata.parent, &myresult); + assert(myresult == MPI_IDENT); + + MPI_Comm_compare(comm, metadata.comm, &myresult); + assert(myresult == MPI_CONGRUENT); + } + + io_metadata_free(&metadata); + + return ifail; +} diff --git a/tests/unit/test_io_options.c b/tests/unit/test_io_options.c index 3c7595bb2..75015d923 100644 --- a/tests/unit/test_io_options.c +++ b/tests/unit/test_io_options.c @@ -24,6 +24,9 @@ __host__ int test_io_options_mode_valid(void); __host__ int test_io_options_record_format_valid(void); __host__ int test_io_options_metadata_version_valid(void); __host__ int test_io_options_default(void); +__host__ int test_io_options_with_mode(void); +__host__ int test_io_options_with_format(void); +__host__ int test_io_options_with_iogrid(void); /***************************************************************************** * @@ -41,6 +44,9 @@ __host__ int test_io_options_suite(void) { test_io_options_record_format_valid(); test_io_options_metadata_version_valid(); test_io_options_default(); + test_io_options_with_mode(); + test_io_options_with_format(); + test_io_options_with_iogrid(); pe_info(pe, "PASS ./unit/test_io_options\n"); @@ -50,7 +56,6 @@ __host__ int test_io_options_suite(void) { } - /***************************************************************************** * * test_io_options_mode_valid @@ -61,7 +66,8 @@ __host__ int test_io_options_mode_valid(void) { io_mode_enum_t mode1 = IO_MODE_SINGLE; io_mode_enum_t mode2 = IO_MODE_MULTIPLE; - io_mode_enum_t mode3 = IO_MODE_INVALID; + io_mode_enum_t mode3 = IO_MODE_MPIIO; + io_mode_enum_t mode9 = IO_MODE_INVALID; int isvalid = 0; isvalid = io_options_mode_valid(mode1); @@ -71,6 +77,9 @@ __host__ int test_io_options_mode_valid(void) { assert(isvalid); isvalid = io_options_mode_valid(mode3); + assert(isvalid == 1); + + isvalid = io_options_mode_valid(mode9); assert(isvalid == 0); return isvalid; @@ -140,6 +149,12 @@ __host__ int test_io_options_metadata_version_valid(void) { assert(io_options_metadata_version_valid(&opts)); + /* Right */ + opts.mode = IO_MODE_MPIIO; + opts.metadata_version = IO_METADATA_V2; + + assert(io_options_metadata_version_valid(&opts)); + return isvalid; } @@ -155,7 +170,7 @@ __host__ int test_io_options_default(void) { io_options_t opts = io_options_default(); /* If entries are changed in the struct, the tests should be updated... */ - assert(sizeof(io_options_t) == 20); + assert(sizeof(io_options_t) == 36); assert(io_options_mode_valid(opts.mode)); assert(io_options_record_format_valid(opts.iorformat)); @@ -164,6 +179,122 @@ __host__ int test_io_options_default(void) { assert(opts.report == 0); assert(opts.asynchronous == 0); + assert(opts.compression_levl == 0); + assert(opts.iogrid[0] == 1); + assert(opts.iogrid[1] == 1); + assert(opts.iogrid[2] == 1); return opts.report; } + +/***************************************************************************** + * + * test_io_options_with_mode + * + *****************************************************************************/ + +__host__ int test_io_options_with_mode(void) { + + int ifail = 0; + + { + /* IO_MODE_SINGLE */ + io_options_t options = io_options_with_mode(IO_MODE_SINGLE); + assert(options.mode == IO_MODE_SINGLE); + assert(options.iorformat == IO_RECORD_BINARY); + assert(options.metadata_version == IO_METADATA_SINGLE_V1); + assert(options.report == 0); + assert(options.asynchronous == 0); + assert(options.compression_levl == 0); + assert(options.iogrid[0] == 1); + assert(options.iogrid[1] == 1); + assert(options.iogrid[2] == 1); + ifail = options.report; + } + + { + /* IO_MODE_MULTIPLE */ + io_options_t options = io_options_with_mode(IO_MODE_MULTIPLE); + assert(options.mode == IO_MODE_MULTIPLE); + assert(options.iorformat == IO_RECORD_BINARY); + assert(options.metadata_version == IO_METADATA_MULTI_V1); + assert(options.report == 0); + assert(options.asynchronous == 0); + assert(options.compression_levl == 0); + assert(options.iogrid[0] == 1); + assert(options.iogrid[1] == 1); + assert(options.iogrid[2] == 1); + ifail = options.report; + } + + { + /* IO_MODE_MPIIO */ + io_options_t options = io_options_with_mode(IO_MODE_MPIIO); + assert(options.mode == IO_MODE_MPIIO); + assert(options.iorformat == IO_RECORD_BINARY); + assert(options.metadata_version == IO_METADATA_V2); + assert(options.report == 1); + assert(options.asynchronous == 0); + assert(options.compression_levl == 0); + assert(options.iogrid[0] == 1); + assert(options.iogrid[1] == 1); + assert(options.iogrid[2] == 1); + ifail = options.asynchronous; + } + + return ifail; +} + +/***************************************************************************** + * + * test_io_options_with_format + * + *****************************************************************************/ + +__host__ int test_io_options_with_format(void) { + + int ifail = 0; + io_mode_enum_t mode = IO_MODE_MPIIO; + + { + io_options_t opts = io_options_with_format(mode, IO_RECORD_ASCII); + assert(opts.mode == mode); + assert(opts.iorformat == IO_RECORD_ASCII); + if (opts.iorformat != IO_RECORD_ASCII) ifail = -1; + } + + { + io_options_t opts = io_options_with_format(mode, IO_RECORD_BINARY); + assert(opts.mode == mode); + assert(opts.iorformat == IO_RECORD_BINARY); + if (opts.iorformat != IO_RECORD_BINARY) ifail = -2; + } + + return ifail; +} + +/***************************************************************************** + * + * test_io_options_with_iogrid + * + *****************************************************************************/ + +__host__ int test_io_options_with_iogrid(void) { + + int ifail = 0; + io_mode_enum_t mode = IO_MODE_MPIIO; + io_record_format_enum_t ior = IO_RECORD_ASCII; + + { + int iogrid[3] = {2, 3, 4}; + io_options_t opts = io_options_with_iogrid(mode, ior, iogrid); + assert(opts.mode == mode); + assert(opts.iorformat == ior); + assert(opts.iogrid[0] == iogrid[0]); + assert(opts.iogrid[1] == iogrid[1]); + assert(opts.iogrid[2] == iogrid[2]); + if (opts.iogrid[0]*opts.iogrid[1]*opts.iogrid[2] != 24) ifail = -1; + } + + return ifail; +} diff --git a/tests/unit/test_io_options_rt.c b/tests/unit/test_io_options_rt.c index a2202989b..8c06abf40 100644 --- a/tests/unit/test_io_options_rt.c +++ b/tests/unit/test_io_options_rt.c @@ -93,13 +93,13 @@ __host__ int test_io_options_rt_rformat(pe_t * pe) { rt_create(pe, &rt); - rt_add_key_value(rt, "lb_input_io_format", "ASCII"); - rt_add_key_value(rt, "lb_output_io_format", "BINARY"); + rt_add_key_value(rt, "lb_io_input_format", "ASCII"); + rt_add_key_value(rt, "lb_io_output_format", "BINARY"); - io_options_rt_record_format(rt, RT_NONE, "lb_input_io_format", &iorformat); + io_options_rt_record_format(rt, RT_NONE, "lb_io_input_format", &iorformat); assert(iorformat == IO_RECORD_ASCII); - io_options_rt_record_format(rt, RT_NONE, "lb_output_io_format", &iorformat); + io_options_rt_record_format(rt, RT_NONE, "lb_io_output_format", &iorformat); assert(iorformat == IO_RECORD_BINARY); rt_free(rt); @@ -204,7 +204,6 @@ __host__ int test_io_options_rt_default(pe_t * pe) { __host__ int test_io_options_rt(pe_t * pe) { rt_t * rt = NULL; - io_options_t opts = io_options_default(); assert(pe); @@ -214,13 +213,40 @@ __host__ int test_io_options_rt(pe_t * pe) { rt_add_key_value(rt, "default_io_format", "ascii"); rt_add_key_value(rt, "default_io_report", "yes"); - io_options_rt(rt, RT_FATAL, "default", &opts); + { + io_options_t opts = io_options_with_mode(IO_MODE_SINGLE); + io_options_rt(rt, RT_FATAL, "default", &opts); + + assert(opts.mode == IO_MODE_MULTIPLE); + assert(opts.iorformat == IO_RECORD_ASCII); + assert(opts.metadata_version == IO_METADATA_MULTI_V1); + assert(opts.report == 1); + assert(opts.asynchronous == 0); + } + + /* "single" */ + rt_add_key_value(rt, "rho_io_mode", "single"); - assert(opts.mode == IO_MODE_MULTIPLE); - assert(opts.iorformat == IO_RECORD_ASCII); - assert(opts.metadata_version == IO_METADATA_MULTI_V1); - assert(opts.report == 1); - assert(opts.asynchronous == 0); + { + io_options_t opts = io_options_with_mode(IO_MODE_MPIIO); + io_options_rt(rt, RT_FATAL, "rho", &opts); + assert(opts.mode == IO_MODE_SINGLE); + assert(opts.iorformat == IO_RECORD_BINARY); + assert(opts.metadata_version == IO_METADATA_SINGLE_V1); + assert(opts.report == 0); + } + + /* "mpiio" */ + rt_add_key_value(rt, "vel_io_mode", "mpiio"); + + { + io_options_t opts = io_options_with_mode(IO_MODE_SINGLE); + io_options_rt(rt, RT_FATAL, "vel", &opts); + assert(opts.mode == IO_MODE_MPIIO); + assert(opts.iorformat == IO_RECORD_BINARY); + assert(opts.metadata_version == IO_METADATA_V2); + assert(opts.report == 1); + } rt_free(rt); diff --git a/tests/unit/test_io_subfile.c b/tests/unit/test_io_subfile.c new file mode 100644 index 000000000..6bd5ebe6d --- /dev/null +++ b/tests/unit/test_io_subfile.c @@ -0,0 +1,378 @@ +/***************************************************************************** + * + * test_io_subfile.c + * + * This is one place where it would be useful to test MPI decompositions + * of significant extent in three dimensions. + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford(kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include +#include +#include +#include + +#include "pe.h" +#include "coords_s.h" +#include "io_subfile.h" + +int test_io_subfile_default(pe_t * pe); +int test_io_subfile_create(pe_t * pe, int ntotal[3], int iogrid[3]); +int test_io_subfile_to_json(void); +int test_io_subfile_from_json(void); +int test_io_subfile_name(pe_t * pe, int iogrid[3]); +int test_io_subfile(cs_t * cs, int iogrid[3], const io_subfile_t * subfile); + +/***************************************************************************** + * + * test_io_subfile_suite + * + *****************************************************************************/ + +int test_io_subfile_suite(void) { + + int sz = -1; + pe_t * pe = NULL; + + MPI_Comm_size(MPI_COMM_WORLD, &sz); + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + /* If the structure changes size, the tests change */ + assert(sizeof(io_subfile_t) == 60); + + test_io_subfile_default(pe); + test_io_subfile_to_json(); + test_io_subfile_from_json(); + + /* "Serial": iosize = {1, 1, 1} always works */ + { + int ntotal[3] = {11, 13, 15}; + int iosize[3] = {1, 1, 1}; + test_io_subfile_create(pe, ntotal, iosize); + test_io_subfile_name(pe, iosize); + } + + /* Small: MPI_Comm_size() <= 8 */ + + if (sz >= 2) { + { + int ntotal[3] = {8, 8, 8}; + int iogrid[3] = {2, 1, 1}; + test_io_subfile_create(pe, ntotal, iogrid); + test_io_subfile_name(pe, iogrid); + } + { + int ntotal[3] = {15, 8, 8}; + int iosize[3] = { 2, 1, 1}; + test_io_subfile_create(pe, ntotal, iosize); + } + } + + if (sz >= 4) { + { + int ntotal[3] = {8, 8, 8}; + int iogrid[3] = {2, 2, 1}; + test_io_subfile_create(pe, ntotal, iogrid); + } + } + + /* Medium sz >= 8 */ + + if (sz >= 8) { + { + int ntotal[3] = {8, 8, 8}; + int iogrid[3] = {2, 2, 2}; + test_io_subfile_create(pe, ntotal, iogrid); + } + { + int ntotal[3] = {32, 16, 8}; + int iogrid[3] = {4, 2, 1}; + test_io_subfile_create(pe, ntotal, iogrid); + } + } + + pe_info(pe, "%-9s %s\n", "PASS", __FILE__); + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_io_subfile_default + * + *****************************************************************************/ + +int test_io_subfile_default(pe_t * pe) { + + int ifail = 0; + cs_t * cs = NULL; + int ntotal[3] = {16, 18, 20}; + + cs_create(pe, &cs); + cs_ntotal_set(cs, ntotal); + cs_init(cs); + + { + /* Always a {1, 1, 1} decomposition */ + io_subfile_t subfile = io_subfile_default(cs); + int nlocal[3] = {0}; + + cs_ntotal(cs, ntotal); + cs_nlocal(cs, nlocal); + + assert(subfile.index == 0); + assert(subfile.nfile == 1); + + assert(subfile.iosize[X] == 1); + assert(subfile.iosize[Y] == 1); + assert(subfile.iosize[Z] == 1); + assert(subfile.coords[X] == 0); + assert(subfile.coords[Y] == 0); + assert(subfile.coords[Z] == 0); + assert(subfile.offset[X] == 0); + assert(subfile.offset[Y] == 0); + assert(subfile.offset[Z] == 0); + + assert(subfile.ndims == 3); + assert(subfile.sizes[X] == ntotal[X]); + assert(subfile.sizes[Y] == ntotal[Y]); + assert(subfile.sizes[Z] == ntotal[Z]); + if (subfile.ndims != 3) ifail = -1; + } + + cs_free(cs); + + return ifail; +} + +/***************************************************************************** + * + * test_io_subfile_create + * + *****************************************************************************/ + +int test_io_subfile_create(pe_t * pe, int ntotal[3], int iogrid[3]) { + + cs_t * cs = NULL; + + cs_create(pe, &cs); + cs_ntotal_set(cs, ntotal); + cs_decomposition_set(cs, iogrid); /* Try 1 rank per file */ + cs_init(cs); + + { + io_subfile_t subfile = {0}; + + io_subfile_create(cs, iogrid, &subfile); + test_io_subfile(cs, iogrid, &subfile); + } + + cs_free(cs); + + return 0; +} + + +/***************************************************************************** + * + * test_io_subfile + * + * Check the io_subfile_t is consistent with cs_t and iogrid. + * + *****************************************************************************/ + +int test_io_subfile(cs_t * cs, int iogrid[3], const io_subfile_t * subfile) { + + int ifail = 0; + MPI_Comm comm = MPI_COMM_NULL; + int ntotal[3] = {0}; + int nlocal[3] = {0}; + int offset[3] = {0}; + int cartsz[3] = {0}; + + assert(cs); + assert(subfile); + + /* Check */ + cs_cart_comm(cs, &comm); + cs_ntotal(cs, ntotal); + cs_nlocal(cs, nlocal); + cs_nlocal_offset(cs, offset); + cs_cartsz(cs, cartsz); + + { + /* File and indices */ + int nfile = iogrid[X]*iogrid[Y]*iogrid[Z]; + + assert(0 <= subfile->index && subfile->index < nfile); + assert(subfile->nfile == nfile); + if (subfile->nfile != nfile) ifail = -1; + } + + assert(subfile->iosize[X] == iogrid[X]); + assert(subfile->iosize[Y] == iogrid[Y]); + assert(subfile->iosize[Z] == iogrid[Z]); + + assert(0 <= subfile->coords[X] && subfile->coords[X] < iogrid[X]); + assert(0 <= subfile->coords[Y] && subfile->coords[Y] < iogrid[Y]); + assert(0 <= subfile->coords[Z] && subfile->coords[Z] < iogrid[Z]); + + assert(subfile->ndims == 3); + + /* Sizes: the sum of the relevant ranks' nlocal */ + + for (int ia = 0; ia < 3; ia++) { + int perio = cartsz[ia]/iogrid[ia]; + int p0 = subfile->coords[ia]*perio; + int sz = 0; + for (int ib = p0; ib < p0 + perio; ib++) { + sz += cs->listnlocal[ia][ib]; + } + assert(sz == subfile->sizes[ia]); + } + + /* (File) Offset */ + for (int ia = 0; ia < 3; ia++) { + int offsetrank = (cartsz[ia]/iogrid[ia])*subfile->coords[ia]; + assert(subfile->offset[ia] == cs->listnoffset[ia][offsetrank]); + if (subfile->offset[ia] != cs->listnoffset[ia][offsetrank]) ifail = -1; + } + + return ifail; +} + +/***************************************************************************** + * + * test_io_subfile_to_json + * + *****************************************************************************/ + +int test_io_subfile_to_json(void) { + + int ifail = 0; + + /* I'm not sure this is a particularly consistent example, but ... */ + io_subfile_t subfile = {.nfile = 8, + .index = 1, + .iosize = {1, 2, 4}, + .coords = {0, 1, 3}, + .ndims = 3, + .sizes = {16, 32, 64}, + .offset = {8, 16, 21}}; + cJSON * json = NULL; + ifail = io_subfile_to_json(&subfile, &json); + assert(ifail == 0); + assert(json != NULL); + + { + /* We're going to do a slightly circular test ... */ + io_subfile_t subtest = {0}; + ifail = io_subfile_from_json(json, &subtest); + assert(ifail == 0); + assert(subtest.nfile == subfile.nfile); + assert(subtest.index == subfile.index); + assert(subtest.iosize[X] == subfile.iosize[X]); + assert(subtest.iosize[Y] == subfile.iosize[Y]); + assert(subtest.iosize[Z] == subfile.iosize[Z]); + assert(subtest.coords[X] == subfile.coords[X]); + assert(subtest.coords[Y] == subfile.coords[Y]); + assert(subtest.coords[Z] == subfile.coords[Z]); + assert(subtest.ndims == 3); + assert(subtest.sizes[X] == subfile.sizes[X]); + assert(subtest.sizes[Y] == subfile.sizes[Y]); + assert(subtest.sizes[Z] == subfile.sizes[Z]); + assert(subtest.offset[X] == subfile.offset[X]); + assert(subtest.offset[Y] == subfile.offset[Y]); + assert(subtest.offset[Z] == subfile.offset[Z]); + } + + cJSON_Delete(json); + + return ifail; +} + +/***************************************************************************** + * + * test_io_subfile_from_json + * + *****************************************************************************/ + +int test_io_subfile_from_json(void) { + + int ifail = 0; + const char * jstr = "{\"Number of files\": 8," + " \"File index\": 1," + " \"Topology\": [1, 2, 4]," + " \"Coordinate\": [0, 1, 3]," + " \"Data ndims\": 3," + " \"File size (sites)\": [16, 32, 64]," + " \"File offset (sites)\": [7,15,31] }"; + + cJSON * json = cJSON_Parse(jstr); + + assert(json != NULL); + + { + /* Check the result is correct. */ + io_subfile_t subfile = {0}; + ifail = io_subfile_from_json(json, &subfile); + assert(ifail == 0); + assert(subfile.nfile == 8); + assert(subfile.index == 1); + assert(subfile.iosize[X] == 1); + assert(subfile.iosize[Y] == 2); + assert(subfile.iosize[Z] == 4); + assert(subfile.coords[X] == 0); + assert(subfile.coords[Y] == 1); + assert(subfile.coords[Z] == 3); + + assert(subfile.ndims == 3); + assert(subfile.sizes[X] == 16); + assert(subfile.sizes[Y] == 32); + assert(subfile.sizes[Z] == 64); + assert(subfile.offset[X] == 7); + assert(subfile.offset[Y] == 15); + assert(subfile.offset[Z] == 31); + } + + cJSON_Delete(json); + + return ifail; +} + +/***************************************************************************** + * + * test_io_subfile_name + * + *****************************************************************************/ + +int test_io_subfile_name(pe_t * pe, int iogrid[3]) { + + cs_t * cs = NULL; + + cs_create(pe, &cs); + cs_init(cs); + + { + io_subfile_t subfile = io_subfile_default(cs); + int iteration = 0; + char filename[BUFSIZ] = {0}; + + io_subfile_create(cs, iogrid, &subfile); + io_subfile_name(&subfile, "stub", iteration, filename, BUFSIZ); + assert(strncmp("stub-", filename, 5) == 0); + } + + cs_free(cs); + + return 0; +} diff --git a/tests/unit/test_util_io.c b/tests/unit/test_util_io.c index e769d3443..39a6167b7 100644 --- a/tests/unit/test_util_io.c +++ b/tests/unit/test_util_io.c @@ -49,28 +49,34 @@ int test_util_io_suite(void) { int test_util_io_string_to_mpi_datatype(void) { + int ifail = 0; + { MPI_Datatype dt = util_io_string_to_mpi_datatype(NULL); assert(dt == MPI_DATATYPE_NULL); + if (dt != MPI_DATATYPE_NULL) ifail = -1; } { MPI_Datatype dt = util_io_string_to_mpi_datatype("MPI_DOUBLE"); assert(dt == MPI_DOUBLE); + if (dt != MPI_DOUBLE) ifail = -1; } { MPI_Datatype dt = util_io_string_to_mpi_datatype("MPI_INT"); assert(dt == MPI_INT); + if (dt != MPI_INT) ifail = -1; } /* An unrecognised value -> MPI_DATATYPE_NULL */ { MPI_Datatype dt = util_io_string_to_mpi_datatype("RUBBISH"); assert(dt == MPI_DATATYPE_NULL); + if (dt != MPI_DATATYPE_NULL) ifail = -1; } - return 0; + return ifail; } /***************************************************************************** diff --git a/tests/unit/tests.c b/tests/unit/tests.c index 5f51f74da..a60672280 100644 --- a/tests/unit/tests.c +++ b/tests/unit/tests.c @@ -74,13 +74,13 @@ __host__ int tests_create() { test_halo_suite(); test_hydro_suite(); test_io_aggregator_suite(); - test_io_aggr_mpio_suite(); - test_io_cart_sub_suite(); test_io_element_suite(); test_io_options_suite(); test_io_options_rt_suite(); test_io_info_args_suite(); test_io_info_args_rt_suite(); + test_io_subfile_suite(); + test_io_impl_mpio_suite(); test_io_suite(); test_lb_d2q9_suite(); test_lb_d3q15_suite(); diff --git a/tests/unit/tests.h b/tests/unit/tests.h index 6342892b2..430bc05ce 100644 --- a/tests/unit/tests.h +++ b/tests/unit/tests.h @@ -53,13 +53,13 @@ int test_gradient_d3q27_suite(void); int test_halo_suite(void); int test_hydro_suite(void); int test_io_aggregator_suite(void); -int test_io_aggr_mpio_suite(void); -int test_io_cart_sub_suite(void); int test_io_element_suite(void); int test_io_info_args_suite(void); int test_io_info_args_rt_suite(void); int test_io_options_suite(void); int test_io_options_rt_suite(void); +int test_io_subfile_suite(void); +int test_io_impl_mpio_suite(void); int test_io_suite(void); int test_lb_d2q9_suite(void); int test_lb_d3q15_suite(void); From 93c7443a08f1479c2757551a555f8cf346adb5f0 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 19 Nov 2022 16:15:41 +0000 Subject: [PATCH 125/244] Remove unused svd routines --- src/util.c | 347 ----------------------------------------- src/util.h | 2 - tests/unit/test_util.c | 113 -------------- 3 files changed, 462 deletions(-) diff --git a/src/util.c b/src/util.c index b57808001..87b8636a5 100644 --- a/src/util.c +++ b/src/util.c @@ -594,89 +594,6 @@ int util_gauss_jordan(const int n, double * a, double * b) { return 0; } -/***************************************************************************** - * - * util_svd_solve - * - * Solve Ax = b for overdetermined system via SVD. - * - * A is m x n matrix and b[m] is right-hand side on entry. - * x[n] is solution on exit. - * - * The x returned minimises |Ax - b| in least squares sense. - * - *****************************************************************************/ - -__host__ -int util_svd_solve(int m, int n, double ** a, double * b, double * x) { - - int i, j, k; - int ifail = 0; - double sum; - double wmin, wmax; - - double ** u = NULL; - double ** v = NULL; - double * w = NULL; - double * tmp = NULL; - - ifail += util_matrix_create(m, n, &u); - ifail += util_matrix_create(n, n, &v); - ifail += util_vector_create(n, &w); - ifail += util_vector_create(n, &tmp); - - /* Copy the input a to u and do the SVD */ - - for (i = 0; i < m; i++) { - for (j = 0; j < n; j++) { - u[i][j] = a[i][j]; - } - } - - ifail += util_svd(m, n, u, w, v); - - /* What is the maximum singular value? Set the minimum. */ - - wmax = 0.0; - for (j = 0; j < n; j++) { - wmax = dmax(wmax, w[j]); - } - wmin = DBL_EPSILON*wmax; - - /* Do backsubstitution; k counts 'non-zero' singular values */ - - k = 0; - - for (j = 0; j < n; j++) { - sum = 0.0; - if (w[j] > wmin) { - for (i = 0; i < m; i++) { - sum += u[i][j]*b[i]; - } - sum /= w[j]; - k += 1; - } - tmp[j] = sum; - } - - if (k != n) ifail += 1; - - for (j = 0; j < n; j++) { - sum = 0.0; - for (k = 0; k < n; k++) { - sum += v[j][k]*tmp[k]; - } - x[j] = sum; - } - - util_vector_free(&tmp); - util_vector_free(&w); - util_matrix_free(n, &v); - util_matrix_free(m, &u); - - return ifail; -} - /***************************************************************************** * * util_vector_create @@ -762,270 +679,6 @@ int util_matrix_free(int m, double ***p) { return 0; } -/***************************************************************************** - * - * util_singular_value_decomposition - * - * For matrix a, with m rows and n columns, compute the singular - * value decompsition a = u w v^t - * where u[m][n] replaces a on output, the singular values are w[n] - * and v[n][n] is returned (not its transpose). - * - *****************************************************************************/ - -#define MAX_SVD_ITERATION 30 - -__host__ -int util_svd(int m, int n, double ** a, double * w, double ** v) { - - int i, j, k, ell = 0; - int jj, nm; - int iteration, flag; - - double anorm, scale; - double g, f, h; - double c, s; - double x, y, z; - double * rv1 = NULL; - - assert(m >= n); /* Number of rows is >= number of columns */ - - rv1 = (double*) calloc(n, sizeof(double)); - if (rv1 == NULL) return -1; - - g = scale = anorm = 0.0; - - for (i = 0; i < n; i++) { - ell = i + 1; - rv1[i] = scale*g; - g = s = scale = 0.0; - if (i < m) { - for (k = i; k < m; k++) { - scale += fabs(a[k][i]); - } - if (scale) { - for (k = i; k < m; k++) { - a[k][i] /= scale; - s += a[k][i]*a[k][i]; - } - f = a[i][i]; - g = -copysign(sqrt(s), f); - h = f*g - s; - a[i][i] = f - g; - for (j = ell; j < n; j++) { - for (s = 0.0, k = i; k < m; k++) s += a[k][i]*a[k][j]; - f = s/h; - for (k = i; k < m; k++) a[k][j] += f*a[k][i]; - } - for (k = i; k < m; k++) a[k][i] *= scale; - } - } - - w[i] = scale*g; - g = s = scale = 0.0; - - if (i < m && i != n - 1) { - - for (k = ell; k < n; k++) scale += fabs(a[i][k]); - - if (scale) { - for (k = ell; k < n; k++) { - a[i][k] /= scale; - s += a[i][k]*a[i][k]; - } - f = a[i][ell]; - g = - copysign(sqrt(s), f); - h = f*g - s; - a[i][ell] = f - g; - - for (k = ell; k < n; k++) rv1[k] = a[i][k]/h; - for (j = ell; j < m; j++) { - for (s = 0.0, k = ell; k < n; k++) s += a[j][k]*a[i][k]; - for (k = ell; k < n; k++) a[j][k] += s*rv1[k]; - } - for (k = ell; k < n; k++) a[i][k] *= scale; - } - } - - anorm = dmax(anorm, fabs(w[i]) + fabs(rv1[i])); - } - - /* Accumulation of right-hand transformations */ - - for (i = n - 1; i >= 0; i--) { - if (i < n - 1) { - if (g) { - for (j = ell; j < n; j++) { - /* Double division here avoids possible underflow */ - v[j][i] = (a[i][j]/a[i][ell]) / g; - } - for (j = ell; j < n; j++) { - for (s = 0.0, k = ell; k < n; k++) s += a[i][k]*v[k][j]; - for (k = ell; k < n; k++) v[k][j] += s*v[k][i]; - } - } - for (j = ell; j < n; j++) v[i][j] = v[j][i] = 0.0; - } - v[i][i] = 1.0; - g = rv1[i]; - ell = i; - } - - /* Accumulation of left-hand transforms */ - - for (i = n - 1; i >= 0; i--) { - assert(0 <= i); assert(i < n); - ell = i + 1; - g = w[i]; - for (j = ell; j < n; j++) a[i][j] = 0.0; - - if (g) { - g = 1.0 / g; - for (j = ell; j < n; j++) { - for (s = 0.0, k = ell; k < m; k++) s += a[k][i]*a[k][j]; - f = (s / a[i][i])*g; - for (k = i; k < m; k++) a[k][j] += f*a[k][i]; - } - for (j = i; j < m; j++) a[j][i] *= g; - } - else { - for (j = i; j < m; j++) a[j][i] = 0.0; - } - a[i][i] += 1.0; - } - - - /* Diagonalisation of the bidiagonal form */ - - nm = -1; - - for (k = n - 1; k >= 0; k--) { - - for (iteration = 1; iteration <= MAX_SVD_ITERATION; iteration++) { - - flag = 1; - for (ell = k; ell >= 0; ell--) { - nm = ell - 1; - /* Note we should always have rv1[0] = 0 to prevent nm = -1 below */ - if (fabs(rv1[ell]) + anorm == anorm) { - flag = 0; - break; - } - assert(0 <= nm); assert(nm < n); - if (fabs(w[nm]) + anorm == anorm) break; - } - - if (flag) { - c = 0.0; - s = 1.0; - for (i = ell; i <= k; i++) { - assert(0 <= i); assert(i < n); - f = s*rv1[i]; - rv1[i] = c*rv1[i]; - if (fabs(f) + anorm == anorm) break; - g = w[i]; - util_dpythag(f, g, &h); - w[i] = h; - h = 1.0 / h; - c = g*h; - s = -f*h; - for (j = 0; j < m; j++) { - y = a[j][nm]; - z = a[j][i]; - a[j][nm] = y*c + z*s; - a[j][i] = z*c - y*s; - } - } - } - - /* Convergence */ - - z = w[k]; - if (ell == k) { - if (z < 0.0) { - /* Singular value is non-negative */ - w[k] = -z; - for (j = 0; j < n; j++) v[j][k] = -v[j][k]; - } - break; - } - - if (iteration >= MAX_SVD_ITERATION) { - free(rv1); - return -2; - } - - x = w[ell]; - nm = k - 1; - assert(0 <= nm); assert(nm < n); - y = w[nm]; - g = rv1[nm]; - h = rv1[k]; - f = ((y - z)*(y + z) + (g - h)*(g + h)) / (2.0*h*y); - util_dpythag(f, 1.0, &g); - f = ((x - z)*(x + z) + h*((y / (f + copysign(g, f))) - h)) / x; - - /* Next QR transformation */ - - c = s = 1.0; - - for (j = ell; j <= nm; j++) { - assert(0 <= j); assert(j < n); - i = j + 1; - assert(0 <= i); assert(i < n); - g = rv1[i]; - y = w[i]; - h = s*g; - g = c*g; - util_dpythag(f, h, &z); - rv1[j] = z; - c = f/z; - s = h/z; - f = x*c + g*s; - g = g*c - x*s; - h = y*s; - y *= c; - - for (jj = 0; jj < n; jj++) { - x = v[jj][j]; - z = v[jj][i]; - v[jj][j] = x*c + z*s; - v[jj][i] = z*c - x*s; - } - - util_dpythag(f, h, &z); - w[j] = z; - - if (z) { - z = 1.0/z; - c = f*z; - s = h*z; - } - - f = c*g + s*y; - x = c*y - s*g; - - for (jj = 0; jj < m; jj++) { - y = a[jj][j]; - z = a[jj][i]; - a[jj][j] = y*c + z*s; - a[jj][i] = z*c - y*s; - } - } - - rv1[ell] = 0.0; - rv1[k] = f; - w[k] = x; - - /* End iteration */ - } - } - - free(rv1); - - return 0; -} - /***************************************************************************** * * util_matrix_invert diff --git a/src/util.h b/src/util.h index f7a75628a..bdf64981b 100644 --- a/src/util.h +++ b/src/util.h @@ -54,8 +54,6 @@ __host__ int util_jacobi_sort(double a[3][3], double vals[3], double vecs[3][3]) __host__ int util_discrete_volume_sphere(double r0[3], double a0, double * vn); __host__ int util_gauss_jordan(const int n, double * a, double * b); __host__ __device__ int util_dpythag(double a, double b, double * p); -__host__ int util_svd(int m, int n, double ** u, double * w, double ** v); -__host__ int util_svd_solve(int m, int n, double ** a, double * b, double * x); __host__ int util_matrix_create(int m, int n, double *** p); __host__ int util_vector_create(int m, double ** p); __host__ int util_matrix_free(int m, double *** p); diff --git a/tests/unit/test_util.c b/tests/unit/test_util.c index d0febc9c8..1fcfc03d4 100644 --- a/tests/unit/test_util.c +++ b/tests/unit/test_util.c @@ -23,15 +23,10 @@ #include "util.h" #include "tests.h" -/* For SVD tests, SVD_EPSILON is increased according to matrix elements... */ -#define SVD_EPSILON (2.0*DBL_EPSILON) - /* For RNG tests */ #define NLARGE 10000000 #define STAT_TOLERANCE 0.001 - -int util_svd_check(int m, int n, double ** a); int util_random_unit_vector_check(void); int util_str_tolower_check(void); int util_rectangle_conductance_check(void); @@ -44,34 +39,10 @@ int util_rectangle_conductance_check(void); int test_util_suite(void) { - int ifail = 0; - int m = 3; - int n = 2; - - double ** a = NULL; - double b[3] = {1.0, 2.0, 3.0}; - double x[2]; pe_t * pe = NULL; pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); - ifail = util_matrix_create(m, n, &a); - test_assert(ifail == 0); - - a[0][0] = -1.0; - a[0][1] = 0.0; - a[1][0] = 0.0; - a[1][1] = 3.0; - a[2][0] = 2.0; - a[2][1] = -1.0; - - ifail = util_svd_check(m, n, a); - test_assert(ifail == 0); - - ifail = util_svd_solve(m, n, a, b, x); - test_assert(ifail == 0); - - util_matrix_free(m, &a); util_random_unit_vector_check(); util_str_tolower_check(); @@ -83,90 +54,6 @@ int test_util_suite(void) { return 0; } -/***************************************************************************** - * - * svd_check - * - *****************************************************************************/ - -int util_svd_check(int m, int n, double **a) { - - int i, j, k; - int ifail = 0; - double sum; - double svd_epsilon; /* Test tolerance for this a matrix */ - - double ** u = NULL; /* m x n matrix u (copy of a on input to svd) */ - double * w = NULL; /* Singular values w[n] */ - double ** v = NULL; /* n x n matrix v */ - - ifail += util_matrix_create(m, n, &u); - ifail += util_matrix_create(n, n, &v); - ifail += util_vector_create(n, &w); - - /* Copy input matrix. Use largest fabs(a[i][j]) to set a tolerance */ - - sum = 0.0; - - for (i = 0; i < m; i++) { - for (j = 0; j < n; j++) { - u[i][j] = a[i][j]; - sum = dmax(sum, fabs(a[i][j])); - } - } - svd_epsilon = sum*SVD_EPSILON; - - ifail += util_svd(m, n, u, w, v); - if (ifail > 0) printf("SVD routine failed\n"); - - /* Assert u is orthonormal \sum_{k=0}^{m-1} u_ki u_kj = delta_ij - * for 0 <= i, j < n: */ - - for (i = 0; i < n; i++) { - for (j = 0; j < n; j++) { - sum = 0.0; - for (k = 0; k < m; k++) { - sum += u[k][j]*u[k][i]; - } - if (fabs(sum - 1.0*(i == j)) > svd_epsilon) ifail += 1; - } - } - if (ifail > 0) printf("U not orthonormal\n"); - - /* Assert v is orthonormal \sum_{k=0}^{n-1} v_ki v_kj = delta_ij - * for <= 0 i, j, < n: */ - - for (i = 0; i < n; i++) { - for (j = 0; j < n; j++) { - sum = 0.0; - for (k = 0; k < n; k++) { - sum += v[k][j]*v[k][i]; - } - if (fabs(sum - 1.0*(i == j)) > svd_epsilon) ifail += 1; - } - } - if (ifail > 0) printf("V not orthonormal\n"); - - /* Assert u w v^t = a, ie., the decomposition is correct. */ - - for (i = 0; i < m; i++) { - for (j = 0; j < n; j++) { - sum = 0.0; - for (k = 0; k < n; k++) { - sum += u[i][k]*w[k]*v[j][k]; - } - if (fabs(sum - a[i][j]) > svd_epsilon) ifail += 1; - } - } - if (ifail > 0) printf("Decomposition incorrect\n"); - - util_vector_free(&w); - util_matrix_free(n, &v); - util_matrix_free(m, &u); - - return ifail; -} - /***************************************************************************** * * util_random_unit_vector_check From 362a9987458d39e0d2374316fb025795ae56944e Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 19 Nov 2022 16:16:24 +0000 Subject: [PATCH 126/244] Remove floating point assertion --- src/blue_phase.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/blue_phase.c b/src/blue_phase.c index 8d681541e..e2b95361b 100644 --- a/src/blue_phase.c +++ b/src/blue_phase.c @@ -991,7 +991,6 @@ __host__ __device__ int fe_lc_mol_field(fe_lc_t * fe, int index, double dsq[3][3]; assert(fe); - assert(fe->param->kappa0 == fe->param->kappa1); /* Exactly */ field_tensor(fe->q, index, q); field_grad_tensor_grad(fe->dq, index, dq); From b1b82661e556e57e0f56a4d860a507d202729355 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 19 Nov 2022 16:17:13 +0000 Subject: [PATCH 127/244] Use util_fopen() --- src/stats_rheology.c | 18 +++++++++--------- src/stats_turbulent.c | 7 ++++--- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/stats_rheology.c b/src/stats_rheology.c index f610936fe..9508b3c62 100644 --- a/src/stats_rheology.c +++ b/src/stats_rheology.c @@ -17,7 +17,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2010-2020 The University of Edinburgh + * (c) 2010-2022 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -31,11 +31,11 @@ #include "pe.h" #include "coords.h" -#include "util.h" #include "control.h" #include "physics.h" #include "free_energy.h" #include "stats_rheology.h" +#include "util_fopen.h" struct stats_rheo_s { pe_t * pe; @@ -245,7 +245,7 @@ int stats_rheology_free_energy_density_profile(stats_rheo_t * stat, fe_t * fe, if (mpi_cartcoords[X] == 0) { /* First to write ... */ - fp_output = fopen(filename, "w"); + fp_output = util_fopen(filename, "w"); } else { /* Block unitl the token is received from the previous process, @@ -253,7 +253,7 @@ int stats_rheology_free_energy_density_profile(stats_rheo_t * stat, fe_t * fe, rank = cs_cart_neighb(stat->cs, CS_BACK, X); MPI_Recv(&token, 1, MPI_INT, rank, tag_token, comm, &status); - fp_output = fopen(filename, "a"); + fp_output = util_fopen(filename, "a"); } if (fp_output == NULL) pe_fatal(stat->pe, "fopen(%s) failed\n", filename); @@ -498,7 +498,7 @@ int stats_rheology_stress_profile(stats_rheo_t * stat, const char * filename) { if (mpi_cartcoords[X] == 0) { /* First to write ... */ - fp_output = fopen(filename, "w"); + fp_output = util_fopen(filename, "w"); } else { /* Block unitl the token is received from the previous process, @@ -506,7 +506,7 @@ int stats_rheology_stress_profile(stats_rheo_t * stat, const char * filename) { rank = cs_cart_neighb(stat->cs, CS_BACK, X); MPI_Recv(&token, 1, MPI_INT, rank, tag_token, comm, &status); - fp_output = fopen(filename, "a"); + fp_output = util_fopen(filename, "a"); } if (fp_output == NULL) pe_fatal(stat->pe, "fopen(%s) failed\n", filename); @@ -649,7 +649,7 @@ int stats_rheology_stress_section(stats_rheo_t * stat, const char * filename) { if (mpi_cartcoords[X] == 0) { /* Open the file */ - if (is_writing) fp_output = fopen(filename, "w"); + if (is_writing) fp_output = util_fopen(filename, "w"); } else { /* Block until we get the token from the previous process and @@ -657,7 +657,7 @@ int stats_rheology_stress_section(stats_rheo_t * stat, const char * filename) { rank = cs_cart_neighb(stat->cs, CS_BACK, X); MPI_Recv(&token, 1, MPI_INT, rank, tag_token, comm, &status); - if (is_writing) fp_output = fopen(filename, "a"); + if (is_writing) fp_output = util_fopen(filename, "a"); } if (is_writing) { @@ -839,7 +839,7 @@ int stats_rheology_mean_stress(lb_t * lb, fe_t * fe, const char * filename) { /* Use filename supplied */ if (pe_mpi_rank(lb->pe) == 0) { - fp = fopen(filename, "a"); + fp = util_fopen(filename, "a"); if (fp == NULL) pe_fatal(lb->pe, "fopen(%s) failed\n", filename); fprintf(fp, "%9d ", physics_control_timestep(phys)); diff --git a/src/stats_turbulent.c b/src/stats_turbulent.c index 351215d11..8d2a29870 100644 --- a/src/stats_turbulent.c +++ b/src/stats_turbulent.c @@ -9,7 +9,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2008-2020 The University of Edinburgh + * (c) 2008-2022 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -21,6 +21,7 @@ #include #include "stats_turbulent.h" +#include "util_fopen.h" struct stats_turb_s { pe_t * pe; @@ -280,7 +281,7 @@ int stats_turbulent_ubar_output(stats_turb_t * stat, const char * filename) { if (mpi_cartcoords[X] == 0) { /* Open the file */ - if (is_writing) fp_output = fopen(filename, "w"); + if (is_writing) fp_output = util_fopen(filename, "w"); } else { /* Block until we get the token from the previous process and @@ -288,7 +289,7 @@ int stats_turbulent_ubar_output(stats_turb_t * stat, const char * filename) { rank = cs_cart_neighb(stat->cs, CS_BACK, X); MPI_Recv(&token, 1, MPI_INT, rank, tag_token, comm, &status); - if (is_writing) fp_output = fopen(filename, "a"); + if (is_writing) fp_output = util_fopen(filename, "a"); } if (is_writing) { From 94f30bd0806c1c0b0114ec86961078bb7b525ea7 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 19 Nov 2022 16:17:39 +0000 Subject: [PATCH 128/244] Avoid floating point comparison --- src/psi_rt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/psi_rt.c b/src/psi_rt.c index 2c0e14a4e..f7faf4cec 100644 --- a/src/psi_rt.c +++ b/src/psi_rt.c @@ -27,6 +27,7 @@ #include "psi_rt.h" #include "psi_init.h" #include "io_harness.h" +#include "util_bits.h" /***************************************************************************** * @@ -256,7 +257,7 @@ int psi_rt_init_rho(pe_t * pe, rt_t * rt, psi_t * obj, map_t * map) { psi_epsilon2(obj, &eps2); /* Unless really the same number ... */ - if (eps1 != eps2) { + if (0 == util_double_same(eps1, eps2)) { psi_debye_length2(obj, rho_el, &ld2); pe_info(pe, "Second Debye length: %14.7e\n", ld2); } @@ -288,7 +289,7 @@ int psi_rt_init_rho(pe_t * pe, rt_t * rt, psi_t * obj, map_t * map) { psi_epsilon2(obj, &eps2); /* Unless really the same number... */ - if (eps1 != eps2) { + if (0 == util_double_same(eps1, eps2)) { psi_debye_length2(obj, rho_el, &ld2); pe_info(pe, "Second Debye length: %14.7e\n", ld2); } From 8dc3ce910b5169eb44eea33d4e209c74ef10bc2e Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 19 Nov 2022 17:18:36 +0000 Subject: [PATCH 129/244] Add check on one elastic constant --- src/blue_phase_rt.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/blue_phase_rt.c b/src/blue_phase_rt.c index e5c4c5c69..7d079116e 100644 --- a/src/blue_phase_rt.c +++ b/src/blue_phase_rt.c @@ -26,6 +26,7 @@ #include "blue_phase_init.h" #include "blue_phase_rt.h" #include "physics.h" +#include "util_bits.h" int blue_phase_rt_coll_anchoring(pe_t * pe, rt_t * rt, rt_enum_t rt_err_level, lc_anchoring_param_t * coll); @@ -128,8 +129,11 @@ __host__ int blue_phase_init_rt(pe_t * pe, rt_t *rt, pe_info(pe, "Amplitude (uniaxial) order = %14.7e\n", fe_param.amplitude0); /* One-constant approximation enforced. Exactly. */ - /* Might really be a run-time check. */ - assert(fe_param.kappa0 == fe_param.kappa1); + + if (0 == util_double_same(fe_param.kappa0, fe_param.kappa1)) { + pe_info(pe, "Must have elastic constants the same\n"); + pe_fatal(pe, "Please check and try again\n"); + } fe_lc_param_set(fe, &fe_param); From d6f0fc08d14f259ef1a198839a7f7fab5b7a310b Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 19 Nov 2022 17:19:05 +0000 Subject: [PATCH 130/244] Use util_fopen() --- src/io_harness.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/io_harness.c b/src/io_harness.c index de271fca7..71c8ad3f1 100644 --- a/src/io_harness.c +++ b/src/io_harness.c @@ -34,6 +34,7 @@ #include "pe.h" #include "util.h" +#include "util_fopen.h" #include "coords_s.h" #include "leesedwards.h" #include "io_harness.h" @@ -466,7 +467,7 @@ int io_write_metadata_file(io_info_t * info, char * filename_stub) { ny = info->io_comm->ngroup[Y]; nz = info->io_comm->ngroup[Z]; - fp_meta = fopen(filename_io, "w"); + fp_meta = util_fopen(filename_io, "w"); if (fp_meta == NULL) pe_fatal(info->pe, "fopen(%s) failed\n", filename_io); fprintf(fp_meta, "Metadata for file set prefix: %s\n", filename_stub); @@ -837,7 +838,7 @@ int io_write_data_p(io_info_t * obj, const char * filename_stub, void * data) { if (obj->io_comm->rank == 0) { /* Open the file anew */ - fp_state = fopen(filename_io, "wb"); + fp_state = util_fopen(filename_io, "wb"); } else { @@ -847,7 +848,7 @@ int io_write_data_p(io_info_t * obj, const char * filename_stub, void * data) { MPI_Recv(&token, 1, MPI_INT, obj->io_comm->rank - 1, io_tag, obj->io_comm->comm, &status); - fp_state = fopen(filename_io, "ab"); + fp_state = util_fopen(filename_io, "ab"); } if (fp_state == NULL) pe_fatal(obj->pe, "Failed to open %s\n", filename_io); @@ -936,7 +937,7 @@ int io_write_data_s(io_info_t * obj, const char * filename_stub, void * data) { localsz = itemsz*nlocal[X]*nlocal[Y]*nlocal[Z]; buf = (char *) malloc(localsz*sizeof(char)); - fp_buf = fopen("/dev/null", "w"); /* TODO: de-hardwire this */ + fp_buf = util_fopen("/dev/null", "w"); /* TODO: de-hardwire this */ setvbuf(fp_buf, buf, _IOFBF, localsz); if (buf == NULL || fp_buf == NULL) { @@ -987,13 +988,13 @@ int io_write_data_s(io_info_t * obj, const char * filename_stub, void * data) { * before allowing other groups to write at appropriate offset. */ if (obj->io_comm->index == 0) { - fp_state = fopen(filename_io, "w"); + fp_state = util_fopen(filename_io, "w"); } MPI_Bcast(&itemsz, 1, MPI_INT, 0, obj->io_comm->xcomm); if (obj->io_comm->index > 0) { - fp_state = fopen(filename_io, "r+"); + fp_state = util_fopen(filename_io, "r+"); offset = (long int) itemsz* obj->io_comm->offset[X]*obj->io_comm->nsite[Y]*obj->io_comm->nsite[Z]; fseek(fp_state, offset, SEEK_SET); @@ -1122,7 +1123,7 @@ int io_read_data(io_info_t * obj, const char * filename_stub, void * data) { if (obj->io_comm->rank == 0) { - fp_state = fopen(filename_io, "r"); + fp_state = util_fopen(filename_io, "r"); } else { @@ -1131,7 +1132,7 @@ int io_read_data(io_info_t * obj, const char * filename_stub, void * data) { MPI_Recv(&token, 1, MPI_LONG, obj->io_comm->rank - 1, io_tag, obj->io_comm->comm, &status); - fp_state = fopen(filename_io, "r"); + fp_state = util_fopen(filename_io, "r"); } if (fp_state == NULL) pe_fatal(obj->pe, "Failed to open %s\n", filename_io); From 0972dcdac1c6bb98eb9db7237ab7ed3c8d0bf632 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 19 Nov 2022 18:43:18 +0000 Subject: [PATCH 131/244] Trial --- src/main.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/main.c b/src/main.c index 92521ecf3..e2d1186c2 100644 --- a/src/main.c +++ b/src/main.c @@ -9,7 +9,7 @@ * Edinburgh Parallel Computing Centre * * Kevin Stratford (kevin@epcc.ed.ac.uk) - * (c) 2011-2021 The University of Edinburgh + * (c) 2011-2022 The University of Edinburgh * *****************************************************************************/ @@ -21,6 +21,8 @@ #include "petscksp.h" #endif +int process_command_line(const char * arg, char * filename, size_t bufsz); + /***************************************************************************** * * main @@ -36,8 +38,10 @@ int main(int argc, char ** argv) { MPI_Init_thread(&argc, &argv, MPI_THREAD_FUNNELED, &provided); #ifdef PETSC PetscInitialize(&argc, &argv, (char*) 0, NULL); -#endif if (argc > 1) snprintf(inputfile, FILENAME_MAX, "%s", argv[1]); +#endif + + if (argc > 1) process_command_line(argv[1], inputfile, FILENAME_MAX); ludwig_run(inputfile); @@ -48,3 +52,27 @@ int main(int argc, char ** argv) { return 0; } + +#include +#include + +int process_command_line(const char * arg, char * filename, size_t bufsz) { + + int ifail = 0; + + /* The first character should be alphabetical */ + + if (isalpha(arg[0])) { + int ndot = 0; + size_t len = strnlen(arg, bufsz-1); + for (size_t i = 0; i < len; i++) { + const char c = arg[i]; + if (c == '.') ndot += 1; + filename[i] = '_'; + if (isalnum(c) || c == '_' || c == '-' || c == '.') filename[i] = c; + } + filename[len] = '\0'; + } + + return ifail; +} From 9795a8f5d3d8f1173f801215773dfcb51b5497bf Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 19 Nov 2022 19:04:37 +0000 Subject: [PATCH 132/244] Try again --- src/main.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index e2d1186c2..026080cff 100644 --- a/src/main.c +++ b/src/main.c @@ -14,6 +14,7 @@ *****************************************************************************/ #include +#include #include "pe.h" #include "ludwig.h" @@ -41,7 +42,13 @@ int main(int argc, char ** argv) { if (argc > 1) snprintf(inputfile, FILENAME_MAX, "%s", argv[1]); #endif - if (argc > 1) process_command_line(argv[1], inputfile, FILENAME_MAX); + if (argc > 1) { + int ifail = process_command_line(argv[1], inputfile, FILENAME_MAX); + if (ifail != 0) { + printf("Input file name %s is not valid\n", argv[1]); + exit(-1); + } + } ludwig_run(inputfile); @@ -72,6 +79,7 @@ int process_command_line(const char * arg, char * filename, size_t bufsz) { if (isalnum(c) || c == '_' || c == '-' || c == '.') filename[i] = c; } filename[len] = '\0'; + ifail = strncmp(arg, filename, bufsz-1); } return ifail; From 7ae83b5fa693c77f41035f0b5ab98385d072a57c Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 19 Nov 2022 19:31:11 +0000 Subject: [PATCH 133/244] Try again --- src/main.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main.c b/src/main.c index 026080cff..afd612e3a 100644 --- a/src/main.c +++ b/src/main.c @@ -44,13 +44,17 @@ int main(int argc, char ** argv) { if (argc > 1) { int ifail = process_command_line(argv[1], inputfile, FILENAME_MAX); - if (ifail != 0) { + if (ifail == 0) { + ludwig_run(inputfile); + } + else { printf("Input file name %s is not valid\n", argv[1]); - exit(-1); } } + else { + ludwig_run("input"); + } - ludwig_run(inputfile); #ifdef PETSC PetscFinalize(); @@ -65,16 +69,14 @@ int main(int argc, char ** argv) { int process_command_line(const char * arg, char * filename, size_t bufsz) { - int ifail = 0; + int ifail = -1; /* The first character should be alphabetical */ if (isalpha(arg[0])) { - int ndot = 0; size_t len = strnlen(arg, bufsz-1); for (size_t i = 0; i < len; i++) { const char c = arg[i]; - if (c == '.') ndot += 1; filename[i] = '_'; if (isalnum(c) || c == '_' || c == '-' || c == '.') filename[i] = c; } From 5e5c5dc12780c1f545913a2e99f4b4716ed438bf Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 19 Nov 2022 20:33:24 +0000 Subject: [PATCH 134/244] Last try --- src/main.c | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/src/main.c b/src/main.c index afd612e3a..eb1fa0ce8 100644 --- a/src/main.c +++ b/src/main.c @@ -13,8 +13,9 @@ * *****************************************************************************/ +#include #include -#include +#include #include "pe.h" #include "ludwig.h" @@ -32,29 +33,32 @@ int process_command_line(const char * arg, char * filename, size_t bufsz); int main(int argc, char ** argv) { - char inputfile[FILENAME_MAX] = "input"; int provided = MPI_THREAD_SINGLE; - MPI_Init_thread(&argc, &argv, MPI_THREAD_FUNNELED, &provided); #ifdef PETSC PetscInitialize(&argc, &argv, (char*) 0, NULL); if (argc > 1) snprintf(inputfile, FILENAME_MAX, "%s", argv[1]); #endif - if (argc > 1) { - int ifail = process_command_line(argv[1], inputfile, FILENAME_MAX); + if (argc == 1) { + ludwig_run("input"); + } + else if (argc > 1) { + char filename[BUFSIZ/2] = {0}; + int ifail = process_command_line(argv[1], filename, BUFSIZ/2); if (ifail == 0) { - ludwig_run(inputfile); + char buf[BUFSIZ] = "./"; + char * f = buf; + strncat(f+2, filename, BUFSIZ-3); + ludwig_run(f); } else { - printf("Input file name %s is not valid\n", argv[1]); + printf("Input file name: %s\n" + "Please use a posix file name with only alphanumeric\n" + "characters or _ or - or .\n", argv[1]); } } - else { - ludwig_run("input"); - } - #ifdef PETSC PetscFinalize(); @@ -64,8 +68,16 @@ int main(int argc, char ** argv) { return 0; } -#include -#include +/***************************************************************************** + * + * process_command_line + * + * Require a posix portable filename, with the additional condition + * that the first character is alphabetical. + * + * A valid filename will return zero. + * + *****************************************************************************/ int process_command_line(const char * arg, char * filename, size_t bufsz) { From 2e280ca510e80e7c147a848b6c079feee7ae2feb Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 19 Nov 2022 21:00:34 +0000 Subject: [PATCH 135/244] Really the last effort --- src/main.c | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/src/main.c b/src/main.c index eb1fa0ce8..656814552 100644 --- a/src/main.c +++ b/src/main.c @@ -23,7 +23,7 @@ #include "petscksp.h" #endif -int process_command_line(const char * arg, char * filename, size_t bufsz); +int process_command_line(const char * arg); /***************************************************************************** * @@ -38,26 +38,13 @@ int main(int argc, char ** argv) { MPI_Init_thread(&argc, &argv, MPI_THREAD_FUNNELED, &provided); #ifdef PETSC PetscInitialize(&argc, &argv, (char*) 0, NULL); - if (argc > 1) snprintf(inputfile, FILENAME_MAX, "%s", argv[1]); #endif if (argc == 1) { ludwig_run("input"); } - else if (argc > 1) { - char filename[BUFSIZ/2] = {0}; - int ifail = process_command_line(argv[1], filename, BUFSIZ/2); - if (ifail == 0) { - char buf[BUFSIZ] = "./"; - char * f = buf; - strncat(f+2, filename, BUFSIZ-3); - ludwig_run(f); - } - else { - printf("Input file name: %s\n" - "Please use a posix file name with only alphanumeric\n" - "characters or _ or - or .\n", argv[1]); - } + else if (argc > 1 && process_command_line(argv[1]) == 0) { + ludwig_run(argv[1]); } #ifdef PETSC @@ -79,21 +66,24 @@ int main(int argc, char ** argv) { * *****************************************************************************/ -int process_command_line(const char * arg, char * filename, size_t bufsz) { +int process_command_line(const char * arg) { int ifail = -1; /* The first character should be alphabetical */ if (isalpha(arg[0])) { - size_t len = strnlen(arg, bufsz-1); - for (size_t i = 0; i < len; i++) { + for (size_t i = 0; i < strnlen(arg, FILENAME_MAX); i++) { const char c = arg[i]; - filename[i] = '_'; - if (isalnum(c) || c == '_' || c == '-' || c == '.') filename[i] = c; + ifail = -1; + if (isalnum(c) || c == '_' || c == '-' || c == '.') ifail = 0; } - filename[len] = '\0'; - ifail = strncmp(arg, filename, bufsz-1); + } + + if (ifail != 0) { + printf("Input file name: %s\n" + "Please use a posix file name with only alphanumeric\n" + "characters or _ or - or .\n", arg); } return ifail; From fd7241e663b42f8ebe8366d0dcd6a8f6c58ec1c3 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 21 Nov 2022 17:44:20 +0000 Subject: [PATCH 136/244] Remove unused code --- src/io_harness.c | 66 ++++++++------------------------------------ src/io_harness.h | 19 +------------ tests/unit/test_io.c | 12 ++++---- 3 files changed, 18 insertions(+), 79 deletions(-) diff --git a/src/io_harness.c b/src/io_harness.c index de271fca7..d321521a4 100644 --- a/src/io_harness.c +++ b/src/io_harness.c @@ -34,6 +34,7 @@ #include "pe.h" #include "util.h" +#include "util_fopen.h" #include "coords_s.h" #include "leesedwards.h" #include "io_harness.h" @@ -99,51 +100,6 @@ int io_info_create(pe_t * pe, cs_t * cs, io_info_args_t * arg, io_info_t ** p) { return 0; } -/***************************************************************************** - * - * io_info_create_impl - * - * Create and initialise an io_info_t. - * - *****************************************************************************/ - -__host__ int io_info_create_impl(pe_t * pe, cs_t * cs, io_info_args_t args, - const io_implementation_t * impl, - io_info_t ** info) { - io_info_t * p = NULL; - - assert(pe); - assert(cs); - assert(info); - - p = (io_info_t *) calloc(1, sizeof(io_info_t)); - assert(p); - - if (p == NULL) pe_fatal(pe, "Failed to allocate io_info_t struct\n"); - - /* Retain pointers to the parallel environment, coordinate system. - * Copy the argument and implementation details (assumed correct here). - * Create decomposition from the grid */ - - p->pe = pe; - p->cs = cs; - - p->args = args; - p->impl = *impl; - - io_decomposition_create(pe, cs, args.grid, &p->comm); - - /* Local rank and group counts */ - /* Root stores max group size in case of irregular decomposition */ - - p->nsites = p->comm->nsite[X]*p->comm->nsite[Y]*p->comm->nsite[Z]; - MPI_Reduce(&p->nsites, &p->maxlocal, 1, MPI_INT, MPI_MAX, 0, p->comm->comm); - - *info = p; - - return 0; -} - /***************************************************************************** * * io_decomposition_create @@ -466,7 +422,7 @@ int io_write_metadata_file(io_info_t * info, char * filename_stub) { ny = info->io_comm->ngroup[Y]; nz = info->io_comm->ngroup[Z]; - fp_meta = fopen(filename_io, "w"); + fp_meta = util_fopen(filename_io, "w"); if (fp_meta == NULL) pe_fatal(info->pe, "fopen(%s) failed\n", filename_io); fprintf(fp_meta, "Metadata for file set prefix: %s\n", filename_stub); @@ -713,10 +669,10 @@ static __host__ int io_info_bytesize(io_info_t * info, switch (iorformat) { case IO_RECORD_ASCII: - *bs = info->impl.bytesize_ascii; + *bs = info->bytesize_ascii; break; case IO_RECORD_BINARY: - *bs = info->impl.bytesize_binary; + *bs = info->bytesize_binary; break; default: err = 1; @@ -837,7 +793,7 @@ int io_write_data_p(io_info_t * obj, const char * filename_stub, void * data) { if (obj->io_comm->rank == 0) { /* Open the file anew */ - fp_state = fopen(filename_io, "wb"); + fp_state = util_fopen(filename_io, "wb"); } else { @@ -847,7 +803,7 @@ int io_write_data_p(io_info_t * obj, const char * filename_stub, void * data) { MPI_Recv(&token, 1, MPI_INT, obj->io_comm->rank - 1, io_tag, obj->io_comm->comm, &status); - fp_state = fopen(filename_io, "ab"); + fp_state = util_fopen(filename_io, "ab"); } if (fp_state == NULL) pe_fatal(obj->pe, "Failed to open %s\n", filename_io); @@ -936,7 +892,7 @@ int io_write_data_s(io_info_t * obj, const char * filename_stub, void * data) { localsz = itemsz*nlocal[X]*nlocal[Y]*nlocal[Z]; buf = (char *) malloc(localsz*sizeof(char)); - fp_buf = fopen("/dev/null", "w"); /* TODO: de-hardwire this */ + fp_buf = util_fopen("/dev/null", "w"); /* TODO: de-hardwire this */ setvbuf(fp_buf, buf, _IOFBF, localsz); if (buf == NULL || fp_buf == NULL) { @@ -987,13 +943,13 @@ int io_write_data_s(io_info_t * obj, const char * filename_stub, void * data) { * before allowing other groups to write at appropriate offset. */ if (obj->io_comm->index == 0) { - fp_state = fopen(filename_io, "w"); + fp_state = util_fopen(filename_io, "w"); } MPI_Bcast(&itemsz, 1, MPI_INT, 0, obj->io_comm->xcomm); if (obj->io_comm->index > 0) { - fp_state = fopen(filename_io, "r+"); + fp_state = util_fopen(filename_io, "r+"); offset = (long int) itemsz* obj->io_comm->offset[X]*obj->io_comm->nsite[Y]*obj->io_comm->nsite[Z]; fseek(fp_state, offset, SEEK_SET); @@ -1122,7 +1078,7 @@ int io_read_data(io_info_t * obj, const char * filename_stub, void * data) { if (obj->io_comm->rank == 0) { - fp_state = fopen(filename_io, "r"); + fp_state = util_fopen(filename_io, "r"); } else { @@ -1131,7 +1087,7 @@ int io_read_data(io_info_t * obj, const char * filename_stub, void * data) { MPI_Recv(&token, 1, MPI_LONG, obj->io_comm->rank - 1, io_tag, obj->io_comm->comm, &status); - fp_state = fopen(filename_io, "r"); + fp_state = util_fopen(filename_io, "r"); } if (fp_state == NULL) pe_fatal(obj->pe, "Failed to open %s\n", filename_io); diff --git a/src/io_harness.h b/src/io_harness.h index 5ade2f674..d6002f69a 100644 --- a/src/io_harness.h +++ b/src/io_harness.h @@ -8,7 +8,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2007-2020 The University of Edinburgh + * (c) 2007-2022 The University of Edinburgh * * Contributin authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -32,22 +32,11 @@ typedef enum io_format_enum {IO_FORMAT_NULL, IO_FORMAT_BINARY_SERIAL, IO_FORMAT_DEFAULT} io_format_enum_t; -typedef struct io_implementation_s io_implementation_t; typedef struct io_info_s io_info_t; /* Callback signature for lattice site I/O */ typedef int (*io_rw_cb_ft)(FILE * fp, int index, void * self); -struct io_implementation_s { - char name[BUFSIZ]; /* Descriptive name */ - io_rw_cb_ft write_ascii; /* Callback function for ascii write */ - io_rw_cb_ft write_binary; /* Callback function for binary write */ - io_rw_cb_ft read_ascii; /* Callback function for ascii read */ - io_rw_cb_ft read_binary; /* Callback function for binary read */ - size_t bytesize_ascii; /* Bytes per ascii read */ - size_t bytesize_binary; /* Bytes per binary read */ -}; - typedef struct io_decomposition_s io_decomposition_t; struct io_decomposition_s { @@ -68,7 +57,6 @@ struct io_info_s { cs_t * cs; io_info_args_t args; - io_implementation_t impl; io_decomposition_t * comm; io_decomposition_t * io_comm; @@ -94,11 +82,6 @@ struct io_info_s { __host__ int io_info_create(pe_t * pe, cs_t * cs, io_info_args_t * arg, io_info_t ** pinfo); __host__ int io_info_free(io_info_t *); - -__host__ int io_info_create_impl(pe_t * pe, cs_t * cs, io_info_args_t arg, - const io_implementation_t * impl, - io_info_t ** info); - __host__ int io_info_input_bytesize(io_info_t * info, size_t * bs); __host__ int io_info_output_bytesize(io_info_t * info, size_t * bs); diff --git a/tests/unit/test_io.c b/tests/unit/test_io.c index 3e666d7c4..8fbea0b51 100644 --- a/tests/unit/test_io.c +++ b/tests/unit/test_io.c @@ -35,9 +35,9 @@ static int test_io_read1(FILE *, int index, void * self); static int test_io_write1(FILE *, int index, void * self); static int test_io_read3(FILE *, int index, void * self); static int test_io_write3(FILE *, int index, void * self); - +#ifdef OLDSHIT __host__ int test_io_info_create_impl_a(pe_t * pe, cs_t * cs); - +#endif /***************************************************************************** * * test_io_suite @@ -54,9 +54,9 @@ int test_io_suite(void) { cs_init(cs); do_test_io_info_struct(pe, cs); - +#ifdef OLDSHIT test_io_info_create_impl_a(pe, cs); - +#endif pe_info(pe, "PASS ./unit/test_io\n"); cs_free(cs); pe_free(pe); @@ -140,7 +140,7 @@ int do_test_io_info_struct(pe_t * pe, cs_t * cs) { * test_io_info_create_impl_b * *****************************************************************************/ - +#ifdef OLDSHIT __host__ int test_io_info_create_impl_a(pe_t * pe, cs_t * cs) { io_info_args_t args = io_info_args_default(); @@ -196,7 +196,7 @@ __host__ int test_io_info_create_impl_a(pe_t * pe, cs_t * cs) { return 0; } - +#endif /***************************************************************************** * * test_write_1 From c0483618e4a0b0256611fd4c04a8526aef2a86a1 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 21 Nov 2022 17:45:50 +0000 Subject: [PATCH 137/244] Add new read/write functions --- src/lb_data.h | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/lb_data.h b/src/lb_data.h index 827b88a50..6a495924e 100644 --- a/src/lb_data.h +++ b/src/lb_data.h @@ -24,7 +24,8 @@ #include "lb_data_options.h" #include "lb_model.h" -#include "io_harness.h" +#include "io_impl.h" +#include "io_harness.h" /* Scheduled for removal. Use io_impl.h */ #include "halo_swap.h" /* Residual compile-time switches scheduled for removal */ @@ -41,6 +42,8 @@ enum {NDIM = 3, NVEL = 19}; enum {NDIM = 3, NVEL = 27}; #endif +#define NVELMAX 27 + typedef struct lb_collide_param_s lb_collide_param_t; typedef struct lb_halo_s lb_halo_t; typedef struct lb_data_s lb_t; @@ -151,19 +154,20 @@ __host__ int lb_io_info(lb_t * lb, io_info_t ** io_info); __host__ int lb_io_info_set(lb_t * lb, io_info_t * io_info, int fin, int fout); __host__ int lb_io_rho_set(lb_t *lb, io_info_t * io_rho, int fin, int fout); -__host__ int lb_io_info_commit(lb_t * lb, io_info_args_t args); - __host__ __device__ int lb_ndist(lb_t * lb, int * ndist); __host__ __device__ int lb_f(lb_t * lb, int index, int p, int n, double * f); __host__ __device__ int lb_f_set(lb_t * lb, int index, int p, int n, double f); __host__ __device__ int lb_0th_moment(lb_t * lb, int index, lb_dist_enum_t nd, double * rho); -/* These could be __host__ __device__ pending removal of - * static constants */ __host__ int lb_init_rest_f(lb_t * lb, double rho0); __host__ int lb_1st_moment(lb_t * lb, int index, lb_dist_enum_t nd, double g[3]); __host__ int lb_2nd_moment(lb_t * lb, int index, lb_dist_enum_t nd, double s[3][3]); __host__ int lb_1st_moment_equilib_set(lb_t * lb, int index, double rho, double u[3]); +__host__ int lb_read_buf(lb_t * lb, int index, const char * buf); +__host__ int lb_read_buf_asc(lb_t * lb, int index, const char * buf); +__host__ int lb_write_buf(const lb_t * lb, int index, char * buf); +__host__ int lb_write_buf_asc(const lb_t * lb, int index, char * buf); + #endif From 73c95b77d03b50d0edf393ca0fd870cf165e940b Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 21 Nov 2022 17:46:16 +0000 Subject: [PATCH 138/244] Add initialiser --- src/lb_data_options.c | 19 +++++++++++++++++++ src/lb_data_options.h | 2 ++ 2 files changed, 21 insertions(+) diff --git a/src/lb_data_options.c b/src/lb_data_options.c index 4fada0176..65391a154 100644 --- a/src/lb_data_options.c +++ b/src/lb_data_options.c @@ -38,6 +38,25 @@ lb_data_options_t lb_data_options_default(void) { return opts; } +/***************************************************************************** + * + * lb_data_options_ndim_nvel_ndist + * + *****************************************************************************/ + +lb_data_options_t lb_data_options_ndim_nvel_ndist(int ndim, int nvel, + int ndist) { + + lb_data_options_t opts = lb_data_options_default(); + + /* There are no checks at this points ... */ + opts.ndim = ndim; + opts.nvel = nvel; + opts.ndist = ndist; + + return opts; +} + /***************************************************************************** * * lb_data_options_valid diff --git a/src/lb_data_options.h b/src/lb_data_options.h index 9b792a5f2..6c83722c7 100644 --- a/src/lb_data_options.h +++ b/src/lb_data_options.h @@ -42,6 +42,8 @@ struct lb_data_options_s { }; lb_data_options_t lb_data_options_default(void); +lb_data_options_t lb_data_options_ndim_nvel_ndist(int ndim, int nvel, + int ndist); int lb_data_options_valid(const lb_data_options_t * opts); #endif From 329dfa0e7f720b480870a1ca4fbfcfc4d0d0f3e8 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 21 Nov 2022 17:46:36 +0000 Subject: [PATCH 139/244] Add return code --- src/io_impl.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/io_impl.c b/src/io_impl.c index 85cb65df1..7c1a8b73c 100644 --- a/src/io_impl.c +++ b/src/io_impl.c @@ -14,24 +14,37 @@ * *****************************************************************************/ +#include + #include "io_impl.h" #include "io_impl_mpio.h" int io_impl_create(const io_metadata_t * metadata, io_impl_t ** io) { + int ifail = 0; + + assert(io); + *io = NULL; switch (metadata->options.mode) { + case IO_MODE_SINGLE: + ifail = -1; + break; + case IO_MODE_MULTIPLE: + ifail = -1; + break; case IO_MODE_MPIIO: { io_impl_mpio_t * mpio = NULL; - io_impl_mpio_create(metadata, &mpio); + ifail = io_impl_mpio_create(metadata, &mpio); *io = (io_impl_t *) mpio; } break; default: - ; + /* Internal error */ + assert(0); } - return 0; + return ifail; } From de9a7a27a1b2650b740bddf47c1ba780496e8dfe Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 21 Nov 2022 17:47:00 +0000 Subject: [PATCH 140/244] Add new read/write functions --- src/model.c | 141 +++++++++++++++++++++++++++------- tests/unit/test_model.c | 164 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 271 insertions(+), 34 deletions(-) diff --git a/src/model.c b/src/model.c index 1bf842b0f..91abdde8a 100644 --- a/src/model.c +++ b/src/model.c @@ -399,34 +399,6 @@ static int lb_mpi_init(lb_t * lb) { return 0; } -/***************************************************************************** - * - * lb_io_info_commit - * - *****************************************************************************/ - -__host__ int lb_io_info_commit(lb_t * lb, io_info_args_t args) { - - io_implementation_t impl = {0}; - - assert(lb); - assert(lb->io_info == NULL); - - sprintf(impl.name, "%1d x Distribution: d%dq%d", lb->ndist, lb->ndim, - lb->nvel); - - impl.write_ascii = lb_f_write_ascii; - impl.read_ascii = lb_f_read_ascii; - impl.write_binary = lb_f_write; - impl.read_binary = lb_f_read; - impl.bytesize_ascii = 0; /* HOW MANY BYTES! */ - impl.bytesize_binary = sizeof(double)*lb->ndist*lb->nvel; - - io_info_create_impl(lb->pe, lb->cs, args, &impl, &lb->io_info); - - return 0; -} - /***************************************************************************** * * lb_io_info_set @@ -1430,3 +1402,116 @@ int lb_halo_free(lb_t * lb, lb_halo_t * h) { return 0; } + +/***************************************************************************** + * + * lb_write_buf + * + * Write output buffer independent of in-memory order. + * + *****************************************************************************/ + +int lb_write_buf(const lb_t * lb, int index, char * buf) { + + double data[NVELMAX] = {0}; + + assert(lb); + assert(buf); + + for (int n = 0; n < lb->ndist; n++) { + size_t sz = lb->model.nvel*sizeof(double); + for (int p = 0; p < lb->model.nvel; p++) { + int laddr = LB_ADDR(lb->nsite, lb->ndist, lb->model.nvel, index, n, p); + data[p] = lb->f[laddr]; + } + memcpy(buf + n*sz, data, sz); + } + + return 0; +} + +/***************************************************************************** + * + * lb_read_buf + * + *****************************************************************************/ + +int lb_read_buf(lb_t * lb, int index, const char * buf) { + + double data[NVELMAX] = {0}; + + assert(lb); + assert(buf); + + for (int n = 0; n < lb->ndist; n++) { + size_t sz = lb->model.nvel*sizeof(double); + memcpy(data, buf + n*sz, sz); + for (int p = 0; p < lb->model.nvel; p++) { + int laddr = LB_ADDR(lb->nsite, lb->ndist, lb->model.nvel, index, n, p); + lb->f[laddr] = data[p]; + } + } + + return 0; +} + +/***************************************************************************** + * + * lb_write_buf_asc + * + * For ascii, we are going to put ndist distributions on a single line... + * This is merely cosmetic, and for appearances. + * + *****************************************************************************/ + +int lb_write_buf_asc(const lb_t * lb, int index, char * buf) { + + const int nbyte = 23; /* bytes per " %22.15s" datum */ + int ifail = 0; + + assert(lb); + assert(buf); + assert((lb->ndist*nbyte + 1)*sizeof(char) < BUFSIZ); + + for (int p = 0; p < lb->model.nvel; p++) { + char tmp[BUFSIZ] = {0}; + size_t poffset = p*(lb->ndist*nbyte + 1); /* +1 for each newline */ + for (int n = 0; n < lb->ndist; n++) { + int laddr = LB_ADDR(lb->nsite, lb->ndist, lb->model.nvel, index, n, p); + int np = snprintf(tmp, nbyte + 1, " %22.15e", lb->f[laddr]); + if (np != nbyte) ifail = 1; + memcpy(buf + poffset + n*nbyte, tmp, nbyte*sizeof(char)); + } + /* Add newline */ + if (1 != snprintf(tmp, 2, "\n")) ifail = 2; + memcpy(buf + poffset + lb->ndist*nbyte, tmp, sizeof(char)); + } + + return ifail; +} + +/***************************************************************************** + * + * lb_read_buf_asc + * + *****************************************************************************/ + +int lb_read_buf_asc(lb_t * lb, int index, const char * buf) { + + const int nbyte = 23; /* bytes per " %22.15s" datum */ + int ifail = 0; + + assert(lb); + assert(buf); + + for (int p = 0; p < lb->model.nvel; p++) { + size_t poffset = p*(lb->ndist*nbyte + 1); /* +1 for each newline */ + for (int n = 0; n < lb->ndist; n++) { + int laddr = LB_ADDR(lb->nsite, lb->ndist, lb->model.nvel, index, n, p); + int nr = sscanf(buf + poffset + n*nbyte, "%le", lb->f + laddr); + if (nr != 1) ifail = 1; + } + } + + return ifail; +} diff --git a/tests/unit/test_model.c b/tests/unit/test_model.c index 7a8bc3cd2..a25f7fe7e 100644 --- a/tests/unit/test_model.c +++ b/tests/unit/test_model.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "pe.h" #include "coords.h" @@ -34,6 +35,11 @@ int do_test_model_distributions(pe_t * pe, cs_t * cs); int do_test_model_halo_swap(pe_t * pe, cs_t * cs); int do_test_model_reduced_halo_swap(pe_t * pe, cs_t * cs); int do_test_lb_model_io(pe_t * pe, cs_t * cs); + +int test_lb_data_write(pe_t * pe, cs_t * cs); +int test_lb_write_buf(pe_t * pe, cs_t * cs, const lb_data_options_t * opt); +int test_lb_write_buf_asc(pe_t * pe, cs_t * cs, const lb_data_options_t * opt); + static int test_model_is_domain(cs_t * cs, int ic, int jc, int kc); @@ -291,7 +297,8 @@ int test_model_suite(void) { do_test_model_distributions(pe, cs); do_test_model_halo_swap(pe, cs); do_test_model_reduced_halo_swap(pe, cs); - do_test_lb_model_io(pe, cs); + + test_lb_data_write(pe, cs); pe_info(pe, "PASS ./unit/test_model\n"); cs_free(cs); @@ -613,19 +620,164 @@ static int test_model_is_domain(cs_t * cs, int ic, int jc, int kc) { /***************************************************************************** * - * do_test_lb_model_io + * test_lb_data_write + * + *****************************************************************************/ + +int test_lb_data_write(pe_t * pe, cs_t * cs) { + + assert(NVELMAX == 27); + + { + lb_data_options_t opts = lb_data_options_ndim_nvel_ndist(2, 9, 1); + test_lb_write_buf(pe, cs, &opts); + test_lb_write_buf_asc(pe, cs, &opts); + } + + { + lb_data_options_t opts = lb_data_options_ndim_nvel_ndist(3, 15, 1); + test_lb_write_buf(pe, cs, &opts); + test_lb_write_buf_asc(pe, cs, &opts); + } + + { + lb_data_options_t opts = lb_data_options_ndim_nvel_ndist(3, 19, 1); + test_lb_write_buf(pe, cs, &opts); + test_lb_write_buf_asc(pe, cs, &opts); + } + + { + /* As D3Q19 is typically what was used for ndist = 2, here it is ... */ + lb_data_options_t opts = lb_data_options_ndim_nvel_ndist(3, 19, 2); + test_lb_write_buf(pe, cs, &opts); + test_lb_write_buf_asc(pe, cs, &opts); + } + + { + lb_data_options_t opts = lb_data_options_ndim_nvel_ndist(3, 27, 1); + test_lb_write_buf(pe, cs, &opts); + test_lb_write_buf_asc(pe, cs, &opts); + } + + return 0; +} + +/***************************************************************************** + * + * test_lb_write_buf + * + * It is convenient to test lb_read_buf() at the same time. + * + *****************************************************************************/ + +int test_lb_write_buf(pe_t * pe, cs_t * cs, const lb_data_options_t * opts) { + + lb_t * lb = NULL; + char buf[BUFSIZ] = {0}; + + assert(pe); + assert(cs); + assert(opts); + + lb_data_create(pe, cs, opts, &lb); + + assert(lb->ndist*lb->model.nvel*sizeof(double) < sizeof(buf)); + + { + /* Set some data at position (2,3,4) */ + int index = cs_index(cs, 2, 3, 4); + + for (int n = 0; n < lb->ndist; n++) { + for (int p = 0; p < lb->model.nvel; p++) { + double f = 1.0*(1 + n*lb->model.nvel + p); /* Test data, avoid zero */ + lb_f_set(lb, index, p, n, f); + } + } + + lb_write_buf(lb, index, buf); + } + + { + /* Read same buf in a different location */ + int index = cs_index(cs, 3, 4, 5); + lb_read_buf(lb, index, buf); + + /* Check the result in new position */ + for (int n = 0; n < lb->ndist; n++) { + for (int p = 0; p < lb->model.nvel; p++) { + double fref = 1.0*(1 + n*lb->model.nvel + p); + double f = -1.0; + lb_f(lb, index, p, n, &f); + assert(fabs(f - fref) < DBL_EPSILON); + } + } + } + + lb_free(lb); + + return 0; +} + +/***************************************************************************** + * + * test_lb_write_buf_asc * *****************************************************************************/ -int do_test_lb_model_io(pe_t * pe, cs_t * cs) { +int test_lb_write_buf_asc(pe_t * pe, cs_t * cs, + const lb_data_options_t * opts) { + + lb_t * lb = NULL; + char buf[BUFSIZ] = {0}; assert(pe); assert(cs); + assert(opts); + + /* Size of ascii record musst fir in buffer ... */ + assert(opts->nvel*(opts->ndist*23 + 1) < BUFSIZ); + + lb_data_create(pe, cs, opts, &lb); + + /* Write some data */ + + { + /* Set some data at position (2,3,4) */ + int index = cs_index(cs, 2, 3, 4); + + for (int n = 0; n < lb->ndist; n++) { + for (int p = 0; p < lb->model.nvel; p++) { + double f = 1.0*(1 + n*lb->model.nvel + p); /* Test data, avoid zero */ + lb_f_set(lb, index, p, n, f); + } + } - /* Write */ - /* Read */ + lb_write_buf_asc(lb, index, buf); - /* Compare */ + { + /* Have we got the correct size? */ + size_t sz = lb->model.nvel*(lb->ndist*23 + 1)*sizeof(char); + assert(sz == strnlen(buf, BUFSIZ)); + } + } + + { + /* Read back in different memory position */ + int index = cs_index(cs, 4, 5, 6); + lb_read_buf_asc(lb, index, buf); + + /* Check the result in new position */ + for (int n = 0; n < lb->ndist; n++) { + for (int p = 0; p < lb->model.nvel; p++) { + double fref = 1.0*(1 + n*lb->model.nvel + p); + double f = -1.0; + lb_f(lb, index, p, n, &f); + assert(fabs(f - fref) < DBL_EPSILON); + } + } + } + + lb_free(lb); return 0; } From 6f6b87e407697973140afc68fd4ca7a01365fc81 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 21 Nov 2022 17:56:40 +0000 Subject: [PATCH 141/244] Correct size_t problem --- src/model.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/model.c b/src/model.c index 91abdde8a..41e3e4dbd 100644 --- a/src/model.c +++ b/src/model.c @@ -1475,7 +1475,7 @@ int lb_write_buf_asc(const lb_t * lb, int index, char * buf) { for (int p = 0; p < lb->model.nvel; p++) { char tmp[BUFSIZ] = {0}; - size_t poffset = p*(lb->ndist*nbyte + 1); /* +1 for each newline */ + int poffset = p*(lb->ndist*nbyte + 1); /* +1 for each newline */ for (int n = 0; n < lb->ndist; n++) { int laddr = LB_ADDR(lb->nsite, lb->ndist, lb->model.nvel, index, n, p); int np = snprintf(tmp, nbyte + 1, " %22.15e", lb->f[laddr]); @@ -1505,7 +1505,7 @@ int lb_read_buf_asc(lb_t * lb, int index, const char * buf) { assert(buf); for (int p = 0; p < lb->model.nvel; p++) { - size_t poffset = p*(lb->ndist*nbyte + 1); /* +1 for each newline */ + int poffset = p*(lb->ndist*nbyte + 1); /* +1 for each newline */ for (int n = 0; n < lb->ndist; n++) { int laddr = LB_ADDR(lb->nsite, lb->ndist, lb->model.nvel, index, n, p); int nr = sscanf(buf + poffset + n*nbyte, "%le", lb->f + laddr); From 0ec062ffc678b9edae41166074e08dcc64d10185 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 21 Nov 2022 17:57:09 +0000 Subject: [PATCH 142/244] Update actions to v2 --- .github/workflows/codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 5236a7baa..b714e1725 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -32,7 +32,7 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'cpp', 'python' ] + language: [ 'cpp' ] steps: - name: Checkout repository @@ -40,7 +40,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} queries: +security-and-quality @@ -53,4 +53,4 @@ jobs: make - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 From 3131fe06fd258e6da89e8674b70562ecee4c3df0 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 22 Nov 2022 08:48:27 +0000 Subject: [PATCH 143/244] Try removing isnan ifdef --- src/util_cJSON.c | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/util_cJSON.c b/src/util_cJSON.c index c27c75501..8214bbe47 100644 --- a/src/util_cJSON.c +++ b/src/util_cJSON.c @@ -69,6 +69,7 @@ #endif #define false ((cJSON_bool)0) +#ifdef TRY_WITHOUT /* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ #ifndef isinf #define isinf(d) (isnan((d - d)) && !isnan(d)) @@ -76,6 +77,7 @@ #ifndef isnan #define isnan(d) (d != d) #endif +#endif /* TRY_WITHOUT */ #ifndef NAN #ifdef _WIN32 @@ -3062,31 +3064,27 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons { cJSON *a_element = NULL; cJSON *b_element = NULL; - cJSON_ArrayForEach(a_element, a) - { - /* TODO This has O(n^2) runtime, which is horrible! */ - b_element = get_object_item(b, a_element->string, case_sensitive); - if (b_element == NULL) { - return false; - } - - if (!cJSON_Compare(a_element, b_element, case_sensitive)) { - return false; - } + cJSON_ArrayForEach(a_element, a) { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) { + return false; + } + if (!cJSON_Compare(a_element, b_element, case_sensitive)) { + return false; + } } /* doing this twice, once on a and b to prevent true comparison if a subset of b * TODO: Do this the proper way, this is just a fix for now */ - cJSON_ArrayForEach(b_element, b) - { - a_element = get_object_item(a, b_element->string, case_sensitive); - if (a_element == NULL) { - return false; - } - - if (!cJSON_Compare(b_element, a_element, case_sensitive)) { - return false; - } + cJSON_ArrayForEach(b_element, b) { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) { + return false; + } + if (!cJSON_Compare(b_element, a_element, case_sensitive)) { + return false; + } } return true; From 3a62618916375da5aff5a9a38b1a88a4aa4cd5c2 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 25 Nov 2022 09:37:32 +0000 Subject: [PATCH 144/244] Additions to new i/o methods --- src/field.c | 111 +++++++++++++++++-- src/field.h | 16 ++- src/io_event.h | 27 +++++ src/io_metadata.c | 67 +++++++++++- src/io_metadata.h | 9 +- src/lb_data.h | 15 ++- src/lb_data_options.c | 4 +- src/lb_data_options.h | 2 +- src/model.c | 102 ++++++++++++++++-- tests/unit/test_field.c | 148 ++++++++++++++++++++------ tests/unit/test_io_impl_mpio.c | 24 ++--- tests/unit/test_io_metadata.c | 35 +++++- tests/unit/test_model.c | 189 ++++++++++++++++++++++++++++----- tests/unit/tests.c | 1 + tests/unit/tests.h | 1 + 15 files changed, 645 insertions(+), 106 deletions(-) create mode 100644 src/io_event.h diff --git a/src/field.c b/src/field.c index d7be50f7f..6b0b4eedb 100644 --- a/src/field.c +++ b/src/field.c @@ -82,13 +82,7 @@ __host__ int field_create(pe_t * pe, cs_t * cs, lees_edw_t * le, if (obj == NULL) pe_fatal(pe, "calloc(obj) failed\n"); obj->nf = opts->ndata; - - obj->name = (char *) calloc(strlen(name) + 1, sizeof(char)); - assert(obj->name); - if (obj->name == NULL) pe_fatal(pe, "calloc(name) failed\n"); - - strncpy(obj->name, name, imin(strlen(name), BUFSIZ)); - obj->name[strlen(name)] = '\0'; + obj->name = name; obj->pe = pe; obj->cs = cs; @@ -111,8 +105,22 @@ __host__ int field_create(pe_t * pe, cs_t * cs, lees_edw_t * le, .datasize = sizeof(double), .count = obj->opts.ndata, .endian = io_endianness()}; - obj->ascii = elasc; - obj->binary = elbin; + { + /* Input metadata */ + int ifail = 0; + io_element_t element = {0}; + if (opts->iodata.input.iorformat == IO_RECORD_ASCII) element = elasc; + if (opts->iodata.input.iorformat == IO_RECORD_BINARY) element = elbin; + ifail = io_metadata_initialise(cs, &opts->iodata.input, &element, + &obj->iometadata_in); + assert(ifail == 0); /* FIXME run time failure */ + /* Output metadata */ + if (opts->iodata.output.iorformat == IO_RECORD_ASCII) element = elasc; + if (opts->iodata.output.iorformat == IO_RECORD_BINARY) element = elbin; + ifail = io_metadata_initialise(cs, &opts->iodata.output, &element, + &obj->iometadata_out); + assert(ifail == 0); /* FIXME run time failure please */ + } } if (obj->opts.haloverbose) field_halo_info(obj); @@ -145,7 +153,6 @@ __host__ int field_free(field_t * obj) { } if (obj->data) free(obj->data); - if (obj->name) free(obj->name); if (obj->halo) halo_swap_free(obj->halo); if (obj->info) io_info_free(obj->info); @@ -1634,3 +1641,87 @@ int field_halo_free(field_halo_t * h) { return 0; } + +/***************************************************************************** + * + * field_io_write + * + *****************************************************************************/ + +int field_io_write(field_t * field, int timestep, io_event_t * event) { + + const io_metadata_t * meta = &field->iometadata_out; + + /* old ANSI */ + if (meta->options.mode == IO_MODE_SINGLE) { + char filename[BUFSIZ] = {0}; + sprintf(filename, "%s-%8.8d", field->name, timestep); + io_write_data(field->info, filename, field); + } + + /* MPIIO only at the moment */ + + if (meta->options.mode == IO_MODE_MPIIO) { + + io_impl_t * io = NULL; + char filename[BUFSIZ] = {0}; + + io_subfile_name(&meta->subfile, field->name, timestep, filename, BUFSIZ); + io_impl_create(meta, &io); /* CAN FAIL */ + assert(io); + + field_io_aggr_pack(field, io->aggr); + + io->impl->write(io, filename); + + /* REPORT HERE >>>>> */ + + io->impl->free(&io); + } + + return 0; +} + +/***************************************************************************** + * + * field_io_read + * + *****************************************************************************/ + +int field_io_read(field_t * field, int timestep, io_event_t * event) { + + assert(field); + assert(event); + + const io_metadata_t * meta = &field->iometadata_in; + + /* old ANSI */ + if (field->opts.iodata.input.mode == IO_MODE_SINGLE) { + char filename[BUFSIZ] = {0}; + sprintf(filename, "%s-%8.8d", field->name, timestep); + io_read_data(field->info, filename, field); + } + + /* MPIIO only at the moment */ + + if (meta->options.mode == IO_MODE_MPIIO) { + + io_impl_t * io = NULL; + char filename[BUFSIZ] = {0}; + + io_subfile_name(&meta->subfile, field->name, timestep, filename, BUFSIZ); + + io_impl_create(meta, &io); /* CAN FAIL */ + assert(io); + + io->impl->read(io, filename); + + field_io_aggr_unpack(field, io->aggr); + + /* REPORT HERE >>>>> */ + + io->impl->free(&io); + } + + return 0; +} diff --git a/src/field.h b/src/field.h index f5fa7867c..41c03276b 100644 --- a/src/field.h +++ b/src/field.h @@ -22,8 +22,8 @@ #include "pe.h" #include "coords.h" -#include "io_element.h" -#include "io_aggregator.h" /* Aggregation buffer */ +#include "io_impl.h" +#include "io_event.h" #include "io_harness.h" /* To be removed in favour of refactored io */ #include "leesedwards.h" #include "halo_swap.h" @@ -56,16 +56,19 @@ struct field_s { int nhcomm; /* Halo width required */ int nsites; /* Local sites (allocated) */ double * data; /* Field data */ - char * name; /* "phi", "p", "q" etc. */ + const char * name; /* "phi", "p", "q" etc. */ double field_init_sum; /* field sum at the beginning */ pe_t * pe; /* Parallel environment */ cs_t * cs; /* Coordinate system */ lees_edw_t * le; /* Lees-Edwards */ - io_element_t ascii; /* I/O record information (ascii) */ - io_element_t binary; /* Binary */ + + io_metadata_t iometadata_in; /* Input details */ + io_metadata_t iometadata_out; /* Output details */ + io_info_t * info; /* I/O Handler (to be removed) */ + halo_swap_t * halo; /* Halo swap driver object */ field_halo_t h; /* Host halo */ field_options_t opts; /* Options */ @@ -116,4 +119,7 @@ int field_write_buf_ascii(field_t * field, int index, char * buf); int field_io_aggr_pack(field_t * field, io_aggregator_t * aggr); int field_io_aggr_unpack(field_t * field, const io_aggregator_t * aggr); +int field_io_write(field_t * field, int timestep, io_event_t * event); +int field_io_read(field_t * field, int timestep, io_event_t * event); + #endif diff --git a/src/io_event.h b/src/io_event.h new file mode 100644 index 000000000..9bbe38030 --- /dev/null +++ b/src/io_event.h @@ -0,0 +1,27 @@ +/***************************************************************************** + * + * io_event.h + * + * This is currently a placeholder to allow additional information + * to be provided to specific i/o read/write instances. + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#ifndef LUDWIG_IO_EVENT_H +#define LUDWIG_IO_EVENT_H + +typedef struct io_event_s io_event_t; + +struct io_event_s { + int isconfig; /* Is this a configuration step */ +}; + +#endif diff --git a/src/io_metadata.c b/src/io_metadata.c index 801bc4f3e..d8b3f7a0a 100644 --- a/src/io_metadata.c +++ b/src/io_metadata.c @@ -26,15 +26,71 @@ * * io_metadata_create * - * Generate once only per run as the cost of the MPI_Comm_split() - * should not be repeated. + * Note this can fail and return non-zero if there is no decomposition + * available for the given coordinate system/options. + * + * Success returns 0. * *****************************************************************************/ int io_metadata_create(cs_t * cs, const io_options_t * options, const io_element_t * element, - io_metadata_t * meta) { + io_metadata_t ** metadata) { + + io_metadata_t * meta = NULL; + + assert(cs); + assert(options); + assert(element); + assert(metadata); + + meta = (io_metadata_t *) calloc(1, sizeof(io_metadata_t)); + assert(meta); + if (meta == NULL) goto err; + + if (io_metadata_initialise(cs, options, element, meta) != 0) goto err; + + *metadata = meta; + + return 0; + + err: + + if (meta) free(meta); + return -1; +} + +/***************************************************************************** + * + * io_metadata_free + * + *****************************************************************************/ + +int io_metadata_free(io_metadata_t ** metadata) { + + assert(metadata); + + io_metadata_finalise(*metadata); + free(*metadata); + *metadata = NULL; + + return 0; +} + +/***************************************************************************** + * + * io_metadata_initialise + * + * Generate once only per run as the cost of the MPI_Comm_split() + * should not be repeated. + * + *****************************************************************************/ + +int io_metadata_initialise(cs_t * cs, + const io_options_t * options, + const io_element_t * element, + io_metadata_t * meta) { assert(cs); assert(options); assert(element); @@ -76,16 +132,17 @@ int io_metadata_create(cs_t * cs, /***************************************************************************** * - * io_metadata_free + * io_metadata_finalise * *****************************************************************************/ -int io_metadata_free(io_metadata_t * meta) { +int io_metadata_finalise(io_metadata_t * meta) { assert(meta); MPI_Comm_free(&meta->comm); *meta = (io_metadata_t) {0}; + meta->comm = MPI_COMM_NULL; return 0; } diff --git a/src/io_metadata.h b/src/io_metadata.h index 43e96df98..c22e25419 100644 --- a/src/io_metadata.h +++ b/src/io_metadata.h @@ -38,7 +38,12 @@ struct io_metadata_s { int io_metadata_create(cs_t * cs, const io_options_t * options, const io_element_t * element, - io_metadata_t * metadata); -int io_metadata_free(io_metadata_t * metadata); + io_metadata_t ** metadata); +int io_metadata_free(io_metadata_t ** metadata); +int io_metadata_initialise(cs_t * cs, + const io_options_t * options, + const io_element_t * element, + io_metadata_t * metadata); +int io_metadata_finalise(io_metadata_t * metadata); #endif diff --git a/src/lb_data.h b/src/lb_data.h index 6a495924e..5e32dce4d 100644 --- a/src/lb_data.h +++ b/src/lb_data.h @@ -43,6 +43,7 @@ enum {NDIM = 3, NVEL = 27}; #endif #define NVELMAX 27 +#define LB_RECORD_LENGTH_ASCII 23 typedef struct lb_collide_param_s lb_collide_param_t; typedef struct lb_halo_s lb_halo_t; @@ -105,9 +106,16 @@ struct lb_data_s { lb_model_t model; /* Current LB model information */ halo_swap_t * halo; /* halo swap driver */ + + /* io_info_t scheduled to be replaced. Use metadata types */ io_info_t * io_info; /* Distributions */ io_info_t * io_rho; /* Fluid density (here; could be hydrodynamics...) */ + io_element_t ascii; /* ASCII record description */ + io_element_t binary; /* Binary record description */ + io_metadata_t input; /* Metadata for io implementation (input) */ + io_metadata_t output; /* Ditto (for output) */ + double * f; /* Distributions */ double * fprime; /* used in propagation only */ @@ -166,8 +174,11 @@ __host__ int lb_2nd_moment(lb_t * lb, int index, lb_dist_enum_t nd, double s[3][ __host__ int lb_1st_moment_equilib_set(lb_t * lb, int index, double rho, double u[3]); __host__ int lb_read_buf(lb_t * lb, int index, const char * buf); -__host__ int lb_read_buf_asc(lb_t * lb, int index, const char * buf); +__host__ int lb_read_buf_ascii(lb_t * lb, int index, const char * buf); __host__ int lb_write_buf(const lb_t * lb, int index, char * buf); -__host__ int lb_write_buf_asc(const lb_t * lb, int index, char * buf); +__host__ int lb_write_buf_ascii(const lb_t * lb, int index, char * buf); + +__host__ int lb_io_aggr_pack(const lb_t * lb, io_aggregator_t * aggr); +__host__ int lb_io_aggr_unpack(lb_t * lb, const io_aggregator_t * aggr); #endif diff --git a/src/lb_data_options.c b/src/lb_data_options.c index 65391a154..d4f004f32 100644 --- a/src/lb_data_options.c +++ b/src/lb_data_options.c @@ -32,8 +32,8 @@ lb_data_options_t lb_data_options_default(void) { .halo = LB_HALO_TARGET, .reportimbalance = 0, .usefirsttouch = 0, - .data = io_info_args_default(), - .rho = io_info_args_default()}; + .iodata = io_info_args_default(), + .rho = io_info_args_default()}; return opts; } diff --git a/src/lb_data_options.h b/src/lb_data_options.h index 6c83722c7..7118b3dcd 100644 --- a/src/lb_data_options.h +++ b/src/lb_data_options.h @@ -37,7 +37,7 @@ struct lb_data_options_s { int reportimbalance; int usefirsttouch; - io_info_args_t data; + io_info_args_t iodata; io_info_args_t rho; }; diff --git a/src/model.c b/src/model.c index 41e3e4dbd..54b13c72c 100644 --- a/src/model.c +++ b/src/model.c @@ -133,6 +133,26 @@ int lb_data_create(pe_t * pe, cs_t * cs, const lb_data_options_t * options, lb_halo_create(obj, &obj->h, obj->haloscheme); lb_init(obj); + /* i/o metadata */ + { + io_element_t ascii = { + .datatype = MPI_CHAR, + .datasize = sizeof(char), + .count = obj->nvel*(1 + LB_RECORD_LENGTH_ASCII*obj->ndist), + .endian = io_endianness() + }; + + io_element_t binary = { + .datatype = MPI_DOUBLE, + .datasize = sizeof(double), + .count = obj->nvel*obj->ndist, + .endian = io_endianness() + }; + + obj->ascii = ascii; + obj->binary = binary; + } + *lb = obj; return 0; @@ -1457,16 +1477,16 @@ int lb_read_buf(lb_t * lb, int index, const char * buf) { /***************************************************************************** * - * lb_write_buf_asc + * lb_write_buf_ascii * * For ascii, we are going to put ndist distributions on a single line... * This is merely cosmetic, and for appearances. * *****************************************************************************/ -int lb_write_buf_asc(const lb_t * lb, int index, char * buf) { +int lb_write_buf_ascii(const lb_t * lb, int index, char * buf) { - const int nbyte = 23; /* bytes per " %22.15s" datum */ + const int nbyte = LB_RECORD_LENGTH_ASCII; /* bytes per " %22.15s" datum */ int ifail = 0; assert(lb); @@ -1492,13 +1512,13 @@ int lb_write_buf_asc(const lb_t * lb, int index, char * buf) { /***************************************************************************** * - * lb_read_buf_asc + * lb_read_buf_ascii * *****************************************************************************/ -int lb_read_buf_asc(lb_t * lb, int index, const char * buf) { +int lb_read_buf_ascii(lb_t * lb, int index, const char * buf) { - const int nbyte = 23; /* bytes per " %22.15s" datum */ + const int nbyte = LB_RECORD_LENGTH_ASCII; /* bytes per " %22.15s" datum */ int ifail = 0; assert(lb); @@ -1515,3 +1535,73 @@ int lb_read_buf_asc(lb_t * lb, int index, const char * buf) { return ifail; } + +/***************************************************************************** + * + * lb_io_aggr_pack + * + *****************************************************************************/ + +__host__ int lb_io_aggr_pack(const lb_t * lb, io_aggregator_t * aggr) { + + assert(lb); + assert(aggr); + assert(aggr->buf); + + #pragma omp parallel + { + int iasc = lb->opts.iodata.output.iorformat == IO_RECORD_ASCII; + int ibin = lb->opts.iodata.output.iorformat == IO_RECORD_BINARY; + assert(iasc ^ ibin); /* One or other */ + + #pragma omp for + for (int ib = 0; ib < cs_limits_size(aggr->lim); ib++) { + int ic = cs_limits_ic(aggr->lim, ib); + int jc = cs_limits_jc(aggr->lim, ib); + int kc = cs_limits_kc(aggr->lim, ib); + + /* Write data (ic,jc,kc) */ + int index = cs_index(lb->cs, ic, jc, kc); + int offset = ib*aggr->szelement; + if (iasc) lb_write_buf_ascii(lb, index, aggr->buf + offset); + if (ibin) lb_write_buf(lb, index, aggr->buf + offset); + } + } + + return 0; +} + +/***************************************************************************** + * + * lb_io_aggr_buf_unpack + * + *****************************************************************************/ + +__host__ int lb_io_aggr_unpack(lb_t * lb, const io_aggregator_t * aggr) { + + assert(lb); + assert(aggr); + assert(aggr->buf); + + #pragma omp parallel + { + int iasc = lb->opts.iodata.input.iorformat == IO_RECORD_ASCII; + int ibin = lb->opts.iodata.input.iorformat == IO_RECORD_BINARY; + assert(iasc ^ ibin); + + #pragma omp for + for (int ib = 0; ib < cs_limits_size(aggr->lim); ib++) { + int ic = cs_limits_ic(aggr->lim, ib); + int jc = cs_limits_jc(aggr->lim, ib); + int kc = cs_limits_kc(aggr->lim, ib); + + /* Read data at (ic,jc,kc) */ + int index = cs_index(lb->cs, ic, jc, kc); + int offset = ib*aggr->szelement; + if (iasc) lb_read_buf_ascii(lb, index, aggr->buf + offset); + if (ibin) lb_read_buf(lb, index, aggr->buf + offset); + } + } + + return 0; +} diff --git a/tests/unit/test_field.c b/tests/unit/test_field.c index 2a5e1ed92..0b7fc5f45 100644 --- a/tests/unit/test_field.c +++ b/tests/unit/test_field.c @@ -42,6 +42,10 @@ int test_field_write_buf(pe_t * pe); int test_field_write_buf_ascii(pe_t * pe); int test_field_io_aggr_pack(pe_t * pe); +int test_field_io_read_write(pe_t * pe); +int test_field_io_write(pe_t * pe, cs_t * cs, const field_options_t * opts); +int test_field_io_read(pe_t * pe, cs_t * cs, const field_options_t * opts); + int util_field_data_check(field_t * field); int util_field_data_check_set(field_t * field); @@ -73,10 +77,10 @@ int test_field_suite(void) { test_field_halo_create(pe); - /* Experimental ... */ test_field_write_buf(pe); test_field_write_buf_ascii(pe); test_field_io_aggr_pack(pe); + test_field_io_read_write(pe); pe_info(pe, "PASS ./unit/test_field\n"); pe_free(pe); @@ -84,6 +88,44 @@ int test_field_suite(void) { return 0; } +/***************************************************************************** + * + * test_field_io_read_write + * + * Driver for individual i/o routine tests. We actually do write then read. + * + *****************************************************************************/ + +int test_field_io_read_write(pe_t * pe) { + + int ntotal[3] = {32, 16, 8}; + cs_t * cs = NULL; + + cs_create(pe, &cs); + cs_ntotal_set(cs, ntotal); + cs_init(cs); + + /* ASCII */ + { + io_options_t io = io_options_with_format(IO_MODE_MPIIO, IO_RECORD_ASCII); + field_options_t opts = field_options_ndata_nhalo(3, 0); + opts.iodata.input = io; + opts.iodata.output = io; + + test_field_io_write(pe, cs, &opts); + /* FIXME test_field_io_read(pe, cs, &opts); */ + } + + /* Binary (default) */ + { + /* PENDING */ + } + + cs_free(cs); + + return 0; +} + /***************************************************************************** * * do_test0 @@ -619,50 +661,96 @@ int test_field_io_aggr_pack(pe_t * pe) { cs_init(cs); field_create(pe, cs, NULL, "test_field_io_aggr_pack", &options, &field); - /* This should be elsewhere as part of test_field_create() */ + /* Default options is binary (use output metadata) */ { - /* Note one can use == with pre-defined data types */ + const io_metadata_t * meta = &field->iometadata_out; + io_aggregator_t buf = {0}; + + io_aggregator_initialise(meta->element, meta->limits, &buf); + + util_field_data_check_set(field); + field_io_aggr_pack(field, &buf); - assert(field->ascii.datatype == MPI_CHAR); - assert(field->ascii.datasize == sizeof(char)); - assert(field->ascii.count == 1 + 23*nf); - assert(field->ascii.endian == io_endianness()); + /* Are the values in the buffer correct? */ + /* Clear existing values and unpack. */ - assert(field->binary.datatype == MPI_DOUBLE); - assert(field->binary.datasize == sizeof(double)); - assert(field->binary.count == nf); - assert(field->binary.endian == io_endianness()); + memset(field->data, 0, sizeof(double)*field->nsites*field->nf); + + field_io_aggr_unpack(field, &buf); + util_field_data_check(field); + + io_aggregator_finalise(&buf); } - { - /* Default is binary */ - int nlocal[3] = {0}; - cs_nlocal(cs, nlocal); - { - cs_limits_t lim = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; - io_aggregator_t buf = {0}; + /* Repeat for ASCII */ - io_aggregator_initialise(field->binary, lim, &buf); + field_free(field); + cs_free(cs); - util_field_data_check_set(field); - field_io_aggr_pack(field, &buf); + return 0; +} - /* Are the values in the buffer correct? */ - /* Clear existing values and unpack. */ +/***************************************************************************** + * + * test_field_io_write + * + *****************************************************************************/ - memset(field->data, 0, sizeof(double)*field->nsites*field->nf); +int test_field_io_write(pe_t * pe, cs_t * cs, const field_options_t * opts) { - field_io_aggr_unpack(field, &buf); - util_field_data_check(field); + field_t * field = NULL; - io_aggregator_finalise(&buf); - } + assert(pe); + assert(cs); + assert(opts); + + /* Establish data and test values. */ + + field_create(pe, cs, NULL, "test-field-io", opts, &field); + + util_field_data_check_set(field); + + /* Write */ + + { + int it = 0; + io_event_t event = {0}; + field_io_write(field, it, &event); } - /* Repeat for ASCII */ + field_free(field); + + return 0; +} + +/***************************************************************************** + * + * test_field_io_read + * + * This needs to be co-ordinated with test_field_io_write() above. + * + *****************************************************************************/ + +int test_field_io_read(pe_t * pe, cs_t * cs, const field_options_t * opts) { + + field_t * field = NULL; + + assert(pe); + assert(cs); + assert(opts); + + field_create(pe, cs, NULL, "test-field-io", opts, &field); + + { + int it = 0; /* matches time step zero in test_field_io_write() above */ + io_event_t event = {0}; + + field_io_read(field, it, &event); + + util_field_data_check(field); + } field_free(field); - cs_free(cs); return 0; } diff --git a/tests/unit/test_io_impl_mpio.c b/tests/unit/test_io_impl_mpio.c index bd2b60d88..22a7236be 100644 --- a/tests/unit/test_io_impl_mpio.c +++ b/tests/unit/test_io_impl_mpio.c @@ -78,7 +78,7 @@ int test_io_impl_mpio_suite(void) { const char * filename = "io-impl-mpio-asc.dat"; const char * afilename = "io-impl-mpio-async-asc.dat"; - io_metadata_create(cs, &opts, &element_asc, &metadata); + io_metadata_initialise(cs, &opts, &element_asc, &metadata); test_io_impl_mpio_create(cs, &metadata); test_io_impl_mpio_initialise(cs, &metadata); @@ -93,7 +93,7 @@ int test_io_impl_mpio_suite(void) { test_io_impl_mpio_write_end(&io); } - io_metadata_free(&metadata); + io_metadata_finalise(&metadata); MPI_Barrier(MPI_COMM_WORLD); if (pe_mpi_rank(pe) == 0) remove(filename); @@ -106,14 +106,14 @@ int test_io_impl_mpio_suite(void) { io_metadata_t metadata = {0}; const char * filename = "io-impl-mpio-bin.dat"; - io_metadata_create(cs, &opts, &element_bin, &metadata); + io_metadata_initialise(cs, &opts, &element_bin, &metadata); test_io_impl_mpio_create(cs, &metadata); test_io_impl_mpio_initialise(cs, &metadata); test_io_impl_mpio_write(cs, &metadata, filename); test_io_impl_mpio_read(cs, &metadata, filename); - io_metadata_free(&metadata); + io_metadata_finalise(&metadata); MPI_Barrier(MPI_COMM_WORLD); if (pe_mpi_rank(pe) == 0) remove(filename); @@ -126,24 +126,24 @@ int test_io_impl_mpio_suite(void) { io_record_format_enum_t ior = IO_RECORD_ASCII; int iosize[3] = {2, 1, 1}; io_options_t opts = io_options_with_iogrid(mode, ior, iosize); - io_metadata_t metadata = {0}; + io_metadata_t * metadata = NULL; const char * filestub = "io-impl-mpio"; char filename[BUFSIZ] = {0}; io_metadata_create(cs, &opts, &element_asc, &metadata); - io_subfile_name(&metadata.subfile, filestub, 0, filename, BUFSIZ); + io_subfile_name(&metadata->subfile, filestub, 0, filename, BUFSIZ); - test_io_impl_mpio_create(cs, &metadata); - test_io_impl_mpio_initialise(cs, &metadata); - test_io_impl_mpio_write(cs, &metadata, filename); - test_io_impl_mpio_read(cs, &metadata, filename); + test_io_impl_mpio_create(cs, metadata); + test_io_impl_mpio_initialise(cs, metadata); + test_io_impl_mpio_write(cs, metadata, filename); + test_io_impl_mpio_read(cs, metadata, filename); - MPI_Barrier(metadata.comm); + MPI_Barrier(metadata->comm); { /* Clean up */ int rank = -1; - MPI_Comm_rank(metadata.comm, &rank); + MPI_Comm_rank(metadata->comm, &rank); if (rank == 0) remove(filename); } diff --git a/tests/unit/test_io_metadata.c b/tests/unit/test_io_metadata.c index df798a62b..80cc81995 100644 --- a/tests/unit/test_io_metadata.c +++ b/tests/unit/test_io_metadata.c @@ -16,6 +16,7 @@ #include "io_metadata.h" +int test_io_metadata_initialise(cs_t * cs); int test_io_metadata_create(cs_t * cs); /***************************************************************************** @@ -34,6 +35,7 @@ int test_io_metadata_suite(void) { cs_create(pe, &cs); cs_init(cs); + test_io_metadata_initialise(cs); test_io_metadata_create(cs); pe_info(pe, "%-9s %s\n", "PASS", __FILE__); @@ -51,12 +53,40 @@ int test_io_metadata_suite(void) { int test_io_metadata_create(cs_t * cs) { + int ifail = 0; + io_options_t options = io_options_default(); + io_element_t element = {0}; + io_metadata_t * meta = NULL; + + assert(cs); + + ifail = io_metadata_create(cs, &options, &element, &meta); + assert(ifail == 0); + + assert(meta); + + io_metadata_free(&meta); + assert(meta == NULL); + + return 0; +} + +/***************************************************************************** + * + * test_io_metadata_initialise + * + *****************************************************************************/ + +int test_io_metadata_initialise(cs_t * cs) { + int ifail = 0; io_metadata_t metadata = {0}; io_element_t element = {0}; io_options_t options = io_options_default(); - ifail = io_metadata_create(cs, &options, &element, &metadata); + assert(cs); + + ifail = io_metadata_initialise(cs, &options, &element, &metadata); assert(ifail == 0); /* Bad decomposition */ /* Really want a proper compare method for components */ @@ -94,7 +124,8 @@ int test_io_metadata_create(cs_t * cs) { assert(myresult == MPI_CONGRUENT); } - io_metadata_free(&metadata); + io_metadata_finalise(&metadata); + assert(metadata.comm == MPI_COMM_NULL); return ifail; } diff --git a/tests/unit/test_model.c b/tests/unit/test_model.c index a25f7fe7e..02dd65eb4 100644 --- a/tests/unit/test_model.c +++ b/tests/unit/test_model.c @@ -37,8 +37,9 @@ int do_test_model_reduced_halo_swap(pe_t * pe, cs_t * cs); int do_test_lb_model_io(pe_t * pe, cs_t * cs); int test_lb_data_write(pe_t * pe, cs_t * cs); -int test_lb_write_buf(pe_t * pe, cs_t * cs, const lb_data_options_t * opt); -int test_lb_write_buf_asc(pe_t * pe, cs_t * cs, const lb_data_options_t * opt); +int test_lb_write_buf(pe_t * pe, cs_t * cs, const lb_data_options_t * opts); +int test_lb_write_buf_ascii(pe_t * pe, cs_t * cs, const lb_data_options_t * opts); +int test_lb_io_aggr_pack(pe_t * pe, cs_t * cs, const lb_data_options_t * opts); static int test_model_is_domain(cs_t * cs, int ic, int jc, int kc); @@ -52,12 +53,13 @@ static int test_model_is_domain(cs_t * cs, int ic, int jc, int kc); #include -int64_t lb_data_index(lb_t * lb, int ic, int jc, int kc, int p) { +int64_t lb_data_index(lb_t * lb, int ic, int jc, int kc, int n, int p) { int64_t index = INT64_MIN; int64_t nall[3] = {0}; int64_t nstr[3] = {0}; int64_t pstr = 0; + int64_t dstr = 0; int ntotal[3] = {0}; int offset[3] = {0}; @@ -65,6 +67,8 @@ int64_t lb_data_index(lb_t * lb, int ic, int jc, int kc, int p) { assert(lb); assert(0 <= p && p < lb->model.nvel); + assert(lb->ndist == 1 || lb->ndist == 2); + assert(0 <= n && n < lb->ndist); cs_ntotal(lb->cs, ntotal); cs_nlocal_offset(lb->cs, offset); @@ -77,6 +81,7 @@ int64_t lb_data_index(lb_t * lb, int ic, int jc, int kc, int p) { nstr[Y] = nstr[Z]*nall[Z]; nstr[X] = nstr[Y]*nall[Y]; pstr = nstr[X]*nall[X]; + dstr = pstr*lb->model.nvel; { int igl = offset[X] + ic; @@ -95,7 +100,7 @@ int64_t lb_data_index(lb_t * lb, int ic, int jc, int kc, int p) { assert(1 <= jgl && jgl <= ntotal[Y]); assert(1 <= kgl && kgl <= ntotal[Z]); - index = pstr*p + nstr[X]*igl + nstr[Y]*jgl + nstr[Z]*kgl; + index = dstr*n + pstr*p + nstr[X]*igl + nstr[Y]*jgl + nstr[Z]*kgl; } return index; @@ -120,10 +125,12 @@ int util_lb_data_check_set(lb_t * lb) { for (int ic = 1; ic <= nlocal[X]; ic++) { for (int jc = 1; jc <= nlocal[Y]; jc++) { for (int kc = 1; kc <= nlocal[Z]; kc++) { - for (int p = 0 ; p < lb->model.nvel; p++) { - int index = cs_index(lb->cs, ic, jc, kc); - int laddr = LB_ADDR(lb->nsite, lb->ndist, lb->nvel, index, 0, p); - lb->f[laddr] = 1.0*lb_data_index(lb, ic, jc, kc, p); + for (int n = 0; n < lb->ndist; n++) { + for (int p = 0 ; p < lb->model.nvel; p++) { + int index = cs_index(lb->cs, ic, jc, kc); + int laddr = LB_ADDR(lb->nsite, lb->ndist, lb->nvel, index, n, p); + lb->f[laddr] = 1.0*lb_data_index(lb, ic, jc, kc, n, p); + } } } } @@ -165,24 +172,69 @@ int util_lb_data_check(lb_t * lb, int full) { int index = cs_index(lb->cs, ic, jc, kc); - for (int p = 0; p < lb->model.nvel; p++) { + for (int n = 0; n < lb->ndist; n++) { + for (int p = 0; p < lb->model.nvel; p++) { - /* Look for propagating distributions (into domain). */ - int icdt = ic + lb->model.cv[p][X]; - int jcdt = jc + lb->model.cv[p][Y]; - int kcdt = kc + lb->model.cv[p][Z]; + /* Look for propagating distributions (into domain). */ + int icdt = ic + lb->model.cv[p][X]; + int jcdt = jc + lb->model.cv[p][Y]; + int kcdt = kc + lb->model.cv[p][Z]; - is_halo = (icdt < 1 || jcdt < 1 || kcdt < 1 || + is_halo = (icdt < 1 || jcdt < 1 || kcdt < 1 || icdt > nlocal[X] || jcdt > nlocal[Y] || kcdt > nlocal[Z]); - if (full || is_halo == 0) { + if (full || is_halo == 0) { + /* Check */ + int laddr = LB_ADDR(lb->nsite, lb->ndist, lb->nvel, index, n, p); + double fex = 1.0*lb_data_index(lb, ic, jc, kc, n, p); + if (fabs(fex - lb->f[laddr]) > DBL_EPSILON) ifail += 1; + assert(fabs(fex - lb->f[laddr]) < DBL_EPSILON); + } + } + } + /* Next (ic,jc,kc) */ + } + } + } + + return ifail; +} + +/***************************************************************************** + * + * util_lb_data_check_no_halo + * + * Examine non-halo values. + * + *****************************************************************************/ + +int util_lb_data_check_no_halo(lb_t * lb) { + + int ifail = 0; + int nlocal[3] = {0}; + + assert(lb); + + cs_nlocal(lb->cs, nlocal); + + /* Fix for 2d, where there should be no halo regions in Z */ + + for (int ic = 1; ic <= nlocal[X]; ic++) { + for (int jc = 1; jc <= nlocal[Y]; jc++) { + for (int kc = 1; kc <= nlocal[Z]; kc++) { + + int index = cs_index(lb->cs, ic, jc, kc); + + for (int n = 0; n < lb->ndist; n++) { + for (int p = 0; p < lb->model.nvel; p++) { /* Check */ - int laddr = LB_ADDR(lb->nsite, lb->ndist, lb->nvel, index, 0, p); - double fex = 1.0*lb_data_index(lb, ic, jc, kc, p); + int laddr = LB_ADDR(lb->nsite, lb->ndist, lb->nvel, index, n, p); + double fex = 1.0*lb_data_index(lb, ic, jc, kc, n, p); if (fabs(fex - lb->f[laddr]) > DBL_EPSILON) ifail += 1; assert(fabs(fex - lb->f[laddr]) < DBL_EPSILON); } } + /* Next (ic,jc,kc) */ } } } @@ -631,32 +683,37 @@ int test_lb_data_write(pe_t * pe, cs_t * cs) { { lb_data_options_t opts = lb_data_options_ndim_nvel_ndist(2, 9, 1); test_lb_write_buf(pe, cs, &opts); - test_lb_write_buf_asc(pe, cs, &opts); + test_lb_write_buf_ascii(pe, cs, &opts); + test_lb_io_aggr_pack(pe, cs, &opts); } { lb_data_options_t opts = lb_data_options_ndim_nvel_ndist(3, 15, 1); test_lb_write_buf(pe, cs, &opts); - test_lb_write_buf_asc(pe, cs, &opts); + test_lb_write_buf_ascii(pe, cs, &opts); + test_lb_io_aggr_pack(pe, cs, &opts); } { lb_data_options_t opts = lb_data_options_ndim_nvel_ndist(3, 19, 1); test_lb_write_buf(pe, cs, &opts); - test_lb_write_buf_asc(pe, cs, &opts); + test_lb_write_buf_ascii(pe, cs, &opts); + test_lb_io_aggr_pack(pe, cs, &opts); } { /* As D3Q19 is typically what was used for ndist = 2, here it is ... */ lb_data_options_t opts = lb_data_options_ndim_nvel_ndist(3, 19, 2); test_lb_write_buf(pe, cs, &opts); - test_lb_write_buf_asc(pe, cs, &opts); + test_lb_write_buf_ascii(pe, cs, &opts); + test_lb_io_aggr_pack(pe, cs, &opts); } { lb_data_options_t opts = lb_data_options_ndim_nvel_ndist(3, 27, 1); test_lb_write_buf(pe, cs, &opts); - test_lb_write_buf_asc(pe, cs, &opts); + test_lb_write_buf_ascii(pe, cs, &opts); + test_lb_io_aggr_pack(pe, cs, &opts); } return 0; @@ -720,12 +777,12 @@ int test_lb_write_buf(pe_t * pe, cs_t * cs, const lb_data_options_t * opts) { /***************************************************************************** * - * test_lb_write_buf_asc + * test_lb_write_buf_ascii * *****************************************************************************/ -int test_lb_write_buf_asc(pe_t * pe, cs_t * cs, - const lb_data_options_t * opts) { +int test_lb_write_buf_ascii(pe_t * pe, cs_t * cs, + const lb_data_options_t * opts) { lb_t * lb = NULL; char buf[BUFSIZ] = {0}; @@ -735,7 +792,7 @@ int test_lb_write_buf_asc(pe_t * pe, cs_t * cs, assert(opts); /* Size of ascii record musst fir in buffer ... */ - assert(opts->nvel*(opts->ndist*23 + 1) < BUFSIZ); + assert(opts->nvel*(opts->ndist*LB_RECORD_LENGTH_ASCII + 1) < BUFSIZ); lb_data_create(pe, cs, opts, &lb); @@ -752,11 +809,11 @@ int test_lb_write_buf_asc(pe_t * pe, cs_t * cs, } } - lb_write_buf_asc(lb, index, buf); + lb_write_buf_ascii(lb, index, buf); { /* Have we got the correct size? */ - size_t sz = lb->model.nvel*(lb->ndist*23 + 1)*sizeof(char); + size_t sz = lb->nvel*(lb->ndist*LB_RECORD_LENGTH_ASCII + 1)*sizeof(char); assert(sz == strnlen(buf, BUFSIZ)); } } @@ -764,7 +821,7 @@ int test_lb_write_buf_asc(pe_t * pe, cs_t * cs, { /* Read back in different memory position */ int index = cs_index(cs, 4, 5, 6); - lb_read_buf_asc(lb, index, buf); + lb_read_buf_ascii(lb, index, buf); /* Check the result in new position */ for (int n = 0; n < lb->ndist; n++) { @@ -781,3 +838,77 @@ int test_lb_write_buf_asc(pe_t * pe, cs_t * cs, return 0; } + +/***************************************************************************** + * + * test_lb_io_aggr_pack + * + * It is convenient to test lb_io_aggr_unpack() at the same time. + * + *****************************************************************************/ + +int test_lb_io_aggr_pack(pe_t * pe, cs_t * cs, const lb_data_options_t *opts) { + + lb_t * lb = NULL; + int nlocal[3] = {0}; + + assert(pe); + assert(cs); + assert(opts); + + cs_nlocal(cs, nlocal); + + lb_data_create(pe, cs, opts, &lb); + + assert(lb->ascii.datatype == MPI_CHAR); + assert(lb->ascii.datasize == sizeof(char)); + assert(lb->ascii.count == lb->nvel*(1 + lb->ndist*LB_RECORD_LENGTH_ASCII)); + assert(lb->binary.datatype == MPI_DOUBLE); + assert(lb->binary.datasize == sizeof(double)); + assert(lb->binary.count == lb->nvel*lb->ndist); + + /* ASCII */ + /* Aggregator */ + + { + /* We don't use the metadata quantities here */ + cs_limits_t lim = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; + io_aggregator_t aggr = {0}; + + io_aggregator_initialise(lb->ascii, lim, &aggr); + util_lb_data_check_set(lb); + lb_io_aggr_pack(lb, &aggr); + + /* Clear the ditributions, unpack, and check */ + memset(lb->f, 0, sizeof(double)*lb->nvel*lb->ndist*lb->nsite); + + lb_io_aggr_unpack(lb, &aggr); + util_lb_data_check_no_halo(lb); + + io_aggregator_finalise(&aggr); + } + + /* BINARY */ + + { + /* We don't use the metadata quantities here */ + cs_limits_t lim = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; + io_aggregator_t aggr = {0}; + + io_aggregator_initialise(lb->binary, lim, &aggr); + util_lb_data_check_set(lb); + lb_io_aggr_pack(lb, &aggr); + + /* Clear the ditributions, unpack, and check */ + memset(lb->f, 0, sizeof(double)*lb->nvel*lb->ndist*lb->nsite); + + lb_io_aggr_unpack(lb, &aggr); + util_lb_data_check_no_halo(lb); + + io_aggregator_finalise(&aggr); + } + + lb_free(lb); + + return 0; +} diff --git a/tests/unit/tests.c b/tests/unit/tests.c index a60672280..739579360 100644 --- a/tests/unit/tests.c +++ b/tests/unit/tests.c @@ -80,6 +80,7 @@ __host__ int tests_create() { test_io_info_args_suite(); test_io_info_args_rt_suite(); test_io_subfile_suite(); + test_io_metadata_suite(); test_io_impl_mpio_suite(); test_io_suite(); test_lb_d2q9_suite(); diff --git a/tests/unit/tests.h b/tests/unit/tests.h index 430bc05ce..53f8ca7fc 100644 --- a/tests/unit/tests.h +++ b/tests/unit/tests.h @@ -59,6 +59,7 @@ int test_io_info_args_rt_suite(void); int test_io_options_suite(void); int test_io_options_rt_suite(void); int test_io_subfile_suite(void); +int test_io_metadata_suite(void); int test_io_impl_mpio_suite(void); int test_io_suite(void); int test_lb_d2q9_suite(void); From 19d339d34faa5351893503d06989203a8d023c82 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 25 Nov 2022 09:59:53 +0000 Subject: [PATCH 145/244] Fix for bad conversion to larger type --- tests/unit/test_model.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit/test_model.c b/tests/unit/test_model.c index 02dd65eb4..706e2fd8c 100644 --- a/tests/unit/test_model.c +++ b/tests/unit/test_model.c @@ -813,7 +813,8 @@ int test_lb_write_buf_ascii(pe_t * pe, cs_t * cs, { /* Have we got the correct size? */ - size_t sz = lb->nvel*(lb->ndist*LB_RECORD_LENGTH_ASCII + 1)*sizeof(char); + int count = lb->nvel*(lb->ndist*LB_RECORD_LENGTH_ASCII + 1); + size_t sz = count*sizeof(char); assert(sz == strnlen(buf, BUFSIZ)); } } From b1f11d8b58623f8783428079fb0db1cd97f60f7a Mon Sep 17 00:00:00 2001 From: not populated Date: Fri, 25 Nov 2022 12:09:31 +0000 Subject: [PATCH 146/244] Test fixes --- tests/unit/test_fe_ternary.c | 56 ++++++++++++++++++---------------- tests/unit/test_field.c | 16 ++++++++-- tests/unit/test_io_impl_mpio.c | 2 +- 3 files changed, 44 insertions(+), 30 deletions(-) diff --git a/tests/unit/test_fe_ternary.c b/tests/unit/test_fe_ternary.c index 46d367550..84c08cc69 100644 --- a/tests/unit/test_fe_ternary.c +++ b/tests/unit/test_fe_ternary.c @@ -231,45 +231,47 @@ __host__ int test_fe_ternary_str(pe_t * pe, cs_t * cs, field_t * phi) { field_scalar_array_set(phi, index, phi0); fe_ternary_str(fe, index, s); - test_assert(fabs(s[0][0] - 5.2552500e-01) < DBL_EPSILON); - test_assert(fabs(s[0][1] - 0.0000000e+00) < DBL_EPSILON); - test_assert(fabs(s[0][2] - 0.0000000e+00) < DBL_EPSILON); - test_assert(fabs(s[1][0] - 0.0000000e+00) < DBL_EPSILON); - test_assert(fabs(s[1][1] - 5.2552500e-01) < DBL_EPSILON); - test_assert(fabs(s[1][2] - 0.0000000e+00) < DBL_EPSILON); - test_assert(fabs(s[2][0] - 0.0000000e+00) < DBL_EPSILON); - test_assert(fabs(s[2][1] - 0.0000000e+00) < DBL_EPSILON); - test_assert(fabs(s[2][2] - 5.2552500e-01) < DBL_EPSILON); + /* DBL_EPSILON is just too tight for some platform/compiler combinations */ + + test_assert(fabs(s[0][0] - 5.2552500e-01) < 2.0*DBL_EPSILON); + test_assert(fabs(s[0][1] - 0.0000000e+00) < 2.0*DBL_EPSILON); + test_assert(fabs(s[0][2] - 0.0000000e+00) < 2.0*DBL_EPSILON); + test_assert(fabs(s[1][0] - 0.0000000e+00) < 2.0*DBL_EPSILON); + test_assert(fabs(s[1][1] - 5.2552500e-01) < 2.0*DBL_EPSILON); + test_assert(fabs(s[1][2] - 0.0000000e+00) < 2.0*DBL_EPSILON); + test_assert(fabs(s[2][0] - 0.0000000e+00) < 2.0*DBL_EPSILON); + test_assert(fabs(s[2][1] - 0.0000000e+00) < 2.0*DBL_EPSILON); + test_assert(fabs(s[2][2] - 5.2552500e-01) < 2.0*DBL_EPSILON); /* With grad */ field_grad_pair_grad_set(dphi, index, grad); fe_ternary_str(fe, index, s); - test_assert(fabs(s[0][0] - 4.4077500e-01) < DBL_EPSILON); - test_assert(fabs(s[0][1] - -5.7062500e-02) < DBL_EPSILON); - test_assert(fabs(s[0][2] - 8.0000000e-02) < DBL_EPSILON); - test_assert(fabs(s[1][0] - -5.7062500e-02) < DBL_EPSILON); - test_assert(fabs(s[1][1] - 4.6777500e-01) < DBL_EPSILON); - test_assert(fabs(s[1][2] - -1.0150000e-01) < DBL_EPSILON); - test_assert(fabs(s[2][0] - 8.0000000e-02) < DBL_EPSILON); - test_assert(fabs(s[2][1] - -1.0150000e-01) < DBL_EPSILON); - test_assert(fabs(s[2][2] - 5.3796250e-01) < DBL_EPSILON); + test_assert(fabs(s[0][0] - 4.4077500e-01) < 2.0*DBL_EPSILON); + test_assert(fabs(s[0][1] - -5.7062500e-02) < 2.0*DBL_EPSILON); + test_assert(fabs(s[0][2] - 8.0000000e-02) < 2.0*DBL_EPSILON); + test_assert(fabs(s[1][0] - -5.7062500e-02) < 2.0*DBL_EPSILON); + test_assert(fabs(s[1][1] - 4.6777500e-01) < 2.0*DBL_EPSILON); + test_assert(fabs(s[1][2] - -1.0150000e-01) < 2.0*DBL_EPSILON); + test_assert(fabs(s[2][0] - 8.0000000e-02) < 2.0*DBL_EPSILON); + test_assert(fabs(s[2][1] - -1.0150000e-01) < 2.0*DBL_EPSILON); + test_assert(fabs(s[2][2] - 5.3796250e-01) < 2.0*DBL_EPSILON); /* With delsq */ field_grad_pair_delsq_set(dphi, index, d2phi); fe_ternary_str(fe, index, s); - test_assert(fabs(s[0][0] - 3.9790000e-01) < DBL_EPSILON); - test_assert(fabs(s[0][1] - -5.7062500e-02) < DBL_EPSILON); - test_assert(fabs(s[0][2] - 8.0000000e-02) < DBL_EPSILON); - test_assert(fabs(s[1][0] - -5.7062500e-02) < DBL_EPSILON); - test_assert(fabs(s[1][1] - 4.2490000e-01) < DBL_EPSILON); - test_assert(fabs(s[1][2] - -1.0150000e-01) < DBL_EPSILON); - test_assert(fabs(s[2][0] - 8.0000000e-02) < DBL_EPSILON); - test_assert(fabs(s[2][1] - -1.0150000e-01) < DBL_EPSILON); - test_assert(fabs(s[2][2] - 4.9508750e-01) < DBL_EPSILON); + test_assert(fabs(s[0][0] - 3.9790000e-01) < 2.0*DBL_EPSILON); + test_assert(fabs(s[0][1] - -5.7062500e-02) < 2.0*DBL_EPSILON); + test_assert(fabs(s[0][2] - 8.0000000e-02) < 2.0*DBL_EPSILON); + test_assert(fabs(s[1][0] - -5.7062500e-02) < 2.0*DBL_EPSILON); + test_assert(fabs(s[1][1] - 4.2490000e-01) < 2.0*DBL_EPSILON); + test_assert(fabs(s[1][2] - -1.0150000e-01) < 2.0*DBL_EPSILON); + test_assert(fabs(s[2][0] - 8.0000000e-02) < 2.0*DBL_EPSILON); + test_assert(fabs(s[2][1] - -1.0150000e-01) < 2.0*DBL_EPSILON); + test_assert(fabs(s[2][2] - 4.9508750e-01) < 2.0*DBL_EPSILON); fe_ternary_free(fe); field_grad_free(dphi); diff --git a/tests/unit/test_field.c b/tests/unit/test_field.c index 0b7fc5f45..67e33b052 100644 --- a/tests/unit/test_field.c +++ b/tests/unit/test_field.c @@ -99,11 +99,13 @@ int test_field_suite(void) { int test_field_io_read_write(pe_t * pe) { int ntotal[3] = {32, 16, 8}; + MPI_Comm comm = MPI_COMM_NULL; cs_t * cs = NULL; cs_create(pe, &cs); cs_ntotal_set(cs, ntotal); cs_init(cs); + cs_cart_comm(cs, &comm); /* ASCII */ { @@ -113,12 +115,22 @@ int test_field_io_read_write(pe_t * pe) { opts.iodata.output = io; test_field_io_write(pe, cs, &opts); - /* FIXME test_field_io_read(pe, cs, &opts); */ + test_field_io_read(pe, cs, &opts); + + MPI_Barrier(comm); /* Make sure we finish before any further action */ } /* Binary (default) */ { - /* PENDING */ + io_options_t io = io_options_with_format(IO_MODE_MPIIO, IO_RECORD_BINARY); + field_options_t opts = field_options_ndata_nhalo(5, 2); + opts.iodata.input = io; + opts.iodata.output = io; + + test_field_io_write(pe, cs, &opts); + test_field_io_read(pe, cs, &opts); + + MPI_Barrier(comm); /* Make sure we finish before any further action */ } cs_free(cs); diff --git a/tests/unit/test_io_impl_mpio.c b/tests/unit/test_io_impl_mpio.c index 22a7236be..96b60db0f 100644 --- a/tests/unit/test_io_impl_mpio.c +++ b/tests/unit/test_io_impl_mpio.c @@ -470,7 +470,7 @@ static int test_buf_unpack_asc(cs_t * cs, const io_aggregator_t * buf) { if (nc != 4) ifail += 1; if (iz != izread) ifail += 1; if (iy != iyread) ifail += 1; - if (ix != ixread) ifail =+ 1; + if (ix != ixread) ifail += 1; if (ivalread != ival) ifail += 1; } ib += 1; From fe79c754b7fddce1a925b74e4bb3f7a6737503b0 Mon Sep 17 00:00:00 2001 From: not populated Date: Fri, 25 Nov 2022 12:24:16 +0000 Subject: [PATCH 147/244] Arrange clobbering --- src/io_impl_mpio.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/io_impl_mpio.c b/src/io_impl_mpio.c index 7ed895a1f..8fe7302f4 100644 --- a/src/io_impl_mpio.c +++ b/src/io_impl_mpio.c @@ -174,8 +174,14 @@ int io_impl_mpio_write(io_impl_mpio_t * io, const char * filename) { MPI_Offset disp = 0; int count = 1; - MPI_File_open(comm, filename, MPI_MODE_WRONLY + MPI_MODE_CREATE, info, - &io->fh); + /* We want the equivalent of fopen() with mode = "w", i.e., O_TRUNC */ + MPI_File_open(comm, filename, + MPI_MODE_CREATE | MPI_MODE_DELETE_ON_CLOSE | MPI_MODE_WRONLY, + info, &io->fh); + MPI_File_close(&io->fh); + MPI_File_open(comm, filename, MPI_MODE_CREATE | MPI_MODE_WRONLY, + info, &io->fh); + MPI_File_set_view(io->fh, disp, io->element, io->file, "native", info); MPI_File_write_all(io->fh, io->super.aggr->buf, count, io->array, &io->status); @@ -229,8 +235,14 @@ int io_impl_mpio_write_begin(io_impl_mpio_t * io, const char * filename) { MPI_Offset disp = 0; int count = 1; - MPI_File_open(comm, filename, MPI_MODE_WRONLY + MPI_MODE_CREATE, info, - &io->fh); + /* Again, this is O_TRUNC */ + MPI_File_open(comm, filename, + MPI_MODE_CREATE | MPI_MODE_DELETE_ON_CLOSE | MPI_MODE_WRONLY, + info, &io->fh); + MPI_File_close(&io->fh); + MPI_File_open(comm, filename, MPI_MODE_CREATE | MPI_MODE_WRONLY, + info, &io->fh); + MPI_File_set_view(io->fh, disp, io->element, io->file, "native", info); MPI_File_write_all_begin(io->fh, io->super.aggr->buf, count, io->array); } From 5eb9982b6327821df23efc02020ccde5f540ba1f Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 25 Nov 2022 14:54:00 +0000 Subject: [PATCH 148/244] Repair potential string overrun --- src/field.c | 19 ++++++++++++++++--- src/model.c | 4 +++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/field.c b/src/field.c index 6b0b4eedb..ee1a8b269 100644 --- a/src/field.c +++ b/src/field.c @@ -95,6 +95,10 @@ __host__ int field_create(pe_t * pe, cs_t * cs, lees_edw_t * le, field_halo_create(obj, &obj->h); /* I/O single record information */ + /* As the communicator creation is a relatively high overhead operation, + * we only want to create the metadata objects once. They're here. */ + /* There should be a check on a valid i/o decomposition before this + * point, but in preicpile we can fail here... */ { io_element_t elasc = {.datatype = MPI_CHAR, @@ -113,13 +117,18 @@ __host__ int field_create(pe_t * pe, cs_t * cs, lees_edw_t * le, if (opts->iodata.input.iorformat == IO_RECORD_BINARY) element = elbin; ifail = io_metadata_initialise(cs, &opts->iodata.input, &element, &obj->iometadata_in); - assert(ifail == 0); /* FIXME run time failure */ + + assert(ifail == 0); + if (ifail != 0) pe_fatal(pe, "Field: Bad input i/o decomposition\n"); + /* Output metadata */ if (opts->iodata.output.iorformat == IO_RECORD_ASCII) element = elasc; if (opts->iodata.output.iorformat == IO_RECORD_BINARY) element = elbin; ifail = io_metadata_initialise(cs, &opts->iodata.output, &element, &obj->iometadata_out); - assert(ifail == 0); /* FIXME run time failure please */ + + assert(ifail == 0); + if (ifail != 0) pe_fatal(pe, "Field: Bad output i/o decomposition\n"); } } @@ -1136,7 +1145,10 @@ int field_read_buf_ascii(field_t * field, int index, const char * buf) { assert(buf); for (int n = 0; n < field->nf; n++) { - int nr = sscanf(buf + n*nbyte, "%le", array + n); + /* First, make sure we have a \0, before sscanf() */ + char tmp[BUFSIZ] = {0}; + memcpy(tmp, buf + n*nbyte, nbyte*sizeof(char)); + int nr = sscanf(tmp, "%le", array + n); if (nr != 1) ifail = 1; } @@ -1211,6 +1223,7 @@ int field_io_aggr_unpack(field_t * field, const io_aggregator_t * aggr) { /* Read/write data for (ic,jc,kc) */ int index = cs_index(field->cs, ic, jc, kc); int offset = ib*aggr->szelement; + assert(0 <= offset && offset < aggr->szbuf); if (iasc) field_read_buf_ascii(field, index, aggr->buf + offset); if (ibin) field_read_buf(field, index, aggr->buf + offset); } diff --git a/src/model.c b/src/model.c index 54b13c72c..60c0dbc40 100644 --- a/src/model.c +++ b/src/model.c @@ -1528,7 +1528,9 @@ int lb_read_buf_ascii(lb_t * lb, int index, const char * buf) { int poffset = p*(lb->ndist*nbyte + 1); /* +1 for each newline */ for (int n = 0; n < lb->ndist; n++) { int laddr = LB_ADDR(lb->nsite, lb->ndist, lb->model.nvel, index, n, p); - int nr = sscanf(buf + poffset + n*nbyte, "%le", lb->f + laddr); + char tmp[BUFSIZ] = {0}; /* Make sure we have a \0 */ + memcpy(tmp, buf + poffset + n*nbyte, nbyte*sizeof(char)); + int nr = sscanf(tmp, "%le", lb->f + laddr); if (nr != 1) ifail = 1; } } From 9956661c0d8e4fe9468f9cedb2023239df5fd126 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 25 Nov 2022 15:22:16 +0000 Subject: [PATCH 149/244] Correct type --- src/field.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/field.c b/src/field.c index ee1a8b269..56784047d 100644 --- a/src/field.c +++ b/src/field.c @@ -1222,7 +1222,7 @@ int field_io_aggr_unpack(field_t * field, const io_aggregator_t * aggr) { /* Read/write data for (ic,jc,kc) */ int index = cs_index(field->cs, ic, jc, kc); - int offset = ib*aggr->szelement; + size_t offset = ib*aggr->szelement; assert(0 <= offset && offset < aggr->szbuf); if (iasc) field_read_buf_ascii(field, index, aggr->buf + offset); if (ibin) field_read_buf(field, index, aggr->buf + offset); From bfe34533f503913b9a382f7218610f9f8959e226 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 25 Nov 2022 15:25:53 +0000 Subject: [PATCH 150/244] Add comments --- src/io_impl_mpio.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/io_impl_mpio.c b/src/io_impl_mpio.c index 8fe7302f4..5da3c765a 100644 --- a/src/io_impl_mpio.c +++ b/src/io_impl_mpio.c @@ -4,6 +4,11 @@ * * Read/write aggregated data buffers using MPI/IO. * + * Historically, users have expected i/o to clobber existing files. + * MPI/IO does not really have an analogue of this, so there is + * some phaff at each MPI_File_open() to retain the expected + * behaviour. + * * * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre @@ -38,17 +43,22 @@ static int io_impl_mpio_types_create(io_impl_mpio_t * io); int io_impl_mpio_create(const io_metadata_t * metadata, io_impl_mpio_t ** io) { + int ifail = 0; io_impl_mpio_t * mpio = NULL; mpio = (io_impl_mpio_t *) calloc(1, sizeof(io_impl_mpio_t)); if (mpio == NULL) goto err; - io_impl_mpio_initialise(metadata, mpio); + ifail = io_impl_mpio_initialise(metadata, mpio); + if (ifail != 0) goto err; + *io = mpio; return 0; err: + if (mpio) free(mpio); + return -1; } @@ -74,25 +84,28 @@ int io_impl_mpio_free(io_impl_mpio_t ** io) { * * io_impl_mpio_initialise * + * There could in principle be a failure to allocate the aggregator, + * which will result in a non-zero exit code. + * *****************************************************************************/ int io_impl_mpio_initialise(const io_metadata_t * metadata, io_impl_mpio_t * io) { + int ifail = 0; assert(metadata); assert(io); *io = (io_impl_mpio_t) {0}; io->super.impl = &vt_; - io_aggregator_create(metadata->element, metadata->limits, &io->super.aggr); - /* ALLOW FAILURE? */ - + ifail = io_aggregator_create(metadata->element, metadata->limits, + &io->super.aggr); io->metadata = metadata; io->fh = MPI_FILE_NULL; io_impl_mpio_types_create(io); - return 0; + return ifail; } /***************************************************************************** @@ -250,7 +263,6 @@ int io_impl_mpio_write_begin(io_impl_mpio_t * io, const char * filename) { return 0; } - /***************************************************************************** * * io_impl_mpio_write_end From 247c258c22813ae90227d7e3e58d629d293d49bc Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 25 Nov 2022 15:37:55 +0000 Subject: [PATCH 151/244] Remove pointless assertion --- src/field.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/field.c b/src/field.c index 56784047d..e4af9d4cc 100644 --- a/src/field.c +++ b/src/field.c @@ -1220,10 +1220,9 @@ int field_io_aggr_unpack(field_t * field, const io_aggregator_t * aggr) { int jc = cs_limits_jc(aggr->lim, ib); int kc = cs_limits_kc(aggr->lim, ib); - /* Read/write data for (ic,jc,kc) */ + /* Read data for (ic,jc,kc) */ int index = cs_index(field->cs, ic, jc, kc); size_t offset = ib*aggr->szelement; - assert(0 <= offset && offset < aggr->szbuf); if (iasc) field_read_buf_ascii(field, index, aggr->buf + offset); if (ibin) field_read_buf(field, index, aggr->buf + offset); } From ea82eff6f30f3942b47194e0032f841a0c6f7bc5 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 25 Nov 2022 16:04:25 +0000 Subject: [PATCH 152/244] Pending one CodeQL alert --- src/main.c | 47 +++-------------------------------------------- 1 file changed, 3 insertions(+), 44 deletions(-) diff --git a/src/main.c b/src/main.c index 656814552..796fa1819 100644 --- a/src/main.c +++ b/src/main.c @@ -13,9 +13,7 @@ * *****************************************************************************/ -#include #include -#include #include "pe.h" #include "ludwig.h" @@ -23,8 +21,6 @@ #include "petscksp.h" #endif -int process_command_line(const char * arg); - /***************************************************************************** * * main @@ -33,19 +29,16 @@ int process_command_line(const char * arg); int main(int argc, char ** argv) { + char inputfile[FILENAME_MAX] = "input"; int provided = MPI_THREAD_SINGLE; MPI_Init_thread(&argc, &argv, MPI_THREAD_FUNNELED, &provided); #ifdef PETSC PetscInitialize(&argc, &argv, (char*) 0, NULL); #endif + if (argc > 1) snprintf(inputfile, FILENAME_MAX, "%s", argv[1]); - if (argc == 1) { - ludwig_run("input"); - } - else if (argc > 1 && process_command_line(argv[1]) == 0) { - ludwig_run(argv[1]); - } + ludwig_run(inputfile); #ifdef PETSC PetscFinalize(); @@ -54,37 +47,3 @@ int main(int argc, char ** argv) { return 0; } - -/***************************************************************************** - * - * process_command_line - * - * Require a posix portable filename, with the additional condition - * that the first character is alphabetical. - * - * A valid filename will return zero. - * - *****************************************************************************/ - -int process_command_line(const char * arg) { - - int ifail = -1; - - /* The first character should be alphabetical */ - - if (isalpha(arg[0])) { - for (size_t i = 0; i < strnlen(arg, FILENAME_MAX); i++) { - const char c = arg[i]; - ifail = -1; - if (isalnum(c) || c == '_' || c == '-' || c == '.') ifail = 0; - } - } - - if (ifail != 0) { - printf("Input file name: %s\n" - "Please use a posix file name with only alphanumeric\n" - "characters or _ or - or .\n", arg); - } - - return ifail; -} From 16b07a766b42abd018fecb09dcd6701f6d4dd4ff Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 25 Nov 2022 16:56:13 +0000 Subject: [PATCH 153/244] Add MPI_Comm_split_type() --- mpi_s/mpi.h | 9 +++++++++ mpi_s/mpi_serial.c | 18 ++++++++++++++++++ mpi_s/mpi_tests.c | 21 +++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/mpi_s/mpi.h b/mpi_s/mpi.h index 1ac0425c9..c63cff503 100644 --- a/mpi_s/mpi.h +++ b/mpi_s/mpi.h @@ -31,6 +31,8 @@ typedef MPI_Handle MPI_Datatype; typedef MPI_Handle MPI_Request; typedef MPI_Handle MPI_Op; typedef MPI_Handle MPI_Errhandler; +typedef MPI_Handle MPI_File; +typedef MPI_Handle MPI_Info; typedef struct { int MPI_SOURCE; @@ -102,11 +104,16 @@ enum reserved_communicators{MPI_COMM_WORLD, MPI_COMM_SELF}; #define MPI_REQUEST_NULL -4 #define MPI_OP_NULL -5 #define MPI_ERRHANDLER_NULL -6 +#define MPI_FILE_NULL -7 +#define MPI_INFO_NULL -8 /* Special values */ #define MPI_IN_PLACE ((void *) 1) +/* Only one "split_type" is available for MPI_Comm_split_type() */ +#define MPI_COMM_TYPE_SHARED 42 + /* Thread support level */ #define MPI_THREAD_SINGLE 1 @@ -182,6 +189,8 @@ int MPI_Allreduce(void * send, void * recv, int count, MPI_Datatype type, MPI_Op op, MPI_Comm comm); int MPI_Comm_split(MPI_Comm comm, int colour, int key, MPI_Comm * newcomm); +int MPI_Comm_split_type(MPI_Comm comm, int split_type, int key, + MPI_Info info, MPI_Comm * newcomm); int MPI_Comm_free(MPI_Comm * comm); int MPI_Comm_dup(MPI_Comm oldcomm, MPI_Comm * newcomm); diff --git a/mpi_s/mpi_serial.c b/mpi_s/mpi_serial.c index 04ea6e3e9..3df3b9e22 100644 --- a/mpi_s/mpi_serial.c +++ b/mpi_s/mpi_serial.c @@ -608,6 +608,24 @@ int MPI_Comm_split(MPI_Comm comm, int colour, int key, MPI_Comm * newcomm) { return MPI_SUCCESS; } +/***************************************************************************** + * + * MPI_Comm_split_type + * + *****************************************************************************/ + +int MPI_Comm_split_type(MPI_Comm comm, int split_type, int key, MPI_Info info, + MPI_Comm * newcomm) { + + assert(mpi_is_valid_comm(comm)); + assert(newcomm); + assert(split_type == MPI_COMM_TYPE_SHARED); + + *newcomm = comm; + + return MPI_SUCCESS; +} + /***************************************************************************** * * MPI_Comm_free diff --git a/mpi_s/mpi_tests.c b/mpi_s/mpi_tests.c index 1d11b8c26..a129cb993 100644 --- a/mpi_s/mpi_tests.c +++ b/mpi_s/mpi_tests.c @@ -22,6 +22,8 @@ static int test_mpi_type_contiguous(void); static int test_mpi_type_create_struct(void); static int test_mpi_op_create(void); +static int test_mpi_comm_split_type(void); + int main (int argc, char ** argv) { int ireturn; @@ -40,6 +42,7 @@ int main (int argc, char ** argv) { test_mpi_type_contiguous(); test_mpi_type_create_struct(); test_mpi_op_create(); + test_mpi_comm_split_type(); ireturn = MPI_Finalize(); assert(ireturn == MPI_SUCCESS); @@ -335,3 +338,21 @@ static int test_mpi_op_create(void) { return MPI_SUCCESS; } + +/***************************************************************************** + * + * test_mpi_comm_split_type + * + *****************************************************************************/ + +int test_mpi_comm_split_type(void) { + + MPI_Comm comm = MPI_COMM_NULL; + int key = 0; + + MPI_Comm_split_type(MPI_COMM_WORLD, MPI_COMM_TYPE_SHARED, key, MPI_INFO_NULL, + &comm); + MPI_Comm_free(&comm); + + return 0; +} From bfd15ec85286cda7710347845263849f44324785 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 25 Nov 2022 17:11:27 +0000 Subject: [PATCH 154/244] Remove __NVCC__ conidition compliation --- src/ludwig.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/ludwig.c b/src/ludwig.c index f22ac667a..5496e3af9 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -476,16 +476,34 @@ void ludwig_run(const char * inputfile) { pe_create(MPI_COMM_WORLD, PE_VERBOSE, &ludwig->pe); pe_mpi_comm(ludwig->pe, &comm); -#ifdef __NVCC__ + { - /* Pending a more formal approach */ - int nd = 0; /* GPU devices per node */ - int id = 0; /* Assume MPI ranks per node == nd */ - cudaGetDeviceCount(&nd); - id = pe_mpi_rank(ludwig->pe) % nd; - cudaSetDevice(id); + /* We currently assume one GPU per MPI task, and that GPU id + * can be equated to rank in the shared (node) communicator. */ + MPI_Comm node_comm = MPI_COMM_NULL; + int rank = -1; + int node_rank = -1; + int node_size = -1; + int ndevice = -1; + + MPI_Comm_rank(comm, &rank); + MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, + &node_comm); + MPI_Comm_rank(node_comm, &node_rank); + MPI_Comm_size(node_comm, &node_size); + + tdpGetDeviceCount(&ndevice); + + if (ndevice > 0 && ndevice != node_size) { + pe_info(ludwig->pe, "MPI tasks per node: %d\n", node_size); + pe_info(ludwig->pe, "GPUs per node: %d\n", ndevice); + pe_fatal(ludwig->pe, "Expecting one GPU per MPI task\n"); + } + + tdpAssert(tdpSetDevice(node_rank)); + MPI_Comm_free(&node_comm); } -#endif + rt_create(ludwig->pe, &ludwig->rt); rt_read_input_file(ludwig->rt, inputfile); rt_info(ludwig->rt); From fc80eddabf336bfaeeb9b99b304084cc6e054ea1 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 29 Nov 2022 14:36:42 +0000 Subject: [PATCH 155/244] Fix default host halo swap to full --- src/ludwig.c | 5 ++--- src/model.c | 4 +++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ludwig.c b/src/ludwig.c index 5496e3af9..8d09573a5 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -2207,9 +2207,6 @@ int ludwig_colloids_update(ludwig_t * ludwig) { tdpGetDeviceCount(&ndevice); - /* __NVCC__ TODO: remove */ - lb_memcpy(ludwig->lb, tdpMemcpyDeviceToHost); - lb_ndist(ludwig->lb, &ndist); iconserve = (ludwig->psi || (ludwig->phi && ndist == 1)); @@ -2231,6 +2228,8 @@ int ludwig_colloids_update(ludwig_t * ludwig) { lb_halo(ludwig->lb); } else { + /* Pull data back, then full host halo swap */ + lb_memcpy(ludwig->lb, tdpMemcpyDeviceToHost); lb_halo_swap(ludwig->lb, LB_HALO_OPENMP_FULL); } diff --git a/src/model.c b/src/model.c index 1bf842b0f..16fdf798f 100644 --- a/src/model.c +++ b/src/model.c @@ -1178,7 +1178,9 @@ int lb_halo_create(const lb_t * lb, lb_halo_t * h, lb_halo_enum_t scheme) { cs_cart_comm(lb->cs, &h->comm); h->tagbase = 211216; - if (scheme == LB_HALO_OPENMP_FULL) h->full = 1; + /* Default to full swap unless reduced is requested. */ + h->full = 1; + if (scheme == LB_HALO_OPENMP_REDUCED) h->full = 0; /* Determine look-up table of ranks of neighbouring processes */ { From b4d5a32f914be7ff3cf38bc7ed3e591d4973f83b Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 2 Dec 2022 15:25:40 +0000 Subject: [PATCH 156/244] Fix default full/reduced halo --- src/model.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/model.c b/src/model.c index 60c0dbc40..6f922e73a 100644 --- a/src/model.c +++ b/src/model.c @@ -1170,7 +1170,8 @@ int lb_halo_create(const lb_t * lb, lb_halo_t * h, lb_halo_enum_t scheme) { cs_cart_comm(lb->cs, &h->comm); h->tagbase = 211216; - if (scheme == LB_HALO_OPENMP_FULL) h->full = 1; + h->full = 1; + if (scheme == LB_HALO_OPENMP_REDUCED) h->full = 0; /* Determine look-up table of ranks of neighbouring processes */ { From 6ed4eed12966653c8259d9bdfda40a7379667f81 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 2 Dec 2022 15:26:44 +0000 Subject: [PATCH 157/244] Increare internal limits --- mpi_s/mpi_serial.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mpi_s/mpi_serial.c b/mpi_s/mpi_serial.c index 1ecafd888..e63d6db6f 100644 --- a/mpi_s/mpi_serial.c +++ b/mpi_s/mpi_serial.c @@ -43,8 +43,8 @@ /* Internal state */ -#define MAX_CART_COMM 16 -#define MAX_USER_DT 32 +#define MAX_CART_COMM 128 +#define MAX_USER_DT 128 #define MAX_USER_FILE 16 /* We are not going to deal with all possible data types; encode From 1568bd83f0c82e762447703ef10ede6b26e88fe8 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 2 Dec 2022 15:27:14 +0000 Subject: [PATCH 158/244] Update hydro rho/eta implementation to field_t --- src/collision.c | 4 +- src/hydro.c | 77 +++++++++++++++----------------- src/hydro.h | 6 ++- src/visc_arrhenius.c | 2 +- tests/unit/test_visc_arrhenius.c | 2 +- 5 files changed, 43 insertions(+), 48 deletions(-) diff --git a/src/collision.c b/src/collision.c index e4530e3cc..54b0d12b9 100644 --- a/src/collision.c +++ b/src/collision.c @@ -395,7 +395,7 @@ void lb_collision_mrt1_site(lb_t * lb, hydro_t * hydro, map_t * map, /* Use viscosity model values hydro->eta */ /* Bulk viscosity will be (eta_bulk/eta_shear)_newtonian*eta_local */ for_simd_v(iv, NSIMDVL) { - eta[iv] = hydro->eta[addr_rank0(hydro->nsite, index0+iv)]; + eta[iv] = hydro->eta->data[addr_rank0(hydro->nsite, index0+iv)]; eta_bulk[iv] = (_cp.eta_bulk/_cp.eta_shear)*eta[iv]; } } @@ -570,7 +570,7 @@ void lb_collision_mrt1_site(lb_t * lb, hydro_t * hydro, map_t * map, } /* density */ for_simd_v(iv, NSIMDVL) { - hydro->rho[addr_rank0(hydro->nsite, index0+iv)] = rho[iv]; + hydro->rho->data[addr_rank0(hydro->nsite, index0+iv)] = rho[iv]; } /* velocity */ for (ia = 0; ia < 3; ia++) { diff --git a/src/hydro.c b/src/hydro.c index dd9eb8867..6e78121a7 100644 --- a/src/hydro.c +++ b/src/hydro.c @@ -80,21 +80,30 @@ __host__ int hydro_create(pe_t * pe, cs_t * cs, lees_edw_t * le, cs_nsites(cs, &obj->nsite); if (le) lees_edw_nsites(le, &obj->nsite); - obj->rho = (double *) mem_aligned_calloc(MEM_PAGESIZE, obj->nsite, - sizeof(double)); + /* Fields */ + { + /* rho: scalar field with no halo swap (halo width zero). */ + field_options_t options = field_options_ndata_nhalo(1, 0); + /* haloscheme */ + /* halo verbose */ + /* usefirsttouch */ + /* iodata */ + field_create(pe, cs, le, "rho", &options, &obj->rho); + + /* eta: scalar viscosity with no halo swap */ + field_create(pe, cs, le, "eta", &options, &obj->eta); + } + + /* Original */ + obj->u = (double *) mem_aligned_calloc(MEM_PAGESIZE, NHDIM*obj->nsite, sizeof(double)); - if (obj->rho == NULL) pe_fatal(pe, "calloc(hydro-rho) failed\n"); if (obj->u == NULL) pe_fatal(pe, "calloc(hydro->u) failed\n"); obj->f = (double *) mem_aligned_calloc(MEM_PAGESIZE, NHDIM*obj->nsite, sizeof(double)); if (obj->f == NULL) pe_fatal(pe, "calloc(hydro->f) failed\n"); - obj->eta = - (double *) mem_aligned_calloc(MEM_PAGESIZE, obj->nsite, sizeof(double)); - if (obj->eta == NULL) pe_fatal(pe, "calloc(hydro->eta) failed\n"); - halo_swap_create_r1(pe, cs, opts->nhcomm, obj->nsite, NHDIM, &obj->halo); assert(obj->halo); @@ -112,11 +121,6 @@ __host__ int hydro_create(pe_t * pe, cs_t * cs, lees_edw_t * le, tdpAssert(tdpMalloc((void **) &obj->target, sizeof(hydro_t))); tdpAssert(tdpMemset(obj->target, 0, sizeof(hydro_t))); - tdpAssert(tdpMalloc((void **) &tmp, obj->nsite*sizeof(double))); - tdpAssert(tdpMemset(tmp, 0, obj->nsite*sizeof(double))); - tdpAssert(tdpMemcpy(&obj->target->rho, &tmp, sizeof(double *), - tdpMemcpyHostToDevice)); - tdpAssert(tdpMalloc((void **) &tmp, NHDIM*obj->nsite*sizeof(double))); tdpAssert(tdpMemset(tmp, 0, NHDIM*obj->nsite*sizeof(double))); tdpAssert(tdpMemcpy(&obj->target->u, &tmp, sizeof(double *), @@ -127,13 +131,16 @@ __host__ int hydro_create(pe_t * pe, cs_t * cs, lees_edw_t * le, tdpAssert(tdpMemcpy(&obj->target->f, &tmp, sizeof(double *), tdpMemcpyHostToDevice)); - tdpAssert(tdpMalloc((void **) &tmp, obj->nsite*sizeof(double))); - tdpAssert(tdpMemset(tmp, 0, obj->nsite*sizeof(double))); - tdpAssert(tdpMemcpy(&obj->target->eta, &tmp, sizeof(double *), - tdpMemcpyHostToDevice)); - tdpAssert(tdpMemcpy(&obj->target->nsite, &obj->nsite, sizeof(int), tdpMemcpyHostToDevice)); + + /* Fields: target pointers should be copied ... */ + tdpAssert(tdpMemcpy(&obj->target->rho, &obj->rho->target, + sizeof(field_t *), + tdpMemcpyHostToDevice)); + tdpAssert(tdpMemcpy(&obj->target->eta, &obj->eta->target, + sizeof(field_t *), + tdpMemcpyHostToDevice)); } hydro_halo_create(obj, &obj->h); @@ -160,30 +167,25 @@ __host__ int hydro_free(hydro_t * obj) { tdpGetDeviceCount(&ndevice); if (ndevice > 0) { - - tdpAssert(tdpMemcpy(&tmp, &obj->target->rho, sizeof(double *), - tdpMemcpyDeviceToHost)); - tdpAssert(tdpFree(tmp)); - tdpAssert(tdpMemcpy(&tmp, &obj->target->u, sizeof(double *), tdpMemcpyDeviceToHost)); tdpAssert(tdpFree(tmp)); tdpAssert(tdpMemcpy(&tmp, &obj->target->f, sizeof(double *), tdpMemcpyDeviceToHost)); tdpAssert(tdpFree(tmp)); - tdpAssert(tdpMemcpy(&tmp, &obj->target->eta, sizeof(double *), - tdpMemcpyDeviceToHost)); - tdpAssert(tdpFree(tmp)); tdpAssert(tdpFree(obj->target)); } halo_swap_free(obj->halo); hydro_halo_free(&obj->h); if (obj->info) io_info_free(obj->info); - free(obj->eta); + + field_free(obj->eta); + field_free(obj->rho); + free(obj->f); free(obj->u); - free(obj->rho); + free(obj); return 0; @@ -200,7 +202,6 @@ __host__ int hydro_memcpy(hydro_t * obj, tdpMemcpyKind flag) { int ndevice; double * tmpu; double * tmpf; - double * tmpeta; assert(obj); @@ -211,34 +212,27 @@ __host__ int hydro_memcpy(hydro_t * obj, tdpMemcpyKind flag) { assert(obj->target == obj); } else { - double * tmprho = NULL; - - tdpAssert(tdpMemcpy(&tmprho, &obj->target->rho, sizeof(double *), - tdpMemcpyDeviceToHost)); tdpAssert(tdpMemcpy(&tmpf, &obj->target->f, sizeof(double *), tdpMemcpyDeviceToHost)); tdpAssert(tdpMemcpy(&tmpu, &obj->target->u, sizeof(double *), tdpMemcpyDeviceToHost)); - tdpAssert(tdpMemcpy(&tmpeta, &obj->target->eta, sizeof(double *), - tdpMemcpyDeviceToHost)); switch (flag) { case tdpMemcpyHostToDevice: - tdpAssert(tdpMemcpy(tmprho, obj->rho, obj->nsite*sizeof(double), flag)); tdpAssert(tdpMemcpy(tmpu, obj->u, NHDIM*obj->nsite*sizeof(double), flag)); tdpAssert(tdpMemcpy(tmpf, obj->f, NHDIM*obj->nsite*sizeof(double), flag)); - tdpAssert(tdpMemcpy(tmpeta, obj->eta, obj->nsite*sizeof(double), flag)); tdpAssert(tdpMemcpy(&obj->target->nsite, &obj->nsite, sizeof(int), flag)); break; case tdpMemcpyDeviceToHost: - tdpAssert(tdpMemcpy(obj->rho, tmprho, obj->nsite*sizeof(double), flag)); tdpAssert(tdpMemcpy(obj->f, tmpf, NHDIM*obj->nsite*sizeof(double), flag)); tdpAssert(tdpMemcpy(obj->u, tmpu, NHDIM*obj->nsite*sizeof(double), flag)); - tdpAssert(tdpMemcpy(obj->eta, tmpeta, obj->nsite*sizeof(double), flag)); break; default: pe_fatal(obj->pe, "Bad flag in hydro_memcpy\n"); } + /* Fields */ + field_memcpy(obj->rho, flag); + field_memcpy(obj->eta, flag); } return 0; @@ -423,7 +417,7 @@ __host__ __device__ int hydro_rho_set(hydro_t * hydro, int index, double rho) { assert(hydro); - hydro->rho[addr_rank0(hydro->nsite, index)] = rho; + hydro->rho->data[addr_rank0(hydro->nsite, index)] = rho; return 0; } @@ -439,7 +433,7 @@ __host__ __device__ int hydro_rho(hydro_t * hydro, int index, double * rho) { assert(hydro); assert(rho); - *rho = hydro->rho[addr_rank0(hydro->nsite, index)]; + *rho = hydro->rho->data[addr_rank0(hydro->nsite, index)]; return 0; } @@ -554,9 +548,8 @@ __host__ int hydro_rho0(hydro_t * obj, double rho0) { assert(obj); assert(obj->target); - tdpAssert(tdpMemcpy(&rho, &obj->target->rho, sizeof(double *), + tdpAssert(tdpMemcpy(&rho, &obj->rho->target->data, sizeof(double *), tdpMemcpyDeviceToHost)); - kernel_launch_param(obj->nsite, &nblk, &ntpb); tdpLaunchKernel(hydro_rho0_kernel, nblk, ntpb, 0, 0, obj->nsite, rho0, rho); diff --git a/src/hydro.h b/src/hydro.h index d610a9f9a..89572b960 100644 --- a/src/hydro.h +++ b/src/hydro.h @@ -21,6 +21,7 @@ #include "pe.h" #include "coords.h" +#include "field.h" #include "halo_swap.h" #include "leesedwards.h" #include "io_harness.h" @@ -55,10 +56,11 @@ typedef struct hydro_s hydro_t; struct hydro_s { int nsite; /* Allocated sites (local) */ int nhcomm; /* Width of halo region for u field */ - double * rho; /* Density field */ + double * u; /* Velocity field (on host) */ double * f; /* Body force field (on host) */ - double * eta; /* Local shear stress */ + field_t * rho; /* Density field */ + field_t * eta; /* Scalar viscosity field */ pe_t * pe; /* Parallel environment */ cs_t * cs; /* Coordinate system */ diff --git a/src/visc_arrhenius.c b/src/visc_arrhenius.c index 0cfa892b3..fc138c457 100644 --- a/src/visc_arrhenius.c +++ b/src/visc_arrhenius.c @@ -213,7 +213,7 @@ static __global__ void visc_update_kernel(kernel_ctxt_t * ktx, etaminus = pow(visc_param.eta_minus, 0.5*(1.0 - phi0)); etaplus = pow(visc_param.eta_plus, 0.5*(1.0 + phi0)); - hydro->eta[addr_rank0(hydro->nsite, index)] = etaminus*etaplus; + hydro->eta->data[addr_rank0(hydro->nsite, index)] = etaminus*etaplus; } return; diff --git a/tests/unit/test_visc_arrhenius.c b/tests/unit/test_visc_arrhenius.c index b4fa641a8..aa080c8d5 100644 --- a/tests/unit/test_visc_arrhenius.c +++ b/tests/unit/test_visc_arrhenius.c @@ -223,7 +223,7 @@ int test_visc_arrhenius_eta_uniform(cs_t * cs, hydro_t * hydro, double eta0) { for (kc = 1; kc <= nlocal[Z]; kc++) { index = cs_index(cs, ic, jc, kc); - eta = hydro->eta[addr_rank0(hydro->nsite, index)]; + eta = hydro->eta->data[addr_rank0(hydro->nsite, index)]; if (fabs(eta - eta0) > DBL_EPSILON) ifail = 1; } From 56f9a93b52cc25b18d0a15ecbbfa9c5d33f93cf9 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 2 Dec 2022 15:55:45 +0000 Subject: [PATCH 159/244] Add checks on preseence of mobility --- src/ludwig.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ludwig.c b/src/ludwig.c index 8d09573a5..76756da1e 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -1351,6 +1351,7 @@ int free_energy_init_rt(ludwig_t * ludwig) { pe_info(pe, "\n"); pe_info(pe, "Using Cahn-Hilliard finite difference solver.\n"); + rt_key_required(rt, "mobility", RT_FATAL); rt_double_parameter(rt, "mobility", &value); physics_mobility_set(ludwig->phys, value); pe_info(pe, "Mobility M = %12.5e\n", value); @@ -1432,6 +1433,7 @@ int free_energy_init_rt(ludwig_t * ludwig) { pe_info(pe, "\n"); pe_info(pe, "Using full lattice Boltzmann solver for Cahn-Hilliard:\n"); + rt_key_required(rt, "mobility", RT_FATAL); rt_double_parameter(rt, "mobility", &value); physics_mobility_set(ludwig->phys, value); pe_info(pe, "Mobility M = %12.5e\n", value); @@ -1474,6 +1476,7 @@ int free_energy_init_rt(ludwig_t * ludwig) { pe_info(pe, "\n"); pe_info(pe, "Using Cahn-Hilliard solver:\n"); + rt_key_required(rt, "mobility", RT_FATAL); rt_double_parameter(rt, "mobility", &value); physics_mobility_set(ludwig->phys, value); pe_info(pe, "Mobility M = %12.5e\n", value); @@ -1816,6 +1819,7 @@ int free_energy_init_rt(ludwig_t * ludwig) { pe_info(pe, "\n"); pe_info(pe, "Using Cahn-Hilliard finite difference solver.\n"); + rt_key_required(rt, "mobility", RT_FATAL); rt_double_parameter(rt, "mobility", &value); physics_mobility_set(ludwig->phys, value); pe_info(pe, "Mobility M = %12.5e\n", value); @@ -1991,6 +1995,7 @@ int free_energy_init_rt(ludwig_t * ludwig) { pe_info(pe, "\n"); pe_info(pe, "Using Cahn-Hilliard finite difference solver.\n"); + rt_key_required(rt, "mobility", RT_FATAL); rt_double_parameter(rt, "mobility", &value); physics_mobility_set(ludwig->phys, value); pe_info(pe, "Mobility M = %12.5e\n", value); From 63277c73fc75fac390ee26a919b8a55d4748517b Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 5 Dec 2022 16:23:21 +0000 Subject: [PATCH 160/244] Update hydro implementation to use fields --- src/advection.c | 100 ++--- src/blue_phase_beris_edwards.c | 69 ++-- src/collision.c | 17 +- src/field.c | 2 +- src/hydro.c | 671 +++----------------------------- src/hydro.h | 55 +-- src/hydro_impl.h | 144 +++++++ src/hydro_options.c | 42 +- src/hydro_options.h | 16 +- src/hydro_rt.c | 8 +- src/phi_force.c | 16 +- src/phi_force_colloid.c | 10 +- tests/unit/test_hydro.c | 30 +- tests/unit/test_hydro_options.c | 118 ++++++ tests/unit/tests.c | 1 + tests/unit/tests.h | 1 + 16 files changed, 513 insertions(+), 787 deletions(-) create mode 100644 src/hydro_impl.h create mode 100644 tests/unit/test_hydro_options.c diff --git a/src/advection.c b/src/advection.c index cbc773509..6b30bf09c 100644 --- a/src/advection.c +++ b/src/advection.c @@ -557,7 +557,6 @@ __global__ void advection_le_1st_kernel(kernel_ctxt_t * ktx, for_simt_parallel(kindex, kiter, 1) { - int ia; int n; int ic, jc, kc; int index0, index1, index; @@ -574,13 +573,11 @@ __global__ void advection_le_1st_kernel(kernel_ctxt_t * ktx, /* west face (icm1 and ic) */ - for (ia = 0; ia < 3; ia++) { - u0[ia] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index0, ia)]; - } + hydro_u(hydro, index0, u0); index1 = kernel_coords_index(ktx, icm1, jc, kc); + hydro_u(hydro, index1, u1); - u1[X] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index1, X)]; u = 0.5*(u0[X] + u1[X]); index = index0; @@ -595,8 +592,8 @@ __global__ void advection_le_1st_kernel(kernel_ctxt_t * ktx, /* east face (ic and icp1) */ index1 = kernel_coords_index(ktx, icp1, jc, kc); + hydro_u(hydro, index1, u1); - u1[X] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index1, X)]; u = 0.5*(u0[X] + u1[X]); index = index0; @@ -610,8 +607,8 @@ __global__ void advection_le_1st_kernel(kernel_ctxt_t * ktx, /* y direction */ index1 = kernel_coords_index(ktx, ic, jc+1, kc); + hydro_u(hydro, index1, u1); - u1[Y] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index1, Y)]; u = 0.5*(u0[Y] + u1[Y]); index = index0; @@ -625,8 +622,8 @@ __global__ void advection_le_1st_kernel(kernel_ctxt_t * ktx, /* z direction */ index1 = kernel_coords_index(ktx, ic, jc, kc+1); + hydro_u(hydro, index1, u1); - u1[Z] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index1, Z)]; u = 0.5*(u0[Z] + u1[Z]); index = index0; @@ -709,7 +706,7 @@ __global__ void advection_2nd_kernel(kernel_ctxt_t * ktx, advflux_t * flux, for_simt_parallel(kindex, kiter, 1) { - int ia, n; + int n; int ic, jc, kc; int icm1, icp1; int index0, index1; @@ -724,14 +721,13 @@ __global__ void advection_2nd_kernel(kernel_ctxt_t * ktx, advflux_t * flux, icm1 = lees_edw_ic_to_buff(flux->le, ic, -1); icp1 = lees_edw_ic_to_buff(flux->le, ic, +1); - for (ia = 0; ia < NHDIM; ia++) { - u0[ia] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index0, ia)]; - } + hydro_u(hydro, index0, u0); /* west face (ic - 1 and ic) */ index1 = kernel_coords_index(ktx, icm1, jc, kc); - u1[X] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index1, X)]; + hydro_u(hydro, index1, u1); + u = 0.5*(u0[X] + u1[X]); for (n = 0; n < field->nf; n++) { @@ -743,7 +739,8 @@ __global__ void advection_2nd_kernel(kernel_ctxt_t * ktx, advflux_t * flux, /* east face (ic and ic + 1) */ index1 = kernel_coords_index(ktx, icp1, jc, kc); - u1[X] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index1, X)]; + hydro_u(hydro, index1, u1); + u = 0.5*(u0[X] + u1[X]); for (n = 0; n < flux->nf; n++) { @@ -755,7 +752,8 @@ __global__ void advection_2nd_kernel(kernel_ctxt_t * ktx, advflux_t * flux, /* y direction */ index1 = kernel_coords_index(ktx, ic, jc+1, kc); - u1[Y] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index1, Y)]; + hydro_u(hydro, index1, u1); + u = 0.5*(u0[Y] + u1[Y]); for (n = 0; n < flux->nf; n++) { @@ -767,7 +765,8 @@ __global__ void advection_2nd_kernel(kernel_ctxt_t * ktx, advflux_t * flux, /* z direction */ index1 = kernel_coords_index(ktx, ic, jc, kc+1); - u1[Z] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index1, Z)]; + hydro_u(hydro, index1, u1); + u = 0.5*(u0[Z] + u1[Z]); for (n = 0; n < flux->nf; n++) { @@ -824,8 +823,10 @@ __global__ void advection_2nd_kernel_v(kernel_ctxt_t * ktx, advflux_t * flux, for (ia = 0; ia < NHDIM; ia++) { for_simd_v(iv, NSIMDVL) { - u0[ia][iv] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index0[iv], ia)]; + int haddr = addr_rank1(hydro->nsite, NHDIM, index0[iv], ia); + u0[ia][iv] = hydro->u->data[haddr]; } + } /* west face (ic - 1 and ic) */ @@ -834,7 +835,8 @@ __global__ void advection_2nd_kernel_v(kernel_ctxt_t * ktx, advflux_t * flux, for (ia = 0; ia < NHDIM; ia++) { for_simd_v(iv, NSIMDVL) { - u1[ia][iv] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index1[iv], ia)]; + int haddr = addr_rank1(hydro->nsite, NHDIM, index1[iv], ia); + u1[ia][iv] = hydro->u->data[haddr]; } } @@ -853,7 +855,8 @@ __global__ void advection_2nd_kernel_v(kernel_ctxt_t * ktx, advflux_t * flux, for (ia = 0; ia < NHDIM; ia++) { for_simd_v(iv, NSIMDVL) { - u1[ia][iv] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index1[iv], ia)]; + int haddr = addr_rank1(hydro->nsite, NHDIM, index1[iv], ia); + u1[ia][iv] = hydro->u->data[haddr]; } } @@ -873,7 +876,8 @@ __global__ void advection_2nd_kernel_v(kernel_ctxt_t * ktx, advflux_t * flux, for (ia = 0; ia < NHDIM; ia++) { for_simd_v(iv, NSIMDVL) { - u1[ia][iv] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index1[iv], ia)]; + int haddr = addr_rank1(hydro->nsite, NHDIM, index1[iv], ia); + u1[ia][iv] = hydro->u->data[haddr]; } } @@ -893,7 +897,8 @@ __global__ void advection_2nd_kernel_v(kernel_ctxt_t * ktx, advflux_t * flux, for (ia = 0; ia < NHDIM; ia++) { for_simd_v(iv, NSIMDVL) { - u1[ia][iv] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index1[iv], ia)]; + int haddr = addr_rank1(hydro->nsite, NHDIM, index1[iv], ia); + u1[ia][iv] = hydro->u->data[haddr]; } } @@ -1009,7 +1014,8 @@ __global__ void advection_le_3rd_kernel_v(kernel_ctxt_t * ktx, for (ia = 0; ia < NHDIM; ia++) { for_simd_v(iv, NSIMDVL) { - u0[ia][iv] = hydro->u[addr_rank1(hydro->nsite,NHDIM,index0[iv],ia)]; + int haddr = addr_rank1(hydro->nsite, NHDIM, index0[iv], ia); + u0[ia][iv] = hydro->u->data[haddr]; } } @@ -1026,7 +1032,8 @@ __global__ void advection_le_3rd_kernel_v(kernel_ctxt_t * ktx, for (ia = 0; ia < NHDIM; ia++) { for_simd_v(iv, NSIMDVL) { - u1[ia][iv] = hydro->u[addr_rank1(hydro->nsite,NHDIM,index1[iv],ia)]; + int haddr = addr_rank1(hydro->nsite, NHDIM, index1[iv], ia); + u1[ia][iv] = hydro->u->data[haddr]; } } @@ -1058,7 +1065,8 @@ __global__ void advection_le_3rd_kernel_v(kernel_ctxt_t * ktx, for (ia = 0; ia < NHDIM; ia++) { for_simd_v(iv, NSIMDVL) { - u1[ia][iv] = hydro->u[addr_rank1(hydro->nsite,NHDIM,index3[iv],ia)]; + int haddr = addr_rank1(hydro->nsite, NHDIM, index3[iv], ia); + u1[ia][iv] = hydro->u->data[haddr]; } } @@ -1097,7 +1105,8 @@ __global__ void advection_le_3rd_kernel_v(kernel_ctxt_t * ktx, for (ia = 0; ia < NHDIM; ia++) { for_simd_v(iv, NSIMDVL) { - u1[ia][iv] = hydro->u[addr_rank1(fld->nsites,NHDIM,index1[iv],ia)]; + int haddr = addr_rank1(hydro->nsite, NHDIM, index1[iv], ia); + u1[ia][iv] = hydro->u->data[haddr]; } } @@ -1135,7 +1144,8 @@ __global__ void advection_le_3rd_kernel_v(kernel_ctxt_t * ktx, for (ia = 0; ia < NHDIM; ia++) { for_simd_v(iv, NSIMDVL) { - u1[ia][iv] = hydro->u[addr_rank1(hydro->nsite,NHDIM,index1[iv],ia)]; + int haddr = addr_rank1(hydro->nsite, NHDIM, index1[iv], ia); + u1[ia][iv] = hydro->u->data[haddr]; } } @@ -1682,17 +1692,15 @@ __global__ void advflux_cs_1st_kernel(kernel_ctxt_t * ktx, ic = kernel_coords_ic(ktx, kindex); jc = kernel_coords_jc(ktx, kindex); kc = kernel_coords_kc(ktx, kindex); - index0 = kernel_coords_index(ktx, ic, jc, kc); - u0[X] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index0, X)]; - u0[Y] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index0, Y)]; - u0[Z] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index0, Z)]; + index0 = kernel_coords_index(ktx, ic, jc, kc); + hydro_u(hydro, index0, u0); /* x-direction (between ic and ic+1) */ index1 = kernel_coords_index(ktx, ic+1, jc, kc); + hydro_u(hydro, index1, u1); - u1[X] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index1, X)]; u = 0.5*(u0[X] + u1[X]); index = index0; @@ -1706,8 +1714,8 @@ __global__ void advflux_cs_1st_kernel(kernel_ctxt_t * ktx, /* y direction */ index1 = kernel_coords_index(ktx, ic, jc+1, kc); + hydro_u(hydro, index1, u1); - u1[Y] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index1, Y)]; u = 0.5*(u0[Y] + u1[Y]); index = index0; @@ -1721,8 +1729,8 @@ __global__ void advflux_cs_1st_kernel(kernel_ctxt_t * ktx, /* z direction */ index1 = kernel_coords_index(ktx, ic, jc, kc+1); + hydro_u(hydro, index1, u1); - u1[Z] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index1, Z)]; u = 0.5*(u0[Z] + u1[Z]); index = index0; @@ -1775,15 +1783,13 @@ __global__ void advflux_cs_2nd_kernel(kernel_ctxt_t * ktx, advflux_t * flux, kc = kernel_coords_kc(ktx, kindex); index0 = kernel_coords_index(ktx, ic, jc, kc); - - u0[X] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index0, X)]; - u0[Y] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index0, Y)]; - u0[Z] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index0, Z)]; + hydro_u(hydro, index0, u0); /* x-direction (between ic and ic + 1) */ index1 = kernel_coords_index(ktx, ic+1, jc, kc); - u1[X] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index1, X)]; + hydro_u(hydro, index1, u1); + u = 0.5*(u0[X] + u1[X]); for (n = 0; n < flux->nf; n++) { @@ -1795,7 +1801,8 @@ __global__ void advflux_cs_2nd_kernel(kernel_ctxt_t * ktx, advflux_t * flux, /* y direction */ index1 = kernel_coords_index(ktx, ic, jc+1, kc); - u1[Y] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index1, Y)]; + hydro_u(hydro, index1, u1); + u = 0.5*(u0[Y] + u1[Y]); for (n = 0; n < flux->nf; n++) { @@ -1807,7 +1814,8 @@ __global__ void advflux_cs_2nd_kernel(kernel_ctxt_t * ktx, advflux_t * flux, /* z direction */ index1 = kernel_coords_index(ktx, ic, jc, kc+1); - u1[Z] = hydro->u[addr_rank1(hydro->nsite, NHDIM, index1, Z)]; + hydro_u(hydro, index1, u1); + u = 0.5*(u0[Z] + u1[Z]); for (n = 0; n < flux->nf; n++) { @@ -1871,7 +1879,8 @@ __global__ void advflux_cs_3rd_kernel_v(kernel_ctxt_t * ktx, for (ia = 0; ia < NHDIM; ia++) { for_simd_v(iv, NSIMDVL) { - u0[ia][iv] = hydro->u[addr_rank1(hydro->nsite,NHDIM,index0[iv],ia)]; + int haddr = addr_rank1(hydro->nsite, NHDIM, index0[iv], ia); + u0[ia][iv] = hydro->u->data[haddr]; } } @@ -1887,7 +1896,8 @@ __global__ void advflux_cs_3rd_kernel_v(kernel_ctxt_t * ktx, for (ia = 0; ia < NHDIM; ia++) { for_simd_v(iv, NSIMDVL) { - u1[ia][iv] = hydro->u[addr_rank1(hydro->nsite,NHDIM,index3[iv],ia)]; + int haddr = addr_rank1(hydro->nsite, NHDIM, index3[iv], ia); + u1[ia][iv] = hydro->u->data[haddr]; } } @@ -1926,7 +1936,8 @@ __global__ void advflux_cs_3rd_kernel_v(kernel_ctxt_t * ktx, for (ia = 0; ia < NHDIM; ia++) { for_simd_v(iv, NSIMDVL) { - u1[ia][iv] = hydro->u[addr_rank1(field->nsites,NHDIM,index1[iv],ia)]; + int haddr = addr_rank1(hydro->nsite, NHDIM, index1[iv], ia); + u1[ia][iv] = hydro->u->data[haddr]; } } @@ -1964,7 +1975,8 @@ __global__ void advflux_cs_3rd_kernel_v(kernel_ctxt_t * ktx, for (ia = 0; ia < NHDIM; ia++) { for_simd_v(iv, NSIMDVL) { - u1[ia][iv] = hydro->u[addr_rank1(hydro->nsite,NHDIM,index1[iv],ia)]; + int haddr = addr_rank1(hydro->nsite, NHDIM, index1[iv], ia); + u1[ia][iv] = hydro->u->data[haddr]; } } diff --git a/src/blue_phase_beris_edwards.c b/src/blue_phase_beris_edwards.c index 580139280..0c25b0471 100644 --- a/src/blue_phase_beris_edwards.c +++ b/src/blue_phase_beris_edwards.c @@ -630,22 +630,22 @@ void beris_edw_kernel_v(kernel_ctxt_t * ktx, beris_edw_t * be, for_simd_v(iv, NSIMDVL) { if (maskv[iv]) { w[X][X][iv] = 0.5* - (hydro->u[addr_rank1(hydro->nsite, NHDIM, ip1[iv], X)] - - hydro->u[addr_rank1(hydro->nsite, NHDIM, im1[iv], X)]); + (hydro->u->data[addr_rank1(hydro->nsite, NHDIM, ip1[iv], X)] - + hydro->u->data[addr_rank1(hydro->nsite, NHDIM, im1[iv], X)]); } } for_simd_v(iv, NSIMDVL) { if (maskv[iv]) { w[Y][X][iv] = 0.5* - (hydro->u[addr_rank1(hydro->nsite, NHDIM, ip1[iv], Y)] - - hydro->u[addr_rank1(hydro->nsite, NHDIM, im1[iv], Y)]); + (hydro->u->data[addr_rank1(hydro->nsite, NHDIM, ip1[iv], Y)] - + hydro->u->data[addr_rank1(hydro->nsite, NHDIM, im1[iv], Y)]); } } for_simd_v(iv, NSIMDVL) { if (maskv[iv]) { w[Z][X][iv] = 0.5* - (hydro->u[addr_rank1(hydro->nsite, NHDIM, ip1[iv], Z)] - - hydro->u[addr_rank1(hydro->nsite, NHDIM, im1[iv], Z)]); + (hydro->u->data[addr_rank1(hydro->nsite, NHDIM, ip1[iv], Z)] - + hydro->u->data[addr_rank1(hydro->nsite, NHDIM, im1[iv], Z)]); } } @@ -658,18 +658,18 @@ void beris_edw_kernel_v(kernel_ctxt_t * ktx, beris_edw_t * be, for_simd_v(iv, NSIMDVL) { w[X][Y][iv] = 0.5* - (hydro->u[addr_rank1(hydro->nsite, NHDIM, ip1[iv], X)] - - hydro->u[addr_rank1(hydro->nsite, NHDIM, im1[iv], X)]); + (hydro->u->data[addr_rank1(hydro->nsite, NHDIM, ip1[iv], X)] - + hydro->u->data[addr_rank1(hydro->nsite, NHDIM, im1[iv], X)]); } for_simd_v(iv, NSIMDVL) { w[Y][Y][iv] = 0.5* - (hydro->u[addr_rank1(hydro->nsite, NHDIM, ip1[iv], Y)] - - hydro->u[addr_rank1(hydro->nsite, NHDIM, im1[iv], Y)]); + (hydro->u->data[addr_rank1(hydro->nsite, NHDIM, ip1[iv], Y)] - + hydro->u->data[addr_rank1(hydro->nsite, NHDIM, im1[iv], Y)]); } for_simd_v(iv, NSIMDVL) { w[Z][Y][iv] = 0.5* - (hydro->u[addr_rank1(hydro->nsite, NHDIM, ip1[iv], Z)] - - hydro->u[addr_rank1(hydro->nsite, NHDIM, im1[iv], Z)]); + (hydro->u->data[addr_rank1(hydro->nsite, NHDIM, ip1[iv], Z)] - + hydro->u->data[addr_rank1(hydro->nsite, NHDIM, im1[iv], Z)]); } for_simd_v(iv, NSIMDVL) { @@ -681,18 +681,18 @@ void beris_edw_kernel_v(kernel_ctxt_t * ktx, beris_edw_t * be, for_simd_v(iv, NSIMDVL) { w[X][Z][iv] = 0.5* - (hydro->u[addr_rank1(hydro->nsite, NHDIM, ip1[iv], X)] - - hydro->u[addr_rank1(hydro->nsite, NHDIM, im1[iv], X)]); + (hydro->u->data[addr_rank1(hydro->nsite, NHDIM, ip1[iv], X)] - + hydro->u->data[addr_rank1(hydro->nsite, NHDIM, im1[iv], X)]); } for_simd_v(iv, NSIMDVL) { w[Y][Z][iv] = 0.5* - (hydro->u[addr_rank1(hydro->nsite, NHDIM, ip1[iv], Y)] - - hydro->u[addr_rank1(hydro->nsite, NHDIM, im1[iv], Y)]); + (hydro->u->data[addr_rank1(hydro->nsite, NHDIM, ip1[iv], Y)] - + hydro->u->data[addr_rank1(hydro->nsite, NHDIM, im1[iv], Y)]); } for_simd_v(iv, NSIMDVL) { w[Z][Z][iv] = 0.5* - (hydro->u[addr_rank1(hydro->nsite, NHDIM, ip1[iv], Z)] - - hydro->u[addr_rank1(hydro->nsite, NHDIM, im1[iv], Z)]); + (hydro->u->data[addr_rank1(hydro->nsite, NHDIM, ip1[iv], Z)] - + hydro->u->data[addr_rank1(hydro->nsite, NHDIM, im1[iv], Z)]); } /* Enforce tracelessness */ @@ -1080,26 +1080,18 @@ void beris_edw_fix_swd_kernel(kernel_ctxt_t * ktx, colloids_info_t * cinfo, kiterations = kernel_iterations(ktx); for_simt_parallel(kindex, kiterations, 1) { - - int ic, jc, kc, index; - int ia; - - double u[3]; - double rb[3]; - double x, y, z; colloid_t * pc = NULL; - ic = kernel_coords_ic(ktx, kindex); - jc = kernel_coords_jc(ktx, kindex); - kc = kernel_coords_kc(ktx, kindex); - index = kernel_coords_index(ktx, ic, jc, kc); + int ic = kernel_coords_ic(ktx, kindex); + int jc = kernel_coords_jc(ktx, kindex); + int kc = kernel_coords_kc(ktx, kindex); + int index = kernel_coords_index(ktx, ic, jc, kc); /* To include stationary walls. */ if (map->status[index] != MAP_FLUID) { - for (ia = 0; ia < 3; ia++) { - hydro->u[addr_rank1(hydro->nsite, NHDIM, index, ia)] = 0.0; - } + double u0[3] = {0.0, 0.0, 0.0}; + hydro_u_set(hydro, index, u0); } /* Colloids */ @@ -1109,9 +1101,12 @@ void beris_edw_fix_swd_kernel(kernel_ctxt_t * ktx, colloids_info_t * cinfo, /* Set the lattice velocity here to the solid body * rotational velocity: v + Omega x r_b */ - x = noffsetx + ic; - y = noffsety + jc; - z = noffsetz + kc; + double u[3]; + double rb[3]; + + double x = noffsetx + ic; + double y = noffsety + jc; + double z = noffsetz + kc; rb[X] = x - pc->s.r[X]; rb[Y] = y - pc->s.r[Y]; @@ -1125,9 +1120,7 @@ void beris_edw_fix_swd_kernel(kernel_ctxt_t * ktx, colloids_info_t * cinfo, u[Y] += pc->s.v[Y]; u[Z] += pc->s.v[Z]; - for (ia = 0; ia < 3; ia++) { - hydro->u[addr_rank1(hydro->nsite, NHDIM, index, ia)] = u[ia]; - } + hydro_u_set(hydro, index, u); } } diff --git a/src/collision.c b/src/collision.c index e424641b6..a44db7b1a 100644 --- a/src/collision.c +++ b/src/collision.c @@ -330,7 +330,7 @@ void lb_collision_mrt1_site(lb_t * lb, hydro_t * hydro, map_t * map, for (ia = 0; ia < 3; ia++) { for_simd_v(iv, NSIMDVL) { force[ia][iv] = _cp.force_global[ia] - + hydro->f[addr_rank1(hydro->nsite, NHDIM, index0+iv, ia)]; + + hydro->force->data[addr_rank1(hydro->nsite, 3, index0+iv, ia)]; } } @@ -575,7 +575,7 @@ void lb_collision_mrt1_site(lb_t * lb, hydro_t * hydro, map_t * map, /* velocity */ for (ia = 0; ia < 3; ia++) { for_simd_v(iv, NSIMDVL) { - hydro->u[addr_rank1(hydro->nsite, NHDIM, index0+iv, ia)] = u[ia][iv]; + hydro->u->data[addr_rank1(hydro->nsite, 3, index0+iv, ia)] = u[ia][iv]; } } } @@ -588,9 +588,9 @@ void lb_collision_mrt1_site(lb_t * lb, hydro_t * hydro, map_t * map, = fchunk[p*NSIMDVL+iv]; } /* velocity */ - for (ia = 0; ia < 3; ia++) { - hydro->u[addr_rank1(hydro->nsite, NHDIM, index0 + iv, ia)] = u[ia][iv]; + int haddr = addr_rank1(hydro->nsite, 3, index0 + iv, ia); + hydro->u->data[haddr] = u[ia][iv]; } } } @@ -816,15 +816,16 @@ __device__ void lb_collision_mrt2_site(lb_t * lb, hydro_t * hydro, for (ia = 0; ia < 3; ia++) { for_simd_v(iv, NSIMDVL) { - force[ia][iv] = _cp.force_global[ia] - + hydro->f[addr_rank1(hydro->nsite, NHDIM, index0+iv, ia)]; + int haddr = addr_rank1(hydro->nsite, 3, index0 + iv, ia); + force[ia][iv] = _cp.force_global[ia] + hydro->force->data[haddr]; u[ia][iv] = rrho[iv]*(u[ia][iv] + 0.5*force[ia][iv]); } } - for (ia = 0; ia < 3; ia++) { + for (ia = 0; ia < 3; ia++) { for_simd_v(iv, NSIMDVL) { - hydro->u[addr_rank1(hydro->nsite, NHDIM, index0+iv, ia)] = u[ia][iv]; + int haddr = addr_rank1(hydro->nsite, 3, index0 + iv, ia); + hydro->u->data[haddr] = u[ia][iv]; } } diff --git a/src/field.c b/src/field.c index e4af9d4cc..e29d51b55 100644 --- a/src/field.c +++ b/src/field.c @@ -74,7 +74,7 @@ __host__ int field_create(pe_t * pe, cs_t * cs, lees_edw_t * le, assert(pobj); if (field_options_valid(opts) == 0) { - pe_fatal(pe, "Internal error: invalid field options\n"); + pe_fatal(pe, "Internal error: invalid field options: %s\n", name); } obj = (field_t *) calloc(1, sizeof(field_t)); diff --git a/src/hydro.c b/src/hydro.c index 6e78121a7..7123d9782 100644 --- a/src/hydro.c +++ b/src/hydro.c @@ -20,11 +20,8 @@ #include #include "kernel.h" -#include "coords_field.h" -#include "util.h" #include "hydro.h" - -#include "timer.h" +#include "util.h" static int hydro_lees_edwards_parallel(hydro_t * obj); static int hydro_u_write(FILE * fp, int index, void * self); @@ -59,8 +56,7 @@ __global__ void hydro_rho0_kernel(int nsite, double rho0, double * rho); __host__ int hydro_create(pe_t * pe, cs_t * cs, lees_edw_t * le, const hydro_options_t * opts, hydro_t ** pobj) { - int ndevice; - double * tmp; + int ndevice = 0; hydro_t * obj = (hydro_t *) NULL; assert(pe); @@ -81,33 +77,15 @@ __host__ int hydro_create(pe_t * pe, cs_t * cs, lees_edw_t * le, if (le) lees_edw_nsites(le, &obj->nsite); /* Fields */ - { - /* rho: scalar field with no halo swap (halo width zero). */ - field_options_t options = field_options_ndata_nhalo(1, 0); - /* haloscheme */ - /* halo verbose */ - /* usefirsttouch */ - /* iodata */ - field_create(pe, cs, le, "rho", &options, &obj->rho); - - /* eta: scalar viscosity with no halo swap */ - field_create(pe, cs, le, "eta", &options, &obj->eta); - } - - /* Original */ - - obj->u = (double *) mem_aligned_calloc(MEM_PAGESIZE, NHDIM*obj->nsite, - sizeof(double)); - if (obj->u == NULL) pe_fatal(pe, "calloc(hydro->u) failed\n"); - - obj->f = (double *) mem_aligned_calloc(MEM_PAGESIZE, NHDIM*obj->nsite, - sizeof(double)); - if (obj->f == NULL) pe_fatal(pe, "calloc(hydro->f) failed\n"); + /* rho: scalar field with no halo swap (halo width zero). */ + /* vel: vector velocity field */ + /* force: vector body force dencity */ + /* eta: scalar viscosity with no halo swap */ - halo_swap_create_r1(pe, cs, opts->nhcomm, obj->nsite, NHDIM, &obj->halo); - assert(obj->halo); - - halo_swap_handlers_set(obj->halo, halo_swap_pack_rank1, halo_swap_unpack_rank1); + field_create(pe, cs, le, "rho", &opts->rho, &obj->rho); + field_create(pe, cs, le, "vel", &opts->u, &obj->u); + field_create(pe, cs, le, "force", &opts->force, &obj->force); + field_create(pe, cs, le, "eta", &opts->eta, &obj->eta); /* Allocate target copy of structure (or alias) */ @@ -121,31 +99,20 @@ __host__ int hydro_create(pe_t * pe, cs_t * cs, lees_edw_t * le, tdpAssert(tdpMalloc((void **) &obj->target, sizeof(hydro_t))); tdpAssert(tdpMemset(obj->target, 0, sizeof(hydro_t))); - tdpAssert(tdpMalloc((void **) &tmp, NHDIM*obj->nsite*sizeof(double))); - tdpAssert(tdpMemset(tmp, 0, NHDIM*obj->nsite*sizeof(double))); - tdpAssert(tdpMemcpy(&obj->target->u, &tmp, sizeof(double *), - tdpMemcpyHostToDevice)); - - tdpAssert(tdpMalloc((void **) &tmp, NHDIM*obj->nsite*sizeof(double))); - tdpAssert(tdpMemset(tmp, 0, NHDIM*obj->nsite*sizeof(double))); - tdpAssert(tdpMemcpy(&obj->target->f, &tmp, sizeof(double *), - tdpMemcpyHostToDevice)); - tdpAssert(tdpMemcpy(&obj->target->nsite, &obj->nsite, sizeof(int), tdpMemcpyHostToDevice)); /* Fields: target pointers should be copied ... */ tdpAssert(tdpMemcpy(&obj->target->rho, &obj->rho->target, - sizeof(field_t *), - tdpMemcpyHostToDevice)); + sizeof(field_t *), tdpMemcpyHostToDevice)); + tdpAssert(tdpMemcpy(&obj->target->u, &obj->u->target, + sizeof(field_t *), tdpMemcpyHostToDevice)); + tdpAssert(tdpMemcpy(&obj->target->force, &obj->force->target, + sizeof(field_t *), tdpMemcpyHostToDevice)); tdpAssert(tdpMemcpy(&obj->target->eta, &obj->eta->target, - sizeof(field_t *), - tdpMemcpyHostToDevice)); + sizeof(field_t *), tdpMemcpyHostToDevice)); } - hydro_halo_create(obj, &obj->h); - obj->opts = *opts; - *pobj = obj; return 0; @@ -160,32 +127,19 @@ __host__ int hydro_create(pe_t * pe, cs_t * cs, lees_edw_t * le, __host__ int hydro_free(hydro_t * obj) { int ndevice; - double * tmp; assert(obj); tdpGetDeviceCount(&ndevice); - if (ndevice > 0) { - tdpAssert(tdpMemcpy(&tmp, &obj->target->u, sizeof(double *), - tdpMemcpyDeviceToHost)); - tdpAssert(tdpFree(tmp)); - tdpAssert(tdpMemcpy(&tmp, &obj->target->f, sizeof(double *), - tdpMemcpyDeviceToHost)); - tdpAssert(tdpFree(tmp)); - tdpAssert(tdpFree(obj->target)); - } - - halo_swap_free(obj->halo); - hydro_halo_free(&obj->h); + if (ndevice > 0) tdpAssert(tdpFree(obj->target)); if (obj->info) io_info_free(obj->info); field_free(obj->eta); + field_free(obj->force); + field_free(obj->u); field_free(obj->rho); - free(obj->f); - free(obj->u); - free(obj); return 0; @@ -200,8 +154,6 @@ __host__ int hydro_free(hydro_t * obj) { __host__ int hydro_memcpy(hydro_t * obj, tdpMemcpyKind flag) { int ndevice; - double * tmpu; - double * tmpf; assert(obj); @@ -212,27 +164,15 @@ __host__ int hydro_memcpy(hydro_t * obj, tdpMemcpyKind flag) { assert(obj->target == obj); } else { - tdpAssert(tdpMemcpy(&tmpf, &obj->target->f, sizeof(double *), - tdpMemcpyDeviceToHost)); - tdpAssert(tdpMemcpy(&tmpu, &obj->target->u, sizeof(double *), - tdpMemcpyDeviceToHost)); - - switch (flag) { - case tdpMemcpyHostToDevice: - tdpAssert(tdpMemcpy(tmpu, obj->u, NHDIM*obj->nsite*sizeof(double), flag)); - tdpAssert(tdpMemcpy(tmpf, obj->f, NHDIM*obj->nsite*sizeof(double), flag)); + + if (flag == tdpMemcpyHostToDevice) { tdpAssert(tdpMemcpy(&obj->target->nsite, &obj->nsite, sizeof(int), flag)); - break; - case tdpMemcpyDeviceToHost: - tdpAssert(tdpMemcpy(obj->f, tmpf, NHDIM*obj->nsite*sizeof(double), flag)); - tdpAssert(tdpMemcpy(obj->u, tmpu, NHDIM*obj->nsite*sizeof(double), flag)); - break; - default: - pe_fatal(obj->pe, "Bad flag in hydro_memcpy\n"); } /* Fields */ - field_memcpy(obj->rho, flag); - field_memcpy(obj->eta, flag); + field_memcpy(obj->rho, flag); + field_memcpy(obj->u, flag); + field_memcpy(obj->force, flag); + field_memcpy(obj->eta, flag); } return 0; @@ -242,13 +182,16 @@ __host__ int hydro_memcpy(hydro_t * obj, tdpMemcpyKind flag) { * * hydro_u_halo * + * This means the velocity field only. + * If other fields are requirted, use field_halo() explicitly. + * *****************************************************************************/ __host__ int hydro_u_halo(hydro_t * obj) { assert(obj); - hydro_halo_swap(obj, obj->opts.haloscheme); + field_halo(obj->u); return 0; } @@ -257,33 +200,15 @@ __host__ int hydro_u_halo(hydro_t * obj) { * * hydro_halo_swap * - * There is no halo swap in the density at the moment, as it is never - * required. + * Again, this means only the velocity field. * *****************************************************************************/ -__host__ int hydro_halo_swap(hydro_t * obj, hydro_halo_enum_t flag) { - - double * data; +__host__ int hydro_halo_swap(hydro_t * obj, field_halo_enum_t flag) { assert(obj); - switch (flag) { - case HYDRO_U_HALO_HOST: - halo_swap_host_rank1(obj->halo, obj->u, MPI_DOUBLE); - break; - case HYDRO_U_HALO_TARGET: - tdpAssert(tdpMemcpy(&data, &obj->target->u, sizeof(double *), - tdpMemcpyDeviceToHost)); - halo_swap_packed(obj->halo, data); - break; - case HYDRO_U_HALO_OPENMP: - hydro_halo_post(obj); - hydro_halo_wait(obj); - break; - default: - assert(0); - } + field_halo_swap(obj->u, flag); return 0; } @@ -345,139 +270,6 @@ __host__ int hydro_io_info(hydro_t * obj, io_info_t ** info) { return 0; } -/***************************************************************************** - * - * hydro_f_local_set - * - *****************************************************************************/ - -__host__ __device__ -int hydro_f_local_set(hydro_t * obj, int index, const double force[3]) { - - int ia; - - assert(obj); - - for (ia = 0; ia < 3; ia++) { - obj->f[addr_rank1(obj->nsite, NHDIM, index, ia)] = force[ia]; - } - - return 0; -} - -/***************************************************************************** - * - * hydro_f_local - * - *****************************************************************************/ - -__host__ __device__ -int hydro_f_local(hydro_t * obj, int index, double force[3]) { - - int ia; - - assert(obj); - - for (ia = 0; ia < 3; ia++) { - force[ia] = obj->f[addr_rank1(obj->nsite, NHDIM, index, ia)]; - } - - return 0; -} - -/***************************************************************************** - * - * hydro_f_local_add - * - * Accumulate (repeat, accumulate) the fluid force at site index. - * - *****************************************************************************/ - -__host__ __device__ -int hydro_f_local_add(hydro_t * obj, int index, const double force[3]) { - - int ia; - - assert(obj); - - for (ia = 0; ia < 3; ia++) { - obj->f[addr_rank1(obj->nsite, NHDIM, index, ia)] += force[ia]; - } - - return 0; -} - -/***************************************************************************** - * - * hydro_rho_set - * - *****************************************************************************/ - -__host__ __device__ int hydro_rho_set(hydro_t * hydro, int index, double rho) { - - assert(hydro); - - hydro->rho->data[addr_rank0(hydro->nsite, index)] = rho; - - return 0; -} - -/***************************************************************************** - * - * hydro_rho - * - *****************************************************************************/ - -__host__ __device__ int hydro_rho(hydro_t * hydro, int index, double * rho) { - - assert(hydro); - assert(rho); - - *rho = hydro->rho->data[addr_rank0(hydro->nsite, index)]; - - return 0; -} - -/***************************************************************************** - * - * hydro_u_set - * - *****************************************************************************/ - -__host__ __device__ -int hydro_u_set(hydro_t * obj, int index, const double u[3]) { - - int ia; - - assert(obj); - - for (ia = 0; ia < 3; ia++) { - obj->u[addr_rank1(obj->nsite, NHDIM, index, ia)] = u[ia]; - } - - return 0; -} - -/***************************************************************************** - * - * hydro_u - * - *****************************************************************************/ - -__host__ __device__ -int hydro_u(hydro_t * obj, int index, double u[3]) { - - int ia; - - assert(obj); - - for (ia = 0; ia < 3; ia++) { - u[ia] = obj->u[addr_rank1(obj->nsite, NHDIM, index, ia)]; - } - - return 0; -} - /***************************************************************************** * * hydro_u_zero @@ -491,7 +283,7 @@ __host__ int hydro_u_zero(hydro_t * obj, const double uzero[NHDIM]) { assert(obj); - tdpAssert(tdpMemcpy(&u, &obj->target->u, sizeof(double *), + tdpAssert(tdpMemcpy(&u, &obj->u->target->data, sizeof(double *), tdpMemcpyDeviceToHost)); kernel_launch_param(obj->nsite, &nblk, &ntpb); @@ -519,7 +311,7 @@ __host__ int hydro_f_zero(hydro_t * obj, const double fzero[NHDIM]) { assert(obj); assert(obj->target); - tdpAssert(tdpMemcpy(&f, &obj->target->f, sizeof(double *), + tdpAssert(tdpMemcpy(&f, &obj->force->target->data, sizeof(double *), tdpMemcpyDeviceToHost)); kernel_launch_param(obj->nsite, &nblk, &ntpb); @@ -550,6 +342,7 @@ __host__ int hydro_rho0(hydro_t * obj, double rho0) { tdpAssert(tdpMemcpy(&rho, &obj->rho->target->data, sizeof(double *), tdpMemcpyDeviceToHost)); + kernel_launch_param(obj->nsite, &nblk, &ntpb); tdpLaunchKernel(hydro_rho0_kernel, nblk, ntpb, 0, 0, obj->nsite, rho0, rho); @@ -689,9 +482,9 @@ __host__ int hydro_lees_edwards(hydro_t * obj) { index1 = lees_edw_index(obj->le, ic, j1, kc); index2 = lees_edw_index(obj->le, ic, j2, kc); for (ia = 0; ia < 3; ia++) { - obj->u[addr_rank1(obj->nsite, NHDIM, index0, ia)] = ule[ia] + - obj->u[addr_rank1(obj->nsite, NHDIM, index1, ia)]*fr + - obj->u[addr_rank1(obj->nsite, NHDIM, index2, ia)]*(1.0 - fr); + obj->u->data[addr_rank1(obj->nsite, NHDIM, index0, ia)] = ule[ia] + + obj->u->data[addr_rank1(obj->nsite, NHDIM, index1, ia)]*fr + + obj->u->data[addr_rank1(obj->nsite, NHDIM, index2, ia)]*(1.0 - fr); } } } @@ -826,7 +619,7 @@ static int hydro_lees_edwards_parallel(hydro_t * obj) { for (ia = 0; ia < NHDIM; ia++) { j1 = (jc - 1)*NHDIM*(nlocal[Z] + 2*nhalo) + NHDIM*(kc + nhalo - 1) + ia; assert(j1 >= 0 && j1 < nsend); - sbuf[j1] = obj->u[addr_rank1(obj->nsite, NHDIM, index, ia)]; + sbuf[j1] = obj->u->data[addr_rank1(obj->nsite, NHDIM, index, ia)]; } } } @@ -852,7 +645,7 @@ static int hydro_lees_edwards_parallel(hydro_t * obj) { for (kc = 1 - nhalo; kc <= nlocal[Z] + nhalo; kc++) { index = lees_edw_index(obj->le, ib0 + ib, jc, kc); for (ia = 0; ia < NHDIM; ia++) { - obj->u[addr_rank1(obj->nsite, NHDIM, index, ia)] = ule[ia] + obj->u->data[addr_rank1(obj->nsite, NHDIM, index, ia)] = ule[ia] + fr*rbuf[NHDIM*(j1 + kc + nhalo - 1) + ia] + (1.0 - fr)*rbuf[NHDIM*(j2 + kc + nhalo - 1) + ia]; } @@ -987,32 +780,32 @@ __host__ int hydro_u_gradient_tensor(hydro_t * obj, int ic, int jc, int kc, ip1 = lees_edw_ic_to_buff(obj->le, ic, +1); ip1 = lees_edw_index(obj->le, ip1, jc, kc); - w[X][X] = 0.5*(obj->u[addr_rank1(obj->nsite, NHDIM, ip1, X)] - - obj->u[addr_rank1(obj->nsite, NHDIM, im1, X)]); - w[Y][X] = 0.5*(obj->u[addr_rank1(obj->nsite, NHDIM, ip1, Y)] - - obj->u[addr_rank1(obj->nsite, NHDIM, im1, Y)]); - w[Z][X] = 0.5*(obj->u[addr_rank1(obj->nsite, NHDIM, ip1, Z)] - - obj->u[addr_rank1(obj->nsite, NHDIM, im1, Z)]); + w[X][X] = 0.5*(obj->u->data[addr_rank1(obj->nsite, NHDIM, ip1, X)] - + obj->u->data[addr_rank1(obj->nsite, NHDIM, im1, X)]); + w[Y][X] = 0.5*(obj->u->data[addr_rank1(obj->nsite, NHDIM, ip1, Y)] - + obj->u->data[addr_rank1(obj->nsite, NHDIM, im1, Y)]); + w[Z][X] = 0.5*(obj->u->data[addr_rank1(obj->nsite, NHDIM, ip1, Z)] - + obj->u->data[addr_rank1(obj->nsite, NHDIM, im1, Z)]); im1 = lees_edw_index(obj->le, ic, jc - 1, kc); ip1 = lees_edw_index(obj->le, ic, jc + 1, kc); - w[X][Y] = 0.5*(obj->u[addr_rank1(obj->nsite, NHDIM, ip1, X)] - - obj->u[addr_rank1(obj->nsite, NHDIM, im1, X)]); - w[Y][Y] = 0.5*(obj->u[addr_rank1(obj->nsite, NHDIM, ip1, Y)] - - obj->u[addr_rank1(obj->nsite, NHDIM, im1, Y)]); - w[Z][Y] = 0.5*(obj->u[addr_rank1(obj->nsite, NHDIM, ip1, Z)] - - obj->u[addr_rank1(obj->nsite, NHDIM, im1, Z)]); + w[X][Y] = 0.5*(obj->u->data[addr_rank1(obj->nsite, NHDIM, ip1, X)] - + obj->u->data[addr_rank1(obj->nsite, NHDIM, im1, X)]); + w[Y][Y] = 0.5*(obj->u->data[addr_rank1(obj->nsite, NHDIM, ip1, Y)] - + obj->u->data[addr_rank1(obj->nsite, NHDIM, im1, Y)]); + w[Z][Y] = 0.5*(obj->u->data[addr_rank1(obj->nsite, NHDIM, ip1, Z)] - + obj->u->data[addr_rank1(obj->nsite, NHDIM, im1, Z)]); im1 = lees_edw_index(obj->le, ic, jc, kc - 1); ip1 = lees_edw_index(obj->le, ic, jc, kc + 1); - w[X][Z] = 0.5*(obj->u[addr_rank1(obj->nsite, NHDIM, ip1, X)] - - obj->u[addr_rank1(obj->nsite, NHDIM, im1, X)]); - w[Y][Z] = 0.5*(obj->u[addr_rank1(obj->nsite, NHDIM, ip1, Y)] - - obj->u[addr_rank1(obj->nsite, NHDIM, im1, Y)]); - w[Z][Z] = 0.5*(obj->u[addr_rank1(obj->nsite, NHDIM, ip1, Z)] - - obj->u[addr_rank1(obj->nsite, NHDIM, im1, Z)]); + w[X][Z] = 0.5*(obj->u->data[addr_rank1(obj->nsite, NHDIM, ip1, X)] - + obj->u->data[addr_rank1(obj->nsite, NHDIM, im1, X)]); + w[Y][Z] = 0.5*(obj->u->data[addr_rank1(obj->nsite, NHDIM, ip1, Y)] - + obj->u->data[addr_rank1(obj->nsite, NHDIM, im1, Y)]); + w[Z][Z] = 0.5*(obj->u->data[addr_rank1(obj->nsite, NHDIM, ip1, Z)] - + obj->u->data[addr_rank1(obj->nsite, NHDIM, im1, Z)]); /* Enforce tracelessness */ @@ -1206,7 +999,7 @@ __global__ void hydro_accumulate_kernel_v(kernel_ctxt_t * ktx, hydro_t * hydro, for (ia = 0; ia < 3; ia++) { double ftmp = 0.0; for_simd_v_reduction(iv, NSIMDVL, +: ftmp) { - ftmp += hydro->f[addr_rank1(hydro->nsite, NHDIM, index+iv, ia)]; + ftmp += hydro->force->data[addr_rank1(hydro->nsite,NHDIM,index+iv,ia)]; } f[ia] = ftmp; } @@ -1283,9 +1076,6 @@ __global__ void hydro_correct_kernel_v(kernel_ctxt_t * ktx, hydro_t * hydro, int kindex; int kiterations; - int index; - int ia; - int iv; assert(ktx); assert(hydro); @@ -1294,349 +1084,16 @@ __global__ void hydro_correct_kernel_v(kernel_ctxt_t * ktx, hydro_t * hydro, for_simt_parallel(kindex, kiterations, NSIMDVL) { - index = kernel_baseindex(ktx, kindex); + int index = kernel_baseindex(ktx, kindex); - for (ia = 0; ia < 3; ia++) { + for (int ia = 0; ia < 3; ia++) { + int iv = 0; for_simd_v(iv, NSIMDVL) { - hydro->f[addr_rank1(hydro->nsite, NHDIM, index+iv, ia)] += fnet[ia]; + int haddr = addr_rank1(hydro->nsite, NHDIM, index + iv, ia); + hydro->force->data[haddr] += fnet[ia]; } } } return; } - -/***************************************************************************** - * - * hydro_halo_size - * - *****************************************************************************/ - -int hydro_halo_size(cs_limits_t lim) { - - int szx = 1 + lim.imax - lim.imin; - int szy = 1 + lim.jmax - lim.jmin; - int szz = 1 + lim.kmax - lim.kmin; - - return szx*szy*szz; -} - -/***************************************************************************** - * - * hydro_halo_enqueue_send - * - *****************************************************************************/ - -int hydro_halo_enqueue_send(const hydro_t * hydro, hydro_halo_t * h, int ireq) { - - assert(hydro); - assert(h); - assert(1 <= ireq && ireq < h->nvel); - - int nx = 1 + h->slim[ireq].imax - h->slim[ireq].imin; - int ny = 1 + h->slim[ireq].jmax - h->slim[ireq].jmin; - int nz = 1 + h->slim[ireq].kmax - h->slim[ireq].kmin; - - int strz = 1; - int stry = strz*nz; - int strx = stry*ny; - - #pragma omp for nowait - for (int ih = 0; ih < nx*ny*nz; ih++) { - int ic = h->slim[ireq].imin + ih/strx; - int jc = h->slim[ireq].jmin + (ih % strx)/stry; - int kc = h->slim[ireq].kmin + (ih % stry)/strz; - int index = cs_index(hydro->cs, ic, jc, kc); - - for (int ibf = 0; ibf < NHDIM; ibf++) { - int uaddr = addr_rank1(hydro->nsite, NHDIM, index, ibf); - h->send[ireq][ih*NHDIM + ibf] = hydro->u[uaddr]; - } - } - - return 0; -} - -/***************************************************************************** - * - * hydro_halo_dequeue_recv - * - *****************************************************************************/ - -int hydro_halo_dequeue_recv(hydro_t * hydro, const hydro_halo_t * h, int ireq) { - assert(hydro); - assert(h); - assert(1 <= ireq && ireq < h->nvel); - - int nx = 1 + h->rlim[ireq].imax - h->rlim[ireq].imin; - int ny = 1 + h->rlim[ireq].jmax - h->rlim[ireq].jmin; - int nz = 1 + h->rlim[ireq].kmax - h->rlim[ireq].kmin; - - int strz = 1; - int stry = strz*nz; - int strx = stry*ny; - - double * recv = h->recv[ireq]; - - /* Check if this a copy from our own send buffer */ - { - int i = 1 + h->cv[h->nvel - ireq][X]; - int j = 1 + h->cv[h->nvel - ireq][Y]; - int k = 1 + h->cv[h->nvel - ireq][Z]; - - if (h->nbrrank[i][j][k] == h->nbrrank[1][1][1]) recv = h->send[ireq]; - } - - #pragma omp for nowait - for (int ih = 0; ih < nx*ny*nz; ih++) { - int ic = h->rlim[ireq].imin + ih/strx; - int jc = h->rlim[ireq].jmin + (ih % strx)/stry; - int kc = h->rlim[ireq].kmin + (ih % stry)/strz; - int index = cs_index(hydro->cs, ic, jc, kc); - - for (int ibf = 0; ibf < NHDIM; ibf++) { - int uaddr = addr_rank1(hydro->nsite, NHDIM, index, ibf); - hydro->u[uaddr] = recv[ih*NHDIM + ibf]; - } - } - - return 0; -} - -/***************************************************************************** - * - * hydro_halo_create - * - * It's convenient to borrow the velocity notation from the lb for - * the commnunication directions. - * - *****************************************************************************/ - -#include "lb_d3q27.h" - -int hydro_halo_create(const hydro_t * hydro, hydro_halo_t * h) { - - int nlocal[3] = {0}; - - assert(hydro); - assert(h); - - *h = (hydro_halo_t) {0}; - - /* Communictation model */ - - cs_cart_comm(hydro->cs, &h->comm); - - { - LB_CV_D3Q27(cv27); - - h->nvel = 27; - for (int p = 0; p < h->nvel; p++) { - h->cv[p][X] = cv27[p][X]; - h->cv[p][Y] = cv27[p][Y]; - h->cv[p][Z] = cv27[p][Z]; - } - } - - /* Ranks of Cartesian neighbours */ - - { - int dims[3] = {0}; - int periods[3] = {0}; - int coords[3] = {0}; - - MPI_Cart_get(h->comm, 3, dims, periods, coords); - - for (int p = 0; p < h->nvel; p++) { - int nbr[3] = {0}; - int out[3] = {0}; /* Out-of-range is erroneous for non-perioidic dims */ - int i = 1 + h->cv[p][X]; - int j = 1 + h->cv[p][Y]; - int k = 1 + h->cv[p][Z]; - - nbr[X] = coords[X] + h->cv[p][X]; - nbr[Y] = coords[Y] + h->cv[p][Y]; - nbr[Z] = coords[Z] + h->cv[p][Z]; - out[X] = (!periods[X] && (nbr[X] < 0 || nbr[X] > dims[X] - 1)); - out[Y] = (!periods[Y] && (nbr[Y] < 0 || nbr[Y] > dims[Y] - 1)); - out[Z] = (!periods[Z] && (nbr[Z] < 0 || nbr[Z] > dims[Z] - 1)); - - if (out[X] || out[Y] || out[Z]) { - h->nbrrank[i][j][k] = MPI_PROC_NULL; - } - else { - MPI_Cart_rank(h->comm, nbr, &h->nbrrank[i][j][k]); - } - } - /* I must be in the middle */ - assert(h->nbrrank[1][1][1] == cs_cart_rank(hydro->cs)); - } - - /* Set out limits for send and recv regions. */ - - cs_nlocal(hydro->cs, nlocal); - - for (int p = 1; p < h->nvel; p++) { - - int8_t cx = h->cv[p][X]; - int8_t cy = h->cv[p][Y]; - int8_t cz = h->cv[p][Z]; - int nhalo = hydro->nhcomm; - - cs_limits_t send = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; - cs_limits_t recv = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; - - if (cx == -1) send.imax = nhalo; - if (cx == +1) send.imin = send.imax - (nhalo - 1); - if (cy == -1) send.jmax = nhalo; - if (cy == +1) send.jmin = send.jmax - (nhalo - 1); - if (cz == -1) send.kmax = nhalo; - if (cz == +1) send.kmin = send.kmax - (nhalo - 1); - - /* For recv, direction is reversed cf. send */ - if (cx == +1) { recv.imin = 1 - nhalo; recv.imax = 0;} - if (cx == -1) { recv.imin = recv.imax + 1; recv.imax = recv.imax + nhalo;} - if (cy == +1) { recv.jmin = 1 - nhalo; recv.jmax = 0;} - if (cy == -1) { recv.jmin = recv.jmax + 1; recv.jmax = recv.jmax + nhalo;} - if (cz == +1) { recv.kmin = 1 - nhalo; recv.kmax = 0;} - if (cz == -1) { recv.kmin = recv.kmax + 1; recv.kmax = recv.kmax + nhalo;} - - h->slim[p] = send; - h->rlim[p] = recv; - } - - /* Message count and buffers (NHDIM is always 3 for u) */ - - for (int p = 1; p < h->nvel; p++) { - - int scount = NHDIM*hydro_halo_size(h->slim[p]); - int rcount = NHDIM*hydro_halo_size(h->rlim[p]); - - h->send[p] = (double *) calloc(scount, sizeof(double)); - h->recv[p] = (double *) calloc(rcount, sizeof(double)); - assert(h->send[p]); - assert(h->recv[p]); - } - - for (int ireq = 0; ireq < 2*27; ireq++) { - h->request[ireq] = MPI_REQUEST_NULL; - } - - return 0; -} - -/***************************************************************************** - * - * hydro_halo_post - * - *****************************************************************************/ - -int hydro_halo_post(hydro_t * hydro) { - - assert(hydro); - - const int tagbase = 2022; - hydro_halo_t * h = &hydro->h; - - /* Post recvs */ - - TIMER_start(TIMER_HYDRO_HALO_IRECV); - - for (int ireq = 1; ireq < h->nvel; ireq++) { - - int i = 1 + h->cv[h->nvel - ireq][X]; - int j = 1 + h->cv[h->nvel - ireq][Y]; - int k = 1 + h->cv[h->nvel - ireq][Z]; - int mcount = NHDIM*hydro_halo_size(h->rlim[ireq]); - - if (h->nbrrank[i][j][k] == h->nbrrank[1][1][1]) mcount = 0; - - MPI_Irecv(h->recv[ireq], mcount, MPI_DOUBLE, h->nbrrank[i][j][k], - tagbase + ireq, h->comm, h->request + ireq); - } - - TIMER_stop(TIMER_HYDRO_HALO_IRECV); - - /* Load send buffers; post sends */ - - TIMER_start(TIMER_HYDRO_HALO_PACK); - - #pragma omp parallel - { - for (int ireq = 1; ireq < h->nvel; ireq++) { - hydro_halo_enqueue_send(hydro, h, ireq); - } - } - - TIMER_stop(TIMER_HYDRO_HALO_PACK); - - TIMER_start(TIMER_HYDRO_HALO_ISEND); - - for (int ireq = 1; ireq < h->nvel; ireq++) { - int i = 1 + h->cv[ireq][X]; - int j = 1 + h->cv[ireq][Y]; - int k = 1 + h->cv[ireq][Z]; - int mcount = NHDIM*hydro_halo_size(h->slim[ireq]); - - if (h->nbrrank[i][j][k] == h->nbrrank[1][1][1]) mcount = 0; - - MPI_Isend(h->send[ireq], mcount, MPI_DOUBLE, h->nbrrank[i][j][k], - tagbase + ireq, h->comm, h->request + 27 + ireq); - } - - TIMER_stop(TIMER_HYDRO_HALO_ISEND); - - return 0; -} - -/***************************************************************************** - * - * hydro_halo_wait - * - *****************************************************************************/ - -int hydro_halo_wait(hydro_t * hydro) { - - assert(hydro); - - hydro_halo_t * h = &hydro->h; - - TIMER_start(TIMER_HYDRO_HALO_WAITALL); - - MPI_Waitall(2*h->nvel, h->request, MPI_STATUSES_IGNORE); - - TIMER_stop(TIMER_HYDRO_HALO_WAITALL); - - TIMER_start(TIMER_HYDRO_HALO_UNPACK); - - #pragma omp parallel - { - for (int ireq = 1; ireq < h->nvel; ireq++) { - hydro_halo_dequeue_recv(hydro, h, ireq); - } - } - - TIMER_stop(TIMER_HYDRO_HALO_UNPACK); - - return 0; -} - -/***************************************************************************** - * - * hydro_halo_free - * - *****************************************************************************/ - -int hydro_halo_free(hydro_halo_t * h) { - - assert(h); - - for (int p = 1; p < h->nvel; p++) { - free(h->send[p]); - free(h->recv[p]); - } - - *h = (hydro_halo_t) {0}; - - return 0; -} diff --git a/src/hydro.h b/src/hydro.h index 89572b960..1cea9b589 100644 --- a/src/hydro.h +++ b/src/hydro.h @@ -2,7 +2,7 @@ * * hydro.h * - * Various hydrodynamic-related quantities. + * Hydrodynamic sector: rho, velocity, force, viscosity. * * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre @@ -17,36 +17,13 @@ #ifndef LUDWIG_HYDRO_H #define LUDWIG_HYDRO_H -#include - #include "pe.h" #include "coords.h" #include "field.h" -#include "halo_swap.h" #include "leesedwards.h" #include "io_harness.h" #include "hydro_options.h" -/* Halo */ - -#include "cs_limits.h" - -typedef struct hydro_halo_s hydro_halo_t; - -struct hydro_halo_s { - - MPI_Comm comm; /* coords: Cartesian communicator */ - int nbrrank[3][3][3]; /* coords: Cartesian neighbours */ - - int nvel; /* Number of directions involved (2d or 3d) */ - int8_t cv[27][3]; /* Send/recv directions */ - cs_limits_t slim[27]; /* halo: send regions (rectangular) */ - cs_limits_t rlim[27]; /* halo: recv regions (rectangular) */ - double * send[27]; /* halo: send data buffers */ - double * recv[27]; /* halo: recv data buffers */ - MPI_Request request[2*27]; /* halo: array of send/recv requests */ -}; - typedef struct hydro_s hydro_t; /* Data storage: Always a 3-vector NHDIM */ @@ -54,22 +31,19 @@ typedef struct hydro_s hydro_t; #define NHDIM 3 struct hydro_s { + int nsite; /* Allocated sites (local) */ int nhcomm; /* Width of halo region for u field */ - double * u; /* Velocity field (on host) */ - double * f; /* Body force field (on host) */ field_t * rho; /* Density field */ + field_t * u; /* velocity field */ + field_t * force; /* Body force on fluid */ field_t * eta; /* Scalar viscosity field */ pe_t * pe; /* Parallel environment */ cs_t * cs; /* Coordinate system */ lees_edw_t * le; /* Lees Edwards */ - io_info_t * info; /* I/O handler. */ - halo_swap_t * halo; /* Halo driver object */ - - hydro_halo_t h; /* Host OpenMP halo swap */ - hydro_options_t opts; /* Copy of the options requested */ + io_info_t * info; /* I/O handler. Scheduled to be removed. */ hydro_t * target; /* structure on target */ }; @@ -82,7 +56,7 @@ __host__ int hydro_init_io_info(hydro_t * obj, int grid[3], int form_in, __host__ int hydro_memcpy(hydro_t * ibj, tdpMemcpyKind flag); __host__ int hydro_io_info(hydro_t * obj, io_info_t ** info); __host__ int hydro_u_halo(hydro_t * obj); -__host__ int hydro_halo_swap(hydro_t * obj, hydro_halo_enum_t flag); +__host__ int hydro_halo_swap(hydro_t * obj, field_halo_enum_t flag); __host__ int hydro_u_gradient_tensor(hydro_t * obj, int ic, int jc, int kc, double w[3][3]); __host__ int hydro_lees_edwards(hydro_t * obj); @@ -91,21 +65,6 @@ __host__ int hydro_f_zero(hydro_t * obj, const double fzero[3]); __host__ int hydro_u_zero(hydro_t * obj, const double uzero[3]); __host__ int hydro_rho0(hydro_t * hydro, double rho0); -__host__ __device__ int hydro_f_local_set(hydro_t * obj, int index, - const double force[3]); -__host__ __device__ int hydro_f_local(hydro_t * obj, int index, double force[3]); -__host__ __device__ int hydro_f_local_add(hydro_t * obj, int index, - const double force[3]); -__host__ __device__ int hydro_rho_set(hydro_t * hydro, int index, double rho); -__host__ __device__ int hydro_rho(hydro_t * hydro, int index, double * rho); -__host__ __device__ int hydro_u_set(hydro_t * obj, int index, const double u[3]); -__host__ __device__ int hydro_u(hydro_t * obj, int index, double u[3]); - -/* Host halo interface */ - -int hydro_halo_create(const hydro_t * hydro, hydro_halo_t * h); -int hydro_halo_post(hydro_t * hydro); -int hydro_halo_wait(hydro_t * hydro); -int hydro_halo_free(hydro_halo_t * h); +#include "hydro_impl.h" #endif diff --git a/src/hydro_impl.h b/src/hydro_impl.h new file mode 100644 index 000000000..f6f1f23fe --- /dev/null +++ b/src/hydro_impl.h @@ -0,0 +1,144 @@ +/***************************************************************************** + * + * hydro_impl.h + * + * Static inline functions. + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Contributing authors: + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#ifndef LUDWIG_HYDRO_IMPL_H +#define LUDWIG_HYDRO_IMPL_H + +#include "hydro.h" + +/***************************************************************************** + * + * hydro_u_set + * + *****************************************************************************/ + +__host__ __device__ static inline void hydro_u_set(hydro_t * hydro, + int index, + const double u[3]) { + assert(hydro); + hydro->u->data[addr_rank1(hydro->nsite, 3, index, X)] = u[X]; + hydro->u->data[addr_rank1(hydro->nsite, 3, index, Y)] = u[Y]; + hydro->u->data[addr_rank1(hydro->nsite, 3, index, Z)] = u[Z]; + + return; +} + +/***************************************************************************** + * + * hydro_u + * + *****************************************************************************/ + +__host__ __device__ static inline void hydro_u(const hydro_t * hydro, + int index, + double u[3]) { + assert(hydro); + u[X] = hydro->u->data[addr_rank1(hydro->nsite, 3, index, X)]; + u[Y] = hydro->u->data[addr_rank1(hydro->nsite, 3, index, Y)]; + u[Z] = hydro->u->data[addr_rank1(hydro->nsite, 3, index, Z)]; + return; +} + +/***************************************************************************** + * + * hydro_f_local_set + * + *****************************************************************************/ + +__host__ __device__ static inline void hydro_f_local_set(hydro_t * hydro, + int index, + const double f[3]) { + assert(hydro); + hydro->force->data[addr_rank1(hydro->nsite, 3, index, X)] = f[X]; + hydro->force->data[addr_rank1(hydro->nsite, 3, index, Y)] = f[Y]; + hydro->force->data[addr_rank1(hydro->nsite, 3, index, Z)] = f[Z]; + + return; +} + +/***************************************************************************** + * + * hydro_f_local + * + *****************************************************************************/ + +__host__ __device__ static inline void hydro_f_local(const hydro_t * hydro, + int index, + double force[3]) { + assert(hydro); + + force[X] = hydro->force->data[addr_rank1(hydro->nsite, 3, index, X)]; + force[Y] = hydro->force->data[addr_rank1(hydro->nsite, 3, index, Y)]; + force[Z] = hydro->force->data[addr_rank1(hydro->nsite, 3, index, Z)]; + + return; +} + +/***************************************************************************** + * + * hydro_f_local_add + * + * Accumulate (repeat, accumulate) the fluid force at site index. + * + *****************************************************************************/ + +__host__ __device__ static inline void hydro_f_local_add(hydro_t * hydro, + int index, + const double f[3]) { + assert(hydro); + + hydro->force->data[addr_rank1(hydro->nsite, 3, index, X)] += f[X]; + hydro->force->data[addr_rank1(hydro->nsite, 3, index, Y)] += f[Y]; + hydro->force->data[addr_rank1(hydro->nsite, 3, index, Z)] += f[Z]; + + return; +} + +/***************************************************************************** + * + * hydro_rho_set + * + *****************************************************************************/ + +__host__ __device__ static inline void hydro_rho_set(hydro_t * hydro, + int index, + double rho) { + assert(hydro); + + hydro->rho->data[addr_rank0(hydro->nsite, index)] = rho; + + return; +} + +/***************************************************************************** + * + * hydro_rho + * + *****************************************************************************/ + +__host__ __device__ static inline void hydro_rho(const hydro_t * hydro, + int index, + double * rho) { + assert(hydro); + assert(rho); + + *rho = hydro->rho->data[addr_rank0(hydro->nsite, index)]; + + return; +} + +#endif diff --git a/src/hydro_options.c b/src/hydro_options.c index b19750390..6ca05a76a 100644 --- a/src/hydro_options.c +++ b/src/hydro_options.c @@ -31,8 +31,17 @@ hydro_options_t hydro_options_default(void) { - hydro_options_t opts = {.nhcomm = 1, .haloscheme = HYDRO_U_HALO_TARGET}; + int nhcomm = 1; + field_options_t rho = field_options_ndata_nhalo(1, nhcomm); + field_options_t u = field_options_ndata_nhalo(3, nhcomm); + field_options_t f = field_options_ndata_nhalo(3, nhcomm); + field_options_t eta = field_options_ndata_nhalo(1, nhcomm); + hydro_options_t opts = {.nhcomm = 1, + .rho = rho, + .u = u, + .force = f, + .eta = eta}; return opts; } @@ -46,11 +55,36 @@ hydro_options_t hydro_options_default(void) { hydro_options_t hydro_options_nhalo(int nhalo) { - hydro_options_t opts = hydro_options_default(); - assert(nhalo >= 0); - opts.nhcomm = nhalo; + field_options_t rho = field_options_ndata_nhalo(1, nhalo); + field_options_t u = field_options_ndata_nhalo(3, nhalo); + field_options_t f = field_options_ndata_nhalo(3, nhalo); + field_options_t eta = field_options_ndata_nhalo(1, nhalo); + + hydro_options_t opts = {.nhcomm = nhalo, + .rho = rho, + .u = u, + .force = f, + .eta = eta}; + + return opts; +} + +/***************************************************************************** + * + * hydro_options_haloscheme + * + *****************************************************************************/ + +hydro_options_t hydro_options_haloscheme(field_halo_enum_t haloscheme) { + + hydro_options_t opts = hydro_options_default(); + + opts.rho.haloscheme = haloscheme; + opts.u.haloscheme = haloscheme; + opts.force.haloscheme = haloscheme; + opts.eta.haloscheme = haloscheme; return opts; } diff --git a/src/hydro_options.h b/src/hydro_options.h index 3b84872e4..ad53aa2c6 100644 --- a/src/hydro_options.h +++ b/src/hydro_options.h @@ -15,22 +15,22 @@ #ifndef LUDWIG_HYDRO_OPTIONS_H #define LUDWIG_HYDRO_OPTIONS_H -/* Possible halo schemes */ - -typedef enum hydro_halo_enum { - HYDRO_U_HALO_HOST, /* older host version */ - HYDRO_U_HALO_TARGET, /* host or target */ - HYDRO_U_HALO_OPENMP /* Host-only OpenMP implementation */ -} hydro_halo_enum_t; +#include "field_options.h" typedef struct hydro_options_s hydro_options_t; struct hydro_options_s { + int nhcomm; /* Actual halo width */ - hydro_halo_enum_t haloscheme; /* Halo exchange method */ + + field_options_t rho; /* Density field (scalar) */ + field_options_t u; /* Velocity field (vector) */ + field_options_t force; /* Body force density on fluid (vector) */ + field_options_t eta; /* Viscosity field (scalar) */ }; hydro_options_t hydro_options_default(void); hydro_options_t hydro_options_nhalo(int nhalo); +hydro_options_t hydro_options_haloscheme(field_halo_enum_t hs); #endif diff --git a/src/hydro_rt.c b/src/hydro_rt.c index bc0e698e8..da02eadba 100644 --- a/src/hydro_rt.c +++ b/src/hydro_rt.c @@ -79,24 +79,26 @@ static int hydro_do_init(pe_t * pe, rt_t * rt, cs_t * cs, lees_edw_t * le, assert(phydro); if (rt_string_parameter(rt, "hydro_halo_scheme", value, BUFSIZ)) { + field_halo_enum_t haloscheme = FIELD_HALO_TARGET; /* The output is only provided if the key is present to * prevent the regression tests getting upset. */ if (strcmp(value, "hydro_u_halo_target") == 0) { /* Should be the default */ - opts.haloscheme = HYDRO_U_HALO_TARGET; + haloscheme = FIELD_HALO_TARGET; pe_info(pe, "Hydro halo: %s\n", "hydro_u_halo_target"); } else if (strcmp(value, "hydro_u_halo_openmp") == 0) { - opts.haloscheme = HYDRO_U_HALO_OPENMP; + haloscheme = FIELD_HALO_OPENMP; pe_info(pe, "Hydro halo: %s\n", "hydro_u_halo_openmp"); } else if (strcmp(value, "hydro_u_halo_host") == 0) { - opts.haloscheme = HYDRO_U_HALO_HOST; + haloscheme = FIELD_HALO_HOST; pe_info(pe, "Hydro halo: %s\n", "hydro_u_halo_host"); } else { pe_fatal(pe, "hydro_halo_scheme is present but not recongnised\n"); } + opts = hydro_options_haloscheme(haloscheme); } hydro_create(pe, cs, le, &opts, &obj); diff --git a/src/phi_force.c b/src/phi_force.c index 20e4a6691..2a184abf0 100644 --- a/src/phi_force.c +++ b/src/phi_force.c @@ -450,19 +450,21 @@ static int phi_force_flux_divergence(cs_t * cs, hydro_t * hydro, for (jc = 1; jc <= nlocal[Y]; jc++) { for (kc = 1; kc <= nlocal[Z]; kc++) { + double force[3] = {0}; + index = cs_index(cs, ic, jc, kc); indexj = cs_index(cs, ic, jc-1, kc); indexk = cs_index(cs, ic, jc, kc-1); for (ia = 0; ia < 3; ia++) { - hydro->f[addr_rank1(hydro->nsite, NHDIM, index, ia)] - += -(+ fluxe[addr_rank1(nsf,3,index,ia)] - - fluxw[addr_rank1(nsf,3,index,ia)] - + fluxy[addr_rank1(nsf,3,index,ia)] - - fluxy[addr_rank1(nsf,3,indexj,ia)] - + fluxz[addr_rank1(nsf,3,index,ia)] - - fluxz[addr_rank1(nsf,3,indexk,ia)]); + force[ia] = -(+ fluxe[addr_rank1(nsf,3,index,ia)] + - fluxw[addr_rank1(nsf,3,index,ia)] + + fluxy[addr_rank1(nsf,3,index,ia)] + - fluxy[addr_rank1(nsf,3,indexj,ia)] + + fluxz[addr_rank1(nsf,3,index,ia)] + - fluxz[addr_rank1(nsf,3,indexk,ia)]); } + hydro_f_local_add(hydro, index, force); } } } diff --git a/src/phi_force_colloid.c b/src/phi_force_colloid.c index e5491066a..6106405ee 100644 --- a/src/phi_force_colloid.c +++ b/src/phi_force_colloid.c @@ -469,9 +469,9 @@ __global__ void pth_force_fluid_kernel_v(kernel_ctxt_t * ktx, pth_t * pth, /* Store the force on lattice */ - for (ia = 0; ia < 3; ia++) { + for (ia = 0; ia < 3; ia++) { for_simd_v(iv, NSIMDVL) { - hydro->f[addr_rank1(hydro->nsite,NHDIM,index+iv,ia)] + hydro->force->data[addr_rank1(hydro->nsite, NHDIM, index+iv, ia)] += force[ia][iv]*maskv[iv]; } } @@ -649,11 +649,9 @@ __global__ void pth_force_map_kernel(kernel_ctxt_t * ktx, pth_t * pth, } } - /* Store the force on lattice */ + /* Accumulate the force on lattice */ - for (ia = 0; ia < 3; ia++) { - hydro->f[addr_rank1(hydro->nsite, NHDIM, index, ia)] += force[ia]; - } + hydro_f_local_add(hydro, index, force); } /* Next site */ } diff --git a/tests/unit/test_hydro.c b/tests/unit/test_hydro.c index 1c63f6517..70a1df12d 100644 --- a/tests/unit/test_hydro.c +++ b/tests/unit/test_hydro.c @@ -8,7 +8,7 @@ * Edinburgh Soft Matter and Statisitical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2014-2021 The University of Edinburgh + * (c) 2014-2022 The University of Edinburgh * * Contributing authors * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -32,7 +32,7 @@ static int do_test1(pe_t * pe); static int do_test_halo1(pe_t * pe, int nhalo, int nhcomm, - hydro_halo_enum_t haloscheme); + field_halo_enum_t haloscheme); static int do_test_io1(pe_t * pe, int io_format); int test_hydro_rho(pe_t * pe); @@ -53,13 +53,13 @@ int test_hydro_suite(void) { tdpGetDeviceCount(&ndevice); do_test1(pe); - do_test_halo1(pe, 1, 1, HYDRO_U_HALO_TARGET); - do_test_halo1(pe, 2, 2, HYDRO_U_HALO_TARGET); - do_test_halo1(pe, 2, 1, HYDRO_U_HALO_TARGET); + do_test_halo1(pe, 1, 1, FIELD_HALO_TARGET); + do_test_halo1(pe, 2, 2, FIELD_HALO_TARGET); + do_test_halo1(pe, 2, 1, FIELD_HALO_TARGET); if (ndevice == 0) { - do_test_halo1(pe, 1, 1, HYDRO_U_HALO_OPENMP); - do_test_halo1(pe, 2, 1, HYDRO_U_HALO_OPENMP); + do_test_halo1(pe, 1, 1, FIELD_HALO_OPENMP); + do_test_halo1(pe, 2, 1, FIELD_HALO_OPENMP); } test_hydro_rho(pe); @@ -102,7 +102,7 @@ static int do_test1(pe_t * pe) { hydro_create(pe, cs, le, &opts, &hydro); assert(hydro); - assert(hydro->opts.nhcomm == 1); + assert(hydro->nhcomm == 1); index = cs_index(cs, 1, 1, 1); hydro_f_local_set(hydro, index, force); @@ -136,12 +136,12 @@ static int do_test1(pe_t * pe) { *****************************************************************************/ static int do_test_halo1(pe_t * pe, int nhalo, int nhcomm, - hydro_halo_enum_t haloscheme) { + field_halo_enum_t haloscheme) { cs_t * cs = NULL; lees_edw_t * le = NULL; - hydro_options_t opts = {.nhcomm = nhcomm, .haloscheme = haloscheme}; + hydro_options_t opts = hydro_options_default(); hydro_t * hydro = NULL; assert(pe); @@ -151,16 +151,20 @@ static int do_test_halo1(pe_t * pe, int nhalo, int nhcomm, cs_init(cs); lees_edw_create(pe, cs, NULL, &le); + opts.nhcomm = nhcomm; + opts.u.nhcomm = nhcomm; + opts.u.haloscheme = haloscheme; + hydro_create(pe, cs, le, &opts, &hydro); assert(hydro); - test_coords_field_set(cs, NHDIM, hydro->u, MPI_DOUBLE, test_ref_double1); + test_coords_field_set(cs, NHDIM, hydro->u->data, MPI_DOUBLE, test_ref_double1); hydro_memcpy(hydro, tdpMemcpyHostToDevice); hydro_u_halo(hydro); hydro_memcpy(hydro, tdpMemcpyDeviceToHost); - test_coords_field_check(cs, nhcomm, NHDIM, hydro->u, MPI_DOUBLE, + test_coords_field_check(cs, nhcomm, NHDIM, hydro->u->data, MPI_DOUBLE, test_ref_double1); hydro_free(hydro); @@ -207,7 +211,7 @@ static int do_test_io1(pe_t * pe, int io_format) { assert(hydro); hydro_init_io_info(hydro, grid, io_format, io_format); - test_coords_field_set(cs, NHDIM, hydro->u, MPI_DOUBLE, test_ref_double1); + test_coords_field_set(cs, NHDIM, hydro->u->data, MPI_DOUBLE, test_ref_double1); hydro_io_info(hydro, &iohandler); assert(iohandler); diff --git a/tests/unit/test_hydro_options.c b/tests/unit/test_hydro_options.c new file mode 100644 index 000000000..f7b973d57 --- /dev/null +++ b/tests/unit/test_hydro_options.c @@ -0,0 +1,118 @@ +/***************************************************************************** + * + * test_hydro_options.c + * + * + *****************************************************************************/ + +#include + +#include "pe.h" +#include "hydro_options.h" + +int test_hydro_options_default(void); +int test_hydro_options_nhalo(void); +int test_hydro_options_haloscheme(void); + +/***************************************************************************** + * + * test_hydro_options_suite + * + *****************************************************************************/ + +int test_hydro_options_suite(void) { + + pe_t * pe = NULL; + + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + test_hydro_options_default(); + test_hydro_options_nhalo(); + test_hydro_options_haloscheme(); + + pe_info(pe, "%-9s %s\n", "PASS", __FILE__); + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_hydro_options_default + * + *****************************************************************************/ + +int test_hydro_options_default(void) { + + int ifail = 0; + hydro_options_t opts = hydro_options_default(); + + assert(opts.nhcomm == 1); + + assert(opts.rho.ndata == 1); + assert(opts.rho.nhcomm == 1); + assert(opts.rho.haloscheme == FIELD_HALO_TARGET); + ifail = field_options_valid(&opts.rho); + assert(ifail == 1); + + assert(opts.u.ndata == 3); + assert(opts.u.nhcomm == 1); + assert(opts.u.haloscheme == FIELD_HALO_TARGET); + ifail = field_options_valid(&opts.u); + assert(ifail == 1); + + assert(opts.force.ndata == 3); + assert(opts.force.nhcomm == 1); + assert(opts.force.haloscheme == FIELD_HALO_TARGET); + ifail = field_options_valid(&opts.force); + assert(ifail == 1); + + assert(opts.eta.ndata == 1); + assert(opts.eta.nhcomm == 1); + assert(opts.eta.haloscheme == FIELD_HALO_TARGET); + ifail = field_options_valid(&opts.eta); + assert(ifail == 1); + + return ifail; +} + +/***************************************************************************** + * + * test_hydro_options_nhalo + * + *****************************************************************************/ + +int test_hydro_options_nhalo(void) { + + { + int nhalo = 2; + hydro_options_t opts = hydro_options_nhalo(nhalo); + assert(opts.rho.nhcomm == nhalo); + assert(opts.u.nhcomm == nhalo); + assert(opts.force.nhcomm == nhalo); + assert(opts.eta.nhcomm == nhalo); + } + + return 0; +} + +/***************************************************************************** + * + * test_hydro_options_haloscheme + * + *****************************************************************************/ + +int test_hydro_options_haloscheme(void) { + + { + field_halo_enum_t hs = FIELD_HALO_OPENMP; + hydro_options_t opts = hydro_options_haloscheme(hs); + + assert(opts.rho.haloscheme == hs); + assert(opts.u.haloscheme == hs); + assert(opts.force.haloscheme == hs); + assert(opts.eta.haloscheme == hs); + } + + return 0; +} diff --git a/tests/unit/tests.c b/tests/unit/tests.c index 959922082..3c1ab3856 100644 --- a/tests/unit/tests.c +++ b/tests/unit/tests.c @@ -74,6 +74,7 @@ __host__ int tests_create() { test_field_suite(); test_field_grad_suite(); test_halo_suite(); + test_hydro_options_suite(); test_hydro_suite(); test_io_aggregator_suite(); test_io_element_suite(); diff --git a/tests/unit/tests.h b/tests/unit/tests.h index 07cd1562b..5696e77db 100644 --- a/tests/unit/tests.h +++ b/tests/unit/tests.h @@ -53,6 +53,7 @@ int test_field_suite(void); int test_field_grad_suite(void); int test_gradient_d3q27_suite(void); int test_halo_suite(void); +int test_hydro_options_suite(void); int test_hydro_suite(void); int test_io_aggregator_suite(void); int test_io_element_suite(void); From 70842b1bbb075bb1ba64eafaeafcbd085b347cc3 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 6 Dec 2022 17:11:13 +0000 Subject: [PATCH 161/244] Remove unused rho --- src/lb_data_options.c | 3 +-- src/lb_data_options.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lb_data_options.c b/src/lb_data_options.c index d4f004f32..669a63f24 100644 --- a/src/lb_data_options.c +++ b/src/lb_data_options.c @@ -32,8 +32,7 @@ lb_data_options_t lb_data_options_default(void) { .halo = LB_HALO_TARGET, .reportimbalance = 0, .usefirsttouch = 0, - .iodata = io_info_args_default(), - .rho = io_info_args_default()}; + .iodata = io_info_args_default()}; return opts; } diff --git a/src/lb_data_options.h b/src/lb_data_options.h index 7118b3dcd..a4d71f940 100644 --- a/src/lb_data_options.h +++ b/src/lb_data_options.h @@ -38,7 +38,6 @@ struct lb_data_options_s { int usefirsttouch; io_info_args_t iodata; - io_info_args_t rho; }; lb_data_options_t lb_data_options_default(void); From 124bea1ddf4495c38435ecf23c3a7ef0c1e4752b Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 6 Dec 2022 17:11:54 +0000 Subject: [PATCH 162/244] Add memcpy at output --- src/field.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/field.c b/src/field.c index e29d51b55..2372d92c3 100644 --- a/src/field.c +++ b/src/field.c @@ -1679,14 +1679,15 @@ int field_io_write(field_t * field, int timestep, io_event_t * event) { char filename[BUFSIZ] = {0}; io_subfile_name(&meta->subfile, field->name, timestep, filename, BUFSIZ); - io_impl_create(meta, &io); /* CAN FAIL */ + io_impl_create(meta, &io); /* CAN FAIL in principle */ assert(io); + field_memcpy(field, tdpMemcpyDeviceToHost); field_io_aggr_pack(field, io->aggr); io->impl->write(io, filename); - /* REPORT HERE >>>>> */ + /* FIXME: REPORT HERE >>>>> */ io->impl->free(&io); } From 0979c5c9a7ce34d48175c635e8979aebfadb6289 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 6 Dec 2022 17:13:09 +0000 Subject: [PATCH 163/244] Use updated i/o --- src/lb_data.h | 14 ++-- src/model.c | 174 +++++++++++++++++++++++++++----------------------- 2 files changed, 103 insertions(+), 85 deletions(-) diff --git a/src/lb_data.h b/src/lb_data.h index 5e32dce4d..9c220aa54 100644 --- a/src/lb_data.h +++ b/src/lb_data.h @@ -25,6 +25,7 @@ #include "lb_model.h" #include "io_impl.h" +#include "io_event.h" #include "io_harness.h" /* Scheduled for removal. Use io_impl.h */ #include "halo_swap.h" @@ -107,12 +108,11 @@ struct lb_data_s { lb_model_t model; /* Current LB model information */ halo_swap_t * halo; /* halo swap driver */ - /* io_info_t scheduled to be replaced. Use metadata types */ - io_info_t * io_info; /* Distributions */ - io_info_t * io_rho; /* Fluid density (here; could be hydrodynamics...) */ + /* io_info_t scheduled to be replaced. Use metadata types instead */ + io_info_t * io_info; /* Distributions */ - io_element_t ascii; /* ASCII record description */ - io_element_t binary; /* Binary record description */ + io_element_t ascii; /* Per site ASCII information. */ + io_element_t binary; /* Per site binary information. */ io_metadata_t input; /* Metadata for io implementation (input) */ io_metadata_t output; /* Ditto (for output) */ @@ -160,7 +160,6 @@ __host__ int lb_halo(lb_t * lb); __host__ int lb_halo_swap(lb_t * lb, lb_halo_enum_t flag); __host__ int lb_io_info(lb_t * lb, io_info_t ** io_info); __host__ int lb_io_info_set(lb_t * lb, io_info_t * io_info, int fin, int fout); -__host__ int lb_io_rho_set(lb_t *lb, io_info_t * io_rho, int fin, int fout); __host__ __device__ int lb_ndist(lb_t * lb, int * ndist); __host__ __device__ int lb_f(lb_t * lb, int index, int p, int n, double * f); @@ -181,4 +180,7 @@ __host__ int lb_write_buf_ascii(const lb_t * lb, int index, char * buf); __host__ int lb_io_aggr_pack(const lb_t * lb, io_aggregator_t * aggr); __host__ int lb_io_aggr_unpack(lb_t * lb, const io_aggregator_t * aggr); +__host__ int lb_io_write(lb_t * lb, int timestep, io_event_t * event); +__host__ int lb_io_read(lb_t * lb, int timestep, io_event_t * event); + #endif diff --git a/src/model.c b/src/model.c index bc59c2ca0..dd0565be5 100644 --- a/src/model.c +++ b/src/model.c @@ -38,8 +38,6 @@ static int lb_f_read(FILE *, int index, void * self); static int lb_f_read_ascii(FILE *, int index, void * self); static int lb_f_write(FILE *, int index, void * self); static int lb_f_write_ascii(FILE *, int index, void * self); -static int lb_rho_write(FILE *, int index, void * self); -static int lb_rho_write_ascii(FILE *, int index, void * self); static int lb_model_param_init(lb_t * lb); static int lb_init(lb_t * lb); static int lb_data_touch(lb_t * lb); @@ -149,8 +147,26 @@ int lb_data_create(pe_t * pe, cs_t * cs, const lb_data_options_t * options, .endian = io_endianness() }; + /* Record element information */ obj->ascii = ascii; obj->binary = binary; + + /* Establish metadata */ + int ifail = 0; + io_element_t element = {0}; + + if (options->iodata.input.iorformat == IO_RECORD_ASCII) element = ascii; + if (options->iodata.input.iorformat == IO_RECORD_BINARY) element = binary; + ifail = io_metadata_initialise(cs, &options->iodata.input, &element, + &obj->input); + if (ifail != 0) pe_fatal(pe, "lb_data: bad i/o input decomposition\n"); + + + if (options->iodata.output.iorformat == IO_RECORD_ASCII) element = ascii; + if (options->iodata.output.iorformat == IO_RECORD_BINARY) element = binary; + ifail = io_metadata_initialise(cs, &options->iodata.output, &element, + &obj->output); + if (ifail != 0) pe_fatal(pe, "lb_data: bad i/o output decomposition\n"); } *lb = obj; @@ -185,6 +201,9 @@ __host__ int lb_free(lb_t * lb) { tdpFree(lb->target); } + io_metadata_finalise(&lb->input); + io_metadata_finalise(&lb->output); + if (lb->halo) halo_swap_free(lb->halo); if (lb->io_info) io_info_free(lb->io_info); if (lb->f) free(lb->f); @@ -448,36 +467,6 @@ __host__ int lb_io_info_set(lb_t * lb, io_info_t * io_info, int form_in, int for return 0; } -/***************************************************************************** - * - * lb_io_rho_set - * - * There is no input for rho, as it is never required. - * - *****************************************************************************/ - -__host__ int lb_io_rho_set(lb_t * lb, io_info_t * io_rho, int form_in, - int form_out) { - - char string[FILENAME_MAX]; - - assert(lb); - assert(io_rho); - - lb->io_rho = io_rho; - - sprintf(string, "Fluid density (rho)"); - - io_info_set_name(lb->io_rho, string); - io_info_write_set(lb->io_rho, IO_FORMAT_BINARY, lb_rho_write); - io_info_set_bytesize(lb->io_rho, IO_FORMAT_BINARY, sizeof(double)); - io_info_write_set(lb->io_rho, IO_FORMAT_ASCII, lb_rho_write_ascii); - io_info_set_bytesize(lb->io_rho, IO_FORMAT_ASCII, 23*sizeof(char)); - io_info_format_set(lb->io_rho, form_in, form_out); - - return 0; -} - /***************************************************************************** * * lb_io_info @@ -756,53 +745,6 @@ static int lb_f_write_ascii(FILE * fp, int index, void * self) { return 0; } -/****************************************************************************** - * - * lb_rho_write - * - * Write density data to file (binary) - * - *****************************************************************************/ - -static int lb_rho_write(FILE * fp, int index, void * self) { - - size_t iw; - double rho; - lb_t * lb = (lb_t *) self; - - assert(fp); - assert(lb); - - lb_0th_moment(lb, index, LB_RHO, &rho); - iw = fwrite(&rho, sizeof(double), 1, fp); - - if (iw != 1) pe_fatal(lb->pe, "lb_rho-write failed\n"); - - return 0; -} - -/***************************************************************************** - * - * lb_rho_write_ascii - * - *****************************************************************************/ - -static int lb_rho_write_ascii(FILE * fp, int index, void * self) { - - int nwrite; - double rho; - lb_t * lb = (lb_t *) self; - - assert(fp); - assert(lb); - - lb_0th_moment(lb, index, LB_RHO, &rho); - nwrite = fprintf(fp, "%22.15e\n", rho); - if (nwrite != 23) pe_fatal(lb->pe, "lb_rho_write_ascii failed\n"); - - return 0; -} - /***************************************************************************** * * lb_ndist @@ -1610,3 +1552,77 @@ __host__ int lb_io_aggr_unpack(lb_t * lb, const io_aggregator_t * aggr) { return 0; } + +/***************************************************************************** + * + * lb_io_write + * + *****************************************************************************/ + +int lb_io_write(lb_t * lb, int timestep, io_event_t * event) { + + assert(lb); + assert(event); + + const io_metadata_t * meta = &lb->output; + + if (meta->options.mode == IO_MODE_SINGLE) { + /* Old-style */ + char filename[BUFSIZ] = {0}; + sprintf(filename, "dist-%8.8d", timestep); + io_write_data(lb->io_info, filename, lb); + } + + if (meta->options.mode == IO_MODE_MPIIO) { + + io_impl_t * io = NULL; + char filename[BUFSIZ] = {0}; + + io_subfile_name(&meta->subfile, "dist", timestep, filename, BUFSIZ); + io_impl_create(meta, &io); + assert(io); + + lb_memcpy(lb, tdpMemcpyDeviceToHost); + lb_io_aggr_pack(lb, io->aggr); + + io->impl->write(io, filename); + io->impl->free(&io); + } + + return 0; +} + +/***************************************************************************** + * + * lb_io_read + * + *****************************************************************************/ + +int lb_io_read(lb_t * lb, int timestep, io_event_t * event) { + + assert(lb); + assert(event); + + const io_metadata_t * meta = &lb->input; + + if (meta->options.mode == IO_MODE_SINGLE) { + char filename[BUFSIZ] = {0}; + sprintf(filename, "dist-%8.8d", timestep); + io_read_data(lb->io_info, filename, lb); + } + + if (meta->options.mode == IO_MODE_MPIIO) { + io_impl_t * io = NULL; + char filename[BUFSIZ] = {0}; + + io_subfile_name(&meta->subfile, "dist", timestep, filename, BUFSIZ); + io_impl_create(meta, &io); + assert(io); + + io->impl->read(io, filename); + lb_io_aggr_unpack(lb, io->aggr); + io->impl->free(&io); + } + + return 0; +} From c0aaba6c23d6ecb5dbf43a079259bc1c7551cdb4 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 6 Dec 2022 17:15:04 +0000 Subject: [PATCH 164/244] Activate updated i/o --- src/hydro.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/hydro.h | 3 +++ src/hydro_rt.c | 8 +++++++ 3 files changed, 74 insertions(+) diff --git a/src/hydro.c b/src/hydro.c index 7123d9782..a7f8bce62 100644 --- a/src/hydro.c +++ b/src/hydro.c @@ -1097,3 +1097,66 @@ __global__ void hydro_correct_kernel_v(kernel_ctxt_t * ktx, hydro_t * hydro, return; } + +/***************************************************************************** + * + * hydro_io_write + * + * This is intended for configuration output ("rho" and "vel"). If other + * fields are wanted, may wish to use field_io_write() directly. + * + *****************************************************************************/ + +int hydro_io_write(hydro_t * hydro, int timestep, io_event_t * event) { + + /* For backwards compatibility, we currently allow old-style output */ + + const io_metadata_t * rmeta = &hydro->rho->iometadata_out; + const io_metadata_t * umeta = &hydro->u->iometadata_out; + + if (rmeta->options.mode == IO_MODE_MPIIO) { + field_io_write(hydro->rho, timestep, event); + } + + if (umeta->options.mode == IO_MODE_MPIIO) { + field_io_write(hydro->u, timestep, event); + } + else { + /* Old style has "vel" only. */ + char filename[BUFSIZ] = {0}; + sprintf(filename, "%s-%8.8d", hydro->u->name, timestep); + io_write_data(hydro->info, filename, hydro); + } + + return 0; +} + +/***************************************************************************** + * + * hydro_io_read + * + * Configuration input is "rho" and "vel". + * + *****************************************************************************/ + +int hydro_io_read(hydro_t * hydro, int timestep, io_event_t * event) { + + const io_metadata_t * rmeta = &hydro->rho->iometadata_in; + const io_metadata_t * umeta = &hydro->u->iometadata_in; + + if (rmeta->options.mode == IO_MODE_MPIIO) { + field_io_read(hydro->rho, timestep, event); + } + + if (umeta->options.mode == IO_MODE_MPIIO) { + field_io_read(hydro->u, timestep, event); + } + else { + /* Old style has "vel" only. */ + char filename[BUFSIZ] = {0}; + sprintf(filename, "%s-%8.8d", hydro->u->name, timestep); + io_read_data(hydro->info, filename, hydro); + } + + return 0; +} diff --git a/src/hydro.h b/src/hydro.h index 1cea9b589..14da7a9da 100644 --- a/src/hydro.h +++ b/src/hydro.h @@ -65,6 +65,9 @@ __host__ int hydro_f_zero(hydro_t * obj, const double fzero[3]); __host__ int hydro_u_zero(hydro_t * obj, const double uzero[3]); __host__ int hydro_rho0(hydro_t * hydro, double rho0); +__host__ int hydro_io_write(hydro_t * hydro, int timestep, io_event_t * event); +__host__ int hydro_io_read(hydro_t * hydro, int timestep, io_event_t * event); + #include "hydro_impl.h" #endif diff --git a/src/hydro_rt.c b/src/hydro_rt.c index da02eadba..fed227c09 100644 --- a/src/hydro_rt.c +++ b/src/hydro_rt.c @@ -17,6 +17,7 @@ #include "pe.h" #include "runtime.h" +#include "io_info_args_rt.h" #include "hydro_rt.h" static int hydro_do_init(pe_t * pe, rt_t * rt, cs_t * cs, lees_edw_t * le, @@ -78,6 +79,8 @@ static int hydro_do_init(pe_t * pe, rt_t * rt, cs_t * cs, lees_edw_t * le, assert(rt); assert(phydro); + /* Halo scheme options (all fields have the same option!) */ + if (rt_string_parameter(rt, "hydro_halo_scheme", value, BUFSIZ)) { field_halo_enum_t haloscheme = FIELD_HALO_TARGET; /* The output is only provided if the key is present to @@ -101,9 +104,14 @@ static int hydro_do_init(pe_t * pe, rt_t * rt, cs_t * cs, lees_edw_t * le, opts = hydro_options_haloscheme(haloscheme); } + /* User i/o options */ + io_info_args_rt(rt, RT_FATAL, "rho", IO_INFO_READ_WRITE, &opts.rho.iodata); + io_info_args_rt(rt, RT_FATAL, "vel", IO_INFO_READ_WRITE, &opts.u.iodata); + hydro_create(pe, cs, le, &opts, &obj); assert(obj); + /* Old-style input (to be removed) */ rt_int_parameter_vector(rt, "default_io_grid", io_grid); rt_string_parameter(rt, "vel_format", value, BUFSIZ); From 74f02176b867da88c2893363fa678cbb7b71370e Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 6 Dec 2022 17:15:46 +0000 Subject: [PATCH 165/244] Move rho i/o to hydrodynamics --- src/distribution_rt.c | 53 +++---------------------------------------- 1 file changed, 3 insertions(+), 50 deletions(-) diff --git a/src/distribution_rt.c b/src/distribution_rt.c index 46027c7e6..ba1584f6d 100644 --- a/src/distribution_rt.c +++ b/src/distribution_rt.c @@ -111,14 +111,7 @@ int lb_run_time(pe_t * pe, cs_t * cs, rt_t * rt, lb_t ** lb) { /* I/O */ io_info_args_t lb_info = io_info_args_default(); - io_info_args_t rho_info = io_info_args_default(); - io_info_args_rt(rt, RT_FATAL, "lb", IO_INFO_READ_WRITE, &lb_info); - - /* density is output only */ - - io_info_args_rt(rt, RT_FATAL, "rho", IO_INFO_WRITE_ONLY, &rho_info); - } return 0; @@ -147,10 +140,8 @@ int lb_run_time_prev(pe_t * pe, cs_t * cs, rt_t * rt, lb_t ** lb) { char memory = ' '; int lb_form_in = IO_FORMAT_DEFAULT; int lb_form_out = IO_FORMAT_DEFAULT; - int rho_wanted; io_info_t * io_info = NULL; - io_info_t * io_rho = NULL; io_mode_enum_t lb_input_opt_mode = io_mode_default(); lb_data_options_t options = lb_data_options_default(); @@ -205,6 +196,9 @@ int lb_run_time_prev(pe_t * pe, cs_t * cs, rt_t * rt, lb_t ** lb) { } } + /* I/O options */ + + io_info_args_rt(rt, RT_FATAL, "lb", IO_INFO_READ_WRITE, &options.iodata); /* I//O Grid */ @@ -259,41 +253,6 @@ int lb_run_time_prev(pe_t * pe, cs_t * cs, rt_t * rt, lb_t ** lb) { pe_info(pe, "I/O grid: %d %d %d\n", io_grid[X], io_grid[Y], io_grid[Z]); - /* Density io_info: - * - * rho_io_wanted switch to indicate output wanted [no] - * rho_io_freq output frequency - * rho_io_grid grid - * rho_io_format ASCII/BINARY (default BINARY) - * */ - - - int rho_form_in = IO_FORMAT_BINARY; - int rho_form_out = IO_FORMAT_BINARY; - io_info_args_t rhoio = io_info_args_default(); - - rho_wanted = rt_switch(rt, "rho_io_wanted"); - rt_int_parameter_vector(rt, "rho_io_grid", rhoio.grid); - rt_string_parameter(rt,"rho_io_format", string, FILENAME_MAX); - - if (strcmp(string, "ASCII") == 0) { - rho_form_in = IO_FORMAT_ASCII; - rho_form_out = IO_FORMAT_ASCII; - } - - if (rho_wanted) { - pe_info(pe, "Fluid density output\n"); - if (rho_form_out == IO_FORMAT_ASCII) { - pe_info(pe, "Output format: ASCII\n"); - } - if (rho_form_out == IO_FORMAT_BINARY) { - pe_info(pe, "Output format: binary\n"); - } - pe_info(pe, "I/O grid: %d %d %d\n", - rhoio.grid[X], rhoio.grid[Y], rhoio.grid[Z]); - } - - /* Initialise */ lb_data_create(pe, cs, &options, lb); @@ -313,12 +272,6 @@ int lb_run_time_prev(pe_t * pe, cs_t * cs, rt_t * rt, lb_t ** lb) { } lb_io_info_set(*lb, io_info, lb_form_in, lb_form_out); - /* rho i/o */ - - io_info_create(pe, cs, &rhoio, &io_rho); - io_info_metadata_filestub_set(io_rho, "rho"); - lb_io_rho_set(*lb, io_rho, rho_form_in, rho_form_out); - return 0; } From e57012d4c259f3518e5c70ffc792b3c080a4f642 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 6 Dec 2022 17:16:23 +0000 Subject: [PATCH 166/244] Implement updated i/o --- src/ludwig.c | 125 +++++++++++++++++++++++++-------------------------- 1 file changed, 60 insertions(+), 65 deletions(-) diff --git a/src/ludwig.c b/src/ludwig.c index 8d09573a5..1147a9d77 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -337,40 +337,40 @@ static int ludwig_rt(ludwig_t * ludwig) { else { /* Distributions */ - sprintf(filename, "%sdist-%8.8d", subdirectory, ntstep); - pe_info(pe, "Re-starting simulation at step %d with data read from " - "config\nfile(s) %s\n", ntstep, filename); - - lb_io_info(ludwig->lb, &iohandler); - io_read_data(iohandler, filename, ludwig->lb); + pe_info(pe, "Re-starting simulation at step %d with data read from file\n", + ntstep); + { + io_event_t event = {0}; + pe_info(pe, "Reading distribution files for step %d\n", ntstep); + lb_io_read(ludwig->lb, ntstep, &event); + } /* Restart t != 0 for order parameter */ if (ludwig->phi) { - sprintf(filename, "%sphi-%8.8d", subdirectory, ntstep); - pe_info(pe, "files(s) %s\n", filename); - field_io_info(ludwig->phi, &iohandler); - io_read_data(iohandler, filename, ludwig->phi); + io_event_t event = {0}; + pe_info(pe, "Reading phi files for step %d\n", ntstep); + field_io_read(ludwig->phi, ntstep, &event); } if (ludwig->p) { - sprintf(filename, "%sp-%8.8d", subdirectory, ntstep); - pe_info(pe, "files(s) %s\n", filename); - field_io_info(ludwig->p, &iohandler); - io_read_data(iohandler, filename, ludwig->p); + io_event_t event = {0}; + pe_info(pe, "Reading p files for step %d\n", ntstep); + field_io_read(ludwig->p, ntstep, &event); } + if (ludwig->q) { - sprintf(filename, "%sq-%8.8d", subdirectory, ntstep); - pe_info(pe, "files(s) %s\n", filename); - field_io_info(ludwig->q, &iohandler); - io_read_data(iohandler, filename, ludwig->q); + io_event_t event = {0}; + pe_info(pe, "Reading q_ab files for step %d\n", ntstep); + field_io_read(ludwig->q, ntstep, &event); } + if (ludwig->hydro) { - sprintf(filename, "%svel-%8.8d", subdirectory, ntstep); - pe_info(pe, "hydro files(s) %s\n", filename); - hydro_io_info(ludwig->hydro, &iohandler); - io_read_data(iohandler, filename, ludwig->hydro); + io_event_t event = {0}; + pe_info(pe, "Reading rho/vel files for step %d\n", ntstep); + hydro_io_read(ludwig->hydro, ntstep, &event); } + if (ludwig->psi) { psi_io_info(ludwig->psi, &iohandler); sprintf(filename,"%spsi-%8.8d", subdirectory, ntstep); @@ -914,18 +914,9 @@ void ludwig_run(const char * inputfile) { /* Configuration dump */ if (is_config_step()) { - lb_memcpy(ludwig->lb, tdpMemcpyDeviceToHost); + io_event_t event = {0}; pe_info(ludwig->pe, "Writing distribution output at step %d!\n", step); - sprintf(filename, "%sdist-%8.8d", subdirectory, step); - lb_io_info(ludwig->lb, &iohandler); - io_write_data(iohandler, filename, ludwig->lb); - } - - if (is_rho_output_step()) { - /* Potential device-host copy required */ - pe_info(ludwig->pe, "Writing density output at step %d!\n", step); - sprintf(filename, "%srho-%8.8d", subdirectory, step); - io_write_data(ludwig->lb->io_rho, filename, ludwig->lb); + lb_io_write(ludwig->lb, step, &event); } /* is_measurement_step() is here to prevent 'breaking' old input @@ -942,18 +933,22 @@ void ludwig_run(const char * inputfile) { if (is_phi_output_step() || is_config_step()) { if (ludwig->phi) { - field_io_info(ludwig->phi, &iohandler); + io_event_t event = {0}; pe_info(ludwig->pe, "Writing phi file at step %d!\n", step); - sprintf(filename,"%sphi-%8.8d", subdirectory, step); - io_write_data(iohandler, filename, ludwig->phi); + field_io_write(ludwig->phi, step, &event); } + + if (ludwig->p) { + io_event_t event = {0}; + pe_info(ludwig->pe, "Writing p file at step %d!\n", step); + field_io_write(ludwig->p, step, &event); + } + if (ludwig->q) { - field_io_info(ludwig->q, &iohandler); - /* replace q-tensor on former colloid sites */ - io_replace_values(ludwig->q, ludwig->map, MAP_COLLOID, 0.00001); + io_event_t event = {0}; pe_info(ludwig->pe, "Writing q file at step %d!\n", step); - sprintf(filename,"%sq-%8.8d", subdirectory, step); - io_write_data(iohandler, filename, ludwig->q); + io_replace_values(ludwig->q, ludwig->map, MAP_COLLOID, 0.00001); + field_io_write(ludwig->q, step, &event); } } @@ -986,10 +981,9 @@ void ludwig_run(const char * inputfile) { } if (is_vel_output_step() || is_config_step()) { - hydro_io_info(ludwig->hydro, &iohandler); - pe_info(ludwig->pe, "Writing velocity output at step %d!\n", step); - sprintf(filename, "%svel-%8.8d", subdirectory, step); - io_write_data(iohandler, filename, ludwig->hydro); + io_event_t event = {0}; + pe_info(ludwig->pe, "Writing rho/velocity output at step %d!\n", step); + hydro_io_write(ludwig->hydro, step, &event); } /* Print progress report */ @@ -1087,40 +1081,41 @@ void ludwig_run(const char * inputfile) { /* Dump the final configuration if required. */ if (is_config_at_end()) { - lb_memcpy(ludwig->lb, tdpMemcpyDeviceToHost); - sprintf(filename, "%sdist-%8.8d", subdirectory, step); - lb_io_info(ludwig->lb, &iohandler); - io_write_data(iohandler, filename, ludwig->lb); + { + io_event_t event = {0}; + lb_io_write(ludwig->lb, step, &event); + } + sprintf(filename, "%s%s%8.8d", subdirectory, "config.cds", step); if (ncolloid > 0) colloid_io_write(ludwig->cio, filename); if (ludwig->phi) { - field_io_info(ludwig->phi, &iohandler); + io_event_t event = {0}; pe_info(ludwig->pe, "Writing phi file at step %d!\n", step); - sprintf(filename,"%sphi-%8.8d", subdirectory, step); - io_write_data(iohandler, filename, ludwig->phi); + field_io_write(ludwig->phi, step, &event); } - if (ludwig->q) { - /* Run the replacement kernel, then copy back */ - io_replace_field_values(ludwig->q, ludwig->map, MAP_COLLOID, 0.0); - field_memcpy(ludwig->q, tdpMemcpyDeviceToHost); + if (ludwig->p) { + io_event_t event = {0}; + pe_info(ludwig->pe, "Writing p file at step %d!\n", step); + field_io_write(ludwig->p, step, &event); + } - field_io_info(ludwig->q, &iohandler); + if (ludwig->q) { + io_event_t event = {0}; pe_info(ludwig->pe, "Writing q file at step %d!\n", step); - sprintf(filename,"%sq-%8.8d", subdirectory, step); - io_write_data(iohandler, filename, ludwig->q); + io_replace_field_values(ludwig->q, ludwig->map, MAP_COLLOID, 0.0); + field_io_write(ludwig->q, step, &event); } + /* Only strictly required if have order parameter dynamics */ if (ludwig->hydro) { - - hydro_memcpy(ludwig->hydro, tdpMemcpyDeviceToHost); - hydro_io_info(ludwig->hydro, &iohandler); - pe_info(ludwig->pe, "Writing velocity output at step %d!\n", step); - sprintf(filename, "%svel-%8.8d", subdirectory, step); - io_write_data(iohandler, filename, ludwig->hydro); + io_event_t event = {0}; + pe_info(ludwig->pe, "Writing rho/velocity output at step %d!\n", step); + hydro_io_write(ludwig->hydro, step, &event); } + if (ludwig->psi) { psi_io_info(ludwig->psi, &iohandler); pe_info(ludwig->pe, "Writing psi file at step %d!\n", step); From cd245013ab5fac95809d0a451d1a6bd32c09a348 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 6 Dec 2022 17:17:59 +0000 Subject: [PATCH 167/244] Cosmetic update for i/o messages --- tests/regression/d3q19-short/serial-rest-c01.log | 2 +- tests/regression/d3q19-short/serial-rest-c02.log | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/regression/d3q19-short/serial-rest-c01.log b/tests/regression/d3q19-short/serial-rest-c01.log index 8d9744c8d..1f9693313 100644 --- a/tests/regression/d3q19-short/serial-rest-c01.log +++ b/tests/regression/d3q19-short/serial-rest-c01.log @@ -117,7 +117,7 @@ Completed cycle 20 colloid_io_write: writing colloid information to config.cds00000020.001-001 etc -Writing velocity output at step 20! +Writing rho/velocity output at step 20! Timer resolution: 1e-06 second diff --git a/tests/regression/d3q19-short/serial-rest-c02.log b/tests/regression/d3q19-short/serial-rest-c02.log index bfe9decec..b6dfc2823 100644 --- a/tests/regression/d3q19-short/serial-rest-c02.log +++ b/tests/regression/d3q19-short/serial-rest-c02.log @@ -75,9 +75,10 @@ Input radius maximum: 2.3000000e+00 Final cell list: 22 22 22 Final cell lengths: 2.9090909e+00 2.9090909e+00 2.9090909e+00 -Re-starting simulation at step 20 with data read from config -file(s) dist-00000020 -hydro files(s) vel-00000020 +Re-starting simulation at step 20 with data read from file +Reading distribution files for step 20 +Reading rho/vel files for step 20 + Initial conditions. Scalars - total mean variance min max From c97ce04a0266c7c26f6c292035a5ba453f692df5 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 6 Dec 2022 17:18:35 +0000 Subject: [PATCH 168/244] Typo fix in clean rule --- tests/regression/d3q19-io/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/d3q19-io/Makefile b/tests/regression/d3q19-io/Makefile index d68efaf18..caf9fe447 100644 --- a/tests/regression/d3q19-io/Makefile +++ b/tests/regression/d3q19-io/Makefile @@ -40,4 +40,4 @@ mpi4: clean: rm -f *new test-diff* rm -f dist*001 dist*002 phi*001 vel*001 - rm -t *.meta \ No newline at end of file + rm -f *.meta From 3ce9d9a9dad5baf346ef794d3c27117d5459b528 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 6 Dec 2022 17:19:06 +0000 Subject: [PATCH 169/244] Remove unused keys --- tests/regression/d3q19-io/iodrop-mpi1-io1.inp | 2 -- tests/regression/d3q19-io/iodrop-mpi1-io2.inp | 2 -- tests/regression/d3q19-io/iodrop-mpi1-io3.inp | 2 -- tests/regression/d3q19-io/iodrop-mpi2-io1.inp | 2 -- tests/regression/d3q19-io/iodrop-mpi2-io2.inp | 2 -- tests/regression/d3q19-io/iodrop-mpi2-io3.inp | 2 -- tests/regression/d3q19-io/iodrop-mpi4-io1.inp | 2 -- tests/regression/d3q19-io/iodrop-mpi4-io2.inp | 2 -- tests/regression/d3q19-io/iodrop-mpi4-io3.inp | 2 -- tests/regression/d3q19-io/serial-lubr-nf1.inp | 4 ---- tests/regression/d3q19-io/serial-lubr-tf1.inp | 4 ---- tests/regression/d3q19-io/serial-pair-lj1.inp | 4 ---- tests/regression/d3q19-io/serial-pair-ss1.inp | 4 ---- tests/regression/d3q19-io/serial-pair-yk1.inp | 4 ---- tests/regression/d3q19-io/serial-spin-c03.inp | 3 --- 15 files changed, 41 deletions(-) diff --git a/tests/regression/d3q19-io/iodrop-mpi1-io1.inp b/tests/regression/d3q19-io/iodrop-mpi1-io1.inp index 5445ab8a0..3d5435960 100644 --- a/tests/regression/d3q19-io/iodrop-mpi1-io1.inp +++ b/tests/regression/d3q19-io/iodrop-mpi1-io1.inp @@ -42,7 +42,6 @@ free_energy symmetric A -0.00625 B 0.00625 K 0.004 -C 0.0 phi0 0.0 phi_initialisation drop @@ -66,7 +65,6 @@ colloid_init no_colloids # ############################################################################### -boundary_walls_on no periodicity 1_1_1 ############################################################################### diff --git a/tests/regression/d3q19-io/iodrop-mpi1-io2.inp b/tests/regression/d3q19-io/iodrop-mpi1-io2.inp index 675217f36..08ad95780 100644 --- a/tests/regression/d3q19-io/iodrop-mpi1-io2.inp +++ b/tests/regression/d3q19-io/iodrop-mpi1-io2.inp @@ -42,7 +42,6 @@ free_energy symmetric A -0.00625 B 0.00625 K 0.004 -C 0.0 phi0 0.0 phi_initialisation drop @@ -66,7 +65,6 @@ colloid_init no_colloids # ############################################################################### -boundary_walls_on no periodicity 1_1_1 ############################################################################### diff --git a/tests/regression/d3q19-io/iodrop-mpi1-io3.inp b/tests/regression/d3q19-io/iodrop-mpi1-io3.inp index 37ac719df..aa9968e0d 100644 --- a/tests/regression/d3q19-io/iodrop-mpi1-io3.inp +++ b/tests/regression/d3q19-io/iodrop-mpi1-io3.inp @@ -42,7 +42,6 @@ free_energy symmetric A -0.00625 B 0.00625 K 0.004 -C 0.0 phi0 0.0 phi_initialisation drop @@ -66,7 +65,6 @@ colloid_init no_colloids # ############################################################################### -boundary_walls_on no periodicity 1_1_1 ############################################################################### diff --git a/tests/regression/d3q19-io/iodrop-mpi2-io1.inp b/tests/regression/d3q19-io/iodrop-mpi2-io1.inp index f14146f7b..05b5d1d80 100644 --- a/tests/regression/d3q19-io/iodrop-mpi2-io1.inp +++ b/tests/regression/d3q19-io/iodrop-mpi2-io1.inp @@ -43,7 +43,6 @@ free_energy symmetric A -0.00625 B 0.00625 K 0.004 -C 0.0 phi0 0.0 phi_initialisation drop @@ -67,7 +66,6 @@ colloid_init no_colloids # ############################################################################### -boundary_walls_on no periodicity 1_1_1 ############################################################################### diff --git a/tests/regression/d3q19-io/iodrop-mpi2-io2.inp b/tests/regression/d3q19-io/iodrop-mpi2-io2.inp index 0c0887f02..07f1da431 100644 --- a/tests/regression/d3q19-io/iodrop-mpi2-io2.inp +++ b/tests/regression/d3q19-io/iodrop-mpi2-io2.inp @@ -41,7 +41,6 @@ free_energy symmetric A -0.00625 B 0.00625 K 0.004 -C 0.0 phi0 0.0 phi_initialisation drop @@ -65,7 +64,6 @@ colloid_init no_colloids # ############################################################################### -boundary_walls_on no periodicity 1_1_1 ############################################################################### diff --git a/tests/regression/d3q19-io/iodrop-mpi2-io3.inp b/tests/regression/d3q19-io/iodrop-mpi2-io3.inp index c063480c2..e937ad2de 100644 --- a/tests/regression/d3q19-io/iodrop-mpi2-io3.inp +++ b/tests/regression/d3q19-io/iodrop-mpi2-io3.inp @@ -41,7 +41,6 @@ free_energy symmetric A -0.00625 B 0.00625 K 0.004 -C 0.0 phi0 0.0 phi_initialisation drop @@ -65,7 +64,6 @@ colloid_init no_colloids # ############################################################################### -boundary_walls_on no periodicity 1_1_1 ############################################################################### diff --git a/tests/regression/d3q19-io/iodrop-mpi4-io1.inp b/tests/regression/d3q19-io/iodrop-mpi4-io1.inp index 45caa764f..39ff6f3ef 100644 --- a/tests/regression/d3q19-io/iodrop-mpi4-io1.inp +++ b/tests/regression/d3q19-io/iodrop-mpi4-io1.inp @@ -44,7 +44,6 @@ free_energy symmetric A -0.00625 B 0.00625 K 0.004 -C 0.0 phi0 0.0 phi_initialisation drop @@ -68,7 +67,6 @@ colloid_init no_colloids # ############################################################################### -boundary_walls_on no periodicity 1_1_1 ############################################################################### diff --git a/tests/regression/d3q19-io/iodrop-mpi4-io2.inp b/tests/regression/d3q19-io/iodrop-mpi4-io2.inp index 273dea517..6401ad030 100644 --- a/tests/regression/d3q19-io/iodrop-mpi4-io2.inp +++ b/tests/regression/d3q19-io/iodrop-mpi4-io2.inp @@ -44,7 +44,6 @@ free_energy symmetric A -0.00625 B 0.00625 K 0.004 -C 0.0 phi0 0.0 phi_initialisation drop @@ -68,7 +67,6 @@ colloid_init no_colloids # ############################################################################### -boundary_walls_on no periodicity 1_1_1 ############################################################################### diff --git a/tests/regression/d3q19-io/iodrop-mpi4-io3.inp b/tests/regression/d3q19-io/iodrop-mpi4-io3.inp index f4844f731..b15bcae8f 100644 --- a/tests/regression/d3q19-io/iodrop-mpi4-io3.inp +++ b/tests/regression/d3q19-io/iodrop-mpi4-io3.inp @@ -44,7 +44,6 @@ free_energy symmetric A -0.00625 B 0.00625 K 0.004 -C 0.0 phi0 0.0 phi_initialisation drop @@ -68,7 +67,6 @@ colloid_init no_colloids # ############################################################################### -boundary_walls_on no periodicity 1_1_1 ############################################################################### diff --git a/tests/regression/d3q19-io/serial-lubr-nf1.inp b/tests/regression/d3q19-io/serial-lubr-nf1.inp index 50d99b25e..2d6cfc1cf 100644 --- a/tests/regression/d3q19-io/serial-lubr-nf1.inp +++ b/tests/regression/d3q19-io/serial-lubr-nf1.inp @@ -12,7 +12,6 @@ N_cycles 10 size 64_64_64 grid 2_2_2 -reduced_halo no ############################################################################## # @@ -39,7 +38,6 @@ free_energy none # ############################################################################### -colloid_type inactive colloid_init from_file colloid_gravity 0.0_0.0_0.0 @@ -53,7 +51,6 @@ lubrication_normal_cutoff 0.5 # ############################################################################### -boundary_walls_on no periodicity 1_1_1 ############################################################################### @@ -66,7 +63,6 @@ freq_statistics 10 config_at_end no colloid_file_stub colloids-serial-lubr -colloids_io_grid 1_1_1 colloid_io_format_input ASCII_SERIAL ############################################################################### diff --git a/tests/regression/d3q19-io/serial-lubr-tf1.inp b/tests/regression/d3q19-io/serial-lubr-tf1.inp index 7a94c3323..eea3baf68 100644 --- a/tests/regression/d3q19-io/serial-lubr-tf1.inp +++ b/tests/regression/d3q19-io/serial-lubr-tf1.inp @@ -12,7 +12,6 @@ N_cycles 10 size 64_64_64 grid 2_2_2 -reduced_halo no ############################################################################## # @@ -39,7 +38,6 @@ free_energy none # ############################################################################### -colloid_type inactive colloid_init from_file colloid_gravity 0.0_0.0_0.0 @@ -53,7 +51,6 @@ lubrication_tangential_cutoff 0.5 # ############################################################################### -boundary_walls_on no periodicity 1_1_1 ############################################################################### @@ -66,7 +63,6 @@ freq_statistics 10 config_at_end no colloid_file_stub colloids-serial-lubr -colloids_io_grid 1_1_1 colloid_io_format_input ASCII_SERIAL ############################################################################### diff --git a/tests/regression/d3q19-io/serial-pair-lj1.inp b/tests/regression/d3q19-io/serial-pair-lj1.inp index b6cdd2ae0..74661df71 100644 --- a/tests/regression/d3q19-io/serial-pair-lj1.inp +++ b/tests/regression/d3q19-io/serial-pair-lj1.inp @@ -10,7 +10,6 @@ N_cycles 10 size 64_64_64 grid 4_1_1 -reduced_halo no ############################################################################## # @@ -37,7 +36,6 @@ free_energy none # ############################################################################### -colloid_type inactive colloid_init from_file colloid_gravity 0.0_0.0_0.0 @@ -55,7 +53,6 @@ lj_cutoff 8.0 # ############################################################################### -boundary_walls_on no periodicity 1_1_1 ############################################################################### @@ -68,7 +65,6 @@ freq_statistics 10 config_at_end no colloid_file_stub colloids-serial-pair -colloids_io_grid 1_1_1 colloid_io_format_input ASCII_SERIAL ############################################################################### diff --git a/tests/regression/d3q19-io/serial-pair-ss1.inp b/tests/regression/d3q19-io/serial-pair-ss1.inp index 7f05f5215..f7f6fdc3f 100644 --- a/tests/regression/d3q19-io/serial-pair-ss1.inp +++ b/tests/regression/d3q19-io/serial-pair-ss1.inp @@ -10,7 +10,6 @@ N_cycles 10 size 64_64_64 grid 4_1_1 -reduced_halo no ############################################################################## # @@ -37,7 +36,6 @@ free_energy none # ############################################################################### -colloid_type inactive colloid_init from_file colloid_gravity 0.0_0.0_0.0 @@ -56,7 +54,6 @@ soft_sphere_cutoff 2.25 # ############################################################################### -boundary_walls_on no periodicity 1_1_1 ############################################################################### @@ -69,7 +66,6 @@ freq_statistics 10 config_at_end no colloid_file_stub colloids-serial-pair -colloids_io_grid 1_1_1 colloid_io_format_input ASCII_SERIAL ############################################################################### diff --git a/tests/regression/d3q19-io/serial-pair-yk1.inp b/tests/regression/d3q19-io/serial-pair-yk1.inp index 91b89b856..0b1a7b3b5 100644 --- a/tests/regression/d3q19-io/serial-pair-yk1.inp +++ b/tests/regression/d3q19-io/serial-pair-yk1.inp @@ -9,7 +9,6 @@ N_cycles 10 size 64_64_64 grid 2_2_2 -reduced_halo no ############################################################################## # @@ -36,7 +35,6 @@ free_energy none # ############################################################################### -colloid_type inactive colloid_init from_file colloid_gravity 0.0_0.0_0.0 @@ -53,7 +51,6 @@ yukawa_cutoff 16.0 # ############################################################################### -boundary_walls_on no periodicity 1_1_1 ############################################################################### @@ -66,7 +63,6 @@ freq_statistics 10 config_at_end no colloid_file_stub colloids-serial-pair -colloids_io_grid 1_1_1 colloid_io_format_input ASCII_SERIAL ############################################################################### diff --git a/tests/regression/d3q19-io/serial-spin-c03.inp b/tests/regression/d3q19-io/serial-spin-c03.inp index cbb2cfe31..95c363bc1 100644 --- a/tests/regression/d3q19-io/serial-spin-c03.inp +++ b/tests/regression/d3q19-io/serial-spin-c03.inp @@ -43,7 +43,6 @@ free_energy symmetric_lb A -0.0625 B 0.0625 K 0.04 -C 0.0 phi0 0.0 phi_initialisation spinodal @@ -58,7 +57,6 @@ fd_gradient_calculation 3d_27pt_solid ############################################################################### colloid_init from_file -colloid_type inactive colloid_io_format_input ASCII_SERIAL colloid_io_format_output ASCII @@ -81,7 +79,6 @@ soft_sphere_cutoff 0.25 # ############################################################################### -boundary_walls_on no periodicity 1_1_1 ############################################################################### From f6b83a0aa7c1e2c081b922672ff4f3d824a56457 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 6 Dec 2022 17:19:35 +0000 Subject: [PATCH 170/244] Cosmetic fixes --- tests/regression/d3q19-io/iodrop-mpi1-io1.log | 4 ++-- tests/regression/d3q19-io/iodrop-mpi1-io2.log | 6 +++--- tests/regression/d3q19-io/iodrop-mpi1-io3.log | 13 +++++++------ tests/regression/d3q19-io/iodrop-mpi2-io1.log | 4 ++-- tests/regression/d3q19-io/iodrop-mpi2-io2.log | 6 +++--- tests/regression/d3q19-io/iodrop-mpi2-io3.log | 13 +++++++------ tests/regression/d3q19-io/iodrop-mpi4-io1.log | 4 ++-- tests/regression/d3q19-io/iodrop-mpi4-io2.log | 6 +++--- tests/regression/d3q19-io/iodrop-mpi4-io3.log | 12 ++++++------ tests/regression/d3q19-io/serial-lubr-nf1.log | 6 +++--- tests/regression/d3q19-io/serial-lubr-tf1.log | 6 +++--- tests/regression/d3q19-io/serial-pair-lj1.log | 6 +++--- tests/regression/d3q19-io/serial-pair-ss1.log | 6 +++--- tests/regression/d3q19-io/serial-pair-yk1.log | 6 +++--- tests/regression/d3q19-io/serial-spin-c03.log | 6 +++--- 15 files changed, 53 insertions(+), 51 deletions(-) diff --git a/tests/regression/d3q19-io/iodrop-mpi1-io1.log b/tests/regression/d3q19-io/iodrop-mpi1-io1.log index c69a97112..7d55ad382 100644 --- a/tests/regression/d3q19-io/iodrop-mpi1-io1.log +++ b/tests/regression/d3q19-io/iodrop-mpi1-io1.log @@ -31,7 +31,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.25000e+00 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- @@ -49,7 +49,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 diff --git a/tests/regression/d3q19-io/iodrop-mpi1-io2.log b/tests/regression/d3q19-io/iodrop-mpi1-io2.log index 8ef68cc1d..25b6d1d96 100644 --- a/tests/regression/d3q19-io/iodrop-mpi1-io2.log +++ b/tests/regression/d3q19-io/iodrop-mpi1-io2.log @@ -31,7 +31,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.25000e+00 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- @@ -49,7 +49,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 @@ -107,7 +107,7 @@ Velocity - x y z Completed cycle 10 Writing phi file at step 10! -Writing velocity output at step 10! +Writing rho/velocity output at step 10! Timer resolution: 1e-06 second diff --git a/tests/regression/d3q19-io/iodrop-mpi1-io3.log b/tests/regression/d3q19-io/iodrop-mpi1-io3.log index d62b46b5d..44bc5a1a3 100644 --- a/tests/regression/d3q19-io/iodrop-mpi1-io3.log +++ b/tests/regression/d3q19-io/iodrop-mpi1-io3.log @@ -31,7 +31,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.25000e+00 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- @@ -49,7 +49,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 @@ -77,10 +77,11 @@ I/O decomposition: 1 1 1 Advection scheme order: 2 Initialising droplet radius: 8.0000000e+00 Initialising droplet amplitude: 1.0000000e+00 -Re-starting simulation at step 10 with data read from config -file(s) dist-00000010 -files(s) phi-00000010 -hydro files(s) vel-00000010 +Re-starting simulation at step 10 with data read from file +Reading distribution files for step 10 +Reading phi files for step 10 +Reading rho/vel files for step 10 + Gradient calculation: 3d_27pt_fluid Initial conditions. diff --git a/tests/regression/d3q19-io/iodrop-mpi2-io1.log b/tests/regression/d3q19-io/iodrop-mpi2-io1.log index b25460d3c..97bff5373 100644 --- a/tests/regression/d3q19-io/iodrop-mpi2-io1.log +++ b/tests/regression/d3q19-io/iodrop-mpi2-io1.log @@ -31,7 +31,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.25000e+00 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- @@ -49,7 +49,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 diff --git a/tests/regression/d3q19-io/iodrop-mpi2-io2.log b/tests/regression/d3q19-io/iodrop-mpi2-io2.log index 8a4e9ef96..716c5a7e5 100644 --- a/tests/regression/d3q19-io/iodrop-mpi2-io2.log +++ b/tests/regression/d3q19-io/iodrop-mpi2-io2.log @@ -31,7 +31,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.25000e+00 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- @@ -49,7 +49,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 @@ -107,7 +107,7 @@ Velocity - x y z Completed cycle 10 Writing phi file at step 10! -Writing velocity output at step 10! +Writing rho/velocity output at step 10! Timer resolution: 1e-06 second diff --git a/tests/regression/d3q19-io/iodrop-mpi2-io3.log b/tests/regression/d3q19-io/iodrop-mpi2-io3.log index 849a3ddf7..1b676bf35 100644 --- a/tests/regression/d3q19-io/iodrop-mpi2-io3.log +++ b/tests/regression/d3q19-io/iodrop-mpi2-io3.log @@ -31,7 +31,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.25000e+00 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- @@ -49,7 +49,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 @@ -77,10 +77,11 @@ I/O decomposition: 1 1 1 Advection scheme order: 2 Initialising droplet radius: 8.0000000e+00 Initialising droplet amplitude: 1.0000000e+00 -Re-starting simulation at step 10 with data read from config -file(s) dist-00000010 -files(s) phi-00000010 -hydro files(s) vel-00000010 +Re-starting simulation at step 10 with data read from file +Reading distribution files for step 10 +Reading phi files for step 10 +Reading rho/vel files for step 10 + Gradient calculation: 3d_27pt_fluid Initial conditions. diff --git a/tests/regression/d3q19-io/iodrop-mpi4-io1.log b/tests/regression/d3q19-io/iodrop-mpi4-io1.log index 84c9923bc..1dfb01f99 100644 --- a/tests/regression/d3q19-io/iodrop-mpi4-io1.log +++ b/tests/regression/d3q19-io/iodrop-mpi4-io1.log @@ -31,7 +31,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.25000e+00 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- @@ -49,7 +49,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 diff --git a/tests/regression/d3q19-io/iodrop-mpi4-io2.log b/tests/regression/d3q19-io/iodrop-mpi4-io2.log index 2c68b29dd..6b845af7d 100644 --- a/tests/regression/d3q19-io/iodrop-mpi4-io2.log +++ b/tests/regression/d3q19-io/iodrop-mpi4-io2.log @@ -31,7 +31,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.25000e+00 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- @@ -49,7 +49,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 2 1 @@ -107,7 +107,7 @@ Velocity - x y z Completed cycle 10 Writing phi file at step 10! -Writing velocity output at step 10! +Writing rho/velocity output at step 10! Timer resolution: 1e-06 second diff --git a/tests/regression/d3q19-io/iodrop-mpi4-io3.log b/tests/regression/d3q19-io/iodrop-mpi4-io3.log index e89c208c7..b23ab37c6 100644 --- a/tests/regression/d3q19-io/iodrop-mpi4-io3.log +++ b/tests/regression/d3q19-io/iodrop-mpi4-io3.log @@ -31,7 +31,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.25000e+00 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- @@ -49,7 +49,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 2 1 @@ -77,10 +77,10 @@ I/O decomposition: 1 1 1 Advection scheme order: 2 Initialising droplet radius: 8.0000000e+00 Initialising droplet amplitude: 1.0000000e+00 -Re-starting simulation at step 10 with data read from config -file(s) dist-00000010 -files(s) phi-00000010 -hydro files(s) vel-00000010 +Re-starting simulation at step 10 with data read from file +Reading distribution files for step 10 +Reading phi files for step 10 +Reading rho/vel files for step 10 Gradient calculation: 3d_27pt_fluid Initial conditions. diff --git a/tests/regression/d3q19-io/serial-lubr-nf1.log b/tests/regression/d3q19-io/serial-lubr-nf1.log index 08da42098..8c9b69063 100644 --- a/tests/regression/d3q19-io/serial-lubr-nf1.log +++ b/tests/regression/d3q19-io/serial-lubr-nf1.log @@ -33,7 +33,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 @@ -89,8 +89,8 @@ Scalars - total mean variance min max [rho] 135767.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] 1.8820934e-12 -5.7722924e-16 1.4857907e-15 -[fluid ] 1.8841456e-12 0.0000000e+00 0.0000000e+00 +[total ] -2.0521779e-15 -5.7722924e-16 1.4857907e-15 +[fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [colloids] -2.0521779e-15 -5.7722924e-16 1.4857907e-15 Starting time step loop. diff --git a/tests/regression/d3q19-io/serial-lubr-tf1.log b/tests/regression/d3q19-io/serial-lubr-tf1.log index d51ca30c3..93d0d5468 100644 --- a/tests/regression/d3q19-io/serial-lubr-tf1.log +++ b/tests/regression/d3q19-io/serial-lubr-tf1.log @@ -33,7 +33,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 @@ -89,8 +89,8 @@ Scalars - total mean variance min max [rho] 135767.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] 1.8820934e-12 -5.7722924e-16 1.4857907e-15 -[fluid ] 1.8841456e-12 0.0000000e+00 0.0000000e+00 +[total ] -2.0521779e-15 -5.7722924e-16 1.4857907e-15 +[fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [colloids] -2.0521779e-15 -5.7722924e-16 1.4857907e-15 Starting time step loop. diff --git a/tests/regression/d3q19-io/serial-pair-lj1.log b/tests/regression/d3q19-io/serial-pair-lj1.log index c5131d673..e9815a393 100644 --- a/tests/regression/d3q19-io/serial-pair-lj1.log +++ b/tests/regression/d3q19-io/serial-pair-lj1.log @@ -33,7 +33,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 @@ -88,8 +88,8 @@ Scalars - total mean variance min max [rho] 202992.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] 2.8170799e-12 0.0000000e+00 0.0000000e+00 -[fluid ] 2.8170799e-12 0.0000000e+00 0.0000000e+00 +[total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [colloids] 0.0000000e+00 0.0000000e+00 0.0000000e+00 Starting time step loop. diff --git a/tests/regression/d3q19-io/serial-pair-ss1.log b/tests/regression/d3q19-io/serial-pair-ss1.log index 3c8354ac0..59f294ed3 100644 --- a/tests/regression/d3q19-io/serial-pair-ss1.log +++ b/tests/regression/d3q19-io/serial-pair-ss1.log @@ -33,7 +33,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 @@ -91,8 +91,8 @@ Scalars - total mean variance min max [rho] 202992.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] 2.8170799e-12 0.0000000e+00 0.0000000e+00 -[fluid ] 2.8170799e-12 0.0000000e+00 0.0000000e+00 +[total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [colloids] 0.0000000e+00 0.0000000e+00 0.0000000e+00 Starting time step loop. diff --git a/tests/regression/d3q19-io/serial-pair-yk1.log b/tests/regression/d3q19-io/serial-pair-yk1.log index 3b4b119d2..bd6aaf642 100644 --- a/tests/regression/d3q19-io/serial-pair-yk1.log +++ b/tests/regression/d3q19-io/serial-pair-yk1.log @@ -33,7 +33,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 @@ -91,8 +91,8 @@ Scalars - total mean variance min max [rho] 202992.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] 2.8170799e-12 0.0000000e+00 0.0000000e+00 -[fluid ] 2.8170799e-12 0.0000000e+00 0.0000000e+00 +[total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [colloids] 0.0000000e+00 0.0000000e+00 0.0000000e+00 Starting time step loop. diff --git a/tests/regression/d3q19-io/serial-spin-c03.log b/tests/regression/d3q19-io/serial-spin-c03.log index 0e3a02a35..daf2af9d6 100644 --- a/tests/regression/d3q19-io/serial-spin-c03.log +++ b/tests/regression/d3q19-io/serial-spin-c03.log @@ -46,7 +46,7 @@ Lattice Boltzmann distributions Model: d3q19 SIMD vector len: 1 Number of sets: 2 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 @@ -111,8 +111,8 @@ Scalars - total mean variance min max [phi] -9.1839082e+00 -5.8482821e-05 8.3390500e-04 -4.9999850e-02 4.9998965e-02 Momentum - x y z -[total ] 2.1793123e-12 0.0000000e+00 0.0000000e+00 -[fluid ] 2.1793123e-12 0.0000000e+00 0.0000000e+00 +[total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [colloids] 0.0000000e+00 0.0000000e+00 0.0000000e+00 Starting time step loop. From 4a95d2278f3d523327d9f39a613e0445ddc8a12f Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 9 Dec 2022 10:10:26 +0000 Subject: [PATCH 171/244] Add json <-> object routines --- src/io_options.c | 197 +++++++++++++++++++++++++++++ src/io_options.h | 15 ++- src/io_options_rt.c | 24 +--- tests/unit/test_io_options.c | 218 ++++++++++++++++++++++++++++++++ tests/unit/test_io_options_rt.c | 4 + 5 files changed, 439 insertions(+), 19 deletions(-) diff --git a/src/io_options.c b/src/io_options.c index dd07daea7..1a6031832 100644 --- a/src/io_options.c +++ b/src/io_options.c @@ -16,7 +16,11 @@ *****************************************************************************/ #include +#include + #include "io_options.h" +#include "util.h" +#include "util_json.h" /* Defaults */ @@ -116,6 +120,7 @@ __host__ int io_options_mode_valid(io_mode_enum_t mode) { valid += (mode == IO_MODE_SINGLE); valid += (mode == IO_MODE_MULTIPLE); + valid += (mode == IO_MODE_ANSI); valid += (mode == IO_MODE_MPIIO); return valid; @@ -202,6 +207,11 @@ __host__ io_options_t io_options_with_mode(io_mode_enum_t mode) { options.metadata_version = IO_METADATA_MULTI_V1; /* otherwise defaults */ break; + case IO_MODE_ANSI: + options.mode = IO_MODE_ANSI; + options.iorformat = IO_RECORD_BINARY; + options.metadata_version = IO_METADATA_SINGLE_V1; + break; case IO_MODE_MPIIO: options.mode = IO_MODE_MPIIO; options.iorformat = IO_RECORD_BINARY; @@ -254,3 +264,190 @@ __host__ io_options_t io_options_with_iogrid(io_mode_enum_t mode, return options; } + +/***************************************************************************** + * + * io_mode_to_string + * + * Always return a lower-case string. + * + *****************************************************************************/ + +__host__ const char * io_mode_to_string(io_mode_enum_t mode) { + + const char * str = NULL; + + switch (mode) { + case IO_MODE_SINGLE: + str = "single"; + break; + case IO_MODE_MULTIPLE: + str = "multiple"; + break; + case IO_MODE_ANSI: + str = "ansi"; + break; + case IO_MODE_MPIIO: + str = "mpiio"; + break; + default: + str = "invalid"; + } + + return str; +} + +/***************************************************************************** + * + * io_mode_from_string + * + *****************************************************************************/ + +__host__ io_mode_enum_t io_mode_from_string(const char * str) { + + int mode = IO_MODE_INVALID; + char value[BUFSIZ] = {0}; + + assert(str); + + strncpy(value, str, BUFSIZ-1); + util_str_tolower(value, strlen(value)); + + if (strcmp(value, "single") == 0) mode = IO_MODE_SINGLE; + if (strcmp(value, "multiple") == 0) mode = IO_MODE_MULTIPLE; + if (strcmp(value, "ansi") == 0) mode = IO_MODE_ANSI; + if (strcmp(value, "mpiio") == 0) mode = IO_MODE_MPIIO; + + return mode; +} + +/***************************************************************************** + * + * io_record_format_to_string + * + *****************************************************************************/ + +const char * io_record_format_to_string(io_record_format_enum_t ior) { + + const char * str = NULL; + + switch (ior) { + case IO_RECORD_ASCII: + str = "ascii"; + break; + case IO_RECORD_BINARY: + str = "binary"; + break; + default: + str = "invalid"; + } + + return str; +} + +/***************************************************************************** + * + * io_record_format_from_string + * + *****************************************************************************/ + +io_record_format_enum_t io_record_format_from_string(const char * str) { + + io_record_format_enum_t ior = IO_RECORD_INVALID; + char value[BUFSIZ] = {0}; + + assert(str); + + strncpy(value, str, BUFSIZ-1); + util_str_tolower(value, strlen(value)); + + if (strcmp(value, "ascii") == 0) ior = IO_RECORD_ASCII; + if (strcmp(value, "binary") == 0) ior = IO_RECORD_BINARY; + + return ior; +} + +/***************************************************************************** + * + * io_options_to_json + * + *****************************************************************************/ + +__host__ int io_options_to_json(const io_options_t * opts, cJSON ** json) { + + int ifail = 0; + + if (json == NULL || *json != NULL) { + ifail = -1; + } + else { + + /* Seven key/value pairs */ + cJSON * myjson = cJSON_CreateObject(); + cJSON * iogrid = cJSON_CreateIntArray(opts->iogrid, 3); + + cJSON_AddStringToObject(myjson, "Mode", io_mode_to_string(opts->mode)); + cJSON_AddStringToObject(myjson, "Record format", + io_record_format_to_string(opts->iorformat)); + cJSON_AddNumberToObject(myjson, "Metadata version", + opts->metadata_version); + cJSON_AddBoolToObject(myjson, "Report", opts->report); + cJSON_AddBoolToObject(myjson, "Asynchronous", opts->asynchronous); + cJSON_AddNumberToObject(myjson, "Compression level", + opts->compression_levl); + cJSON_AddItemToObject(myjson, "I/O grid", iogrid); + + *json = myjson; + } + + return ifail; +} + +/***************************************************************************** + * + * io_options_from_json + * + *****************************************************************************/ + +__host__ int io_options_from_json(const cJSON * json, io_options_t * opts) { + + int ifail = 0; + + if (json == NULL || opts == NULL) { + ifail = -1; + } + else { + /* Note metadata version is just an integer. */ + cJSON * mode = cJSON_GetObjectItemCaseSensitive(json, "Mode"); + cJSON * ior = cJSON_GetObjectItemCaseSensitive(json, "Record format"); + cJSON * meta = cJSON_GetObjectItemCaseSensitive(json, "Metadata version"); + cJSON * report = cJSON_GetObjectItemCaseSensitive(json, "Report"); + cJSON * async = cJSON_GetObjectItemCaseSensitive(json, "Asynchronous"); + cJSON * level= cJSON_GetObjectItemCaseSensitive(json, "Compression level"); + cJSON * iogrid = cJSON_GetObjectItemCaseSensitive(json, "I/O grid"); + + if (mode) { + char * str = cJSON_GetStringValue(mode); + opts->mode = io_mode_from_string(str); + } + if (ior) { + char * str = cJSON_GetStringValue(ior); + opts->iorformat = io_record_format_from_string(str); + } + if (meta) opts->metadata_version = cJSON_GetNumberValue(meta); + if (report) opts->report = cJSON_IsTrue(report); + if (async) opts->asynchronous = cJSON_IsTrue(async); + if (level) opts->compression_levl = cJSON_GetNumberValue(level); + + /* Errors */ + if (mode == NULL) ifail += 1; + if (ior == NULL) ifail += 2; + if (meta == NULL) ifail += 4; + if (report == NULL) ifail += 8; + if (async == NULL) ifail += 16; + if (level == NULL) ifail += 32; + if (3 != util_json_to_int_array(iogrid, opts->iogrid, 3)) ifail += 64; + } + + return ifail; +} diff --git a/src/io_options.h b/src/io_options.h index d841e8a62..2724f08ac 100644 --- a/src/io_options.h +++ b/src/io_options.h @@ -8,7 +8,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2020 The University of Edinburgh + * (c) 2020-2022 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -19,6 +19,7 @@ #define LUDWIG_IO_OPTIONS_H #include "pe.h" +#include "util_cJSON.h" /* * I/O Modes: @@ -28,11 +29,12 @@ * IO_MODE_MULTIPLE: one or more files with decomposition dependent order; * output must be post-processed to recover serial order. * - * IO_MODE_ANSI ANSI implementation PENDING + * IO_MODE_ANSI ANSI implementation currently means IO_MODE_SINGLE * IO_MODE_MPIO MPIO-IO implementation */ enum io_mode_enum {IO_MODE_INVALID, IO_MODE_SINGLE, IO_MODE_MULTIPLE, + IO_MODE_ANSI, IO_MODE_MPIIO}; /* Record formats: */ @@ -82,4 +84,13 @@ __host__ int io_options_mode_valid(io_mode_enum_t mode); __host__ int io_options_record_format_valid(io_record_format_enum_t iorformat); __host__ int io_options_metadata_version_valid(const io_options_t * options); +/* Various additional utility routines */ + +__host__ const char * io_mode_to_string(io_mode_enum_t mode); +__host__ io_mode_enum_t io_mode_from_string(const char * string); +__host__ const char * io_record_format_to_string(io_record_format_enum_t ior); +__host__ io_record_format_enum_t io_record_format_from_string(const char * s); +__host__ int io_options_to_json(const io_options_t * opts, cJSON ** json); +__host__ int io_options_from_json(const cJSON * json, io_options_t * opts); + #endif diff --git a/src/io_options_rt.c b/src/io_options_rt.c index fce16b3f2..20c6b3a0b 100644 --- a/src/io_options_rt.c +++ b/src/io_options_rt.c @@ -88,13 +88,7 @@ __host__ int io_options_rt_mode(rt_t * rt, rt_enum_t lv, const char * key, key_present = rt_string_parameter(rt, key, value, BUFSIZ); if (key_present) { - io_mode_enum_t user_mode = IO_MODE_INVALID; - - util_str_tolower(value, strlen(value)); - - if (strcmp(value, "single") == 0) user_mode = IO_MODE_SINGLE; - if (strcmp(value, "multiple") == 0) user_mode = IO_MODE_MULTIPLE; - if (strcmp(value, "mpiio") == 0) user_mode = IO_MODE_MPIIO; + io_mode_enum_t user_mode = io_mode_from_string(value); if (io_options_mode_valid(user_mode) == 1) { *mode = user_mode; @@ -117,7 +111,8 @@ __host__ int io_options_rt_mode(rt_t * rt, rt_enum_t lv, const char * key, * * io_options_rt_rformat * - * If the key is not present IO_RECORD_INVALID is returned. + * If the key is present and valid, return RT_KEY_OK and set the + * iorformat. * *****************************************************************************/ @@ -126,21 +121,16 @@ __host__ int io_options_rt_record_format(rt_t * rt, rt_enum_t lv, io_record_format_enum_t * iorformat) { int ifail = RT_KEY_MISSING; int key_present = 0; - char value[BUFSIZ] = {0}; + char str[BUFSIZ] = {0}; assert(rt); assert(key); assert(iorformat); - key_present = rt_string_parameter(rt, key, value, BUFSIZ); + key_present = rt_string_parameter(rt, key, str, BUFSIZ); if (key_present) { - io_record_format_enum_t user_iorformat = IO_RECORD_INVALID; - - util_str_tolower(value, strlen(value)); - - if (strcmp(value, "ascii") == 0) user_iorformat = IO_RECORD_ASCII; - if (strcmp(value, "binary") == 0) user_iorformat = IO_RECORD_BINARY; + io_record_format_enum_t user_iorformat = io_record_format_from_string(str); if (io_options_record_format_valid(user_iorformat) == 1) { ifail = RT_KEY_OK; @@ -150,7 +140,7 @@ __host__ int io_options_rt_record_format(rt_t * rt, rt_enum_t lv, ifail = RT_KEY_INVALID; rt_vinfo(rt, lv, "I/O record format present but value not recognised\n"); rt_vinfo(rt, lv, "key: %s\n", key); - rt_vinfo(rt, lv, "value: %s\n", value); + rt_vinfo(rt, lv, "value: %s\n", str); rt_vinfo(rt, lv, "Should be either 'ascii' or 'binary'\n"); rt_fatal(rt, lv, "Please check the input file and try again!\n"); } diff --git a/tests/unit/test_io_options.c b/tests/unit/test_io_options.c index 75015d923..ba9a7b2eb 100644 --- a/tests/unit/test_io_options.c +++ b/tests/unit/test_io_options.c @@ -16,6 +16,8 @@ *****************************************************************************/ #include +#include +#include #include "pe.h" #include "io_options.h" @@ -27,6 +29,12 @@ __host__ int test_io_options_default(void); __host__ int test_io_options_with_mode(void); __host__ int test_io_options_with_format(void); __host__ int test_io_options_with_iogrid(void); +__host__ int test_io_mode_to_string(void); +__host__ int test_io_mode_from_string(void); +__host__ int test_io_record_format_to_string(void); +__host__ int test_io_record_format_from_string(void); +__host__ int test_io_options_to_json(void); +__host__ int test_io_options_from_json(void); /***************************************************************************** * @@ -47,6 +55,12 @@ __host__ int test_io_options_suite(void) { test_io_options_with_mode(); test_io_options_with_format(); test_io_options_with_iogrid(); + test_io_mode_to_string(); + test_io_mode_from_string(); + test_io_record_format_to_string(); + test_io_record_format_from_string(); + test_io_options_to_json(); + test_io_options_from_json(); pe_info(pe, "PASS ./unit/test_io_options\n"); @@ -298,3 +312,207 @@ __host__ int test_io_options_with_iogrid(void) { return ifail; } + +/***************************************************************************** + * + * test_io_mode_to_string + * + *****************************************************************************/ + +__host__ int test_io_mode_to_string(void) { + + int ifail = 0; + + /* Test each case in turn */ + { + const char * str = NULL; + str = io_mode_to_string(IO_MODE_SINGLE); + ifail = strcmp(str, "single"); + assert(ifail == 0); + } + + { + const char * str = NULL; + str = io_mode_to_string(IO_MODE_MULTIPLE); + ifail = strcmp(str, "multiple"); + assert(ifail == 0); + } + + { + const char * str = NULL; + str = io_mode_to_string(IO_MODE_ANSI); + ifail = strcmp(str, "ansi"); + assert(ifail == 0); + } + + { + const char * str = NULL; + str = io_mode_to_string(IO_MODE_MPIIO); + ifail = strcmp(str, "mpiio"); + assert(ifail == 0); + } + + return ifail; +} + +/***************************************************************************** + * + * test_io_mode_from_string + * + *****************************************************************************/ + +__host__ int test_io_mode_from_string(void) { + + int ifail = 0; + + { + io_mode_enum_t mode = io_mode_from_string("SINGLE"); + if (mode != IO_MODE_SINGLE) ifail = -1; + assert(ifail == 0); + } + + { + io_mode_enum_t mode = io_mode_from_string("MULTIPLE"); + if (mode != IO_MODE_MULTIPLE) ifail = -1; + assert(ifail == 0); + } + + { + io_mode_enum_t mode = io_mode_from_string("ANSI"); + if (mode != IO_MODE_ANSI) ifail = -1; + assert(ifail == 0); + } + + { + io_mode_enum_t mode = io_mode_from_string("MPIIO"); + if (mode != IO_MODE_MPIIO) ifail = -1; + assert(ifail == 0); + } + + return ifail; +} + +/***************************************************************************** + * + * test_io_record_format_to_string + * + *****************************************************************************/ + +__host__ int test_io_record_format_to_string(void) { + + int ifail = 0; + + { + const char * str = io_record_format_to_string(IO_RECORD_ASCII); + ifail = strcmp(str, "ascii"); + assert(ifail == 0); + } + + { + const char * str = io_record_format_to_string(IO_RECORD_BINARY); + ifail = strcmp(str, "binary"); + assert(ifail == 0); + } + + return ifail; +} + +/***************************************************************************** + * + * test_io_record_format_from_string + * + *****************************************************************************/ + +__host__ int test_io_record_format_from_string(void) { + + int ifail = 0; + + { + io_record_format_enum_t ior = io_record_format_from_string("ASCII"); + if (ior != IO_RECORD_ASCII) ifail = -1; + assert(ifail == 0); + } + + { + io_record_format_enum_t ior = io_record_format_from_string("BINARY"); + if (ior != IO_RECORD_BINARY) ifail = -1; + assert(ifail == 0); + } + + return ifail; +} + +/***************************************************************************** + * + * test_io_options_to_json + * + *****************************************************************************/ + +__host__ int test_io_options_to_json(void) { + + int ifail = 0; + + io_options_t opts = io_options_default(); + cJSON * json = NULL; + + ifail = io_options_to_json(&opts, &json); + assert(ifail == 0); + + { + /* A somewhat circular test which we excuse by saying the test on + * io_options_from_json() is not also circular */ + io_options_t check = {0}; + io_options_from_json(json, &check); + assert(check.mode == io_mode_default()); + assert(check.iorformat == io_record_format_default()); + assert(check.metadata_version == io_metadata_version_default()); + assert(check.report == opts.report); + assert(check.asynchronous == opts.asynchronous); + assert(check.compression_levl == opts.compression_levl); + assert(check.iogrid[0] == 1); + assert(check.iogrid[1] == 1); + assert(check.iogrid[2] == 1); + } + + cJSON_Delete(json); + + return ifail; +} + +/***************************************************************************** + * + * test_io_options_from_json + * + *****************************************************************************/ + +__host__ int test_io_options_from_json(void) { + + int ifail = 0; + const char * jstr = "{\"Mode\": \"single\"," + "\"Record format\": \"binary\"," + "\"Metadata version\": 2," + "\"Report\": false, " + "\"Asynchronous\": true," + "\"Compression level\": 9," + "\"I/O grid\": [2, 3, 4] }"; + + cJSON * json = cJSON_Parse(jstr); + assert(json); + + { + /* Convert to options and test */ + io_options_t opts = {0}; + ifail = io_options_from_json(json, &opts); + assert(opts.metadata_version == 2); + assert(opts.report == 0); + assert(opts.asynchronous ); + assert(opts.compression_levl == 9); + assert(opts.iogrid[0] == 2); + assert(opts.iogrid[1] == 3); + assert(opts.iogrid[2] == 4); + } + + cJSON_Delete(json); + + return ifail; +} diff --git a/tests/unit/test_io_options_rt.c b/tests/unit/test_io_options_rt.c index 8c06abf40..989068a5e 100644 --- a/tests/unit/test_io_options_rt.c +++ b/tests/unit/test_io_options_rt.c @@ -73,6 +73,10 @@ __host__ int test_io_options_rt_mode(pe_t * pe) { io_options_rt_mode(rt, RT_NONE, "example_output_io_mode", &mode); assert(mode == IO_MODE_MULTIPLE); + rt_add_key_value(rt, "vel_io_mode", "ansi"); + io_options_rt_mode(rt, RT_NONE, "vel_io_mode", &mode); + assert(mode == IO_MODE_ANSI); + rt_free(rt); return 0; From cb1c4f58819075aa24de155a9194816019191032 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 9 Dec 2022 11:18:31 +0000 Subject: [PATCH 172/244] Correction to mode type --- src/io_options.c | 8 ++++++-- tests/unit/test_io_options.c | 10 ++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/io_options.c b/src/io_options.c index 1a6031832..1ed69f558 100644 --- a/src/io_options.c +++ b/src/io_options.c @@ -305,7 +305,7 @@ __host__ const char * io_mode_to_string(io_mode_enum_t mode) { __host__ io_mode_enum_t io_mode_from_string(const char * str) { - int mode = IO_MODE_INVALID; + io_mode_enum_t mode = IO_MODE_INVALID; char value[BUFSIZ] = {0}; assert(str); @@ -434,7 +434,11 @@ __host__ int io_options_from_json(const cJSON * json, io_options_t * opts) { char * str = cJSON_GetStringValue(ior); opts->iorformat = io_record_format_from_string(str); } - if (meta) opts->metadata_version = cJSON_GetNumberValue(meta); + if (meta) { + /* Care with type conversions here ... */ + int version = cJSON_GetNumberValue(meta); + opts->metadata_version = (io_metadata_version_enum_t) version; + } if (report) opts->report = cJSON_IsTrue(report); if (async) opts->asynchronous = cJSON_IsTrue(async); if (level) opts->compression_levl = cJSON_GetNumberValue(level); diff --git a/tests/unit/test_io_options.c b/tests/unit/test_io_options.c index ba9a7b2eb..33f2c1cc3 100644 --- a/tests/unit/test_io_options.c +++ b/tests/unit/test_io_options.c @@ -96,6 +96,12 @@ __host__ int test_io_options_mode_valid(void) { isvalid = io_options_mode_valid(mode9); assert(isvalid == 0); + /* nb., a declaration "io_options_t io = {0};" will not pass + * muster for C++ (bad conversion to enum_t). One can instead do + * "io_options_t io = {IO_MODE_INVALID};" providing ... */ + + assert(IO_MODE_INVALID == 0); + return isvalid; } @@ -461,7 +467,7 @@ __host__ int test_io_options_to_json(void) { { /* A somewhat circular test which we excuse by saying the test on * io_options_from_json() is not also circular */ - io_options_t check = {0}; + io_options_t check = {IO_MODE_INVALID}; io_options_from_json(json, &check); assert(check.mode == io_mode_default()); assert(check.iorformat == io_record_format_default()); @@ -501,7 +507,7 @@ __host__ int test_io_options_from_json(void) { { /* Convert to options and test */ - io_options_t opts = {0}; + io_options_t opts = {IO_MODE_INVALID}; ifail = io_options_from_json(json, &opts); assert(opts.metadata_version == 2); assert(opts.report == 0); From 0566022fa5786f951e4eab7d477ea98a176779ab Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 16 Dec 2022 13:24:50 +0000 Subject: [PATCH 173/244] Control io options for fields --- src/field.c | 7 +++++-- src/ludwig.c | 12 ++++++++++++ src/model.c | 10 ++++++++-- tests/unit/test_field.c | 2 ++ 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/field.c b/src/field.c index 2372d92c3..9bc85438a 100644 --- a/src/field.c +++ b/src/field.c @@ -1665,7 +1665,7 @@ int field_io_write(field_t * field, int timestep, io_event_t * event) { const io_metadata_t * meta = &field->iometadata_out; /* old ANSI */ - if (meta->options.mode == IO_MODE_SINGLE) { + if (meta->options.mode != IO_MODE_MPIIO) { char filename[BUFSIZ] = {0}; sprintf(filename, "%s-%8.8d", field->name, timestep); io_write_data(field->info, filename, field); @@ -1688,6 +1688,9 @@ int field_io_write(field_t * field, int timestep, io_event_t * event) { io->impl->write(io, filename); /* FIXME: REPORT HERE >>>>> */ + if (meta->options.report) { + pe_info(field->pe, "MPIIO wrote to %s\n", filename); + } io->impl->free(&io); } @@ -1709,7 +1712,7 @@ int field_io_read(field_t * field, int timestep, io_event_t * event) { const io_metadata_t * meta = &field->iometadata_in; /* old ANSI */ - if (field->opts.iodata.input.mode == IO_MODE_SINGLE) { + if (field->opts.iodata.input.mode != IO_MODE_MPIIO) { char filename[BUFSIZ] = {0}; sprintf(filename, "%s-%8.8d", field->name, timestep); io_read_data(field->info, filename, field); diff --git a/src/ludwig.c b/src/ludwig.c index 1147a9d77..8c65e1cd0 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -49,6 +49,8 @@ #include "hydro_rt.h" #include "io_harness.h" +#include "io_info_args_rt.h" + #include "phi_stats.h" #include "phi_force.h" #include "phi_force_colloid.h" @@ -1333,6 +1335,7 @@ int free_energy_init_rt(ludwig_t * ludwig) { { field_options_t opts = field_options_ndata_nhalo(nf, nhalo); + io_info_args_rt(rt, RT_FATAL, "phi", IO_INFO_READ_WRITE, &opts.iodata); field_create(pe, cs, le, "phi", &opts, &ludwig->phi); field_grad_create(pe, ludwig->phi, ngrad, &ludwig->phi_grad); } @@ -1414,6 +1417,7 @@ int free_energy_init_rt(ludwig_t * ludwig) { { field_options_t opts = field_options_ndata_nhalo(nf, nhalo); + io_info_args_rt(rt, RT_FATAL, "phi", IO_INFO_READ_WRITE, &opts.iodata); field_create(pe, cs, le, "phi", &opts, &ludwig->phi); field_grad_create(pe, ludwig->phi, ngrad, &ludwig->phi_grad); } @@ -1455,6 +1459,7 @@ int free_energy_init_rt(ludwig_t * ludwig) { { field_options_t opts = field_options_ndata_nhalo(nf, nhalo); + io_info_args_rt(rt, RT_FATAL, "phi", IO_INFO_READ_WRITE, &opts.iodata); field_create(pe, cs, le, "phi", &opts, &ludwig->phi); field_grad_create(pe, ludwig->phi, ngrad, &ludwig->phi_grad); phi_ch_create(pe, cs, le, &ch_options, &ludwig->pch); @@ -1588,6 +1593,7 @@ int free_energy_init_rt(ludwig_t * ludwig) { { field_options_t opts = field_options_ndata_nhalo(nf, nhalo); + io_info_args_rt(rt, RT_FATAL, "phi", IO_INFO_READ_WRITE, &opts.iodata); field_create(pe, cs, NULL, "phi", &opts, &ludwig->phi); } @@ -1675,6 +1681,8 @@ int free_energy_init_rt(ludwig_t * ludwig) { if (rt_switch(rt, "field_data_use_first_touch")) { opts.usefirsttouch = 1; } + io_info_args_rt(rt, RT_FATAL, "q", IO_INFO_READ_WRITE, &opts.iodata); + field_create(pe, cs, le, "q", &opts, &ludwig->q); field_grad_create(pe, ludwig->q, ngrad, &ludwig->q_grad); } @@ -1739,6 +1747,7 @@ int free_energy_init_rt(ludwig_t * ludwig) { { field_options_t opts = field_options_ndata_nhalo(nf, nhalo); + io_info_args_rt(rt, RT_FATAL, "p", IO_INFO_READ_WRITE, &opts.iodata); field_create(pe, cs, le, "p", &opts, &ludwig->p); field_grad_create(pe, ludwig->p, ngrad, &ludwig->p_grad); } @@ -1797,6 +1806,8 @@ int free_energy_init_rt(ludwig_t * ludwig) { if (rt_switch(rt, "field_data_use_first_touch")) { opts.usefirsttouch = 1; } + io_info_args_rt(rt, RT_FATAL, "phi", IO_INFO_READ_WRITE, &opts.iodata); + field_create(pe, cs, le, "phi", &opts, &ludwig->phi); field_grad_create(pe, ludwig->phi, ngrad, &ludwig->phi_grad); phi_ch_create(pe, cs, le, &ch_options, &ludwig->pch); @@ -1829,6 +1840,7 @@ int free_energy_init_rt(ludwig_t * ludwig) { if (rt_switch(rt, "field_data_use_first_touch")) { opts.usefirsttouch = 1; } + io_info_args_rt(rt, RT_FATAL, "q", IO_INFO_READ_WRITE, &opts.iodata); field_create(pe, cs, le, "q", &opts, &ludwig->q); field_grad_create(pe, ludwig->q, ngrad, &ludwig->q_grad); } diff --git a/src/model.c b/src/model.c index dd0565be5..b90f34f25 100644 --- a/src/model.c +++ b/src/model.c @@ -1566,7 +1566,7 @@ int lb_io_write(lb_t * lb, int timestep, io_event_t * event) { const io_metadata_t * meta = &lb->output; - if (meta->options.mode == IO_MODE_SINGLE) { + if (meta->options.mode != IO_MODE_MPIIO) { /* Old-style */ char filename[BUFSIZ] = {0}; sprintf(filename, "dist-%8.8d", timestep); @@ -1586,6 +1586,12 @@ int lb_io_write(lb_t * lb, int timestep, io_event_t * event) { lb_io_aggr_pack(lb, io->aggr); io->impl->write(io, filename); + + /* FIXME need to add a proper report */ + if (meta->options.report) { + pe_info(lb->pe, "MPIIO wrote to %s\n", filename); + } + io->impl->free(&io); } @@ -1605,7 +1611,7 @@ int lb_io_read(lb_t * lb, int timestep, io_event_t * event) { const io_metadata_t * meta = &lb->input; - if (meta->options.mode == IO_MODE_SINGLE) { + if (meta->options.mode != IO_MODE_MPIIO) { char filename[BUFSIZ] = {0}; sprintf(filename, "dist-%8.8d", timestep); io_read_data(lb->io_info, filename, lb); diff --git a/tests/unit/test_field.c b/tests/unit/test_field.c index 67e33b052..3c45d9f7b 100644 --- a/tests/unit/test_field.c +++ b/tests/unit/test_field.c @@ -111,6 +111,7 @@ int test_field_io_read_write(pe_t * pe) { { io_options_t io = io_options_with_format(IO_MODE_MPIIO, IO_RECORD_ASCII); field_options_t opts = field_options_ndata_nhalo(3, 0); + io.report = 0; opts.iodata.input = io; opts.iodata.output = io; @@ -124,6 +125,7 @@ int test_field_io_read_write(pe_t * pe) { { io_options_t io = io_options_with_format(IO_MODE_MPIIO, IO_RECORD_BINARY); field_options_t opts = field_options_ndata_nhalo(5, 2); + io.report = 0; opts.iodata.input = io; opts.iodata.output = io; From 853ce1c585fd2e7d1831330171721758b4b77c9a Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 17 Dec 2022 11:42:56 +0000 Subject: [PATCH 174/244] Add io_event reporting --- src/field.c | 4 ++- src/io_event.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/io_event.h | 16 ++++++++- src/model.c | 4 ++- 4 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 src/io_event.c diff --git a/src/field.c b/src/field.c index 9bc85438a..f9c5f6869 100644 --- a/src/field.c +++ b/src/field.c @@ -1682,17 +1682,19 @@ int field_io_write(field_t * field, int timestep, io_event_t * event) { io_impl_create(meta, &io); /* CAN FAIL in principle */ assert(io); + io_event_record(event, IO_EVENT_AGGR); field_memcpy(field, tdpMemcpyDeviceToHost); field_io_aggr_pack(field, io->aggr); + io_event_record(event, IO_EVENT_WRITE); io->impl->write(io, filename); - /* FIXME: REPORT HERE >>>>> */ if (meta->options.report) { pe_info(field->pe, "MPIIO wrote to %s\n", filename); } io->impl->free(&io); + io_event_report(event, meta, field->name); } return 0; diff --git a/src/io_event.c b/src/io_event.c new file mode 100644 index 000000000..8da7b2c29 --- /dev/null +++ b/src/io_event.c @@ -0,0 +1,96 @@ +/***************************************************************************** + * + * io_event.c + * + * Used to form reports on individual i/o events. + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include + +#include "coords_s.h" +#include "io_event.h" + +/***************************************************************************** + * + * io_event_record + * + *****************************************************************************/ + +int io_event_record(io_event_t * event, io_event_record_t iorec) { + + assert(event); + assert(0 <= iorec && iorec <= IO_EVENT_MAX); + + event->time[iorec] = MPI_Wtime(); + + return 0; +} + +/***************************************************************************** + * + * io_event_report + * + * Report for an i/o event in parallel. + * The aggregation report might want the local data size (currently total). + * The file write is the total data size of the file. + * + * Some refinement might be wanted for multiple files. + * + *****************************************************************************/ + +int io_event_report(io_event_t * event, const io_metadata_t * metadata, + const char * name) { + + assert(event); + assert(metadata); + assert(name); + + + /* End of event (for reporting purposes) */ + event->time[IO_EVENT_REPORT] = MPI_Wtime(); + + if (metadata->options.report) { + + pe_t * pe = pe = metadata->cs->pe; + + const char * units = NULL; + double dunit6 = 1.0e+06; /* Units of data size are MB */ + double dunit9 = 1.0e+09; /* Units of data size are GB */ + /* Times (we assume these have been collected correctly! */ + double ta = event->time[IO_EVENT_WRITE] - event->time[IO_EVENT_AGGR]; + double tw = event->time[IO_EVENT_REPORT] - event->time[IO_EVENT_WRITE]; + /* Record size and total file size. */ + double dr = metadata->element.datasize*metadata->element.count; + double ds = metadata->subfile.sizes[X]*metadata->subfile.sizes[Y]* + metadata->subfile.sizes[Z]; + double db = dr*ds; + + if (db > dunit9) { + /* Use GB */ + units = "GB"; + db = db/dunit9; + } + else { + /* Use MB */ + units = "MB"; + db = db/dunit6; + } + pe_info(pe, "- %10s aggregated %7.3f %2s in %7.3f seconds\n", + name, db, units, ta); + pe_info(pe, "- %10s wrote %7.3f %2s in %7.3f seconds\n", + name, db, units, tw); + pe_info(pe, "- %10s rate %7.3f GB per second\n", + name, dr*ds/dunit9/tw, units, tw); + } + + return 0; +} diff --git a/src/io_event.h b/src/io_event.h index 9bbe38030..afe05988b 100644 --- a/src/io_event.h +++ b/src/io_event.h @@ -18,10 +18,24 @@ #ifndef LUDWIG_IO_EVENT_H #define LUDWIG_IO_EVENT_H +#include "io_metadata.h" + +enum io_event_record_enum { + IO_EVENT_AGGR = 0, + IO_EVENT_WRITE, + IO_EVENT_REPORT, + IO_EVENT_MAX +}; + +typedef enum io_event_record_enum io_event_record_t; typedef struct io_event_s io_event_t; struct io_event_s { - int isconfig; /* Is this a configuration step */ + double time[IO_EVENT_MAX]; /* MPI_Wtime()s */ }; +int io_event_record(io_event_t * event, io_event_record_t iorec); +int io_event_report(io_event_t * event, const io_metadata_t * metadata, + const char * name); + #endif diff --git a/src/model.c b/src/model.c index b90f34f25..812ed351d 100644 --- a/src/model.c +++ b/src/model.c @@ -1582,17 +1582,19 @@ int lb_io_write(lb_t * lb, int timestep, io_event_t * event) { io_impl_create(meta, &io); assert(io); + io_event_record(event, IO_EVENT_AGGR); lb_memcpy(lb, tdpMemcpyDeviceToHost); lb_io_aggr_pack(lb, io->aggr); + io_event_record(event, IO_EVENT_WRITE); io->impl->write(io, filename); - /* FIXME need to add a proper report */ if (meta->options.report) { pe_info(lb->pe, "MPIIO wrote to %s\n", filename); } io->impl->free(&io); + io_event_report(event, meta, "dist"); } return 0; From 9a5cb04e4cf51ad20f09c40a1b7e1c7e4b65a44a Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 17 Dec 2022 11:43:21 +0000 Subject: [PATCH 175/244] Add metadata json --- src/io_metadata.c | 75 +++++++++++++++++++++++++++++++++++ src/io_metadata.h | 5 +++ tests/unit/test_io_metadata.c | 53 +++++++++++++++++++++++++ 3 files changed, 133 insertions(+) diff --git a/src/io_metadata.c b/src/io_metadata.c index d8b3f7a0a..300b5adca 100644 --- a/src/io_metadata.c +++ b/src/io_metadata.c @@ -146,3 +146,78 @@ int io_metadata_finalise(io_metadata_t * meta) { return 0; } + +/***************************************************************************** + * + * io_metadata_to_json + * + * The "non-MPI" content can be translated meaningfully. + * + *****************************************************************************/ + +int io_metadata_to_json(const io_metadata_t * meta, cJSON ** json) { + + int ifail = 0; + + assert(meta); + + if (json == NULL || *json != NULL) { + ifail = -1; + } + else { + cJSON * myjson = cJSON_CreateObject(); + + { + /* options */ + cJSON * jtmp = NULL; + ifail = io_options_to_json(&meta->options, &jtmp); + if (ifail == 0) cJSON_AddItemToObject(myjson, "io_options", jtmp); + } + { + /* element */ + cJSON * jtmp = NULL; + ifail = io_element_to_json(&meta->element, &jtmp); + if (ifail == 0) cJSON_AddItemToObject(myjson, "io_element", jtmp); + } + { + /* subfile */ + cJSON * jtmp = NULL; + ifail = io_subfile_to_json(&meta->subfile, &jtmp); + if (ifail == 0) cJSON_AddItemToObject(myjson, "io_subfile", jtmp); + } + *json = myjson; + } + + return ifail; +} + +/***************************************************************************** + * + * io_metadata_from_json + * + *****************************************************************************/ + +int io_metadata_from_json(cs_t * cs, const cJSON * json, io_metadata_t * m) { + + int ifail = 0; + + assert(cs); + assert(json); + + if (m == NULL) { + ifail = -1; + } + else { + /* Generate options, element, from json, and initialise ... */ + io_options_t options = {0}; + io_element_t element = {0}; + + cJSON * jopt = cJSON_GetObjectItemCaseSensitive(json, "io_options"); + cJSON * jelm = cJSON_GetObjectItemCaseSensitive(json, "io_element"); + ifail = io_options_from_json(jopt, &options); + ifail = io_element_from_json(jelm, &element); + ifail = io_metadata_initialise(cs, &options, &element, m); + } + + return ifail; +} diff --git a/src/io_metadata.h b/src/io_metadata.h index c22e25419..74004a68e 100644 --- a/src/io_metadata.h +++ b/src/io_metadata.h @@ -19,6 +19,7 @@ #include "io_options.h" #include "io_element.h" #include "io_subfile.h" +#include "util_cjson.h" typedef struct io_metadata_s io_metadata_t; @@ -46,4 +47,8 @@ int io_metadata_initialise(cs_t * cs, io_metadata_t * metadata); int io_metadata_finalise(io_metadata_t * metadata); +int io_metadata_to_json(const io_metadata_t * metadata, cJSON ** json); +int io_metadata_from_json(cs_t * cs, const cJSON * json, + io_metadata_t * metadata); + #endif diff --git a/tests/unit/test_io_metadata.c b/tests/unit/test_io_metadata.c index 80cc81995..8c0365c20 100644 --- a/tests/unit/test_io_metadata.c +++ b/tests/unit/test_io_metadata.c @@ -13,11 +13,13 @@ *****************************************************************************/ #include +#include #include "io_metadata.h" int test_io_metadata_initialise(cs_t * cs); int test_io_metadata_create(cs_t * cs); +int test_io_metadata_to_json(cs_t * cs); /***************************************************************************** * @@ -37,6 +39,7 @@ int test_io_metadata_suite(void) { test_io_metadata_initialise(cs); test_io_metadata_create(cs); + test_io_metadata_to_json(cs); pe_info(pe, "%-9s %s\n", "PASS", __FILE__); cs_free(cs); @@ -129,3 +132,53 @@ int test_io_metadata_initialise(cs_t * cs) { return ifail; } + +/***************************************************************************** + * + * test_io_metadata_to_json + * + *****************************************************************************/ + +int test_io_metadata_to_json(cs_t * cs) { + + int ifail = 0; + io_metadata_t metadata = {0}; + io_element_t element = {0}; + io_options_t options = io_options_default(); + + assert(cs); + + io_metadata_initialise(cs, &options, &element, &metadata); + + { + cJSON * json = NULL; + ifail = io_metadata_to_json(&metadata, &json); + assert(ifail == 0); + + { + char * str = cJSON_Print(json); + printf("io_metadata: %s\n\n", str); + free(str); + } + /* Check we have the requisite parts. */ + /* We assume the parts themselves are well-formed. */ + { + cJSON * part = NULL; + part = cJSON_GetObjectItemCaseSensitive(json, "io_options"); + if (part == NULL) ifail = -1; + assert(ifail == 0); + part = cJSON_GetObjectItemCaseSensitive(json, "io_element"); + if (part == NULL) ifail = -1; + assert(ifail == 0); + part = cJSON_GetObjectItemCaseSensitive(json, "io_subfile"); + if (part == NULL) ifail = -1; + assert(ifail == 0); + } + + cJSON_Delete(json); + } + + io_metadata_finalise(&metadata); + + return ifail; +} From 279029fd357d61cd622b86be2064e81a04fb8772 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 17 Dec 2022 11:49:28 +0000 Subject: [PATCH 176/244] Typo in include --- src/io_metadata.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io_metadata.h b/src/io_metadata.h index 74004a68e..66b5dedab 100644 --- a/src/io_metadata.h +++ b/src/io_metadata.h @@ -19,7 +19,7 @@ #include "io_options.h" #include "io_element.h" #include "io_subfile.h" -#include "util_cjson.h" +#include "util_json.h" typedef struct io_metadata_s io_metadata_t; From 80839c75342b7495515a9dae2f80c4b87c61830e Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 17 Dec 2022 12:06:54 +0000 Subject: [PATCH 177/244] Correct typo --- src/io_event.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io_event.c b/src/io_event.c index 8da7b2c29..f6e2261cf 100644 --- a/src/io_event.c +++ b/src/io_event.c @@ -60,7 +60,7 @@ int io_event_report(io_event_t * event, const io_metadata_t * metadata, if (metadata->options.report) { - pe_t * pe = pe = metadata->cs->pe; + pe_t * pe = metadata->cs->pe; const char * units = NULL; double dunit6 = 1.0e+06; /* Units of data size are MB */ From 0e4b018aa430a8ccc15447b15cec80cb881cbc35 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 17 Dec 2022 12:24:43 +0000 Subject: [PATCH 178/244] Correct initialisation --- src/io_metadata.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io_metadata.c b/src/io_metadata.c index 300b5adca..1f73d0019 100644 --- a/src/io_metadata.c +++ b/src/io_metadata.c @@ -209,7 +209,7 @@ int io_metadata_from_json(cs_t * cs, const cJSON * json, io_metadata_t * m) { } else { /* Generate options, element, from json, and initialise ... */ - io_options_t options = {0}; + io_options_t options = {IO_MODE_INVALID}; io_element_t element = {0}; cJSON * jopt = cJSON_GetObjectItemCaseSensitive(json, "io_options"); From 3cfa24f33e2907bb64a1a199311ff53398920149 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 17 Dec 2022 16:49:08 +0000 Subject: [PATCH 179/244] Remove options for ansi output --- src/io_harness.c | 22 +++------------------- src/ludwig.c | 28 +++++++--------------------- 2 files changed, 10 insertions(+), 40 deletions(-) diff --git a/src/io_harness.c b/src/io_harness.c index d321521a4..a664f383a 100644 --- a/src/io_harness.c +++ b/src/io_harness.c @@ -84,6 +84,7 @@ int io_info_create(pe_t * pe, cs_t * cs, io_info_args_t * arg, io_info_t ** p) { info->single_file_read = 0; /* Patch to allow old parallel i/o to take effect */ + if (info->io_comm->n_io > 1) { info->args.output.mode = IO_MODE_MULTIPLE; } @@ -736,28 +737,11 @@ __host__ int io_info_output_bytesize(io_info_t * info, size_t * bs) { int io_write_data(io_info_t * info, const char * filename_stub, void * data) { - double t0, t1; - assert(info); assert(data); - if (info->args.output.mode == IO_MODE_MULTIPLE) { - /* Use the standard "parallel" method for the time being. */ - io_write_data_p(info, filename_stub, data); - } - else { - /* This is serial output format if one I/O group */ - t0 = MPI_Wtime(); - io_write_data_s(info, filename_stub, data); - t1 = MPI_Wtime(); - if (info->args.output.report) { - size_t bytesize = 0; - io_info_output_bytesize(info, &bytesize); - pe_info(info->pe, "Write %lu bytes in %f secs %f GB/s\n", - info->nsites*bytesize, t1-t0, - info->nsites*bytesize/(1.0e+09*(t1-t0))); - } - } + /* Use the standard "parallel" method. */ + io_write_data_p(info, filename_stub, data); return 0; } diff --git a/src/ludwig.c b/src/ludwig.c index 8c65e1cd0..1b7a016fe 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -201,14 +201,10 @@ int io_replace_field_values(field_t * field, map_t * map, int status, static int ludwig_rt(ludwig_t * ludwig) { - int form; int ntstep; int n, nstat; char filename[FILENAME_MAX]; char subdirectory[FILENAME_MAX/2]; - char value[BUFSIZ]; - int io_grid_default[3] = {1, 1, 1}; - int io_grid[3]; pe_t * pe = NULL; cs_t * cs = NULL; @@ -257,29 +253,18 @@ static int ludwig_rt(ludwig_t * ludwig) { lb_bc_open_rt(pe, rt, cs, ludwig->lb, &ludwig->inflow, &ludwig->outflow); phi_bc_open_rt(pe, rt, cs, &ludwig->phi_inflow, &ludwig->phi_outflow); - /* PHI I/O */ - - rt_int_parameter_vector(rt, "default_io_grid", io_grid_default); - for (n = 0; n < 3; n++) { - io_grid[n] = io_grid_default[n]; - } - rt_int_parameter_vector(rt, "phi_io_grid", io_grid); - - form = IO_FORMAT_DEFAULT; - strcpy(value, ""); /* REPLACE Really need a way to get string from "form" */ - n = rt_string_parameter(rt, "phi_format", value, BUFSIZ); - if (n != 0 && strcmp(value, "ASCII") == 0) { - form = IO_FORMAT_ASCII; - } - - - /* All the same I/O grid */ + { + /* Old i/o options scheduled for removal */ + /* Only default options are now possible. */ + int form = IO_FORMAT_DEFAULT; + int io_grid[3] = {1, 1, 1}; if (ludwig->phi) field_init_io_info(ludwig->phi, io_grid, form, form); if (ludwig->p) field_init_io_info(ludwig->p, io_grid, form, form); if (ludwig->q) field_init_io_info(ludwig->q, io_grid, form, form); if (ludwig->phi || ludwig->p || ludwig->q) { + const char * value = ""; /* BLANK appeared in old output */ pe_info(pe, "\n"); pe_info(pe, "Order parameter I/O\n"); pe_info(pe, "-------------------\n"); @@ -289,6 +274,7 @@ static int ludwig_rt(ludwig_t * ludwig) { io_grid[X], io_grid[Y], io_grid[Z]); advection_init_rt(pe, rt); } + } /* Can we move this down to t = 0 initialisation? */ From 635fabb1e62ea7d074c46a4ddb3768768c45d3c3 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 17 Dec 2022 17:02:15 +0000 Subject: [PATCH 180/244] Fix number of arguments in format --- src/io_event.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io_event.c b/src/io_event.c index f6e2261cf..336608f36 100644 --- a/src/io_event.c +++ b/src/io_event.c @@ -89,7 +89,7 @@ int io_event_report(io_event_t * event, const io_metadata_t * metadata, pe_info(pe, "- %10s wrote %7.3f %2s in %7.3f seconds\n", name, db, units, tw); pe_info(pe, "- %10s rate %7.3f GB per second\n", - name, dr*ds/dunit9/tw, units, tw); + name, dr*ds/dunit9/tw); } return 0; From 627ae98dec6319860664703f52fc430d1ab66cb4 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sun, 18 Dec 2022 15:46:16 +0000 Subject: [PATCH 181/244] Relax constraint on ndist in halo --- src/model.c | 53 ++++++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/src/model.c b/src/model.c index 16fdf798f..842848069 100644 --- a/src/model.c +++ b/src/model.c @@ -1035,7 +1035,6 @@ int lb_halo_size(cs_limits_t lim) { int lb_halo_enqueue_send(const lb_t * lb, lb_halo_t * h, int ireq) { assert(0 <= ireq && ireq < h->map.nvel); - assert(lb->ndist == 1); if (h->count[ireq] > 0) { @@ -1061,17 +1060,19 @@ int lb_halo_enqueue_send(const lb_t * lb, lb_halo_t * h, int ireq) { int kc = h->slim[ireq].kmin + (ih % stry)/strz; int ib = 0; /* Buffer index */ - for (int p = 0; p < lb->nvel; p++) { - /* Recall, if full, we need p = 0 */ - int8_t px = lb->model.cv[p][X]; - int8_t py = lb->model.cv[p][Y]; - int8_t pz = lb->model.cv[p][Z]; - int dot = mx*px + my*py + mz*pz; - if (h->full || dot == mm) { - int index = cs_index(lb->cs, ic, jc, kc); - int laddr = LB_ADDR(lb->nsite, lb->ndist, lb->nvel, index, 0, p); - h->send[ireq][ih*h->count[ireq] + ib] = lb->f[laddr]; - ib++; + for (int n = 0; n < lb->ndist; n++) { + for (int p = 0; p < lb->nvel; p++) { + /* Recall, if full, we need p = 0 */ + int8_t px = lb->model.cv[p][X]; + int8_t py = lb->model.cv[p][Y]; + int8_t pz = lb->model.cv[p][Z]; + int dot = mx*px + my*py + mz*pz; + if (h->full || dot == mm) { + int index = cs_index(lb->cs, ic, jc, kc); + int laddr = LB_ADDR(lb->nsite, lb->ndist, lb->nvel, index, n, p); + h->send[ireq][ih*h->count[ireq] + ib] = lb->f[laddr]; + ib++; + } } } assert(ib == h->count[ireq]); @@ -1094,7 +1095,6 @@ int lb_halo_dequeue_recv(lb_t * lb, const lb_halo_t * h, int ireq) { assert(lb); assert(h); assert(0 <= ireq && ireq < h->map.nvel); - assert(lb->ndist == 1); if (h->count[ireq] > 0) { @@ -1131,18 +1131,20 @@ int lb_halo_dequeue_recv(lb_t * lb, const lb_halo_t * h, int ireq) { int kc = h->rlim[ireq].kmin + (ih % stry)/strz; int ib = 0; /* Buffer index */ - for (int p = 0; p < lb->nvel; p++) { - /* For reduced swap, we must have -cv[p] here... */ - int8_t px = lb->model.cv[lb->nvel-p][X]; - int8_t py = lb->model.cv[lb->nvel-p][Y]; - int8_t pz = lb->model.cv[lb->nvel-p][Z]; - int dot = mx*px + my*py + mz*pz; - - if (h->full || dot == mm) { - int index = cs_index(lb->cs, ic, jc, kc); - int laddr = LB_ADDR(lb->nsite, lb->ndist, lb->nvel, index, 0, p); - lb->f[laddr] = recv[ih*h->count[ireq] + ib]; - ib++; + for (int n = 0; n < lb->ndist; n++) { + for (int p = 0; p < lb->nvel; p++) { + /* For reduced swap, we must have -cv[p] here... */ + int8_t px = lb->model.cv[lb->nvel-p][X]; + int8_t py = lb->model.cv[lb->nvel-p][Y]; + int8_t pz = lb->model.cv[lb->nvel-p][Z]; + int dot = mx*px + my*py + mz*pz; + + if (h->full || dot == mm) { + int index = cs_index(lb->cs, ic, jc, kc); + int laddr = LB_ADDR(lb->nsite, lb->ndist, lb->nvel, index, n, p); + lb->f[laddr] = recv[ih*h->count[ireq] + ib]; + ib++; + } } } assert(ib == h->count[ireq]); @@ -1275,6 +1277,7 @@ int lb_halo_create(const lb_t * lb, lb_halo_t * h, lb_halo_enum_t scheme) { } } + count = lb->ndist*count; h->count[p] = count; /* Allocate send buffer for send region */ if (count > 0) { From 61d792562d2d4654a108b02b48b395ae03605c82 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 20 Dec 2022 15:50:01 +0000 Subject: [PATCH 182/244] Fix unused variable warning at DNDEBUG --- tests/unit/test_hydro_options.c | 10 ++++++++-- tests/unit/test_model.c | 13 +++++++++---- tests/unit/test_wall.c | 15 +++++++++------ 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/tests/unit/test_hydro_options.c b/tests/unit/test_hydro_options.c index f7b973d57..7803c2dfb 100644 --- a/tests/unit/test_hydro_options.c +++ b/tests/unit/test_hydro_options.c @@ -84,6 +84,8 @@ int test_hydro_options_default(void) { int test_hydro_options_nhalo(void) { + int ifail = 0; + { int nhalo = 2; hydro_options_t opts = hydro_options_nhalo(nhalo); @@ -91,9 +93,10 @@ int test_hydro_options_nhalo(void) { assert(opts.u.nhcomm == nhalo); assert(opts.force.nhcomm == nhalo); assert(opts.eta.nhcomm == nhalo); + if (opts.rho.nhcomm != nhalo) ifail = -1; } - return 0; + return ifail; } /***************************************************************************** @@ -104,6 +107,8 @@ int test_hydro_options_nhalo(void) { int test_hydro_options_haloscheme(void) { + int ifail = 0; + { field_halo_enum_t hs = FIELD_HALO_OPENMP; hydro_options_t opts = hydro_options_haloscheme(hs); @@ -112,7 +117,8 @@ int test_hydro_options_haloscheme(void) { assert(opts.u.haloscheme == hs); assert(opts.force.haloscheme == hs); assert(opts.eta.haloscheme == hs); + if (opts.rho.haloscheme != hs) ifail = -1; } - return 0; + return ifail; } diff --git a/tests/unit/test_model.c b/tests/unit/test_model.c index 706e2fd8c..8fd08c8b6 100644 --- a/tests/unit/test_model.c +++ b/tests/unit/test_model.c @@ -729,6 +729,7 @@ int test_lb_data_write(pe_t * pe, cs_t * cs) { int test_lb_write_buf(pe_t * pe, cs_t * cs, const lb_data_options_t * opts) { + int ifail = 0; lb_t * lb = NULL; char buf[BUFSIZ] = {0}; @@ -766,13 +767,14 @@ int test_lb_write_buf(pe_t * pe, cs_t * cs, const lb_data_options_t * opts) { double f = -1.0; lb_f(lb, index, p, n, &f); assert(fabs(f - fref) < DBL_EPSILON); + if (fabs(f - fref) >= DBL_EPSILON) ifail += 1; } } } lb_free(lb); - return 0; + return ifail; } /***************************************************************************** @@ -784,6 +786,7 @@ int test_lb_write_buf(pe_t * pe, cs_t * cs, const lb_data_options_t * opts) { int test_lb_write_buf_ascii(pe_t * pe, cs_t * cs, const lb_data_options_t * opts) { + int ifail = 0; lb_t * lb = NULL; char buf[BUFSIZ] = {0}; @@ -815,7 +818,8 @@ int test_lb_write_buf_ascii(pe_t * pe, cs_t * cs, /* Have we got the correct size? */ int count = lb->nvel*(lb->ndist*LB_RECORD_LENGTH_ASCII + 1); size_t sz = count*sizeof(char); - assert(sz == strnlen(buf, BUFSIZ)); + if (sz != strnlen(buf, BUFSIZ)) ifail = -1; + assert(ifail == 0); } } @@ -830,14 +834,15 @@ int test_lb_write_buf_ascii(pe_t * pe, cs_t * cs, double fref = 1.0*(1 + n*lb->model.nvel + p); double f = -1.0; lb_f(lb, index, p, n, &f); - assert(fabs(f - fref) < DBL_EPSILON); + if (fabs(f - fref) >= DBL_EPSILON) ifail = -1; + assert(ifail == 0); } } } lb_free(lb); - return 0; + return ifail; } /***************************************************************************** diff --git a/tests/unit/test_wall.c b/tests/unit/test_wall.c index c172d10d6..925640284 100644 --- a/tests/unit/test_wall.c +++ b/tests/unit/test_wall.c @@ -6,13 +6,13 @@ * * (Porous media should be considered as a separate case). * - * Edinburgh Soft Matter and Statistical Physics Group and - * Edinburgh Parallel Computing Centre + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre * - * (c) 2020-2022 The University of Edinburgh + * (c) 2020-2022 The University of Edinburgh * - * Contributing authors: - * Kevin Stratford (kevin@epcc.ed.ac.uk) + * Contributing authors: + * Kevin Stratford (kevin@epcc.ed.ac.uk) * *****************************************************************************/ @@ -471,7 +471,10 @@ __host__ int test_wall_lubr_drag(void) { double h = 1.0; double hc = 1.2; double zeta = wall_lubr_drag(eta, ah, h, hc); - assert(fabs(zeta - -6.0*pi*eta*ah*ah*(1.0/h - 1.0/hc)) < DBL_EPSILON); + if (fabs(zeta - -6.0*pi*eta*ah*ah*(1.0/h - 1.0/hc)) >= DBL_EPSILON) { + ifail = -1; + } + assert(ifail == 0); } return ifail; From 2c9b94c29cfc0fc51e97693c02e5c50037a63e82 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 22 Dec 2022 11:58:12 +0000 Subject: [PATCH 183/244] Correction to condition --- tests/unit/test_wall.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_wall.c b/tests/unit/test_wall.c index 925640284..775fa4261 100644 --- a/tests/unit/test_wall.c +++ b/tests/unit/test_wall.c @@ -461,8 +461,9 @@ __host__ int test_wall_lubr_drag(void) { double h = 1.2; double hc = 1.0; double zeta = wall_lubr_drag(eta, ah, h, hc); - assert(fabs(zeta) < DBL_EPSILON); - if (fabs(zeta) < DBL_EPSILON) ifail = 1; + + if (fabs(zeta) >= DBL_EPSILON) ifail = 1; + assert(ifail == 0); } { /* h < hc */ From 8126c8091dec05ceffc6e85aa79df4e8f2c1d586 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 22 Dec 2022 17:17:21 +0000 Subject: [PATCH 184/244] Add a double array routine --- src/util_json.c | 24 ++++++++++++++++++++++++ src/util_json.h | 1 + 2 files changed, 25 insertions(+) diff --git a/src/util_json.c b/src/util_json.c index a63b1abd2..1283ea94c 100644 --- a/src/util_json.c +++ b/src/util_json.c @@ -43,3 +43,27 @@ int util_json_to_int_array(const cJSON * const json, int * array, int sz) { return icount; } + +/***************************************************************************** + * + * util_json_to_double_array + * + * Actually the same as the int version except for the argument. + * + *****************************************************************************/ + +int util_json_to_double_array(const cJSON * const json, double * array, int sz) { + + int icount = 0; + + if (cJSON_IsArray(json) && cJSON_GetArraySize(json) == sz) { + cJSON * element = NULL; + cJSON_ArrayForEach(element, json) { + if (cJSON_IsNumber(element)) { + array[icount++] = cJSON_GetNumberValue(element); + } + } + } + + return icount; +} diff --git a/src/util_json.h b/src/util_json.h index fa97392a9..6cc901007 100644 --- a/src/util_json.h +++ b/src/util_json.h @@ -17,5 +17,6 @@ #include "util_cJSON.h" int util_json_to_int_array(const cJSON * const json, int * array, int sz); +int util_json_to_double_array(const cJSON * const json, double * array, int sz); #endif From e0374c18f3c39c402439a9babed9a8289edd361e Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 22 Dec 2022 17:18:01 +0000 Subject: [PATCH 185/244] Separate Lees Edwards options --- src/lees_edwards_options.c | 143 +++++++++++++++++++++++++++++++++++++ src/lees_edwards_options.h | 47 ++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 src/lees_edwards_options.c create mode 100644 src/lees_edwards_options.h diff --git a/src/lees_edwards_options.c b/src/lees_edwards_options.c new file mode 100644 index 000000000..291e82f96 --- /dev/null +++ b/src/lees_edwards_options.c @@ -0,0 +1,143 @@ +/***************************************************************************** + * + * lees_edwards_options.c + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include +#include +#include + +#include "util.h" +#include "lees_edwards_options.h" + +/***************************************************************************** + * + * lees_edw_type_to_string + * + * Being "steady" or "oscillatory". + * + *****************************************************************************/ + +const char * lees_edw_type_to_string(lees_edw_enum_t mytype) { + + const char * str = "INVALID"; + if (mytype == LE_SHEAR_TYPE_STEADY) str = "STEADY"; + if (mytype == LE_SHEAR_TYPE_OSCILLATORY) str = "OSCILLATORY"; + + return str; +} + +/***************************************************************************** + * + * lees_edw_type_from_string + * + *****************************************************************************/ + +lees_edw_enum_t lees_edw_type_from_string(const char * str) { + + lees_edw_enum_t mytype = LE_SHEAR_TYPE_INVALID; + char tmp[BUFSIZ] = {0}; + + strncpy(tmp, str, BUFSIZ-1); + util_str_tolower(tmp, strlen(tmp)); + + if (strcmp(tmp, "steady") == 0) mytype = LE_SHEAR_TYPE_STEADY; + if (strcmp(tmp, "oscillatory") == 0) mytype = LE_SHEAR_TYPE_OSCILLATORY; + + return mytype; +} + +/***************************************************************************** + * + * lees_edw_opts_to_json + * + *****************************************************************************/ + +int lees_edw_opts_to_json(const lees_edw_options_t * opts, cJSON ** json) { + + int ifail = 0; + + assert(opts); + + if (json == NULL || *json != NULL) { + ifail = -1; + } + else { + int nplane = opts->nplanes; + cJSON * myjson = cJSON_CreateObject(); + + cJSON_AddNumberToObject(myjson, "Number of planes", nplane); + if (nplane > 0) { + cJSON_AddStringToObject(myjson, "Shear type", + lees_edw_type_to_string(opts->type)); + if (opts->type == LE_SHEAR_TYPE_OSCILLATORY) { + cJSON_AddNumberToObject(myjson, "Period (timesteps)", opts->period); + } + cJSON_AddNumberToObject(myjson, "Reference time", opts->nt0); + cJSON_AddNumberToObject(myjson, "Plane speed", opts->uy); + } + + *json = myjson; + } + + return ifail; +} + +/***************************************************************************** + * + * lees_edw_opts_from_json + * + * Acceptable formats include: + * { "Number of planes:", 0} can omit everything else + * { ... , "Shear type": "STEADY", ... } may omit period + * { ... , "Shear type": "OSCILLATORY", ... } must include period + * + *****************************************************************************/ + +int lees_edw_opts_from_json(const cJSON * json, lees_edw_options_t * opts) { + + int ifail = 0; + + if (json == NULL || opts == NULL) { + ifail = -1; + } + else { + lees_edw_options_t myopt = {.nplanes = -1, .type = LE_SHEAR_TYPE_INVALID}; + cJSON * np = cJSON_GetObjectItemCaseSensitive(json, "Number of planes"); + cJSON * st = cJSON_GetObjectItemCaseSensitive(json, "Shear type"); + cJSON * sp = cJSON_GetObjectItemCaseSensitive(json, "Period (timesteps)"); + cJSON * rt = cJSON_GetObjectItemCaseSensitive(json, "Reference time"); + cJSON * ps = cJSON_GetObjectItemCaseSensitive(json, "Plane speed"); + + if (np) myopt.nplanes = cJSON_GetNumberValue(np); + + if (myopt.nplanes > 0) { + if (st) { + myopt.type = lees_edw_type_from_string(cJSON_GetStringValue(st)); + /* Then period only if oscillatory ... */ + if (myopt.type == LE_SHEAR_TYPE_OSCILLATORY && sp) { + myopt.period = cJSON_GetNumberValue(sp); + if (myopt.period <= 0) ifail += 2; + } + } + if (rt) myopt.nt0 = cJSON_GetNumberValue(rt); + if (ps) myopt.uy = cJSON_GetNumberValue(ps); + } + + /* Error condition */ + if (myopt.nplanes < 0) ifail += 1; + + *opts = myopt; + } + + return ifail; +} diff --git a/src/lees_edwards_options.h b/src/lees_edwards_options.h new file mode 100644 index 000000000..0a3b8e7de --- /dev/null +++ b/src/lees_edwards_options.h @@ -0,0 +1,47 @@ +/***************************************************************************** + * + * lees_edwards_options.h + * + * Meta-data to define Lees Edwards configuration. + * Only the number of planes is required, as the plane spacing is always + * uniform; all the planes have the same "speed". + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#ifndef LUDWIG_LEES_EDWARDS_OPTIONS_H +#define LUDWIG_LEES_EDWARDS_OPTIONS_H + +#include "util_json.h" + +typedef struct lees_edw_options_s lees_edw_options_t; + +typedef enum lees_edw_enum { + LE_SHEAR_TYPE_INVALID, + LE_SHEAR_TYPE_STEADY, /* Steady shear (default) */ + LE_SHEAR_TYPE_OSCILLATORY /* Oscillatory shear uy = uy cos(wt) */ +} lees_edw_enum_t; + +struct lees_edw_options_s { + int nplanes; /* Number of planes */ + lees_edw_enum_t type; /* Shear type */ + int period; /* Oscillatory shear period (timesteps) */ + int nt0; /* Reference time (usually t0 = 0) */ + double uy; /* "Plane speed"; stricly the velocity + * jump crossing the plane. */ +}; + +const char * lees_edw_type_to_string(lees_edw_enum_t mytype); +lees_edw_enum_t lees_edw_type_from_string(const char * str); + +int lees_edw_opts_to_json(const lees_edw_options_t * opts, cJSON ** json); +int lees_edw_opts_from_json(const cJSON * json, lees_edw_options_t * opts); + +#endif From 85142ba4892a4a6f1eb08479a9f2e3cc69486b15 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 22 Dec 2022 17:22:36 +0000 Subject: [PATCH 186/244] Combine coords_s with coords.h --- src/cahn_hilliard.c | 1 - src/collision.c | 1 - src/colloid_sums.c | 2 +- src/colloids_halo.c | 2 +- src/coords_field.c | 1 - src/coords_s.h | 43 ------------------------------------------- src/kernel.c | 1 - src/propagation.c | 1 - 8 files changed, 2 insertions(+), 50 deletions(-) delete mode 100644 src/coords_s.h diff --git a/src/cahn_hilliard.c b/src/cahn_hilliard.c index d890dddf0..1760c5915 100644 --- a/src/cahn_hilliard.c +++ b/src/cahn_hilliard.c @@ -31,7 +31,6 @@ #include "advection_s.h" #include "advection_bcs.h" -#include "coords_s.h" #include "cahn_hilliard.h" __host__ int ch_update_forward_step(ch_t * ch, field_t * phif); diff --git a/src/collision.c b/src/collision.c index a44db7b1a..3d3c47d1b 100644 --- a/src/collision.c +++ b/src/collision.c @@ -28,7 +28,6 @@ #include "pe.h" #include "util.h" -#include "coords_s.h" #include "physics.h" #include "free_energy.h" #include "visc.h" diff --git a/src/colloid_sums.c b/src/colloid_sums.c index d980b844c..7faf5e8b2 100644 --- a/src/colloid_sums.c +++ b/src/colloid_sums.c @@ -30,7 +30,7 @@ #include #include "pe.h" -#include "coords_s.h" +#include "coords.h" #include "colloids.h" #include "colloid_sums.h" diff --git a/src/colloids_halo.c b/src/colloids_halo.c index 230d44623..a5584aa99 100644 --- a/src/colloids_halo.c +++ b/src/colloids_halo.c @@ -20,7 +20,7 @@ #include #include "pe.h" -#include "coords_s.h" +#include "coords.h" #include "colloids.h" #include "colloids_halo.h" #include "util.h" diff --git a/src/coords_field.c b/src/coords_field.c index ddac39ca8..1332ad9c2 100644 --- a/src/coords_field.c +++ b/src/coords_field.c @@ -20,7 +20,6 @@ #include "pe.h" #include "util.h" -#include "coords_s.h" #include "memory.h" #include "coords_field.h" diff --git a/src/coords_s.h b/src/coords_s.h deleted file mode 100644 index d973061ab..000000000 --- a/src/coords_s.h +++ /dev/null @@ -1,43 +0,0 @@ - -#ifndef LUDWIG_COORDS_S_H -#define LUDWIG_COORDS_S_H - -#include "coords.h" - -typedef struct coords_param_s cs_param_t; - -struct coords_param_s { - int nhalo; /* Width of halo region */ - int nsites; /* Total sites (incl. halo) */ - int ntotal[3]; /* System (physical) size */ - int nlocal[3]; /* Local system size */ - int noffset[3]; /* Local system offset */ - int str[3]; /* Memory strides */ - int periodic[3]; /* Periodic boundary (non-periodic = 0) */ - - int mpi_cartsz[3]; /* Cartesian size */ - int mpi_cartcoords[3]; /* Cartesian coordinates lookup */ - - double lmin[3]; /* System L_min */ -}; - -struct coords_s { - pe_t * pe; /* Retain a reference to pe */ - int nref; /* Reference count */ - - cs_param_t * param; /* Constants */ - - /* Host data */ - int mpi_cartrank; /* MPI Cartesian rank */ - int reorder; /* MPI reorder flag */ - int mpi_cart_neighbours[2][3]; /* Ranks of Cartesian neighbours lookup */ - int * listnlocal[3]; /* Rectilinear decomposition */ - int * listnoffset[3]; /* Rectilinear offsets */ - - MPI_Comm commcart; /* Cartesian communicator */ - MPI_Comm commperiodic; /* Cartesian periodic communicator */ - - cs_t * target; /* Host pointer to target memory */ -}; - -#endif diff --git a/src/kernel.c b/src/kernel.c index fe9a7fd3c..18100fb32 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -20,7 +20,6 @@ #include #include "pe.h" -#include "coords_s.h" #include "kernel.h" /* Static kernel context information */ diff --git a/src/propagation.c b/src/propagation.c index 77188cb9c..ba7f7f334 100644 --- a/src/propagation.c +++ b/src/propagation.c @@ -19,7 +19,6 @@ #include #include "pe.h" -#include "coords_s.h" #include "kernel.h" #include "propagation.h" #include "timer.h" From 8d6d73db681f2388c70cae3efa5b4b90fa985d1d Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 22 Dec 2022 17:24:00 +0000 Subject: [PATCH 187/244] Remove unused code --- tests/unit/test_io.c | 70 ++------------------------------------------ 1 file changed, 2 insertions(+), 68 deletions(-) diff --git a/tests/unit/test_io.c b/tests/unit/test_io.c index 8fbea0b51..3aa5345f8 100644 --- a/tests/unit/test_io.c +++ b/tests/unit/test_io.c @@ -35,9 +35,7 @@ static int test_io_read1(FILE *, int index, void * self); static int test_io_write1(FILE *, int index, void * self); static int test_io_read3(FILE *, int index, void * self); static int test_io_write3(FILE *, int index, void * self); -#ifdef OLDSHIT -__host__ int test_io_info_create_impl_a(pe_t * pe, cs_t * cs); -#endif + /***************************************************************************** * * test_io_suite @@ -54,9 +52,7 @@ int test_io_suite(void) { cs_init(cs); do_test_io_info_struct(pe, cs); -#ifdef OLDSHIT - test_io_info_create_impl_a(pe, cs); -#endif + pe_info(pe, "PASS ./unit/test_io\n"); cs_free(cs); pe_free(pe); @@ -135,68 +131,6 @@ int do_test_io_info_struct(pe_t * pe, cs_t * cs) { return 0; } -/***************************************************************************** - * - * test_io_info_create_impl_b - * - *****************************************************************************/ -#ifdef OLDSHIT -__host__ int test_io_info_create_impl_a(pe_t * pe, cs_t * cs) { - - io_info_args_t args = io_info_args_default(); - io_implementation_t impl = {0}; - io_info_t * info = {0}; - - assert(pe); - assert(cs); - - sprintf(impl.name, "%s", "Test implementaton"); - - impl.write_binary = test_io_write1; - impl.read_binary = test_io_read1; - impl.write_ascii = test_io_write3; - impl.read_ascii = test_io_read3; - impl.bytesize_ascii = 10; - impl.bytesize_binary = sizeof(double); - - io_info_create_impl(pe, cs, args, &impl, &info); - - assert(info); - - { - /* Copied implementation correctly... */ - assert((info->pe == pe)); - assert((info->cs == cs)); - assert((info->impl.write_binary == impl.write_binary)); - assert((info->impl.read_binary == impl.read_binary)); - assert((info->impl.write_ascii == impl.write_ascii)); - assert((info->impl.read_ascii == impl.read_ascii)); - - assert(info->impl.bytesize_ascii == impl.bytesize_ascii); - assert(info->impl.bytesize_binary == impl.bytesize_binary); - } - - { - /* Default input format binary ... */ - size_t bs; - assert(info->args.input.iorformat == IO_RECORD_BINARY); - io_info_input_bytesize(info, &bs); - assert(bs == impl.bytesize_binary); - } - - { - /* Default output format is binary... */ - size_t bytesize; - assert(info->args.output.iorformat == IO_RECORD_BINARY); - io_info_output_bytesize(info, &bytesize); - assert(bytesize == impl.bytesize_binary); - } - - io_info_free(info); - - return 0; -} -#endif /***************************************************************************** * * test_write_1 From 40c29a4615ac24b0694c8880b2743b8f33b8c133 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 22 Dec 2022 17:25:14 +0000 Subject: [PATCH 188/244] Restore two io routines --- src/io_harness.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/io_harness.c b/src/io_harness.c index a664f383a..8e46c7a9c 100644 --- a/src/io_harness.c +++ b/src/io_harness.c @@ -35,7 +35,6 @@ #include "pe.h" #include "util.h" #include "util_fopen.h" -#include "coords_s.h" #include "leesedwards.h" #include "io_harness.h" @@ -740,8 +739,24 @@ int io_write_data(io_info_t * info, const char * filename_stub, void * data) { assert(info); assert(data); - /* Use the standard "parallel" method. */ - io_write_data_p(info, filename_stub, data); + if (info->args.output.mode == IO_MODE_MULTIPLE) { + /* Use the standard "parallel" method for the time being. */ + io_write_data_p(info, filename_stub, data); + } + else { + /* This is serial output format if one I/O group */ + double t0, t1; + t0 = MPI_Wtime(); + io_write_data_s(info, filename_stub, data); + t1 = MPI_Wtime(); + if (info->args.output.report) { + size_t bytesize = 0; + io_info_output_bytesize(info, &bytesize); + pe_info(info->pe, "Write %lu bytes in %f secs %f GB/s\n", + info->nsites*bytesize, t1-t0, + info->nsites*bytesize/(1.0e+09*(t1-t0))); + } + } return 0; } From 19b77c17f1f1edeb738d08759e9c17f7066f2336 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 22 Dec 2022 17:25:49 +0000 Subject: [PATCH 189/244] Add new metadata output --- src/coords.c | 162 ++++++++++++++++++++++++- src/coords.h | 49 +++++++- src/field.c | 8 ++ src/hydro.c | 3 + src/io_event.c | 1 - src/io_metadata.c | 68 ++++++++++- src/io_metadata.h | 4 + src/io_subfile.c | 1 - src/leesedwards.c | 31 ++--- src/leesedwards.h | 20 +--- src/leesedwards_rt.c | 4 +- src/leesedwards_rt.h | 5 +- src/ludwig.c | 4 +- src/model.c | 7 ++ tests/unit/test_coords.c | 159 ++++++++++++++++++++++++- tests/unit/test_io_metadata.c | 48 +++++++- tests/unit/test_io_subfile.c | 1 - tests/unit/test_le.c | 215 +++++++++++++++++++++++++++++++++- tests/unit/test_phi_ch.c | 4 +- 19 files changed, 740 insertions(+), 54 deletions(-) diff --git a/src/coords.c b/src/coords.c index 3cb298107..5422d0a8a 100644 --- a/src/coords.c +++ b/src/coords.c @@ -7,7 +7,7 @@ * Edinburgh Soft Matter and Statistical Physics and * Edinburgh Parallel Computing Centre * - * (c) 2010-2021 The University of Edinburgh + * (c) 2010-2022 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -19,7 +19,7 @@ #include #include "util.h" -#include "coords_s.h" +#include "coords.h" static __host__ int cs_is_ok_decomposition(cs_t * cs); static __host__ int cs_rectilinear_decomposition(cs_t * cs); @@ -909,3 +909,161 @@ __host__ int cs_cart_rank(cs_t * cs) { assert(cs); return cs->mpi_cartrank; } + + +/***************************************************************************** + * + * cs_options_to_json + * + * Experimental. + * An ad-hoc object which is "cs_options" currently a subset of cs_param_t. + * First argument to be replaced by the thing itself, when available. A + * correct cs_options_t must allow creation of cs_t without further + * information (excepting pe_t). + * + *****************************************************************************/ + +int cs_options_to_json(const cs_param_t * opts, cJSON ** json) { + + int ifail = 0; + + assert(opts); + + if (json == NULL || *json != NULL) { + ifail = -1; + } + else { + cJSON * myjson = cJSON_CreateObject(); + cJSON * ntotal = cJSON_CreateIntArray(opts->ntotal, 3); + cJSON * period = cJSON_CreateIntArray(opts->periodic, 3); + cJSON * lmin = cJSON_CreateDoubleArray(opts->lmin, 3); + + cJSON_AddItemToObject(myjson, "System size (total)", ntotal); + cJSON_AddItemToObject(myjson, "Periodic boundaries", period); + cJSON_AddItemToObject(myjson, "Left-end limit Lmin", lmin); + + *json = myjson; + } + + return ifail; +} + +/***************************************************************************** + * + * cs_options_from_json + * + * Experimental. + * This is just the pair of the above cs_options_to_json(). + * We currently set the relevant components of cs_param_t (see above). + * Missing at least "nhalo". + * + *****************************************************************************/ + +int cs_options_from_json(const cJSON * json, cs_param_t * opts) { + + int ifail = 0; + + if (json == NULL || opts == NULL) { + ifail = -1; + } + else { + /* Note no default for system size: must be present */ + cs_param_t param = {.periodic = {1,1,1}, .lmin = {0.5,0.5,0.5}}; + cJSON * jn = cJSON_GetObjectItemCaseSensitive(json, "System size (total)"); + cJSON * jp = cJSON_GetObjectItemCaseSensitive(json, "Periodic boundaries"); + cJSON * jl = cJSON_GetObjectItemCaseSensitive(json, "Left-end limit Lmin"); + + if (jn) util_json_to_int_array(jn, param.ntotal, 3); + if (jp) util_json_to_int_array(jp, param.periodic, 3); + if (jl) util_json_to_double_array(jl, param.lmin, 3); + + /* Error conditions */ + if (!jn) ifail = 1; + + *opts = param; + } + + return ifail; +} + +/***************************************************************************** + * + * cs_to_json + * + * Experimental design + * - "physical part" + * - coords options + * - Lees Edwards options + * - MPI decomposition part + * + * The idea would be that one can create a cs_t from the physical part + * only, irrespective of MPI configuration. + * + *****************************************************************************/ + +int cs_to_json(const cs_t * cs, cJSON ** json) { + + int ifail = 0; + + assert(cs); + + if (json == NULL || *json != NULL) { + ifail = -1; + } + else { + /* Add options, lees edwards */ + /* No decomposition yet. */ + cJSON * myson = cJSON_CreateObject(); + { + cJSON * jtmp = NULL; + ifail += cs_options_to_json(cs->param, &jtmp); + if (ifail == 0) cJSON_AddItemToObject(myson, "options", jtmp); + } + { + cJSON * jtmp = NULL; + ifail += lees_edw_opts_to_json(&cs->leopts, &jtmp); + if (ifail == 0) cJSON_AddItemToObject(myson, "lees_edwards", jtmp); + } + + *json = myson; + } + + return ifail; +} + +/***************************************************************************** + * + * cs_from_json + * + * An (somewhat) experimental placeholder. + * Create and return a new cs_t from the json. + * + *****************************************************************************/ + +int cs_from_json(pe_t * pe, const cJSON * json, cs_t ** cs) { + + int ifail = 0; + cs_t * obj = NULL; + + if (json == NULL || cs == NULL) { + ifail = -1; + } + else { + /* Get cs_options and initialise in the existing picture... */ + cJSON * jopt = cJSON_GetObjectItemCaseSensitive(json, "options"); + cJSON * jlep = cJSON_GetObjectItemCaseSensitive(json, "lees_edwards"); + cs_param_t opts = {0}; + ifail = cs_options_from_json(jopt, &opts); + if (ifail == 0) { + cs_create(pe, &obj); + cs_ntotal_set(obj, opts.ntotal); + cs_periodicity_set(obj, opts.periodic); + cs_init(obj); + lees_edw_opts_from_json(jlep, &obj->leopts); + } + } + + *cs = obj; + + return ifail; +} diff --git a/src/coords.h b/src/coords.h index 16b1a7fbf..786571f65 100644 --- a/src/coords.h +++ b/src/coords.h @@ -5,7 +5,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2010-2021 The University of Edinburgh + * (c) 2010-2022 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -17,8 +17,50 @@ #include "pe.h" #include "cartesian.h" +#include "lees_edwards_options.h" +#include "util_json.h" + +/* Note that there is a copy of the Lees Edwards options here to + * allow the relevant metadata to be constructed from coords_t alone. + * All the Lees Edwards operations are in leesedwards.c */ typedef struct coords_s cs_t; +typedef struct coords_param_s cs_param_t; + +struct coords_param_s { + int nhalo; /* Width of halo region */ + int nsites; /* Total sites (incl. halo) */ + int ntotal[3]; /* System (physical) size */ + int nlocal[3]; /* Local system size */ + int noffset[3]; /* Local system offset */ + int str[3]; /* Memory strides */ + int periodic[3]; /* Periodic boundary (non-periodic = 0) */ + + int mpi_cartsz[3]; /* Cartesian size */ + int mpi_cartcoords[3]; /* Cartesian coordinates lookup */ + + double lmin[3]; /* System L_min */ +}; + +struct coords_s { + pe_t * pe; /* Retain a reference to pe */ + int nref; /* Reference count */ + + cs_param_t * param; /* Constants */ + + /* Host data */ + int mpi_cartrank; /* MPI Cartesian rank */ + int reorder; /* MPI reorder flag */ + int mpi_cart_neighbours[2][3]; /* Ranks of Cartesian neighbours lookup */ + int * listnlocal[3]; /* Rectilinear decomposition */ + int * listnoffset[3]; /* Rectilinear offsets */ + lees_edw_options_t leopts; /* Copy of LE opts (no. of planes etc.) */ + + MPI_Comm commcart; /* Cartesian communicator */ + MPI_Comm commperiodic; /* Cartesian periodic communicator */ + + cs_t * target; /* Host pointer to target memory */ +}; #define NSYMM 6 /* Elements for general symmetric tensor */ @@ -70,4 +112,9 @@ __host__ __device__ int cs_nall(cs_t * cs, int nall[3]); __host__ int cs_cart_shift(MPI_Comm comm, int dim, int direction, int * rank); +int cs_options_to_json(const cs_param_t * opts, cJSON ** json); +int cs_options_from_json(const cJSON * json, cs_param_t * opts); +int cs_to_json(const cs_t * cs, cJSON ** json); +int cs_from_json(pe_t * pe, const cJSON * json, cs_t ** cs); + #endif diff --git a/src/field.c b/src/field.c index f9c5f6869..0fbfdc25f 100644 --- a/src/field.c +++ b/src/field.c @@ -1664,6 +1664,14 @@ int field_io_write(field_t * field, int timestep, io_event_t * event) { const io_metadata_t * meta = &field->iometadata_out; + /* Metadata */ + if (meta->iswriten == 0) { + /* No extra comments at the moment */ + cJSON * comments = NULL; + int ifail = io_metadata_write(meta, field->name, comments); + if (ifail == 0) field->iometadata_out.iswriten = 1; + } + /* old ANSI */ if (meta->options.mode != IO_MODE_MPIIO) { char filename[BUFSIZ] = {0}; diff --git a/src/hydro.c b/src/hydro.c index a7f8bce62..8980423a0 100644 --- a/src/hydro.c +++ b/src/hydro.c @@ -1110,6 +1110,9 @@ __global__ void hydro_correct_kernel_v(kernel_ctxt_t * ktx, hydro_t * hydro, int hydro_io_write(hydro_t * hydro, int timestep, io_event_t * event) { /* For backwards compatibility, we currently allow old-style output */ + /* Call to io_write_data() must be here, as only have hydro->info + for the old options (not u->info etc). This will go away with + a consistent impl. */ const io_metadata_t * rmeta = &hydro->rho->iometadata_out; const io_metadata_t * umeta = &hydro->u->iometadata_out; diff --git a/src/io_event.c b/src/io_event.c index 336608f36..77d156eaa 100644 --- a/src/io_event.c +++ b/src/io_event.c @@ -16,7 +16,6 @@ #include -#include "coords_s.h" #include "io_event.h" /***************************************************************************** diff --git a/src/io_metadata.c b/src/io_metadata.c index 1f73d0019..b1bb5078f 100644 --- a/src/io_metadata.c +++ b/src/io_metadata.c @@ -21,6 +21,7 @@ #include #include "io_metadata.h" +#include "util_fopen.h" /***************************************************************************** * @@ -167,6 +168,12 @@ int io_metadata_to_json(const io_metadata_t * meta, cJSON ** json) { else { cJSON * myjson = cJSON_CreateObject(); + { + /* coords (including Lees Edwards information) */ + cJSON * jtmp = NULL; + ifail = cs_to_json(meta->cs, &jtmp); + if (ifail == 0) cJSON_AddItemToObject(myjson, "coords", jtmp); + } { /* options */ cJSON * jtmp = NULL; @@ -195,6 +202,10 @@ int io_metadata_to_json(const io_metadata_t * meta, cJSON ** json) { * * io_metadata_from_json * + * Assumes we have an existing coordinate system. + * FIXME: However, Lees Edwards options cs->leopts is initialised here. + * The question of initialising cs_t from JSON is PENDING. + * *****************************************************************************/ int io_metadata_from_json(cs_t * cs, const cJSON * json, io_metadata_t * m) { @@ -203,14 +214,16 @@ int io_metadata_from_json(cs_t * cs, const cJSON * json, io_metadata_t * m) { assert(cs); assert(json); + assert(cs->leopts.nplanes == 0); /* No exsiting information */ if (m == NULL) { ifail = -1; } else { /* Generate options, element, from json, and initialise ... */ - io_options_t options = {IO_MODE_INVALID}; - io_element_t element = {0}; + /* This initialises cs->leopts */ + io_options_t options = {IO_MODE_INVALID}; + io_element_t element = {0}; cJSON * jopt = cJSON_GetObjectItemCaseSensitive(json, "io_options"); cJSON * jelm = cJSON_GetObjectItemCaseSensitive(json, "io_element"); @@ -221,3 +234,54 @@ int io_metadata_from_json(cs_t * cs, const cJSON * json, io_metadata_t * m) { return ifail; } + +/***************************************************************************** + * + * io_metadata_write + * + * Driver to write to file with an optional extra comment block. + * + *****************************************************************************/ + +int io_metadata_write(const io_metadata_t * metadata, + const char * stub, + const cJSON * comments) { + + cJSON * json = NULL; + char filename[BUFSIZ] = {0}; + + assert(metadata); + assert(stub); + + if (metadata->iswriten) return 0; + + /* Generate a json with the header inserted (gets deleted below) */ + + io_metadata_to_json(metadata, &json); + if (comments) { + cJSON * jtmp = cJSON_Duplicate(comments, 1); + cJSON_AddItemToObject(json, "comments", jtmp); + } + + /* The extension uses indices in natural numbers 001-002 etc. */ + sprintf(filename, "%s-metadata.%3.3d-%3.3d", stub, + 1 + metadata->subfile.index, metadata->subfile.nfile); + + { + /* Write to file (root only) */ + int rank = -1; + MPI_Comm_rank(metadata->comm, &rank); + if (rank == 0) { + FILE * fp = NULL; + char * str = cJSON_Print(json); + fp = util_fopen(filename, "w"); + fprintf(fp, "%s\n", str); + fclose(fp); + free(str); + } + } + + cJSON_Delete(json); + + return 0; +} diff --git a/src/io_metadata.h b/src/io_metadata.h index 66b5dedab..5d39e4d6c 100644 --- a/src/io_metadata.h +++ b/src/io_metadata.h @@ -29,6 +29,7 @@ struct io_metadata_s { cs_limits_t limits; /* Always local size with no halo */ MPI_Comm parent; /* Cartesian communicator */ MPI_Comm comm; /* Cartesian sub-communicator */ + int iswriten; /* updated to true if file is writen */ io_options_t options; io_element_t element; @@ -51,4 +52,7 @@ int io_metadata_to_json(const io_metadata_t * metadata, cJSON ** json); int io_metadata_from_json(cs_t * cs, const cJSON * json, io_metadata_t * metadata); +int io_metadata_write(const io_metadata_t * metadata, + const char * stub, + const cJSON * comments); #endif diff --git a/src/io_subfile.c b/src/io_subfile.c index aee384ece..6aa71e0d6 100644 --- a/src/io_subfile.c +++ b/src/io_subfile.c @@ -17,7 +17,6 @@ #include #include -#include "coords_s.h" #include "io_subfile.h" /***************************************************************************** diff --git a/src/leesedwards.c b/src/leesedwards.c index 55c7bc5fd..c986e7274 100644 --- a/src/leesedwards.c +++ b/src/leesedwards.c @@ -9,7 +9,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2010-2021 The University of Edinburgh + * (c) 2010-2022 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -26,8 +26,6 @@ typedef struct lees_edw_param_s lees_edw_param_t; -enum shear_type {LEES_EDW_LINEAR, LEES_EDW_OSCILLATORY}; - struct lees_edw_s { pe_t * pe; /* Parallel environment */ cs_t * cs; /* Coordinate system */ @@ -68,7 +66,7 @@ struct lees_edw_param_s { double time0; /* time offset */ }; -static int lees_edw_init(lees_edw_t * le, lees_edw_info_t * info); +static int lees_edw_init(lees_edw_t * le, const lees_edw_options_t * info); static int lees_edw_checks(lees_edw_t * le); static int lees_edw_init_tables(lees_edw_t * le); @@ -84,9 +82,13 @@ __host__ __device__ int lees_edw_index_buffer_to_real(lees_edw_t * le, int ibuf) * * lees_edw_create * + * info may be NULL, in which case no planes. + * This NULL possibility should probably be removed. + * *****************************************************************************/ -__host__ int lees_edw_create(pe_t * pe, cs_t * cs, lees_edw_info_t * info, +__host__ int lees_edw_create(pe_t * pe, cs_t * cs, + const lees_edw_options_t * info, lees_edw_t ** ple) { int ndevice; @@ -106,6 +108,7 @@ __host__ int lees_edw_create(pe_t * pe, cs_t * cs, lees_edw_info_t * info, le->pe = pe; pe_retain(pe); le->cs = cs; + if (info) cs->leopts = *info; /* Copy of options for i/o metadata */ cs_retain(cs); le->param->nplanetotal = 0; @@ -254,7 +257,7 @@ int lees_edw_oscillatory_set(lees_edw_t * le, int period) { assert(le); - le->param->type = LEES_EDW_OSCILLATORY; + le->param->type = LE_SHEAR_TYPE_OSCILLATORY; le->param->period = period; le->param->omega = 2.0*4.0*atan(1.0)/le->param->period; @@ -289,7 +292,7 @@ int lees_edw_toffset_set(lees_edw_t * le, int nt0) { * *****************************************************************************/ -static int lees_edw_init(lees_edw_t * le, lees_edw_info_t * info) { +static int lees_edw_init(lees_edw_t * le, const lees_edw_options_t * info) { int ntotal[3]; @@ -409,7 +412,7 @@ __host__ int lees_edw_info(lees_edw_t * le) { (int)(le->param->dx_min + np*le->param->dx_sep), le->param->uy); } - if (le->param->type == LEES_EDW_LINEAR) { + if (le->param->type == LE_SHEAR_TYPE_STEADY) { pe_info(le->pe, "Overall shear rate = %f\n", gammadot); } else { @@ -684,7 +687,7 @@ int lees_edw_steady_uy(lees_edw_t * le, int ic, double * uy) { double xglobal; assert(le); - assert(le->param->type == LEES_EDW_LINEAR); + assert(le->param->type == LE_SHEAR_TYPE_STEADY); cs_nlocal_offset(le->cs, offset); lees_edw_shear_rate(le, &gammadot); @@ -725,7 +728,7 @@ int lees_edw_block_uy(lees_edw_t * le, int ic, double * uy) { double ltot[3]; assert(le); - assert(le->param->type == LEES_EDW_LINEAR); + assert(le->param->type == LE_SHEAR_TYPE_STEADY); cs_lmin(le->cs, lmin); cs_ltot(le->cs, ltot); @@ -766,7 +769,7 @@ int lees_edw_plane_uy_now(lees_edw_t * le, double t, double * uy) { assert(tle >= 0.0); *uy = le->param->uy; - if (le->param->type == LEES_EDW_OSCILLATORY) *uy *= cos(le->param->omega*tle); + if (le->param->type == LE_SHEAR_TYPE_OSCILLATORY) *uy *= cos(le->param->omega*tle); return 0; } @@ -874,12 +877,12 @@ int lees_edw_buffer_displacement(lees_edw_t * le, int ib, double t, double * dy) *dy = 0.0; - if (le->param->type == LEES_EDW_LINEAR) { + if (le->param->type == LE_SHEAR_TYPE_STEADY) { *dy = tle*le->param->uy*le->buffer_duy[ib]; assert(le->buffer_duy[ib] == lees_edw_buffer_duy(le, ib)); } - if (le->param->type == LEES_EDW_OSCILLATORY) { + if (le->param->type == LE_SHEAR_TYPE_OSCILLATORY) { *dy = le->param->uy*sin(le->param->omega*tle)/le->param->omega; } @@ -1201,7 +1204,7 @@ __host__ int lees_edw_buffer_du(lees_edw_t * le, int ib, double ule[3]) { assert(le); assert(ib >= 0 && ib < le->param->nxbuffer); - if (le->param->type == LEES_EDW_LINEAR) { + if (le->param->type == LE_SHEAR_TYPE_STEADY) { ule[X] = 0.0; ule[Y] = le->param->uy*le->buffer_duy[ib]; assert(le->buffer_duy[ib] == lees_edw_buffer_duy(le, ib)); diff --git a/src/leesedwards.h b/src/leesedwards.h index 2a800a8cd..03812738f 100644 --- a/src/leesedwards.h +++ b/src/leesedwards.h @@ -6,7 +6,7 @@ * Edinburgh Parallel Computing Centre * * Kevin Stratford (kevin@epcc.ed.ac.uk) - * (c) 2010-2021 The University of Edinburgh + * (c) 2010-2022 The University of Edinburgh * *****************************************************************************/ @@ -18,23 +18,12 @@ #include "runtime.h" #include "coords.h" #include "physics.h" +#include "lees_edwards_options.h" typedef struct lees_edw_s lees_edw_t; -typedef struct lees_edw_info_s lees_edw_info_t; -struct lees_edw_info_s { - int nplanes; - int type; - int period; - int nt0; - double uy; -}; - -typedef enum lees_edw_enum {LE_SHEAR_TYPE_STEADY, - LE_SHEAR_TYPE_OSCILLATORY} lees_edw_enum_t; - - -__host__ int lees_edw_create(pe_t * pe, cs_t * coords, lees_edw_info_t * info, +__host__ int lees_edw_create(pe_t * pe, cs_t * coords, + const lees_edw_options_t * opts, lees_edw_t ** le); __host__ int lees_edw_free(lees_edw_t * le); __host__ int lees_edw_retain(lees_edw_t * le); @@ -81,5 +70,4 @@ __host__ __device__ int lees_edw_ic_to_buff(lees_edw_t * le, int ic, int di); __host__ __device__ void lees_edw_index_v(lees_edw_t * le, int ic[NSIMDVL], int jc[NSIMDVL], int kc[NSIMDVL], int index[NSIMDVL]); - #endif diff --git a/src/leesedwards_rt.c b/src/leesedwards_rt.c index 0e68c9551..f92a1e06e 100644 --- a/src/leesedwards_rt.c +++ b/src/leesedwards_rt.c @@ -2,6 +2,8 @@ * * leesedwards_rt.c * + * This could be combined with lees_edwards_options.[ch] + * *****************************************************************************/ #include @@ -13,7 +15,7 @@ * *****************************************************************************/ -int lees_edw_init_rt(rt_t * rt, lees_edw_info_t * info) { +int lees_edw_init_rt(rt_t * rt, lees_edw_options_t * info) { int key; diff --git a/src/leesedwards_rt.h b/src/leesedwards_rt.h index 5739a6c93..ac7bbd96c 100644 --- a/src/leesedwards_rt.h +++ b/src/leesedwards_rt.h @@ -2,6 +2,7 @@ * * leesedwards_rt.h * + * See comment in leesedwards_rt.c. * *****************************************************************************/ @@ -9,8 +10,8 @@ #define LUDWIG_LEESEDWARDS_RT_H #include "runtime.h" -#include "leesedwards.h" +#include "lees_edwards_options.h" -int lees_edw_init_rt(rt_t * rt, lees_edw_info_t * info); +int lees_edw_init_rt(rt_t * rt, lees_edw_options_t * info); #endif diff --git a/src/ludwig.c b/src/ludwig.c index 1b7a016fe..a7ab6c101 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -1267,8 +1267,8 @@ int free_energy_init_rt(ludwig_t * ludwig) { cs_t * cs = NULL; lees_edw_t * le = NULL; - lees_edw_info_t le_info = {0}; - lees_edw_info_t * info = &le_info; + lees_edw_options_t le_info = {0}; + lees_edw_options_t * info = &le_info; assert(ludwig); assert(ludwig->pe); diff --git a/src/model.c b/src/model.c index 812ed351d..0c61e7dca 100644 --- a/src/model.c +++ b/src/model.c @@ -1566,6 +1566,13 @@ int lb_io_write(lb_t * lb, int timestep, io_event_t * event) { const io_metadata_t * meta = &lb->output; + if (meta->iswriten == 0) { + /* No comments at the moment */ + cJSON * comments = NULL; + int ifail = io_metadata_write(meta, "dist", comments); + if (ifail == 0) lb->output.iswriten = 1; + } + if (meta->options.mode != IO_MODE_MPIIO) { /* Old-style */ char filename[BUFSIZ] = {0}; diff --git a/tests/unit/test_coords.c b/tests/unit/test_coords.c index 3dbc3325d..6475d4292 100644 --- a/tests/unit/test_coords.c +++ b/tests/unit/test_coords.c @@ -7,7 +7,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2009-2018 The University of Edinburgh + * (c) 2009-2022 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "pe.h" @@ -33,6 +34,11 @@ static int test_coords_sub_communicator(cs_t * cs); static int test_coords_periodic_comm(cs_t * cs); static int neighbour_rank(cs_t * cs, int nx, int ny, int nz); +int test_cs_options_to_json(void); +int test_cs_options_from_json(void); +int test_cs_to_json(pe_t * pe); +int test_cs_from_json(pe_t * pe); + __host__ int do_test_coords_device1(pe_t * pe); __global__ void do_test_coords_kernel1(cs_t * cs); @@ -104,6 +110,11 @@ int test_coords_suite(void) { test_coords_periodic_comm(cs); cs_free(cs); + test_cs_options_to_json(); + test_cs_options_from_json(); + test_cs_to_json(pe); + test_cs_from_json(pe); + /* Device tests */ do_test_coords_device1(pe); @@ -593,3 +604,149 @@ __global__ void do_test_coords_kernel1(cs_t * cs) { return; } + +/***************************************************************************** + * + * test_cs_options_to_json + * + *****************************************************************************/ + +int test_cs_options_to_json(void) { + + int ifail = 0; + + { + cs_param_t opts = {.ntotal = {2, 3, 4}, + .periodic = {1, 1, 1}, + .lmin = {0.5, 0.5, 0.5}}; + cJSON * json = NULL; + + ifail = cs_options_to_json(&opts, &json); + assert(ifail == 0); + + assert(json); + cJSON_Delete(json); + } + + return ifail; +} + +/***************************************************************************** + * + * test_cs_options_from_json + * + *****************************************************************************/ + +int test_cs_options_from_json(void) { + + int ifail = 0; + + { + /* Size only is ok. */ + cJSON * json = cJSON_Parse("{\"System size (total)\": [2, 3, 4]}"); + cs_param_t param = {0}; + + assert(json); + ifail = cs_options_from_json(json, ¶m); + assert(ifail == 0); + assert(param.ntotal[X] == 2); + assert(param.ntotal[Y] == 3); + assert(param.ntotal[Z] == 4); + + cJSON_Delete(json); + } + + { + /* All available options (with implausible test values) */ + cJSON * json = cJSON_Parse("{" + "\"System size (total)\": [2, 3, 4], " + "\"Periodic boundaries\": [5, 6, 7], " + "\"Left-end limit Lmin\": [0.1,0.2,0.3]" + "}"); + cs_param_t param = {0}; + + assert(json); + ifail = cs_options_from_json(json, ¶m); + assert(param.periodic[X] == 5); + assert(param.periodic[Y] == 6); + assert(param.periodic[Z] == 7); + assert(fabs(param.lmin[X] - 0.1) < DBL_EPSILON); + assert(fabs(param.lmin[Y] - 0.2) < DBL_EPSILON); + assert(fabs(param.lmin[Z] - 0.3) < DBL_EPSILON); + + cJSON_Delete(json); + } + + return ifail; +} + +/***************************************************************************** + * + * test_cs_to_json + * + *****************************************************************************/ + +int test_cs_to_json(pe_t * pe) { + + int ifail = 0; + cs_t * cs = NULL; + + assert(pe); + + { + /* A default cs_t: just make sure we have the right components */ + cJSON * json = NULL; + cs_create(pe, &cs); + cs_init(cs); + + ifail = cs_to_json(cs, &json); + assert(ifail == 0); + assert(cJSON_GetObjectItemCaseSensitive(json, "options")); + assert(cJSON_GetObjectItemCaseSensitive(json, "lees_edwards")); + + cJSON_Delete(json); + cs_free(cs); + } + + return ifail; +} + +/***************************************************************************** + * + * test_cs_from_json + * + *****************************************************************************/ + +int test_cs_from_json(pe_t * pe) { + + int ifail = 0; + cs_t * cs = NULL; + + assert(pe); + + { + /* Just about the bare minumum of information */ + + cJSON * json = cJSON_Parse("{" + "\"options\": {" + "\"System size (total)\": [8, 16, 32]" + "}," + "\"lees_edwards\": {" + "\"Number of planes\": 0" + "}" + "}"); + assert(json); + + ifail = cs_from_json(pe, json, &cs); + assert(ifail == 0); + assert(cs->param->ntotal[X] == 8); + assert(cs->param->ntotal[Y] == 16); + assert(cs->param->ntotal[Z] == 32); + assert(cs->leopts.nplanes == 0); + + cs_free(cs); + cJSON_Delete(json); + } + + return ifail; +} diff --git a/tests/unit/test_io_metadata.c b/tests/unit/test_io_metadata.c index 8c0365c20..ade6423bf 100644 --- a/tests/unit/test_io_metadata.c +++ b/tests/unit/test_io_metadata.c @@ -17,9 +17,12 @@ #include "io_metadata.h" +#define IO_METADATA_VERBOSE 0 + int test_io_metadata_initialise(cs_t * cs); int test_io_metadata_create(cs_t * cs); int test_io_metadata_to_json(cs_t * cs); +int test_io_metadata_write(cs_t * cs); /***************************************************************************** * @@ -40,6 +43,7 @@ int test_io_metadata_suite(void) { test_io_metadata_initialise(cs); test_io_metadata_create(cs); test_io_metadata_to_json(cs); + test_io_metadata_write(cs); pe_info(pe, "%-9s %s\n", "PASS", __FILE__); cs_free(cs); @@ -71,7 +75,7 @@ int test_io_metadata_create(cs_t * cs) { io_metadata_free(&meta); assert(meta == NULL); - return 0; + return ifail; } /***************************************************************************** @@ -155,11 +159,12 @@ int test_io_metadata_to_json(cs_t * cs) { ifail = io_metadata_to_json(&metadata, &json); assert(ifail == 0); - { + if (IO_METADATA_VERBOSE) { char * str = cJSON_Print(json); printf("io_metadata: %s\n\n", str); free(str); } + /* Check we have the requisite parts. */ /* We assume the parts themselves are well-formed. */ { @@ -182,3 +187,42 @@ int test_io_metadata_to_json(cs_t * cs) { return ifail; } + +/***************************************************************************** + * + * test_io_metadata_write + * + *****************************************************************************/ + +int test_io_metadata_write(cs_t * cs) { + + int ifail = 0; + io_metadata_t * meta = NULL; + + io_element_t element = {.datatype = MPI_DOUBLE, + .datasize = sizeof(double), + .count = 5, + .endian = io_endianness()}; + io_options_t options = io_options_with_format(IO_MODE_MPIIO, + IO_RECORD_BINARY); + + cJSON * header = cJSON_Parse("{\"Test\": \"via test_io_metadata.c\"}"); + assert(cs); + + io_metadata_create(cs, &options, &element, &meta); + ifail = io_metadata_write(meta, "test-io", header); + assert(ifail == 0); + + /* Remove at rank 0 (a test that the file exists with the correct name) */ + { + int rank = -1; + MPI_Comm_rank(meta->comm, &rank); + if (rank == 0) ifail = remove("test-io-metadata.001-001"); + assert(ifail == 0); + } + + io_metadata_free(&meta); + cJSON_Delete(header); + + return ifail; +} diff --git a/tests/unit/test_io_subfile.c b/tests/unit/test_io_subfile.c index 6bd5ebe6d..09fe4eb0c 100644 --- a/tests/unit/test_io_subfile.c +++ b/tests/unit/test_io_subfile.c @@ -21,7 +21,6 @@ #include #include "pe.h" -#include "coords_s.h" #include "io_subfile.h" int test_io_subfile_default(pe_t * pe); diff --git a/tests/unit/test_le.c b/tests/unit/test_le.c index 3c066a921..2d10fa7e2 100644 --- a/tests/unit/test_le.c +++ b/tests/unit/test_le.c @@ -7,7 +7,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2010-2017 The University of Edinburgh + * (c) 2010-2022 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -15,7 +15,10 @@ *****************************************************************************/ #include +#include #include +#include +#include #include "pe.h" #include "coords.h" @@ -27,6 +30,11 @@ static int test_parallel1(pe_t * pe, cs_t * cs); static int test_le_parallel2(pe_t * pe, cs_t * cs); +int test_lees_edw_type_to_string(void); +int test_lees_edw_type_from_string(void); +int test_lees_edw_opts_to_json(void); +int test_lees_edw_opts_from_json(void); + /***************************************************************************** * * test_le_suite @@ -49,6 +57,11 @@ int test_le_suite(void) { test_parallel1(pe, cs); test_le_parallel2(pe, cs); + test_lees_edw_type_to_string(); + test_lees_edw_type_from_string(); + test_lees_edw_opts_to_json(); + test_lees_edw_opts_from_json(); + physics_free(phys); cs_free(cs); @@ -89,19 +102,19 @@ int test_parallel1(pe_t * pe, cs_t * cs) { double dy; double len[3]; - lees_edw_info_t myinfo = {0}; - lees_edw_info_t * info = &myinfo; + lees_edw_options_t myopts = {0}; + lees_edw_options_t * opts = &myopts; lees_edw_t * le = NULL; MPI_Comm comm; assert(pe); assert(cs); - info->nplanes = nplane; - info->uy = uy_set; + opts->nplanes = nplane; + opts->uy = uy_set; cs_cartsz(cs, cartsz); - lees_edw_create(pe, cs, info, &le); + lees_edw_create(pe, cs, opts, &le); /* Total number of planes... */ test_assert(lees_edw_nplane_total(le) == nplane); @@ -269,3 +282,193 @@ static int test_le_parallel2(pe_t * pe, cs_t * cs) { return 0; } + +/***************************************************************************** + * + * test_lees_edw_type_to_string + * + *****************************************************************************/ + +int test_lees_edw_type_to_string(void) { + + int ifail = 0; + + { + lees_edw_enum_t mytype = LE_SHEAR_TYPE_INVALID; + const char * str = lees_edw_type_to_string(mytype); + + if (strcmp(str, "INVALID") != 0) ifail = -1; + assert(ifail == 0); + } + + { + lees_edw_enum_t mytype = LE_SHEAR_TYPE_STEADY; + const char * str = lees_edw_type_to_string(mytype); + + if (strcmp(str, "STEADY") != 0) ifail = -1; + assert(ifail == 0); + } + + { + lees_edw_enum_t mytype = LE_SHEAR_TYPE_OSCILLATORY; + const char * str = lees_edw_type_to_string(mytype); + + if (strcmp(str, "OSCILLATORY") != 0) ifail = -1; + assert(ifail == 0); + } + + return ifail; +} + +/***************************************************************************** + * + * test_lees_edw_type_from_string + * + *****************************************************************************/ + +int test_lees_edw_type_from_string(void) { + + int ifail = 0; + + { + lees_edw_enum_t mytype = lees_edw_type_from_string("RUBBISH"); + if (mytype != LE_SHEAR_TYPE_INVALID) ifail = -1; + assert(ifail == 0); + } + + { + lees_edw_enum_t mytype = lees_edw_type_from_string("STEADY"); + if (mytype != LE_SHEAR_TYPE_STEADY) ifail = -1; + assert(ifail == 0); + } + + { + lees_edw_enum_t mytype = lees_edw_type_from_string("OSCILLATORY"); + if (mytype != LE_SHEAR_TYPE_OSCILLATORY) ifail = -1; + assert(ifail == 0); + } + + return ifail; +} + +/***************************************************************************** + * + * test_lees_edw_opts_to_json + * + *****************************************************************************/ + +int test_lees_edw_opts_to_json(void) { + + int ifail = 0; + + { + /* No planes. Something of a smoke test here ... */ + cJSON * json = NULL; + lees_edw_options_t opts = {0}; + + ifail = lees_edw_opts_to_json(&opts, &json); + assert(ifail == 0); + + assert(json); + cJSON_Delete(json); + } + + { + /* Steady case. */ + cJSON * json = NULL; + lees_edw_options_t opts = {.nplanes = 2, + .type = LE_SHEAR_TYPE_STEADY, + .nt0 = 10, + .uy = 0.001}; + + ifail = lees_edw_opts_to_json(&opts, &json); + assert(ifail == 0); + + assert(json); + cJSON_Delete(json); + } + + { + /* Oscillatory case */ + cJSON * json = NULL; + lees_edw_options_t opts = {.nplanes = 8, + .type = LE_SHEAR_TYPE_OSCILLATORY, + .period = 100, + .nt0 = 0, + .uy = 0.02}; + + ifail = lees_edw_opts_to_json(&opts, &json); + assert(ifail == 0); + + assert(json); + cJSON_Delete(json); + } + + return ifail; +} + +/***************************************************************************** + * + * test_lees_edw_opts_from_json + * + *****************************************************************************/ + +int test_lees_edw_opts_from_json(void) { + + int ifail = 0; + + { + /* No planes */ + cJSON * json = cJSON_Parse("{\"Number of planes\": 0}"); + lees_edw_options_t opts = {.nplanes = 1}; + ifail = lees_edw_opts_from_json(json, &opts); + assert(ifail == 0); + assert(opts.nplanes == 0); + + cJSON_Delete(json); + } + + { + /* Steady shear case */ + cJSON * json = cJSON_Parse("{" + "\"Number of planes\": 2," + "\"Shear type\": \"STEADY\"," + "\"Reference time\": 10," + "\"Plane speed\": 0.001" + "}"); + lees_edw_options_t opts = {0}; + + ifail = lees_edw_opts_from_json(json, &opts); + assert(ifail == 0); + assert(opts.nplanes == 2); + assert(opts.type == LE_SHEAR_TYPE_STEADY); + assert(opts.nt0 == 10); + assert(fabs(opts.uy - 0.001) < DBL_EPSILON); + + cJSON_Delete(json); + } + + { + /* Oscillatory shear case */ + cJSON * json = cJSON_Parse("{" + "\"Number of planes\": 8," + "\"Shear type\": \"OSCILLATORY\"," + "\"Period (timesteps)\": 100," + "\"Reference time\": 0," + "\"Plane speed\": 0.02" + "}"); + lees_edw_options_t opts = {0}; + + ifail = lees_edw_opts_from_json(json, &opts); + assert(ifail == 0); + assert(opts.nplanes == 8); + assert(opts.type == LE_SHEAR_TYPE_OSCILLATORY); + assert(opts.period == 100); + assert(opts.nt0 == 0); + assert(fabs(opts.uy - 0.02) < DBL_EPSILON); + + cJSON_Delete(json); + } + + return ifail; +} diff --git a/tests/unit/test_phi_ch.c b/tests/unit/test_phi_ch.c index 36af56b85..61a7ce162 100644 --- a/tests/unit/test_phi_ch.c +++ b/tests/unit/test_phi_ch.c @@ -68,7 +68,7 @@ int test_phi_ch_create(pe_t * pe) { cs_init(cs); { - lees_edw_info_t opts = {0}; + lees_edw_options_t opts = {0}; lees_edw_create(pe, cs, &opts, &le); } @@ -133,7 +133,7 @@ int test_phi_cahn_hilliard(pe_t * pe) { fe_null_create(pe, &fe); { - lees_edw_info_t opts = {0}; + lees_edw_options_t opts = {0}; lees_edw_create(pe, cs, &opts, &le); } From 6c041be685ab2ec9f8ecb6bf8cab7b1fadb344b9 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Dec 2022 15:01:03 +0000 Subject: [PATCH 190/244] Complete refactor to add kernels --- src/leslie_ericksen.c | 295 +++++++++++++++++++++++++++++++++++++++++- src/leslie_ericksen.h | 40 ++++-- 2 files changed, 325 insertions(+), 10 deletions(-) diff --git a/src/leslie_ericksen.c b/src/leslie_ericksen.c index 15c947edf..6b5f2e9a8 100644 --- a/src/leslie_ericksen.c +++ b/src/leslie_ericksen.c @@ -8,13 +8,14 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2010-2019 The University of Edinburgh + * (c) 2010-2022 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) * *****************************************************************************/ +#ifdef OLD_VERSION #include #include @@ -225,3 +226,295 @@ static int leslie_ericksen_add_swimming_velocity(field_t * fp, return 0; } +#else +#include +#include +#include + +#include "pe.h" +#include "coords.h" +#include "kernel.h" +#include "advection_s.h" +#include "leslie_ericksen.h" + +__global__ static void leslie_update_kernel(kernel_ctxt_t * ktx, + fe_polar_t * fe, + field_t * fp, + hydro_t * hydro, + advflux_t * flux, + leslie_param_t param); + +__global__ static void leslie_self_advection_kernel(kernel_ctxt_t * ktx, + field_t * p, + hydro_t * hydro, + double swim); + +/***************************************************************************** + * + * leslie_ericksen_create + * + *****************************************************************************/ + +int leslie_ericksen_create(pe_t * pe, cs_t * cs, fe_polar_t * fe, field_t * p, + const leslie_param_t * param, + leslie_ericksen_t ** pobj) { + + leslie_ericksen_t * obj = NULL; + + obj = (leslie_ericksen_t *) calloc(1, sizeof(leslie_ericksen_t)); + assert(obj); + + /* Initialise */ + + obj->pe = pe; + obj->cs = cs; + obj->fe = fe; + obj->p = p; + obj->param = *param; + + *pobj = obj; + + return 0; +} + +/***************************************************************************** + * + * leslie_ericksen_free + * + *****************************************************************************/ + +int leslie_ericksen_free(leslie_ericksen_t ** pobj) { + + assert(pobj && *pobj); + + free(*pobj); + *pobj = NULL; + + return 0; +} + +/***************************************************************************** + * + * leslie_ericksen_update + * + * The hydro is allowed to be NULL, in which case the dynamics is + * purely relaxational. + * + * Note there is a side effect on the velocity field here if the + * self-advection term is not zero. + * + *****************************************************************************/ + +__host__ int leslie_ericksen_update(leslie_ericksen_t * obj, hydro_t * hydro) { + + int nlocal[3] = {0}; + advflux_t * flux = NULL; + + assert(obj); + assert(hydro); + + cs_nlocal(obj->cs, nlocal); + + /* Device version should allocate flux obj only once on creation. */ + advflux_cs_create(obj->pe, obj->cs, 3, &flux); + + if (hydro) { + /* Add self-advection term if present; halo swap; compute + * advective fluxes */ + leslie_ericksen_self_advection(obj, hydro); + hydro_u_halo(hydro); + advflux_cs_compute(flux, hydro, obj->p); + } + + { + /* Update driver */ + dim3 nblk, ntpb; + kernel_info_t limits = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; + kernel_ctxt_t * ctxt = NULL; + + kernel_ctxt_create(obj->cs, 1, limits, &ctxt); + kernel_ctxt_launch_param(ctxt, &nblk, &ntpb); + + tdpLaunchKernel(leslie_update_kernel, nblk, ntpb, 0, 0, + ctxt->target, obj->fe, obj->p->target, hydro->target, + flux->target, obj->param); + tdpAssert(tdpPeekAtLastError()); + tdpAssert(tdpDeviceSynchronize()); + + kernel_ctxt_free(ctxt); + } + + advflux_free(flux); + + return 0; +} + +/***************************************************************************** + * + * leslie_update_kernel + * + * hydro is allowed to be NULL, in which case we have relaxational + * dynamics only. + * + *****************************************************************************/ + +__global__ static void leslie_update_kernel(kernel_ctxt_t * ktx, + fe_polar_t * fe, + field_t * fp, + hydro_t * hydro, + advflux_t * flux, + leslie_param_t param) { + int kindex = 0; + int kiterations = 0; + const double dt = 1.0; + + assert(ktx); + assert(fe); + assert(fp); + assert(flux); + + kiterations = kernel_iterations(ktx); + + for_simt_parallel(kindex, kiterations, 1) { + + int ic = kernel_coords_ic(ktx, kindex); + int jc = kernel_coords_jc(ktx, kindex); + int kc = kernel_coords_kc(ktx, kindex); + int index = kernel_coords_index(ktx, ic, jc, kc); + + double p[3] = {0}; + double h[3] = {0}; /* molecular field (vector) */ + double w[3][3] = {0}; /* Velocity gradient tensor */ + double d[3][3] = {0}; /* Symmetric velocity gradient tensor */ + double omega[3][3] = {0}; /* Antisymmetric ditto */ + + field_vector(fp, index, p); + fe_polar_mol_field(fe, index, h); + if (hydro) hydro_u_gradient_tensor(hydro, ic, jc, kc, w); + + /* Note that the convection for Leslie Ericksen is that + * w_ab = d_a u_b, which is the transpose of what the + * above returns. Hence an extra minus sign in the + * omega term in the following. */ + + for (int ia = 0; ia < 3; ia++) { + for (int ib = 0; ib < 3; ib++) { + d[ia][ib] = 0.5*(w[ia][ib] + w[ib][ia]); + omega[ia][ib] = -0.5*(w[ia][ib] - w[ib][ia]); + } + } + + /* updates involve the following fluxes */ + + int im1 = cs_index(flux->cs, ic-1, jc, kc); + int jm1 = cs_index(flux->cs, ic, jc-1, kc); + int km1 = cs_index(flux->cs, ic, jc, kc-1); + + for (int ia = 0; ia < 3; ia++) { + + double sum = 0.0; + for (int ib = 0; ib < 3; ib++) { + sum += param.lambda*d[ia][ib]*p[ib] - omega[ia][ib]*p[ib]; + } + + p[ia] += dt*(- flux->fx[addr_rank1(flux->nsite, 3, index, ia)] + + flux->fx[addr_rank1(flux->nsite, 3, im1, ia)] + - flux->fy[addr_rank1(flux->nsite, 3, index, ia)] + + flux->fy[addr_rank1(flux->nsite, 3, jm1, ia)] + - flux->fz[addr_rank1(flux->nsite, 3, index, ia)] + + flux->fz[addr_rank1(flux->nsite, 3, km1, ia)] + + sum + param.Gamma*h[ia]); + } + + field_vector_set(fp, index, p); + } + + return; +} + +/***************************************************************************** + * + * leslie_self_advection_kernel + * + *****************************************************************************/ + +__global__ static void leslie_self_advection_kernel(kernel_ctxt_t * ktx, + field_t * p, + hydro_t * hydro, + double swim) { + int kindex = 0; + int kiterations = 0; + + assert(ktx); + assert(p); + assert(hydro); + + kiterations = kernel_iterations(ktx); + + for_simt_parallel(kindex, kiterations, 1) { + + int ic = kernel_coords_ic(ktx, kindex); + int jc = kernel_coords_jc(ktx, kindex); + int kc = kernel_coords_kc(ktx, kindex); + int index = kernel_coords_index(ktx, ic, jc, kc); + double p3[3] = {0}; + double u3[3] = {0}; + + field_vector(p, index, p3); + hydro_u(hydro, index, u3); + + u3[X] += swim*p3[X]; + u3[Y] += swim*p3[Y]; + u3[Z] += swim*p3[Z]; + + hydro_u_set(hydro, index, u3); + } + + return; +} + +/***************************************************************************** + * + * leslie_self_advection + * + * Driver to add the self-advection velocity to the current hydro->u. + * This then appears in advective fluxes. + * + * 1. There is a somewhat open question on whether one really wants the + * self advection to appear anywhere else as a side-effect. + * 2. If we have a halo in p, then we can avoid a halo in u. + * + *****************************************************************************/ + +__host__ int leslie_ericksen_self_advection(leslie_ericksen_t * obj, + hydro_t * hydro) { + int nlocal[3] = {0}; + + assert(obj); + assert(hydro); + + cs_nlocal(obj->cs, nlocal); + + /* Don't bother if swim = 0 */ + + if (fabs(obj->param.swim) > 0.0) { + + dim3 nblk, ntpb; + kernel_info_t limits = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; + kernel_ctxt_t * ctxt = NULL; + + kernel_ctxt_create(obj->cs, 1, limits, &ctxt); + kernel_ctxt_launch_param(ctxt, &nblk, &ntpb); + + tdpLaunchKernel(leslie_self_advection_kernel, nblk, ntpb, 0, 0, + ctxt->target, obj->p->target, hydro->target, + obj->param.swim); + tdpAssert(tdpPeekAtLastError()); + tdpAssert(tdpDeviceSynchronize()); + + kernel_ctxt_free(ctxt); + } + + return 0; +} +#endif diff --git a/src/leslie_ericksen.h b/src/leslie_ericksen.h index 87fd13b8c..1db56718f 100644 --- a/src/leslie_ericksen.h +++ b/src/leslie_ericksen.h @@ -2,26 +2,48 @@ * * leslie_ericksen.h * - * $Id: leslie_ericksen.h,v 1.2 2010-10-15 12:40:03 kevin Exp $ * * Edinburgh Soft Matter and Statistical Physics Group * and Edinburgh Parallel Computing Centre * + * (c) 2010-2022 The University of Edinburgh + * * Kevin Stratford (kevin@epcc.ed.ac.uk) - * (c) 2010-2016 The University of Edinburgh * *****************************************************************************/ -#ifndef LESLIE_ERICKSEN_H -#define LESLIE_ERICKSEN_H +#ifndef LUDWIG_LESLIE_ERICKSEN_H +#define LUDWIG_LESLIE_ERICKSEN_H +#include "pe.h" #include "coords.h" -#include "polar_active.h" +#include "field.h" #include "hydro.h" +#include "polar_active.h" + +typedef struct leslie_param_s leslie_param_t; +typedef struct leslie_ericksen_s leslie_ericksen_t; + +struct leslie_param_s { + double Gamma; /* Rotational diffusion constant */ + double swim; /* Self-advection parameter */ + double lambda; /* Flow aligning/flow tumbling parameter */ +}; + +struct leslie_ericksen_s { + pe_t * pe; /* Parallel environment */ + cs_t * cs; /* Coordinate system */ + fe_polar_t * fe; /* Free energy */ + field_t * p; /* Vector order parameter field */ + leslie_param_t param; /* Parameters */ +}; + +int leslie_ericksen_create(pe_t * pe, cs_t * cs, fe_polar_t * fe, field_t * p, + const leslie_param_t * param, + leslie_ericksen_t ** obj); +int leslie_ericksen_free(leslie_ericksen_t ** obj); -int leslie_ericksen_update(cs_t * cs, fe_polar_t * fe, field_t * p, - hydro_t * hydro); -int leslie_ericksen_gamma_set(const double gamma); -int leslie_ericksen_swim_set(const double gamma); +int leslie_ericksen_update(leslie_ericksen_t * obj, hydro_t * hydro); +int leslie_ericksen_self_advection(leslie_ericksen_t * obj, hydro_t * hydro); #endif From 8a41878e0b997575b338308f0573c8d60aae73df Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Dec 2022 15:01:20 +0000 Subject: [PATCH 191/244] Update to Leslie Ericksen dynamics --- src/ludwig.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/ludwig.c b/src/ludwig.c index 76756da1e..ca7636a74 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -149,6 +149,7 @@ struct ludwig_s { fe_t * fe; /* Free energy "polymorphic" version */ ch_t * ch; /* Cahn Hilliard (surfactants) */ phi_ch_t * pch; /* Cahn Hilliard dynamics (binary fluid) */ + leslie_ericksen_t * leslie; /* Leslie Ericksen Dynamics */ beris_edw_t * be; /* Beris Edwards dynamics */ pth_t * pth; /* Thermodynamic stress/force calculation */ fe_lc_t * fe_lc; /* LC free energy */ @@ -807,8 +808,7 @@ void ludwig_run(const char * inputfile) { } if (ludwig->p) { - fe_polar_t * fe = (fe_polar_t *) ludwig->fe; - leslie_ericksen_update(ludwig->cs, fe, ludwig->p, ludwig->hydro); + leslie_ericksen_update(ludwig->leslie, ludwig->hydro); } if (ludwig->q) { @@ -1735,6 +1735,7 @@ int free_energy_init_rt(ludwig_t * ludwig) { /* Polar active. */ fe_polar_t * fe = NULL; + leslie_param_t lep = {0}; nf = NVECTOR;/* Vector order parameter */ nhalo = 2; /* Required for stress diveregnce. */ @@ -1759,15 +1760,15 @@ int free_energy_init_rt(ludwig_t * ludwig) { polar_active_run_time(pe, rt, fe); ludwig->fe = (fe_t *) fe; - rt_double_parameter(rt, "leslie_ericksen_gamma", &value); - leslie_ericksen_gamma_set(value); - pe_info(pe, "Rotational diffusion = %12.5e\n", value); + rt_double_parameter(rt, "leslie_ericksen_gamma", &lep.Gamma); + rt_double_parameter(rt, "leslie_ericksen_swim", &lep.swim); + rt_double_parameter(rt, "polar_active_lambda", &lep.lambda); - rt_double_parameter(rt, "leslie_ericksen_swim", &value); - leslie_ericksen_swim_set(value); - pe_info(pe, "Self-advection parameter = %12.5e\n", value); + pe_info(pe, "Rotational diffusion = %12.5e\n", lep.Gamma); + pe_info(pe, "Self-advection parameter = %12.5e\n", lep.swim); pth_create(pe, cs, FE_FORCE_METHOD_STRESS_DIVERGENCE, &ludwig->pth); + leslie_ericksen_create(pe, cs, fe, ludwig->p, &lep, &ludwig->leslie); } else if(strcmp(description, "lc_droplet") == 0) { From cefbeaf872d5b90310cf32fd51348b1e632556a9 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Dec 2022 15:13:48 +0000 Subject: [PATCH 192/244] Remove old code --- src/leslie_ericksen.c | 213 ------------------------------------------ 1 file changed, 213 deletions(-) diff --git a/src/leslie_ericksen.c b/src/leslie_ericksen.c index 6b5f2e9a8..824c1b897 100644 --- a/src/leslie_ericksen.c +++ b/src/leslie_ericksen.c @@ -15,218 +15,6 @@ * *****************************************************************************/ -#ifdef OLD_VERSION -#include -#include - -#include "pe.h" -#include "coords.h" -#include "advection_s.h" -#include "leslie_ericksen.h" - -static double Gamma_; /* Rotational diffusion constant */ -static double swim_ = 0.0; /* Self-advection parameter */ - -static int leslie_ericksen_update_fluid(fe_polar_t *fe, - field_t * p, hydro_t * hydro, - advflux_t * flux); -static int leslie_ericksen_add_swimming_velocity(field_t * p, - hydro_t * hydro); - -/***************************************************************************** - * - * leslie_ericken_gamma_set - * - *****************************************************************************/ - -int leslie_ericksen_gamma_set(const double gamma) { - - Gamma_ = gamma; - return 0; -} - -/***************************************************************************** - * - * leslie_ericksen_swim_set - * - *****************************************************************************/ - -int leslie_ericksen_swim_set(const double s) { - - swim_ = s; - return 0; -} - -/***************************************************************************** - * - * leslie_ericksen_update - * - * The hydro is allowed to be NULL, in which case the dynamics is - * purely relaxational. - * - * Note there is a side effect on the velocity field here if the - * self-advection term is not zero. - * - *****************************************************************************/ - -int leslie_ericksen_update(cs_t * cs, fe_polar_t * fe, field_t * p, - hydro_t * hydro) { - - int nf; - advflux_t * flux = NULL; - - assert(cs); - assert(p); - - field_nf(p, &nf); - assert(nf == NVECTOR); - advflux_cs_create(p->pe, cs, nf, &flux); - - if (hydro) { - if (swim_ != 0.0) leslie_ericksen_add_swimming_velocity(p, hydro); - hydro_u_halo(hydro); - advflux_cs_compute(flux, hydro, p); - } - - leslie_ericksen_update_fluid(fe, p, hydro, flux); - - advflux_free(flux); - - return 0; -} - -/***************************************************************************** - * - * leslie_ericksen_update_fluid - * - * hydro is allowed to be NULL, in which case there is relaxational - * dynmaics only. - * - *****************************************************************************/ - -static int leslie_ericksen_update_fluid(fe_polar_t * fe, - field_t * fp, - hydro_t * hydro, - advflux_t * flux) { - int ic, jc, kc, index; - int im1, jm1, km1; - int ia, ib; - int nlocal[3]; - - double p[3]; - double h[3]; - double d[3][3]; - double omega[3][3]; - double w[3][3]; - double sum; - fe_polar_param_t param; - const double dt = 1.0; - - assert(fe); - assert(fp); - assert(flux); - - fe_polar_param(fe, ¶m); - cs_nlocal(flux->cs, nlocal); - - for (ia = 0; ia < 3; ia++) { - for (ib = 0; ib < 3; ib++) { - w[ia][ib] = 0.0; - } - } - - for (ic = 1; ic <= nlocal[X]; ic++) { - for (jc = 1; jc <= nlocal[Y]; jc++) { - for (kc = 1; kc <= nlocal[Z]; kc++) { - - index = cs_index(flux->cs, ic, jc, kc); - field_vector(fp, index, p); - fe_polar_mol_field(fe, index, h); - if (hydro) hydro_u_gradient_tensor(hydro, ic, jc, kc, w); - - /* Note that the convection for Leslie Ericksen is that - * w_ab = d_a u_b, which is the transpose of what the - * above returns. Hence an extra minus sign in the - * omega term in the following. */ - - for (ia = 0; ia < 3; ia++) { - for (ib = 0; ib < 3; ib++) { - d[ia][ib] = 0.5*(w[ia][ib] + w[ib][ia]); - omega[ia][ib] = -0.5*(w[ia][ib] - w[ib][ia]); - } - } - - /* updates involve the following fluxes */ - - im1 = cs_index(flux->cs, ic-1, jc, kc); - jm1 = cs_index(flux->cs, ic, jc-1, kc); - km1 = cs_index(flux->cs, ic, jc, kc-1); - - for (ia = 0; ia < 3; ia++) { - - sum = 0.0; - for (ib = 0; ib < 3; ib++) { - sum += param.lambda*d[ia][ib]*p[ib] - omega[ia][ib]*p[ib]; - } - - p[ia] += dt*(- flux->fx[addr_rank1(flux->nsite, 3, index, ia)] - + flux->fx[addr_rank1(flux->nsite, 3, im1, ia)] - - flux->fy[addr_rank1(flux->nsite, 3, index, ia)] - + flux->fy[addr_rank1(flux->nsite, 3, jm1, ia)] - - flux->fz[addr_rank1(flux->nsite, 3, index, ia)] - + flux->fz[addr_rank1(flux->nsite, 3, km1, ia)] - + sum + Gamma_*h[ia]); - } - - field_vector_set(fp, index, p); - - /* Next lattice site */ - } - } - } - - return 0; -} - -/***************************************************************************** - * - * leslie_ericksen_add_swimming_velocity - * - *****************************************************************************/ - -static int leslie_ericksen_add_swimming_velocity(field_t * fp, - hydro_t * hydro) { - int ic, jc, kc, index; - int ia; - int nlocal[3]; - - double p[3]; - double u[3]; - - assert(fp); - assert(hydro); - - cs_nlocal(fp->cs, nlocal); - - for (ic = 1; ic <= nlocal[X]; ic++) { - for (jc = 1; jc <= nlocal[Y]; jc++) { - for (kc = 1; kc <= nlocal[Z]; kc++) { - - index = cs_index(fp->cs, ic, jc, kc); - field_vector(fp, index, p); - hydro_u(hydro, index, u); - - for (ia = 0; ia < 3; ia++) { - u[ia] += swim_*p[ia]; - } - hydro_u_set(hydro, index, u); - } - } - } - - return 0; -} -#else #include #include #include @@ -517,4 +305,3 @@ __host__ int leslie_ericksen_self_advection(leslie_ericksen_t * obj, return 0; } -#endif From abef3aa163ab3dd8741cb142f51569ea77bc3d31 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Dec 2022 16:21:34 +0000 Subject: [PATCH 193/244] Add device velocity gradient tensor routine --- src/leslie_ericksen.c | 64 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/src/leslie_ericksen.c b/src/leslie_ericksen.c index 824c1b897..125d900d4 100644 --- a/src/leslie_ericksen.c +++ b/src/leslie_ericksen.c @@ -37,6 +37,10 @@ __global__ static void leslie_self_advection_kernel(kernel_ctxt_t * ktx, hydro_t * hydro, double swim); +__device__ static void leslie_u_gradient_tensor(hydro_t * hydro, + int ic, int jc, int kc, + double w[3][3]); + /***************************************************************************** * * leslie_ericksen_create @@ -178,7 +182,7 @@ __global__ static void leslie_update_kernel(kernel_ctxt_t * ktx, field_vector(fp, index, p); fe_polar_mol_field(fe, index, h); - if (hydro) hydro_u_gradient_tensor(hydro, ic, jc, kc, w); + if (hydro) leslie_u_gradient_tensor(hydro, ic, jc, kc, w); /* Note that the convection for Leslie Ericksen is that * w_ab = d_a u_b, which is the transpose of what the @@ -305,3 +309,61 @@ __host__ int leslie_ericksen_self_advection(leslie_ericksen_t * obj, return 0; } + +/***************************************************************************** + * + * leslie_u_gradient_tensor + * + * A copy of the hydro_u_gradient_tensor() routine with no Lees-Edwards + * conditions. + * + * A __device__ version is required. + * + *****************************************************************************/ + +__device__ static void leslie_u_gradient_tensor(hydro_t * hydro, + int ic, int jc, int kc, + double w[3][3]) { + assert(hydro); + + int m1 = cs_index(hydro->cs, ic - 1, jc, kc); + int p1 = cs_index(hydro->cs, ic + 1, jc, kc); + + w[X][X] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, X)] - + hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, X)]); + w[Y][X] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, Y)] - + hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, Y)]); + w[Z][X] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, Z)] - + hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, Z)]); + + m1 = cs_index(hydro->cs, ic, jc - 1, kc); + p1 = cs_index(hydro->cs, ic, jc + 1, kc); + + w[X][Y] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, X)] - + hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, X)]); + w[Y][Y] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, Y)] - + hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, Y)]); + w[Z][Y] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, Z)] - + hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, Z)]); + + m1 = cs_index(hydro->cs, ic, jc, kc - 1); + p1 = cs_index(hydro->cs, ic, jc, kc + 1); + + w[X][Z] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, X)] - + hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, X)]); + w[Y][Z] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, Y)] - + hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, Y)]); + w[Z][Z] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, Z)] - + hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, Z)]); + + /* Enforce tracelessness */ + + { + double tr = (1.0/3.0)*(w[X][X] + w[Y][Y] + w[Z][Z]); + w[X][X] -= tr; + w[Y][Y] -= tr; + w[Z][Z] -= tr; + } + + return; +} From ead464028e1eccc9ece0b619c66f92b20bcd1163 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Dec 2022 17:25:03 +0000 Subject: [PATCH 194/244] Expose target implementation --- src/polar_active.c | 10 ---------- src/polar_active.h | 12 ++++++++++++ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/polar_active.c b/src/polar_active.c index f9614fd90..296911e39 100644 --- a/src/polar_active.c +++ b/src/polar_active.c @@ -51,16 +51,6 @@ #include "polar_active.h" #include "util.h" -struct fe_polar_s { - fe_t super; /* Superclass */ - pe_t * pe; /* Parallel environment */ - cs_t * cs; /* Coordinate system */ - fe_polar_param_t * param; /* Parameters */ - field_t * p; /* Vector order parameter */ - field_grad_t * dp; /* Gradients thereof */ - fe_polar_t * target; /* Device pointer */ -}; - static __constant__ fe_polar_param_t const_param; static fe_vt_t fe_polar_hvt = { diff --git a/src/polar_active.h b/src/polar_active.h index 89d36215e..8112db2d7 100644 --- a/src/polar_active.h +++ b/src/polar_active.h @@ -37,6 +37,18 @@ struct fe_polar_param_s { double radius; /* Used for spherical 'active region' */ }; +/* Structure */ + +struct fe_polar_s { + fe_t super; /* Superclass */ + pe_t * pe; /* Parallel environment */ + cs_t * cs; /* Coordinate system */ + fe_polar_param_t * param; /* Parameters */ + field_t * p; /* Vector order parameter */ + field_grad_t * dp; /* Gradients thereof */ + fe_polar_t * target; /* Device pointer */ +}; + __host__ int fe_polar_create(pe_t * pe, cs_t * cs, field_t * p, field_grad_t * dp, fe_polar_t ** fe); From 90562a196377f9ba0f5514c6ed50ad5096e71724 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Dec 2022 17:25:24 +0000 Subject: [PATCH 195/244] Working device version --- src/leslie_ericksen.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/leslie_ericksen.c b/src/leslie_ericksen.c index 125d900d4..9a76e7989 100644 --- a/src/leslie_ericksen.c +++ b/src/leslie_ericksen.c @@ -37,7 +37,8 @@ __global__ static void leslie_self_advection_kernel(kernel_ctxt_t * ktx, hydro_t * hydro, double swim); -__device__ static void leslie_u_gradient_tensor(hydro_t * hydro, +__device__ static void leslie_u_gradient_tensor(kernel_ctxt_t * ktx, + hydro_t * hydro, int ic, int jc, int kc, double w[3][3]); @@ -128,8 +129,8 @@ __host__ int leslie_ericksen_update(leslie_ericksen_t * obj, hydro_t * hydro) { kernel_ctxt_launch_param(ctxt, &nblk, &ntpb); tdpLaunchKernel(leslie_update_kernel, nblk, ntpb, 0, 0, - ctxt->target, obj->fe, obj->p->target, hydro->target, - flux->target, obj->param); + ctxt->target, obj->fe->target, obj->p->target, + hydro->target, flux->target, obj->param); tdpAssert(tdpPeekAtLastError()); tdpAssert(tdpDeviceSynchronize()); @@ -182,7 +183,7 @@ __global__ static void leslie_update_kernel(kernel_ctxt_t * ktx, field_vector(fp, index, p); fe_polar_mol_field(fe, index, h); - if (hydro) leslie_u_gradient_tensor(hydro, ic, jc, kc, w); + if (hydro) leslie_u_gradient_tensor(ktx, hydro, ic, jc, kc, w); /* Note that the convection for Leslie Ericksen is that * w_ab = d_a u_b, which is the transpose of what the @@ -321,13 +322,14 @@ __host__ int leslie_ericksen_self_advection(leslie_ericksen_t * obj, * *****************************************************************************/ -__device__ static void leslie_u_gradient_tensor(hydro_t * hydro, +__device__ static void leslie_u_gradient_tensor(kernel_ctxt_t * ktx, + hydro_t * hydro, int ic, int jc, int kc, double w[3][3]) { assert(hydro); - int m1 = cs_index(hydro->cs, ic - 1, jc, kc); - int p1 = cs_index(hydro->cs, ic + 1, jc, kc); + int m1 = kernel_coords_index(ktx, ic - 1, jc, kc); + int p1 = kernel_coords_index(ktx, ic + 1, jc, kc); w[X][X] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, X)] - hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, X)]); @@ -336,8 +338,8 @@ __device__ static void leslie_u_gradient_tensor(hydro_t * hydro, w[Z][X] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, Z)] - hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, Z)]); - m1 = cs_index(hydro->cs, ic, jc - 1, kc); - p1 = cs_index(hydro->cs, ic, jc + 1, kc); + m1 = kernel_coords_index(ktx, ic, jc - 1, kc); + p1 = kernel_coords_index(ktx, ic, jc + 1, kc); w[X][Y] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, X)] - hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, X)]); @@ -346,8 +348,8 @@ __device__ static void leslie_u_gradient_tensor(hydro_t * hydro, w[Z][Y] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, Z)] - hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, Z)]); - m1 = cs_index(hydro->cs, ic, jc, kc - 1); - p1 = cs_index(hydro->cs, ic, jc, kc + 1); + m1 = kernel_coords_index(ktx, ic, jc, kc - 1); + p1 = kernel_coords_index(ktx, ic, jc, kc + 1); w[X][Z] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, X)] - hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, X)]); From aba009e91f191bf58246e7ccb11f0cd8ce71d692 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Dec 2022 17:25:45 +0000 Subject: [PATCH 196/244] Correction for device polar active --- src/ludwig.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ludwig.c b/src/ludwig.c index ca7636a74..38f12f657 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -1023,7 +1023,9 @@ void ludwig_run(const char * inputfile) { } if (ludwig->p) { + /* Get the gradients as well for the free energy below */ field_memcpy(ludwig->p, tdpMemcpyDeviceToHost); + field_grad_memcpy(ludwig->p_grad, tdpMemcpyDeviceToHost); stats_field_info(ludwig->p, ludwig->map); } From 83e1e1f0978a9a4e23969bf76cfaf5769bb84a6e Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 27 Dec 2022 14:20:46 +0000 Subject: [PATCH 197/244] Kernel implemnetation for fluctuating case --- src/phi_cahn_hilliard.c | 369 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 361 insertions(+), 8 deletions(-) diff --git a/src/phi_cahn_hilliard.c b/src/phi_cahn_hilliard.c index dabe4a3ac..5cf4404ee 100644 --- a/src/phi_cahn_hilliard.c +++ b/src/phi_cahn_hilliard.c @@ -47,7 +47,6 @@ #include "phi_cahn_hilliard.h" static int phi_ch_flux_mu1(phi_ch_t * pch, fe_t * fes); -static int phi_ch_flux_mu2(phi_ch_t * pch, fe_t * fes); static int phi_ch_update_forward_step(phi_ch_t * pch, field_t * phif); static int phi_ch_flux_mu_ext(phi_ch_t * pch); @@ -55,8 +54,10 @@ static int phi_ch_update_conserve(phi_ch_t * pch, field_t * phif); static int phi_ch_le_fix_fluxes(phi_ch_t * pch, int nf); static int phi_ch_le_fix_fluxes_parallel(phi_ch_t * pch, int nf); +#ifdef NOT_USED +static int phi_ch_flux_mu2(phi_ch_t * pch, fe_t * fes); static int phi_ch_random_flux(phi_ch_t * pch, noise_t * noise); - +#endif static int phi_ch_subtract_sum_phi_after_forward_step(phi_ch_t * pch, field_t * phif, map_t * map); @@ -78,6 +79,7 @@ struct phi_correct_s { double phi; /* Sum current. */ }; + __global__ void phi_ch_flux_mu1_kernel(kernel_ctxt_t * ktx, lees_edw_t * le, fe_t * fe, advflux_t * flux, double mobility); @@ -91,6 +93,25 @@ __global__ void phi_ch_csum_kernel(kernel_ctxt_t * ktx, lees_edw_t *le, field_t * field, advflux_t * flux, field_t * csum, int ys, double wz); +__host__ int phi_ch_dif_flux_driver(phi_ch_t * pch, fe_t * fe, + double mobility); +__global__ static void phi_ch_dif_flux_kernel(kernel_ctxt_t * ktx, + advflux_t * flux, + fe_t * fe, + double mobility); + +__host__ int phi_ch_var_flux_driver(field_t * var, noise_t * noise, + double mobility, double kt); +__global__ static void phi_ch_var_flux_kernel(kernel_ctxt_t * ktx, + field_t * var, + noise_t * noise, + double mktvar); + +__host__ int phi_ch_var_flux_acc_driver(phi_ch_t * pch, const field_t * var); +__global__ static void phi_ch_var_flux_acc_kernel(kernel_ctxt_t * ktx, + const field_t * var, + advflux_t * flux); + /***************************************************************************** * * phi_ch_create @@ -210,12 +231,32 @@ int phi_cahn_hilliard(phi_ch_t * pch, fe_t * fe, field_t * phi, advflux_zero(pch->flux); } - if (noise_phi) { - phi_ch_flux_mu2(pch, fe); - phi_ch_random_flux(pch, noise); + if (noise_phi == 0) { + phi_ch_flux_mu1(pch, fe); } else { - phi_ch_flux_mu1(pch, fe); + /* Bring the mobility and temperature in here, as physics_t is slated + * for removal; the information is part of the Cahn Hilliard parameters. */ + /* We also have a temporary field for random fluxes computed per site + * which should really be made permanent if performance is a + * consideration. */ + physics_t * phys = NULL; + double mobility = 0.0; + double kt = 0.0; + field_options_t opts = field_options_ndata_nhalo(3, 3); + field_t * var = NULL; + + physics_ref(&phys); + physics_mobility(phys, &mobility); + physics_kt(phys, &kt); + + field_create(pch->pe, pch->cs, pch->le, "pch-var", &opts, &var); + + phi_ch_dif_flux_driver(pch, fe, mobility); + phi_ch_var_flux_driver(var, noise, mobility, kt); + phi_ch_var_flux_acc_driver(pch, var); + + field_free(var); } /* External chemical potential gradient (could switch out if zero) */ @@ -367,7 +408,7 @@ __global__ void phi_ch_flux_mu1_kernel(kernel_ctxt_t * ktx, return; } - +#ifdef NOT_USED /***************************************************************************** * * phi_ch_flux_mu2 @@ -557,7 +598,7 @@ static int phi_ch_random_flux(phi_ch_t * pch, noise_t * noise) { return 0; } - +#endif /***************************************************************************** * * phi_ch_le_fix_fluxes @@ -1392,3 +1433,315 @@ __global__ void phi_ch_flux_mu_ext_kernel(kernel_ctxt_t * ktx, return; } + +/***************************************************************************** + * + * phi_ch_dif_flux_driver + * + * Driver for fourth order diffusive fluxes. + * + *****************************************************************************/ + +__host__ int phi_ch_dif_flux_driver(phi_ch_t * pch, fe_t * fe, + double mobility) { + int nlocal[3] = {0}; + fe_t * fetarget = NULL; + + assert(pch); + assert(fe); + + cs_nlocal(pch->cs, nlocal); + fe->func->target(fe, &fetarget); + + { + dim3 nblk, ntpb; + kernel_info_t lim = {1, nlocal[X], 0, nlocal[Y], 0, nlocal[Z]}; + kernel_ctxt_t * ctxt = NULL; + + kernel_ctxt_create(pch->cs, 1, lim, &ctxt); + kernel_ctxt_launch_param(ctxt, &nblk, &ntpb); + + tdpLaunchKernel(phi_ch_dif_flux_kernel, nblk, ntpb, 0, 0, + ctxt->target, pch->flux->target, fetarget, mobility); + tdpAssert(tdpPeekAtLastError()); + tdpAssert(tdpDeviceSynchronize()); + + kernel_ctxt_free(ctxt); + } + + return 0; +} + +/***************************************************************************** + * + * phi_ch_dif_flux_kernel + * + * Accumulate [add to previously computed advective fluxes] + * diffusive fluxes related to the mobility. + * + * This version is based on Sumesh et al to allow correct + * treatment of noise. The fluxes are calculated via + * + * grad_x mu = 0.5*(mu(i+1) - mu(i-1)) etc + * flux_x(x + 1/2) = -0.5*M*(grad_x mu(i) + grad_x mu(i+1)) etc + * + * In contrast to Sumesh et al., we don't have 'diagonal' fluxes. + * There are no Lees Edwards planes implemented, but we maintain + * the distiction between east and west in the fluxes. + * + *****************************************************************************/ + +__global__ static void phi_ch_dif_flux_kernel(kernel_ctxt_t * ktx, + advflux_t * flux, + fe_t * fe, + double mobility) { + int kindex = 0; + int kiterations = 0; + + assert(ktx); + assert(flux); + assert(fe); + assert(fe->func->mu); + + kiterations = kernel_iterations(ktx); + + for_simt_parallel(kindex, kiterations, 1) { + + int ic = kernel_coords_ic(ktx, kindex); + int jc = kernel_coords_jc(ktx, kindex); + int kc = kernel_coords_kc(ktx, kindex); + + int indexm2 = kernel_coords_index(ktx, ic-2, jc, kc); + int indexm1 = kernel_coords_index(ktx, ic-1, jc, kc); + int index00 = kernel_coords_index(ktx, ic, jc, kc); + int indexp1 = kernel_coords_index(ktx, ic+1, jc, kc); + int indexp2 = kernel_coords_index(ktx, ic+2, jc, kc); + + double mum2 = 0.0; + double mum1 = 0.0; + double mu00 = 0.0; + double mup1 = 0.0; + double mup2 = 0.0; + + fe->func->mu(fe, indexm2, &mum2); + fe->func->mu(fe, indexm1, &mum1); + fe->func->mu(fe, index00, &mu00); + fe->func->mu(fe, indexp1, &mup1); + fe->func->mu(fe, indexp2, &mup2); + + /* x-direction (between ic-1 and ic) */ + + flux->fw[addr_rank0(flux->nsite, index00)] + -= 0.25*mobility*(mup1 + mu00 - mum1 - mum2); + + /* ...and between ic and ic+1 */ + + flux->fe[addr_rank0(flux->nsite, index00)] + -= 0.25*mobility*(mup2 + mup1 - mu00 - mum1); + + /* y direction between jc and jc+1 */ + + indexm1 = kernel_coords_index(ktx, ic, jc-1, kc); + indexp1 = kernel_coords_index(ktx, ic, jc+1, kc); + indexp2 = kernel_coords_index(ktx, ic, jc+2, kc); + + fe->func->mu(fe, indexm1, &mum1); + fe->func->mu(fe, indexp1, &mup1); + fe->func->mu(fe, indexp2, &mup2); + + flux->fy[addr_rank0(flux->nsite, index00)] + -= 0.25*mobility*(mup2 + mup1 - mu00 - mum1); + + /* z direction between kc and kc+1 */ + + indexm1 = kernel_coords_index(ktx, ic, jc, kc-1); + indexp1 = kernel_coords_index(ktx, ic, jc, kc+1); + indexp2 = kernel_coords_index(ktx, ic, jc, kc+2); + + fe->func->mu(fe, indexm1, &mum1); + fe->func->mu(fe, indexp1, &mup1); + fe->func->mu(fe, indexp2, &mup2); + + flux->fz[addr_rank0(flux->nsite, index00)] + -= 0.25*mobility*(mup2 + mup1 - mu00 - mum1); + } + + return; +} + +/***************************************************************************** + * + * phi_ch_var_flux_driver + * + * Compute fluctuating contribution to fluxes per site. The results must + * be translated to face-fluxes before appearing in the final update. + * + *****************************************************************************/ + +__host__ int phi_ch_var_flux_driver(field_t * var, + noise_t * noise, + double mobility, + double kt) { + int nlocal[3] = {0}; + + cs_nlocal(var->cs, nlocal); + + { + /* Fluctuation dissipation says ... */ + double mktvar = sqrt(2.0*mobility*kt); + /* Limits have nextra = 1 site at each end */ + kernel_info_t lim = {0, nlocal[X]+1, 0, nlocal[Y]+1, 0, nlocal[Z]+1}; + kernel_ctxt_t * ctxt = NULL; + dim3 nblk, ntpb; + + kernel_ctxt_create(var->cs, 1, lim, &ctxt); + kernel_ctxt_launch_param(ctxt, &nblk, &ntpb); + + tdpLaunchKernel(phi_ch_var_flux_kernel, nblk, ntpb, 0, 0, + ctxt->target, var->target, noise->target, mktvar); + + tdpAssert(tdpPeekAtLastError()); + tdpAssert(tdpDeviceSynchronize()); + + kernel_ctxt_free(ctxt); + } + + return 0; +} + +/***************************************************************************** + * + * phi_ch_var_flux_kernel + * + * Compute per site. + * Variance of the noise is from fluctuation dissipation relation. + * + *****************************************************************************/ + +__global__ static void phi_ch_var_flux_kernel(kernel_ctxt_t * ktx, + field_t * var, + noise_t * noise, + double mktvar) { + int kindex = 0; + int kiterations = 0; + + kiterations = kernel_iterations(ktx); + + for_simt_parallel(kindex, kiterations, 1) { + + int ic = kernel_coords_ic(ktx, kindex); + int jc = kernel_coords_jc(ktx, kindex); + int kc = kernel_coords_kc(ktx, kindex); + + int index0 = kernel_coords_index(ktx, ic, jc, kc); + + double reap[3] = {0}; + noise_reap_n(noise, index0, 3, reap); + + var->data[addr_rank1(var->nsites, 3, index0, X)] = mktvar*reap[X]; + var->data[addr_rank1(var->nsites, 3, index0, Y)] = mktvar*reap[Y]; + var->data[addr_rank1(var->nsites, 3, index0, Z)] = mktvar*reap[Z]; + } + + return; +} + +/***************************************************************************** + * + * phi_ch_var_flux_acc_driver + * + * Accumulate the random fluxes computed at sites to the relevant face + * faces. This is just a simple average of the two site either side of + * a given cell face. + * + *****************************************************************************/ + +__host__ int phi_ch_var_flux_acc_driver(phi_ch_t * pch, const field_t * var) { + + int nlocal[3] = {0}; + + cs_nlocal(pch->cs, nlocal); + + { + kernel_info_t lim = {1, nlocal[X], 0, nlocal[Y], 0, nlocal[Z]}; + kernel_ctxt_t * ctxt = NULL; + dim3 nblk, ntpb; + + kernel_ctxt_create(pch->cs, 1, lim, &ctxt); + kernel_ctxt_launch_param(ctxt, &nblk, &ntpb); + + tdpLaunchKernel(phi_ch_var_flux_acc_kernel, nblk, ntpb, 0, 0, + ctxt->target, var->target, pch->flux->target); + + tdpAssert(tdpPeekAtLastError()); + tdpAssert(tdpDeviceSynchronize()); + + kernel_ctxt_free(ctxt); + } + + return 0; +} + +/***************************************************************************** + * + * phi_ch_var_flux_acc_kernel + * + * Accumulate fluctuation-dissipation part of face fluxes. + * + *****************************************************************************/ + +__global__ static void phi_ch_var_flux_acc_kernel(kernel_ctxt_t * ktx, + const field_t * var, + advflux_t * flux) { + int kindex = 0; + int kiterations = 0; + + kiterations = kernel_iterations(ktx); + + for_simt_parallel(kindex, kiterations, 1) { + + int ic = kernel_coords_ic(ktx, kindex); + int jc = kernel_coords_jc(ktx, kindex); + int kc = kernel_coords_kc(ktx, kindex); + + int index0 = kernel_coords_index(ktx, ic, jc, kc); + + /* x-direction (west face) */ + { + int index1 = kernel_coords_index(ktx, ic-1, jc, kc); + int vaddr0 = addr_rank1(var->nsites, 3, index0, X); + int vaddr1 = addr_rank1(var->nsites, 3, index1, X); + flux->fw[addr_rank0(flux->nsite, index0)] + += 0.5*(var->data[vaddr0] + var->data[vaddr1]); + } + + /* x-direction (east face) */ + { + int index1 = kernel_coords_index(ktx, ic+1, jc, kc); + int vaddr0 = addr_rank1(var->nsites, 3, index0, X); + int vaddr1 = addr_rank1(var->nsites, 3, index1, X); + flux->fe[addr_rank0(flux->nsite, index0)] + += 0.5*(var->data[vaddr0] + var->data[vaddr1]); + } + + /* y direction */ + { + int index1 = kernel_coords_index(ktx, ic, jc+1, kc); + int vaddr0 = addr_rank1(var->nsites, 3, index0, Y); + int vaddr1 = addr_rank1(var->nsites, 3, index1, Y); + flux->fy[addr_rank0(flux->nsite, index0)] + += 0.5*(var->data[vaddr0] + var->data[vaddr1]); + } + + /* z direction */ + { + int index1 = kernel_coords_index(ktx, ic, jc, kc+1); + int vaddr0 = addr_rank1(var->nsites, 3, index0, Z); + int vaddr1 = addr_rank1(var->nsites, 3, index1, Z); + flux->fz[addr_rank0(flux->nsite, index0)] + += 0.5*(var->data[vaddr0] + var->data[vaddr1]); + } + } + + return; +} From f0cef6f177845bf410eb99cec5317a1c46bdb1cb Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 30 Dec 2022 09:40:54 +0000 Subject: [PATCH 198/244] New statistics kernel for Q_ab --- src/field_stats_q.c | 244 ++++++++++++++++++++++++++++++++++++++++++++ src/field_stats_q.h | 23 +++++ 2 files changed, 267 insertions(+) create mode 100644 src/field_stats_q.c create mode 100644 src/field_stats_q.h diff --git a/src/field_stats_q.c b/src/field_stats_q.c new file mode 100644 index 000000000..c79b26026 --- /dev/null +++ b/src/field_stats_q.c @@ -0,0 +1,244 @@ +/***************************************************************************** + * + * field_stats_q.c + * + * Order parameter statistics for Q_ab tansor order parameters. + * + * The single kernel here could be split a number of ways: one + * kernel per scalar order parameter, or one kernel per global + * quantity; or some combination thereof. + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Contributing authors: + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include +#include +#include +#include + +#include "field_stats_q.h" +#include "kernel.h" +#include "util_sum.h" + +typedef struct sum_s sum_t; +struct sum_s { + klein_t qsum[NQAB]; /* Avoid summation order differences */ + double qvar[NQAB]; + double qmin[NQAB]; + double qmax[NQAB]; + double vol; +}; + +int field_stats_q_reduce(field_t * q, map_t * map, sum_t * sum, int, MPI_Comm); +__global__ void field_stats_q_kernel(kernel_ctxt_t * ktx, field_t * q, + map_t * map, sum_t * sum); + +static double double_min(double x, double y) {return (x < y) ? x : y;} +static double double_max(double x, double y) {return (x > y) ? x : y;} + +/***************************************************************************** + * + * field_stats_q + * + *****************************************************************************/ + +int field_stats_q(field_t * obj, map_t * map) { + + MPI_Comm comm; + sum_t sum = {0}; + /* + const char * qxx[5] = {"Qxx", "Qxy", "Qxz", "Qyy", "Qyz"}; + */ + const char * qxx[5] = {"phi", "phi", "phi", "phi", "phi"}; + assert(obj); + assert(map); + + pe_mpi_comm(obj->pe, &comm); + field_stats_q_reduce(obj, map, &sum, 0, comm); + + for (int n = 0; n < NQAB; n++) { + + double qsum = klein_sum(sum.qsum + n); + double rvol = 1.0/sum.vol; + double qbar = rvol*qsum; /* mean */ + double qvar = rvol*sum.qvar[n] - qbar*qbar; /* variance */ + + pe_info(obj->pe, "[%3s] %14.7e %14.7e %14.7e %14.7e %14.7e\n", + qxx[n], qsum, qbar, qvar, sum.qmin[n], sum.qmax[n]); + } + + return 0; +} + +/***************************************************************************** + * + * field_stats_q_reduce + * + * This is a global reduction to rank in communicator comm. + * + * We expect and assert NQAB to be the largest number of field elements + * to avoid memory allocation and deallocation here. + * + *****************************************************************************/ + +int field_stats_q_reduce(field_t * obj, map_t * map, sum_t * sum, + int rank, MPI_Comm comm) { + + int nlocal[3] = {0}; + sum_t sum_local = {0}; + + assert(obj); + assert(map); + + /* Local sum */ + for (int n = 0; n < NQAB; n++) { + sum_local.qsum[n] = klein_zero(); + sum_local.qvar[n] = 0.0; + sum_local.qmin[n] = +DBL_MAX; + sum_local.qmax[n] = -DBL_MAX; + } + sum_local.vol = 0.0; + + cs_nlocal(obj->cs, nlocal); + + { + kernel_info_t lim = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; + kernel_ctxt_t * ctxt = NULL; + dim3 nblk, ntpb; + + kernel_ctxt_create(obj->cs, 1, lim, &ctxt); + kernel_ctxt_launch_param(ctxt, &nblk, &ntpb); + + /* FIXME: sum_local device memory */ + tdpLaunchKernel(field_stats_q_kernel, nblk, ntpb, 0, 0, + ctxt->target, obj->target, map->target, &sum_local); + + tdpAssert(tdpPeekAtLastError()); + tdpAssert(tdpDeviceSynchronize()); + + kernel_ctxt_free(ctxt); + } + + MPI_Reduce(sum_local.qmin, sum->qmin, NQAB, MPI_DOUBLE, MPI_MIN, rank, comm); + MPI_Reduce(sum_local.qmax, sum->qmax, NQAB, MPI_DOUBLE, MPI_MAX, rank, comm); + MPI_Reduce(sum_local.qvar, sum->qvar, NQAB, MPI_DOUBLE, MPI_SUM, rank, comm); + MPI_Reduce(&sum_local.vol, &sum->vol, 1, MPI_DOUBLE, MPI_SUM, rank, comm); + + { + MPI_Datatype dt = MPI_DATATYPE_NULL; + MPI_Op op = MPI_OP_NULL; + + klein_mpi_datatype(&dt); + klein_mpi_op_sum(&op); + + MPI_Reduce(sum_local.qsum, sum->qsum, NQAB, dt, op, rank, comm); + + MPI_Op_free(&op); + MPI_Type_free(&dt); + } + + return 0; +} + +/***************************************************************************** + * + * field_stats_q_kernel + * + *****************************************************************************/ + +void field_stats_q_kernel(kernel_ctxt_t * ktx, field_t * obj, map_t * map, + sum_t * sum) { + int kindex = 0; + int kiterations = 0; + + assert(obj); + assert(map); + + __shared__ sum_t lsum[TARGET_MAX_THREADS_PER_BLOCK]; + int tid = threadIdx.x; + + /* Local sum */ + + for (int n = 0; n < NQAB; n++) { + lsum[tid].qsum[n] = klein_zero(); + lsum[tid].qvar[n] = 0.0; + lsum[tid].qmin[n] = +DBL_MAX; + lsum[tid].qmax[n] = -DBL_MAX; + } + lsum[tid].vol = 0.0; + + kiterations = kernel_iterations(ktx); + + for_simt_parallel(kindex, kiterations, 1) { + + int ic = kernel_coords_ic(ktx, kindex); + int jc = kernel_coords_jc(ktx, kindex); + int kc = kernel_coords_kc(ktx, kindex); + int index = kernel_coords_index(ktx, ic, jc, kc); + int status = MAP_BOUNDARY; + + map_status(map, index, &status); + + if (status == MAP_FLUID) { + double q0[NQAB] = {0}; + + lsum[tid].vol += 1.0; + field_scalar_array(obj, index, q0); + + for (int n = 0; n < NQAB; n++) { + lsum[tid].qmin[n] = double_min(lsum[tid].qmin[n], q0[n]); + lsum[tid].qmax[n] = double_max(lsum[tid].qmax[n], q0[n]); + lsum[tid].qvar[n] += q0[n]*q0[n]; + klein_add_double(&lsum[tid].qsum[n], q0[n]); + } + } + } + + __syncthreads(); + + if (tid == 0) { + + /* Accumulate each total for this block */ + + sum_t bsum = {0}; + + for (int n = 0; n < NQAB; n++) { + bsum.qsum[n] = klein_zero(); + bsum.qvar[n] = 0.0; + bsum.qmin[n] = +DBL_MAX; + bsum.qmax[n] = -DBL_MAX; + bsum.vol = 0.0; + } + + for (int it = 0; it < blockDim.x; it++) { + for (int n = 0; n < NQAB; n++) { + klein_add(&bsum.qsum[n], lsum[it].qsum[n]); + bsum.qvar[n] += lsum[it].qvar[n]; + bsum.qmin[n] = double_min(bsum.qmin[n], lsum[it].qmin[n]); + bsum.qmax[n] = double_max(bsum.qmax[n], lsum[it].qmax[n]); + } + bsum.vol += lsum[it].vol; + } + + /* Accumulate to final result */ + for (int n = 0; n < NQAB; n++) { + /* FIXME: start unsafe update */ + klein_add(&sum->qsum[n], bsum.qsum[n]); + /* end */ + tdpAtomicAddDouble(&sum->qvar[n], bsum.qvar[n]); + tdpAtomicMinDouble(&sum->qmin[n], bsum.qmin[n]); + tdpAtomicMaxDouble(&sum->qmax[n], bsum.qmax[n]); + } + tdpAtomicAddDouble(&sum->vol, bsum.vol); + } + + return; +} diff --git a/src/field_stats_q.h b/src/field_stats_q.h new file mode 100644 index 000000000..400a46899 --- /dev/null +++ b/src/field_stats_q.h @@ -0,0 +1,23 @@ +/***************************************************************************** + * + * field_stats_q.h + * + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2022 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#ifndef LUDWIG_FIELD_STATS_Q_H +#define LUDWIG_FIELD_STATS_Q_H + +#include "field.h" +#include "map.h" + +int field_stats_q(field_t * q, map_t * map); + +#endif From 986518f0f325660e289b97b929d1d0a1797b75b0 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 30 Dec 2022 17:01:17 +0000 Subject: [PATCH 199/244] Recast to single scalar compoenent in kernel --- src/field_stats_q.c | 153 ++++++++++++++++++++++---------------------- 1 file changed, 78 insertions(+), 75 deletions(-) diff --git a/src/field_stats_q.c b/src/field_stats_q.c index c79b26026..4a13b8eac 100644 --- a/src/field_stats_q.c +++ b/src/field_stats_q.c @@ -30,16 +30,28 @@ typedef struct sum_s sum_t; struct sum_s { - klein_t qsum[NQAB]; /* Avoid summation order differences */ - double qvar[NQAB]; - double qmin[NQAB]; - double qmax[NQAB]; - double vol; + klein_t qsum; /* Sensitive to round-off */ + double qvar; /* Computed in single-sweep form */ + double qmin; /* minimum */ + double qmax; /* maximum */ + double vol; /* Volume is the same in all cases */ }; -int field_stats_q_reduce(field_t * q, map_t * map, sum_t * sum, int, MPI_Comm); +__host__ __device__ static inline sum_t sum_zero() { + sum_t sum = { + .qsum = klein_zero(), + .qvar = 0.0, + .qmin = +DBL_MAX, + .qmax = -DBL_MAX, + .vol = 0.0 + }; + return sum; +} + +int field_stats_q_reduce(field_t * q, map_t * map, int nxx, sum_t * sum, + int rank, MPI_Comm comm); __global__ void field_stats_q_kernel(kernel_ctxt_t * ktx, field_t * q, - map_t * map, sum_t * sum); + map_t * map, int nxx, sum_t * sum); static double double_min(double x, double y) {return (x < y) ? x : y;} static double double_max(double x, double y) {return (x > y) ? x : y;} @@ -53,26 +65,30 @@ static double double_max(double x, double y) {return (x > y) ? x : y;} int field_stats_q(field_t * obj, map_t * map) { MPI_Comm comm; - sum_t sum = {0}; /* const char * qxx[5] = {"Qxx", "Qxy", "Qxz", "Qyy", "Qyz"}; */ const char * qxx[5] = {"phi", "phi", "phi", "phi", "phi"}; + assert(obj); assert(map); pe_mpi_comm(obj->pe, &comm); - field_stats_q_reduce(obj, map, &sum, 0, comm); for (int n = 0; n < NQAB; n++) { - double qsum = klein_sum(sum.qsum + n); - double rvol = 1.0/sum.vol; - double qbar = rvol*qsum; /* mean */ - double qvar = rvol*sum.qvar[n] - qbar*qbar; /* variance */ + sum_t sum = {0}; + field_stats_q_reduce(obj, map, n, &sum, 0, comm); - pe_info(obj->pe, "[%3s] %14.7e %14.7e %14.7e %14.7e %14.7e\n", - qxx[n], qsum, qbar, qvar, sum.qmin[n], sum.qmax[n]); + { + double qsum = klein_sum(&sum.qsum); + double rvol = 1.0/sum.vol; + double qbar = rvol*qsum; /* mean */ + double qvar = rvol*sum.qvar - qbar*qbar; /* variance */ + + pe_info(obj->pe, "[%3s] %14.7e %14.7e %14.7e %14.7e %14.7e\n", + qxx[n], qsum, qbar, qvar, sum.qmin, sum.qmax); + } } return 0; @@ -89,26 +105,22 @@ int field_stats_q(field_t * obj, map_t * map) { * *****************************************************************************/ -int field_stats_q_reduce(field_t * obj, map_t * map, sum_t * sum, +int field_stats_q_reduce(field_t * obj, map_t * map, int nxx, sum_t * sum, int rank, MPI_Comm comm) { int nlocal[3] = {0}; - sum_t sum_local = {0}; + sum_t sum_local = sum_zero(); /* host copy */ + sum_t * dsum = NULL; /* device copy */ assert(obj); assert(map); - /* Local sum */ - for (int n = 0; n < NQAB; n++) { - sum_local.qsum[n] = klein_zero(); - sum_local.qvar[n] = 0.0; - sum_local.qmin[n] = +DBL_MAX; - sum_local.qmax[n] = -DBL_MAX; - } - sum_local.vol = 0.0; - cs_nlocal(obj->cs, nlocal); + /* Copy initial values; then the kernel... */ + tdpAssert(tdpMalloc((void **) &dsum, sizeof(sum_t))); + tdpAssert(tdpMemcpy(dsum, &sum_local, sizeof(sum_t), tdpMemcpyHostToDevice)); + { kernel_info_t lim = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; kernel_ctxt_t * ctxt = NULL; @@ -117,9 +129,8 @@ int field_stats_q_reduce(field_t * obj, map_t * map, sum_t * sum, kernel_ctxt_create(obj->cs, 1, lim, &ctxt); kernel_ctxt_launch_param(ctxt, &nblk, &ntpb); - /* FIXME: sum_local device memory */ tdpLaunchKernel(field_stats_q_kernel, nblk, ntpb, 0, 0, - ctxt->target, obj->target, map->target, &sum_local); + ctxt->target, obj->target, map->target, nxx, dsum); tdpAssert(tdpPeekAtLastError()); tdpAssert(tdpDeviceSynchronize()); @@ -127,10 +138,12 @@ int field_stats_q_reduce(field_t * obj, map_t * map, sum_t * sum, kernel_ctxt_free(ctxt); } - MPI_Reduce(sum_local.qmin, sum->qmin, NQAB, MPI_DOUBLE, MPI_MIN, rank, comm); - MPI_Reduce(sum_local.qmax, sum->qmax, NQAB, MPI_DOUBLE, MPI_MAX, rank, comm); - MPI_Reduce(sum_local.qvar, sum->qvar, NQAB, MPI_DOUBLE, MPI_SUM, rank, comm); - MPI_Reduce(&sum_local.vol, &sum->vol, 1, MPI_DOUBLE, MPI_SUM, rank, comm); + tdpAssert(tdpMemcpy(&sum_local, dsum, sizeof(sum_t), tdpMemcpyDeviceToHost)); + + MPI_Reduce(&sum_local.qmin, &sum->qmin, 1, MPI_DOUBLE, MPI_MIN, rank, comm); + MPI_Reduce(&sum_local.qmax, &sum->qmax, 1, MPI_DOUBLE, MPI_MAX, rank, comm); + MPI_Reduce(&sum_local.qvar, &sum->qvar, 1, MPI_DOUBLE, MPI_SUM, rank, comm); + MPI_Reduce(&sum_local.vol, &sum->vol, 1, MPI_DOUBLE, MPI_SUM, rank, comm); { MPI_Datatype dt = MPI_DATATYPE_NULL; @@ -139,12 +152,14 @@ int field_stats_q_reduce(field_t * obj, map_t * map, sum_t * sum, klein_mpi_datatype(&dt); klein_mpi_op_sum(&op); - MPI_Reduce(sum_local.qsum, sum->qsum, NQAB, dt, op, rank, comm); + MPI_Reduce(&sum_local.qsum, &sum->qsum, 1, dt, op, rank, comm); MPI_Op_free(&op); MPI_Type_free(&dt); } + tdpFree(dsum); + return 0; } @@ -152,10 +167,12 @@ int field_stats_q_reduce(field_t * obj, map_t * map, sum_t * sum, * * field_stats_q_kernel * + * Kernel for one order parameter entry "nxx", + * *****************************************************************************/ void field_stats_q_kernel(kernel_ctxt_t * ktx, field_t * obj, map_t * map, - sum_t * sum) { + int nxx, sum_t * sum) { int kindex = 0; int kiterations = 0; @@ -167,14 +184,7 @@ void field_stats_q_kernel(kernel_ctxt_t * ktx, field_t * obj, map_t * map, /* Local sum */ - for (int n = 0; n < NQAB; n++) { - lsum[tid].qsum[n] = klein_zero(); - lsum[tid].qvar[n] = 0.0; - lsum[tid].qmin[n] = +DBL_MAX; - lsum[tid].qmax[n] = -DBL_MAX; - } - lsum[tid].vol = 0.0; - + lsum[tid] = sum_zero(); kiterations = kernel_iterations(ktx); for_simt_parallel(kindex, kiterations, 1) { @@ -190,15 +200,13 @@ void field_stats_q_kernel(kernel_ctxt_t * ktx, field_t * obj, map_t * map, if (status == MAP_FLUID) { double q0[NQAB] = {0}; - lsum[tid].vol += 1.0; field_scalar_array(obj, index, q0); - for (int n = 0; n < NQAB; n++) { - lsum[tid].qmin[n] = double_min(lsum[tid].qmin[n], q0[n]); - lsum[tid].qmax[n] = double_max(lsum[tid].qmax[n], q0[n]); - lsum[tid].qvar[n] += q0[n]*q0[n]; - klein_add_double(&lsum[tid].qsum[n], q0[n]); - } + klein_add_double(&lsum[tid].qsum, q0[nxx]); + lsum[tid].qvar += q0[nxx]*q0[nxx]; + lsum[tid].qmin = double_min(lsum[tid].qmin, q0[nxx]); + lsum[tid].qmax = double_max(lsum[tid].qmax, q0[nxx]); + lsum[tid].vol += 1.0; } } @@ -208,36 +216,31 @@ void field_stats_q_kernel(kernel_ctxt_t * ktx, field_t * obj, map_t * map, /* Accumulate each total for this block */ - sum_t bsum = {0}; - - for (int n = 0; n < NQAB; n++) { - bsum.qsum[n] = klein_zero(); - bsum.qvar[n] = 0.0; - bsum.qmin[n] = +DBL_MAX; - bsum.qmax[n] = -DBL_MAX; - bsum.vol = 0.0; - } + sum_t bsum = sum_zero(); for (int it = 0; it < blockDim.x; it++) { - for (int n = 0; n < NQAB; n++) { - klein_add(&bsum.qsum[n], lsum[it].qsum[n]); - bsum.qvar[n] += lsum[it].qvar[n]; - bsum.qmin[n] = double_min(bsum.qmin[n], lsum[it].qmin[n]); - bsum.qmax[n] = double_max(bsum.qmax[n], lsum[it].qmax[n]); - } - bsum.vol += lsum[it].vol; + klein_add(&bsum.qsum, lsum[it].qsum); + bsum.qvar += lsum[it].qvar; + bsum.qmin = double_min(bsum.qmin, lsum[it].qmin); + bsum.qmax = double_max(bsum.qmax, lsum[it].qmax); + bsum.vol += lsum[it].vol; } - /* Accumulate to final result */ - for (int n = 0; n < NQAB; n++) { - /* FIXME: start unsafe update */ - klein_add(&sum->qsum[n], bsum.qsum[n]); - /* end */ - tdpAtomicAddDouble(&sum->qvar[n], bsum.qvar[n]); - tdpAtomicMinDouble(&sum->qmin[n], bsum.qmin[n]); - tdpAtomicMaxDouble(&sum->qmax[n], bsum.qmax[n]); - } - tdpAtomicAddDouble(&sum->vol, bsum.vol); + /* Accumulate to final result with protected update */ + + while (atomicCAS(&sum->qsum.lock, 0, 1) != 0) + ; + __threadfence(); + + klein_add(&sum->qsum, bsum.qsum); + + __threadfence(); + atomicExch(&sum->qsum.lock, 0); + + tdpAtomicAddDouble(&sum->qvar, bsum.qvar); + tdpAtomicMinDouble(&sum->qmin, bsum.qmin); + tdpAtomicMaxDouble(&sum->qmax, bsum.qmax); + tdpAtomicAddDouble(&sum->vol, bsum.vol); } return; From bb47a4b946018993cab092b065e2ab100993b85a Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 30 Dec 2022 17:03:58 +0000 Subject: [PATCH 200/244] Sort out spaces in order parameter stats --- .../d3q19-short/serial-anch-cn1.log | 20 ++++++++-------- .../d3q19-short/serial-anch-cn2.log | 20 ++++++++-------- .../d3q19-short/serial-anch-wn1.log | 10 ++++---- .../d3q19-short/serial-anch-wn2.log | 10 ++++---- .../d3q19-short/serial-anch-wn3.log | 10 ++++---- .../d3q19-short/serial-chol-fld.log | 20 ++++++++-------- .../d3q19-short/serial-chol-n02.log | 20 ++++++++-------- .../d3q19-short/serial-chol-n03.log | 20 ++++++++-------- .../d3q19-short/serial-chol-st3.log | 10 ++++---- .../d3q19-short/serial-chol-st4.log | 10 ++++---- .../d3q19-short/serial-chol-st5.log | 10 ++++---- .../d3q19-short/serial-chol-st6.log | 10 ++++---- .../d3q19-short/serial-chol-w01.log | 10 ++++---- .../d3q19-short/serial-chol-w02.log | 10 ++++---- .../d3q19-short/serial-chol-w03.log | 10 ++++---- .../d3q19-short/serial-drop-lc2.log | 24 +++++++++---------- 16 files changed, 112 insertions(+), 112 deletions(-) diff --git a/tests/regression/d3q19-short/serial-anch-cn1.log b/tests/regression/d3q19-short/serial-anch-cn1.log index ac9903498..c6994921a 100644 --- a/tests/regression/d3q19-short/serial-anch-cn1.log +++ b/tests/regression/d3q19-short/serial-anch-cn1.log @@ -127,11 +127,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 260569.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 8.6856333e+04 3.3333333e-01-2.9758140e-13 3.3333333e-01 3.3333333e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] -4.3428167e+04 -1.6666667e-01-7.4395351e-14 -1.6666667e-01-1.6666667e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] 8.6856333e+04 3.3333333e-01 -2.9758140e-13 3.3333333e-01 3.3333333e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] -4.3428167e+04 -1.6666667e-01 -7.4395351e-14 -1.6666667e-01 -1.6666667e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -148,11 +148,11 @@ Colloid velocities - x y z Scalars - total mean variance min max [rho] 260569.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 8.6853199e+04 3.3332130e-01 9.6005342e-08 3.1768067e-01 3.3333343e-01 -[phi] -1.1144240e-17 -4.2768865e-23 1.5584868e-08 -7.4425078e-03 7.4425078e-03 -[phi] -1.7800662e-18 -6.8314581e-24 1.5584868e-08 -7.4425078e-03 7.4425078e-03 -[phi] -4.3426599e+04 -1.6666065e-01 3.5559934e-08 -1.6717510e-01-1.5587390e-01 -[phi] 5.7324278e-19 2.1999654e-24 1.8288922e-08 -8.0877770e-03 8.0877770e-03 +[phi] 8.6853199e+04 3.3332130e-01 9.6005342e-08 3.1768067e-01 3.3333343e-01 +[phi] -1.1144240e-17 -4.2768865e-23 1.5584868e-08 -7.4425078e-03 7.4425078e-03 +[phi] -1.7800662e-18 -6.8314581e-24 1.5584868e-08 -7.4425078e-03 7.4425078e-03 +[phi] -4.3426599e+04 -1.6666065e-01 3.5559934e-08 -1.6717510e-01 -1.5587390e-01 +[phi] 5.7324278e-19 2.1999654e-24 1.8288922e-08 -8.0877770e-03 8.0877770e-03 Free energies - timestep f v f/v f_s a f_s/a [fe] 2 -1.4680007512e+01 2.6056900000e+05 -5.6338273211e-05 1.2978165671e+00 1.0140000000e+03 1.2798979952e-03 diff --git a/tests/regression/d3q19-short/serial-anch-cn2.log b/tests/regression/d3q19-short/serial-anch-cn2.log index aea5c9fe6..9ec0fba0e 100644 --- a/tests/regression/d3q19-short/serial-anch-cn2.log +++ b/tests/regression/d3q19-short/serial-anch-cn2.log @@ -128,11 +128,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 260569.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 8.6856333e+04 3.3333333e-01-2.9758140e-13 3.3333333e-01 3.3333333e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] -4.3428167e+04 -1.6666667e-01-7.4395351e-14 -1.6666667e-01-1.6666667e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] 8.6856333e+04 3.3333333e-01 -2.9758140e-13 3.3333333e-01 3.3333333e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] -4.3428167e+04 -1.6666667e-01 -7.4395351e-14 -1.6666667e-01 -1.6666667e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -149,11 +149,11 @@ Colloid velocities - x y z Scalars - total mean variance min max [rho] 260569.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 8.6854570e+04 3.3332657e-01 3.9599019e-08 3.2273571e-01 3.3333345e-01 -[phi] -5.9732106e-18 -2.2923719e-23 6.0655261e-09 -5.0352107e-03 5.0352107e-03 -[phi] -3.1520214e-19 -1.2096686e-24 6.0655261e-09 -5.0352107e-03 5.0352107e-03 -[phi] -4.3427285e+04 -1.6666328e-01 1.0805859e-08 -1.6666674e-01-1.5995154e-01 -[phi] 1.1520177e-18 4.4211619e-24 8.2287936e-10 -2.0266288e-03 2.0266288e-03 +[phi] 8.6854570e+04 3.3332657e-01 3.9599019e-08 3.2273571e-01 3.3333345e-01 +[phi] -5.9732106e-18 -2.2923719e-23 6.0655261e-09 -5.0352107e-03 5.0352107e-03 +[phi] -3.1520214e-19 -1.2096686e-24 6.0655261e-09 -5.0352107e-03 5.0352107e-03 +[phi] -4.3427285e+04 -1.6666328e-01 1.0805859e-08 -1.6666674e-01 -1.5995154e-01 +[phi] 1.1520177e-18 4.4211619e-24 8.2287936e-10 -2.0266288e-03 2.0266288e-03 Free energies - timestep f v f/v f_s a f_s/a [fe] 2 -1.7142944558e+01 2.6056900000e+05 -6.5790422339e-05 1.4735048183e+00 1.0140000000e+03 1.4531605703e-03 diff --git a/tests/regression/d3q19-short/serial-anch-wn1.log b/tests/regression/d3q19-short/serial-anch-wn1.log index f631ecb1a..164a58191 100644 --- a/tests/regression/d3q19-short/serial-anch-wn1.log +++ b/tests/regression/d3q19-short/serial-anch-wn1.log @@ -110,11 +110,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 256.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 2.2385250e+01 8.7442384e-02 3.0434697e-02 -1.6666121e-01 3.3270644e-01 -[phi] -1.9573868e+00 -7.6460423e-03 3.1989956e-02 -2.4998334e-01 2.4991631e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00-0.0000000e+00 -[phi] 2.0281416e+01 7.9224283e-02 3.0434697e-02 -1.6603977e-01 3.3332787e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] 2.2385250e+01 8.7442384e-02 3.0434697e-02 -1.6666121e-01 3.3270644e-01 +[phi] -1.9573868e+00 -7.6460423e-03 3.1989956e-02 -2.4998334e-01 2.4991631e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 +[phi] 2.0281416e+01 7.9224283e-02 3.0434697e-02 -1.6603977e-01 3.3332787e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-anch-wn2.log b/tests/regression/d3q19-short/serial-anch-wn2.log index c91ce3702..c325156d5 100644 --- a/tests/regression/d3q19-short/serial-anch-wn2.log +++ b/tests/regression/d3q19-short/serial-anch-wn2.log @@ -111,11 +111,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 256.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 2.2385250e+01 8.7442384e-02 3.0434697e-02 -1.6666121e-01 3.3270644e-01 -[phi] -1.9573868e+00 -7.6460423e-03 3.1989956e-02 -2.4998334e-01 2.4991631e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00-0.0000000e+00 -[phi] 2.0281416e+01 7.9224283e-02 3.0434697e-02 -1.6603977e-01 3.3332787e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] 2.2385250e+01 8.7442384e-02 3.0434697e-02 -1.6666121e-01 3.3270644e-01 +[phi] -1.9573868e+00 -7.6460423e-03 3.1989956e-02 -2.4998334e-01 2.4991631e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 +[phi] 2.0281416e+01 7.9224283e-02 3.0434697e-02 -1.6603977e-01 3.3332787e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-anch-wn3.log b/tests/regression/d3q19-short/serial-anch-wn3.log index 1fabedefd..9a20ca05d 100644 --- a/tests/regression/d3q19-short/serial-anch-wn3.log +++ b/tests/regression/d3q19-short/serial-anch-wn3.log @@ -111,11 +111,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 256.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 2.2385250e+01 8.7442384e-02 3.0434697e-02 -1.6666121e-01 3.3270644e-01 -[phi] -1.9573868e+00 -7.6460423e-03 3.1989956e-02 -2.4998334e-01 2.4991631e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00-0.0000000e+00 -[phi] 2.0281416e+01 7.9224283e-02 3.0434697e-02 -1.6603977e-01 3.3332787e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] 2.2385250e+01 8.7442384e-02 3.0434697e-02 -1.6666121e-01 3.3270644e-01 +[phi] -1.9573868e+00 -7.6460423e-03 3.1989956e-02 -2.4998334e-01 2.4991631e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 +[phi] 2.0281416e+01 7.9224283e-02 3.0434697e-02 -1.6603977e-01 3.3332787e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-fld.log b/tests/regression/d3q19-short/serial-chol-fld.log index 46ffcf1a1..d35f25981 100644 --- a/tests/regression/d3q19-short/serial-chol-fld.log +++ b/tests/regression/d3q19-short/serial-chol-fld.log @@ -104,11 +104,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 4096.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 2.0480000e+02 5.0000000e-02-2.7495367e-16 5.0000000e-02 5.0000000e-02 -[phi] 6.1440000e+02 1.5000000e-01 2.0781987e-15 1.5000000e-01 1.5000000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 2.0480000e+02 5.0000000e-02-2.7495367e-16 5.0000000e-02 5.0000000e-02 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] 2.0480000e+02 5.0000000e-02 -2.7495367e-16 5.0000000e-02 5.0000000e-02 +[phi] 6.1440000e+02 1.5000000e-01 2.0781987e-15 1.5000000e-01 1.5000000e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] 2.0480000e+02 5.0000000e-02 -2.7495367e-16 5.0000000e-02 5.0000000e-02 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -118,11 +118,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 4096.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 2.1364907e+02 5.2160417e-02-4.9136042e-16 5.2160417e-02 5.2160417e-02 -[phi] 6.3827142e+02 1.5582798e-01 1.5092094e-15 1.5582798e-01 1.5582798e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 2.1227628e+02 5.1825263e-02 5.0090140e-16 5.1825263e-02 5.1825263e-02 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] 2.1364907e+02 5.2160417e-02 -4.9136042e-16 5.2160417e-02 5.2160417e-02 +[phi] 6.3827142e+02 1.5582798e-01 1.5092094e-15 1.5582798e-01 1.5582798e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] 2.1227628e+02 5.1825263e-02 5.0090140e-16 5.1825263e-02 5.1825263e-02 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Free energies - timestep f v f/v f_bulk/v f_grad/v redshift [fe] 10 -1.6165785011e+00 4.0960000000e+03 -3.9467248563e-04 -3.8894438981e-04 0.0000000000e+00 1.0000000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-n02.log b/tests/regression/d3q19-short/serial-chol-n02.log index 7cc01315b..6882bba95 100644 --- a/tests/regression/d3q19-short/serial-chol-n02.log +++ b/tests/regression/d3q19-short/serial-chol-n02.log @@ -141,11 +141,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 258994.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] -4.3165667e+04 -1.6666667e-01-7.7306217e-14 -1.6666667e-01-1.6666667e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 2.1271805e+04 8.2132426e-02 3.1281495e-02 -1.6666667e-01 3.3333333e-01 -[phi] 1.8665093e-02 7.2067665e-08 3.1217063e-02 -2.5000000e-01 2.5000000e-01 +[phi] -4.3165667e+04 -1.6666667e-01 -7.7306217e-14 -1.6666667e-01 -1.6666667e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] 2.1271805e+04 8.2132426e-02 3.1281495e-02 -1.6666667e-01 3.3333333e-01 +[phi] 1.8665093e-02 7.2067665e-08 3.1217063e-02 -2.5000000e-01 2.5000000e-01 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -162,11 +162,11 @@ Colloid velocities - x y z Scalars - total mean variance min max [rho] 258994.00 1.00000000000 2.6341434e-08 0.99544798423 1.00149280815 -[phi] -4.3152703e+04 -1.6661661e-01 5.8269849e-08 -1.6740144e-01-1.5670750e-01 -[phi] -2.3050805e-15 -8.9001309e-21 2.6690464e-08 -7.0823033e-03 7.0823033e-03 -[phi] 4.8169716e-15 1.8598777e-20 3.3769091e-08 -7.5924269e-03 7.5924269e-03 -[phi] 2.1265219e+04 8.2106995e-02 3.1278470e-02 -1.6690066e-01 3.3397530e-01 -[phi] 1.8653835e-02 7.2024197e-08 3.1212676e-02 -2.5040482e-01 2.5040481e-01 +[phi] -4.3152703e+04 -1.6661661e-01 5.8269849e-08 -1.6740144e-01 -1.5670750e-01 +[phi] -2.3050805e-15 -8.9001309e-21 2.6690464e-08 -7.0823033e-03 7.0823033e-03 +[phi] 4.8169716e-15 1.8598777e-20 3.3769091e-08 -7.5924269e-03 7.5924269e-03 +[phi] 2.1265219e+04 8.2106995e-02 3.1278470e-02 -1.6690066e-01 3.3397530e-01 +[phi] 1.8653835e-02 7.2024197e-08 3.1212676e-02 -2.5040482e-01 2.5040481e-01 Free energies - timestep f v f/v f_s a f_s/a [fe] 5 -1.6870562224e+01 2.5899400000e+05 -6.5138814893e-05 8.4425272198e-01 2.0280000000e+03 4.1629818638e-04 diff --git a/tests/regression/d3q19-short/serial-chol-n03.log b/tests/regression/d3q19-short/serial-chol-n03.log index 917d9462f..5305f42d2 100644 --- a/tests/regression/d3q19-short/serial-chol-n03.log +++ b/tests/regression/d3q19-short/serial-chol-n03.log @@ -141,11 +141,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 258994.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 2.1271805e+04 8.2132426e-02 3.1281495e-02 -1.6666667e-01 3.3333333e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] -1.8665093e-02 -7.2067667e-08 3.1217063e-02 -2.5000000e-01 2.5000000e-01 -[phi] -4.3165667e+04 -1.6666667e-01-7.7306217e-14 -1.6666667e-01-1.6666667e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00-0.0000000e+00 +[phi] 2.1271805e+04 8.2132426e-02 3.1281495e-02 -1.6666667e-01 3.3333333e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] -1.8665093e-02 -7.2067667e-08 3.1217063e-02 -2.5000000e-01 2.5000000e-01 +[phi] -4.3165667e+04 -1.6666667e-01 -7.7306217e-14 -1.6666667e-01 -1.6666667e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -162,11 +162,11 @@ Colloid velocities - x y z Scalars - total mean variance min max [rho] 258994.00 1.00000000000 2.6340951e-08 0.99544798423 1.00149280815 -[phi] 2.1265219e+04 8.2106995e-02 3.1278470e-02 -1.6690066e-01 3.3397530e-01 -[phi] -7.4178591e-15 -2.8641046e-20 2.6690464e-08 -7.0823033e-03 7.0823033e-03 -[phi] -1.8653836e-02 -7.2024203e-08 3.1212676e-02 -2.5040481e-01 2.5040482e-01 -[phi] -4.3152703e+04 -1.6661661e-01 5.8269888e-08 -1.6740144e-01-1.5670750e-01 -[phi] 9.3192335e-16 3.5982430e-21 3.3769091e-08 -7.5924269e-03 7.5924269e-03 +[phi] 2.1265219e+04 8.2106995e-02 3.1278470e-02 -1.6690066e-01 3.3397530e-01 +[phi] -7.4178591e-15 -2.8641046e-20 2.6690464e-08 -7.0823033e-03 7.0823033e-03 +[phi] -1.8653836e-02 -7.2024203e-08 3.1212676e-02 -2.5040481e-01 2.5040482e-01 +[phi] -4.3152703e+04 -1.6661661e-01 5.8269888e-08 -1.6740144e-01 -1.5670750e-01 +[phi] 9.3192335e-16 3.5982430e-21 3.3769091e-08 -7.5924269e-03 7.5924269e-03 Free energies - timestep f v f/v f_s a f_s/a [fe] 5 -1.6870562224e+01 2.5899400000e+05 -6.5138814893e-05 8.4425272198e-01 2.0280000000e+03 4.1629818638e-04 diff --git a/tests/regression/d3q19-short/serial-chol-st3.log b/tests/regression/d3q19-short/serial-chol-st3.log index 03cd6a2d0..2e90ee44a 100644 --- a/tests/regression/d3q19-short/serial-chol-st3.log +++ b/tests/regression/d3q19-short/serial-chol-st3.log @@ -110,11 +110,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 -[phi] 2.4576000e+03 7.4999999e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 -[phi] -3.1310888e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00-0.0000000e+00 -[phi] 2.4576000e+03 7.5000001e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] 2.4576000e+03 7.4999999e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 +[phi] -3.1310888e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 +[phi] 2.4576000e+03 7.5000001e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] -4.2632564e-14 -2.6093711e-14 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-st4.log b/tests/regression/d3q19-short/serial-chol-st4.log index f27c6bbca..af64aa13f 100644 --- a/tests/regression/d3q19-short/serial-chol-st4.log +++ b/tests/regression/d3q19-short/serial-chol-st4.log @@ -110,11 +110,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 -[phi] 2.4576000e+03 7.4999999e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 -[phi] -3.1310888e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00-0.0000000e+00 -[phi] 2.4576000e+03 7.5000001e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] 2.4576000e+03 7.4999999e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 +[phi] -3.1310888e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 +[phi] 2.4576000e+03 7.5000001e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] -4.2632564e-14 -2.6093711e-14 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-st5.log b/tests/regression/d3q19-short/serial-chol-st5.log index 64eb64ce3..58d7760b3 100644 --- a/tests/regression/d3q19-short/serial-chol-st5.log +++ b/tests/regression/d3q19-short/serial-chol-st5.log @@ -110,11 +110,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 -[phi] 2.4576000e+03 7.4999999e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 -[phi] -3.1310888e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00-0.0000000e+00 -[phi] 2.4576000e+03 7.5000001e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] 2.4576000e+03 7.4999999e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 +[phi] -3.1310888e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 +[phi] 2.4576000e+03 7.5000001e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] -4.2632564e-14 -2.6093711e-14 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-st6.log b/tests/regression/d3q19-short/serial-chol-st6.log index 712cb2842..e72bcb6e2 100644 --- a/tests/regression/d3q19-short/serial-chol-st6.log +++ b/tests/regression/d3q19-short/serial-chol-st6.log @@ -110,11 +110,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 -[phi] 2.4576000e+03 7.4999999e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 -[phi] -3.1310888e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00-0.0000000e+00 -[phi] 2.4576000e+03 7.5000001e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] 2.4576000e+03 7.4999999e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 +[phi] -3.1310888e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 +[phi] 2.4576000e+03 7.5000001e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] -4.2632564e-14 -2.6093711e-14 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-w01.log b/tests/regression/d3q19-short/serial-chol-w01.log index ccf161520..1d78f23ba 100644 --- a/tests/regression/d3q19-short/serial-chol-w01.log +++ b/tests/regression/d3q19-short/serial-chol-w01.log @@ -117,11 +117,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 262144.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 9.7012768e-12 3.7007434e-17-3.5259967e-45 3.7007434e-17 3.7007434e-17 -[phi] 4.3690667e+04 1.6666667e-01-7.1515710e-14 1.6666667e-01 1.6666667e-01 -[phi] 4.3690667e+04 1.6666667e-01-7.1515710e-14 1.6666667e-01 1.6666667e-01 -[phi] 9.7012768e-12 3.7007434e-17-3.5259967e-45 3.7007434e-17 3.7007434e-17 -[phi] 4.3690667e+04 1.6666667e-01-7.1515710e-14 1.6666667e-01 1.6666667e-01 +[phi] 9.7012768e-12 3.7007434e-17 -3.5259967e-45 3.7007434e-17 3.7007434e-17 +[phi] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 +[phi] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 +[phi] 9.7012768e-12 3.7007434e-17 -3.5259967e-45 3.7007434e-17 3.7007434e-17 +[phi] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-w02.log b/tests/regression/d3q19-short/serial-chol-w02.log index aca0e2bf0..e48184930 100644 --- a/tests/regression/d3q19-short/serial-chol-w02.log +++ b/tests/regression/d3q19-short/serial-chol-w02.log @@ -117,11 +117,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 262144.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 9.7012768e-12 3.7007434e-17-3.5259967e-45 3.7007434e-17 3.7007434e-17 -[phi] 4.3690667e+04 1.6666667e-01-7.1515710e-14 1.6666667e-01 1.6666667e-01 -[phi] 4.3690667e+04 1.6666667e-01-7.1515710e-14 1.6666667e-01 1.6666667e-01 -[phi] 9.7012768e-12 3.7007434e-17-3.5259967e-45 3.7007434e-17 3.7007434e-17 -[phi] 4.3690667e+04 1.6666667e-01-7.1515710e-14 1.6666667e-01 1.6666667e-01 +[phi] 9.7012768e-12 3.7007434e-17 -3.5259967e-45 3.7007434e-17 3.7007434e-17 +[phi] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 +[phi] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 +[phi] 9.7012768e-12 3.7007434e-17 -3.5259967e-45 3.7007434e-17 3.7007434e-17 +[phi] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-w03.log b/tests/regression/d3q19-short/serial-chol-w03.log index 1aefa74cf..c11a159c5 100644 --- a/tests/regression/d3q19-short/serial-chol-w03.log +++ b/tests/regression/d3q19-short/serial-chol-w03.log @@ -117,11 +117,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 262144.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 9.7012768e-12 3.7007434e-17-3.5259967e-45 3.7007434e-17 3.7007434e-17 -[phi] 4.3690667e+04 1.6666667e-01-7.1515710e-14 1.6666667e-01 1.6666667e-01 -[phi] 4.3690667e+04 1.6666667e-01-7.1515710e-14 1.6666667e-01 1.6666667e-01 -[phi] 9.7012768e-12 3.7007434e-17-3.5259967e-45 3.7007434e-17 3.7007434e-17 -[phi] 4.3690667e+04 1.6666667e-01-7.1515710e-14 1.6666667e-01 1.6666667e-01 +[phi] 9.7012768e-12 3.7007434e-17 -3.5259967e-45 3.7007434e-17 3.7007434e-17 +[phi] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 +[phi] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 +[phi] 9.7012768e-12 3.7007434e-17 -3.5259967e-45 3.7007434e-17 3.7007434e-17 +[phi] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-drop-lc2.log b/tests/regression/d3q19-short/serial-drop-lc2.log index 4fe70c7b3..cc0d17614 100644 --- a/tests/regression/d3q19-short/serial-drop-lc2.log +++ b/tests/regression/d3q19-short/serial-drop-lc2.log @@ -117,12 +117,12 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 2.5921966e+04 7.9107564e-01 2.8826342e-01 -9.9999854e-01 1.0000000e+00 -[phi] 6.5536000e+03 2.0000000e-01 2.7332303e-14 2.0000000e-01 2.0000000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] -3.2768000e+03 -1.0000000e-01 6.8330758e-15 -1.0000000e-01-1.0000000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] 2.5921966e+04 7.9107564e-01 2.8826342e-01 -9.9999854e-01 1.0000000e+00 +[phi] 6.5536000e+03 2.0000000e-01 2.7332303e-14 2.0000000e-01 2.0000000e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[phi] -3.2768000e+03 -1.0000000e-01 6.8330758e-15 -1.0000000e-01 -1.0000000e-01 +[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 @@ -132,12 +132,12 @@ Starting time step loop. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 7.4358254e-05 0.98825685222 1.04535239220 -[phi] 2.5921966e+04 7.9107564e-01 2.8784076e-01 -1.0321446e+00 1.0001387e+00 -[phi] 6.8165821e+03 2.0802558e-01 1.6595190e-05 1.8489779e-01 2.3156183e-01 -[phi] -1.7723974e-15 -5.4089276e-20 1.0388496e-05 -2.8577286e-02 2.8577286e-02 -[phi] -1.9914589e-16 -6.0774504e-21 1.0388496e-05 -2.8577286e-02 2.8577286e-02 -[phi] -3.4082910e+03 -1.0401279e-01 1.0237638e-05 -1.1602526e-01-7.4916195e-02 -[phi] 2.7113360e-16 8.2743409e-21 6.8406602e-06 -2.1881175e-02 2.1881175e-02 +[phi] 2.5921966e+04 7.9107564e-01 2.8784076e-01 -1.0321446e+00 1.0001387e+00 +[phi] 6.8165821e+03 2.0802558e-01 1.6595190e-05 1.8489779e-01 2.3156183e-01 +[phi] -1.7723974e-15 -5.4089276e-20 1.0388496e-05 -2.8577286e-02 2.8577286e-02 +[phi] -1.9914589e-16 -6.0774504e-21 1.0388496e-05 -2.8577286e-02 2.8577286e-02 +[phi] -3.4082910e+03 -1.0401279e-01 1.0237638e-05 -1.1602526e-01 -7.4916195e-02 +[phi] 2.7113360e-16 8.2743409e-21 6.8406602e-06 -2.1881175e-02 2.1881175e-02 Free energy density - timestep total fluid [fed] 10 -1.4369701257e-02 -1.4369701257e-02 From 5a7838b7fba96bab5a823c63ec089c9145be1351 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 3 Jan 2023 11:09:47 +0000 Subject: [PATCH 201/244] Cosmetic: replace label phi with Px Py Pz --- tests/regression/d3q19-short/serial-pola-r01.log | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/regression/d3q19-short/serial-pola-r01.log b/tests/regression/d3q19-short/serial-pola-r01.log index 662e53bdb..3847d4d7a 100644 --- a/tests/regression/d3q19-short/serial-pola-r01.log +++ b/tests/regression/d3q19-short/serial-pola-r01.log @@ -84,9 +84,9 @@ Initial conditions. Scalars - total mean variance min max [rho] 262144.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 3.2494065e+03 1.2395502e-02 3.3317841e-01 -1.0000000e+00 1.0000000e+00 -[phi] 3.2504065e+03 1.2399317e-02 3.3318213e-01 -1.0000000e+00 1.0000000e+00 -[phi] 3.2494065e+03 1.2395502e-02 3.3317841e-01 -1.0000000e+00 1.0000000e+00 +[Px ] 3.2494065e+03 1.2395502e-02 3.3317841e-01 -1.0000000e+00 1.0000000e+00 +[Py ] 3.2504065e+03 1.2399317e-02 3.3318213e-01 -1.0000000e+00 1.0000000e+00 +[Pz ] 3.2494065e+03 1.2395502e-02 3.3317841e-01 -1.0000000e+00 1.0000000e+00 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -96,9 +96,9 @@ Starting time step loop. Scalars - total mean variance min max [rho] 262144.00 1.00000000000 2.5114189e-05 0.97118083085 1.00851344195 -[phi] 3.2552075e+03 1.2417631e-02 3.3142024e-01 -1.0055681e+00 1.0055715e+00 -[phi] 3.2562674e+03 1.2421674e-02 3.3141885e-01 -1.0055681e+00 1.0055715e+00 -[phi] 3.2551708e+03 1.2417491e-02 3.3141233e-01 -1.0055681e+00 1.0055715e+00 +[Px ] 3.2552075e+03 1.2417631e-02 3.3142024e-01 -1.0055681e+00 1.0055715e+00 +[Py ] 3.2562674e+03 1.2421674e-02 3.3141885e-01 -1.0055681e+00 1.0055715e+00 +[Pz ] 3.2551708e+03 1.2417491e-02 3.3141233e-01 -1.0055681e+00 1.0055715e+00 Free energy density - timestep total fluid [fed] 10 -2.4695620854e-02 -2.4695620854e-02 From deb2336cd5a42aec8df7fb98604a5fd5016cb3db Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 3 Jan 2023 11:10:53 +0000 Subject: [PATCH 202/244] Use compenstated sum in order parameter statistics --- CHANGES.md | 4 + src/field_stats_q.c | 247 ---------------- src/field_stats_q.h | 23 -- src/phi_stats.c | 268 ++++++++++++++++-- src/phi_stats.h | 14 +- .../d3q19-short/serial-chol-n02.log | 2 +- .../d3q19-short/serial-drop-lc3.log | 10 +- .../d3q19-short/serial-drop-lc5.log | 4 +- .../d3q19-short/serial-init-bp2.log | 12 +- 9 files changed, 261 insertions(+), 323 deletions(-) delete mode 100644 src/field_stats_q.c delete mode 100644 src/field_stats_q.h diff --git a/CHANGES.md b/CHANGES.md index eac484901..7560d2e9c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,6 +13,10 @@ version 0.19.0 - For scalar order parameters an extra version of the "phi_gradmu" approach is avaialble" "phi_gradmu_correction". - See https://ludwig.epcc.ed.ac.uk/inputs/force.html for details. +- Order parameter statistics are now reported as "phi" for scalars + [Px,Py,Pz] for vectors, and [Qxx, Qxy, Qxz, Qyy, Qyz] for liquid + crystal. The computation of the total has been improved by using a + compensated sum, which is more robust to threads/MPI. version 0.18.0 diff --git a/src/field_stats_q.c b/src/field_stats_q.c deleted file mode 100644 index 4a13b8eac..000000000 --- a/src/field_stats_q.c +++ /dev/null @@ -1,247 +0,0 @@ -/***************************************************************************** - * - * field_stats_q.c - * - * Order parameter statistics for Q_ab tansor order parameters. - * - * The single kernel here could be split a number of ways: one - * kernel per scalar order parameter, or one kernel per global - * quantity; or some combination thereof. - * - * - * Edinburgh Soft Matter and Statistical Physics Group and - * Edinburgh Parallel Computing Centre - * - * (c) 2022 The University of Edinburgh - * - * Contributing authors: - * Kevin Stratford (kevin@epcc.ed.ac.uk) - * - *****************************************************************************/ - -#include -#include -#include -#include - -#include "field_stats_q.h" -#include "kernel.h" -#include "util_sum.h" - -typedef struct sum_s sum_t; -struct sum_s { - klein_t qsum; /* Sensitive to round-off */ - double qvar; /* Computed in single-sweep form */ - double qmin; /* minimum */ - double qmax; /* maximum */ - double vol; /* Volume is the same in all cases */ -}; - -__host__ __device__ static inline sum_t sum_zero() { - sum_t sum = { - .qsum = klein_zero(), - .qvar = 0.0, - .qmin = +DBL_MAX, - .qmax = -DBL_MAX, - .vol = 0.0 - }; - return sum; -} - -int field_stats_q_reduce(field_t * q, map_t * map, int nxx, sum_t * sum, - int rank, MPI_Comm comm); -__global__ void field_stats_q_kernel(kernel_ctxt_t * ktx, field_t * q, - map_t * map, int nxx, sum_t * sum); - -static double double_min(double x, double y) {return (x < y) ? x : y;} -static double double_max(double x, double y) {return (x > y) ? x : y;} - -/***************************************************************************** - * - * field_stats_q - * - *****************************************************************************/ - -int field_stats_q(field_t * obj, map_t * map) { - - MPI_Comm comm; - /* - const char * qxx[5] = {"Qxx", "Qxy", "Qxz", "Qyy", "Qyz"}; - */ - const char * qxx[5] = {"phi", "phi", "phi", "phi", "phi"}; - - assert(obj); - assert(map); - - pe_mpi_comm(obj->pe, &comm); - - for (int n = 0; n < NQAB; n++) { - - sum_t sum = {0}; - field_stats_q_reduce(obj, map, n, &sum, 0, comm); - - { - double qsum = klein_sum(&sum.qsum); - double rvol = 1.0/sum.vol; - double qbar = rvol*qsum; /* mean */ - double qvar = rvol*sum.qvar - qbar*qbar; /* variance */ - - pe_info(obj->pe, "[%3s] %14.7e %14.7e %14.7e %14.7e %14.7e\n", - qxx[n], qsum, qbar, qvar, sum.qmin, sum.qmax); - } - } - - return 0; -} - -/***************************************************************************** - * - * field_stats_q_reduce - * - * This is a global reduction to rank in communicator comm. - * - * We expect and assert NQAB to be the largest number of field elements - * to avoid memory allocation and deallocation here. - * - *****************************************************************************/ - -int field_stats_q_reduce(field_t * obj, map_t * map, int nxx, sum_t * sum, - int rank, MPI_Comm comm) { - - int nlocal[3] = {0}; - sum_t sum_local = sum_zero(); /* host copy */ - sum_t * dsum = NULL; /* device copy */ - - assert(obj); - assert(map); - - cs_nlocal(obj->cs, nlocal); - - /* Copy initial values; then the kernel... */ - tdpAssert(tdpMalloc((void **) &dsum, sizeof(sum_t))); - tdpAssert(tdpMemcpy(dsum, &sum_local, sizeof(sum_t), tdpMemcpyHostToDevice)); - - { - kernel_info_t lim = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; - kernel_ctxt_t * ctxt = NULL; - dim3 nblk, ntpb; - - kernel_ctxt_create(obj->cs, 1, lim, &ctxt); - kernel_ctxt_launch_param(ctxt, &nblk, &ntpb); - - tdpLaunchKernel(field_stats_q_kernel, nblk, ntpb, 0, 0, - ctxt->target, obj->target, map->target, nxx, dsum); - - tdpAssert(tdpPeekAtLastError()); - tdpAssert(tdpDeviceSynchronize()); - - kernel_ctxt_free(ctxt); - } - - tdpAssert(tdpMemcpy(&sum_local, dsum, sizeof(sum_t), tdpMemcpyDeviceToHost)); - - MPI_Reduce(&sum_local.qmin, &sum->qmin, 1, MPI_DOUBLE, MPI_MIN, rank, comm); - MPI_Reduce(&sum_local.qmax, &sum->qmax, 1, MPI_DOUBLE, MPI_MAX, rank, comm); - MPI_Reduce(&sum_local.qvar, &sum->qvar, 1, MPI_DOUBLE, MPI_SUM, rank, comm); - MPI_Reduce(&sum_local.vol, &sum->vol, 1, MPI_DOUBLE, MPI_SUM, rank, comm); - - { - MPI_Datatype dt = MPI_DATATYPE_NULL; - MPI_Op op = MPI_OP_NULL; - - klein_mpi_datatype(&dt); - klein_mpi_op_sum(&op); - - MPI_Reduce(&sum_local.qsum, &sum->qsum, 1, dt, op, rank, comm); - - MPI_Op_free(&op); - MPI_Type_free(&dt); - } - - tdpFree(dsum); - - return 0; -} - -/***************************************************************************** - * - * field_stats_q_kernel - * - * Kernel for one order parameter entry "nxx", - * - *****************************************************************************/ - -void field_stats_q_kernel(kernel_ctxt_t * ktx, field_t * obj, map_t * map, - int nxx, sum_t * sum) { - int kindex = 0; - int kiterations = 0; - - assert(obj); - assert(map); - - __shared__ sum_t lsum[TARGET_MAX_THREADS_PER_BLOCK]; - int tid = threadIdx.x; - - /* Local sum */ - - lsum[tid] = sum_zero(); - kiterations = kernel_iterations(ktx); - - for_simt_parallel(kindex, kiterations, 1) { - - int ic = kernel_coords_ic(ktx, kindex); - int jc = kernel_coords_jc(ktx, kindex); - int kc = kernel_coords_kc(ktx, kindex); - int index = kernel_coords_index(ktx, ic, jc, kc); - int status = MAP_BOUNDARY; - - map_status(map, index, &status); - - if (status == MAP_FLUID) { - double q0[NQAB] = {0}; - - field_scalar_array(obj, index, q0); - - klein_add_double(&lsum[tid].qsum, q0[nxx]); - lsum[tid].qvar += q0[nxx]*q0[nxx]; - lsum[tid].qmin = double_min(lsum[tid].qmin, q0[nxx]); - lsum[tid].qmax = double_max(lsum[tid].qmax, q0[nxx]); - lsum[tid].vol += 1.0; - } - } - - __syncthreads(); - - if (tid == 0) { - - /* Accumulate each total for this block */ - - sum_t bsum = sum_zero(); - - for (int it = 0; it < blockDim.x; it++) { - klein_add(&bsum.qsum, lsum[it].qsum); - bsum.qvar += lsum[it].qvar; - bsum.qmin = double_min(bsum.qmin, lsum[it].qmin); - bsum.qmax = double_max(bsum.qmax, lsum[it].qmax); - bsum.vol += lsum[it].vol; - } - - /* Accumulate to final result with protected update */ - - while (atomicCAS(&sum->qsum.lock, 0, 1) != 0) - ; - __threadfence(); - - klein_add(&sum->qsum, bsum.qsum); - - __threadfence(); - atomicExch(&sum->qsum.lock, 0); - - tdpAtomicAddDouble(&sum->qvar, bsum.qvar); - tdpAtomicMinDouble(&sum->qmin, bsum.qmin); - tdpAtomicMaxDouble(&sum->qmax, bsum.qmax); - tdpAtomicAddDouble(&sum->vol, bsum.vol); - } - - return; -} diff --git a/src/field_stats_q.h b/src/field_stats_q.h deleted file mode 100644 index 400a46899..000000000 --- a/src/field_stats_q.h +++ /dev/null @@ -1,23 +0,0 @@ -/***************************************************************************** - * - * field_stats_q.h - * - * - * Edinburgh Soft Matter and Statistical Physics Group and - * Edinburgh Parallel Computing Centre - * - * (c) 2022 The University of Edinburgh - * - * Kevin Stratford (kevin@epcc.ed.ac.uk) - * - *****************************************************************************/ - -#ifndef LUDWIG_FIELD_STATS_Q_H -#define LUDWIG_FIELD_STATS_Q_H - -#include "field.h" -#include "map.h" - -int field_stats_q(field_t * q, map_t * map); - -#endif diff --git a/src/phi_stats.c b/src/phi_stats.c index 6861761c1..2fcdbd96f 100644 --- a/src/phi_stats.c +++ b/src/phi_stats.c @@ -2,15 +2,18 @@ * * phi_stats.c * - * TODO: a misnomer - rename to field_stats.c * Order parameter statistics. + * There is a general version using a compensated sum for the global + * total of each scalar (sensitive to threads/order in some cases). + * + * There is also a version which adds a correction for BBL + * (specifically for the case of binary fluid). * - * $Id: phi_stats.c,v 1.9 2010-10-15 12:40:03 kevin Exp $ * * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2008-2017 The University of Edinburgh + * (c) 2008-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -22,11 +25,56 @@ #include #include -#include "pe.h" -#include "coords.h" -#include "field.h" -#include "util.h" #include "phi_stats.h" +#include "kernel.h" +#include "util_sum.h" + +/* For internal use in accumulating sum for one sclar order parameter */ + +typedef struct sum_s sum_t; +struct sum_s { + klein_t qsum; /* Global sum sensitive to round-off ... */ + double qvar; /* Ditto, but don't care for variance. */ + double qmin; /* minimum */ + double qmax; /* maximum */ + double vol; /* Volume is the same in all cases */ +}; + +int stats_field_q_reduce(field_t * field, map_t * map, int nxx, sum_t * sum, + int rank, MPI_Comm comm); +__global__ void stats_field_q_kernel(kernel_ctxt_t * ktx, field_t * q, + map_t * map, int nxx, sum_t * sum); + +int stats_field_reduce(field_t * obj, map_t * map, double * fmin, + double * fmax, double * fsum, double * fvar, + double * fvol, int rank, MPI_Comm comm); +int stats_field_local(field_t * obj, map_t * map, double * fmin, double * fmax, + double * fsum, double * fvar, double * fvol); + +/***************************************************************************** + * + * Utilities + * + *****************************************************************************/ + +__host__ __device__ static inline double double_min(double x, double y) { + return (x < y) ? x : y; +} + +__host__ __device__ static inline double double_max(double x, double y) { + return (x > y) ? x : y; +} + +__host__ __device__ static inline sum_t sum_zero() { + sum_t sum = { + .qsum = klein_zero(), + .qvar = 0.0, + .qmin = +DBL_MAX, + .qmax = -DBL_MAX, + .vol = 0.0 + }; + return sum; +} /***************************************************************************** * @@ -34,41 +82,203 @@ * *****************************************************************************/ -int stats_field_info(field_t * obj, map_t * map) { +int stats_field_info(field_t * field, map_t * map) { - int n, nf; - MPI_Comm comm; + MPI_Comm comm = MPI_COMM_NULL; - double fmin[NQAB]; - double fmax[NQAB]; - double fsum[NQAB]; - double fvar[NQAB]; - double fvol, rvol; - double fbar, f2; + /* Labelling */ + const char * q1[5] = {"phi", "phi", "phi", "phi", "phi"}; /* default */ + const char * q3[3] = {"Px ", "Py ", "Pz "}; + const char * q5[5] = {"Qxx", "Qxy", "Qxz", "Qyy", "Qyz"}; + const char ** q = NULL; - assert(obj); + assert(field); assert(map); - field_nf(obj, &nf); - assert(nf <= NQAB); + switch (field->nf) { + case 3: + q = q3; + break; + case 6: /* FIXME: 5 when tests are fixed */ + q = q5; + break; + default: + q = q1; + } - pe_mpi_comm(obj->pe, &comm); - stats_field_reduce(obj, map, fmin, fmax, fsum, fvar, &fvol, 0, comm); + pe_mpi_comm(field->pe, &comm); - rvol = 1.0 / fvol; + for (int n = 0; n < field->nf; n++) { - for (n = 0; n < nf; n++) { + sum_t sum = {0}; + stats_field_q_reduce(field, map, n, &sum, 0, comm); - fbar = rvol*fsum[n]; /* mean */ - f2 = rvol*fvar[n] - fbar*fbar; /* variance */ + { + double qsum = klein_sum(&sum.qsum); + double rvol = 1.0/sum.vol; + double qbar = rvol*qsum; /* mean */ + double qvar = rvol*sum.qvar - qbar*qbar; /* variance */ - pe_info(obj->pe, "[phi] %14.7e %14.7e%14.7e %14.7e%14.7e\n", - fsum[n], fbar, f2, fmin[n], fmax[n]); + pe_info(field->pe, "[%3s] %14.7e %14.7e %14.7e %14.7e %14.7e\n", + q[n], qsum, qbar, qvar, sum.qmin, sum.qmax); + } } return 0; } +/***************************************************************************** + * + * stats_field_q_reduce + * + * This is a global reduction to rank in communicator comm. + * + * We expect and assert NQAB to be the largest number of field elements + * to avoid memory allocation and deallocation here. + * + *****************************************************************************/ + +int stats_field_q_reduce(field_t * field, map_t * map, int nxx, sum_t * sum, + int rank, MPI_Comm comm) { + + int nlocal[3] = {0}; + sum_t sum_local = sum_zero(); /* host copy */ + sum_t * dsum = NULL; /* device copy */ + + assert(field); + assert(map); + + cs_nlocal(field->cs, nlocal); + + /* Copy initial values; then the kernel... */ + tdpAssert(tdpMalloc((void **) &dsum, sizeof(sum_t))); + tdpAssert(tdpMemcpy(dsum, &sum_local, sizeof(sum_t), tdpMemcpyHostToDevice)); + + { + kernel_info_t lim = {1, nlocal[X], 1, nlocal[Y], 1, nlocal[Z]}; + kernel_ctxt_t * ctxt = NULL; + dim3 nblk, ntpb; + + kernel_ctxt_create(field->cs, 1, lim, &ctxt); + kernel_ctxt_launch_param(ctxt, &nblk, &ntpb); + + tdpLaunchKernel(stats_field_q_kernel, nblk, ntpb, 0, 0, + ctxt->target, field->target, map->target, nxx, dsum); + + tdpAssert(tdpPeekAtLastError()); + tdpAssert(tdpDeviceSynchronize()); + + kernel_ctxt_free(ctxt); + } + + tdpAssert(tdpMemcpy(&sum_local, dsum, sizeof(sum_t), tdpMemcpyDeviceToHost)); + + MPI_Reduce(&sum_local.qmin, &sum->qmin, 1, MPI_DOUBLE, MPI_MIN, rank, comm); + MPI_Reduce(&sum_local.qmax, &sum->qmax, 1, MPI_DOUBLE, MPI_MAX, rank, comm); + MPI_Reduce(&sum_local.qvar, &sum->qvar, 1, MPI_DOUBLE, MPI_SUM, rank, comm); + MPI_Reduce(&sum_local.vol, &sum->vol, 1, MPI_DOUBLE, MPI_SUM, rank, comm); + + { + MPI_Datatype dt = MPI_DATATYPE_NULL; + MPI_Op op = MPI_OP_NULL; + + klein_mpi_datatype(&dt); + klein_mpi_op_sum(&op); + + MPI_Reduce(&sum_local.qsum, &sum->qsum, 1, dt, op, rank, comm); + + MPI_Op_free(&op); + MPI_Type_free(&dt); + } + + tdpFree(dsum); + + return 0; +} + +/***************************************************************************** + * + * stats_field_q_kernel + * + * Kernel for one order parameter entry "nxx", + * + *****************************************************************************/ + +__global__ void stats_field_q_kernel(kernel_ctxt_t * ktx, field_t * field, + map_t * map, int nxx, sum_t * sum) { + int kindex = 0; + int kiterations = 0; + + assert(field); + assert(map); + + __shared__ sum_t lsum[TARGET_MAX_THREADS_PER_BLOCK]; + int tid = threadIdx.x; + + /* Local sum */ + + lsum[tid] = sum_zero(); + kiterations = kernel_iterations(ktx); + + for_simt_parallel(kindex, kiterations, 1) { + + int ic = kernel_coords_ic(ktx, kindex); + int jc = kernel_coords_jc(ktx, kindex); + int kc = kernel_coords_kc(ktx, kindex); + int index = kernel_coords_index(ktx, ic, jc, kc); + int status = MAP_BOUNDARY; + + map_status(map, index, &status); + + if (status == MAP_FLUID) { + double q0[NQAB] = {0}; + + field_scalar_array(field, index, q0); + + klein_add_double(&lsum[tid].qsum, q0[nxx]); + lsum[tid].qvar += q0[nxx]*q0[nxx]; + lsum[tid].qmin = double_min(lsum[tid].qmin, q0[nxx]); + lsum[tid].qmax = double_max(lsum[tid].qmax, q0[nxx]); + lsum[tid].vol += 1.0; + } + } + + __syncthreads(); + + if (tid == 0) { + + /* Accumulate each total for this block */ + + sum_t bsum = sum_zero(); + + for (int it = 0; it < blockDim.x; it++) { + klein_add(&bsum.qsum, lsum[it].qsum); + bsum.qvar += lsum[it].qvar; + bsum.qmin = double_min(bsum.qmin, lsum[it].qmin); + bsum.qmax = double_max(bsum.qmax, lsum[it].qmax); + bsum.vol += lsum[it].vol; + } + + /* Accumulate to final result with protected update */ + + while (atomicCAS(&sum->qsum.lock, 0, 1) != 0) + ; + __threadfence(); + + klein_add(&sum->qsum, bsum.qsum); + + __threadfence(); + atomicExch(&sum->qsum.lock, 0); + + tdpAtomicAddDouble(&sum->qvar, bsum.qvar); + tdpAtomicMinDouble(&sum->qmin, bsum.qmin); + tdpAtomicMaxDouble(&sum->qmax, bsum.qmax); + tdpAtomicAddDouble(&sum->vol, bsum.vol); + } + + return; +} + /***************************************************************************** * * stats_field_info_bbl @@ -226,8 +436,8 @@ int stats_field_local(field_t * obj, map_t * map, double * fmin, double * fmax, field_scalar_array(obj, index, f0); for (n = 0; n < nf; n++) { - fmin[n] = dmin(fmin[n], f0[n]); - fmax[n] = dmax(fmax[n], f0[n]); + fmin[n] = double_min(fmin[n], f0[n]); + fmax[n] = double_max(fmax[n], f0[n]); fsum[n] += f0[n]; fvar[n] += f0[n]*f0[n]; } diff --git a/src/phi_stats.h b/src/phi_stats.h index 28604974a..c417350e2 100644 --- a/src/phi_stats.h +++ b/src/phi_stats.h @@ -2,18 +2,17 @@ * * phi_stats.h * - * $Id: phi_stats.h,v 1.5 2010-10-15 12:40:03 kevin Exp $ - * * Edinburgh Soft Matter and Statistical Physics Group * and Edinburgh Parallel Computing Centre * + * (c) 2008-2023 The University of Edinburgh + * * Kevin Stratford (kevin@epcc.ed.ac.uk) - * (c) The University of Edinburgh (2008) * *****************************************************************************/ -#ifndef PHI_STATS_ -#define PHI_STATS_ +#ifndef LUDWIG_PHI_STATS_H +#define LUDWIG_PHI_STATS_H #include #include "field.h" @@ -21,11 +20,6 @@ #include "bbl.h" int stats_field_info(field_t * obj, map_t * map); -int stats_field_reduce(field_t * obj, map_t * map, double * fmin, - double * fmax, double * fsum, double * fvar, - double * fvol, int rank, MPI_Comm comm); -int stats_field_local(field_t * obj, map_t * map, double * fmin, double * fmax, - double * fsum, double * fvar, double * fvol); int stats_field_info_bbl(field_t * obj, map_t * map, bbl_t * bbl); #endif diff --git a/tests/regression/d3q19-short/serial-chol-n02.log b/tests/regression/d3q19-short/serial-chol-n02.log index 6882bba95..24ce9c0c9 100644 --- a/tests/regression/d3q19-short/serial-chol-n02.log +++ b/tests/regression/d3q19-short/serial-chol-n02.log @@ -166,7 +166,7 @@ Scalars - total mean variance min max [phi] -2.3050805e-15 -8.9001309e-21 2.6690464e-08 -7.0823033e-03 7.0823033e-03 [phi] 4.8169716e-15 1.8598777e-20 3.3769091e-08 -7.5924269e-03 7.5924269e-03 [phi] 2.1265219e+04 8.2106995e-02 3.1278470e-02 -1.6690066e-01 3.3397530e-01 -[phi] 1.8653835e-02 7.2024197e-08 3.1212676e-02 -2.5040482e-01 2.5040481e-01 +[phi] 1.8653836e-02 7.2024203e-08 3.1212676e-02 -2.5040482e-01 2.5040481e-01 Free energies - timestep f v f/v f_s a f_s/a [fe] 5 -1.6870562224e+01 2.5899400000e+05 -6.5138814893e-05 8.4425272198e-01 2.0280000000e+03 4.1629818638e-04 diff --git a/tests/regression/d3q19-short/serial-drop-lc3.log b/tests/regression/d3q19-short/serial-drop-lc3.log index f3be8f39e..85badbe16 100644 --- a/tests/regression/d3q19-short/serial-drop-lc3.log +++ b/tests/regression/d3q19-short/serial-drop-lc3.log @@ -121,11 +121,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 262144.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] 2.1068800e+05 8.0371094e-01 3.5404873e-01 -1.0000000e+00 1.0000000e+00 -[phi] 4.1760137e-12 1.5930228e-17 6.0000000e-02 -4.4966062e-01 4.4966064e-01 -[phi] -3.1326420e-08 -1.1950081e-13 5.0000000e-02 -6.0000000e-01 6.0000000e-01 -[phi] -3.1329084e-08 -1.1951097e-13 5.0000000e-02 -6.0000000e-01 6.0000000e-01 -[phi] 1.4539411e-13 5.5463453e-19 6.0000000e-02 -4.4966062e-01 4.4966064e-01 -[phi] -3.1333445e-08 -1.1952761e-13 5.0000000e-02 -6.0000000e-01 6.0000000e-01 +[phi] -4.7213797e-14 -1.8010634e-19 6.0000000e-02 -4.4966062e-01 4.4966064e-01 +[phi] -3.1328486e-08 -1.1950869e-13 5.0000000e-02 -6.0000000e-01 6.0000000e-01 +[phi] -3.1328486e-08 -1.1950869e-13 5.0000000e-02 -6.0000000e-01 6.0000000e-01 +[phi] -4.7213797e-14 -1.8010634e-19 6.0000000e-02 -4.4966062e-01 4.4966064e-01 +[phi] -3.1328486e-08 -1.1950869e-13 5.0000000e-02 -6.0000000e-01 6.0000000e-01 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-drop-lc5.log b/tests/regression/d3q19-short/serial-drop-lc5.log index 118e61a95..209a6caf7 100644 --- a/tests/regression/d3q19-short/serial-drop-lc5.log +++ b/tests/regression/d3q19-short/serial-drop-lc5.log @@ -115,10 +115,10 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] 1.8176000e+04 5.5468750e-01 6.9232178e-01 -1.0000000e+00 1.0000000e+00 -[phi] 4.0040471e-12 1.2219382e-16 6.0000000e-02 -4.4966055e-01 4.4966059e-01 +[phi] -8.6591172e-15 -2.6425529e-19 6.0000000e-02 -4.4966055e-01 4.4966059e-01 [phi] -3.8895517e+03 -1.1869970e-01 3.8159319e-02 -6.0000000e-01 4.8051711e-01 [phi] -3.8895517e+03 -1.1869970e-01 3.8159319e-02 -6.0000000e-01 4.8051711e-01 -[phi] 5.6144776e-12 1.7134026e-16 6.0000000e-02 -4.4966055e-01 4.4966059e-01 +[phi] -8.6591172e-15 -2.6425529e-19 6.0000000e-02 -4.4966055e-01 4.4966059e-01 [phi] -3.8895517e+03 -1.1869970e-01 3.8159319e-02 -6.0000000e-01 4.8051711e-01 Momentum - x y z diff --git a/tests/regression/d3q19-short/serial-init-bp2.log b/tests/regression/d3q19-short/serial-init-bp2.log index dd9a193f6..2afb90b7b 100644 --- a/tests/regression/d3q19-short/serial-init-bp2.log +++ b/tests/regression/d3q19-short/serial-init-bp2.log @@ -89,11 +89,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 2.6610658e-15 8.1209284e-20 8.9999982e-02 -6.0000000e-01 6.0000000e-01 +[phi] 0.0000000e+00 0.0000000e+00 8.9999982e-02 -6.0000000e-01 6.0000000e-01 [phi] -4.0156293e-04 -1.2254728e-08 4.5000009e-02 -3.0000000e-01 3.0000000e-01 -[phi] -4.0156294e-04 -1.2254728e-08 4.5000009e-02 -3.0000000e-01 3.0000000e-01 -[phi] -8.9217488e-12 -2.7227016e-16 8.9999982e-02 -6.0000000e-01 6.0000000e-01 -[phi] -4.0156287e-04 -1.2254726e-08 4.5000009e-02 -3.0000000e-01 3.0000000e-01 +[phi] -4.0156293e-04 -1.2254728e-08 4.5000009e-02 -3.0000000e-01 3.0000000e-01 +[phi] 0.0000000e+00 0.0000000e+00 8.9999982e-02 -6.0000000e-01 6.0000000e-01 +[phi] -4.0156293e-04 -1.2254726e-08 4.5000009e-02 -3.0000000e-01 3.0000000e-01 Momentum - x y z [total ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 @@ -103,10 +103,10 @@ Starting time step loop. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 5.5946818e-07 0.99868639745 1.00197940975 -[phi] -2.8587896e-13 -8.7243335e-18 8.9762988e-02 -5.9884923e-01 5.9934603e-01 +[phi] 2.5125264e-15 7.6676219e-20 8.9762988e-02 -5.9884923e-01 5.9934603e-01 [phi] -4.0099021e-04 -1.2237250e-08 4.4898274e-02 -2.9996132e-01 2.9996132e-01 [phi] -4.0099021e-04 -1.2237250e-08 4.4898274e-02 -2.9996132e-01 2.9996132e-01 -[phi] 3.4842060e-12 1.0632953e-16 8.9762988e-02 -5.9884923e-01 5.9934603e-01 +[phi] 9.8944335e-16 3.0195415e-20 8.9762988e-02 -5.9884923e-01 5.9934603e-01 [phi] -4.0099021e-04 -1.2237250e-08 4.4898274e-02 -2.9996132e-01 2.9996132e-01 Free energies - timestep f v f/v f_bulk/v f_grad/v redshift From ba792b283ea590bfe9d3ced7cecd09b499406ec6 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 3 Jan 2023 12:44:40 +0000 Subject: [PATCH 203/244] Use compensated sum in order parameter stats --- tests/regression/d3q19-short/serial-chol-st3.log | 2 +- tests/regression/d3q19-short/serial-chol-st4.log | 2 +- tests/regression/d3q19-short/serial-chol-st5.log | 2 +- tests/regression/d3q19-short/serial-chol-st6.log | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/regression/d3q19-short/serial-chol-st3.log b/tests/regression/d3q19-short/serial-chol-st3.log index 2e90ee44a..dd6887681 100644 --- a/tests/regression/d3q19-short/serial-chol-st3.log +++ b/tests/regression/d3q19-short/serial-chol-st3.log @@ -111,7 +111,7 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 [phi] 2.4576000e+03 7.4999999e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 -[phi] -3.1310888e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 +[phi] -3.1310879e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 [phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 [phi] 2.4576000e+03 7.5000001e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 [phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-st4.log b/tests/regression/d3q19-short/serial-chol-st4.log index af64aa13f..80455b64d 100644 --- a/tests/regression/d3q19-short/serial-chol-st4.log +++ b/tests/regression/d3q19-short/serial-chol-st4.log @@ -111,7 +111,7 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 [phi] 2.4576000e+03 7.4999999e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 -[phi] -3.1310888e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 +[phi] -3.1310879e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 [phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 [phi] 2.4576000e+03 7.5000001e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 [phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-st5.log b/tests/regression/d3q19-short/serial-chol-st5.log index 58d7760b3..eec8de91d 100644 --- a/tests/regression/d3q19-short/serial-chol-st5.log +++ b/tests/regression/d3q19-short/serial-chol-st5.log @@ -111,7 +111,7 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 [phi] 2.4576000e+03 7.4999999e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 -[phi] -3.1310888e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 +[phi] -3.1310879e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 [phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 [phi] 2.4576000e+03 7.5000001e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 [phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-st6.log b/tests/regression/d3q19-short/serial-chol-st6.log index e72bcb6e2..606f40952 100644 --- a/tests/regression/d3q19-short/serial-chol-st6.log +++ b/tests/regression/d3q19-short/serial-chol-st6.log @@ -111,7 +111,7 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 [phi] 2.4576000e+03 7.4999999e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 -[phi] -3.1310888e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 +[phi] -3.1310879e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 [phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 [phi] 2.4576000e+03 7.5000001e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 [phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 From 038ff9a52f2e5bf089b3ad039a0f6d4d46bb3b8b Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 3 Jan 2023 12:45:25 +0000 Subject: [PATCH 204/244] Update comments on compensated sum --- src/phi_stats.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/phi_stats.c b/src/phi_stats.c index 2fcdbd96f..8806953c9 100644 --- a/src/phi_stats.c +++ b/src/phi_stats.c @@ -6,6 +6,10 @@ * There is a general version using a compensated sum for the global * total of each scalar (sensitive to threads/order in some cases). * + * The variance is always computed in a single sweep form; we don't + * care too much about the exact result, so this is just a standard + * floating point sum. + * * There is also a version which adds a correction for BBL * (specifically for the case of binary fluid). * @@ -22,14 +26,13 @@ #include #include -#include #include #include "phi_stats.h" #include "kernel.h" #include "util_sum.h" -/* For internal use in accumulating sum for one sclar order parameter */ +/* For internal use in accumulating sum for one scalar order parameter */ typedef struct sum_s sum_t; struct sum_s { From cfb35324c13ea3a57288e918ff77323f70c0c06a Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 3 Jan 2023 15:19:07 +0000 Subject: [PATCH 205/244] Cosmetic: replace labels phi with Qxx Qxy etc --- src/phi_stats.c | 2 +- .../d3q19-short/serial-actv-s01.log | 20 +++++++++---------- .../d3q19-short/serial-actv-s02.log | 20 +++++++++---------- .../d3q19-short/serial-anch-cn1.log | 20 +++++++++---------- .../d3q19-short/serial-anch-cn2.log | 20 +++++++++---------- .../d3q19-short/serial-anch-wn1.log | 20 +++++++++---------- .../d3q19-short/serial-anch-wn2.log | 20 +++++++++---------- .../d3q19-short/serial-anch-wn3.log | 20 +++++++++---------- .../d3q19-short/serial-chol-fld.log | 20 +++++++++---------- .../d3q19-short/serial-chol-n01.log | 20 +++++++++---------- .../d3q19-short/serial-chol-n02.log | 20 +++++++++---------- .../d3q19-short/serial-chol-n03.log | 20 +++++++++---------- .../d3q19-short/serial-chol-n04.log | 20 +++++++++---------- .../d3q19-short/serial-chol-p01.log | 20 +++++++++---------- .../d3q19-short/serial-chol-st1.log | 20 +++++++++---------- .../d3q19-short/serial-chol-st2.log | 20 +++++++++---------- .../d3q19-short/serial-chol-st3.log | 20 +++++++++---------- .../d3q19-short/serial-chol-st4.log | 20 +++++++++---------- .../d3q19-short/serial-chol-st5.log | 20 +++++++++---------- .../d3q19-short/serial-chol-st6.log | 20 +++++++++---------- .../d3q19-short/serial-chol-st7.log | 20 +++++++++---------- .../d3q19-short/serial-chol-w01.log | 20 +++++++++---------- .../d3q19-short/serial-chol-w02.log | 20 +++++++++---------- .../d3q19-short/serial-chol-w03.log | 20 +++++++++---------- .../d3q19-short/serial-chol-w04.log | 20 +++++++++---------- .../d3q19-short/serial-chol-w05.log | 20 +++++++++---------- .../d3q19-short/serial-drop-lc1.log | 20 +++++++++---------- .../d3q19-short/serial-drop-lc2.log | 20 +++++++++---------- .../d3q19-short/serial-drop-lc3.log | 20 +++++++++---------- .../d3q19-short/serial-drop-lc4.log | 20 +++++++++---------- .../d3q19-short/serial-drop-lc5.log | 20 +++++++++---------- .../d3q19-short/serial-init-bp1.log | 20 +++++++++---------- .../d3q19-short/serial-init-bp2.log | 20 +++++++++---------- .../d3q19-short/serial-init-br1.log | 20 +++++++++---------- .../d3q19-short/serial-init-br2.log | 20 +++++++++---------- .../d3q19-short/serial-init-lcb.log | 20 +++++++++---------- .../d3q19-short/serial-init-lcr.log | 20 +++++++++---------- .../d3q19-short/serial-relx-bp1.log | 20 +++++++++---------- 38 files changed, 371 insertions(+), 371 deletions(-) diff --git a/src/phi_stats.c b/src/phi_stats.c index 8806953c9..12df44b8c 100644 --- a/src/phi_stats.c +++ b/src/phi_stats.c @@ -102,7 +102,7 @@ int stats_field_info(field_t * field, map_t * map) { case 3: q = q3; break; - case 6: /* FIXME: 5 when tests are fixed */ + case 5: q = q5; break; default: diff --git a/tests/regression/d3q19-short/serial-actv-s01.log b/tests/regression/d3q19-short/serial-actv-s01.log index a4eadef20..16bc5a0a9 100644 --- a/tests/regression/d3q19-short/serial-actv-s01.log +++ b/tests/regression/d3q19-short/serial-actv-s01.log @@ -91,11 +91,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 4096.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] -6.2059650e+02 -1.5151282e-01 7.1188129e-03 -1.6666667e-01 3.1825649e-01 -[phi] -3.6082248e-16 -8.8091427e-20 2.2847222e-04 -8.5505036e-02 8.5505036e-02 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 1.3032632e+03 3.1817948e-01 7.1188129e-03 -1.5158982e-01 3.3333333e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] -6.2059650e+02 -1.5151282e-01 7.1188129e-03 -1.6666667e-01 3.1825649e-01 +[Qxy] -3.6082248e-16 -8.8091427e-20 2.2847222e-04 -8.5505036e-02 8.5505036e-02 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qyy] 1.3032632e+03 3.1817948e-01 7.1188129e-03 -1.5158982e-01 3.3333333e-01 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] 5.6843419e-14 0.0000000e+00 0.0000000e+00 @@ -105,11 +105,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 4096.00 1.00000000000 2.1166918e-06 0.98789531168 1.00580926654 -[phi] -6.2429132e+02 -1.5241487e-01 5.5639370e-03 -1.6702433e-01 2.8093481e-01 -[phi] 6.8850686e-16 1.6809249e-19 4.5406352e-04 -1.2374555e-01 1.2374555e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 1.3036556e+03 3.1827530e-01 5.9054578e-03 -1.2835456e-01 3.3386466e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] -6.2429132e+02 -1.5241487e-01 5.5639370e-03 -1.6702433e-01 2.8093481e-01 +[Qxy] 6.8850686e-16 1.6809249e-19 4.5406352e-04 -1.2374555e-01 1.2374555e-01 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qyy] 1.3036556e+03 3.1827530e-01 5.9054578e-03 -1.2835456e-01 3.3386466e-01 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Free energies - timestep f v f/v f_bulk/v f_grad/v redshift [fe] 20 -2.7835856233e+01 4.0960000000e+03 -6.7958633380e-03 -6.9138978555e-03 1.1803451752e-04 1.0000000000e+00 diff --git a/tests/regression/d3q19-short/serial-actv-s02.log b/tests/regression/d3q19-short/serial-actv-s02.log index 2a747d2a2..c06598dfe 100644 --- a/tests/regression/d3q19-short/serial-actv-s02.log +++ b/tests/regression/d3q19-short/serial-actv-s02.log @@ -91,11 +91,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 4096.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] -6.2059650e+02 -1.5151282e-01 7.1188129e-03 -1.6666667e-01 3.1825649e-01 -[phi] -3.6082248e-16 -8.8091427e-20 2.2847222e-04 -8.5505036e-02 8.5505036e-02 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 1.3032632e+03 3.1817948e-01 7.1188129e-03 -1.5158982e-01 3.3333333e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] -6.2059650e+02 -1.5151282e-01 7.1188129e-03 -1.6666667e-01 3.1825649e-01 +[Qxy] -3.6082248e-16 -8.8091427e-20 2.2847222e-04 -8.5505036e-02 8.5505036e-02 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qyy] 1.3032632e+03 3.1817948e-01 7.1188129e-03 -1.5158982e-01 3.3333333e-01 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] 5.6843419e-14 0.0000000e+00 0.0000000e+00 @@ -105,11 +105,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 4096.00 1.00000000000 6.9021590e-05 0.94703141779 1.01928719360 -[phi] -6.2562059e+02 -1.5273940e-01 5.2634346e-03 -1.6776749e-01 2.8546866e-01 -[phi] -1.9247111e-15 -4.6990017e-19 6.8345581e-04 -1.5507906e-01 1.5507906e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 1.3052116e+03 3.1865516e-01 5.5474665e-03 -1.3107958e-01 3.3505841e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] -6.2562059e+02 -1.5273940e-01 5.2634346e-03 -1.6776749e-01 2.8546866e-01 +[Qxy] -1.9247111e-15 -4.6990017e-19 6.8345581e-04 -1.5507906e-01 1.5507906e-01 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qyy] 1.3052116e+03 3.1865516e-01 5.5474665e-03 -1.3107958e-01 3.3505841e-01 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Free energies - timestep f v f/v f_bulk/v f_grad/v redshift [fe] 20 -2.7856522638e+01 4.0960000000e+03 -6.8009088471e-03 -6.9151716814e-03 1.1426283429e-04 1.0000000000e+00 diff --git a/tests/regression/d3q19-short/serial-anch-cn1.log b/tests/regression/d3q19-short/serial-anch-cn1.log index c6994921a..8e657895a 100644 --- a/tests/regression/d3q19-short/serial-anch-cn1.log +++ b/tests/regression/d3q19-short/serial-anch-cn1.log @@ -127,11 +127,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 260569.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 8.6856333e+04 3.3333333e-01 -2.9758140e-13 3.3333333e-01 3.3333333e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] -4.3428167e+04 -1.6666667e-01 -7.4395351e-14 -1.6666667e-01 -1.6666667e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] 8.6856333e+04 3.3333333e-01 -2.9758140e-13 3.3333333e-01 3.3333333e-01 +[Qxy] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qyy] -4.3428167e+04 -1.6666667e-01 -7.4395351e-14 -1.6666667e-01 -1.6666667e-01 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -148,11 +148,11 @@ Colloid velocities - x y z Scalars - total mean variance min max [rho] 260569.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 8.6853199e+04 3.3332130e-01 9.6005342e-08 3.1768067e-01 3.3333343e-01 -[phi] -1.1144240e-17 -4.2768865e-23 1.5584868e-08 -7.4425078e-03 7.4425078e-03 -[phi] -1.7800662e-18 -6.8314581e-24 1.5584868e-08 -7.4425078e-03 7.4425078e-03 -[phi] -4.3426599e+04 -1.6666065e-01 3.5559934e-08 -1.6717510e-01 -1.5587390e-01 -[phi] 5.7324278e-19 2.1999654e-24 1.8288922e-08 -8.0877770e-03 8.0877770e-03 +[Qxx] 8.6853199e+04 3.3332130e-01 9.6005342e-08 3.1768067e-01 3.3333343e-01 +[Qxy] -1.1144240e-17 -4.2768865e-23 1.5584868e-08 -7.4425078e-03 7.4425078e-03 +[Qxz] -1.7800662e-18 -6.8314581e-24 1.5584868e-08 -7.4425078e-03 7.4425078e-03 +[Qyy] -4.3426599e+04 -1.6666065e-01 3.5559934e-08 -1.6717510e-01 -1.5587390e-01 +[Qyz] 5.7324278e-19 2.1999654e-24 1.8288922e-08 -8.0877770e-03 8.0877770e-03 Free energies - timestep f v f/v f_s a f_s/a [fe] 2 -1.4680007512e+01 2.6056900000e+05 -5.6338273211e-05 1.2978165671e+00 1.0140000000e+03 1.2798979952e-03 diff --git a/tests/regression/d3q19-short/serial-anch-cn2.log b/tests/regression/d3q19-short/serial-anch-cn2.log index 9ec0fba0e..78c91c4ee 100644 --- a/tests/regression/d3q19-short/serial-anch-cn2.log +++ b/tests/regression/d3q19-short/serial-anch-cn2.log @@ -128,11 +128,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 260569.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 8.6856333e+04 3.3333333e-01 -2.9758140e-13 3.3333333e-01 3.3333333e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] -4.3428167e+04 -1.6666667e-01 -7.4395351e-14 -1.6666667e-01 -1.6666667e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] 8.6856333e+04 3.3333333e-01 -2.9758140e-13 3.3333333e-01 3.3333333e-01 +[Qxy] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qyy] -4.3428167e+04 -1.6666667e-01 -7.4395351e-14 -1.6666667e-01 -1.6666667e-01 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -149,11 +149,11 @@ Colloid velocities - x y z Scalars - total mean variance min max [rho] 260569.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 8.6854570e+04 3.3332657e-01 3.9599019e-08 3.2273571e-01 3.3333345e-01 -[phi] -5.9732106e-18 -2.2923719e-23 6.0655261e-09 -5.0352107e-03 5.0352107e-03 -[phi] -3.1520214e-19 -1.2096686e-24 6.0655261e-09 -5.0352107e-03 5.0352107e-03 -[phi] -4.3427285e+04 -1.6666328e-01 1.0805859e-08 -1.6666674e-01 -1.5995154e-01 -[phi] 1.1520177e-18 4.4211619e-24 8.2287936e-10 -2.0266288e-03 2.0266288e-03 +[Qxx] 8.6854570e+04 3.3332657e-01 3.9599019e-08 3.2273571e-01 3.3333345e-01 +[Qxy] -5.9732106e-18 -2.2923719e-23 6.0655261e-09 -5.0352107e-03 5.0352107e-03 +[Qxz] -3.1520214e-19 -1.2096686e-24 6.0655261e-09 -5.0352107e-03 5.0352107e-03 +[Qyy] -4.3427285e+04 -1.6666328e-01 1.0805859e-08 -1.6666674e-01 -1.5995154e-01 +[Qyz] 1.1520177e-18 4.4211619e-24 8.2287936e-10 -2.0266288e-03 2.0266288e-03 Free energies - timestep f v f/v f_s a f_s/a [fe] 2 -1.7142944558e+01 2.6056900000e+05 -6.5790422339e-05 1.4735048183e+00 1.0140000000e+03 1.4531605703e-03 diff --git a/tests/regression/d3q19-short/serial-anch-wn1.log b/tests/regression/d3q19-short/serial-anch-wn1.log index 164a58191..600a6ebf6 100644 --- a/tests/regression/d3q19-short/serial-anch-wn1.log +++ b/tests/regression/d3q19-short/serial-anch-wn1.log @@ -110,11 +110,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 256.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 2.2385250e+01 8.7442384e-02 3.0434697e-02 -1.6666121e-01 3.3270644e-01 -[phi] -1.9573868e+00 -7.6460423e-03 3.1989956e-02 -2.4998334e-01 2.4991631e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 -[phi] 2.0281416e+01 7.9224283e-02 3.0434697e-02 -1.6603977e-01 3.3332787e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] 2.2385250e+01 8.7442384e-02 3.0434697e-02 -1.6666121e-01 3.3270644e-01 +[Qxy] -1.9573868e+00 -7.6460423e-03 3.1989956e-02 -2.4998334e-01 2.4991631e-01 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 +[Qyy] 2.0281416e+01 7.9224283e-02 3.0434697e-02 -1.6603977e-01 3.3332787e-01 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -125,11 +125,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 256.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 2.5211098e+01 9.8480851e-02 2.8023479e-02 -1.6754810e-01 3.3359867e-01 -[phi] -1.4108509e+01 -5.5111365e-02 1.8452527e-02 -2.4648916e-01 2.3150652e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 1.4012109e+01 5.4734803e-02 2.7685464e-02 -1.6668036e-01 3.3412667e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] 2.5211098e+01 9.8480851e-02 2.8023479e-02 -1.6754810e-01 3.3359867e-01 +[Qxy] -1.4108509e+01 -5.5111365e-02 1.8452527e-02 -2.4648916e-01 2.3150652e-01 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qyy] 1.4012109e+01 5.4734803e-02 2.7685464e-02 -1.6668036e-01 3.3412667e-01 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Free energies - timestep f v f/v f_s1 fs_s2 redshift [fe] 1000 -1.3145616110e-02 2.5600000000e+02 -5.1350062928e-05 1.5863007482e-04 4.2491896032e-04 1.0000000000e+00 diff --git a/tests/regression/d3q19-short/serial-anch-wn2.log b/tests/regression/d3q19-short/serial-anch-wn2.log index c325156d5..c21f5d079 100644 --- a/tests/regression/d3q19-short/serial-anch-wn2.log +++ b/tests/regression/d3q19-short/serial-anch-wn2.log @@ -111,11 +111,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 256.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 2.2385250e+01 8.7442384e-02 3.0434697e-02 -1.6666121e-01 3.3270644e-01 -[phi] -1.9573868e+00 -7.6460423e-03 3.1989956e-02 -2.4998334e-01 2.4991631e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 -[phi] 2.0281416e+01 7.9224283e-02 3.0434697e-02 -1.6603977e-01 3.3332787e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] 2.2385250e+01 8.7442384e-02 3.0434697e-02 -1.6666121e-01 3.3270644e-01 +[Qxy] -1.9573868e+00 -7.6460423e-03 3.1989956e-02 -2.4998334e-01 2.4991631e-01 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 +[Qyy] 2.0281416e+01 7.9224283e-02 3.0434697e-02 -1.6603977e-01 3.3332787e-01 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -126,11 +126,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 256.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 2.7759754e+01 1.0843654e-01 2.7900674e-02 -1.6658228e-01 3.3545673e-01 -[phi] -1.3445297e+01 -5.2520691e-02 1.8571508e-02 -2.4652161e-01 2.4228311e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 1.1372296e+01 4.4423032e-02 2.7662195e-02 -1.6795611e-01 3.3366463e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] 2.7759754e+01 1.0843654e-01 2.7900674e-02 -1.6658228e-01 3.3545673e-01 +[Qxy] -1.3445297e+01 -5.2520691e-02 1.8571508e-02 -2.4652161e-01 2.4228311e-01 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qyy] 1.1372296e+01 4.4423032e-02 2.7662195e-02 -1.6795611e-01 3.3366463e-01 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Free energies - timestep f v f/v f_s1 fs_s2 redshift [fe] 1000 -1.3493135565e-02 2.5600000000e+02 -5.2707560800e-05 1.0117484724e-03 9.2013682890e-04 1.0000000000e+00 diff --git a/tests/regression/d3q19-short/serial-anch-wn3.log b/tests/regression/d3q19-short/serial-anch-wn3.log index 9a20ca05d..790b78c18 100644 --- a/tests/regression/d3q19-short/serial-anch-wn3.log +++ b/tests/regression/d3q19-short/serial-anch-wn3.log @@ -111,11 +111,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 256.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 2.2385250e+01 8.7442384e-02 3.0434697e-02 -1.6666121e-01 3.3270644e-01 -[phi] -1.9573868e+00 -7.6460423e-03 3.1989956e-02 -2.4998334e-01 2.4991631e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 -[phi] 2.0281416e+01 7.9224283e-02 3.0434697e-02 -1.6603977e-01 3.3332787e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] 2.2385250e+01 8.7442384e-02 3.0434697e-02 -1.6666121e-01 3.3270644e-01 +[Qxy] -1.9573868e+00 -7.6460423e-03 3.1989956e-02 -2.4998334e-01 2.4991631e-01 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 +[Qyy] 2.0281416e+01 7.9224283e-02 3.0434697e-02 -1.6603977e-01 3.3332787e-01 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -126,11 +126,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 256.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 3.4574182e+01 1.3505540e-01 2.6601803e-02 -1.5887406e-01 3.3604568e-01 -[phi] -1.4552712e+01 -5.6846531e-02 1.7934216e-02 -2.4653069e-01 2.4573024e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 4.8271411e+00 1.8856020e-02 2.5572584e-02 -1.6833015e-01 3.2060711e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] 3.4574182e+01 1.3505540e-01 2.6601803e-02 -1.5887406e-01 3.3604568e-01 +[Qxy] -1.4552712e+01 -5.6846531e-02 1.7934216e-02 -2.4653069e-01 2.4573024e-01 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qyy] 4.8271411e+00 1.8856020e-02 2.5572584e-02 -1.6833015e-01 3.2060711e-01 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Free energies - timestep f v f/v f_s1 fs_s2 redshift [fe] 1000 -1.3615060139e-02 2.5600000000e+02 -5.3183828668e-05 3.7994558242e-04 3.7604271017e-04 1.0000000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-fld.log b/tests/regression/d3q19-short/serial-chol-fld.log index d35f25981..bdcf56383 100644 --- a/tests/regression/d3q19-short/serial-chol-fld.log +++ b/tests/regression/d3q19-short/serial-chol-fld.log @@ -104,11 +104,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 4096.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 2.0480000e+02 5.0000000e-02 -2.7495367e-16 5.0000000e-02 5.0000000e-02 -[phi] 6.1440000e+02 1.5000000e-01 2.0781987e-15 1.5000000e-01 1.5000000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 2.0480000e+02 5.0000000e-02 -2.7495367e-16 5.0000000e-02 5.0000000e-02 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] 2.0480000e+02 5.0000000e-02 -2.7495367e-16 5.0000000e-02 5.0000000e-02 +[Qxy] 6.1440000e+02 1.5000000e-01 2.0781987e-15 1.5000000e-01 1.5000000e-01 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qyy] 2.0480000e+02 5.0000000e-02 -2.7495367e-16 5.0000000e-02 5.0000000e-02 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -118,11 +118,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 4096.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 2.1364907e+02 5.2160417e-02 -4.9136042e-16 5.2160417e-02 5.2160417e-02 -[phi] 6.3827142e+02 1.5582798e-01 1.5092094e-15 1.5582798e-01 1.5582798e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 2.1227628e+02 5.1825263e-02 5.0090140e-16 5.1825263e-02 5.1825263e-02 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] 2.1364907e+02 5.2160417e-02 -4.9136042e-16 5.2160417e-02 5.2160417e-02 +[Qxy] 6.3827142e+02 1.5582798e-01 1.5092094e-15 1.5582798e-01 1.5582798e-01 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qyy] 2.1227628e+02 5.1825263e-02 5.0090140e-16 5.1825263e-02 5.1825263e-02 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Free energies - timestep f v f/v f_bulk/v f_grad/v redshift [fe] 10 -1.6165785011e+00 4.0960000000e+03 -3.9467248563e-04 -3.8894438981e-04 0.0000000000e+00 1.0000000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-n01.log b/tests/regression/d3q19-short/serial-chol-n01.log index 7e9d1135a..cf8e7501b 100644 --- a/tests/regression/d3q19-short/serial-chol-n01.log +++ b/tests/regression/d3q19-short/serial-chol-n01.log @@ -133,11 +133,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 260569.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 2.1558645e+04 8.2736799e-02 3.1266048e-02 -1.6666667e-01 3.3333333e-01 -[phi] 2.5523242e-02 9.7951952e-08 3.1233596e-02 -2.5000000e-01 2.5000000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 2.1869522e+04 8.3929867e-02 3.1266048e-02 -1.6666667e-01 3.3333333e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] 2.1558645e+04 8.2736799e-02 3.1266048e-02 -1.6666667e-01 3.3333333e-01 +[Qxy] 2.5523242e-02 9.7951952e-08 3.1233596e-02 -2.5000000e-01 2.5000000e-01 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qyy] 2.1869522e+04 8.3929867e-02 3.1266048e-02 -1.6666667e-01 3.3333333e-01 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -154,11 +154,11 @@ Colloid velocities - x y z Scalars - total mean variance min max [rho] 260569.00 1.00000000000 1.3737886e-08 0.99540262959 1.00081057824 -[phi] 2.1552657e+04 8.2713819e-02 3.1264223e-02 -1.6677639e-01 3.3397529e-01 -[phi] 2.5514676e-02 9.7919077e-08 3.1231097e-02 -2.5040446e-01 2.5040445e-01 -[phi] 3.5656963e-15 1.3684269e-20 1.3263027e-08 -7.0686683e-03 7.0686683e-03 -[phi] 2.1863610e+04 8.3907181e-02 3.1263429e-02 -1.6726082e-01 3.3376209e-01 -[phi] 1.0729669e-15 4.1177842e-21 1.6878684e-08 -7.5822286e-03 7.5822286e-03 +[Qxx] 2.1552657e+04 8.2713819e-02 3.1264223e-02 -1.6677639e-01 3.3397529e-01 +[Qxy] 2.5514676e-02 9.7919077e-08 3.1231097e-02 -2.5040446e-01 2.5040445e-01 +[Qxz] 3.5656963e-15 1.3684269e-20 1.3263027e-08 -7.0686683e-03 7.0686683e-03 +[Qyy] 2.1863610e+04 8.3907181e-02 3.1263429e-02 -1.6726082e-01 3.3376209e-01 +[Qyz] 1.0729669e-15 4.1177842e-21 1.6878684e-08 -7.5822286e-03 7.5822286e-03 Free energies - timestep f v f/v f_s a f_s/a [fe] 5 -1.7258433555e+01 2.6056900000e+05 -6.6233640822e-05 4.2196306291e-01 1.0140000000e+03 4.1613714291e-04 diff --git a/tests/regression/d3q19-short/serial-chol-n02.log b/tests/regression/d3q19-short/serial-chol-n02.log index 24ce9c0c9..bdb069326 100644 --- a/tests/regression/d3q19-short/serial-chol-n02.log +++ b/tests/regression/d3q19-short/serial-chol-n02.log @@ -141,11 +141,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 258994.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] -4.3165667e+04 -1.6666667e-01 -7.7306217e-14 -1.6666667e-01 -1.6666667e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 2.1271805e+04 8.2132426e-02 3.1281495e-02 -1.6666667e-01 3.3333333e-01 -[phi] 1.8665093e-02 7.2067665e-08 3.1217063e-02 -2.5000000e-01 2.5000000e-01 +[Qxx] -4.3165667e+04 -1.6666667e-01 -7.7306217e-14 -1.6666667e-01 -1.6666667e-01 +[Qxy] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qyy] 2.1271805e+04 8.2132426e-02 3.1281495e-02 -1.6666667e-01 3.3333333e-01 +[Qyz] 1.8665093e-02 7.2067665e-08 3.1217063e-02 -2.5000000e-01 2.5000000e-01 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -162,11 +162,11 @@ Colloid velocities - x y z Scalars - total mean variance min max [rho] 258994.00 1.00000000000 2.6341434e-08 0.99544798423 1.00149280815 -[phi] -4.3152703e+04 -1.6661661e-01 5.8269849e-08 -1.6740144e-01 -1.5670750e-01 -[phi] -2.3050805e-15 -8.9001309e-21 2.6690464e-08 -7.0823033e-03 7.0823033e-03 -[phi] 4.8169716e-15 1.8598777e-20 3.3769091e-08 -7.5924269e-03 7.5924269e-03 -[phi] 2.1265219e+04 8.2106995e-02 3.1278470e-02 -1.6690066e-01 3.3397530e-01 -[phi] 1.8653836e-02 7.2024203e-08 3.1212676e-02 -2.5040482e-01 2.5040481e-01 +[Qxx] -4.3152703e+04 -1.6661661e-01 5.8269849e-08 -1.6740144e-01 -1.5670750e-01 +[Qxy] -2.3050805e-15 -8.9001309e-21 2.6690464e-08 -7.0823033e-03 7.0823033e-03 +[Qxz] 4.8169716e-15 1.8598777e-20 3.3769091e-08 -7.5924269e-03 7.5924269e-03 +[Qyy] 2.1265219e+04 8.2106995e-02 3.1278470e-02 -1.6690066e-01 3.3397530e-01 +[Qyz] 1.8653836e-02 7.2024203e-08 3.1212676e-02 -2.5040482e-01 2.5040481e-01 Free energies - timestep f v f/v f_s a f_s/a [fe] 5 -1.6870562224e+01 2.5899400000e+05 -6.5138814893e-05 8.4425272198e-01 2.0280000000e+03 4.1629818638e-04 diff --git a/tests/regression/d3q19-short/serial-chol-n03.log b/tests/regression/d3q19-short/serial-chol-n03.log index 5305f42d2..cd123b751 100644 --- a/tests/regression/d3q19-short/serial-chol-n03.log +++ b/tests/regression/d3q19-short/serial-chol-n03.log @@ -141,11 +141,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 258994.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 2.1271805e+04 8.2132426e-02 3.1281495e-02 -1.6666667e-01 3.3333333e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] -1.8665093e-02 -7.2067667e-08 3.1217063e-02 -2.5000000e-01 2.5000000e-01 -[phi] -4.3165667e+04 -1.6666667e-01 -7.7306217e-14 -1.6666667e-01 -1.6666667e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 +[Qxx] 2.1271805e+04 8.2132426e-02 3.1281495e-02 -1.6666667e-01 3.3333333e-01 +[Qxy] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxz] -1.8665093e-02 -7.2067667e-08 3.1217063e-02 -2.5000000e-01 2.5000000e-01 +[Qyy] -4.3165667e+04 -1.6666667e-01 -7.7306217e-14 -1.6666667e-01 -1.6666667e-01 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -162,11 +162,11 @@ Colloid velocities - x y z Scalars - total mean variance min max [rho] 258994.00 1.00000000000 2.6340951e-08 0.99544798423 1.00149280815 -[phi] 2.1265219e+04 8.2106995e-02 3.1278470e-02 -1.6690066e-01 3.3397530e-01 -[phi] -7.4178591e-15 -2.8641046e-20 2.6690464e-08 -7.0823033e-03 7.0823033e-03 -[phi] -1.8653836e-02 -7.2024203e-08 3.1212676e-02 -2.5040481e-01 2.5040482e-01 -[phi] -4.3152703e+04 -1.6661661e-01 5.8269888e-08 -1.6740144e-01 -1.5670750e-01 -[phi] 9.3192335e-16 3.5982430e-21 3.3769091e-08 -7.5924269e-03 7.5924269e-03 +[Qxx] 2.1265219e+04 8.2106995e-02 3.1278470e-02 -1.6690066e-01 3.3397530e-01 +[Qxy] -7.4178591e-15 -2.8641046e-20 2.6690464e-08 -7.0823033e-03 7.0823033e-03 +[Qxz] -1.8653836e-02 -7.2024203e-08 3.1212676e-02 -2.5040481e-01 2.5040482e-01 +[Qyy] -4.3152703e+04 -1.6661661e-01 5.8269888e-08 -1.6740144e-01 -1.5670750e-01 +[Qyz] 9.3192335e-16 3.5982430e-21 3.3769091e-08 -7.5924269e-03 7.5924269e-03 Free energies - timestep f v f/v f_s a f_s/a [fe] 5 -1.6870562224e+01 2.5899400000e+05 -6.5138814893e-05 8.4425272198e-01 2.0280000000e+03 4.1629818638e-04 diff --git a/tests/regression/d3q19-short/serial-chol-n04.log b/tests/regression/d3q19-short/serial-chol-n04.log index 430f20564..46cdee31e 100644 --- a/tests/regression/d3q19-short/serial-chol-n04.log +++ b/tests/regression/d3q19-short/serial-chol-n04.log @@ -141,11 +141,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 258994.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 2.1271805e+04 8.2132426e-02 3.1281495e-02 -1.6666667e-01 3.3333333e-01 -[phi] 1.8665093e-02 7.2067667e-08 3.1217063e-02 -2.5000000e-01 2.5000000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 2.1893861e+04 8.4534241e-02 3.1281495e-02 -1.6666667e-01 3.3333333e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] 2.1271805e+04 8.2132426e-02 3.1281495e-02 -1.6666667e-01 3.3333333e-01 +[Qxy] 1.8665093e-02 7.2067667e-08 3.1217063e-02 -2.5000000e-01 2.5000000e-01 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qyy] 2.1893861e+04 8.4534241e-02 3.1281495e-02 -1.6666667e-01 3.3333333e-01 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -162,11 +162,11 @@ Colloid velocities - x y z Scalars - total mean variance min max [rho] 258994.00 1.00000000000 2.6340954e-08 0.99544798423 1.00149280815 -[phi] 2.1265219e+04 8.2106995e-02 3.1278470e-02 -1.6690066e-01 3.3397530e-01 -[phi] 1.8653836e-02 7.2024203e-08 3.1212676e-02 -2.5040482e-01 2.5040481e-01 -[phi] 3.5318878e-15 1.3636948e-20 2.6690464e-08 -7.0823033e-03 7.0823033e-03 -[phi] 2.1887484e+04 8.4509620e-02 3.1277010e-02 -1.6726083e-01 3.3412078e-01 -[phi] 1.2298985e-15 4.7487530e-21 3.3769091e-08 -7.5924269e-03 7.5924269e-03 +[Qxx] 2.1265219e+04 8.2106995e-02 3.1278470e-02 -1.6690066e-01 3.3397530e-01 +[Qxy] 1.8653836e-02 7.2024203e-08 3.1212676e-02 -2.5040482e-01 2.5040481e-01 +[Qxz] 3.5318878e-15 1.3636948e-20 2.6690464e-08 -7.0823033e-03 7.0823033e-03 +[Qyy] 2.1887484e+04 8.4509620e-02 3.1277010e-02 -1.6726083e-01 3.3412078e-01 +[Qyz] 1.2298985e-15 4.7487530e-21 3.3769091e-08 -7.5924269e-03 7.5924269e-03 Free energies - timestep f v f/v f_s a f_s/a [fe] 5 -1.6870562224e+01 2.5899400000e+05 -6.5138814893e-05 8.4425272198e-01 2.0280000000e+03 4.1629818638e-04 diff --git a/tests/regression/d3q19-short/serial-chol-p01.log b/tests/regression/d3q19-short/serial-chol-p01.log index 07ac7dc61..3d83d3ea3 100644 --- a/tests/regression/d3q19-short/serial-chol-p01.log +++ b/tests/regression/d3q19-short/serial-chol-p01.log @@ -135,11 +135,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 260569.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 2.1558645e+04 8.2736799e-02 3.1266048e-02 -1.6666667e-01 3.3333333e-01 -[phi] 2.7809292e-02 1.0672525e-07 3.1233596e-02 -2.5000000e-01 2.5000000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 2.1869522e+04 8.3929867e-02 3.1266048e-02 -1.6666667e-01 3.3333333e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] 2.1558645e+04 8.2736799e-02 3.1266048e-02 -1.6666667e-01 3.3333333e-01 +[Qxy] 2.7809292e-02 1.0672525e-07 3.1233596e-02 -2.5000000e-01 2.5000000e-01 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qyy] 2.1869522e+04 8.3929867e-02 3.1266048e-02 -1.6666667e-01 3.3333333e-01 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -156,11 +156,11 @@ Colloid velocities - x y z Scalars - total mean variance min max [rho] 260569.00 1.00000000000 3.3844305e-10 0.99962709849 1.00087765729 -[phi] 2.1552649e+04 8.2713788e-02 3.1264471e-02 -1.6671722e-01 3.3341082e-01 -[phi] 2.7797868e-02 1.0668141e-07 3.1231897e-02 -2.5010500e-01 2.5010500e-01 -[phi] 3.7963496e-15 1.4569460e-20 1.1183254e-09 -2.8944174e-03 2.8944174e-03 -[phi] 2.1864111e+04 8.3909103e-02 3.1264604e-02 -1.6674308e-01 3.3337179e-01 -[phi] 1.6783262e-15 6.4410050e-21 3.1708578e-09 -4.3312639e-03 4.3312639e-03 +[Qxx] 2.1552649e+04 8.2713788e-02 3.1264471e-02 -1.6671722e-01 3.3341082e-01 +[Qxy] 2.7797868e-02 1.0668141e-07 3.1231897e-02 -2.5010500e-01 2.5010500e-01 +[Qxz] 3.7963496e-15 1.4569460e-20 1.1183254e-09 -2.8944174e-03 2.8944174e-03 +[Qyy] 2.1864111e+04 8.3909103e-02 3.1264604e-02 -1.6674308e-01 3.3337179e-01 +[Qyz] 1.6783262e-15 6.4410050e-21 3.1708578e-09 -4.3312639e-03 4.3312639e-03 Free energies - timestep f v f/v f_s a f_s/a [fe] 5 -1.7466225231e+01 2.6056900000e+05 -6.7031094380e-05 1.4963681906e-01 1.0140000000e+03 1.4757082747e-04 diff --git a/tests/regression/d3q19-short/serial-chol-st1.log b/tests/regression/d3q19-short/serial-chol-st1.log index 98ab6fc41..61f8ac8a5 100644 --- a/tests/regression/d3q19-short/serial-chol-st1.log +++ b/tests/regression/d3q19-short/serial-chol-st1.log @@ -147,11 +147,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 32663.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 1.1604113e-03 3.5526783e-08 2.0055948e-09 -5.0000000e-05 9.9999628e-05 -[phi] 1.0906567e-02 3.3391199e-07 1.5029505e-09 -7.4986395e-05 7.4987285e-05 -[phi] -1.6557474e-02 -5.0691836e-07 1.4928613e-09 -7.4994285e-05 7.4987352e-05 -[phi] -9.1437873e-03 -2.7994328e-07 1.9647331e-09 -5.0000000e-05 9.9995019e-05 -[phi] 2.1448907e-03 6.5667290e-08 1.5181251e-09 -7.4997452e-05 7.4998120e-05 +[Qxx] 1.1604113e-03 3.5526783e-08 2.0055948e-09 -5.0000000e-05 9.9999628e-05 +[Qxy] 1.0906567e-02 3.3391199e-07 1.5029505e-09 -7.4986395e-05 7.4987285e-05 +[Qxz] -1.6557474e-02 -5.0691836e-07 1.4928613e-09 -7.4994285e-05 7.4987352e-05 +[Qyy] -9.1437873e-03 -2.7994328e-07 1.9647331e-09 -5.0000000e-05 9.9995019e-05 +[Qyz] 2.1448907e-03 6.5667290e-08 1.5181251e-09 -7.4997452e-05 7.4998120e-05 Momentum - x y z [total ] 4.5329018e-13 0.0000000e+00 0.0000000e+00 @@ -170,11 +170,11 @@ Colloid velocities - x y z Scalars - total mean variance min max [rho] 32662.00 1.00000000000 9.8038422e-10 0.99950225520 1.00032483656 -[phi] -2.3253103e-01 -7.1193140e-06 7.2047424e-08 -8.7209250e-03 9.7276954e-03 -[phi] 1.0763449e-02 3.2954043e-07 9.2035557e-08 -1.0627204e-02 1.0556469e-02 -[phi] -1.6512964e-02 -5.0557112e-07 9.2101818e-08 -1.0591286e-02 1.0606404e-02 -[phi] 1.0771971e-01 3.2980132e-06 1.0113391e-07 -5.8549799e-03 1.3610596e-02 -[phi] 2.1670320e-03 6.6347194e-08 1.3270983e-07 -1.3455774e-02 1.3464588e-02 +[Qxx] -2.3253103e-01 -7.1193140e-06 7.2047424e-08 -8.7209250e-03 9.7276954e-03 +[Qxy] 1.0763449e-02 3.2954043e-07 9.2035557e-08 -1.0627204e-02 1.0556469e-02 +[Qxz] -1.6512964e-02 -5.0557112e-07 9.2101818e-08 -1.0591286e-02 1.0606404e-02 +[Qyy] 1.0771971e-01 3.2980132e-06 1.0113391e-07 -5.8549799e-03 1.3610596e-02 +[Qyz] 2.1670320e-03 6.6347194e-08 1.3270983e-07 -1.3455774e-02 1.3464588e-02 Free energies - timestep f v f/v f_s a f_s/a [fe] 20 8.5627643742e-04 3.2662000000e+04 2.6216289187e-08 3.5558486805e-02 2.1800000000e+02 1.6311232479e-04 diff --git a/tests/regression/d3q19-short/serial-chol-st2.log b/tests/regression/d3q19-short/serial-chol-st2.log index 099c0031b..3da66f935 100644 --- a/tests/regression/d3q19-short/serial-chol-st2.log +++ b/tests/regression/d3q19-short/serial-chol-st2.log @@ -147,11 +147,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 16332.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 1.0145331e-04 6.2119344e-09 1.9970849e-09 -5.0000000e-05 9.9927698e-05 -[phi] 8.0005280e-03 4.8986824e-07 1.5016377e-09 -7.4981012e-05 7.4987285e-05 -[phi] -1.0191371e-02 -6.2401243e-07 1.5009586e-09 -7.4994285e-05 7.4987352e-05 -[phi] -5.1697246e-03 -3.1653959e-07 1.9621852e-09 -5.0000000e-05 9.9979055e-05 -[phi] -2.4078974e-04 -1.4743433e-08 1.5200098e-09 -7.4985154e-05 7.4998120e-05 +[Qxx] 1.0145331e-04 6.2119344e-09 1.9970849e-09 -5.0000000e-05 9.9927698e-05 +[Qxy] 8.0005280e-03 4.8986824e-07 1.5016377e-09 -7.4981012e-05 7.4987285e-05 +[Qxz] -1.0191371e-02 -6.2401243e-07 1.5009586e-09 -7.4994285e-05 7.4987352e-05 +[Qyy] -5.1697246e-03 -3.1653959e-07 1.9621852e-09 -5.0000000e-05 9.9979055e-05 +[Qyz] -2.4078974e-04 -1.4743433e-08 1.5200098e-09 -7.4985154e-05 7.4998120e-05 Momentum - x y z [total ] 2.2665203e-13 0.0000000e+00 0.0000000e+00 @@ -169,11 +169,11 @@ Colloid velocities - x y z Scalars - total mean variance min max [rho] 16331.00 1.00000000000 8.0200913e-10 0.99975796761 1.00102806657 -[phi] 1.3299377e+01 8.1436389e-04 4.0028897e-06 -8.7498846e-03 6.1220572e-03 -[phi] 7.9857001e-03 4.8899027e-07 8.8733714e-08 -1.0744226e-02 1.0750290e-02 -[phi] -1.0219360e-02 -6.2576451e-07 8.8608923e-08 -1.0785523e-02 1.0775357e-02 -[phi] -6.6548322e+00 -4.0749692e-04 1.0848011e-06 -6.0884448e-03 1.3677595e-02 -[phi] -2.2652574e-04 -1.3870905e-08 1.3305503e-07 -1.3459337e-02 1.3460328e-02 +[Qxx] 1.3299377e+01 8.1436389e-04 4.0028897e-06 -8.7498846e-03 6.1220572e-03 +[Qxy] 7.9857001e-03 4.8899027e-07 8.8733714e-08 -1.0744226e-02 1.0750290e-02 +[Qxz] -1.0219360e-02 -6.2576451e-07 8.8608923e-08 -1.0785523e-02 1.0775357e-02 +[Qyy] -6.6548322e+00 -4.0749692e-04 1.0848011e-06 -6.0884448e-03 1.3677595e-02 +[Qyz] -2.2652574e-04 -1.3870905e-08 1.3305503e-07 -1.3459337e-02 1.3460328e-02 Free energies - timestep f v f/v f_s1 fs_s2 redshift [fe] 20 1.2196018159e-02 1.6331000000e+04 7.4680167526e-07 1.7334752024e-01 1.7329399846e-01 1.0000000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-st3.log b/tests/regression/d3q19-short/serial-chol-st3.log index dd6887681..e48f5d193 100644 --- a/tests/regression/d3q19-short/serial-chol-st3.log +++ b/tests/regression/d3q19-short/serial-chol-st3.log @@ -110,11 +110,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 -[phi] 2.4576000e+03 7.4999999e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 -[phi] -3.1310879e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 -[phi] 2.4576000e+03 7.5000001e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] 2.4576000e+03 7.4999999e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 +[Qxy] -3.1310879e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 +[Qyy] 2.4576000e+03 7.5000001e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] -4.2632564e-14 -2.6093711e-14 0.0000000e+00 @@ -124,11 +124,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 8.3266727e-14 0.99999947685 1.00000053230 -[phi] 2.4579631e+03 7.5011081e-02 2.5502115e-02 -1.5073458e-01 3.0075664e-01 -[phi] -7.3689943e-06 -2.2488386e-10 2.5502115e-02 -2.2574577e-01 2.2574545e-01 -[phi] -6.4186464e-15 -1.9588154e-19 2.1221305e-10 -4.3245127e-05 4.3245127e-05 -[phi] 2.4579634e+03 7.5011090e-02 2.5502115e-02 -1.5073467e-01 3.0075655e-01 -[phi] -7.2327281e-15 -2.2072535e-19 1.6964642e-10 -3.8126680e-05 3.8126680e-05 +[Qxx] 2.4579631e+03 7.5011081e-02 2.5502115e-02 -1.5073458e-01 3.0075664e-01 +[Qxy] -7.3689943e-06 -2.2488386e-10 2.5502115e-02 -2.2574577e-01 2.2574545e-01 +[Qxz] -6.4186464e-15 -1.9588154e-19 2.1221305e-10 -4.3245127e-05 4.3245127e-05 +[Qyy] 2.4579634e+03 7.5011090e-02 2.5502115e-02 -1.5073467e-01 3.0075655e-01 +[Qyz] -7.2327281e-15 -2.2072535e-19 1.6964642e-10 -3.8126680e-05 3.8126680e-05 Free energies - timestep f v f/v f_bulk/v f_grad/v redshift [fe] 10 -2.4538559501e+00 3.2768000000e+04 -7.4885740665e-05 -8.7261882304e-05 1.2376141639e-05 1.0000000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-st4.log b/tests/regression/d3q19-short/serial-chol-st4.log index 80455b64d..ccb69bb3f 100644 --- a/tests/regression/d3q19-short/serial-chol-st4.log +++ b/tests/regression/d3q19-short/serial-chol-st4.log @@ -110,11 +110,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 -[phi] 2.4576000e+03 7.4999999e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 -[phi] -3.1310879e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 -[phi] 2.4576000e+03 7.5000001e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] 2.4576000e+03 7.4999999e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 +[Qxy] -3.1310879e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 +[Qyy] 2.4576000e+03 7.5000001e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] -4.2632564e-14 -2.6093711e-14 0.0000000e+00 @@ -124,11 +124,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 4.9293902e-14 0.99999947685 1.00000053222 -[phi] 2.4579631e+03 7.5011081e-02 2.5502129e-02 -1.5073464e-01 3.0075664e-01 -[phi] -7.1282745e-06 -2.1753767e-10 2.5502129e-02 -2.2574580e-01 2.2574548e-01 -[phi] -1.3765417e-15 -4.2008718e-20 2.1221389e-10 -4.3245136e-05 4.3245136e-05 -[phi] 2.4579634e+03 7.5011090e-02 2.5502129e-02 -1.5073467e-01 3.0075661e-01 -[phi] -1.1221214e-15 -3.4244429e-20 1.6964743e-10 -3.8126592e-05 3.8126592e-05 +[Qxx] 2.4579631e+03 7.5011081e-02 2.5502129e-02 -1.5073464e-01 3.0075664e-01 +[Qxy] -7.1282745e-06 -2.1753767e-10 2.5502129e-02 -2.2574580e-01 2.2574548e-01 +[Qxz] -1.3765417e-15 -4.2008718e-20 2.1221389e-10 -4.3245136e-05 4.3245136e-05 +[Qyy] 2.4579634e+03 7.5011090e-02 2.5502129e-02 -1.5073467e-01 3.0075661e-01 +[Qyz] -1.1221214e-15 -3.4244429e-20 1.6964743e-10 -3.8126592e-05 3.8126592e-05 Free energies - timestep f v f/v f_bulk/v f_grad/v redshift [fe] 10 -2.4538566291e+00 3.2768000000e+04 -7.4885761385e-05 -8.7261903333e-05 1.2376141948e-05 1.0000000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-st5.log b/tests/regression/d3q19-short/serial-chol-st5.log index eec8de91d..12a12cd23 100644 --- a/tests/regression/d3q19-short/serial-chol-st5.log +++ b/tests/regression/d3q19-short/serial-chol-st5.log @@ -110,11 +110,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 -[phi] 2.4576000e+03 7.4999999e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 -[phi] -3.1310879e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 -[phi] 2.4576000e+03 7.5000001e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] 2.4576000e+03 7.4999999e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 +[Qxy] -3.1310879e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 +[Qyy] 2.4576000e+03 7.5000001e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] -4.2632564e-14 -2.6093711e-14 0.0000000e+00 @@ -124,11 +124,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 1.3877788e-13 0.99999947684 1.00000053224 -[phi] 2.4579631e+03 7.5011081e-02 2.5502129e-02 -1.5073464e-01 3.0075664e-01 -[phi] -7.1293497e-06 -2.1757049e-10 2.5502129e-02 -2.2574580e-01 2.2574548e-01 -[phi] -5.2662946e-15 -1.6071456e-19 2.1221307e-10 -4.3245131e-05 4.3245131e-05 -[phi] 2.4579634e+03 7.5011090e-02 2.5502129e-02 -1.5073467e-01 3.0075661e-01 -[phi] -4.7291339e-16 -1.4432171e-20 1.6964653e-10 -3.8126445e-05 3.8126445e-05 +[Qxx] 2.4579631e+03 7.5011081e-02 2.5502129e-02 -1.5073464e-01 3.0075664e-01 +[Qxy] -7.1293497e-06 -2.1757049e-10 2.5502129e-02 -2.2574580e-01 2.2574548e-01 +[Qxz] -5.2662946e-15 -1.6071456e-19 2.1221307e-10 -4.3245131e-05 4.3245131e-05 +[Qyy] 2.4579634e+03 7.5011090e-02 2.5502129e-02 -1.5073467e-01 3.0075661e-01 +[Qyz] -4.7291339e-16 -1.4432171e-20 1.6964653e-10 -3.8126445e-05 3.8126445e-05 Free energies - timestep f v f/v f_bulk/v f_grad/v redshift [fe] 10 -2.4538566341e+00 3.2768000000e+04 -7.4885761540e-05 -8.7261903158e-05 1.2376141618e-05 1.0000000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-st6.log b/tests/regression/d3q19-short/serial-chol-st6.log index 606f40952..aefb77263 100644 --- a/tests/regression/d3q19-short/serial-chol-st6.log +++ b/tests/regression/d3q19-short/serial-chol-st6.log @@ -110,11 +110,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 -[phi] 2.4576000e+03 7.4999999e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 -[phi] -3.1310879e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 -[phi] 2.4576000e+03 7.5000001e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] 2.4576000e+03 7.4999999e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 +[Qxy] -3.1310879e-06 -9.5553246e-11 2.5312500e-02 -2.2500000e-01 2.2500000e-01 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -0.0000000e+00 -0.0000000e+00 +[Qyy] 2.4576000e+03 7.5000001e-02 2.5312500e-02 -1.5000000e-01 3.0000000e-01 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] -4.2632564e-14 -2.6093711e-14 0.0000000e+00 @@ -124,11 +124,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 1.5831780e-13 0.99999947685 1.00000053223 -[phi] 2.4579631e+03 7.5011081e-02 2.5502129e-02 -1.5073464e-01 3.0075664e-01 -[phi] -7.1276153e-06 -2.1751756e-10 2.5502129e-02 -2.2574580e-01 2.2574548e-01 -[phi] -3.5621172e-15 -1.0870719e-19 2.1221354e-10 -4.3245134e-05 4.3245134e-05 -[phi] 2.4579634e+03 7.5011090e-02 2.5502129e-02 -1.5073467e-01 3.0075661e-01 -[phi] 2.5155436e-15 7.6768299e-20 1.6964709e-10 -3.8126522e-05 3.8126522e-05 +[Qxx] 2.4579631e+03 7.5011081e-02 2.5502129e-02 -1.5073464e-01 3.0075664e-01 +[Qxy] -7.1276153e-06 -2.1751756e-10 2.5502129e-02 -2.2574580e-01 2.2574548e-01 +[Qxz] -3.5621172e-15 -1.0870719e-19 2.1221354e-10 -4.3245134e-05 4.3245134e-05 +[Qyy] 2.4579634e+03 7.5011090e-02 2.5502129e-02 -1.5073467e-01 3.0075661e-01 +[Qyz] 2.5155436e-15 7.6768299e-20 1.6964709e-10 -3.8126522e-05 3.8126522e-05 Free energies - timestep f v f/v f_bulk/v f_grad/v redshift [fe] 10 -2.4538566341e+00 3.2768000000e+04 -7.4885761539e-05 -8.7261903334e-05 1.2376141795e-05 1.0000000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-st7.log b/tests/regression/d3q19-short/serial-chol-st7.log index 7fd244cf4..17d4746a1 100644 --- a/tests/regression/d3q19-short/serial-chol-st7.log +++ b/tests/regression/d3q19-short/serial-chol-st7.log @@ -146,11 +146,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 32663.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 1.1604113e-03 3.5526783e-08 2.0055948e-09 -5.0000000e-05 9.9999628e-05 -[phi] 1.0906567e-02 3.3391199e-07 1.5029505e-09 -7.4986395e-05 7.4987285e-05 -[phi] -1.6557474e-02 -5.0691836e-07 1.4928613e-09 -7.4994285e-05 7.4987352e-05 -[phi] -9.1437873e-03 -2.7994328e-07 1.9647331e-09 -5.0000000e-05 9.9995019e-05 -[phi] 2.1448907e-03 6.5667290e-08 1.5181251e-09 -7.4997452e-05 7.4998120e-05 +[Qxx] 1.1604113e-03 3.5526783e-08 2.0055948e-09 -5.0000000e-05 9.9999628e-05 +[Qxy] 1.0906567e-02 3.3391199e-07 1.5029505e-09 -7.4986395e-05 7.4987285e-05 +[Qxz] -1.6557474e-02 -5.0691836e-07 1.4928613e-09 -7.4994285e-05 7.4987352e-05 +[Qyy] -9.1437873e-03 -2.7994328e-07 1.9647331e-09 -5.0000000e-05 9.9995019e-05 +[Qyz] 2.1448907e-03 6.5667290e-08 1.5181251e-09 -7.4997452e-05 7.4998120e-05 Momentum - x y z [total ] 4.5329018e-13 0.0000000e+00 0.0000000e+00 @@ -169,11 +169,11 @@ Colloid velocities - x y z Scalars - total mean variance min max [rho] 32662.00 1.00000000000 9.8038422e-10 0.99950225520 1.00032483656 -[phi] -2.3253103e-01 -7.1193140e-06 7.2047424e-08 -8.7209250e-03 9.7276954e-03 -[phi] 1.0763449e-02 3.2954043e-07 9.2035557e-08 -1.0627204e-02 1.0556469e-02 -[phi] -1.6512964e-02 -5.0557112e-07 9.2101818e-08 -1.0591286e-02 1.0606404e-02 -[phi] 1.0771971e-01 3.2980132e-06 1.0113391e-07 -5.8549799e-03 1.3610596e-02 -[phi] 2.1670320e-03 6.6347194e-08 1.3270983e-07 -1.3455774e-02 1.3464588e-02 +[Qxx] -2.3253103e-01 -7.1193140e-06 7.2047424e-08 -8.7209250e-03 9.7276954e-03 +[Qxy] 1.0763449e-02 3.2954043e-07 9.2035557e-08 -1.0627204e-02 1.0556469e-02 +[Qxz] -1.6512964e-02 -5.0557112e-07 9.2101818e-08 -1.0591286e-02 1.0606404e-02 +[Qyy] 1.0771971e-01 3.2980132e-06 1.0113391e-07 -5.8549799e-03 1.3610596e-02 +[Qyz] 2.1670320e-03 6.6347194e-08 1.3270983e-07 -1.3455774e-02 1.3464588e-02 Free energies - timestep f v f/v f_s a f_s/a [fe] 20 8.5627643742e-04 3.2662000000e+04 2.6216289187e-08 3.5558486805e-02 2.1800000000e+02 1.6311232479e-04 diff --git a/tests/regression/d3q19-short/serial-chol-w01.log b/tests/regression/d3q19-short/serial-chol-w01.log index 1d78f23ba..9cf9056e1 100644 --- a/tests/regression/d3q19-short/serial-chol-w01.log +++ b/tests/regression/d3q19-short/serial-chol-w01.log @@ -117,11 +117,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 262144.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 9.7012768e-12 3.7007434e-17 -3.5259967e-45 3.7007434e-17 3.7007434e-17 -[phi] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 -[phi] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 -[phi] 9.7012768e-12 3.7007434e-17 -3.5259967e-45 3.7007434e-17 3.7007434e-17 -[phi] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 +[Qxx] 9.7012768e-12 3.7007434e-17 -3.5259967e-45 3.7007434e-17 3.7007434e-17 +[Qxy] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 +[Qxz] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 +[Qyy] 9.7012768e-12 3.7007434e-17 -3.5259967e-45 3.7007434e-17 3.7007434e-17 +[Qyz] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -132,11 +132,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 262144.00 1.00000000000 3.8911763e-11 0.99998183823 1.00002849919 -[phi] -2.3659264e+00 -9.0252928e-06 2.5115252e-09 -2.8805401e-04 3.6923029e-17 -[phi] 4.3682812e+04 1.6663670e-01 5.8770038e-09 1.6611543e-01 1.6665250e-01 -[phi] 4.3682812e+04 1.6663670e-01 5.8770047e-09 1.6611543e-01 1.6665250e-01 -[phi] 1.1829632e+00 4.5126464e-06 6.9139670e-10 -2.1901579e-06 1.8898600e-04 -[phi] 4.3686449e+04 1.6665058e-01 9.1139804e-12 1.6665002e-01 1.6666719e-01 +[Qxx] -2.3659264e+00 -9.0252928e-06 2.5115252e-09 -2.8805401e-04 3.6923029e-17 +[Qxy] 4.3682812e+04 1.6663670e-01 5.8770038e-09 1.6611543e-01 1.6665250e-01 +[Qxz] 4.3682812e+04 1.6663670e-01 5.8770047e-09 1.6611543e-01 1.6665250e-01 +[Qyy] 1.1829632e+00 4.5126464e-06 6.9139670e-10 -2.1901579e-06 1.8898600e-04 +[Qyz] 4.3686449e+04 1.6665058e-01 9.1139804e-12 1.6665002e-01 1.6666719e-01 Free energies - timestep f v f/v f_s1 fs_s2 redshift [fe] 2 -1.4672998738e+01 2.6214400000e+05 -5.5973048163e-05 3.0965027533e+00 3.0965027533e+00 1.0000000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-w02.log b/tests/regression/d3q19-short/serial-chol-w02.log index e48184930..1b3286872 100644 --- a/tests/regression/d3q19-short/serial-chol-w02.log +++ b/tests/regression/d3q19-short/serial-chol-w02.log @@ -117,11 +117,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 262144.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 9.7012768e-12 3.7007434e-17 -3.5259967e-45 3.7007434e-17 3.7007434e-17 -[phi] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 -[phi] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 -[phi] 9.7012768e-12 3.7007434e-17 -3.5259967e-45 3.7007434e-17 3.7007434e-17 -[phi] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 +[Qxx] 9.7012768e-12 3.7007434e-17 -3.5259967e-45 3.7007434e-17 3.7007434e-17 +[Qxy] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 +[Qxz] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 +[Qyy] 9.7012768e-12 3.7007434e-17 -3.5259967e-45 3.7007434e-17 3.7007434e-17 +[Qyz] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -132,11 +132,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 262144.00 1.00000000000 3.8567816e-11 0.99998183823 1.00002849919 -[phi] 1.1829632e+00 4.5126464e-06 6.9139670e-10 -2.1901579e-06 1.8898600e-04 -[phi] 4.3682812e+04 1.6663670e-01 5.8770076e-09 1.6611543e-01 1.6665250e-01 -[phi] 4.3686449e+04 1.6665058e-01 9.1140845e-12 1.6665002e-01 1.6666719e-01 -[phi] -2.3659264e+00 -9.0252928e-06 2.5115252e-09 -2.8805401e-04 3.6923029e-17 -[phi] 4.3682812e+04 1.6663670e-01 5.8770077e-09 1.6611543e-01 1.6665250e-01 +[Qxx] 1.1829632e+00 4.5126464e-06 6.9139670e-10 -2.1901579e-06 1.8898600e-04 +[Qxy] 4.3682812e+04 1.6663670e-01 5.8770076e-09 1.6611543e-01 1.6665250e-01 +[Qxz] 4.3686449e+04 1.6665058e-01 9.1140845e-12 1.6665002e-01 1.6666719e-01 +[Qyy] -2.3659264e+00 -9.0252928e-06 2.5115252e-09 -2.8805401e-04 3.6923029e-17 +[Qyz] 4.3682812e+04 1.6663670e-01 5.8770077e-09 1.6611543e-01 1.6665250e-01 Free energies - timestep f v f/v f_s1 fs_s2 redshift [fe] 2 -1.4672998738e+01 2.6214400000e+05 -5.5973048163e-05 3.0965027533e+00 3.0965027533e+00 1.0000000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-w03.log b/tests/regression/d3q19-short/serial-chol-w03.log index c11a159c5..5dced4a8a 100644 --- a/tests/regression/d3q19-short/serial-chol-w03.log +++ b/tests/regression/d3q19-short/serial-chol-w03.log @@ -117,11 +117,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 262144.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 9.7012768e-12 3.7007434e-17 -3.5259967e-45 3.7007434e-17 3.7007434e-17 -[phi] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 -[phi] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 -[phi] 9.7012768e-12 3.7007434e-17 -3.5259967e-45 3.7007434e-17 3.7007434e-17 -[phi] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 +[Qxx] 9.7012768e-12 3.7007434e-17 -3.5259967e-45 3.7007434e-17 3.7007434e-17 +[Qxy] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 +[Qxz] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 +[Qyy] 9.7012768e-12 3.7007434e-17 -3.5259967e-45 3.7007434e-17 3.7007434e-17 +[Qyz] 4.3690667e+04 1.6666667e-01 -7.1515710e-14 1.6666667e-01 1.6666667e-01 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -132,11 +132,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 262144.00 1.00000000000 3.8567816e-11 0.99998183823 1.00002849919 -[phi] 1.1829632e+00 4.5126464e-06 6.9139670e-10 -2.1901579e-06 1.8898600e-04 -[phi] 4.3686449e+04 1.6665058e-01 9.1140116e-12 1.6665002e-01 1.6666719e-01 -[phi] 4.3682812e+04 1.6663670e-01 5.8770076e-09 1.6611543e-01 1.6665250e-01 -[phi] 1.1829632e+00 4.5126464e-06 6.9139670e-10 -2.1901579e-06 1.8898600e-04 -[phi] 4.3682812e+04 1.6663670e-01 5.8770076e-09 1.6611543e-01 1.6665250e-01 +[Qxx] 1.1829632e+00 4.5126464e-06 6.9139670e-10 -2.1901579e-06 1.8898600e-04 +[Qxy] 4.3686449e+04 1.6665058e-01 9.1140116e-12 1.6665002e-01 1.6666719e-01 +[Qxz] 4.3682812e+04 1.6663670e-01 5.8770076e-09 1.6611543e-01 1.6665250e-01 +[Qyy] 1.1829632e+00 4.5126464e-06 6.9139670e-10 -2.1901579e-06 1.8898600e-04 +[Qyz] 4.3682812e+04 1.6663670e-01 5.8770076e-09 1.6611543e-01 1.6665250e-01 Free energies - timestep f v f/v f_s1 fs_s2 redshift [fe] 2 -1.4672998738e+01 2.6214400000e+05 -5.5973048163e-05 3.0965027533e+00 3.0965027533e+00 1.0000000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-w04.log b/tests/regression/d3q19-short/serial-chol-w04.log index a00f1f6dd..07b7fccbb 100644 --- a/tests/regression/d3q19-short/serial-chol-w04.log +++ b/tests/regression/d3q19-short/serial-chol-w04.log @@ -112,11 +112,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 65536.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 2.4253192e-12 3.7007434e-17 2.7119367e-45 3.7007434e-17 3.7007434e-17 -[phi] 1.0922667e+04 1.6666667e-01 5.5004612e-14 1.6666667e-01 1.6666667e-01 -[phi] 1.0922667e+04 1.6666667e-01 5.5004612e-14 1.6666667e-01 1.6666667e-01 -[phi] 2.4253192e-12 3.7007434e-17 2.7119367e-45 3.7007434e-17 3.7007434e-17 -[phi] 1.0922667e+04 1.6666667e-01 5.5004612e-14 1.6666667e-01 1.6666667e-01 +[Qxx] 2.4253192e-12 3.7007434e-17 2.7119367e-45 3.7007434e-17 3.7007434e-17 +[Qxy] 1.0922667e+04 1.6666667e-01 5.5004612e-14 1.6666667e-01 1.6666667e-01 +[Qxz] 1.0922667e+04 1.6666667e-01 5.5004612e-14 1.6666667e-01 1.6666667e-01 +[Qyy] 2.4253192e-12 3.7007434e-17 2.7119367e-45 3.7007434e-17 3.7007434e-17 +[Qyz] 1.0922667e+04 1.6666667e-01 5.5004612e-14 1.6666667e-01 1.6666667e-01 Momentum - x y z [total ] 9.0949470e-13 0.0000000e+00 0.0000000e+00 @@ -127,11 +127,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 65536.00 1.00000000000 1.6640631e-09 0.99979643442 1.00012987303 -[phi] -8.5752738e-01 -1.3084829e-05 7.4456802e-09 -6.5882372e-04 1.5676158e-05 -[phi] 1.0920738e+04 1.6663723e-01 7.6430207e-09 1.6596498e-01 1.6666188e-01 -[phi] 1.0920712e+04 1.6663684e-01 6.3756764e-09 1.6609743e-01 1.6666405e-01 -[phi] 1.7670244e+00 2.6962653e-05 2.4030180e-08 -5.4789149e-06 1.0804739e-03 -[phi] 1.0920691e+04 1.6663652e-01 6.0137023e-09 1.6610763e-01 1.6665448e-01 +[Qxx] -8.5752738e-01 -1.3084829e-05 7.4456802e-09 -6.5882372e-04 1.5676158e-05 +[Qxy] 1.0920738e+04 1.6663723e-01 7.6430207e-09 1.6596498e-01 1.6666188e-01 +[Qxz] 1.0920712e+04 1.6663684e-01 6.3756764e-09 1.6609743e-01 1.6666405e-01 +[Qyy] 1.7670244e+00 2.6962653e-05 2.4030180e-08 -5.4789149e-06 1.0804739e-03 +[Qyz] 1.0920691e+04 1.6663652e-01 6.0137023e-09 1.6610763e-01 1.6665448e-01 Free energies - timestep f v f/v f_s1 fs_s2 redshift [fe] 2 -3.1280977443e+00 6.5536000000e+04 -4.7730983648e-05 4.4039622513e-01 4.4027826070e-01 1.0000000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-w05.log b/tests/regression/d3q19-short/serial-chol-w05.log index c56258d1b..080cd5046 100644 --- a/tests/regression/d3q19-short/serial-chol-w05.log +++ b/tests/regression/d3q19-short/serial-chol-w05.log @@ -112,11 +112,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 65536.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 2.4253192e-12 3.7007434e-17 2.7119367e-45 3.7007434e-17 3.7007434e-17 -[phi] 1.0922667e+04 1.6666667e-01 5.5004612e-14 1.6666667e-01 1.6666667e-01 -[phi] 1.0922667e+04 1.6666667e-01 5.5004612e-14 1.6666667e-01 1.6666667e-01 -[phi] 2.4253192e-12 3.7007434e-17 2.7119367e-45 3.7007434e-17 3.7007434e-17 -[phi] 1.0922667e+04 1.6666667e-01 5.5004612e-14 1.6666667e-01 1.6666667e-01 +[Qxx] 2.4253192e-12 3.7007434e-17 2.7119367e-45 3.7007434e-17 3.7007434e-17 +[Qxy] 1.0922667e+04 1.6666667e-01 5.5004612e-14 1.6666667e-01 1.6666667e-01 +[Qxz] 1.0922667e+04 1.6666667e-01 5.5004612e-14 1.6666667e-01 1.6666667e-01 +[Qyy] 2.4253192e-12 3.7007434e-17 2.7119367e-45 3.7007434e-17 3.7007434e-17 +[Qyz] 1.0922667e+04 1.6666667e-01 5.5004612e-14 1.6666667e-01 1.6666667e-01 Momentum - x y z [total ] 9.0949470e-13 0.0000000e+00 0.0000000e+00 @@ -127,11 +127,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 65536.00 1.00000000000 1.6640631e-09 0.99979643442 1.00012987303 -[phi] 1.7670244e+00 2.6962653e-05 2.4030180e-08 -5.4789149e-06 1.0804739e-03 -[phi] 1.0920738e+04 1.6663723e-01 7.6430207e-09 1.6596498e-01 1.6666188e-01 -[phi] 1.0920691e+04 1.6663652e-01 6.0137023e-09 1.6610763e-01 1.6665448e-01 -[phi] -8.5752738e-01 -1.3084829e-05 7.4456802e-09 -6.5882372e-04 1.5676158e-05 -[phi] 1.0920712e+04 1.6663684e-01 6.3756764e-09 1.6609743e-01 1.6666405e-01 +[Qxx] 1.7670244e+00 2.6962653e-05 2.4030180e-08 -5.4789149e-06 1.0804739e-03 +[Qxy] 1.0920738e+04 1.6663723e-01 7.6430207e-09 1.6596498e-01 1.6666188e-01 +[Qxz] 1.0920691e+04 1.6663652e-01 6.0137023e-09 1.6610763e-01 1.6665448e-01 +[Qyy] -8.5752738e-01 -1.3084829e-05 7.4456802e-09 -6.5882372e-04 1.5676158e-05 +[Qyz] 1.0920712e+04 1.6663684e-01 6.3756764e-09 1.6609743e-01 1.6666405e-01 Free energies - timestep f v f/v f_s1 fs_s2 redshift [fe] 2 -3.1280977443e+00 6.5536000000e+04 -4.7730983648e-05 4.4027826070e-01 4.4039622513e-01 1.0000000000e+00 diff --git a/tests/regression/d3q19-short/serial-drop-lc1.log b/tests/regression/d3q19-short/serial-drop-lc1.log index ab1ef1176..6923b15f3 100644 --- a/tests/regression/d3q19-short/serial-drop-lc1.log +++ b/tests/regression/d3q19-short/serial-drop-lc1.log @@ -123,11 +123,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] 2.5921966e+04 7.9107564e-01 2.8826342e-01 -9.9999854e-01 1.0000000e+00 -[phi] 2.7306856e+03 8.3333910e-02 3.1250069e-02 -1.6666667e-01 3.3333333e-01 -[phi] 3.7616399e-03 1.1479614e-07 3.1249931e-02 -2.5000000e-01 2.5000000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 2.7306478e+03 8.3332756e-02 3.1250069e-02 -1.6666667e-01 3.3333333e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] 2.7306856e+03 8.3333910e-02 3.1250069e-02 -1.6666667e-01 3.3333333e-01 +[Qxy] 3.7616399e-03 1.1479614e-07 3.1249931e-02 -2.5000000e-01 2.5000000e-01 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qyy] 2.7306478e+03 8.3332756e-02 3.1250069e-02 -1.6666667e-01 3.3333333e-01 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 @@ -138,11 +138,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 9.5145803e-05 0.98746726555 1.05551350392 [phi] 2.5921966e+04 7.9107564e-01 2.8795963e-01 -1.0382723e+00 1.0018058e+00 -[phi] 2.7273883e+03 8.3233285e-02 3.1912717e-02 -1.8810564e-01 3.8431862e-01 -[phi] 3.7242203e-03 1.1365418e-07 3.1581129e-02 -2.7910642e-01 2.7910653e-01 -[phi] -1.6875184e-15 -5.1498975e-20 4.4673829e-05 -6.2114388e-02 6.2114388e-02 -[phi] 2.7363540e+03 8.3506897e-02 3.1255597e-02 -1.9368018e-01 3.4309898e-01 -[phi] 8.1583561e-15 2.4897327e-19 6.4946775e-05 -8.0809149e-02 8.0809149e-02 +[Qxx] 2.7273883e+03 8.3233285e-02 3.1912717e-02 -1.8810564e-01 3.8431862e-01 +[Qxy] 3.7242203e-03 1.1365418e-07 3.1581129e-02 -2.7910642e-01 2.7910653e-01 +[Qxz] -1.6875184e-15 -5.1498975e-20 4.4673829e-05 -6.2114388e-02 6.2114388e-02 +[Qyy] 2.7363540e+03 8.3506897e-02 3.1255597e-02 -1.9368018e-01 3.4309898e-01 +[Qyz] 8.1583561e-15 2.4897327e-19 6.4946775e-05 -8.0809149e-02 8.0809149e-02 Free energy density - timestep total fluid [fed] 10 -1.4812782881e-02 -1.4812782881e-02 diff --git a/tests/regression/d3q19-short/serial-drop-lc2.log b/tests/regression/d3q19-short/serial-drop-lc2.log index cc0d17614..e58deb099 100644 --- a/tests/regression/d3q19-short/serial-drop-lc2.log +++ b/tests/regression/d3q19-short/serial-drop-lc2.log @@ -118,11 +118,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] 2.5921966e+04 7.9107564e-01 2.8826342e-01 -9.9999854e-01 1.0000000e+00 -[phi] 6.5536000e+03 2.0000000e-01 2.7332303e-14 2.0000000e-01 2.0000000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[phi] -3.2768000e+03 -1.0000000e-01 6.8330758e-15 -1.0000000e-01 -1.0000000e-01 -[phi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxx] 6.5536000e+03 2.0000000e-01 2.7332303e-14 2.0000000e-01 2.0000000e-01 +[Qxy] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qxz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[Qyy] -3.2768000e+03 -1.0000000e-01 6.8330758e-15 -1.0000000e-01 -1.0000000e-01 +[Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Momentum - x y z [total ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 @@ -133,11 +133,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 7.4358254e-05 0.98825685222 1.04535239220 [phi] 2.5921966e+04 7.9107564e-01 2.8784076e-01 -1.0321446e+00 1.0001387e+00 -[phi] 6.8165821e+03 2.0802558e-01 1.6595190e-05 1.8489779e-01 2.3156183e-01 -[phi] -1.7723974e-15 -5.4089276e-20 1.0388496e-05 -2.8577286e-02 2.8577286e-02 -[phi] -1.9914589e-16 -6.0774504e-21 1.0388496e-05 -2.8577286e-02 2.8577286e-02 -[phi] -3.4082910e+03 -1.0401279e-01 1.0237638e-05 -1.1602526e-01 -7.4916195e-02 -[phi] 2.7113360e-16 8.2743409e-21 6.8406602e-06 -2.1881175e-02 2.1881175e-02 +[Qxx] 6.8165821e+03 2.0802558e-01 1.6595190e-05 1.8489779e-01 2.3156183e-01 +[Qxy] -1.7723974e-15 -5.4089276e-20 1.0388496e-05 -2.8577286e-02 2.8577286e-02 +[Qxz] -1.9914589e-16 -6.0774504e-21 1.0388496e-05 -2.8577286e-02 2.8577286e-02 +[Qyy] -3.4082910e+03 -1.0401279e-01 1.0237638e-05 -1.1602526e-01 -7.4916195e-02 +[Qyz] 2.7113360e-16 8.2743409e-21 6.8406602e-06 -2.1881175e-02 2.1881175e-02 Free energy density - timestep total fluid [fed] 10 -1.4369701257e-02 -1.4369701257e-02 diff --git a/tests/regression/d3q19-short/serial-drop-lc3.log b/tests/regression/d3q19-short/serial-drop-lc3.log index 85badbe16..95679d9a3 100644 --- a/tests/regression/d3q19-short/serial-drop-lc3.log +++ b/tests/regression/d3q19-short/serial-drop-lc3.log @@ -121,11 +121,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 262144.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] 2.1068800e+05 8.0371094e-01 3.5404873e-01 -1.0000000e+00 1.0000000e+00 -[phi] -4.7213797e-14 -1.8010634e-19 6.0000000e-02 -4.4966062e-01 4.4966064e-01 -[phi] -3.1328486e-08 -1.1950869e-13 5.0000000e-02 -6.0000000e-01 6.0000000e-01 -[phi] -3.1328486e-08 -1.1950869e-13 5.0000000e-02 -6.0000000e-01 6.0000000e-01 -[phi] -4.7213797e-14 -1.8010634e-19 6.0000000e-02 -4.4966062e-01 4.4966064e-01 -[phi] -3.1328486e-08 -1.1950869e-13 5.0000000e-02 -6.0000000e-01 6.0000000e-01 +[Qxx] -4.7213797e-14 -1.8010634e-19 6.0000000e-02 -4.4966062e-01 4.4966064e-01 +[Qxy] -3.1328486e-08 -1.1950869e-13 5.0000000e-02 -6.0000000e-01 6.0000000e-01 +[Qxz] -3.1328486e-08 -1.1950869e-13 5.0000000e-02 -6.0000000e-01 6.0000000e-01 +[Qyy] -4.7213797e-14 -1.8010634e-19 6.0000000e-02 -4.4966062e-01 4.4966064e-01 +[Qyz] -3.1328486e-08 -1.1950869e-13 5.0000000e-02 -6.0000000e-01 6.0000000e-01 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -136,11 +136,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 262144.00 1.00000000000 1.4746979e-06 0.99689390182 1.00549825041 [phi] 2.1068800e+05 8.0371094e-01 3.3830387e-01 -1.0359957e+00 1.0519477e+00 -[phi] -1.5799345e+00 -6.0269719e-06 5.1151527e-02 -4.0341293e-01 4.3974912e-01 -[phi] -1.0854376e+00 -4.1406157e-06 3.9233361e-02 -5.1530839e-01 5.0911586e-01 -[phi] 4.6631554e+00 1.7788526e-05 3.9233582e-02 -5.1538782e-01 5.0911586e-01 -[phi] 1.0481852e+00 3.9985092e-06 5.1148029e-02 -4.0329345e-01 4.3959577e-01 -[phi] 3.2927506e+00 1.2560847e-05 3.9241297e-02 -5.1547337e-01 5.1696043e-01 +[Qxx] -1.5799345e+00 -6.0269719e-06 5.1151527e-02 -4.0341293e-01 4.3974912e-01 +[Qxy] -1.0854376e+00 -4.1406157e-06 3.9233361e-02 -5.1530839e-01 5.0911586e-01 +[Qxz] 4.6631554e+00 1.7788526e-05 3.9233582e-02 -5.1538782e-01 5.0911586e-01 +[Qyy] 1.0481852e+00 3.9985092e-06 5.1148029e-02 -4.0329345e-01 4.3959577e-01 +[Qyz] 3.2927506e+00 1.2560847e-05 3.9241297e-02 -5.1547337e-01 5.1696043e-01 Free energy density - timestep total fluid [fed] 2 8.5183195721e-03 8.5183195721e-03 diff --git a/tests/regression/d3q19-short/serial-drop-lc4.log b/tests/regression/d3q19-short/serial-drop-lc4.log index b37726504..bf5093a95 100644 --- a/tests/regression/d3q19-short/serial-drop-lc4.log +++ b/tests/regression/d3q19-short/serial-drop-lc4.log @@ -115,11 +115,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] 2.1632000e+04 6.6015625e-01 5.6419373e-01 -1.0000000e+00 1.0000000e+00 -[phi] -1.6694979e-14 -5.0949032e-19 6.0000000e-02 -4.4966062e-01 4.4966064e-01 -[phi] -3.7856679e-09 -1.1552942e-13 5.0000000e-02 -6.0000000e-01 6.0000000e-01 -[phi] -3.7861401e-09 -1.1554383e-13 5.0000000e-02 -6.0000000e-01 6.0000000e-01 -[phi] -3.9850068e-14 -1.2161276e-18 6.0000000e-02 -4.4966062e-01 4.4966064e-01 -[phi] -3.7858013e-09 -1.1553349e-13 5.0000000e-02 -6.0000000e-01 6.0000000e-01 +[Qxx] -1.6694979e-14 -5.0949032e-19 6.0000000e-02 -4.4966062e-01 4.4966064e-01 +[Qxy] -3.7856679e-09 -1.1552942e-13 5.0000000e-02 -6.0000000e-01 6.0000000e-01 +[Qxz] -3.7861401e-09 -1.1554383e-13 5.0000000e-02 -6.0000000e-01 6.0000000e-01 +[Qyy] -3.9850068e-14 -1.2161276e-18 6.0000000e-02 -4.4966062e-01 4.4966064e-01 +[Qyz] -3.7858013e-09 -1.1553349e-13 5.0000000e-02 -6.0000000e-01 6.0000000e-01 Momentum - x y z [total ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 @@ -130,11 +130,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 9.7313809e-04 0.95622767598 1.06633265218 [phi] 2.1632000e+04 6.6015625e-01 4.7769905e-01 -1.2135542e+00 1.1769234e+00 -[phi] 7.4284844e+00 2.2669935e-04 2.7132050e-02 -2.2969270e-01 3.8686199e-01 -[phi] 5.4714152e-01 1.6697434e-05 1.6192819e-02 -2.8941093e-01 2.8880430e-01 -[phi] 2.4804637e+00 7.5697746e-05 1.6244838e-02 -2.8953670e-01 2.8936710e-01 -[phi] -9.0434750e+00 -2.7598495e-04 2.6973430e-02 -2.2944790e-01 3.8657858e-01 -[phi] 2.6295431e-01 8.0247288e-06 1.6175635e-02 -2.8900822e-01 2.8935612e-01 +[Qxx] 7.4284844e+00 2.2669935e-04 2.7132050e-02 -2.2969270e-01 3.8686199e-01 +[Qxy] 5.4714152e-01 1.6697434e-05 1.6192819e-02 -2.8941093e-01 2.8880430e-01 +[Qxz] 2.4804637e+00 7.5697746e-05 1.6244838e-02 -2.8953670e-01 2.8936710e-01 +[Qyy] -9.0434750e+00 -2.7598495e-04 2.6973430e-02 -2.2944790e-01 3.8657858e-01 +[Qyz] 2.6295431e-01 8.0247288e-06 1.6175635e-02 -2.8900822e-01 2.8935612e-01 Free energy density - timestep total fluid [fed] 10 1.1704103980e-03 1.1704103980e-03 diff --git a/tests/regression/d3q19-short/serial-drop-lc5.log b/tests/regression/d3q19-short/serial-drop-lc5.log index 209a6caf7..4c3d6e2db 100644 --- a/tests/regression/d3q19-short/serial-drop-lc5.log +++ b/tests/regression/d3q19-short/serial-drop-lc5.log @@ -115,11 +115,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] 1.8176000e+04 5.5468750e-01 6.9232178e-01 -1.0000000e+00 1.0000000e+00 -[phi] -8.6591172e-15 -2.6425529e-19 6.0000000e-02 -4.4966055e-01 4.4966059e-01 -[phi] -3.8895517e+03 -1.1869970e-01 3.8159319e-02 -6.0000000e-01 4.8051711e-01 -[phi] -3.8895517e+03 -1.1869970e-01 3.8159319e-02 -6.0000000e-01 4.8051711e-01 -[phi] -8.6591172e-15 -2.6425529e-19 6.0000000e-02 -4.4966055e-01 4.4966059e-01 -[phi] -3.8895517e+03 -1.1869970e-01 3.8159319e-02 -6.0000000e-01 4.8051711e-01 +[Qxx] -8.6591172e-15 -2.6425529e-19 6.0000000e-02 -4.4966055e-01 4.4966059e-01 +[Qxy] -3.8895517e+03 -1.1869970e-01 3.8159319e-02 -6.0000000e-01 4.8051711e-01 +[Qxz] -3.8895517e+03 -1.1869970e-01 3.8159319e-02 -6.0000000e-01 4.8051711e-01 +[Qyy] -8.6591172e-15 -2.6425529e-19 6.0000000e-02 -4.4966055e-01 4.4966059e-01 +[Qyz] -3.8895517e+03 -1.1869970e-01 3.8159319e-02 -6.0000000e-01 4.8051711e-01 Momentum - x y z [total ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 @@ -130,11 +130,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 1.6301405e-03 0.85000305336 1.17752883473 [phi] 1.8176000e+04 5.5468750e-01 5.7963779e-01 -1.1993468e+00 1.1461579e+00 -[phi] -6.7864586e+00 -2.0710628e-04 2.9475128e-02 -3.1570600e-01 4.1288537e-01 -[phi] -2.6280528e+03 -8.0201806e-02 1.4325873e-02 -3.6748237e-01 2.5564799e-01 -[phi] -2.6262098e+03 -8.0145564e-02 1.4272032e-02 -3.6817492e-01 2.4723219e-01 -[phi] 3.7136747e+00 1.1333236e-04 2.9527690e-02 -3.1423372e-01 4.0053667e-01 -[phi] -2.6169331e+03 -7.9862460e-02 1.4397958e-02 -3.7011221e-01 2.5798629e-01 +[Qxx] -6.7864586e+00 -2.0710628e-04 2.9475128e-02 -3.1570600e-01 4.1288537e-01 +[Qxy] -2.6280528e+03 -8.0201806e-02 1.4325873e-02 -3.6748237e-01 2.5564799e-01 +[Qxz] -2.6262098e+03 -8.0145564e-02 1.4272032e-02 -3.6817492e-01 2.4723219e-01 +[Qyy] 3.7136747e+00 1.1333236e-04 2.9527690e-02 -3.1423372e-01 4.0053667e-01 +[Qyz] -2.6169331e+03 -7.9862460e-02 1.4397958e-02 -3.7011221e-01 2.5798629e-01 Free energy density - timestep total fluid [fed] 10 -1.5818799155e-03 -1.5818799155e-03 diff --git a/tests/regression/d3q19-short/serial-init-bp1.log b/tests/regression/d3q19-short/serial-init-bp1.log index f0ffa37c7..c4bf36bc2 100644 --- a/tests/regression/d3q19-short/serial-init-bp1.log +++ b/tests/regression/d3q19-short/serial-init-bp1.log @@ -89,11 +89,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] -7.4301676e-14 -2.2675072e-18 6.0000000e-02 -4.4966058e-01 4.4966058e-01 -[phi] -1.4009349e-12 -4.2753141e-17 5.0000000e-02 -6.0000000e-01 6.0000000e-01 -[phi] -1.2735923e-12 -3.8866954e-17 5.0000000e-02 -6.0000000e-01 6.0000000e-01 -[phi] -2.5340841e-14 -7.7334108e-19 6.0000000e-02 -4.4966058e-01 4.4966058e-01 -[phi] -1.4913071e-12 -4.5511080e-17 5.0000000e-02 -6.0000000e-01 6.0000000e-01 +[Qxx] -7.4301676e-14 -2.2675072e-18 6.0000000e-02 -4.4966058e-01 4.4966058e-01 +[Qxy] -1.4009349e-12 -4.2753141e-17 5.0000000e-02 -6.0000000e-01 6.0000000e-01 +[Qxz] -1.2735923e-12 -3.8866954e-17 5.0000000e-02 -6.0000000e-01 6.0000000e-01 +[Qyy] -2.5340841e-14 -7.7334108e-19 6.0000000e-02 -4.4966058e-01 4.4966058e-01 +[Qyz] -1.4913071e-12 -4.5511080e-17 5.0000000e-02 -6.0000000e-01 6.0000000e-01 Momentum - x y z [total ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 @@ -103,11 +103,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 1.3705446e-05 0.99574253388 1.01118305550 -[phi] -1.7735832e-14 -5.4125464e-19 5.9699031e-02 -4.4806579e-01 4.4927991e-01 -[phi] 6.9706194e-08 2.1272642e-12 4.9622512e-02 -5.9696088e-01 5.9696088e-01 -[phi] 6.9706022e-08 2.1272590e-12 4.9622512e-02 -5.9696088e-01 5.9696088e-01 -[phi] -9.9031913e-14 -3.0222141e-18 5.9699031e-02 -4.4806579e-01 4.4927991e-01 -[phi] 6.9706013e-08 2.1272587e-12 4.9622512e-02 -5.9696088e-01 5.9696088e-01 +[Qxx] -1.7735832e-14 -5.4125464e-19 5.9699031e-02 -4.4806579e-01 4.4927991e-01 +[Qxy] 6.9706194e-08 2.1272642e-12 4.9622512e-02 -5.9696088e-01 5.9696088e-01 +[Qxz] 6.9706022e-08 2.1272590e-12 4.9622512e-02 -5.9696088e-01 5.9696088e-01 +[Qyy] -9.9031913e-14 -3.0222141e-18 5.9699031e-02 -4.4806579e-01 4.4927991e-01 +[Qyz] 6.9706013e-08 2.1272587e-12 4.9622512e-02 -5.9696088e-01 5.9696088e-01 Free energies - timestep f v f/v f_bulk/v f_grad/v redshift [fe] 1 1.3519678062e+02 3.2768000000e+04 4.1258783149e-03 4.0997520325e-03 2.6126282398e-05 8.3000000000e-01 diff --git a/tests/regression/d3q19-short/serial-init-bp2.log b/tests/regression/d3q19-short/serial-init-bp2.log index 2afb90b7b..8e7e46c49 100644 --- a/tests/regression/d3q19-short/serial-init-bp2.log +++ b/tests/regression/d3q19-short/serial-init-bp2.log @@ -89,11 +89,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 0.0000000e+00 0.0000000e+00 8.9999982e-02 -6.0000000e-01 6.0000000e-01 -[phi] -4.0156293e-04 -1.2254728e-08 4.5000009e-02 -3.0000000e-01 3.0000000e-01 -[phi] -4.0156293e-04 -1.2254728e-08 4.5000009e-02 -3.0000000e-01 3.0000000e-01 -[phi] 0.0000000e+00 0.0000000e+00 8.9999982e-02 -6.0000000e-01 6.0000000e-01 -[phi] -4.0156293e-04 -1.2254726e-08 4.5000009e-02 -3.0000000e-01 3.0000000e-01 +[Qxx] 0.0000000e+00 0.0000000e+00 8.9999982e-02 -6.0000000e-01 6.0000000e-01 +[Qxy] -4.0156293e-04 -1.2254728e-08 4.5000009e-02 -3.0000000e-01 3.0000000e-01 +[Qxz] -4.0156293e-04 -1.2254728e-08 4.5000009e-02 -3.0000000e-01 3.0000000e-01 +[Qyy] 0.0000000e+00 0.0000000e+00 8.9999982e-02 -6.0000000e-01 6.0000000e-01 +[Qyz] -4.0156293e-04 -1.2254726e-08 4.5000009e-02 -3.0000000e-01 3.0000000e-01 Momentum - x y z [total ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 @@ -103,11 +103,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 5.5946818e-07 0.99868639745 1.00197940975 -[phi] 2.5125264e-15 7.6676219e-20 8.9762988e-02 -5.9884923e-01 5.9934603e-01 -[phi] -4.0099021e-04 -1.2237250e-08 4.4898274e-02 -2.9996132e-01 2.9996132e-01 -[phi] -4.0099021e-04 -1.2237250e-08 4.4898274e-02 -2.9996132e-01 2.9996132e-01 -[phi] 9.8944335e-16 3.0195415e-20 8.9762988e-02 -5.9884923e-01 5.9934603e-01 -[phi] -4.0099021e-04 -1.2237250e-08 4.4898274e-02 -2.9996132e-01 2.9996132e-01 +[Qxx] 2.5125264e-15 7.6676219e-20 8.9762988e-02 -5.9884923e-01 5.9934603e-01 +[Qxy] -4.0099021e-04 -1.2237250e-08 4.4898274e-02 -2.9996132e-01 2.9996132e-01 +[Qxz] -4.0099021e-04 -1.2237250e-08 4.4898274e-02 -2.9996132e-01 2.9996132e-01 +[Qyy] 9.8944335e-16 3.0195415e-20 8.9762988e-02 -5.9884923e-01 5.9934603e-01 +[Qyz] -4.0099021e-04 -1.2237250e-08 4.4898274e-02 -2.9996132e-01 2.9996132e-01 Free energies - timestep f v f/v f_bulk/v f_grad/v redshift [fe] 1 5.3564492642e+01 3.2768000000e+04 1.6346585889e-03 1.6242150891e-03 1.0443499818e-05 9.1000000000e-01 diff --git a/tests/regression/d3q19-short/serial-init-br1.log b/tests/regression/d3q19-short/serial-init-br1.log index 068d82acc..d1124e8e3 100644 --- a/tests/regression/d3q19-short/serial-init-br1.log +++ b/tests/regression/d3q19-short/serial-init-br1.log @@ -99,11 +99,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 3.8245084e-13 1.1671473e-17 6.0000000e-02 -4.4966058e-01 4.4966058e-01 -[phi] -1.2562174e-12 -3.8336711e-17 5.0000000e-02 -6.0000000e-01 6.0000000e-01 -[phi] -1.2730927e-12 -3.8851707e-17 5.0000000e-02 -6.0000000e-01 6.0000000e-01 -[phi] 4.1466119e-13 1.2654455e-17 6.0000000e-02 -4.4966058e-01 4.4966058e-01 -[phi] -1.5174528e-12 -4.6308985e-17 5.0000000e-02 -6.0000000e-01 6.0000000e-01 +[Qxx] 3.8245084e-13 1.1671473e-17 6.0000000e-02 -4.4966058e-01 4.4966058e-01 +[Qxy] -1.2562174e-12 -3.8336711e-17 5.0000000e-02 -6.0000000e-01 6.0000000e-01 +[Qxz] -1.2730927e-12 -3.8851707e-17 5.0000000e-02 -6.0000000e-01 6.0000000e-01 +[Qyy] 4.1466119e-13 1.2654455e-17 6.0000000e-02 -4.4966058e-01 4.4966058e-01 +[Qyz] -1.5174528e-12 -4.6308985e-17 5.0000000e-02 -6.0000000e-01 6.0000000e-01 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -113,11 +113,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 1.9600713e-05 0.98788823843 1.01290514128 -[phi] 4.5462961e-13 1.3874195e-17 5.9669179e-02 -4.4806490e-01 4.4918683e-01 -[phi] 6.9706253e-08 2.1272660e-12 4.9602643e-02 -5.9681120e-01 5.9681120e-01 -[phi] -6.9708854e-08 -2.1273454e-12 4.9582755e-02 -5.9673636e-01 5.9673636e-01 -[phi] 2.5597693e-13 7.8117961e-18 5.9669179e-02 -4.4806490e-01 4.4918683e-01 -[phi] 6.9705961e-08 2.1272571e-12 4.9582755e-02 -5.9673636e-01 5.9673636e-01 +[Qxx] 4.5462961e-13 1.3874195e-17 5.9669179e-02 -4.4806490e-01 4.4918683e-01 +[Qxy] 6.9706253e-08 2.1272660e-12 4.9602643e-02 -5.9681120e-01 5.9681120e-01 +[Qxz] -6.9708854e-08 -2.1273454e-12 4.9582755e-02 -5.9673636e-01 5.9673636e-01 +[Qyy] 2.5597693e-13 7.8117961e-18 5.9669179e-02 -4.4806490e-01 4.4918683e-01 +[Qyz] 6.9705961e-08 2.1272571e-12 4.9582755e-02 -5.9673636e-01 5.9673636e-01 Free energies - timestep f v f/v f_bulk/v f_grad/v redshift [fe] 1 1.6108165051e+02 3.2768000000e+04 4.9158218539e-03 4.0945666196e-03 8.2125523425e-04 8.3000000000e-01 diff --git a/tests/regression/d3q19-short/serial-init-br2.log b/tests/regression/d3q19-short/serial-init-br2.log index 12620c991..219cb486e 100644 --- a/tests/regression/d3q19-short/serial-init-br2.log +++ b/tests/regression/d3q19-short/serial-init-br2.log @@ -99,11 +99,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] -1.3851975e-13 -4.2272873e-18 8.4935422e-02 -5.9727840e-01 5.9727840e-01 -[phi] -1.3067714e-11 -3.9879497e-16 4.5000000e-02 -3.0000000e-01 3.0000000e-01 -[phi] -1.2947032e-11 -3.9511207e-16 4.5000000e-02 -3.0000000e-01 3.0000000e-01 -[phi] 4.4785463e+02 1.3667439e-02 9.3490906e-02 -5.9885749e-01 6.0000000e-01 -[phi] 1.2765777e+02 3.8958061e-03 4.4853901e-02 -2.9998998e-01 2.9998998e-01 +[Qxx] -1.3851975e-13 -4.2272873e-18 8.4935422e-02 -5.9727840e-01 5.9727840e-01 +[Qxy] -1.3067714e-11 -3.9879497e-16 4.5000000e-02 -3.0000000e-01 3.0000000e-01 +[Qxz] -1.2947032e-11 -3.9511207e-16 4.5000000e-02 -3.0000000e-01 3.0000000e-01 +[Qyy] 4.4785463e+02 1.3667439e-02 9.3490906e-02 -5.9885749e-01 6.0000000e-01 +[Qyz] 1.2765777e+02 3.8958061e-03 4.4853901e-02 -2.9998998e-01 2.9998998e-01 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 @@ -113,11 +113,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 3.8979679e-06 0.98299322236 1.01391118276 -[phi] -3.8940439e-01 -1.1883679e-05 8.4610481e-02 -5.9600835e-01 5.9650065e-01 -[phi] -7.0577555e-02 -2.1538561e-06 4.4866057e-02 -2.9998132e-01 3.0012729e-01 -[phi] 6.0960252e-02 1.8603593e-06 4.4854191e-02 -3.0000642e-01 3.0002493e-01 -[phi] 4.4732927e+02 1.3651406e-02 9.3163565e-02 -5.9761655e-01 5.9925183e-01 -[phi] 1.2727955e+02 3.8842637e-03 4.4701851e-02 -2.9999229e-01 2.9998792e-01 +[Qxx] -3.8940439e-01 -1.1883679e-05 8.4610481e-02 -5.9600835e-01 5.9650065e-01 +[Qxy] -7.0577555e-02 -2.1538561e-06 4.4866057e-02 -2.9998132e-01 3.0012729e-01 +[Qxz] 6.0960252e-02 1.8603593e-06 4.4854191e-02 -3.0000642e-01 3.0002493e-01 +[Qyy] 4.4732927e+02 1.3651406e-02 9.3163565e-02 -5.9761655e-01 5.9925183e-01 +[Qyz] 1.2727955e+02 3.8842637e-03 4.4701851e-02 -2.9999229e-01 2.9998792e-01 Free energies - timestep f v f/v f_bulk/v f_grad/v redshift [fe] 1 8.3793580224e+01 3.2768000000e+04 2.5571771309e-03 1.6391228183e-03 9.1805431254e-04 9.1000000000e-01 diff --git a/tests/regression/d3q19-short/serial-init-lcb.log b/tests/regression/d3q19-short/serial-init-lcb.log index 3f834a5e1..e2d9aaad9 100644 --- a/tests/regression/d3q19-short/serial-init-lcb.log +++ b/tests/regression/d3q19-short/serial-init-lcb.log @@ -91,11 +91,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] -1.6380994e+02 -4.9990827e-03 7.6716937e-09 -5.0000000e-03 7.2901099e-03 -[phi] 3.1569884e-03 9.6343639e-08 3.7001596e-09 -7.4718555e-03 5.7471922e-03 -[phi] -9.0544414e-04 -2.7631962e-08 2.3862226e-09 -4.2434745e-03 6.7002147e-03 -[phi] 6.8054311e+01 2.0768528e-03 2.6666083e-05 -4.9989978e-03 9.8864636e-03 -[phi] 3.7123537e+00 1.1329204e-04 2.9388328e-05 -7.4853149e-03 7.4766459e-03 +[Qxx] -1.6380994e+02 -4.9990827e-03 7.6716937e-09 -5.0000000e-03 7.2901099e-03 +[Qxy] 3.1569884e-03 9.6343639e-08 3.7001596e-09 -7.4718555e-03 5.7471922e-03 +[Qxz] -9.0544414e-04 -2.7631962e-08 2.3862226e-09 -4.2434745e-03 6.7002147e-03 +[Qyy] 6.8054311e+01 2.0768528e-03 2.6666083e-05 -4.9989978e-03 9.8864636e-03 +[Qyz] 3.7123537e+00 1.1329204e-04 2.9388328e-05 -7.4853149e-03 7.4766459e-03 Momentum - x y z [total ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 @@ -105,11 +105,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 1.5625279e-12 0.99995282288 1.00006654458 -[phi] -1.6377691e+02 -4.9980747e-03 7.5904297e-09 -4.9993263e-03 7.2203522e-03 -[phi] 3.1563518e-03 9.6324211e-08 3.6524547e-09 -7.4158262e-03 5.7118511e-03 -[phi] -9.0526156e-04 -2.7626390e-08 2.3541212e-09 -4.2102191e-03 6.6545195e-03 -[phi] 6.8040588e+01 2.0764340e-03 2.6671410e-05 -5.0006325e-03 9.8865755e-03 -[phi] 3.7116051e+00 1.1326920e-04 2.9387778e-05 -7.4859391e-03 7.4772695e-03 +[Qxx] -1.6377691e+02 -4.9980747e-03 7.5904297e-09 -4.9993263e-03 7.2203522e-03 +[Qxy] 3.1563518e-03 9.6324211e-08 3.6524547e-09 -7.4158262e-03 5.7118511e-03 +[Qxz] -9.0526156e-04 -2.7626390e-08 2.3541212e-09 -4.2102191e-03 6.6545195e-03 +[Qyy] 6.8040588e+01 2.0764340e-03 2.6671410e-05 -5.0006325e-03 9.8865755e-03 +[Qyz] 3.7116051e+00 1.1326920e-04 2.9387778e-05 -7.4859391e-03 7.4772695e-03 Free energies - timestep f v f/v f_bulk/v f_grad/v redshift [fe] 1 -2.3543485060e-04 3.2768000000e+04 -7.1849014467e-09 -7.4624859211e-08 6.7439957764e-08 1.0000000000e+00 diff --git a/tests/regression/d3q19-short/serial-init-lcr.log b/tests/regression/d3q19-short/serial-init-lcr.log index f6baf7840..bcf503cae 100644 --- a/tests/regression/d3q19-short/serial-init-lcr.log +++ b/tests/regression/d3q19-short/serial-init-lcr.log @@ -89,11 +89,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 9.6824908e-02 2.9548617e-06 2.0059123e-05 -5.0000000e-03 9.9999628e-03 -[phi] 1.1240589e+00 3.4303554e-05 1.5019305e-05 -7.4986395e-03 7.4987285e-03 -[phi] -1.6546171e+00 -5.0494907e-05 1.4932612e-05 -7.4994285e-03 7.4987352e-03 -[phi] -9.4761747e-01 -2.8918990e-05 1.9648064e-05 -5.0000000e-03 9.9995019e-03 -[phi] 1.8663767e-01 5.6957296e-06 1.5185990e-05 -7.4997452e-03 7.4998120e-03 +[Qxx] 9.6824908e-02 2.9548617e-06 2.0059123e-05 -5.0000000e-03 9.9999628e-03 +[Qxy] 1.1240589e+00 3.4303554e-05 1.5019305e-05 -7.4986395e-03 7.4987285e-03 +[Qxz] -1.6546171e+00 -5.0494907e-05 1.4932612e-05 -7.4994285e-03 7.4987352e-03 +[Qyy] -9.4761747e-01 -2.8918990e-05 1.9648064e-05 -5.0000000e-03 9.9995019e-03 +[Qyz] 1.8663767e-01 5.6957296e-06 1.5185990e-05 -7.4997452e-03 7.4998120e-03 Momentum - x y z [total ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 @@ -103,11 +103,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 5.1500393e-09 0.99965653573 1.00027956635 -[phi] 9.6805383e-02 2.9542659e-06 1.9810249e-05 -4.9950524e-03 9.9626879e-03 -[phi] 1.1238322e+00 3.4296637e-05 1.4833672e-05 -7.4600000e-03 7.4838634e-03 -[phi] -1.6542835e+00 -5.0484725e-05 1.4747918e-05 -7.4689141e-03 7.4748002e-03 -[phi] -9.4742638e-01 -2.8913159e-05 1.9405274e-05 -4.9953337e-03 9.9515476e-03 -[phi] 1.8660003e-01 5.6945811e-06 1.4999371e-05 -7.4666375e-03 7.4628443e-03 +[Qxx] 9.6805383e-02 2.9542659e-06 1.9810249e-05 -4.9950524e-03 9.9626879e-03 +[Qxy] 1.1238322e+00 3.4296637e-05 1.4833672e-05 -7.4600000e-03 7.4838634e-03 +[Qxz] -1.6542835e+00 -5.0484725e-05 1.4747918e-05 -7.4689141e-03 7.4748002e-03 +[Qyy] -9.4742638e-01 -2.8913159e-05 1.9405274e-05 -4.9953337e-03 9.9515476e-03 +[Qyz] 1.8660003e-01 5.6945811e-06 1.4999371e-05 -7.4666375e-03 7.4628443e-03 Free energies - timestep f v f/v f_bulk/v f_grad/v redshift [fe] 1 4.1939266308e-02 3.2768000000e+04 1.2798848361e-06 -7.3638579886e-08 1.3535234160e-06 1.0000000000e+00 diff --git a/tests/regression/d3q19-short/serial-relx-bp1.log b/tests/regression/d3q19-short/serial-relx-bp1.log index aa06a361d..41921288e 100644 --- a/tests/regression/d3q19-short/serial-relx-bp1.log +++ b/tests/regression/d3q19-short/serial-relx-bp1.log @@ -89,11 +89,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] -7.4301676e-14 -2.2675072e-18 6.0000000e-02 -4.4966058e-01 4.4966058e-01 -[phi] -1.4009349e-12 -4.2753141e-17 5.0000000e-02 -6.0000000e-01 6.0000000e-01 -[phi] -1.2735923e-12 -3.8866954e-17 5.0000000e-02 -6.0000000e-01 6.0000000e-01 -[phi] -2.5340841e-14 -7.7334108e-19 6.0000000e-02 -4.4966058e-01 4.4966058e-01 -[phi] -1.4913071e-12 -4.5511080e-17 5.0000000e-02 -6.0000000e-01 6.0000000e-01 +[Qxx] -7.4301676e-14 -2.2675072e-18 6.0000000e-02 -4.4966058e-01 4.4966058e-01 +[Qxy] -1.4009349e-12 -4.2753141e-17 5.0000000e-02 -6.0000000e-01 6.0000000e-01 +[Qxz] -1.2735923e-12 -3.8866954e-17 5.0000000e-02 -6.0000000e-01 6.0000000e-01 +[Qyy] -2.5340841e-14 -7.7334108e-19 6.0000000e-02 -4.4966058e-01 4.4966058e-01 +[Qyz] -1.4913071e-12 -4.5511080e-17 5.0000000e-02 -6.0000000e-01 6.0000000e-01 Momentum - x y z [total ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 @@ -103,11 +103,11 @@ Starting time step loop. Scalars - total mean variance min max [rho] 32768.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[phi] 4.0195028e-14 1.2266549e-18 4.2061932e-02 -3.3730903e-01 4.2727217e-01 -[phi] 4.1711698e-06 1.2729400e-10 2.9266368e-02 -4.2308335e-01 4.2308335e-01 -[phi] 4.1711700e-06 1.2729401e-10 2.9266368e-02 -4.2308335e-01 4.2308335e-01 -[phi] 7.9920265e-14 2.4389729e-18 4.2061932e-02 -3.3730903e-01 4.2727217e-01 -[phi] 4.1711696e-06 1.2729399e-10 2.9266368e-02 -4.2308335e-01 4.2308335e-01 +[Qxx] 4.0195028e-14 1.2266549e-18 4.2061932e-02 -3.3730903e-01 4.2727217e-01 +[Qxy] 4.1711698e-06 1.2729400e-10 2.9266368e-02 -4.2308335e-01 4.2308335e-01 +[Qxz] 4.1711700e-06 1.2729401e-10 2.9266368e-02 -4.2308335e-01 4.2308335e-01 +[Qyy] 7.9920265e-14 2.4389729e-18 4.2061932e-02 -3.3730903e-01 4.2727217e-01 +[Qyz] 4.1711696e-06 1.2729399e-10 2.9266368e-02 -4.2308335e-01 4.2308335e-01 Free energies - timestep f v f/v f_bulk/v f_grad/v redshift [fe] 100 4.0689349779e+01 3.2768000000e+04 1.2417404107e-03 1.2227885274e-03 1.8951883289e-05 8.3000000000e-01 From c1cb96e620f06ea956d39f43afda5546de65454e Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 3 Jan 2023 17:27:32 +0000 Subject: [PATCH 206/244] Add compiler information and hide from tests --- CHANGES.md | 2 ++ src/Makefile | 9 +++++++- src/compiler.c | 54 ++++++++++++++++++++++++++++++---------------- src/compiler.h | 10 ++++----- src/pe.c | 19 ++++++++++------ tests/test-diff.sh | 9 ++++++-- 6 files changed, 69 insertions(+), 34 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7560d2e9c..441025d17 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,6 +17,8 @@ version 0.19.0 [Px,Py,Pz] for vectors, and [Qxx, Qxy, Qxz, Qyy, Qyz] for liquid crystal. The computation of the total has been improved by using a compensated sum, which is more robust to threads/MPI. +- Added compiler option information and git commit information at run + time. version 0.18.0 diff --git a/src/Makefile b/src/Makefile index 32d2cb36a..c2f9154c1 100644 --- a/src/Makefile +++ b/src/Makefile @@ -11,7 +11,7 @@ # Edinburgh Parallel Computing Centre # # Kevin Stratford (kevin@epcc.ed.ac.uk) -# (c) 2010-2019 The University of Edinburgh +# (c) 2010-2023 The University of Edinburgh # ############################################################################### @@ -84,6 +84,13 @@ lib: $(LIBOBJECTS) .c.o : $(CC) $(MODEL) $(OPTS) $(CFLAGS) $(INCL) -c $? +# Special case for compile-time information + +GIT_REVISION = $(shell git rev-parse HEAD) + +compiler.o: compiler.c + $(CC) $(CFLAGS) -DGIT_HASH=$(GIT_REVISION) -DCFLAGS="$(CFLAGS)" -c $< + # Special case for no optimaisation util_sum.o: util_sum.c diff --git a/src/compiler.c b/src/compiler.c index 6319e05b8..72dc0fb54 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -3,11 +3,12 @@ * compiler.c * * Provide some details of the current compiler. + * The git hash is also provided here, merely not having a different home. * * Edinburgh Soft Matter and Statistical Phyiscs Group and * Edinburgh Parallel Computing Centre * - * (c) 2021-2022 The University of Edinburgh + * (c) 2021-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -20,6 +21,15 @@ #include "compiler.h" +/* Compiler options are stringification of environment variable CFLAGS */ +/* If CFLAGS is not set, this will just appear as "CFLAGS" */ +/* Git commit id likewise is contents of GIT_HASH */ + +#define compiler_options() xstr(CFLAGS) +#define git_hash() xstr(GIT_HASH) +#define xstr(s) str(s) +#define str(s) #s + /***************************************************************************** * * compiler_id @@ -46,8 +56,10 @@ int compiler_id(compiler_info_t * compiler) { compiler->major = __cray_major__; compiler->minor = __cray_minor__; compiler->patchlevel = __cray_patchlevel__; - strncpy(compiler->version, __VERSION__, strnlen(__VERSION__, BUFSIZ-1)); - sprintf(compiler->name, "%s", "Cray Clang"); + compiler->version = __VERSION__; + compiler->name = "Cray Clang"; + compiler->options = compiler_options(); + compiler->commit = git_hash(); return 0; #endif @@ -56,11 +68,10 @@ int compiler_id(compiler_info_t * compiler) { compiler->major = _RELEASE; compiler->minor = _RELEASE_MINOR; compiler->patchlevel = 0; /* Not provided */ - { - int len = strnlen(_RELEASE_STRING, BUFSIZ-1); - strncpy(compiler->version, _RELEASE_STRING, len); - } - sprintf(compiler->name, "%s", "Cray Classic"); + compiler->version = _RELEASE_STRING; + compiler->name = "Cray Classic"; + compiler->options = compiler_options(); + compiler->commit = git_hash(); return 0; #endif @@ -70,8 +81,10 @@ int compiler_id(compiler_info_t * compiler) { compiler->major = __INTEL_COMPILER/100; compiler->minor = __INTEL_COMPILER - 100*compiler->major; compiler->patchlevel = __INTEL_COMPILER_UPDATE; - strncpy(compiler->version, __VERSION__, strnlen(__VERSION__, BUFSIZ-1)); - sprintf(compiler->name, "%s", "Intel"); + compiler->version = __VERSION__; + compiler->name = "Intel"; + compiler->options = compiler_options(); + compiler->commit = git_hash(); return 0; #endif @@ -79,8 +92,10 @@ int compiler_id(compiler_info_t * compiler) { compiler->major = __CUDACC_VER_MAJOR__; compiler->minor = __CUDACC_VER_MINOR__; compiler->patchlevel = __CUDACC_VER_BUILD__; - sprintf(compiler->version, "%s", "null"); /* None provided */ - sprintf(compiler->name, "%s", "NVIDIA nvcc"); + compiler->version = "null"); /* None provided */ + compiler->name, "NVIDIA nvcc"; + compiler->options = compiler_options(); + compiler->commit = git_hash(); /* Include __CUDA_ARCH__ */ return 0; #endif @@ -90,11 +105,10 @@ int compiler_id(compiler_info_t * compiler) { compiler->major = __clang_major__; compiler->minor = __clang_minor__; compiler->patchlevel = __clang_patchlevel__; - { - int len = strnlen(__clang_version__, BUFSIZ-1); - strncpy(compiler->version, __clang_version__, len); - } - sprintf(compiler->name, "%s", "Clang"); + compiler->version = __clang_version__; + compiler->name = "Clang"; + compiler->options = compiler_options(); + compiler->commit = git_hash(); /* CASE */ /* AMD/AOCC defines no specific macros. */ @@ -109,8 +123,10 @@ int compiler_id(compiler_info_t * compiler) { compiler->major = __GNUC__; compiler->minor = __GNUC_MINOR__; compiler->patchlevel = __GNUC_PATCHLEVEL__; - strncpy(compiler->version, __VERSION__, 1 + strlen(__VERSION__)); - sprintf(compiler->name, "%s", "Gnu"); + compiler->version = __VERSION__; + compiler->name = "Gnu"; + compiler->options = compiler_options(); + compiler->commit = git_hash(); ierr = 0; /* CASE */ diff --git a/src/compiler.h b/src/compiler.h index 52baea6bf..1f7efafbb 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -5,7 +5,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2021 The University of Edinburgh + * (c) 2021-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -15,8 +15,6 @@ #ifndef LUDWIG_COMPILER_H #define LUDWIG_COMPILER_H -#include - typedef struct compiler_info_s compiler_info_t; struct compiler_info_s { @@ -24,8 +22,10 @@ struct compiler_info_s { int major; /* Major */ int minor; /* Minor */ int patchlevel; /* Patch */ - char version[BUFSIZ]; /* Version string */ - char name[BUFSIZ]; /* Vendor major.minor.patch */ + const char * version; /* Version string */ + const char * name; /* Vendor major.minor.patch */ + const char * options; /* Compiler options at compile time ("CFLAGS") */ + const char * commit; /* Git 40-character commit hash */ }; int compiler_id(compiler_info_t * compiler); diff --git a/src/pe.c b/src/pe.c index f9ab0310d..64c181826 100644 --- a/src/pe.c +++ b/src/pe.c @@ -15,7 +15,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2010-2022 The University of Edinburgh + * (c) 2010-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -152,14 +152,21 @@ __host__ int pe_free(pe_t * pe) { __host__ int pe_message(pe_t * pe) { + compiler_info_t compiler = {0}; + assert(pe); + compiler_id(&compiler); + pe_info(pe, - "Welcome to Ludwig v%d.%d.%d (%s version running on %d process%s)\n", + "Welcome to: Ludwig v%d.%d.%d (%s version running on %d process%s)\n", LUDWIG_MAJOR_VERSION, LUDWIG_MINOR_VERSION, LUDWIG_PATCH_VERSION, (pe->mpi_size > 1) ? "MPI" : "Serial", pe->mpi_size, (pe->mpi_size == 1) ? "" : "es"); + /* Git */ + printf("Git commit: %s\n\n", compiler.commit); + { char strctime[BUFSIZ] = {0}; pe_time(strctime, BUFSIZ); @@ -168,18 +175,16 @@ __host__ int pe_message(pe_t * pe) { if (pe->mpi_rank == 0) { - compiler_info_t compiler = {0}; - - compiler_id(&compiler); - printf("Compiler:\n"); printf(" name: %s %d.%d.%d\n", compiler.name, compiler.major, compiler.minor, compiler.patchlevel); printf(" version-string: %s\n", compiler.version); + printf(" options: %s\n", compiler.options); printf("\n"); - /* Compilation */ assert(printf("Note assertions via standard C assert() are on.\n\n")); + + /* Thread model */ tdpThreadModelInfo(stdout); printf("\n"); } diff --git a/tests/test-diff.sh b/tests/test-diff.sh index ef6170da6..05ae0227f 100755 --- a/tests/test-diff.sh +++ b/tests/test-diff.sh @@ -13,8 +13,9 @@ # # We have to get rid of some stuff that will not match: # - the run times -# - SVN version information +# - Version information # - exact location of input file +# - compiler deatils # - allow "Model R" tests to pass by looking for "d3q19 R" etc # # Options: @@ -25,7 +26,7 @@ # # Contributing Authors: # Kevin Stratford (kevin@epcc.ed.ac.uk) -# (c) 2013-2019 The University of Edinburgh +# (c) 2013-2023 The University of Edinburgh # ############################################################################### @@ -81,9 +82,11 @@ fi sed '/call)/d' $1 > test-diff-tmp.ref sed -i~ '/calls)/d' test-diff-tmp.ref sed -i~ '/Welcome/d' test-diff-tmp.ref +sed -i~ '/Git commit:/d' test-diff-tmp.ref sed -i~ '/Compiler:/d' test-diff-tmp.ref sed -i~ '/..name:/d' test-diff-tmp.ref sed -i~ '/..version-string:/d' test-diff-tmp.ref +sed -i~ '/..options:/d' test-diff-tmp.ref sed -i~ '/Target thread model:/d' test-diff-tmp.ref sed -i~ '/Default threads per block/d' test-diff-tmp.ref sed -i~ '/OpenMP/d' test-diff-tmp.ref @@ -102,9 +105,11 @@ sed -i~ '/End time/d' test-diff-tmp.ref sed '/call)/d' $2 > test-diff-tmp.log sed -i~ '/calls)/d' test-diff-tmp.log sed -i~ '/Welcome/d' test-diff-tmp.log +sed -i~ '/Git commit:/d' test-diff-tmp.log sed -i~ '/Compiler:/d' test-diff-tmp.log sed -i~ '/..name:/d' test-diff-tmp.log sed -i~ '/..version-string:/d' test-diff-tmp.log +sed -i~ '/..options:/d' test-diff-tmp.log sed -i~ '/Target thread model:/d' test-diff-tmp.log sed -i~ '/Default threads per block/d' test-diff-tmp.log sed -i~ '/OpenMP/d' test-diff-tmp.log From bfc94342267a6f57f683cedd1b9c1e638699b32c Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 4 Jan 2023 16:40:08 +0000 Subject: [PATCH 207/244] Remove unused function (which cannot work) --- src/gradient_rt.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gradient_rt.c b/src/gradient_rt.c index a2618c983..5b08d3f08 100644 --- a/src/gradient_rt.c +++ b/src/gradient_rt.c @@ -88,7 +88,6 @@ int gradient_rt_init(pe_t * pe, rt_t * rt, const char * fieldname, pe_info(pe, "3d_7pt_solid\n"); f2 = grad_3d_7pt_solid_d2; f4 = NULL; - field_grad_dab_set(grad, grad_3d_7pt_solid_dab); assert(map); grad_3d_7pt_solid_set(map, cinfo); } From 6a229e13ee76ddf9c1ae7ff2c9f072dffb8e9479 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 4 Jan 2023 16:40:45 +0000 Subject: [PATCH 208/244] Move repeated code to lc_anchoring --- src/gradient_3d_7pt_solid.c | 420 ++---------------------------------- src/gradient_3d_7pt_solid.h | 9 +- src/gradient_s7_anchoring.c | 294 ++----------------------- src/lc_anchoring.c | 196 +++++++++++++++++ src/lc_anchoring.h | 21 +- src/lc_anchoring_impl.h | 162 ++++++++++++++ 6 files changed, 418 insertions(+), 684 deletions(-) diff --git a/src/gradient_3d_7pt_solid.c b/src/gradient_3d_7pt_solid.c index 9f8f7c440..c124b8451 100644 --- a/src/gradient_3d_7pt_solid.c +++ b/src/gradient_3d_7pt_solid.c @@ -43,7 +43,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2011-2021 The University of Edinburgh + * (c) 2011-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -62,12 +62,10 @@ #include "colloids.h" #include "gradient_3d_7pt_solid.h" -typedef struct param_s param_t; - struct grad_lc_anch_s { pe_t * pe; cs_t * cs; - param_t * param; /* Boundary condition parameters */ + lc_anchoring_matrices_t bc;/* Boundary condition matrices */ map_t * map; /* Supports a map */ field_t * phi; /* Compositional order parameter */ colloids_info_t * cinfo; /* Supports colloids */ @@ -75,28 +73,16 @@ struct grad_lc_anch_s { grad_lc_anch_t * target; /* Device memory */ }; -struct param_s { - double a6inv[3][6]; - double a12inv[3][12][12]; - double a18inv[18][18]; -}; - static grad_lc_anch_t * static_grad = NULL; -static __constant__ param_t static_param; - -__host__ int gradient_param_init(grad_lc_anch_t * anch); -__host__ int gradient_param_commit(grad_lc_anch_t * anch); __host__ int gradient_6x6(grad_lc_anch_t * anch, field_grad_t *grad, int nextra); + __global__ void gradient_6x6_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_lc_anch_t * anch, fe_lc_t * fe, field_grad_t * fg, map_t * map, colloids_info_t * cinfo); __host__ __device__ -int gradient_bcs6x6_coeff(double kappa0, double kappa1, const int dn[3], - double bc[NSYMM][NSYMM][3]); -__host__ __device__ int colloids_q_boundary(fe_lc_param_t * param, const double n[3], double qs[3][3], double q0[3][3], int map_status); @@ -118,7 +104,6 @@ int q_boundary_constants(cs_t * cs, fe_lc_param_t * param, grad_lc_anch_t * anch __host__ int grad_lc_anch_create(pe_t * pe, cs_t * cs, map_t * map, field_t * phi, colloids_info_t * cinfo, fe_lc_t * fe, grad_lc_anch_t ** pobj) { - int ndevice; grad_lc_anch_t * obj = NULL; @@ -130,17 +115,19 @@ __host__ int grad_lc_anch_create(pe_t * pe, cs_t * cs, map_t * map, assert(obj); if (obj == NULL) pe_fatal(pe, "calloc(grad_lc_anch_t) failed\n"); - obj->param = (param_t *) calloc(1, sizeof(param_t)); - assert(obj->param); - if (obj->param == NULL) pe_fatal(pe, "calloc(param_t) failed\n"); - obj->pe = pe; obj->cs = cs; obj->map = map; obj->phi = phi; obj->cinfo = cinfo; obj->fe = fe; - gradient_param_init(obj); + + { + /* Initialise matrix inverses */ + fe_lc_param_t fep = {0}; + fe_lc_param(fe, &fep); + lc_anchoring_matrices(fep.kappa0, fep.kappa1, &obj->bc); + } tdpGetDeviceCount(&ndevice); @@ -148,12 +135,10 @@ __host__ int grad_lc_anch_create(pe_t * pe, cs_t * cs, map_t * map, obj->target = obj; } else { - param_t * tmp = NULL; tdpMalloc((void **) &obj->target, sizeof(grad_lc_anch_t)); - tdpGetSymbolAddress((void **) &tmp, tdpSymbol(static_param)); - tdpMemcpy(&obj->target->param, (const void *) &tmp, sizeof(param_t *), - tdpMemcpyHostToDevice); - gradient_param_commit(obj); + tdpAssert(tdpMemcpy(&obj->target->bc, &obj->bc, + sizeof(lc_anchoring_matrices_t), + tdpMemcpyHostToDevice)); } static_grad = obj; @@ -172,29 +157,11 @@ __host__ int grad_lc_anch_free(grad_lc_anch_t * grad) { assert(grad); if (grad->target != grad) tdpFree(grad->target); - - free(grad->param); free(grad); return 0; } -/***************************************************************************** - * - * gradient_param_commit - * - *****************************************************************************/ - -__host__ int gradient_param_commit(grad_lc_anch_t * anch) { - - assert(anch); - - tdpMemcpyToSymbol(tdpSymbol(static_param), anch->param, sizeof(param_t), 0, - tdpMemcpyHostToDevice); - - return 0; -} - /***************************************************************************** * * grad_3d_7pt_solid_set @@ -271,9 +238,7 @@ int gradient_6x6(grad_lc_anch_t * anch, field_grad_t * fg, int nextra) { kernel_ctxt_create(anch->cs, NSIMDVL, limits, &ctxt); kernel_ctxt_launch_param(ctxt, &nblk, &ntpb); - gradient_param_commit(anch); fe_lc_param_commit(anch->fe); - cs_target(anch->cs, &cstarget); tdpLaunchKernel(gradient_6x6_kernel, nblk, ntpb, 0, 0, @@ -495,7 +460,7 @@ void gradient_6x6_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_lc_anch_t * anch, /* Subtract all three gradient terms from the RHS and then cancel * the one unknown contribution ... works for any normal[0] */ - gradient_bcs6x6_coeff(kappa0, kappa1, bcs[normal[0]], bc); + lc_anchoring_coefficients(kappa0, kappa1, bcs[normal[0]], bc); for (n1 = 0; n1 < NSYMM; n1++) { for (n2 = 0; n2 < NSYMM; n2++) { @@ -508,7 +473,7 @@ void gradient_6x6_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_lc_anch_t * anch, } b18[n1] *= bcsign[normal[0]]; - x18[n1] = anch->param->a6inv[normal[0]/2][n1]*b18[n1]; + x18[n1] = anch->bc.a6inv[normal[0]/2][n1]*b18[n1]; } } @@ -520,7 +485,7 @@ void gradient_6x6_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_lc_anch_t * anch, /* Compute the RHS for two unknowns and one known */ - gradient_bcs6x6_coeff(kappa0, kappa1, bcs[normal[0]], bc); + lc_anchoring_coefficients(kappa0, kappa1, bcs[normal[0]], bc); for (n1 = 0; n1 < NSYMM; n1++) { for (n2 = 0; n2 < NSYMM; n2++) { @@ -534,7 +499,7 @@ void gradient_6x6_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_lc_anch_t * anch, } } - gradient_bcs6x6_coeff(kappa0, kappa1, bcs[normal[1]], bc); + lc_anchoring_coefficients(kappa0, kappa1, bcs[normal[1]], bc); for (n1 = 0; n1 < NSYMM; n1++) { for (n2 = 0; n2 < NSYMM; n2++) { @@ -557,17 +522,17 @@ void gradient_6x6_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_lc_anch_t * anch, for (n1 = 0; n1 < 2*NSYMM; n1++) { x18[n1] = 0.0; for (n2 = 0; n2 < NSYMM; n2++) { - x18[n1] += bcsign[normal[0]]*anch->param->a12inv[ia][n1][n2]*b18[n2]; + x18[n1] += bcsign[normal[0]]*anch->bc.a12inv[ia][n1][n2]*b18[n2]; } for (n2 = NSYMM; n2 < 2*NSYMM; n2++) { - x18[n1] += bcsign[normal[1]]*anch->param->a12inv[ia][n1][n2]*b18[n2]; + x18[n1] += bcsign[normal[1]]*anch->bc.a12inv[ia][n1][n2]*b18[n2]; } } } if (nunknown == 3) { - gradient_bcs6x6_coeff(kappa0, kappa1, bcs[normal[0]], bc); + lc_anchoring_coefficients(kappa0, kappa1, bcs[normal[0]], bc); for (n1 = 0; n1 < NSYMM; n1++) { for (n2 = 0; n2 < NSYMM; n2++) { @@ -580,7 +545,7 @@ void gradient_6x6_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_lc_anch_t * anch, b18[n1] *= bcsign[normal[0]]; } - gradient_bcs6x6_coeff(kappa0, kappa1, bcs[normal[1]], bc); + lc_anchoring_coefficients(kappa0, kappa1, bcs[normal[1]], bc); for (n1 = 0; n1 < NSYMM; n1++) { for (n2 = 0; n2 < NSYMM; n2++) { @@ -593,7 +558,7 @@ void gradient_6x6_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_lc_anch_t * anch, b18[NSYMM + n1] *= bcsign[normal[1]]; } - gradient_bcs6x6_coeff(kappa0, kappa1, bcs[normal[2]], bc); + lc_anchoring_coefficients(kappa0, kappa1, bcs[normal[2]], bc); for (n1 = 0; n1 < NSYMM; n1++) { for (n2 = 0; n2 < NSYMM; n2++) { @@ -611,7 +576,7 @@ void gradient_6x6_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_lc_anch_t * anch, for (n1 = 0; n1 < 3*NSYMM; n1++) { x18[n1] = 0.0; for (n2 = 0; n2 < 3*NSYMM; n2++) { - x18[n1] += anch->param->a18inv[n1][n2]*b18[n2]; + x18[n1] += anch->bc.a18inv[n1][n2]*b18[n2]; } } } @@ -650,264 +615,6 @@ void gradient_6x6_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_lc_anch_t * anch, return; } -/***************************************************************************** - * - * gradient_param_init - * - * Compute and store the inverse matrices used in the boundary conditions. - * - *****************************************************************************/ - -__host__ -int gradient_param_init(grad_lc_anch_t * anch) { - - int ia, n1, n2; - const int bcs[6][3] = {{-1,0,0},{1,0,0},{0,-1,0},{0,1,0},{0,0,-1},{0,0,1}}; - - double bc[6][6][3]; - double ** a12inv[3]; - double ** a18inv; - fe_lc_param_t feparam; - KRONECKER_DELTA_CHAR(d_); - - assert(anch); - fe_lc_param(anch->fe, &feparam); - - /* Compute inverse matrices */ - - util_matrix_create(12, 12, &(a12inv[0])); - util_matrix_create(12, 12, &(a12inv[1])); - util_matrix_create(12, 12, &(a12inv[2])); - util_matrix_create(18, 18, &a18inv); - - for (ia = 0; ia < 3; ia++) { - - gradient_bcs6x6_coeff(feparam.kappa0, feparam.kappa1, bcs[2*ia + 1], bc); - - for (n1 = 0; n1 < NSYMM; n1++) { - anch->param->a6inv[ia][n1] = 1.0/bc[n1][n1][ia]; - } - - for (n1 = 0; n1 < NSYMM; n1++) { - for (n2 = 0; n2 < NSYMM; n2++) { - a18inv[ia*NSYMM + n1][0*NSYMM + n2] = 0.5*(1+d_[ia][X])*bc[n1][n2][X]; - a18inv[ia*NSYMM + n1][1*NSYMM + n2] = 0.5*(1+d_[ia][Y])*bc[n1][n2][Y]; - a18inv[ia*NSYMM + n1][2*NSYMM + n2] = 0.5*(1+d_[ia][Z])*bc[n1][n2][Z]; - - } - } - } - - for (n1 = 0; n1 < 12; n1++) { - for (n2 = 0; n2 < 12; n2++) { - a12inv[0][n1][n2] = a18inv[n1][n2]; - a12inv[2][n1][n2] = a18inv[6+n1][6+n2]; - } - } - - for (n1 = 0; n1 < 6; n1++) { - for (n2 = 0; n2 < 6; n2++) { - a12inv[1][n1][n2] = a18inv[n1][n2]; - a12inv[1][n1][6+n2] = a18inv[n1][12+n2]; - } - } - - for (n1 = 6; n1 < 12; n1++) { - for (n2 = 0; n2 < 6; n2++) { - a12inv[1][n1][n2] = a18inv[6+n1][n2]; - a12inv[1][n1][6+n2] = a18inv[6+n1][12+n2]; - } - } - - ia = util_matrix_invert(12, a12inv[0]); - assert(ia == 0); - ia = util_matrix_invert(12, a12inv[1]); - assert(ia == 0); - ia = util_matrix_invert(12, a12inv[2]); - assert(ia == 0); - ia = util_matrix_invert(18, a18inv); - assert(ia == 0); - - for (n1 = 0; n1 < 18; n1++) { - for (n2 = 0; n2 < 18; n2++) { - anch->param->a18inv[n1][n2] = a18inv[n1][n2]; - } - } - - for (ia = 0; ia < 3; ia++) { - for (n1 = 0; n1 < 12; n1++) { - for (n2 = 0; n2 < 12; n2++) { - anch->param->a12inv[ia][n1][n2] = a12inv[ia][n1][n2]; - } - } - } - - util_matrix_free(18, &a18inv); - util_matrix_free(12, &(a12inv[2])); - util_matrix_free(12, &(a12inv[1])); - util_matrix_free(12, &(a12inv[0])); - - return 0; -} - - -/***************************************************************************** - * - * gradient_bcs6x6_coeff - * - * Full set of coefficients in boundary condition equation for given - * surface normal dn. - * - *****************************************************************************/ - -__host__ __device__ -int gradient_bcs6x6_coeff(double kappa0, double kappa1, const int dn[3], - double bc[NSYMM][NSYMM][3]) { - double kappa2; - - kappa2 = kappa0 + kappa1; - - /* XX equation */ - - bc[XX][XX][X] = kappa0*dn[X]; - bc[XX][XY][X] = -kappa1*dn[Y]; - bc[XX][XZ][X] = -kappa1*dn[Z]; - bc[XX][YY][X] = 0.0; - bc[XX][YZ][X] = 0.0; - bc[XX][ZZ][X] = 0.0; - - bc[XX][XX][Y] = kappa1*dn[Y]; - bc[XX][XY][Y] = kappa0*dn[X]; - bc[XX][XZ][Y] = 0.0; - bc[XX][YY][Y] = 0.0; - bc[XX][YZ][Y] = 0.0; - bc[XX][ZZ][Y] = 0.0; - - bc[XX][XX][Z] = kappa1*dn[Z]; - bc[XX][XY][Z] = 0.0; - bc[XX][XZ][Z] = kappa0*dn[X]; - bc[XX][YY][Z] = 0.0; - bc[XX][YZ][Z] = 0.0; - bc[XX][ZZ][Z] = 0.0; - - /* XY equation */ - - bc[XY][XX][X] = kappa0*dn[Y]; - bc[XY][XY][X] = kappa2*dn[X]; - bc[XY][XZ][X] = 0.0; - bc[XY][YY][X] = -kappa1*dn[Y]; - bc[XY][YZ][X] = -kappa1*dn[Z]; - bc[XY][ZZ][X] = 0.0; - - bc[XY][XX][Y] = -kappa1*dn[X]; - bc[XY][XY][Y] = kappa2*dn[Y]; - bc[XY][XZ][Y] = -kappa1*dn[Z]; - bc[XY][YY][Y] = kappa0*dn[X]; - bc[XY][YZ][Y] = 0.0; - bc[XY][ZZ][Y] = 0.0; - - bc[XY][XX][Z] = 0.0; - bc[XY][XY][Z] = 2.0*kappa1*dn[Z]; - bc[XY][XZ][Z] = kappa0*dn[Y]; - bc[XY][YY][Z] = 0.0; - bc[XY][YZ][Z] = kappa0*dn[X]; - bc[XY][ZZ][Z] = 0.0; - - /* XZ equation */ - - bc[XZ][XX][X] = kappa0*dn[Z]; - bc[XZ][XY][X] = 0.0; - bc[XZ][XZ][X] = kappa2*dn[X]; - bc[XZ][YY][X] = 0.0; - bc[XZ][YZ][X] = -kappa1*dn[Y]; - bc[XZ][ZZ][X] = -kappa1*dn[Z]; - - bc[XZ][XX][Y] = 0.0; - bc[XZ][XY][Y] = kappa0*dn[Z]; - bc[XZ][XZ][Y] = 2.0*kappa1*dn[Y]; - bc[XZ][YY][Y] = 0.0; - bc[XZ][YZ][Y] = kappa0*dn[X]; - bc[XZ][ZZ][Y] = 0.0; - - bc[XZ][XX][Z] = -kappa1*dn[X]; - bc[XZ][XY][Z] = -kappa1*dn[Y]; - bc[XZ][XZ][Z] = kappa2*dn[Z]; - bc[XZ][YY][Z] = 0.0; - bc[XZ][YZ][Z] = 0.0; - bc[XZ][ZZ][Z] = kappa0*dn[X]; - - /* YY equation */ - - bc[YY][XX][X] = 0.0; - bc[YY][XY][X] = kappa0*dn[Y]; - bc[YY][XZ][X] = 0.0; - bc[YY][YY][X] = kappa1*dn[X]; - bc[YY][YZ][X] = 0.0; - bc[YY][ZZ][X] = 0.0; - - bc[YY][XX][Y] = 0.0; - bc[YY][XY][Y] = -kappa1*dn[X]; - bc[YY][XZ][Y] = 0.0; - bc[YY][YY][Y] = kappa0*dn[Y]; - bc[YY][YZ][Y] = -kappa1*dn[Z]; - bc[YY][ZZ][Y] = 0.0; - - bc[YY][XX][Z] = 0.0; - bc[YY][XY][Z] = 0.0; - bc[YY][XZ][Z] = 0.0; - bc[YY][YY][Z] = kappa1*dn[Z]; - bc[YY][YZ][Z] = kappa0*dn[Y]; - bc[YY][ZZ][Z] = 0.0; - - /* YZ equation */ - - bc[YZ][XX][X] = 0.0; - bc[YZ][XY][X] = kappa0*dn[Z]; - bc[YZ][XZ][X] = kappa0*dn[Y]; - bc[YZ][YY][X] = 0.0; - bc[YZ][YZ][X] = 2.0*kappa1*dn[X]; - bc[YZ][ZZ][X] = 0.0; - - bc[YZ][XX][Y] = 0.0; - bc[YZ][XY][Y] = 0.0; - bc[YZ][XZ][Y] = -kappa1*dn[X]; - bc[YZ][YY][Y] = kappa0*dn[Z]; - bc[YZ][YZ][Y] = kappa2*dn[Y]; - bc[YZ][ZZ][Y] = -kappa1*dn[Z]; - - bc[YZ][XX][Z] = 0.0; - bc[YZ][XY][Z] = -kappa1*dn[X]; - bc[YZ][XZ][Z] = 0.0; - bc[YZ][YY][Z] = -kappa1*dn[Y]; - bc[YZ][YZ][Z] = kappa2*dn[Z]; - bc[YZ][ZZ][Z] = kappa0*dn[Y]; - - /* ZZ equation */ - - bc[ZZ][XX][X] = 0.0; - bc[ZZ][XY][X] = 0.0; - bc[ZZ][XZ][X] = kappa0*dn[Z]; - bc[ZZ][YY][X] = 0.0; - bc[ZZ][YZ][X] = 0.0; - bc[ZZ][ZZ][X] = kappa1*dn[X]; - - bc[ZZ][XX][Y] = 0.0; - bc[ZZ][XY][Y] = 0.0; - bc[ZZ][XZ][Y] = 0.0; - bc[ZZ][YY][Y] = 0.0; - bc[ZZ][YZ][Y] = kappa0*dn[Z]; - bc[ZZ][ZZ][Y] = kappa1*dn[Y]; - - bc[ZZ][XX][Z] = 0.0; - bc[ZZ][XY][Z] = 0.0; - bc[ZZ][XZ][Z] = -kappa1*dn[X]; - bc[ZZ][YY][Z] = 0.0; - bc[ZZ][YZ][Z] = -kappa1*dn[Y]; - bc[ZZ][ZZ][Z] = kappa0*dn[Z]; - - return 0; -} - /***************************************************************************** * * q_boundary_constants @@ -1145,84 +852,3 @@ int colloids_q_boundary(fe_lc_param_t * param, return 0; } - -/***************************************************************************** - * - * grad_3d_7pt_solid_dab - * - *****************************************************************************/ - -__host__ int grad_3d_7pt_solid_dab(field_grad_t * df) { - - int nlocal[3]; - int nhalo; - int nextra; - int nsites; - int ic, jc, kc; - int xs, ys, zs; - int index; - double * __restrict__ dab; - double * __restrict__ field; - - cs_t * cs = NULL; - - assert(df); - assert(cs); - - cs_nhalo(cs, &nhalo); - cs_nlocal(cs, nlocal); - cs_nsites(cs, &nsites); - cs_strides(cs, &xs, &ys, &zs); - - nextra = nhalo - 1; - assert(nextra >= 0); - - field = df->field->data; - dab = df->d_ab; - - for (ic = 1 - nextra; ic <= nlocal[X] + nextra; ic++) { - for (jc = 1 - nextra; jc <= nlocal[Y] + nextra; jc++) { - for (kc = 1 - nextra; kc <= nlocal[Z] + nextra; kc++) { - - index = cs_index(cs, ic, jc, kc); - - dab[addr_rank1(nsites, NSYMM, index, XX)] = - (+ 1.0*field[addr_rank0(nsites, index + xs)] - + 1.0*field[addr_rank0(nsites, index - xs)] - - 2.0*field[addr_rank0(nsites, index)]); - - dab[addr_rank1(nsites, NSYMM, index, XY)] = 0.25* - (+ field[addr_rank0(nsites, index + xs + ys)] - - field[addr_rank0(nsites, index + xs - ys)] - - field[addr_rank0(nsites, index - xs + ys)] - + field[addr_rank0(nsites, index - xs - ys)]); - - dab[addr_rank1(nsites, NSYMM, index, XZ)] = 0.25* - (+ field[addr_rank0(nsites, index + xs + 1)] - - field[addr_rank0(nsites, index + xs - 1)] - - field[addr_rank0(nsites, index - xs + 1)] - + field[addr_rank0(nsites, index - xs - 1)]); - - dab[addr_rank1(nsites, NSYMM, index, YY)] = - (+ 1.0*field[addr_rank0(nsites, index + ys)] - + 1.0*field[addr_rank0(nsites, index - ys)] - - 2.0*field[addr_rank0(nsites, index)]); - - - dab[addr_rank1(nsites, NSYMM, index, YZ)] = 0.25* - (+ field[addr_rank0(nsites, index + ys + 1)] - - field[addr_rank0(nsites, index + ys - 1)] - - field[addr_rank0(nsites, index - ys + 1)] - + field[addr_rank0(nsites, index - ys - 1)]); - - dab[addr_rank1(nsites, NSYMM, index, ZZ)] = - (+ 1.0*field[addr_rank0(nsites, index + 1)] - + 1.0*field[addr_rank0(nsites, index - 1)] - - 2.0*field[addr_rank0(nsites, index)]); - - } - } - } - - return 0; -} diff --git a/src/gradient_3d_7pt_solid.h b/src/gradient_3d_7pt_solid.h index be9f704c9..6e2238148 100644 --- a/src/gradient_3d_7pt_solid.h +++ b/src/gradient_3d_7pt_solid.h @@ -2,18 +2,16 @@ * * gradient_3d_7pt_solid.h * - * $Id$ - * * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * * Kevin Stratford (kevin@epcc.ed.ac.uk) - * (c) 2011-2017 The University of Edinburgh + * (c) 2011-2023 The University of Edinburgh * *****************************************************************************/ -#ifndef GRADIENT_3D_7PT_SOLID_H -#define GRADIENT_3D_7PT_SOLID_H +#ifndef LUDWIG_GRADIENT_3D_7PT_SOLID_H +#define LUDWIG_GRADIENT_3D_7PT_SOLID_H #include "pe.h" #include "coords.h" @@ -28,7 +26,6 @@ __host__ int grad_lc_anch_create(pe_t * pe, cs_t * cs, map_t * map, field_t * phi, colloids_info_t * cinfo, fe_lc_t * fe, grad_lc_anch_t ** p); __host__ int grad_3d_7pt_solid_d2(field_grad_t * fg); -__host__ int grad_3d_7pt_solid_dab(field_grad_t * fg); __host__ int grad_3d_7pt_solid_set(map_t * map, colloids_info_t * cinfo); #endif diff --git a/src/gradient_s7_anchoring.c b/src/gradient_s7_anchoring.c index 6a759818e..3e1ba2d19 100644 --- a/src/gradient_s7_anchoring.c +++ b/src/gradient_s7_anchoring.c @@ -60,18 +60,10 @@ #include "lc_anchoring.h" #include "gradient_s7_anchoring.h" -typedef struct param_s param_t; - -struct param_s { - double a6inv[3][6]; - double a12inv[3][12][12]; - double a18inv[18][18]; -}; - struct grad_s7_anch_s { pe_t * pe; cs_t * cs; - param_t bc; /* Boundary condition parameters */ + lc_anchoring_matrices_t bc;/* Boundary condition parameters */ map_t * map; /* Supports a map */ fe_lc_t * fe; /* Liquid crystal free energy */ colloids_info_t * cinfo; /* Colloid information */ @@ -80,14 +72,10 @@ struct grad_s7_anch_s { static grad_s7_anch_t * static_grad = NULL; -__host__ int grad_s7_anchoring_param_init(grad_s7_anch_t * anch); __global__ void grad_s7_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_s7_anch_t * anch, fe_lc_t * fe, field_grad_t * fg, map_t * map); -__host__ __device__ -int grad_s7_bcs_coeff(double kappa0, double kappa1, const int dn[3], - double bc[NSYMM][NSYMM][3]); __host__ __device__ int grad_s7_boundary_c(fe_lc_param_t * param, grad_s7_anch_t * anch, @@ -124,7 +112,12 @@ __host__ int grad_s7_anchoring_create(pe_t * pe, cs_t * cs, map_t * map, obj->cs = cs; obj->map = map; obj->fe = fe; - grad_s7_anchoring_param_init(obj); + + { + fe_lc_param_t fep = {0}; + fe_lc_param(fe, &fep); + lc_anchoring_matrices(fep.kappa0, fep.kappa1, &obj->bc); + } tdpGetDeviceCount(&ndevice); @@ -140,7 +133,8 @@ __host__ int grad_s7_anchoring_create(pe_t * pe, cs_t * cs, map_t * map, tdpMemcpy(&obj->target->cs, &cstarget, sizeof(cs_t *), tdpMemcpyHostToDevice); - tdpAssert(tdpMemcpy(&obj->target->bc, &obj->bc, sizeof(param_t), + tdpAssert(tdpMemcpy(&obj->target->bc, &obj->bc, + sizeof(lc_anchoring_matrices_t), tdpMemcpyHostToDevice)); } @@ -512,7 +506,7 @@ void grad_s7_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_s7_anch_t * anch, /* Subtract all three gradient terms from the RHS and then cancel * the one unknown contribution ... works for any normal[0] */ - grad_s7_bcs_coeff(kappa0, kappa1, bcs[normal[0]], bc); + lc_anchoring_coefficients(kappa0, kappa1, bcs[normal[0]], bc); for (n1 = 0; n1 < NSYMM; n1++) { for (n2 = 0; n2 < NSYMM; n2++) { @@ -537,7 +531,7 @@ void grad_s7_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_s7_anch_t * anch, /* Compute the RHS for two unknowns and one known */ - grad_s7_bcs_coeff(kappa0, kappa1, bcs[normal[0]], bc); + lc_anchoring_coefficients(kappa0, kappa1, bcs[normal[0]], bc); for (n1 = 0; n1 < NSYMM; n1++) { for (n2 = 0; n2 < NSYMM; n2++) { @@ -550,7 +544,7 @@ void grad_s7_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_s7_anch_t * anch, } } - grad_s7_bcs_coeff(kappa0, kappa1, bcs[normal[1]], bc); + lc_anchoring_coefficients(kappa0, kappa1, bcs[normal[1]], bc); for (n1 = 0; n1 < NSYMM; n1++) { for (n2 = 0; n2 < NSYMM; n2++) { @@ -583,7 +577,7 @@ void grad_s7_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_s7_anch_t * anch, if (nunknown == 3) { - grad_s7_bcs_coeff(kappa0, kappa1, bcs[normal[0]], bc); + lc_anchoring_coefficients(kappa0, kappa1, bcs[normal[0]], bc); for (n1 = 0; n1 < NSYMM; n1++) { for (n2 = 0; n2 < NSYMM; n2++) { @@ -596,7 +590,7 @@ void grad_s7_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_s7_anch_t * anch, b18[n1] *= bcsign[normal[0]]; } - grad_s7_bcs_coeff(kappa0, kappa1, bcs[normal[1]], bc); + lc_anchoring_coefficients(kappa0, kappa1, bcs[normal[1]], bc); for (n1 = 0; n1 < NSYMM; n1++) { for (n2 = 0; n2 < NSYMM; n2++) { @@ -609,7 +603,7 @@ void grad_s7_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_s7_anch_t * anch, b18[NSYMM + n1] *= bcsign[normal[1]]; } - grad_s7_bcs_coeff(kappa0, kappa1, bcs[normal[2]], bc); + lc_anchoring_coefficients(kappa0, kappa1, bcs[normal[2]], bc); for (n1 = 0; n1 < NSYMM; n1++) { for (n2 = 0; n2 < NSYMM; n2++) { @@ -666,264 +660,6 @@ void grad_s7_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_s7_anch_t * anch, return; } -/***************************************************************************** - * - * grad_s7_anchoring_param_init - * - * Compute and store the inverse matrices used in the boundary conditions. - * - *****************************************************************************/ - -__host__ -int grad_s7_anchoring_param_init(grad_s7_anch_t * anch) { - - int ia, n1, n2; - const int bcs[6][3] = {{-1,0,0},{1,0,0},{0,-1,0},{0,1,0},{0,0,-1},{0,0,1}}; - - double bc[6][6][3]; - double ** a12inv[3]; - double ** a18inv; - fe_lc_param_t feparam; - KRONECKER_DELTA_CHAR(d_); - - assert(anch); - fe_lc_param(anch->fe, &feparam); - - /* Compute inverse matrices */ - - util_matrix_create(12, 12, &(a12inv[0])); - util_matrix_create(12, 12, &(a12inv[1])); - util_matrix_create(12, 12, &(a12inv[2])); - util_matrix_create(18, 18, &a18inv); - - for (ia = 0; ia < 3; ia++) { - - grad_s7_bcs_coeff(feparam.kappa0, feparam.kappa1, bcs[2*ia + 1], bc); - - for (n1 = 0; n1 < NSYMM; n1++) { - anch->bc.a6inv[ia][n1] = 1.0/bc[n1][n1][ia]; - } - - for (n1 = 0; n1 < NSYMM; n1++) { - for (n2 = 0; n2 < NSYMM; n2++) { - a18inv[ia*NSYMM + n1][0*NSYMM + n2] = 0.5*(1+d_[ia][X])*bc[n1][n2][X]; - a18inv[ia*NSYMM + n1][1*NSYMM + n2] = 0.5*(1+d_[ia][Y])*bc[n1][n2][Y]; - a18inv[ia*NSYMM + n1][2*NSYMM + n2] = 0.5*(1+d_[ia][Z])*bc[n1][n2][Z]; - - } - } - } - - for (n1 = 0; n1 < 12; n1++) { - for (n2 = 0; n2 < 12; n2++) { - a12inv[0][n1][n2] = a18inv[n1][n2]; - a12inv[2][n1][n2] = a18inv[6+n1][6+n2]; - } - } - - for (n1 = 0; n1 < 6; n1++) { - for (n2 = 0; n2 < 6; n2++) { - a12inv[1][n1][n2] = a18inv[n1][n2]; - a12inv[1][n1][6+n2] = a18inv[n1][12+n2]; - } - } - - for (n1 = 6; n1 < 12; n1++) { - for (n2 = 0; n2 < 6; n2++) { - a12inv[1][n1][n2] = a18inv[6+n1][n2]; - a12inv[1][n1][6+n2] = a18inv[6+n1][12+n2]; - } - } - - ia = util_matrix_invert(12, a12inv[0]); - assert(ia == 0); - ia = util_matrix_invert(12, a12inv[1]); - assert(ia == 0); - ia = util_matrix_invert(12, a12inv[2]); - assert(ia == 0); - ia = util_matrix_invert(18, a18inv); - assert(ia == 0); - - for (n1 = 0; n1 < 18; n1++) { - for (n2 = 0; n2 < 18; n2++) { - anch->bc.a18inv[n1][n2] = a18inv[n1][n2]; - } - } - - for (ia = 0; ia < 3; ia++) { - for (n1 = 0; n1 < 12; n1++) { - for (n2 = 0; n2 < 12; n2++) { - anch->bc.a12inv[ia][n1][n2] = a12inv[ia][n1][n2]; - } - } - } - - util_matrix_free(18, &a18inv); - util_matrix_free(12, &(a12inv[2])); - util_matrix_free(12, &(a12inv[1])); - util_matrix_free(12, &(a12inv[0])); - - return 0; -} - - -/***************************************************************************** - * - * grad_s7_bcs_coeff - * - * Full set of coefficients in boundary condition equation for given - * surface normal dn. - * - *****************************************************************************/ - -__host__ __device__ -int grad_s7_bcs_coeff(double kappa0, double kappa1, const int dn[3], - double bc[NSYMM][NSYMM][3]) { - double kappa2; - - kappa2 = kappa0 + kappa1; - - /* XX equation */ - - bc[XX][XX][X] = kappa0*dn[X]; - bc[XX][XY][X] = -kappa1*dn[Y]; - bc[XX][XZ][X] = -kappa1*dn[Z]; - bc[XX][YY][X] = 0.0; - bc[XX][YZ][X] = 0.0; - bc[XX][ZZ][X] = 0.0; - - bc[XX][XX][Y] = kappa1*dn[Y]; - bc[XX][XY][Y] = kappa0*dn[X]; - bc[XX][XZ][Y] = 0.0; - bc[XX][YY][Y] = 0.0; - bc[XX][YZ][Y] = 0.0; - bc[XX][ZZ][Y] = 0.0; - - bc[XX][XX][Z] = kappa1*dn[Z]; - bc[XX][XY][Z] = 0.0; - bc[XX][XZ][Z] = kappa0*dn[X]; - bc[XX][YY][Z] = 0.0; - bc[XX][YZ][Z] = 0.0; - bc[XX][ZZ][Z] = 0.0; - - /* XY equation */ - - bc[XY][XX][X] = kappa0*dn[Y]; - bc[XY][XY][X] = kappa2*dn[X]; - bc[XY][XZ][X] = 0.0; - bc[XY][YY][X] = -kappa1*dn[Y]; - bc[XY][YZ][X] = -kappa1*dn[Z]; - bc[XY][ZZ][X] = 0.0; - - bc[XY][XX][Y] = -kappa1*dn[X]; - bc[XY][XY][Y] = kappa2*dn[Y]; - bc[XY][XZ][Y] = -kappa1*dn[Z]; - bc[XY][YY][Y] = kappa0*dn[X]; - bc[XY][YZ][Y] = 0.0; - bc[XY][ZZ][Y] = 0.0; - - bc[XY][XX][Z] = 0.0; - bc[XY][XY][Z] = 2.0*kappa1*dn[Z]; - bc[XY][XZ][Z] = kappa0*dn[Y]; - bc[XY][YY][Z] = 0.0; - bc[XY][YZ][Z] = kappa0*dn[X]; - bc[XY][ZZ][Z] = 0.0; - - /* XZ equation */ - - bc[XZ][XX][X] = kappa0*dn[Z]; - bc[XZ][XY][X] = 0.0; - bc[XZ][XZ][X] = kappa2*dn[X]; - bc[XZ][YY][X] = 0.0; - bc[XZ][YZ][X] = -kappa1*dn[Y]; - bc[XZ][ZZ][X] = -kappa1*dn[Z]; - - bc[XZ][XX][Y] = 0.0; - bc[XZ][XY][Y] = kappa0*dn[Z]; - bc[XZ][XZ][Y] = 2.0*kappa1*dn[Y]; - bc[XZ][YY][Y] = 0.0; - bc[XZ][YZ][Y] = kappa0*dn[X]; - bc[XZ][ZZ][Y] = 0.0; - - bc[XZ][XX][Z] = -kappa1*dn[X]; - bc[XZ][XY][Z] = -kappa1*dn[Y]; - bc[XZ][XZ][Z] = kappa2*dn[Z]; - bc[XZ][YY][Z] = 0.0; - bc[XZ][YZ][Z] = 0.0; - bc[XZ][ZZ][Z] = kappa0*dn[X]; - - /* YY equation */ - - bc[YY][XX][X] = 0.0; - bc[YY][XY][X] = kappa0*dn[Y]; - bc[YY][XZ][X] = 0.0; - bc[YY][YY][X] = kappa1*dn[X]; - bc[YY][YZ][X] = 0.0; - bc[YY][ZZ][X] = 0.0; - - bc[YY][XX][Y] = 0.0; - bc[YY][XY][Y] = -kappa1*dn[X]; - bc[YY][XZ][Y] = 0.0; - bc[YY][YY][Y] = kappa0*dn[Y]; - bc[YY][YZ][Y] = -kappa1*dn[Z]; - bc[YY][ZZ][Y] = 0.0; - - bc[YY][XX][Z] = 0.0; - bc[YY][XY][Z] = 0.0; - bc[YY][XZ][Z] = 0.0; - bc[YY][YY][Z] = kappa1*dn[Z]; - bc[YY][YZ][Z] = kappa0*dn[Y]; - bc[YY][ZZ][Z] = 0.0; - - /* YZ equation */ - - bc[YZ][XX][X] = 0.0; - bc[YZ][XY][X] = kappa0*dn[Z]; - bc[YZ][XZ][X] = kappa0*dn[Y]; - bc[YZ][YY][X] = 0.0; - bc[YZ][YZ][X] = 2.0*kappa1*dn[X]; - bc[YZ][ZZ][X] = 0.0; - - bc[YZ][XX][Y] = 0.0; - bc[YZ][XY][Y] = 0.0; - bc[YZ][XZ][Y] = -kappa1*dn[X]; - bc[YZ][YY][Y] = kappa0*dn[Z]; - bc[YZ][YZ][Y] = kappa2*dn[Y]; - bc[YZ][ZZ][Y] = -kappa1*dn[Z]; - - bc[YZ][XX][Z] = 0.0; - bc[YZ][XY][Z] = -kappa1*dn[X]; - bc[YZ][XZ][Z] = 0.0; - bc[YZ][YY][Z] = -kappa1*dn[Y]; - bc[YZ][YZ][Z] = kappa2*dn[Z]; - bc[YZ][ZZ][Z] = kappa0*dn[Y]; - - /* ZZ equation */ - - bc[ZZ][XX][X] = 0.0; - bc[ZZ][XY][X] = 0.0; - bc[ZZ][XZ][X] = kappa0*dn[Z]; - bc[ZZ][YY][X] = 0.0; - bc[ZZ][YZ][X] = 0.0; - bc[ZZ][ZZ][X] = kappa1*dn[X]; - - bc[ZZ][XX][Y] = 0.0; - bc[ZZ][XY][Y] = 0.0; - bc[ZZ][XZ][Y] = 0.0; - bc[ZZ][YY][Y] = 0.0; - bc[ZZ][YZ][Y] = kappa0*dn[Z]; - bc[ZZ][ZZ][Y] = kappa1*dn[Y]; - - bc[ZZ][XX][Z] = 0.0; - bc[ZZ][XY][Z] = 0.0; - bc[ZZ][XZ][Z] = -kappa1*dn[X]; - bc[ZZ][YY][Z] = 0.0; - bc[ZZ][YZ][Z] = -kappa1*dn[Y]; - bc[ZZ][ZZ][Z] = kappa0*dn[Z]; - - return 0; -} - /***************************************************************************** * * grad_s7_boundary_coll diff --git a/src/lc_anchoring.c b/src/lc_anchoring.c index a8f2be85b..480ad44de 100644 --- a/src/lc_anchoring.c +++ b/src/lc_anchoring.c @@ -16,6 +16,7 @@ #include #include "lc_anchoring.h" +#include "util.h" /***************************************************************************** * @@ -68,3 +69,198 @@ const char * lc_anchoring_type_from_enum(lc_anchoring_enum_t type) { return "invalid"; } + +/***************************************************************************** + * + * lc_anchoring_matrix1 + * + * Coefficients in the gradient terms. + * + * There are 3 cases (flat face in each coordinate direction) each with + * six equations; for each case we have a diagonal matrix for which the + * inverse may be computed in simple fashion... + * + * We only store the diagonal elements a[3][6]. + * + *****************************************************************************/ + +int lc_anchoring_matrix1(double kappa0, double kappa1, double a[3][6]) { + + /* Normals in six-point stencil */ + const int bcs[6][3] = {{-1,0,0},{1,0,0},{0,-1,0},{0,1,0},{0,0,-1},{0,0,1}}; + + for (int ia = 0; ia < 3; ia++) { + + double bc[6][6][3] = {0}; + + lc_anchoring_coefficients(kappa0, kappa1, bcs[2*ia + 1], bc); + + for (int n1 = 0; n1 < 6; n1++) { + a[ia][n1] = 1.0/bc[n1][n1][ia]; + } + } + + return 0; +} + +/***************************************************************************** + * + * lc_anchoring_matrix12 + * + * Three different cases here for "edges" - the join between two + * adjacent faces. The relevant normals can be x and y, x and z, + * or y and z. + * + * + *****************************************************************************/ + +int lc_anchoring_matrix2(double kappa0, double kappa1, double a[3][12][12]) { + + int ifail = 0; + + double a18[18][18] = {0}; + double ** a12inv[3] = {0}; + + /* Compute inverse matrices */ + + util_matrix_create(12, 12, &(a12inv[0])); + util_matrix_create(12, 12, &(a12inv[1])); + util_matrix_create(12, 12, &(a12inv[2])); + + for (int ia = 0; ia < 3; ia++) { + + int n[3] = {0}; n[ia] = 1; /* Unit normal */ + double bc[6][6][3] = {0}; + + lc_anchoring_coefficients(kappa0, kappa1, n, bc); + + for (int n1 = 0; n1 < NSYMM; n1++) { + for (int n2 = 0; n2 < NSYMM; n2++) { + for (int ib = 0; ib < 3; ib++) { + double dab = (ia == ib); + a18[ia*NSYMM + n1][ib*NSYMM + n2] = 0.5*(1.0 + dab)*bc[n1][n2][ib]; + } + } + } + } + + /* xy: just the first 12 rows ... */ + for (int n1 = 0; n1 < 12; n1++) { + for (int n2 = 0; n2 < 12; n2++) { + a12inv[0][n1][n2] = a18[n1][n2]; + } + } + + /* xz: first 6 and last six rows */ + for (int n1 = 0; n1 < 6; n1++) { + for (int n2 = 0; n2 < 6; n2++) { + a12inv[1][n1][ n2] = a18[n1][n2]; + a12inv[1][n1][6+n2] = a18[n1][12+n2]; + } + } + + for (int n1 = 6; n1 < 12; n1++) { + for (int n2 = 0; n2 < 6; n2++) { + a12inv[1][n1][ n2] = a18[6+n1][n2]; + a12inv[1][n1][6+n2] = a18[6+n1][12+n2]; + } + } + + /* yz: last twelve rows */ + for (int n1 = 0; n1 < 12; n1++) { + for (int n2 = 0; n2 < 12; n2++) { + a12inv[2][n1][n2] = a18[6+n1][6+n2]; + } + } + + ifail += util_matrix_invert(12, a12inv[0]); + ifail += util_matrix_invert(12, a12inv[1]); + ifail += util_matrix_invert(12, a12inv[2]); + + for (int ia = 0; ia < 3; ia++) { + for (int n1 = 0; n1 < 12; n1++) { + for (int n2 = 0; n2 < 12; n2++) { + a[ia][n1][n2] = a12inv[ia][n1][n2]; + } + } + } + + util_matrix_free(12, &(a12inv[2])); + util_matrix_free(12, &(a12inv[1])); + util_matrix_free(12, &(a12inv[0])); + + return ifail; +} + +/***************************************************************************** + * + * lc_anchoring_matrix3 + * + * The most general case we consider: three faces meeting at a corner. + * This gives rise to a single 18x18 matrix. + * + *****************************************************************************/ + +int lc_anchoring_matrix3(double kappa0, double kappa1, double a18[18][18]) { + + int ifail = 0; + + /* Compute system matrix for utility routine */ + double ** a18inv = {0}; + + util_matrix_create(18, 18, &a18inv); + + for (int ia = 0; ia < 3; ia++) { + + int n[3] = {0}; n[ia] = 1; /* Unit normal */ + double bc[6][6][3] = {0}; + + lc_anchoring_coefficients(kappa0, kappa1, n, bc); + + /* One 6x18 block */ + for (int n1 = 0; n1 < NSYMM; n1++) { + for (int n2 = 0; n2 < NSYMM; n2++) { + /* The factor restores the factor 1/2 in off-diagonal blocks + * cf the raw bc[][][] values */ + for (int ib = 0; ib < 3; ib++) { + double dab = (ia == ib); + a18inv[ia*NSYMM + n1][ib*NSYMM + n2] = 0.5*(1.0+dab)*bc[n1][n2][ib]; + } + } + } + } + + /* And invert ... */ + + ifail = util_matrix_invert(18, a18inv); + + for (int n1 = 0; n1 < 18; n1++) { + for (int n2 = 0; n2 < 18; n2++) { + a18[n1][n2] = a18inv[n1][n2]; + } + } + + util_matrix_free(18, &a18inv); + + return ifail; +} + +/***************************************************************************** + * + * lc_anchoring_matrices + * + *****************************************************************************/ + +int lc_anchoring_matrices(double kappa0, double kappa1, + lc_anchoring_matrices_t * matrices) { + + int ifail = 0; + + assert(matrices); + + ifail += lc_anchoring_matrix1(kappa0, kappa1, matrices->a6inv); + ifail += lc_anchoring_matrix2(kappa0, kappa1, matrices->a12inv); + ifail += lc_anchoring_matrix3(kappa0, kappa1, matrices->a18inv); + + return ifail; +} diff --git a/src/lc_anchoring.h b/src/lc_anchoring.h index d9dc7d99b..3151d16a1 100644 --- a/src/lc_anchoring.h +++ b/src/lc_anchoring.h @@ -5,7 +5,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2022 The University of Edinburgh + * (c) 2022-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -15,7 +15,8 @@ #ifndef LUDWIG_LC_ANCHORING_H #define LUDWIG_LC_ANCHORING_H -/* Surface anchoring types */ +/* Surface anchoring types and associated structure */ + typedef enum lc_anchoring_enum { LC_ANCHORING_NONE = 0, LC_ANCHORING_PLANAR, @@ -36,6 +37,22 @@ struct lc_anchoring_param_s { const char * lc_anchoring_type_from_enum(lc_anchoring_enum_t type); lc_anchoring_enum_t lc_anchoring_type_from_string(const char * str); +/* Matrices for gradient terms in the boundary condition */ + +typedef struct lc_anchoring_matrices_s lc_anchoring_matrices_t; + +struct lc_anchoring_matrices_s { + double a6inv[3][6]; /* Single "unknown" (faces) */ + double a12inv[3][12][12]; /* Two unknowns (edges) */ + double a18inv[18][18]; /* Three unknowns (corners) */ +}; + +int lc_anchoring_matrix1(double kappa0, double kappa1, double a6[3][6]); +int lc_anchoring_matrix2(double kappa0, double kappa1, double a12[3][12][12]); +int lc_anchoring_matrix3(double kappa0, double kappa1, double a18[18][18]); +int lc_anchoring_matrices(double kappa0, double kappa1, + lc_anchoring_matrices_t * matrices); + /* Inline */ #include "lc_anchoring_impl.h" diff --git a/src/lc_anchoring_impl.h b/src/lc_anchoring_impl.h index 572f1d851..cea0d78ec 100644 --- a/src/lc_anchoring_impl.h +++ b/src/lc_anchoring_impl.h @@ -272,4 +272,166 @@ static inline void lc_anchoring_planar_ct(const lc_anchoring_param_t * anchor, return; } +/***************************************************************************** + * + * lc_anchoring_coefficents + * + * This computes the coefficients in the gradient terms of the boundary + * conidtion equation which are related to the two elastic constants + * and the unit outward normal (only). The outward normal is the + * integer unit vector dn. + * + * There are six equations each with eighteen terms d_gamma Q_ab. + * The eighteen derivatives are the bc[][6][3] read Q_ab,gamma. + * + *****************************************************************************/ + +__host__ __device__ +static inline void lc_anchoring_coefficients(double kappa0, + double kappa1, + const int dn[3], + double bc[NSYMM][NSYMM][3]) { + double kappa2 = kappa0 + kappa1; + + /* XX equation */ + + bc[XX][XX][X] = kappa0*dn[X]; + bc[XX][XY][X] = -kappa1*dn[Y]; + bc[XX][XZ][X] = -kappa1*dn[Z]; + bc[XX][YY][X] = 0.0; + bc[XX][YZ][X] = 0.0; + bc[XX][ZZ][X] = 0.0; + + bc[XX][XX][Y] = kappa1*dn[Y]; + bc[XX][XY][Y] = kappa0*dn[X]; + bc[XX][XZ][Y] = 0.0; + bc[XX][YY][Y] = 0.0; + bc[XX][YZ][Y] = 0.0; + bc[XX][ZZ][Y] = 0.0; + + bc[XX][XX][Z] = kappa1*dn[Z]; + bc[XX][XY][Z] = 0.0; + bc[XX][XZ][Z] = kappa0*dn[X]; + bc[XX][YY][Z] = 0.0; + bc[XX][YZ][Z] = 0.0; + bc[XX][ZZ][Z] = 0.0; + + /* XY equation */ + + bc[XY][XX][X] = kappa0*dn[Y]; + bc[XY][XY][X] = kappa2*dn[X]; + bc[XY][XZ][X] = 0.0; + bc[XY][YY][X] = -kappa1*dn[Y]; + bc[XY][YZ][X] = -kappa1*dn[Z]; + bc[XY][ZZ][X] = 0.0; + + bc[XY][XX][Y] = -kappa1*dn[X]; + bc[XY][XY][Y] = kappa2*dn[Y]; + bc[XY][XZ][Y] = -kappa1*dn[Z]; + bc[XY][YY][Y] = kappa0*dn[X]; + bc[XY][YZ][Y] = 0.0; + bc[XY][ZZ][Y] = 0.0; + + bc[XY][XX][Z] = 0.0; + bc[XY][XY][Z] = 2.0*kappa1*dn[Z]; + bc[XY][XZ][Z] = kappa0*dn[Y]; + bc[XY][YY][Z] = 0.0; + bc[XY][YZ][Z] = kappa0*dn[X]; + bc[XY][ZZ][Z] = 0.0; + + /* XZ equation */ + + bc[XZ][XX][X] = kappa0*dn[Z]; + bc[XZ][XY][X] = 0.0; + bc[XZ][XZ][X] = kappa2*dn[X]; + bc[XZ][YY][X] = 0.0; + bc[XZ][YZ][X] = -kappa1*dn[Y]; + bc[XZ][ZZ][X] = -kappa1*dn[Z]; + + bc[XZ][XX][Y] = 0.0; + bc[XZ][XY][Y] = kappa0*dn[Z]; + bc[XZ][XZ][Y] = 2.0*kappa1*dn[Y]; + bc[XZ][YY][Y] = 0.0; + bc[XZ][YZ][Y] = kappa0*dn[X]; + bc[XZ][ZZ][Y] = 0.0; + + bc[XZ][XX][Z] = -kappa1*dn[X]; + bc[XZ][XY][Z] = -kappa1*dn[Y]; + bc[XZ][XZ][Z] = kappa2*dn[Z]; + bc[XZ][YY][Z] = 0.0; + bc[XZ][YZ][Z] = 0.0; + bc[XZ][ZZ][Z] = kappa0*dn[X]; + + /* YY equation */ + + bc[YY][XX][X] = 0.0; + bc[YY][XY][X] = kappa0*dn[Y]; + bc[YY][XZ][X] = 0.0; + bc[YY][YY][X] = kappa1*dn[X]; + bc[YY][YZ][X] = 0.0; + bc[YY][ZZ][X] = 0.0; + + bc[YY][XX][Y] = 0.0; + bc[YY][XY][Y] = -kappa1*dn[X]; + bc[YY][XZ][Y] = 0.0; + bc[YY][YY][Y] = kappa0*dn[Y]; + bc[YY][YZ][Y] = -kappa1*dn[Z]; + bc[YY][ZZ][Y] = 0.0; + + bc[YY][XX][Z] = 0.0; + bc[YY][XY][Z] = 0.0; + bc[YY][XZ][Z] = 0.0; + bc[YY][YY][Z] = kappa1*dn[Z]; + bc[YY][YZ][Z] = kappa0*dn[Y]; + bc[YY][ZZ][Z] = 0.0; + + /* YZ equation */ + + bc[YZ][XX][X] = 0.0; + bc[YZ][XY][X] = kappa0*dn[Z]; + bc[YZ][XZ][X] = kappa0*dn[Y]; + bc[YZ][YY][X] = 0.0; + bc[YZ][YZ][X] = 2.0*kappa1*dn[X]; + bc[YZ][ZZ][X] = 0.0; + + bc[YZ][XX][Y] = 0.0; + bc[YZ][XY][Y] = 0.0; + bc[YZ][XZ][Y] = -kappa1*dn[X]; + bc[YZ][YY][Y] = kappa0*dn[Z]; + bc[YZ][YZ][Y] = kappa2*dn[Y]; + bc[YZ][ZZ][Y] = -kappa1*dn[Z]; + + bc[YZ][XX][Z] = 0.0; + bc[YZ][XY][Z] = -kappa1*dn[X]; + bc[YZ][XZ][Z] = 0.0; + bc[YZ][YY][Z] = -kappa1*dn[Y]; + bc[YZ][YZ][Z] = kappa2*dn[Z]; + bc[YZ][ZZ][Z] = kappa0*dn[Y]; + + /* ZZ equation */ + + bc[ZZ][XX][X] = 0.0; + bc[ZZ][XY][X] = 0.0; + bc[ZZ][XZ][X] = kappa0*dn[Z]; + bc[ZZ][YY][X] = 0.0; + bc[ZZ][YZ][X] = 0.0; + bc[ZZ][ZZ][X] = kappa1*dn[X]; + + bc[ZZ][XX][Y] = 0.0; + bc[ZZ][XY][Y] = 0.0; + bc[ZZ][XZ][Y] = 0.0; + bc[ZZ][YY][Y] = 0.0; + bc[ZZ][YZ][Y] = kappa0*dn[Z]; + bc[ZZ][ZZ][Y] = kappa1*dn[Y]; + + bc[ZZ][XX][Z] = 0.0; + bc[ZZ][XY][Z] = 0.0; + bc[ZZ][XZ][Z] = -kappa1*dn[X]; + bc[ZZ][YY][Z] = 0.0; + bc[ZZ][YZ][Z] = -kappa1*dn[Y]; + bc[ZZ][ZZ][Z] = kappa0*dn[Z]; + + return; +} + #endif From 9da478a45217415f533f1fd037bd99e44cc457eb Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 5 Jan 2023 08:48:37 +0000 Subject: [PATCH 209/244] Correct typos in nvcc section --- src/compiler.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler.c b/src/compiler.c index 72dc0fb54..3b8c17adc 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -92,8 +92,8 @@ int compiler_id(compiler_info_t * compiler) { compiler->major = __CUDACC_VER_MAJOR__; compiler->minor = __CUDACC_VER_MINOR__; compiler->patchlevel = __CUDACC_VER_BUILD__; - compiler->version = "null"); /* None provided */ - compiler->name, "NVIDIA nvcc"; + compiler->version = "none"; /* None provided */ + compiler->name = "NVIDIA nvcc"; compiler->options = compiler_options(); compiler->commit = git_hash(); /* Include __CUDA_ARCH__ */ From b81296e1cad63e3e688330e0e5a7103782362176 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 5 Jan 2023 16:42:07 +0000 Subject: [PATCH 210/244] Remove duplicate surface anchoring parameters --- src/blue_phase.c | 3 +- src/blue_phase.h | 16 ++------ src/blue_phase_rt.c | 54 ++++++++++++++----------- src/build.c | 4 +- src/fe_lc_stats.c | 98 ++++++++++++++++++++++++++++++++++++++++----- 5 files changed, 124 insertions(+), 51 deletions(-) diff --git a/src/blue_phase.c b/src/blue_phase.c index e2b95361b..de6b1fbf9 100644 --- a/src/blue_phase.c +++ b/src/blue_phase.c @@ -1373,7 +1373,8 @@ int fe_lc_redshift_set(fe_lc_t * fe, double redshift) { * *****************************************************************************/ -__host__ __device__ int fe_lc_amplitude_compute(fe_lc_param_t * param, double * a) { +__host__ __device__ int fe_lc_amplitude_compute(const fe_lc_param_t * param, + double * a) { assert(a); diff --git a/src/blue_phase.h b/src/blue_phase.h index 6400434ff..a05545bcb 100644 --- a/src/blue_phase.h +++ b/src/blue_phase.h @@ -1,15 +1,13 @@ - /***************************************************************************** * * fe_lc.h * - * This file name is a bit of a misnomer. It's for any LC type, not - * just blue phases. + * Liquid crystal free energy. * * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2010-2022 The University of Edinburgh + * (c) 2010-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -69,14 +67,6 @@ struct fe_lc_param_s { double e0[3]; /* Electric field (external) */ double coswt; /* Electric field (phase) */ - double w1_coll; /* Anchoring strength parameter */ - double w2_coll; /* Second anchoring parameter */ - double w1_wall; - double w2_wall; - - double nfix[3]; /* Fixed anchoring orientation */ - int anchoring_coll; /* Colloids anchoring type */ - int anchoring_wall; /* Wall anchoring type */ int is_redshift_updated; /* Switch */ int is_active; /* Switch for active fluid */ @@ -187,7 +177,7 @@ __host__ int fe_lc_dimensionless_field_strength(const fe_lc_param_t * param, double * e0); __host__ __device__ -int fe_lc_amplitude_compute(fe_lc_param_t * param, double * a); +int fe_lc_amplitude_compute(const fe_lc_param_t * param, double * a); __host__ __device__ int fe_lc_q_uniaxial(fe_lc_param_t * param, const double n[3], double q[3][3]); diff --git a/src/blue_phase_rt.c b/src/blue_phase_rt.c index 7d079116e..9b1c31ad6 100644 --- a/src/blue_phase_rt.c +++ b/src/blue_phase_rt.c @@ -9,7 +9,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2009-2022 The University of Edinburgh + * (c) 2009-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -218,11 +218,6 @@ __host__ int blue_phase_init_rt(pe_t * pe, rt_t *rt, if (fe_param.wall.type == LC_ANCHORING_PLANAR) { pe_info(pe, "Wall anchoring w2: %14.7e\n", fe_param.wall.w2); } - - /* Still need to get energy right ... */ - fe_param.anchoring_wall = fe_param.wall.type; - fe_param.w1_wall = fe_param.wall.w1; - fe_param.w2_wall = fe_param.wall.w2; } if (fe_param.coll.type != LC_ANCHORING_NONE) { @@ -240,14 +235,15 @@ __host__ int blue_phase_init_rt(pe_t * pe, rt_t *rt, pe_info(pe, "Colloid anchoring w1: %14.7e\n", fe_param.coll.w1); pe_info(pe, "Colloid anchoring w2: %14.7e\n", fe_param.coll.w2); } - /* Temporary fix to get energy right ... */ - fe_param.anchoring_coll = fe_param.coll.type; - fe_param.w1_coll = fe_param.coll.w1; - fe_param.w2_coll = fe_param.coll.w2; } } else if (strcmp(method, "two") == 0) { + lc_anchoring_enum_t anchoring_wall = LC_ANCHORING_NONE; + lc_anchoring_enum_t anchoring_coll = LC_ANCHORING_NONE; + double w1_coll; + double w2_coll; + /* Older-style input for "lc_anchoring_method". The name "two" * is because, historically, it was the second method tried. */ @@ -264,11 +260,11 @@ __host__ int blue_phase_init_rt(pe_t * pe, rt_t *rt, rt_string_parameter(rt, "lc_coll_anchoring", type, FILENAME_MAX); if (strcmp(type, "normal") == 0) { - fe_param.anchoring_coll = LC_ANCHORING_NORMAL; + anchoring_coll = LC_ANCHORING_NORMAL; } if (strcmp(type, "planar") == 0) { - fe_param.anchoring_coll = LC_ANCHORING_PLANAR; + anchoring_coll = LC_ANCHORING_PLANAR; } /* Surface free energy parameter */ @@ -292,13 +288,13 @@ __host__ int blue_phase_init_rt(pe_t * pe, rt_t *rt, rt_string_parameter(rt, "lc_wall_anchoring", type_wall, FILENAME_MAX); if (strcmp(type_wall, "normal") == 0) { - fe_param.anchoring_wall = LC_ANCHORING_NORMAL; + anchoring_wall = LC_ANCHORING_NORMAL; w1_wall = w1; w2_wall = 0.0; } if (strcmp(type_wall, "planar") == 0) { - fe_param.anchoring_wall = LC_ANCHORING_PLANAR; + anchoring_wall = LC_ANCHORING_PLANAR; w1_wall = w1; w2_wall = w2; } @@ -306,15 +302,18 @@ __host__ int blue_phase_init_rt(pe_t * pe, rt_t *rt, if (strcmp(type_wall, "fixed") == 0) { double nfix[3] = {0.0, 1.0, 0.0}; /* default orientation */ double rmod; - fe_param.anchoring_wall = LC_ANCHORING_FIXED; + anchoring_wall = LC_ANCHORING_FIXED; w1_wall = w1; w2_wall = 0.0; rt_double_parameter_vector(rt, "lc_wall_fixed_orientation", nfix); /* Make sure it's a unit vector */ rmod = 1.0/sqrt(nfix[X]*nfix[X] + nfix[Y]*nfix[Y] + nfix[Z]*nfix[Z]); - fe_param.nfix[X] = rmod*nfix[X]; - fe_param.nfix[Y] = rmod*nfix[Y]; - fe_param.nfix[Z] = rmod*nfix[Z]; + nfix[X] = rmod*nfix[X]; + nfix[Y] = rmod*nfix[Y]; + nfix[Z] = rmod*nfix[Z]; + fe_param.wall.nfix[X] = nfix[X]; + fe_param.wall.nfix[Y] = nfix[Y]; + fe_param.wall.nfix[Z] = nfix[Z]; } /* Colloids default, then look for specific value */ @@ -330,8 +329,8 @@ __host__ int blue_phase_init_rt(pe_t * pe, rt_t *rt, if (strcmp(type, "fixed") == 0) w2 = 0.0; } - fe_param.w1_coll = w1; - fe_param.w2_coll = w2; + w1_coll = w1; + w2_coll = w2; /* Wall */ @@ -342,8 +341,8 @@ __host__ int blue_phase_init_rt(pe_t * pe, rt_t *rt, if (strcmp(type_wall, "fixed") == 0) w2_wall = 0.0; } - fe_param.w1_wall = w1_wall; - fe_param.w2_wall = w2_wall; + w1_wall = w1_wall; + w2_wall = w2_wall; fe_lc_amplitude_compute(&fe_param, &0); pe_info(pe, "Anchoring type (walls): = %14s\n", type_wall); @@ -357,15 +356,22 @@ __host__ int blue_phase_init_rt(pe_t * pe, rt_t *rt, w1_wall/fe_param.kappa0); pe_info(pe, "Computed surface order f(gamma) = %14.7e\n", amp0); - if (fe_param.anchoring_wall == LC_ANCHORING_FIXED) { + if (anchoring_wall == LC_ANCHORING_FIXED) { pe_info(pe, "Wall fixed anchoring orientation = %14.7e %14.7e %14.7e\n", - fe_param.nfix[X], fe_param.nfix[Y], fe_param.nfix[Z]); + fe_param.wall.nfix[X], fe_param.wall.nfix[Y], fe_param.wall.nfix[Z]); } /* For computed anchoring order [see fe_lc_amplitude_compute()] */ if (fe_param.gamma < (8.0/3.0)) { pe_fatal(pe, "Please check anchoring amplitude\n"); } + + fe_param.coll.type = anchoring_coll; + fe_param.coll.w1 = w1_coll; + fe_param.coll.w2 = w2_coll; + fe_param.wall.type = anchoring_wall; + fe_param.wall.w1 = w1_wall; + fe_param.wall.w2 = w2_wall; } else { /* not recognised */ diff --git a/src/build.c b/src/build.c index cb047cae9..0f59151fe 100644 --- a/src/build.c +++ b/src/build.c @@ -9,7 +9,7 @@ * Edinburgh Soft Matter and Statisitical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2006-2021 The University of Edinburgh + * (c) 2006-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -1200,7 +1200,7 @@ int build_replace_q_local(fe_t * fe, colloids_info_t * info, colloid_t * pc, /* For planar degenerate anchoring we subtract the projection of a randomly oriented unit vector on rb and renormalise the result */ - if (lc_param->anchoring_coll == LC_ANCHORING_PLANAR) { + if (lc_param->coll.type == LC_ANCHORING_PLANAR) { util_random_unit_vector(&pc->s.rng, rhat); diff --git a/src/fe_lc_stats.c b/src/fe_lc_stats.c index f7ebb3cf8..12ca8db72 100644 --- a/src/fe_lc_stats.c +++ b/src/fe_lc_stats.c @@ -8,7 +8,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2017-2022 The University of Edinburgh + * (c) 2017-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -379,6 +379,87 @@ int fe_lc_wallz(cs_t * cs, fe_lc_t * fe, double * fs) { return 0; } +/***************************************************************************** + * + * colloids_q_boundary + * + * Produce an estimate of the surface order parameter Q^0_ab for + * normal or planar anchoring. + * + * This will depend on the outward surface normal nhat, and in the + * case of planar anchoring may depend on the estimate of the + * existing order parameter at the surface Qs_ab. + * + * This planar anchoring idea follows e.g., Fournier and Galatola + * Europhys. Lett. 72, 403 (2005). + * + *****************************************************************************/ + +int colloids_q_boundary(fe_lc_param_t * param, + const double nhat[3], double qs[3][3], + double q0[3][3], int map_status) { + int ia, ib, ic, id; + int anchoring; + + double qtilde[3][3]; + double amp; + KRONECKER_DELTA_CHAR(d); + + assert(map_status == MAP_COLLOID || map_status == MAP_BOUNDARY); + + anchoring = param->coll.type; + if (map_status == MAP_BOUNDARY) anchoring = param->wall.type; + + fe_lc_amplitude_compute(param, &); + + if (anchoring == LC_ANCHORING_FIXED) { + assert(map_status == MAP_BOUNDARY); + for (ia = 0; ia < 3; ia++) { + double na = param->wall.nfix[ia]; + for (ib = 0; ib < 3; ib++) { + double nb = param->wall.nfix[ib]; + q0[ia][ib] = 0.5*amp*(3.0*na*nb - d[ia][ib]); + } + } + } + + if (anchoring == LC_ANCHORING_NORMAL) { + for (ia = 0; ia < 3; ia++) { + for (ib = 0; ib < 3; ib++) { + q0[ia][ib] = 0.5*amp*(3.0*nhat[ia]*nhat[ib] - d[ia][ib]); + } + } + } + + if (anchoring == LC_ANCHORING_PLANAR) { + + /* Planar: use the fluid Q_ab to find ~Q_ab */ + + for (ia = 0; ia < 3; ia++) { + for (ib = 0; ib < 3; ib++) { + qtilde[ia][ib] = qs[ia][ib] + 0.5*amp*d[ia][ib]; + } + } + + for (ia = 0; ia < 3; ia++) { + for (ib = 0; ib < 3; ib++) { + q0[ia][ib] = 0.0; + for (ic = 0; ic < 3; ic++) { + for (id = 0; id < 3; id++) { + q0[ia][ib] += (d[ia][ic] - nhat[ia]*nhat[ic])*qtilde[ic][id] + *(d[id][ib] - nhat[id]*nhat[ib]); + } + } + /* Return Q^0_ab = ~Q_ab - (1/2) A d_ab */ + q0[ia][ib] -= 0.5*amp*d[ia][ib]; + } + } + + } + + return 0; +} + /***************************************************************************** * * fe_lc_colloid @@ -531,12 +612,6 @@ static int fe_lc_colloid(fe_lc_t * fe, cs_t * cs, colloids_info_t * cinfo, * fluid Q_ab qs * site map status * - * TODO: There is a rather ugly depedency on the order parameter - * gradient calculation currently in gradient_3d_7pt_solid which - * needs to be refactored. That is: colloids_q_boundary(). - * - * E.g., Can we use lc_anchoring.h? - * *****************************************************************************/ __host__ int blue_phase_fs(fe_lc_param_t * feparam, const double dn[3], @@ -555,12 +630,12 @@ __host__ int blue_phase_fs(fe_lc_param_t * feparam, const double dn[3], colloids_q_boundary(feparam, dn, qs, q0, status); - w1 = feparam->w1_coll; - w2 = feparam->w2_coll; + w1 = feparam->coll.w1; + w2 = feparam->coll.w2; if (status == MAP_BOUNDARY) { - w1 = feparam->w1_wall; - w2 = feparam->w2_wall; + w1 = feparam->wall.w1; + w2 = feparam->wall.w2; } fe_lc_amplitude_compute(feparam, &litude); @@ -582,6 +657,7 @@ __host__ int blue_phase_fs(fe_lc_param_t * feparam, const double dn[3], return 0; } + /***************************************************************************** * * fe_lc_bulk_grad From b84b928f45185b9eb8693343704812b1b17378a8 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 5 Jan 2023 16:42:43 +0000 Subject: [PATCH 211/244] Remove unnecessary values --- src/lc_anchoring.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lc_anchoring.c b/src/lc_anchoring.c index 480ad44de..48f39950f 100644 --- a/src/lc_anchoring.c +++ b/src/lc_anchoring.c @@ -86,14 +86,12 @@ const char * lc_anchoring_type_from_enum(lc_anchoring_enum_t type) { int lc_anchoring_matrix1(double kappa0, double kappa1, double a[3][6]) { - /* Normals in six-point stencil */ - const int bcs[6][3] = {{-1,0,0},{1,0,0},{0,-1,0},{0,1,0},{0,0,-1},{0,0,1}}; - for (int ia = 0; ia < 3; ia++) { + int n[3] = {0}; n[ia] = 1; /* Unit normal */ double bc[6][6][3] = {0}; - lc_anchoring_coefficients(kappa0, kappa1, bcs[2*ia + 1], bc); + lc_anchoring_coefficients(kappa0, kappa1, n, bc); for (int n1 = 0; n1 < 6; n1++) { a[ia][n1] = 1.0/bc[n1][n1][ia]; From 7ba1072209eb447bd0f2f4641ff49920ac38f2d0 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 5 Jan 2023 16:43:07 +0000 Subject: [PATCH 212/244] Remove replicated code --- src/gradient_3d_7pt_solid.c | 282 +++++++++++++----------------------- 1 file changed, 100 insertions(+), 182 deletions(-) diff --git a/src/gradient_3d_7pt_solid.c b/src/gradient_3d_7pt_solid.c index c124b8451..fc4a6dcc8 100644 --- a/src/gradient_3d_7pt_solid.c +++ b/src/gradient_3d_7pt_solid.c @@ -56,10 +56,12 @@ #include #include "pe.h" -#include "util.h" #include "coords.h" #include "kernel.h" #include "colloids.h" + +#include "util.h" +#include "lc_anchoring.h" #include "gradient_3d_7pt_solid.h" struct grad_lc_anch_s { @@ -82,16 +84,14 @@ __global__ void gradient_6x6_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_lc_anch_t * anch, fe_lc_t * fe, field_grad_t * fg, map_t * map, colloids_info_t * cinfo); -__host__ __device__ -int colloids_q_boundary(fe_lc_param_t * param, const double n[3], - double qs[3][3], double q0[3][3], - int map_status); -__host__ __device__ -int q_boundary_constants(cs_t * cs, fe_lc_param_t * param, grad_lc_anch_t * anch, - int ic, int jc, int kc, - double qs[3][3], - const int di[3], int status, double c[3][3], - colloids_info_t * cinfo); + +__host__ __device__ int grad_3d_7pt_bc(grad_lc_anch_t * anch, + fe_lc_param_t * fep, + int ic, int jc, int kc, + int status, + const double qs[3][3], + const int di[3], + double c[3][3]); /***************************************************************************** * @@ -391,10 +391,10 @@ void gradient_6x6_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_lc_anch_t * anch, qs[Z][Z] = 0.0 - q->data[addr_rank1(q->nsites, NQAB, index, XX)] - q->data[addr_rank1(q->nsites, NQAB, index, YY)]; - - q_boundary_constants(cs, fe->param, anch, ic, jc, kc, qs, - bcs[normal[0]], status[normal[0]], c, cinfo); - + + grad_3d_7pt_bc(anch, fe->param, ic, jc, kc, status[normal[0]], qs, + bcs[normal[0]], c); + /* Constant terms all move to RHS (hence -ve sign). Factors * of two in off-diagonals agree with matrix coefficients. */ @@ -415,10 +415,10 @@ void gradient_6x6_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_lc_anch_t * anch, } if (nunknown > 1) { - - q_boundary_constants(cs, fe->param, anch, ic, jc, kc, qs, - bcs[normal[1]], status[normal[1]], c, cinfo); - + + grad_3d_7pt_bc(anch, fe->param, ic, jc, kc, status[normal[1]], qs, + bcs[normal[1]], c); + b18[1*NSYMM + XX] = -1.0*c[X][X]; b18[1*NSYMM + XY] = -2.0*c[X][Y]; b18[1*NSYMM + XZ] = -2.0*c[X][Z]; @@ -435,10 +435,10 @@ void gradient_6x6_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_lc_anch_t * anch, } if (nunknown > 2) { - - q_boundary_constants(cs, fe->param, anch, ic, jc, kc, qs, - bcs[normal[2]], status[normal[2]], c, cinfo); - + + grad_3d_7pt_bc(anch, fe->param, ic, jc, kc, status[normal[2]], qs, + bcs[normal[2]], c); + b18[2*NSYMM + XX] = -1.0*c[X][X]; b18[2*NSYMM + XY] = -2.0*c[X][Y]; b18[2*NSYMM + XZ] = -2.0*c[X][Z]; @@ -617,65 +617,68 @@ void gradient_6x6_kernel(kernel_ctxt_t * ktx, cs_t * cs, grad_lc_anch_t * anch, /***************************************************************************** * - * q_boundary_constants + * grad_3d_7pt_bc + * + * Driver to compute the constant term c_ab in the boundary consition + * dependent on the relevant surface free energy. + * + * (1) Watch out for the different unit vectors here. di[] is the + * lattice vector at the outward face; dnhat is relative to + * colloid centre in the colloid case and is used to compute the + * preferred surface Q_0. This was the choice at + * the time this was first implemented. * - * Compute the constant term in the cholesteric boundary condition. - * Fluid point is (ic, jc, kc) with fluid Q_ab = qs - * The outward normal is di[3], and the map status is as given. + * (2) The "s7" method, in contrast, uses dnhat for both purposes, + * and so may be a more consistent choice. * *****************************************************************************/ -__host__ __device__ -int q_boundary_constants(cs_t * cs, - fe_lc_param_t * param, grad_lc_anch_t * anch, - int ic, int jc, int kc, - double qs[3][3], - const int di[3], int status, double c[3][3], - colloids_info_t * cinfo) { - int index; - int ia, ib, ig, ih; +__host__ __device__ int grad_3d_7pt_bc(grad_lc_anch_t * anch, + fe_lc_param_t * fep, + int ic, int jc, int kc, + int status, + const double qs[3][3], + const int di[3], + double c[3][3]) { + assert(anch); + assert(fep); + int anchor; int noffset[3]; double w1, w2; double dnhat[3]; - double qtilde[3][3]; + double qtilde[3][3] = {0}; double q0[3][3]; double q2 = 0.0; double rd; double amp; - double phi, wphi; KRONECKER_DELTA_CHAR(d); - LEVI_CIVITA_CHAR(e); - colloid_t * pc = NULL; - - assert(cs); - assert(param); - assert(cinfo); + fe_lc_amplitude_compute(fep, &); /* Default -> outward normal, ie., flat wall */ - w1 = param->w1_wall; - w2 = param->w2_wall; - anchor = param->anchoring_wall; + w1 = fep->wall.w1; + w2 = fep->wall.w2; + anchor = fep->wall.type; dnhat[X] = 1.0*di[X]; dnhat[Y] = 1.0*di[Y]; dnhat[Z] = 1.0*di[Z]; - fe_lc_amplitude_compute(param, &); if (status == MAP_COLLOID) { - cs_nlocal_offset(cs, noffset); - index = cs_index(cs, ic - di[X], jc - di[Y], kc - di[Z]); + int index = cs_index(anch->cs, ic - di[X], jc - di[Y], kc - di[Z]); + colloid_t * pc = anch->cinfo->map_new[index]; + + cs_nlocal_offset(anch->cs, noffset); - pc = cinfo->map_new[index]; assert(pc); - w1 = param->w1_coll; - w2 = param->w2_coll; - anchor = param->anchoring_coll; + w1 = fep->coll.w1; + w2 = fep->coll.w2; + anchor = fep->coll.type; dnhat[X] = 1.0*(noffset[X] + ic) - pc->s.r[X]; dnhat[Y] = 1.0*(noffset[Y] + jc) - pc->s.r[Y]; @@ -688,39 +691,40 @@ int q_boundary_constants(cs_t * cs, dnhat[Z] *= rd; } + + if (anchor == LC_ANCHORING_FIXED) { - for (ia = 0; ia < 3; ia++) { - for (ib = 0; ib < 3; ib++) { - q0[ia][ib] = 0.5*amp*(3.0*param->nfix[ia]*param->nfix[ib] - d[ia][ib]); - qtilde[ia][ib] = 0.0; - } - } + /* Wall anchoring only. */ + lc_anchoring_fixed_ct(&fep->wall, qs, dnhat, fep->kappa1, fep->q0, amp, c); } if (anchor == LC_ANCHORING_NORMAL) { - for (ia = 0; ia < 3; ia++) { - for (ib = 0; ib < 3; ib++) { - q0[ia][ib] = 0.5*amp*(3.0*dnhat[ia]*dnhat[ib] - d[ia][ib]); - qtilde[ia][ib] = 0.0; + /* Here's the latice unit vector (note dnhat used for preferred Q_0) */ + double nhat[3] = {1.0*di[X], 1.0*di[Y], 1.0*di[Z]}; + + lc_anchoring_normal_q0(dnhat, amp, q0); + lc_anchoring_kappa1_ct(fep->kappa1, fep->q0, nhat, qs, c); + + for (int ia = 0; ia < 3; ia++) { + for (int ib = 0; ib < 3; ib++) { + c[ia][ib] += -w1*(qs[ia][ib] - q0[ia][ib]); } } } if (anchor == LC_ANCHORING_PLANAR) { - /* Compute qtilde, and its projection */ + /* See note above */ + double hat[3] = {1.0*di[X], 1.0*di[Y], 1.0*di[Z]}; + q2 = 0.0; - for (ia = 0; ia < 3; ia++) { - for (ib = 0; ib < 3; ib++) { - qtilde[ia][ib] = qs[ia][ib] + 0.5*amp*d[ia][ib]; - q2 += qtilde[ia][ib]*qtilde[ia][ib]; - } - } + lc_anchoring_planar_qtilde(amp, qs, qtilde); - for (ia = 0; ia < 3; ia++) { - for (ib = 0; ib < 3; ib++) { + for (int ia = 0; ia < 3; ia++) { + for (int ib = 0; ib < 3; ib++) { q0[ia][ib] = 0.0; - for (ig = 0; ig < 3; ig++) { - for (ih = 0; ih < 3; ih++) { + q2 += qtilde[ia][ib]*qtilde[ia][ib]; + for (int ig = 0; ig < 3; ig++) { + for (int ih = 0; ih < 3; ih++) { q0[ia][ib] += (d[ia][ig] - dnhat[ia]*dnhat[ig])*qtilde[ig][ih] *(d[ih][ib] - dnhat[ih]*dnhat[ib]); } @@ -728,126 +732,40 @@ int q_boundary_constants(cs_t * cs, q0[ia][ib] -= 0.5*amp*d[ia][ib]; } } - } - - /* Compute c[a][b] */ - - for (ia = 0; ia < 3; ia++) { - for (ib = 0; ib < 3; ib++) { - - c[ia][ib] = 0.0; - - for (ig = 0; ig < 3; ig++) { - for (ih = 0; ih < 3; ih++) { - c[ia][ib] -= param->kappa1*param->q0*di[ig]* - (e[ia][ig][ih]*qs[ih][ib] + e[ib][ig][ih]*qs[ih][ia]); - } - } - - /* Normal anchoring: w2 must be zero and q0 is preferred Q - * Planar anchoring: in w1 term q0 is effectively - * (Qtilde^perp - 0.5S_0) while in w2 we - * have Qtilde appearing explicitly. - * See colloids_q_boundary() etc */ - - /* Default case without compositional order parameter */ - if (anch->phi == NULL) { - wphi = 1.0; - } - else { - /* Compositional order parameter for LC wetting: - * The LC anchoring strengths w1 and w2 vanish in the disordered phase. - * We assume this is the phase which has a negative binary - * order parameter e.g. phi = -1. - * The standard anchoring case corresponds to phi = +1 */ - index = cs_index(cs, ic, jc, kc); - phi = anch->phi->data[addr_rank0(anch->phi->nsites, index)]; - wphi = 0.5*(1.0+phi); - } - - c[ia][ib] += - -w1*wphi*(qs[ia][ib] - q0[ia][ib]) - -w2*wphi*(2.0*q2 - 4.5*amp*amp)*qtilde[ia][ib]; - } - } - return 0; -} + /* Compute c[a][b] */ -/***************************************************************************** - * - * colloids_q_boundary - * - * Produce an estimate of the surface order parameter Q^0_ab for - * normal or planar anchoring. - * - * This will depend on the outward surface normal nhat, and in the - * case of planar anchoring may depend on the estimate of the - * existing order parameter at the surface Qs_ab. - * - * This planar anchoring idea follows e.g., Fournier and Galatola - * Europhys. Lett. 72, 403 (2005). - * - *****************************************************************************/ - -__host__ __device__ -int colloids_q_boundary(fe_lc_param_t * param, - const double nhat[3], double qs[3][3], - double q0[3][3], int map_status) { - int ia, ib, ic, id; - int anchoring; - - double qtilde[3][3]; - double amp; - KRONECKER_DELTA_CHAR(d); - - assert(map_status == MAP_COLLOID || map_status == MAP_BOUNDARY); - - anchoring = param->anchoring_coll; - if (map_status == MAP_BOUNDARY) anchoring = param->anchoring_wall; - - fe_lc_amplitude_compute(param, &); + lc_anchoring_kappa1_ct(fep->kappa1, fep->q0, hat, qs, c); - if (anchoring == LC_ANCHORING_FIXED) { - for (ia = 0; ia < 3; ia++) { - for (ib = 0; ib < 3; ib++) { - q0[ia][ib] = 0.5*amp*(3.0*param->nfix[ia]*param->nfix[ib] - d[ia][ib]); + for (int ia = 0; ia < 3; ia++) { + for (int ib = 0; ib < 3; ib++) { + c[ia][ib] += + -w1*(qs[ia][ib] - q0[ia][ib]) + -w2*(2.0*q2 - 4.5*amp*amp)*qtilde[ia][ib]; } } } - if (anchoring == LC_ANCHORING_NORMAL) { - for (ia = 0; ia < 3; ia++) { - for (ib = 0; ib < 3; ib++) { - q0[ia][ib] = 0.5*amp*(3.0*nhat[ia]*nhat[ib] - d[ia][ib]); - } - } - } + /* Experimental liquid crystal emulsion term */ - if (anchoring == LC_ANCHORING_PLANAR) { + if (anch->phi) { - /* Planar: use the fluid Q_ab to find ~Q_ab */ + /* Compositional order parameter for LC wetting: + * The LC anchoring strengths w1 and w2 vanish in the disordered phase. + * We assume this is the phase which has a negative binary + * order parameter e.g. phi = -1. + * The standard anchoring case corresponds to phi = +1 */ - for (ia = 0; ia < 3; ia++) { - for (ib = 0; ib < 3; ib++) { - qtilde[ia][ib] = qs[ia][ib] + 0.5*amp*d[ia][ib]; - } - } + int index = cs_index(anch->cs, ic, jc, kc); + double phi = anch->phi->data[addr_rank0(anch->phi->nsites, index)]; + double wphi = 0.5*(1.0 + phi); - for (ia = 0; ia < 3; ia++) { - for (ib = 0; ib < 3; ib++) { - q0[ia][ib] = 0.0; - for (ic = 0; ic < 3; ic++) { - for (id = 0; id < 3; id++) { - q0[ia][ib] += (d[ia][ic] - nhat[ia]*nhat[ic])*qtilde[ic][id] - *(d[id][ib] - nhat[id]*nhat[ib]); - } - } - /* Return Q^0_ab = ~Q_ab - (1/2) A d_ab */ - q0[ia][ib] -= 0.5*amp*d[ia][ib]; + /* Just an additional factor of wphi ... */ + for (int ia = 0; ia < 3; ia++) { + for (int ib = 0; ib < 3; ib++) { + c[ia][ib] *= wphi; } } - } return 0; From 6ec9718eda4c8e5f431e4790fb000d9c430e6129 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 5 Jan 2023 16:43:42 +0000 Subject: [PATCH 213/244] Fixed surface anchoring free energy correction --- tests/regression/d3q19-short/serial-anch-wn3.log | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/d3q19-short/serial-anch-wn3.log b/tests/regression/d3q19-short/serial-anch-wn3.log index 790b78c18..da1699b32 100644 --- a/tests/regression/d3q19-short/serial-anch-wn3.log +++ b/tests/regression/d3q19-short/serial-anch-wn3.log @@ -133,7 +133,7 @@ Scalars - total mean variance min max [Qyz] 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 Free energies - timestep f v f/v f_s1 fs_s2 redshift -[fe] 1000 -1.3615060139e-02 2.5600000000e+02 -5.3183828668e-05 3.7994558242e-04 3.7604271017e-04 1.0000000000e+00 +[fe] 1000 -1.3615060139e-02 2.5600000000e+02 -5.3183828668e-05 2.6621805403e-04 1.6282220951e-04 1.0000000000e+00 Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 From 7fb73dc67b3dc2081d9a67491e77ba2b58590b0d Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 6 Jan 2023 09:52:21 +0000 Subject: [PATCH 214/244] Copy additional device pointer for colloid information --- src/gradient_3d_7pt_solid.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/gradient_3d_7pt_solid.c b/src/gradient_3d_7pt_solid.c index fc4a6dcc8..327ca69ff 100644 --- a/src/gradient_3d_7pt_solid.c +++ b/src/gradient_3d_7pt_solid.c @@ -135,7 +135,19 @@ __host__ int grad_lc_anch_create(pe_t * pe, cs_t * cs, map_t * map, obj->target = obj; } else { + /* Copy required entities over ... */ + cs_t * tcs = NULL; tdpMalloc((void **) &obj->target, sizeof(grad_lc_anch_t)); + + cs_target(obj->cs, &tcs); + tdpAssert(tdpMemcpy(&obj->target->cs, &tcs, sizeof(cs_t *), + tdpMemcpyHostToDevice)); + + if (cinfo) { + tdpAssert(tdpMemcpy(&obj->target->cinfo, &cinfo->target, + sizeof(colloids_info_t *), tdpMemcpyHostToDevice)); + } + tdpAssert(tdpMemcpy(&obj->target->bc, &obj->bc, sizeof(lc_anchoring_matrices_t), tdpMemcpyHostToDevice)); @@ -170,12 +182,20 @@ __host__ int grad_lc_anch_free(grad_lc_anch_t * grad) { __host__ int grad_3d_7pt_solid_set(map_t * map, colloids_info_t * cinfo) { + int ndevice = 0; + assert(map); assert(static_grad); static_grad->map = map; static_grad->cinfo = cinfo; + tdpGetDeviceCount(&ndevice); + if (ndevice) { + tdpAssert(tdpMemcpy(&static_grad->target->cinfo, &cinfo->target, + sizeof(colloids_info_t *), tdpMemcpyHostToDevice)); + } + return 0; } From fd7c8e33179eaaca174f13ea6f95f0f23c7f85ed Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 19 Jan 2023 17:50:32 +0000 Subject: [PATCH 215/244] Allow correctly centred drop --- src/field_phi_init.c | 16 +++++++++++----- src/field_phi_init.h | 4 ++-- src/field_phi_init_rt.c | 6 ++++-- src/stats_sigma.c | 7 ++++++- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/field_phi_init.c b/src/field_phi_init.c index 541183a73..060666f3e 100644 --- a/src/field_phi_init.c +++ b/src/field_phi_init.c @@ -8,7 +8,7 @@ * Edinburgh Soft Matter and Statistical Physics Group * and Edinburgh Parallel Computing Centre * - * (c) 2010-2021 The University of Edinburgh + * (c) 2010-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -29,16 +29,21 @@ * Droplet based on a profile phi(r) = phistar tanh (r-r0)/xi * with r0 the centre of the system. * + * NB. The original version of this had a centre which did not include + * Lmin. There is therefore a switch which allows the (incorrect) + * older version to be retained to avoid upsetting older tests. + * *****************************************************************************/ int field_phi_init_drop(field_t * phi, double xi, double radius, - double phistar) { + double phistar, int is_centred) { int nlocal[3]; int noffset[3]; int index, ic, jc, kc; double ltot[3]; + double lmin[3]; double position[3]; double centre[3]; double phival, r, rxi0; @@ -48,12 +53,13 @@ int field_phi_init_drop(field_t * phi, double xi, double radius, cs_nlocal(phi->cs, nlocal); cs_nlocal_offset(phi->cs, noffset); cs_ltot(phi->cs, ltot); + cs_lmin(phi->cs, lmin); rxi0 = 1.0/xi; - centre[X] = 0.5*ltot[X]; - centre[Y] = 0.5*ltot[Y]; - centre[Z] = 0.5*ltot[Z]; + centre[X] = is_centred*lmin[X] + 0.5*ltot[X]; + centre[Y] = is_centred*lmin[Y] + 0.5*ltot[Y]; + centre[Z] = is_centred*lmin[Z] + 0.5*ltot[Z]; for (ic = 1; ic <= nlocal[X]; ic++) { for (jc = 1; jc <= nlocal[Y]; jc++) { diff --git a/src/field_phi_init.h b/src/field_phi_init.h index 105dc46bf..44c90d5db 100644 --- a/src/field_phi_init.h +++ b/src/field_phi_init.h @@ -5,7 +5,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2018-2019 The University of Edinburgh + * (c) 2018-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -26,7 +26,7 @@ struct field_phi_info_s { }; int field_phi_init_drop(field_t * phi, double xi, double radius, - double phistar); + double phistar, int is_centred); int field_phi_init_uniform(field_t * phi, double phi0); int field_phi_init_block(field_t * phi, double xi); int field_phi_init_block_X(field_t * phi, double xi, double block_dimension); diff --git a/src/field_phi_init_rt.c b/src/field_phi_init_rt.c index af3d696a1..1e2b73839 100644 --- a/src/field_phi_init_rt.c +++ b/src/field_phi_init_rt.c @@ -7,7 +7,7 @@ * Edinburgh Soft Matter and Statistical Physics Group * and Edinburgh Parallel Computing Centre * - * (c) 2010-2019 The University of Edinburgh + * (c) 2010-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -146,13 +146,15 @@ int field_phi_init_rt(pe_t * pe, rt_t * rt, field_phi_info_t param, } if (p != 0 && strcmp(value, "drop") == 0) { + int is_centred = 0; /* Include Lmin in centre calculation */ double phistar = 1.0; /* "Amplitude", can be negative. */ radius = DEFAULT_RADIUS; rt_double_parameter(rt, "phi_init_drop_radius", &radius); rt_double_parameter(rt, "phi_init_drop_amplitude", &phistar); + is_centred = rt_switch(rt, "phi_init_drop_centred"); pe_info(pe, "Initialising droplet radius: %14.7e\n", radius); pe_info(pe, "Initialising droplet amplitude: %14.7e\n", phistar); - field_phi_init_drop(phi, param.xi0, radius, phistar); + field_phi_init_drop(phi, param.xi0, radius, phistar, is_centred); } if (p != 0 && strcmp(value, "emulsion") == 0) { diff --git a/src/stats_sigma.c b/src/stats_sigma.c index 5b9bb9267..a9468ab2e 100644 --- a/src/stats_sigma.c +++ b/src/stats_sigma.c @@ -134,7 +134,12 @@ int stats_sigma_create(pe_t * pe, cs_t * cs, fe_symm_t * fe, field_t * phi, /* Initialise the order parameter field */ - field_phi_init_drop(phi, obj->drop.xi0, obj->drop.radius, obj->drop.phimax); + { + /* May want to revisit exact centring of drop. */ + int is_centred = 0; + field_phi_init_drop(phi, obj->drop.xi0, obj->drop.radius, + obj->drop.phimax, is_centred); + } /* Print some information */ /* The diffusivity is mobility/A (with A < 0) */ From 08902f6227fe1fa8421386627b8e8d1d08366fa0 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 19 Jan 2023 17:51:22 +0000 Subject: [PATCH 216/244] Add file utilities and tests --- src/util_json.c | 76 +++++++++++++++++++- src/util_json.h | 4 +- tests/unit/test_util_json.c | 136 ++++++++++++++++++++++++++++++++++++ tests/unit/tests.c | 3 +- tests/unit/tests.h | 4 +- 5 files changed, 218 insertions(+), 5 deletions(-) create mode 100644 tests/unit/test_util_json.c diff --git a/src/util_json.c b/src/util_json.c index 1283ea94c..93b64f33a 100644 --- a/src/util_json.c +++ b/src/util_json.c @@ -8,7 +8,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2022 The University of Edinburgh + * (c) 2022-2023 The University of Edinburgh * * Kevin Stratford (kevin@epcc.ed.ac.uk) * @@ -16,8 +16,10 @@ #include -#include +#include +#include +#include "util_fopen.h" #include "util_json.h" /***************************************************************************** @@ -67,3 +69,73 @@ int util_json_to_double_array(const cJSON * const json, double * array, int sz) return icount; } + +/***************************************************************************** + * + * util_json_to_file + * + * Write the stringified version to the file given. + * + *****************************************************************************/ + +int util_json_to_file(const char * filename, const cJSON * json) { + + int ifail = -1; + char * str = cJSON_Print(json); + + assert(filename); + assert(json); + + if (str) { + FILE * fp = util_fopen(filename, "w"); + if (fp) { + fprintf(fp, "%s", str); + fclose(fp); + ifail = 0; + } + free(str); + } + + return ifail; +} + +/***************************************************************************** + * + * util_json_from_file + * + * The file should contain a well-formed json object. + * Success means return value is zero AND json != NULL. + * + * json object to be deleted by caller. + * + *****************************************************************************/ + +int util_json_from_file(const char * filename, cJSON ** json) { + + int ifail = 0; + FILE * fp = util_fopen(filename, "rb"); + + assert(filename); + assert(json && *json == NULL); + + if (fp == NULL) { + ifail = -1; + } + else { + char * buf = NULL; + size_t len = 0; + fseek(fp, 0, SEEK_END); + len = ftell(fp); + fseek(fp, 0, SEEK_SET); + buf = calloc(len+1, sizeof(char)); + if (buf) { + size_t nread = fread(buf, 1, len, fp); + if (nread != len) ifail = +1; + *json = cJSON_Parse(buf); + free(buf); + } + fclose(fp); + } + + return ifail; +} diff --git a/src/util_json.h b/src/util_json.h index 6cc901007..64a5325eb 100644 --- a/src/util_json.h +++ b/src/util_json.h @@ -5,7 +5,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2022 The University of Edinburgh + * (c) 2022-2023 The University of Edinburgh * * Kevin Stratford (kevin@epcc.ed.ac.uk) * @@ -18,5 +18,7 @@ int util_json_to_int_array(const cJSON * const json, int * array, int sz); int util_json_to_double_array(const cJSON * const json, double * array, int sz); +int util_json_to_file(const char * filename, const cJSON * const json); +int util_json_from_file(const char * filename, cJSON ** json); #endif diff --git a/tests/unit/test_util_json.c b/tests/unit/test_util_json.c new file mode 100644 index 000000000..5bb348a75 --- /dev/null +++ b/tests/unit/test_util_json.c @@ -0,0 +1,136 @@ +/***************************************************************************** + * + * test_util_json.c + * + * Edinburgh Soft Matter and Statistical Physics Group and + * Edinburgh Parallel Computing Centre + * + * (c) 2023 The University of Edinburgh + * + * Kevin Stratford (kevin@epcc.ed.ac.uk) + * + *****************************************************************************/ + +#include +#include + +#include "pe.h" +#include "util_bits.h" +#include "util_json.h" + +int test_util_json_to_int_array(void); +int test_util_json_to_double_array(void); +int test_util_json_to_file(pe_t * pe); + +/***************************************************************************** + * + * test_util_json_suite + * + *****************************************************************************/ + +int test_util_json_suite(void) { + + pe_t * pe = NULL; + + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + test_util_json_to_int_array(); + test_util_json_to_double_array(); + test_util_json_to_file(pe); + + pe_info(pe, "%-9s %s\n", "PASS", __FILE__); + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_util_json_to_int_array + * + *****************************************************************************/ + +int test_util_json_to_int_array(void) { + + int ifail = 0; + + { + int array[3] = {1, 2, 3}; + cJSON * json = cJSON_CreateIntArray(array, 3); + { + int val[3] = {0}; + int iret = util_json_to_int_array(json, val, 3); + if (iret != 3) ifail = -1; + assert(val[0] == array[0]); + assert(val[1] == array[1]); + assert(val[2] == array[2]); + } + cJSON_Delete(json); + } + + return ifail; +} + +/***************************************************************************** + * + * test_util_json_to_double_array + * + *****************************************************************************/ + +int test_util_json_to_double_array(void) { + + int ifail = 0; + + { + double array[3] = {1.0, 2.1, -3.2}; + cJSON * json = cJSON_CreateDoubleArray(array, 3); + { + double val[3] = {0}; + int iret = util_json_to_double_array(json, val, 3); + if (iret != 3) ifail = -1; + assert(util_double_same(val[0], array[0])); + assert(util_double_same(val[1], array[1])); + assert(util_double_same(val[2], array[2])); + } + cJSON_Delete(json); + } + + return ifail; +} + +/***************************************************************************** + * + * test_util_json_to_file + * + * It's convenient to test util_json_from_file() at the same time. + * + *****************************************************************************/ + +int test_util_json_to_file(pe_t * pe) { + + int ifail = 0; + char filename[BUFSIZ] = {0}; + + /* Some specimin json */ + cJSON * jstr = cJSON_Parse("{\"Test\": \"data\"}"); + + /* Write/read is entirely serial. So use per-process name ... */ + + sprintf(filename, "test-util-json-to-file-%4.4d.json", pe_mpi_rank(pe)); + ifail = util_json_to_file(filename, jstr); + assert(ifail == 0); + + /* Read (same file). We just test the return code and we have an object. */ + { + cJSON * json = NULL; + ifail = util_json_from_file(filename, &json); + assert(ifail == 0); + assert(json); + cJSON_Delete(json); + } + + remove(filename); + cJSON_Delete(jstr); + + return ifail; +} diff --git a/tests/unit/tests.c b/tests/unit/tests.c index 3c1ab3856..5f7befd5c 100644 --- a/tests/unit/tests.c +++ b/tests/unit/tests.c @@ -7,7 +7,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2010-2022 The University of Edinburgh + * (c) 2010-2023 The University of Edinburgh * * Kevin Stratford (kevin@epcc.ed.ac.uk) * @@ -123,6 +123,7 @@ __host__ int tests_create() { test_util_bits_suite(); test_util_fopen_suite(); test_util_io_suite(); + test_util_json_suite(); test_util_sum_suite(); test_visc_arrhenius_suite(); test_wall_suite(); diff --git a/tests/unit/tests.h b/tests/unit/tests.h index 5696e77db..6cd5e295b 100644 --- a/tests/unit/tests.h +++ b/tests/unit/tests.h @@ -5,8 +5,9 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * + * (c) 2010-2023 The University of Edinburgh + * * Kevin Stratford (kevin@epcc.ed.ac.uk) - * (c) 2010-2022 The University of Edinburgh * *****************************************************************************/ @@ -104,6 +105,7 @@ int test_util_suite(void); int test_util_bits_suite(void); int test_util_fopen_suite(void); int test_util_io_suite(void); +int test_util_json_suite(void); int test_util_sum_suite(void); int test_visc_arrhenius_suite(void); int test_wall_suite(void); From 1a0aa0fbdf58c57f67b1a9a8becf11adb579fa44 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 19 Jan 2023 17:51:52 +0000 Subject: [PATCH 217/244] Add from file routine --- src/io_metadata.c | 67 ++++++++++++++++++++++++++++++----- src/io_metadata.h | 6 +++- tests/unit/test_io_metadata.c | 60 ++++++++++++++++++++++++++++--- 3 files changed, 119 insertions(+), 14 deletions(-) diff --git a/src/io_metadata.c b/src/io_metadata.c index b1bb5078f..d6c436355 100644 --- a/src/io_metadata.c +++ b/src/io_metadata.c @@ -11,7 +11,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2022 The University of Edinburgh + * (c) 2022-2023 The University of Edinburgh * * Kevin Stratford (kevin@epcc.ed.ac.uk) * @@ -247,6 +247,7 @@ int io_metadata_write(const io_metadata_t * metadata, const char * stub, const cJSON * comments) { + int ifail = 0; cJSON * json = NULL; char filename[BUFSIZ] = {0}; @@ -272,16 +273,66 @@ int io_metadata_write(const io_metadata_t * metadata, int rank = -1; MPI_Comm_rank(metadata->comm, &rank); if (rank == 0) { - FILE * fp = NULL; - char * str = cJSON_Print(json); - fp = util_fopen(filename, "w"); - fprintf(fp, "%s\n", str); - fclose(fp); - free(str); + ifail = util_json_to_file(filename, json); } } cJSON_Delete(json); - return 0; + return ifail; +} + +/***************************************************************************** + * + * io_metadata_from_file + * + * Somewhat experimental in that we need to generate a cs_t from the + * file as a first step. This is slightly "in the air" until a + * refactored cs_t is available providing a cleaner mechanism. + * + * This is ok for iogrid = {1,1,1}. Otherwise, the subfile_t + * component must be generated in a different way. This reflects a + * possible difference in the pe_t between writting and reading. + * + * A new cs_t * is returned as part of the new io_metadata_t structure. + * + *****************************************************************************/ + +int io_metadata_from_file(pe_t * pe, const char * filename, + io_metadata_t ** metadata) { + + int ifail = 0; + cs_t * cs = NULL; + cJSON * json = NULL; + + /* Read json */ + ifail = util_json_from_file(filename, &json); + if (ifail != 0 || json == NULL) goto err; + + { + io_options_t options = {0}; + io_element_t element = {0}; + + cJSON * jcoords = cJSON_GetObjectItemCaseSensitive(json, "coords"); + cJSON * joptions = cJSON_GetObjectItemCaseSensitive(json, "io_options"); + cJSON * jelement = cJSON_GetObjectItemCaseSensitive(json, "io_element"); + + ifail += cs_from_json(pe, jcoords, &cs); + ifail += io_options_from_json(joptions, &options); + ifail += io_element_from_json(jelement, &element); + if (ifail != 0) goto err; + + ifail += io_metadata_create(cs, &options, &element, metadata); + if (ifail != 0) goto err; + + cJSON_Delete(json); + } + + return ifail; + + err: + if (json) cJSON_Delete(json); + if (cs) cs_free(cs); + + return ifail; } diff --git a/src/io_metadata.h b/src/io_metadata.h index 5d39e4d6c..1023a7761 100644 --- a/src/io_metadata.h +++ b/src/io_metadata.h @@ -5,7 +5,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2022 The University of Edinburgh + * (c) 2022-2023 The University of Edinburgh * * Kevin Stratford (kevin@epcc.ed.ac.uk) * @@ -14,6 +14,7 @@ #ifndef LUDWIG_IO_METADATA_H #define LUDWIG_IO_METADATA_H +#include "pe.h" #include "coords.h" #include "cs_limits.h" #include "io_options.h" @@ -55,4 +56,7 @@ int io_metadata_from_json(cs_t * cs, const cJSON * json, int io_metadata_write(const io_metadata_t * metadata, const char * stub, const cJSON * comments); +int io_metadata_from_file(pe_t * pe, const char * filename, + io_metadata_t ** metadata); + #endif diff --git a/tests/unit/test_io_metadata.c b/tests/unit/test_io_metadata.c index ade6423bf..4d189b06b 100644 --- a/tests/unit/test_io_metadata.c +++ b/tests/unit/test_io_metadata.c @@ -6,7 +6,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2022 The University of Edinburgh + * (c) 2022-2023 The University of Edinburgh * * Kevin Stratford (kevin@epcc.ed.ac.uk) * @@ -22,7 +22,8 @@ int test_io_metadata_initialise(cs_t * cs); int test_io_metadata_create(cs_t * cs); int test_io_metadata_to_json(cs_t * cs); -int test_io_metadata_write(cs_t * cs); +int test_io_metadata_write(cs_t * cs, int keep); +int test_io_metadata_from_file(pe_t * pe); /***************************************************************************** * @@ -43,7 +44,10 @@ int test_io_metadata_suite(void) { test_io_metadata_initialise(cs); test_io_metadata_create(cs); test_io_metadata_to_json(cs); - test_io_metadata_write(cs); + test_io_metadata_write(cs, 0); + + test_io_metadata_write(cs, 1); + test_io_metadata_from_file(pe); pe_info(pe, "%-9s %s\n", "PASS", __FILE__); cs_free(cs); @@ -194,7 +198,7 @@ int test_io_metadata_to_json(cs_t * cs) { * *****************************************************************************/ -int test_io_metadata_write(cs_t * cs) { +int test_io_metadata_write(cs_t * cs, int keep) { int ifail = 0; io_metadata_t * meta = NULL; @@ -214,7 +218,7 @@ int test_io_metadata_write(cs_t * cs) { assert(ifail == 0); /* Remove at rank 0 (a test that the file exists with the correct name) */ - { + if (keep == 0) { int rank = -1; MPI_Comm_rank(meta->comm, &rank); if (rank == 0) ifail = remove("test-io-metadata.001-001"); @@ -226,3 +230,49 @@ int test_io_metadata_write(cs_t * cs) { return ifail; } + +/***************************************************************************** + * + * test_io_metadata_from_file + * + * Experimental. Use the file generated by the above routine. + * Serial file operations. + * + *****************************************************************************/ + +int test_io_metadata_from_file(pe_t * pe) { + + int ifail = 0; + MPI_Comm comm = MPI_COMM_NULL; + const char * filename = "test-io-metadata.001-001"; + io_metadata_t * metadata = NULL; + + pe_mpi_comm(pe, &comm); + MPI_Barrier(comm); + + /* All ranks read at the moment ... */ + ifail = io_metadata_from_file(pe, filename, &metadata); + assert(ifail == 0); + + /* Check one component of each part */ + assert(metadata->cs); + assert(metadata->options.metadata_version == 3); + assert(metadata->element.datatype == MPI_DOUBLE); + assert(metadata->subfile.sizes[X] == 64); + + { + /* Cleaning up at the moment requires.. */ + cs_t * cs = metadata->cs; + io_metadata_free(&metadata); + cs_free(cs); + } + + MPI_Barrier(comm); + { + int rank = -1; + MPI_Comm_rank(comm, &rank); + ifail = remove(filename); + } + + return ifail; +} From 32181fba06dc96e7ffdbe2faf61c692a4b875923 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 19 Jan 2023 17:52:11 +0000 Subject: [PATCH 218/244] Update to read new metadata file and process --- util/extract.c | 400 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 399 insertions(+), 1 deletion(-) diff --git a/util/extract.c b/util/extract.c index 3e3dd226f..c4af68139 100644 --- a/util/extract.c +++ b/util/extract.c @@ -2,6 +2,18 @@ * * extract.c * + * This is under revision as part of the move to new metadata. + * For most users, there will be no need to consolidate + * parallel output. Only post-processing is relevant (unrolling + * Lees Edwards plane, diagonalisation of liquid crystal order parameter). + * + * ./extract data-file + * + * The relevant metadata file is located from the file stub. + * Most options are still available (see below). + * + * Older version ... + * * This program deals with the consolidation of parallel I/O * (and in serial with Lees-Edwards planes). This program is * serial. @@ -48,7 +60,7 @@ * Kevin Stratford (kevin@epcc.ed.ac.uk) * Oliver Henrich (oliver.henrich@strath.ac.uk) * - * (c) 2011-2022 The University of Edinburgh + * (c) 2011-2023 The University of Edinburgh * ****************************************************************************/ @@ -58,6 +70,7 @@ #include #include +#include "io_metadata.h" #include "util.h" #include "util_fopen.h" @@ -148,6 +161,8 @@ int lc_compute_scalar_ops(double q[3][3], double qs[5]); const char * file_stub_valid(const char * input); int file_get_file_nfile(const char * filename); int file_get_file_index(const char * filename); +int extract_process_and_output(const char * stub, int ntime, + io_metadata_t * meta); /***************************************************************************** * @@ -201,6 +216,44 @@ int main(int argc, char ** argv) { } } + /* New metadata format: completely short circuit the original + processing via a new driver */ + if (optind == argc - 1) { + /* Must have a recognised metadata quantity */ + const char * stub = file_stub_valid(argv[argc-1]); + if (stub == NULL) { + printf("Unrecognised data quantity %s\n", argv[argc-1]); + exit(-1); + } + else { + pe_t * pe = NULL; + char buf[FILENAME_MAX] = {0}; + char * filename = buf; + int ifail = 0; + int itime = 0; + + int ifile = file_get_file_index(argv[argc-1]); + int nfile = file_get_file_nfile(argv[argc-1]); + io_metadata_t * meta = NULL; + + printf("%s %s\n", argv[0], argv[argc-1]); + printf("Identified file name as %s\n", stub); + sprintf(filename, "%s-metadata.%3.3d-%3.3d", stub, nfile, ifile); + printf("Attempt to read metadata file %s\n", filename); + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + ifail = io_metadata_from_file(pe, filename, &meta); + if (ifail != 0) printf("Failed to read/parse metadata file\n"); + + itime = read_data_file_name(argv[argc-1]); + ifail = extract_process_and_output(stub, itime, meta); + /* Release meta resources */ + pe_free(pe); + printf("Complete processing for %s\n", argv[argc-1]); + } + exit(0); + } + + /* Continue with older invocation */ if (optind > argc-2) { printf("Usage: %s [-abk] meta-file data-file\n", argv[0]); exit(EXIT_FAILURE); @@ -1512,3 +1565,348 @@ int file_get_file_index(const char * filename) { return ifile; } + + +/***************************************************************************** + * + * extract_read_single_file + * + *****************************************************************************/ + +int io_metadata_nrecord(const io_metadata_t * meta) { + + /* This is still not very satisfactory. Need no,. of records in metadata */ + int nrecord = meta->element.count; + if (meta->element.datatype == MPI_CHAR) nrecord /= 23; + nrec_ = nrecord; + + return nrecord; +} + +int extract_read_single_file(io_metadata_t * meta, const char * stub, + int itime, double * datatotal) { + + int nrecord = io_metadata_nrecord(meta); + char filename[BUFSIZ] = {0}; + FILE * fp = NULL; + + io_subfile_name(&meta->subfile, stub, itime, filename, BUFSIZ); + + fp = util_fopen(filename, "r+b"); + if (fp == NULL) printf("fopen(%s) failed\n", filename); + + for (int ic = 1; ic <= meta->subfile.sizes[X]; ic++) { + for (int jc = 1; jc <= meta->subfile.sizes[Y]; jc++) { + for (int kc = 1; kc <= meta->subfile.sizes[Z]; kc++) { + + int icd = meta->subfile.offset[X] + ic; + int jcd = meta->subfile.offset[Y] + jc; + int kcd = meta->subfile.offset[Z] + kc; + int indexd = site_index(icd, jcd, kcd, meta->cs->param->ntotal); + + for (int nr = 0; nr < nrecord; nr++) { + double datum = 0.0; + int nread = 0; + if (meta->options.iorformat == IO_RECORD_BINARY) { + nread = fread(&datum, sizeof(double), 1, fp); + } + else { + nread = fscanf(fp, "%le", &datum); + } + if (nread != 1) printf("File out of records!!\n"); + + /* Place at correct offset in the full array */ + *(datatotal + nrecord*indexd + nr) = datum; + } + + } + } + } + + fclose(fp); + + return 0; +} + +/***************************************************************************** + * + * extract_unroll + * + *****************************************************************************/ + +int le_block_id(int nplanes, int nx, int ic) { + + /* Could take local ic and and offset here */ + /* Halo points should be in the outer most block (always) */ + + assert(nplanes >= 0); + assert(nx > 0); + assert(1 <= ic && ic <= nx); + + int bid = 0; + + if (nplanes > 0) { + int s = nx/nplanes; /* Separation */ + int ic0 = nx/2; /* Ref. is centre */ + + if (nplanes % 2 == 0) { + /* blocks are ... -2 | -1 | 0 | +1 | +2 ... */ + if (ic > ic0) bid = (+s/2 + ic - ic0 - 1)/s; + if (ic <= ic0) bid = (-s/2 + ic - ic0 )/s; + } + else { + /* blocks are ... -2 | -1 | +1 | +2 ... (no "block zero") */ + if (ic > ic0) bid = 1 + (ic - ic0 - 1)/s; + if (ic <= ic0) bid = -1 + (ic - ic0 )/s; + } + } + + return bid; +} + +double le_block_displacement_uyt(const cs_t * cs, int ic, int itime) { + + int nplanes = cs->leopts.nplanes; + double uyt = 0.0; + + if (nplanes > 0) { + int nx = cs->param->ntotal[X]; + double uy = cs->leopts.uy; + + if (nplanes % 2 == 0) { + uyt = uy*itime*le_block_id(nplanes, nx, ic); + } + else { + int bid = le_block_id(nplanes, nx, ic); + if (bid < 0) uyt = uy*itime*(0.5 + bid); + if (bid >= 0) uyt = uy*itime*(bid - 0.5); + } + } + + return uyt; +} + +/* A function of time only for non-steady shear. */ + +double le_block_uy(cs_t * cs, int ic, int itime) { + + int nplanes = cs->leopts.nplanes; + int nx = cs->param->ntotal[X]; + double uy = 0.0; + + if (nplanes > 0) { + int bid = le_block_id(nplanes, nx, ic); + if (nplanes % 2 == 0) { + uy = cs->leopts.uy*bid; + } + else { + if (bid > 0) uy = cs->leopts.uy*(bid - 0.5); + if (bid < 0) uy = cs->leopts.uy*(bid + 0.5); + } + } + + return uy; +} + +/***************************************************************************** + * + * extract_unroll + * + * Unroll the time-dependent LE displacements. + * + * If it's the velocity, there's a correction to u_y to account for + * the relative block motion. + * + *****************************************************************************/ + +int extract_unroll(const io_metadata_t * meta, double * data, int itime, + int is_vel) { + + int nrecord = io_metadata_nrecord(meta); + int ntotal[3] = {0}; + double * buffer = NULL; + double du[BUFSIZ] = {0}; + + cs_ntotal(meta->cs, ntotal); + + { + /* Temporary for y-z plane (all reocrds) */ + int nsz = nrecord*ntotal[Y]*ntotal[Z]; + buffer = (double *) malloc(nsz*sizeof(double)); + if (buffer == NULL) printf("malloc(buffer) failed!\n"); + } + + for (int ic = 1; ic <= ntotal[X]; ic++) { + + double dy = le_block_displacement_uyt(meta->cs, ic, itime); + int jdy = floor(dy); + double fr = 1.0 - (dy - jdy); + if (is_vel) du[Y] = le_block_uy(meta->cs, ic, itime); + printf("time %6d ic %3d uyt %7.3f dv %7.4f %3d\n" , itime, ic, dy, + le_block_uy(meta->cs, ic, itime), + le_block_id(meta->cs->leopts.nplanes, ntotal[X], ic)); + + for (int jc = 1; jc <= ntotal[Y]; jc++) { + int j0 = 1 + (jc - jdy - 3 + 1000*ntotal[Y]) % ntotal[Y]; + int j1 = 1 + j0 % ntotal[Y]; + int j2 = 1 + j1 % ntotal[Y]; + int j3 = 1 + j2 % ntotal[Y]; + + for (int kc = 1; kc <= ntotal[Z]; kc++) { + for (int n = 0; n < nrecord; n++) { + int index0 = site_index(ic, j0, kc, ntotal); + int index1 = site_index(ic, j1, kc, ntotal); + int index2 = site_index(ic, j2, kc, ntotal); + int index3 = site_index(ic, j3, kc, ntotal); + buffer[nrecord*site_index(1,jc,kc,ntotal) + n] = + - (1.0/6.0)*fr*(fr-1.0)*(fr-2.0)*data[nrecord*index0 + n] + + 0.5*(fr*fr-1.0)*(fr-2.0)*data[nrecord*index1 + n] + - 0.5*fr*(fr+1.0)*(fr-2.0)*data[nrecord*index2 + n] + + (1.0/6.0)*fr*(fr*fr-1.0)*data[nrecord*index3 + n]; + } + } + } + + /* Put the whole buffer plane back in place */ + + for (int jc = 1; jc <= ntotal[Y]; jc++) { + for (int kc = 1; kc <= ntotal[Z]; kc++) { + int index = site_index(ic, jc, kc, ntotal); + int index1 = site_index(1, jc, kc, ntotal); + for (int n = 0; n < nrecord; n++) { + data[nrecord*index + n] = buffer[nrecord*index1 + n] + du[n]; + } + } + } + /* Next ic */ + } + + free(buffer); + + return 0; +} + +/***************************************************************************** + * + * extract_process_and_output + * + *****************************************************************************/ + +int extract_process_and_output(const char * stub, int ntime, + io_metadata_t * meta) { + + int nrecord = -1; /* Number of records per site (nf) */ + double * datasection = NULL; /* field data */ + FILE * fp_output = NULL; /* Output file */ + + assert(stub); + assert(0 <= ntime && ntime < 1000*1000*1000); + + /* Records */ + + nrecord = io_metadata_nrecord(meta); + assert(nrecord > 0); + + /* No. sites in the target section (always original at moment) */ + + cs_ntotal(meta->cs, ntargets); + + { + size_t n = nrecord*ntargets[0]*ntargets[1]*ntargets[2]; + datasection = (double *) calloc(n, sizeof(double)); + if (datasection == NULL) printf("calloc(datasection) failed\n"); + } + + extract_read_single_file(meta, stub, ntime, datasection); + + /* Unroll the data if Lees Edwards planes are present */ + + if (meta->cs->leopts.nplanes > 0) { + int is_vel = (strcmp(stub, "vel") == 0); + printf("Unrolling LE planes from centre (displacement %f)\n", -999.999); + if (is_vel) printf("Data is velocity field (nrecords = %d)\n", nrecord); + extract_unroll(meta, datasection, ntime, is_vel); + } + + if (nrecord == 5 && strncmp(stub, "q", 1) == 0) { + + if (output_vtk_ == 1 || output_vtk_ == 2) { + /* We mandate this is the transformed Q_ab */ + lc_transform_q5(ntargets, datasection); + write_qab_vtk(ntime, ntargets, datasection); + } + else { + char io_data[BUFSIZ] = {0}; + sprintf(io_data, "q-%9.9d", ntime); + fp_output = util_fopen(io_data, "w+b"); + if (fp_output == NULL) { + printf("fopen(%s) failed\n", io_data); + exit(-1); + } + + /* Here, we could respect the -d, -s, -x options. */ + /* But at the moment, always 5 components ... */ + if (output_q_raw_) { + printf("Writing raw q to %s\n", io_data); + } + else { + printf("Writing computed scalar q etc: %s\n", io_data); + lc_transform_q5(ntargets, datasection); + } + + if (output_cmf_ == 0) write_data(fp_output, ntargets, 0, 5, datasection); + if (output_cmf_ == 1) write_data_cmf(fp_output, ntargets, 0, 5, datasection); + + fclose(fp_output); + } + } + else { + /* A direct input / output */ + + /* Write a single file with the final section */ + char io_data[BUFSIZ] = {0}; + snprintf(io_data, sizeof(io_data), "%s-%9.9d", stub, ntime); + + if (output_vtk_ == 1 || output_vtk_ == 2) { + + strcat(io_data, ".vtk"); + fp_output = util_fopen(io_data, "w+b"); + if (fp_output == NULL) printf("fopen(%s) failed\n", io_data); + printf("\nWriting result to %s\n", io_data); + + if (nrecord == 3 && strncmp(stub, "vel", 3) == 0) { + write_vtk_header(fp_output, nrecord, ntargets, "velocity_field", + VTK_VECTORS); + } + else if (nrecord == 1 && strncmp(stub, "phi", 3) == 0) { + write_vtk_header(fp_output, nrecord, ntargets, "composition", + VTK_SCALARS); + } + else { + /* Assume scalars */ + write_vtk_header(fp_output, nrecord, ntargets, stub, VTK_SCALARS); + } + + } + else { + + fp_output = util_fopen(io_data, "w+b"); + if (fp_output == NULL) printf("fopen(%s) failed\n", io_data); + printf("\nWriting result to %s\n", io_data); + + } + + if (output_cmf_ == 0) { + write_data(fp_output, ntargets, 0, nrecord, datasection); + } + if (output_cmf_ == 1) { + write_data_cmf(fp_output, ntargets, 0, nrecord, datasection); + } + + fclose(fp_output); + } + + free(datasection); + + return 0; +} From 6145d7602f662302c0806dbf2d183539c6ac367f Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 19 Jan 2023 17:52:38 +0000 Subject: [PATCH 219/244] Add tutorial material on Lees Edwards --- .../lees-edwards/lees-edwards-example1.inp | 109 +++++++++++ docs/tutorial/lees-edwards/stdout | 177 ++++++++++++++++++ 2 files changed, 286 insertions(+) create mode 100644 docs/tutorial/lees-edwards/lees-edwards-example1.inp create mode 100644 docs/tutorial/lees-edwards/stdout diff --git a/docs/tutorial/lees-edwards/lees-edwards-example1.inp b/docs/tutorial/lees-edwards/lees-edwards-example1.inp new file mode 100644 index 000000000..d7b4a6688 --- /dev/null +++ b/docs/tutorial/lees-edwards/lees-edwards-example1.inp @@ -0,0 +1,109 @@ +############################################################################## +# +# lees-edwards-example1.inp +# +# The code has been compiled with -D_D2Q9_ as we will consider a +# two dimensional problem. (The same input will work for other +# models, but the output will be slightly different.) +# +# With the symmetric free energy, a droplet is placed in the +# centre of the Lx x Ly = 32 x 64 system. +# +# Two Lees Edwards planes are used with Uy = 0.005. +# One can run for zero steps to see the initial conditions or 3200 +# steps (Uyt = 16.0) to generate the output used in the examples. +# +# We've only retained the relevant composition output (phi) and +# and velocity output (vel) at step 3200. +# +############################################################################## + +N_cycles 3200 + +############################################################################## +# +# System +# +############################################################################## + +size 32_64_1 + +############################################################################## +# +# Fluid parameters +# +############################################################################## + +viscosity 0.1 + +############################################################################## +# +# Free energy parameters +# +############################################################################### + +free_energy symmetric + +symmetric_a -0.0625 +symmetric_b 0.0625 +symmetric_kappa 0.04 + +phi_initialisation drop +phi_init_drop_radius 12.0 +phi_init_drop_amplitude +1.0 +phi_init_drop_centred yes + +mobility 0.15 + +fd_gradient_calculation 3d_7pt_fluid +fd_advection_scheme_order 4 + +############################################################################### +# +# Colloid parameters +# +############################################################################### + +colloid_init no_colloids + +############################################################################### +# +# Periodic conditions / boundaries +# +############################################################################### + +periodicity 1_1_1 + +############################################################################### +# +# Output frequency and type +# +############################################################################### + +default_io_mode mpiio +default_io_format ascii +default_io_report yes + +freq_statistics 1600 +freq_phi 3200 +freq_vel 3200 + +config_at_end no + +############################################################################### +# +# Lees-Edwards planes +# +############################################################################### + +N_LE_plane 2 +LE_plane_vel 0.005 +LE_init_profile 1 + +############################################################################### +# +# Miscellaneous +# +############################################################################### + +random_seed -7361237 diff --git a/docs/tutorial/lees-edwards/stdout b/docs/tutorial/lees-edwards/stdout new file mode 100644 index 000000000..0aea1c85a --- /dev/null +++ b/docs/tutorial/lees-edwards/stdout @@ -0,0 +1,177 @@ +Welcome to Ludwig v0.19.0 (Serial version running on 1 process) +Start time: Thu Jan 19 10:39:15 2023 + +Compiler: + name: Gnu 12.2.0 + version-string: 12.2.0 + +Note assertions via standard C assert() are on. + +Target thread model: OpenMP. +OpenMP threads: 4; maximum number of threads: 8. + +Read 27 user parameters from lees-edwards-example1.inp + +System details +-------------- +System size: 32 64 1 +Decomposition: 1 1 1 +Local domain: 32 64 1 +Periodic: 1 1 1 +Halo nhalo: 2 +Reorder: true +Initialised: 1 + +Lees-Edwards boundary conditions are active: +LE plane 1 is at x = 8 with speed 0.005000 +LE plane 2 is at x = 24 with speed 0.005000 +Overall shear rate = 0.000313 + +Lees-Edwards time offset (time steps): 0 + +Free energy details +------------------- + +Symmetric phi^4 free energy selected. + +Parameters: +Bulk parameter A = -6.25000e-02 +Bulk parameter B = 6.25000e-02 +Surface penalty kappa = 4.00000e-02 +Surface tension = 4.71405e-02 +Interfacial width = 1.13137e+00 + +Using Cahn-Hilliard finite difference solver. +Mobility M = 1.50000e-01 +Order parameter noise = off +Force calculation: stress_divergence + +System properties +---------------- +Mean fluid density: 1.00000e+00 +Shear viscosity 1.00000e-01 +Bulk viscosity 1.00000e-01 +Temperature 0.00000e+00 +External body force density 0.00000e+00 0.00000e+00 0.00000e+00 +External E-field amplitude 0.00000e+00 0.00000e+00 0.00000e+00 +External E-field frequency 0.00000e+00 +External magnetic field 0.00000e+00 0.00000e+00 0.00000e+00 + +Lattice Boltzmann distributions +------------------------------- +Model: d2q9 +SIMD vector len: 1 +Number of sets: 1 +Halo type: lb_halo_target (full halo) +Input format: binary +Output format: binary +I/O grid: 1 1 1 + +Lattice Boltzmann collision +--------------------------- +Relaxation time scheme: M10 +Hydrodynamic modes: on +Ghost modes: on +Isothermal fluctuations: off +Shear relaxation time: 8.00000e-01 +Bulk relaxation time: 8.00000e-01 +Ghost relaxation time: 1.00000e+00 +[User ] Random number seed: -7361237 + +Hydrodynamics +------------- +Hydrodynamics: on + +Order parameter I/O +------------------- +Order parameter I/O format: +I/O decomposition: 1 1 1 + +Advection scheme order: 4 +Initialising droplet radius: 1.2000000e+01 +Initialising droplet amplitude: 1.0000000e+00 +Initialising shear profile +Gradient calculation: 3d_7pt_fluid +Initial conditions. + +Scalars - total mean variance min max +[rho] 2048.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 +[phi] 1.1366180e+03 5.5498927e-01 6.0870297e-01 -1.0000000e+00 1.0000000e+00 + +Momentum - x y z +[total ] -6.9388939e-18 -3.4694470e-18 0.0000000e+00 +[fluid ] -6.9388939e-18 -3.4694470e-18 0.0000000e+00 + +Starting time step loop. + +Scalars - total mean variance min max +[rho] 2048.00 1.00000000000 1.2089861e-05 0.99567958731 1.00855514326 +[phi] 1.1366180e+03 5.5498927e-01 6.0575697e-01 -1.0147629e+00 9.9638920e-01 + +Free energy density - timestep total fluid +[fed] 1600 -1.4032606946e-02 -1.4032606946e-02 + +Momentum - x y z +[total ] -1.5348833e-14 1.8637869e-14 0.0000000e+00 +[fluid ] -1.5348833e-14 1.8637869e-14 0.0000000e+00 + +Velocity - x y z +[minimum ] -4.7035745e-03 -5.5890154e-03 0.0000000e+00 +[maximum ] 4.7035745e-03 5.5890154e-03 1.1754944e-38 + +Completed cycle 1600 +Writing phi file at step 3200! +MPIIO wrote to phi-000003200.001-001 +- phi aggregated 0.049 MB in 0.000 seconds +- phi wrote 0.049 MB in 0.003 seconds +- phi rate 0.016 GB per second +Writing rho/velocity output at step 3200! +MPIIO wrote to rho-000003200.001-001 +- rho aggregated 0.049 MB in 0.000 seconds +- rho wrote 0.049 MB in 0.003 seconds +- rho rate 0.017 GB per second +MPIIO wrote to vel-000003200.001-001 +- vel aggregated 0.143 MB in 0.001 seconds +- vel wrote 0.143 MB in 0.011 seconds +- vel rate 0.013 GB per second + +Scalars - total mean variance min max +[rho] 2048.00 1.00000000000 6.5329120e-06 0.99659531268 1.00681534635 +[phi] 1.1366180e+03 5.5498927e-01 6.0295704e-01 -1.0136615e+00 9.9590545e-01 + +Free energy density - timestep total fluid +[fed] 3200 -1.4032657774e-02 -1.4032657774e-02 + +Momentum - x y z +[total ] -5.1316590e-14 8.0775664e-14 0.0000000e+00 +[fluid ] -5.1316590e-14 8.0775664e-14 0.0000000e+00 + +Velocity - x y z +[minimum ] -4.1890192e-03 -5.7036243e-03 0.0000000e+00 +[maximum ] 4.1890192e-03 5.7036243e-03 1.1754944e-38 + +Completed cycle 3200 + +Timer resolution: 1e-06 second + +Timer statistics + Section: tmin tmax total + Total: 20.836 20.836 20.836 20.836232 (1 call) + Time step loop: 0.006 0.014 20.798 0.006499 (3200 calls) + Propagation: 0.000 0.002 1.376 0.000430 (3200 calls) + Propagtn (krnl) : 0.000 0.002 1.361 0.000425 (3200 calls) + Collision: 0.001 0.002 2.895 0.000905 (3200 calls) + Collision (krnl) : 0.001 0.002 2.870 0.000897 (3200 calls) + Lattice halos: 0.000 0.002 1.735 0.000542 (3200 calls) + phi gradients: 0.001 0.002 2.779 0.000869 (3200 calls) + phi grad (krnl) : 0.000 0.001 1.381 0.000431 (3200 calls) + phi halos: 0.000 0.001 0.722 0.000226 (3200 calls) + Lees Edwards BC: 0.000 0.000 0.337 0.000105 (3200 calls) + BBL: 0.000 0.000 0.002 0.000001 (3200 calls) + Force calculation: 0.001 0.004 4.956 0.001549 (3200 calls) + phi update: 0.002 0.005 6.099 0.001906 (3200 calls) + Advectn (krnl) : 0.001 0.002 2.721 0.000850 (3200 calls) + Advectn BCS (krnl) : 0.000 0.000 0.654 0.000205 (3200 calls) + Free1: 0.000 0.027 0.032 0.000010 (3200 calls) +End time: Thu Jan 19 10:39:35 2023 +Ludwig finished normally. From e866a093a4ef3aa7950ecb2d81633620ca346a54 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 19 Jan 2023 18:24:48 +0000 Subject: [PATCH 220/244] Remove obscure comment --- src/io_metadata.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/io_metadata.c b/src/io_metadata.c index d6c436355..dd6b79775 100644 --- a/src/io_metadata.c +++ b/src/io_metadata.c @@ -203,8 +203,6 @@ int io_metadata_to_json(const io_metadata_t * meta, cJSON ** json) { * io_metadata_from_json * * Assumes we have an existing coordinate system. - * FIXME: However, Lees Edwards options cs->leopts is initialised here. - * The question of initialising cs_t from JSON is PENDING. * *****************************************************************************/ From 45e06896af0005b421898db574a776db9c5af037 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 19 Jan 2023 18:25:21 +0000 Subject: [PATCH 221/244] Merge hydro struct changes --- src/leslie_ericksen.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/leslie_ericksen.c b/src/leslie_ericksen.c index 9a76e7989..aad753bcb 100644 --- a/src/leslie_ericksen.c +++ b/src/leslie_ericksen.c @@ -8,7 +8,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2010-2022 The University of Edinburgh + * (c) 2010-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -331,32 +331,32 @@ __device__ static void leslie_u_gradient_tensor(kernel_ctxt_t * ktx, int m1 = kernel_coords_index(ktx, ic - 1, jc, kc); int p1 = kernel_coords_index(ktx, ic + 1, jc, kc); - w[X][X] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, X)] - - hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, X)]); - w[Y][X] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, Y)] - - hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, Y)]); - w[Z][X] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, Z)] - - hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, Z)]); + w[X][X] = 0.5*(hydro->u->data[addr_rank1(hydro->nsite, NHDIM, p1, X)] - + hydro->u->data[addr_rank1(hydro->nsite, NHDIM, m1, X)]); + w[Y][X] = 0.5*(hydro->u->data[addr_rank1(hydro->nsite, NHDIM, p1, Y)] - + hydro->u->data[addr_rank1(hydro->nsite, NHDIM, m1, Y)]); + w[Z][X] = 0.5*(hydro->u->data[addr_rank1(hydro->nsite, NHDIM, p1, Z)] - + hydro->u->data[addr_rank1(hydro->nsite, NHDIM, m1, Z)]); m1 = kernel_coords_index(ktx, ic, jc - 1, kc); p1 = kernel_coords_index(ktx, ic, jc + 1, kc); - w[X][Y] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, X)] - - hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, X)]); - w[Y][Y] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, Y)] - - hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, Y)]); - w[Z][Y] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, Z)] - - hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, Z)]); + w[X][Y] = 0.5*(hydro->u->data[addr_rank1(hydro->nsite, NHDIM, p1, X)] - + hydro->u->data[addr_rank1(hydro->nsite, NHDIM, m1, X)]); + w[Y][Y] = 0.5*(hydro->u->data[addr_rank1(hydro->nsite, NHDIM, p1, Y)] - + hydro->u->data[addr_rank1(hydro->nsite, NHDIM, m1, Y)]); + w[Z][Y] = 0.5*(hydro->u->data[addr_rank1(hydro->nsite, NHDIM, p1, Z)] - + hydro->u->data[addr_rank1(hydro->nsite, NHDIM, m1, Z)]); m1 = kernel_coords_index(ktx, ic, jc, kc - 1); p1 = kernel_coords_index(ktx, ic, jc, kc + 1); - w[X][Z] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, X)] - - hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, X)]); - w[Y][Z] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, Y)] - - hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, Y)]); - w[Z][Z] = 0.5*(hydro->u[addr_rank1(hydro->nsite, NHDIM, p1, Z)] - - hydro->u[addr_rank1(hydro->nsite, NHDIM, m1, Z)]); + w[X][Z] = 0.5*(hydro->u->data[addr_rank1(hydro->nsite, NHDIM, p1, X)] - + hydro->u->data[addr_rank1(hydro->nsite, NHDIM, m1, X)]); + w[Y][Z] = 0.5*(hydro->u->data[addr_rank1(hydro->nsite, NHDIM, p1, Y)] - + hydro->u->data[addr_rank1(hydro->nsite, NHDIM, m1, Y)]); + w[Z][Z] = 0.5*(hydro->u->data[addr_rank1(hydro->nsite, NHDIM, p1, Z)] - + hydro->u->data[addr_rank1(hydro->nsite, NHDIM, m1, Z)]); /* Enforce tracelessness */ From 6fb90dbba8d88cba5b73442510285338d41d2fbb Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 19 Jan 2023 18:35:41 +0000 Subject: [PATCH 222/244] Minor correction --- src/io_metadata.c | 2 +- src/util_json.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/io_metadata.c b/src/io_metadata.c index dd6b79775..8c1ee916a 100644 --- a/src/io_metadata.c +++ b/src/io_metadata.c @@ -308,7 +308,7 @@ int io_metadata_from_file(pe_t * pe, const char * filename, if (ifail != 0 || json == NULL) goto err; { - io_options_t options = {0}; + io_options_t options = {IO_MODE_INVALID}; io_element_t element = {0}; cJSON * jcoords = cJSON_GetObjectItemCaseSensitive(json, "coords"); diff --git a/src/util_json.c b/src/util_json.c index 93b64f33a..97e239ba0 100644 --- a/src/util_json.c +++ b/src/util_json.c @@ -127,7 +127,7 @@ int util_json_from_file(const char * filename, cJSON ** json) { fseek(fp, 0, SEEK_END); len = ftell(fp); fseek(fp, 0, SEEK_SET); - buf = calloc(len+1, sizeof(char)); + buf = (char *) calloc(len+1, sizeof(char)); if (buf) { size_t nread = fread(buf, 1, len, fp); if (nread != len) ifail = +1; From d4f5e9f1cd76e04d82833f2ec49cdec204f97e77 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 20 Jan 2023 08:46:39 +0000 Subject: [PATCH 223/244] Fix return value checks --- util/extract.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/util/extract.c b/util/extract.c index c4af68139..eb446cda9 100644 --- a/util/extract.c +++ b/util/extract.c @@ -1566,16 +1566,15 @@ int file_get_file_index(const char * filename) { return ifile; } - -/***************************************************************************** +/**************************************************************************** * - * extract_read_single_file + * io_metadata_nrecord * - *****************************************************************************/ + ****************************************************************************/ int io_metadata_nrecord(const io_metadata_t * meta) { - /* This is still not very satisfactory. Need no,. of records in metadata */ + /* This is still not very satisfactory. Need no. of records in metadata */ int nrecord = meta->element.count; if (meta->element.datatype == MPI_CHAR) nrecord /= 23; nrec_ = nrecord; @@ -1583,6 +1582,12 @@ int io_metadata_nrecord(const io_metadata_t * meta) { return nrecord; } +/***************************************************************************** + * + * extract_read_single_file + * + *****************************************************************************/ + int extract_read_single_file(io_metadata_t * meta, const char * stub, int itime, double * datatotal) { @@ -1613,10 +1618,14 @@ int extract_read_single_file(io_metadata_t * meta, const char * stub, else { nread = fscanf(fp, "%le", &datum); } - if (nread != 1) printf("File out of records!!\n"); - /* Place at correct offset in the full array */ - *(datatotal + nrecord*indexd + nr) = datum; + if (nread != 1) { + printf("File missing or out of records!!\n"); + } + else { + /* Place at correct offset in the full array */ + *(datatotal + nrecord*indexd + nr) = datum; + } } } @@ -1812,7 +1821,8 @@ int extract_process_and_output(const char * stub, int ntime, cs_ntotal(meta->cs, ntargets); { - size_t n = nrecord*ntargets[0]*ntargets[1]*ntargets[2]; + size_t nr = nrecord; + size_t n = nr*ntargets[0]*ntargets[1]*ntargets[2]; datasection = (double *) calloc(n, sizeof(double)); if (datasection == NULL) printf("calloc(datasection) failed\n"); } From f4ebf5f52fe4c18dcd60e3d39e16b8560a8b691d Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 20 Jan 2023 09:36:40 +0000 Subject: [PATCH 224/244] Fix return check again --- util/extract.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/util/extract.c b/util/extract.c index eb446cda9..8d35e9b22 100644 --- a/util/extract.c +++ b/util/extract.c @@ -1614,18 +1614,15 @@ int extract_read_single_file(io_metadata_t * meta, const char * stub, int nread = 0; if (meta->options.iorformat == IO_RECORD_BINARY) { nread = fread(&datum, sizeof(double), 1, fp); + if (nread != 1) goto err; } else { nread = fscanf(fp, "%le", &datum); + if (nread != 1) goto err; } - if (nread != 1) { - printf("File missing or out of records!!\n"); - } - else { - /* Place at correct offset in the full array */ - *(datatotal + nrecord*indexd + nr) = datum; - } + /* Place at correct offset in the full array */ + *(datatotal + nrecord*indexd + nr) = datum; } } @@ -1633,8 +1630,12 @@ int extract_read_single_file(io_metadata_t * meta, const char * stub, } fclose(fp); - return 0; + + err: + + if (fp) fclose(fp); + return -1; } /***************************************************************************** From 61532489639e264f068a3e2a2b41052bb5fcb3b2 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 20 Jan 2023 11:59:44 +0000 Subject: [PATCH 225/244] Parallel program please --- src/pe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pe.c b/src/pe.c index 64c181826..4eab3f21f 100644 --- a/src/pe.c +++ b/src/pe.c @@ -165,7 +165,7 @@ __host__ int pe_message(pe_t * pe) { (pe->mpi_size == 1) ? "" : "es"); /* Git */ - printf("Git commit: %s\n\n", compiler.commit); + pe_info(pe, "Git commit: %s\n\n", compiler.commit); { char strctime[BUFSIZ] = {0}; From ce24a4f5389a519b2f831c45d20ea1ec524e4a0a Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 20 Jan 2023 16:43:46 +0000 Subject: [PATCH 226/244] Add advice on avoiding COdeQL problems --- CONTRIBUTING.md | 82 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index abcac61b3..b297041b5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,6 +10,9 @@ Computing Centre. However, contributions are welcome. The code is released under a BSD 3-clause license. +Please note that a contribution means signing over copyright to +The University of Edinburgh. + ### Running the tests Various tests exist in the `test` subdirectory. To check that no @@ -83,3 +86,82 @@ $ git branch -d patch-0.1.2 Release branches must branch from develop and must be merged back into both develop and master. + + +### Code quality + +One hesitates before making pronouncements on style, but here goes +anyway. As with English, the aim is not to follow rules, the aim to +be clear and unambiguous. + +#### Notes on style + +The code has historically been ANSI C, but the advent of GPUs has +meant that the C is a subset of ANSI which is also C++. This means +there are a few things which are legal in ANSI C, but need to be +avoided in the C++ context. + +- Avoid C++ reserved words as variable names. E.g., + ``` + int new = 0; + ``` + +- Explicit casts are required in some situations to satisfy C++. A common + example is + ``` + double * p = NULL; + p = (double *) malloc(...); /* Requires the cast in C++ */ + ``` + +- Don't be tempted by variable-sized object initialisation, e.g., + ``` + int nbyte = 2; + char tmp[nbyte+1] = {0}; /* variable sized initialisation */ + ``` + must be replaced by + ``` + int nbyte = 2; + char tmp[2+1] = {0}; + ``` + or some equivalent compile-time constant expression. ANSI does not + allow variable-sized initialisation. + +Finally, note we use C-style comments `/* ... */` and not C++ +single line comments as a matter of stubborn consistency. + + +#### Avoiding CodeQL alerts + +While some existing alerts remain, the goal is zero alerts. No new alerts +should be added if at all possible. + +- CodeQL failures at severity "High" and above will prevent merges. + They need to be fixed. + +- Use `util_fopen()` from `util_fopen.h` in place of `fopen()`. This + will avoid `File created without restricting permissions` errors. + +- Some care can be required with integer types, e.g., something like + ``` + { + int b = ...; + int c = ...; + size_t a = c*(b+1)*sizeof(double); + } + ``` + will generate a `Multiplication result converted to larger type`. + One needs to have the multiplication before the conversion. + ``` + { + int b = ...; + int c = ...; + int d = c*(b+1); + size_t a = d*sizeof(double); + } +``` +If `c*(b+1)` may overflow, then an explicit cast to `size_t`is required. + +- Anything using `scanf()` and the like must check return values. + +- Try to use fixed `const char *` strings where possible, as the + alternative of character buffers is a good source of problems. From 9940ade5d4cb272e984792c60a1f06bd15e8bc32 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 20 Jan 2023 16:44:18 +0000 Subject: [PATCH 227/244] Add credits --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bce239399..569fe8cd0 100644 --- a/README.md +++ b/README.md @@ -72,13 +72,16 @@ See `CONTRIBUTING.md` for further details of testing and development. -#### Attribution +#### Credits [![DOI](https://zenodo.org/badge/137508275.svg)](https://zenodo.org/badge/latestdoi/137508275) Recent release versions have a Zenodo-provided DOI. Please consider using the appropriate DOI as a reference if you use Ludwig in your publications. +From Version 0.19.0 we have included a copy of `cJSON` which is released +under an MIT license by Gave Gamble at https://github.com/DaveGamble/cJSON. + #### Help From 94a341f9bae45d4e2299139d43cbcd2bf0242752 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 20 Jan 2023 16:45:10 +0000 Subject: [PATCH 228/244] Add note on tutorial material --- CHANGES.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 441025d17..2195d372b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,7 +4,7 @@ version 0.19.0 - The extract_colloids.c utility has been updated so that it takes - only one comand line argument. + only one comand line argument for new I/O metadata. - LTGM.com analysis has been retired as the service is closing. The two outstanding recommendations are covered by CodeQL notes. - Input associated with the choice of force arising from the free energy @@ -19,6 +19,9 @@ version 0.19.0 compensated sum, which is more robust to threads/MPI. - Added compiler option information and git commit information at run time. +- The LaTeX tutorials document has been replaced by html tutorials + at https://ludwig.epcc.ed.ac.uk/ to reflect new I/O and to add a + number of new topics. version 0.18.0 From 728176d0efb74c2522b413a82024a5b1701465d8 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 20 Jan 2023 16:45:49 +0000 Subject: [PATCH 229/244] Update tutorial one to html with new i/o --- docs/tutorial/test1/input | 9 +- docs/tutorial/test1/stdout | 1047 +++-------------- ...00010000.001-001 => vel-000010000.001-001} | 0 docs/tutorial/test1/vel-00010000 | 64 - docs/tutorial/test1/vel.001-001.meta | 16 - 5 files changed, 181 insertions(+), 955 deletions(-) rename docs/tutorial/test1/{vel-00010000.001-001 => vel-000010000.001-001} (100%) delete mode 100644 docs/tutorial/test1/vel-00010000 delete mode 100644 docs/tutorial/test1/vel.001-001.meta diff --git a/docs/tutorial/test1/input b/docs/tutorial/test1/input index 9f94bbd01..38bc39637 100644 --- a/docs/tutorial/test1/input +++ b/docs/tutorial/test1/input @@ -26,7 +26,6 @@ periodicity 0_1_1 viscosity 0.833333 force 0_0.00001_0 isothermal_fluctuations off -#temperature 0.0000001 ############################################################################## # @@ -58,11 +57,11 @@ boundary_walls 1_0_0 # ############################################################################### -freq_vel 1000 -vel_format ASCII +default_io_mode mpiio +default_io_format ascii -freq_statistics 100 -config_at_end yes +freq_statistics 200 +config_at_end yes ############################################################################### # diff --git a/docs/tutorial/test1/stdout b/docs/tutorial/test1/stdout index f66623420..709a599ee 100644 --- a/docs/tutorial/test1/stdout +++ b/docs/tutorial/test1/stdout @@ -1,12 +1,17 @@ -Welcome to Ludwig v0.15.0 (MPI version running on 4 processes) -Start time: Mon Jan 31 12:18:59 2022 +Welcome to: Ludwig v0.19.0 (MPI version running on 2 processes) +Git commit: f4ebf5f52fe4c18dcd60e3d39e16b8560a8b691d + +Start time: Fri Jan 20 12:01:28 2023 Compiler: - name: Gnu 11.2.0 - version-string: 11.2.0 + name: Gnu 12.2.0 + version-string: 12.2.0 + options: -O2 -g -fopenmp -Wall + +Note assertions via standard C assert() are on. Target thread model: OpenMP. -OpenMP threads: 16; maximum number of threads: 16. +OpenMP threads: 1; maximum number of threads: 8. Read 15 user parameters from input @@ -15,8 +20,8 @@ No free energy selected System details -------------- System size: 64 1 1 -Decomposition: 4 1 1 -Local domain: 16 1 1 +Decomposition: 2 1 1 +Local domain: 32 1 1 Periodic: 0 1 1 Halo nhalo: 1 Reorder: true @@ -36,9 +41,9 @@ External magnetic field 0.00000e+00 0.00000e+00 0.00000e+00 Lattice Boltzmann distributions ------------------------------- Model: d3q19 -SIMD vector len: 4 +SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 @@ -80,25 +85,11 @@ Momentum - x y z Starting time step loop. Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -3.2404635e-15 6.4000000e-02 0.0000000e+00 -[fluid ] 5.2527427e-15 5.0510564e-02 0.0000000e+00 -[walls ] -8.4932061e-15 1.3489436e-02 0.0000000e+00 - -Velocity - x y z -[minimum ] -2.7408631e-16 6.8650149e-05 0.0000000e+00 -[maximum ] 4.5102810e-16 9.9147192e-04 1.1754944e-38 - -Completed cycle 100 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 Momentum - x y z -[total ] -2.6992297e-15 1.2800000e-01 0.0000000e+00 -[fluid ] -1.1331214e-14 8.9502713e-02 0.0000000e+00 +[total ] -2.6541269e-15 1.2800000e-01 0.0000000e+00 +[fluid ] -1.1286111e-14 8.9502713e-02 0.0000000e+00 [walls ] 8.6319840e-15 3.8497287e-02 0.0000000e+00 Velocity - x y z @@ -108,25 +99,11 @@ Velocity - x y z Completed cycle 200 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -2.2863655e-15 1.9200000e-01 0.0000000e+00 -[fluid ] 1.1397133e-14 1.2109980e-01 0.0000000e+00 -[walls ] -1.3683499e-14 7.0900205e-02 0.0000000e+00 - -Velocity - x y z -[minimum ] -7.6327833e-17 1.1366827e-04 0.0000000e+00 -[maximum ] 5.3429483e-16 2.6785667e-03 1.1754944e-38 - -Completed cycle 300 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 Momentum - x y z -[total ] -1.9290125e-15 2.5600000e-01 0.0000000e+00 -[fluid ] -1.1143864e-14 1.4688852e-01 0.0000000e+00 +[total ] -1.8735014e-15 2.5600000e-01 0.0000000e+00 +[fluid ] -1.1088352e-14 1.4688852e-01 0.0000000e+00 [walls ] 9.2148511e-15 1.0911148e-01 0.0000000e+00 Velocity - x y z @@ -136,25 +113,11 @@ Velocity - x y z Completed cycle 400 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -5.0619231e-15 3.2000000e-01 0.0000000e+00 -[fluid ] 6.8729744e-15 1.6796527e-01 0.0000000e+00 -[walls ] -1.1934898e-14 1.5203473e-01 0.0000000e+00 - -Velocity - x y z -[minimum ] -2.1510571e-16 1.4200852e-04 0.0000000e+00 -[maximum ] 4.8225313e-16 3.8295104e-03 1.1754944e-38 - -Completed cycle 500 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 4.4408921e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -6.9250161e-15 3.8400000e-01 0.0000000e+00 -[fluid ] -8.9789287e-15 1.8519545e-01 0.0000000e+00 +[total ] -6.8937911e-15 3.8400000e-01 0.0000000e+00 +[fluid ] -8.9477037e-15 1.8519545e-01 0.0000000e+00 [walls ] 2.0539126e-15 1.9880455e-01 0.0000000e+00 Velocity - x y z @@ -164,21 +127,7 @@ Velocity - x y z Completed cycle 600 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -7.1331829e-15 4.4800000e-01 0.0000000e+00 -[fluid ] 2.0816682e-15 1.9928175e-01 0.0000000e+00 -[walls ] -9.2148511e-15 2.4871825e-01 0.0000000e+00 - -Velocity - x y z -[minimum ] -3.9551695e-16 1.6090898e-04 0.0000000e+00 -[maximum ] 3.6082248e-16 4.5993452e-03 1.1754944e-38 - -Completed cycle 700 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 3.3306691e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 Momentum - x y z [total ] -5.5927485e-15 5.1200000e-01 0.0000000e+00 @@ -192,26 +141,11 @@ Velocity - x y z Completed cycle 800 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -4.3159920e-15 5.7600000e-01 0.0000000e+00 -[fluid ] -7.2025719e-15 2.2021294e-01 0.0000000e+00 -[walls ] 2.8865799e-15 3.5578706e-01 0.0000000e+00 - -Velocity - x y z -[minimum ] -4.5796700e-16 1.7354070e-04 0.0000000e+00 -[maximum ] 2.0469737e-16 5.1139024e-03 1.1754944e-38 - -Completed cycle 900 -Writing velocity output at step 1000! - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 3.3306691e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -4.5831394e-15 6.4000000e-01 0.0000000e+00 -[fluid ] 1.1556728e-14 2.2791015e-01 0.0000000e+00 +[total ] -4.6282422e-15 6.4000000e-01 0.0000000e+00 +[fluid ] 1.1511625e-14 2.2791015e-01 0.0000000e+00 [walls ] -1.6139867e-14 4.1208985e-01 0.0000000e+00 Velocity - x y z @@ -221,25 +155,11 @@ Velocity - x y z Completed cycle 1000 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -4.0731307e-15 7.0400000e-01 0.0000000e+00 -[fluid ] -1.5203117e-14 2.3420297e-01 0.0000000e+00 -[walls ] 1.1129986e-14 4.6979703e-01 0.0000000e+00 - -Velocity - x y z -[minimum ] -5.4817262e-16 1.8198349e-04 0.0000000e+00 -[maximum ] 2.6020852e-16 5.4578235e-03 1.1754944e-38 - -Completed cycle 1100 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 Momentum - x y z -[total ] -3.1918912e-15 7.6800000e-01 0.0000000e+00 -[fluid ] 1.3364310e-14 2.3934764e-01 0.0000000e+00 +[total ] -3.2300551e-15 7.6800000e-01 0.0000000e+00 +[fluid ] 1.3326146e-14 2.3934764e-01 0.0000000e+00 [walls ] -1.6556201e-14 5.2865236e-01 0.0000000e+00 Velocity - x y z @@ -249,25 +169,11 @@ Velocity - x y z Completed cycle 1200 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -3.2092384e-15 8.3200000e-01 0.0000000e+00 -[fluid ] -9.6901653e-15 2.4355365e-01 0.0000000e+00 -[walls ] 6.4809269e-15 5.8844635e-01 0.0000000e+00 - -Velocity - x y z -[minimum ] -5.1000870e-16 1.8762650e-04 0.0000000e+00 -[maximum ] 1.8735014e-16 5.6876942e-03 1.1754944e-38 - -Completed cycle 1300 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 4.4408921e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -2.9351521e-15 8.9600000e-01 0.0000000e+00 -[fluid ] 1.0207113e-14 2.4699225e-01 0.0000000e+00 +[total ] -2.9073965e-15 8.9600000e-01 0.0000000e+00 +[fluid ] 1.0234869e-14 2.4699225e-01 0.0000000e+00 [walls ] -1.3142265e-14 6.4900775e-01 0.0000000e+00 Velocity - x y z @@ -277,25 +183,11 @@ Velocity - x y z Completed cycle 1400 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -2.8657632e-15 9.6000000e-01 0.0000000e+00 -[fluid ] -2.5049407e-15 2.4980347e-01 0.0000000e+00 -[walls ] -3.6082248e-16 7.1019653e-01 0.0000000e+00 - -Velocity - x y z -[minimum ] -4.3021142e-16 1.9139818e-04 0.0000000e+00 -[maximum ] 3.4000580e-16 5.8413356e-03 1.1754944e-38 - -Completed cycle 1500 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 6.6613381e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -2.8935188e-15 1.0240000e+00 0.0000000e+00 -[fluid ] 2.4355518e-15 2.5210178e-01 0.0000000e+00 +[total ] -2.9698466e-15 1.0240000e+00 0.0000000e+00 +[fluid ] 2.3592239e-15 2.5210178e-01 0.0000000e+00 [walls ] -5.3290705e-15 7.7189822e-01 0.0000000e+00 Velocity - x y z @@ -305,25 +197,11 @@ Velocity - x y z Completed cycle 1600 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -3.2925052e-15 1.0880000e+00 0.0000000e+00 -[fluid ] -3.6255721e-15 2.5398074e-01 0.0000000e+00 -[walls ] 3.3306691e-16 8.3401926e-01 0.0000000e+00 - -Velocity - x y z -[minimum ] -3.5041414e-16 1.9391911e-04 0.0000000e+00 -[maximum ] 2.3939184e-16 5.9440267e-03 1.1754944e-38 - -Completed cycle 1700 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 7.7715612e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -3.9829251e-15 1.1520000e+00 0.0000000e+00 -[fluid ] -2.3592239e-15 2.5551689e-01 0.0000000e+00 +[total ] -3.9759862e-15 1.1520000e+00 0.0000000e+00 +[fluid ] -2.3522850e-15 2.5551689e-01 0.0000000e+00 [walls ] -1.6237012e-15 8.9648311e-01 0.0000000e+00 Velocity - x y z @@ -333,26 +211,11 @@ Velocity - x y z Completed cycle 1800 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 3.3306691e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 1.1102230e-15 1.00000000000 1.00000000000 Momentum - x y z -[total ] -4.2570114e-15 1.2160000e+00 0.0000000e+00 -[fluid ] 2.0851376e-15 2.5677276e-01 0.0000000e+00 -[walls ] -6.3421490e-15 9.5922724e-01 0.0000000e+00 - -Velocity - x y z -[minimum ] -3.1571967e-16 1.9560405e-04 0.0000000e+00 -[maximum ] 5.5164207e-16 6.0126636e-03 1.1754944e-38 - -Completed cycle 1900 -Writing velocity output at step 2000! - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -5.7003013e-15 1.2800000e+00 0.0000000e+00 -[fluid ] -9.4611818e-15 2.5779949e-01 0.0000000e+00 +[total ] -5.7315264e-15 1.2800000e+00 0.0000000e+00 +[fluid ] -9.4924069e-15 2.5779949e-01 0.0000000e+00 [walls ] 3.7608805e-15 1.0222005e+00 0.0000000e+00 Velocity - x y z @@ -362,25 +225,11 @@ Velocity - x y z Completed cycle 2000 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -5.2978455e-15 1.3440000e+00 0.0000000e+00 -[fluid ] 1.1216722e-14 2.5863889e-01 0.0000000e+00 -[walls ] -1.6514567e-14 1.0853611e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -1.2836954e-16 1.9673023e-04 0.0000000e+00 -[maximum ] 4.8225313e-16 6.0585393e-03 1.1754944e-38 - -Completed cycle 2100 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 4.4408921e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -6.3421490e-15 1.4080000e+00 0.0000000e+00 -[fluid ] -1.7902346e-15 2.5932513e-01 0.0000000e+00 +[total ] -6.2762295e-15 1.4080000e+00 0.0000000e+00 +[fluid ] -1.7243151e-15 2.5932513e-01 0.0000000e+00 [walls ] -4.5519144e-15 1.1486749e+00 0.0000000e+00 Velocity - x y z @@ -390,25 +239,11 @@ Velocity - x y z Completed cycle 2200 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 1.5543122e-15 1.00000000000 1.00000000000 Momentum - x y z -[total ] -7.8825835e-15 1.4720000e+00 0.0000000e+00 -[fluid ] 1.1102230e-16 2.5988617e-01 0.0000000e+00 -[walls ] -7.9936058e-15 1.2121138e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -3.5041414e-16 1.9748295e-04 0.0000000e+00 -[maximum ] 4.3021142e-16 6.0892017e-03 1.1754944e-38 - -Completed cycle 2300 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -1.0217521e-14 1.5360000e+00 0.0000000e+00 -[fluid ] -4.6525284e-15 2.6034485e-01 0.0000000e+00 +[total ] -1.0231399e-14 1.5360000e+00 0.0000000e+00 +[fluid ] -4.6664062e-15 2.6034485e-01 0.0000000e+00 [walls ] -5.5649929e-15 1.2756551e+00 0.0000000e+00 Velocity - x y z @@ -418,25 +253,11 @@ Velocity - x y z Completed cycle 2400 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -1.2587154e-14 1.6000000e+00 0.0000000e+00 -[fluid ] 9.8115960e-15 2.6071984e-01 0.0000000e+00 -[walls ] -2.2398750e-14 1.3392802e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -3.4000580e-16 1.9798606e-04 0.0000000e+00 -[maximum ] 6.6960326e-16 6.1096960e-03 1.1754944e-38 - -Completed cycle 2500 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 6.6613381e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -1.2847362e-14 1.6640000e+00 0.0000000e+00 -[fluid ] -1.4512697e-14 2.6102641e-01 0.0000000e+00 +[total ] -1.2812668e-14 1.6640000e+00 0.0000000e+00 +[fluid ] -1.4478002e-14 2.6102641e-01 0.0000000e+00 [walls ] 1.6653345e-15 1.4029736e+00 0.0000000e+00 Velocity - x y z @@ -446,25 +267,11 @@ Velocity - x y z Completed cycle 2600 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -1.4932500e-14 1.7280000e+00 0.0000000e+00 -[fluid ] 4.3576254e-15 2.6127705e-01 0.0000000e+00 -[walls ] -1.9290125e-14 1.4667230e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -2.1163626e-16 1.9832232e-04 0.0000000e+00 -[maximum ] 3.3306691e-16 6.1233939e-03 1.1754944e-38 - -Completed cycle 2700 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 3.3306691e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -1.2573276e-14 1.7920000e+00 0.0000000e+00 -[fluid ] 3.7331249e-15 2.6148195e-01 0.0000000e+00 +[total ] -1.2618379e-14 1.7920000e+00 0.0000000e+00 +[fluid ] 3.6880221e-15 2.6148195e-01 0.0000000e+00 [walls ] -1.6306401e-14 1.5305180e+00 0.0000000e+00 Velocity - x y z @@ -474,26 +281,11 @@ Velocity - x y z Completed cycle 2800 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 5.5511151e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -1.2306128e-14 1.8560000e+00 0.0000000e+00 -[fluid ] -1.6927432e-14 2.6164947e-01 0.0000000e+00 -[walls ] 4.6213033e-15 1.5943505e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -5.8633653e-16 1.9854708e-04 0.0000000e+00 -[maximum ] 9.3675068e-17 6.1325494e-03 1.1754944e-38 - -Completed cycle 2900 -Writing velocity output at step 3000! - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 3.3306691e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -1.3603701e-14 1.9200000e+00 0.0000000e+00 -[fluid ] 1.3235940e-14 2.6178643e-01 0.0000000e+00 +[total ] -1.3617579e-14 1.9200000e+00 0.0000000e+00 +[fluid ] 1.3222062e-14 2.6178643e-01 0.0000000e+00 [walls ] -2.6839642e-14 1.6582136e+00 0.0000000e+00 Velocity - x y z @@ -503,25 +295,11 @@ Velocity - x y z Completed cycle 3000 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 3.3306691e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -1.2725931e-14 1.9840000e+00 0.0000000e+00 -[fluid ] -3.2751579e-15 2.6189840e-01 0.0000000e+00 -[walls ] -9.4507735e-15 1.7221016e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -4.0592529e-16 1.9869730e-04 0.0000000e+00 -[maximum ] 2.2204460e-16 6.1386688e-03 1.1754944e-38 - -Completed cycle 3100 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 6.6613381e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -1.1376317e-14 2.0480000e+00 0.0000000e+00 -[fluid ] 1.1258355e-14 2.6198993e-01 0.0000000e+00 +[total ] -1.1393664e-14 2.0480000e+00 0.0000000e+00 +[fluid ] 1.1241008e-14 2.6198993e-01 0.0000000e+00 [walls ] -2.2634672e-14 1.7860101e+00 0.0000000e+00 Velocity - x y z @@ -531,25 +309,11 @@ Velocity - x y z Completed cycle 3200 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -9.4230179e-15 2.1120000e+00 0.0000000e+00 -[fluid ] -1.0130785e-14 2.6206477e-01 0.0000000e+00 -[walls ] 7.0776718e-16 1.8499352e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -3.5388359e-16 1.9879771e-04 0.0000000e+00 -[maximum ] 1.7694179e-16 6.1427589e-03 1.1754944e-38 - -Completed cycle 3300 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 4.4408921e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -9.4473041e-15 2.1760000e+00 0.0000000e+00 -[fluid ] 9.5375097e-15 2.6212595e-01 0.0000000e+00 +[total ] -9.4195485e-15 2.1760000e+00 0.0000000e+00 +[fluid ] 9.5652652e-15 2.6212595e-01 0.0000000e+00 [walls ] -1.8984814e-14 1.9138740e+00 0.0000000e+00 Velocity - x y z @@ -559,25 +323,11 @@ Velocity - x y z Completed cycle 3400 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -9.6832264e-15 2.2400000e+00 0.0000000e+00 -[fluid ] -6.6023575e-15 2.6217597e-01 0.0000000e+00 -[walls ] -3.0808689e-15 1.9778240e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -4.4408921e-16 1.9886482e-04 0.0000000e+00 -[maximum ] 1.7347235e-16 6.1454926e-03 1.1754944e-38 - -Completed cycle 3500 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 3.3306691e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -8.9130092e-15 2.3040000e+00 0.0000000e+00 -[fluid ] 9.4056707e-15 2.6221687e-01 0.0000000e+00 +[total ] -8.8609675e-15 2.3040000e+00 0.0000000e+00 +[fluid ] 9.4577124e-15 2.6221687e-01 0.0000000e+00 [walls ] -1.8318680e-14 2.0417831e+00 0.0000000e+00 Velocity - x y z @@ -587,25 +337,11 @@ Velocity - x y z Completed cycle 3600 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 4.4408921e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -7.2615525e-15 2.3680000e+00 0.0000000e+00 -[fluid ] -4.0835391e-15 2.6225030e-01 0.0000000e+00 -[walls ] -3.1780134e-15 2.1057497e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -4.0592529e-16 1.9890967e-04 0.0000000e+00 -[maximum ] 2.2898350e-16 6.1473198e-03 1.1754944e-38 - -Completed cycle 3700 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 1.2212453e-15 1.00000000000 1.00000000000 Momentum - x y z -[total ] -7.5321693e-15 2.4320000e+00 0.0000000e+00 -[fluid ] 1.6448648e-14 2.6227763e-01 0.0000000e+00 +[total ] -7.5529860e-15 2.4320000e+00 0.0000000e+00 +[fluid ] 1.6427831e-14 2.6227763e-01 0.0000000e+00 [walls ] -2.3980817e-14 2.1697224e+00 0.0000000e+00 Velocity - x y z @@ -614,27 +350,12 @@ Velocity - x y z Completed cycle 3800 -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -8.1878948e-15 2.4960000e+00 0.0000000e+00 -[fluid ] -1.9095836e-14 2.6229998e-01 0.0000000e+00 -[walls ] 1.0907941e-14 2.2337000e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -6.8695050e-16 1.9893965e-04 0.0000000e+00 -[maximum ] 5.2041704e-17 6.1485410e-03 1.1754944e-38 - -Completed cycle 3900 -Writing velocity output at step 4000! - Scalars - total mean variance min max [rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -9.4056707e-15 2.5600000e+00 0.0000000e+00 -[fluid ] 1.9779317e-14 2.6231825e-01 0.0000000e+00 +[total ] -9.4022012e-15 2.5600000e+00 0.0000000e+00 +[fluid ] 1.9782787e-14 2.6231825e-01 0.0000000e+00 [walls ] -2.9184988e-14 2.2976818e+00 0.0000000e+00 Velocity - x y z @@ -644,25 +365,11 @@ Velocity - x y z Completed cycle 4000 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -1.0210582e-14 2.6240000e+00 0.0000000e+00 -[fluid ] -1.2458784e-14 2.6233318e-01 0.0000000e+00 -[walls ] 2.2482016e-15 2.3616668e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -5.1694760e-16 1.9895969e-04 0.0000000e+00 -[maximum ] 1.1796120e-16 6.1493573e-03 1.1754944e-38 - -Completed cycle 4100 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 3.3306691e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -1.0800388e-14 2.6880000e+00 0.0000000e+00 -[fluid ] 1.2486540e-14 2.6234539e-01 0.0000000e+00 +[total ] -1.0824674e-14 2.6880000e+00 0.0000000e+00 +[fluid ] 1.2462253e-14 2.6234539e-01 0.0000000e+00 [walls ] -2.3286928e-14 2.4256546e+00 0.0000000e+00 Velocity - x y z @@ -671,26 +378,12 @@ Velocity - x y z Completed cycle 4200 -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -1.0921819e-14 2.7520000e+00 0.0000000e+00 -[fluid ] -9.2703623e-15 2.6235537e-01 0.0000000e+00 -[walls ] -1.6514567e-15 2.4896446e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -4.7531423e-16 1.9897308e-04 0.0000000e+00 -[maximum ] 1.9775848e-16 6.1499028e-03 1.1754944e-38 - -Completed cycle 4300 - Scalars - total mean variance min max [rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 Momentum - x y z -[total ] -1.2958384e-14 2.8160000e+00 0.0000000e+00 -[fluid ] -2.1857516e-16 2.6236354e-01 0.0000000e+00 +[total ] -1.2878587e-14 2.8160000e+00 0.0000000e+00 +[fluid ] -1.3877788e-16 2.6236354e-01 0.0000000e+00 [walls ] -1.2739809e-14 2.5536365e+00 0.0000000e+00 Velocity - x y z @@ -700,25 +393,11 @@ Velocity - x y z Completed cycle 4400 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -1.2906343e-14 2.8800000e+00 0.0000000e+00 -[fluid ] 7.1609385e-15 2.6237021e-01 0.0000000e+00 -[walls ] -2.0067281e-14 2.6176298e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -2.4980018e-16 1.9898203e-04 0.0000000e+00 -[maximum ] 4.4061976e-16 6.1502675e-03 1.1754944e-38 - -Completed cycle 4500 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -1.2302659e-14 2.9440000e+00 0.0000000e+00 -[fluid ] -1.3468393e-14 2.6237566e-01 0.0000000e+00 +[total ] -1.2278373e-14 2.9440000e+00 0.0000000e+00 +[fluid ] -1.3444107e-14 2.6237566e-01 0.0000000e+00 [walls ] 1.1657342e-15 2.6816243e+00 0.0000000e+00 Velocity - x y z @@ -727,26 +406,12 @@ Velocity - x y z Completed cycle 4600 -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -1.4994950e-14 3.0080000e+00 0.0000000e+00 -[fluid ] 7.1678774e-15 2.6238012e-01 0.0000000e+00 -[walls ] -2.2162827e-14 2.7456199e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -2.8102520e-16 1.9898802e-04 0.0000000e+00 -[maximum ] 5.2388649e-16 6.1505112e-03 1.1754944e-38 - -Completed cycle 4700 - Scalars - total mean variance min max [rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -1.3069407e-14 3.0720000e+00 0.0000000e+00 -[fluid ] 7.3899220e-16 2.6238377e-01 0.0000000e+00 +[total ] -1.3034712e-14 3.0720000e+00 0.0000000e+00 +[fluid ] 7.7368667e-16 2.6238377e-01 0.0000000e+00 [walls ] -1.3808399e-14 2.8096162e+00 0.0000000e+00 Velocity - x y z @@ -759,23 +424,8 @@ Scalars - total mean variance min max [rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -1.1567136e-14 3.1360000e+00 0.0000000e+00 -[fluid ] -2.9212743e-15 2.6238675e-01 0.0000000e+00 -[walls ] -8.6458618e-15 2.8736133e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -3.6776138e-16 1.9899202e-04 0.0000000e+00 -[maximum ] 2.4633073e-16 6.1506741e-03 1.1754944e-38 - -Completed cycle 4900 -Writing velocity output at step 5000! - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -1.1067536e-14 3.2000000e+00 0.0000000e+00 -[fluid ] 5.4886651e-15 2.6238919e-01 0.0000000e+00 +[total ] -1.1095291e-14 3.2000000e+00 0.0000000e+00 +[fluid ] 5.4609095e-15 2.6238919e-01 0.0000000e+00 [walls ] -1.6556201e-14 2.9376108e+00 0.0000000e+00 Velocity - x y z @@ -784,26 +434,12 @@ Velocity - x y z Completed cycle 5000 -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -9.5028152e-15 3.2640000e+00 0.0000000e+00 -[fluid ] -5.7558125e-15 2.6239118e-01 0.0000000e+00 -[walls ] -3.7470027e-15 3.0016088e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -4.8225313e-16 1.9899469e-04 0.0000000e+00 -[maximum ] 2.8449465e-16 6.1507830e-03 1.1754944e-38 - -Completed cycle 5100 - Scalars - total mean variance min max [rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -7.1713468e-15 3.3280000e+00 0.0000000e+00 -[fluid ] 3.7227166e-15 2.6239281e-01 0.0000000e+00 +[total ] -7.1019579e-15 3.3280000e+00 0.0000000e+00 +[fluid ] 3.7921055e-15 2.6239281e-01 0.0000000e+00 [walls ] -1.0894063e-14 3.0656072e+00 0.0000000e+00 Velocity - x y z @@ -813,25 +449,11 @@ Velocity - x y z Completed cycle 5200 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -6.8486883e-15 3.3920000e+00 0.0000000e+00 -[fluid ] 9.0205621e-17 2.6239414e-01 0.0000000e+00 -[walls ] -6.9388939e-15 3.1296059e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -2.6714742e-16 1.9899647e-04 0.0000000e+00 -[maximum ] 4.4061976e-16 6.1508558e-03 1.1754944e-38 - -Completed cycle 5300 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -7.6327833e-15 3.4560000e+00 0.0000000e+00 -[fluid ] 3.1780134e-15 2.6239523e-01 0.0000000e+00 +[total ] -7.6084972e-15 3.4560000e+00 0.0000000e+00 +[fluid ] 3.2022995e-15 2.6239523e-01 0.0000000e+00 [walls ] -1.0810797e-14 3.1936048e+00 0.0000000e+00 Velocity - x y z @@ -841,25 +463,11 @@ Velocity - x y z Completed cycle 5400 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 3.3306691e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -8.4758589e-15 3.5200000e+00 0.0000000e+00 -[fluid ] 4.5102810e-17 2.6239612e-01 0.0000000e+00 -[walls ] -8.5209617e-15 3.2576039e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -3.3653635e-16 1.9899767e-04 0.0000000e+00 -[maximum ] 4.2327253e-16 6.1509044e-03 1.1754944e-38 - -Completed cycle 5500 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 8.8817842e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -7.7889084e-15 3.5840000e+00 0.0000000e+00 -[fluid ] 9.0032148e-15 2.6239684e-01 0.0000000e+00 +[total ] -7.8062556e-15 3.5840000e+00 0.0000000e+00 +[fluid ] 8.9858676e-15 2.6239684e-01 0.0000000e+00 [walls ] -1.6792123e-14 3.3216032e+00 0.0000000e+00 Velocity - x y z @@ -872,22 +480,8 @@ Scalars - total mean variance min max [rho] 64.00 1.00000000000 4.4408921e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -3.8580250e-15 3.6480000e+00 0.0000000e+00 -[fluid ] -9.5895514e-15 2.6239744e-01 0.0000000e+00 -[walls ] 5.7315264e-15 3.3856026e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -5.4470317e-16 1.9899847e-04 0.0000000e+00 -[maximum ] 1.3183898e-16 6.1509369e-03 1.1754944e-38 - -Completed cycle 5700 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -4.1355808e-15 3.7120000e+00 0.0000000e+00 -[fluid ] 1.0547119e-14 2.6239793e-01 0.0000000e+00 +[total ] -4.1529280e-15 3.7120000e+00 0.0000000e+00 +[fluid ] 1.0529771e-14 2.6239793e-01 0.0000000e+00 [walls ] -1.4682700e-14 3.4496021e+00 0.0000000e+00 Velocity - x y z @@ -897,26 +491,11 @@ Velocity - x y z Completed cycle 5800 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -2.7408631e-15 3.7760000e+00 0.0000000e+00 -[fluid ] 4.8364091e-15 2.6239832e-01 0.0000000e+00 -[walls ] -7.5772721e-15 3.5136017e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -2.4980018e-16 1.9899900e-04 0.0000000e+00 -[maximum ] 3.3306691e-16 6.1509587e-03 1.1754944e-38 - -Completed cycle 5900 -Writing velocity output at step 6000! - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 4.4408921e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -3.6325110e-15 3.8400000e+00 0.0000000e+00 -[fluid ] -9.3085262e-15 2.6239865e-01 0.0000000e+00 +[total ] -3.6533276e-15 3.8400000e+00 0.0000000e+00 +[fluid ] -9.3293429e-15 2.6239865e-01 0.0000000e+00 [walls ] 5.6760152e-15 3.5776014e+00 0.0000000e+00 Velocity - x y z @@ -926,25 +505,11 @@ Velocity - x y z Completed cycle 6000 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 3.3306691e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -3.5353664e-15 3.9040000e+00 0.0000000e+00 -[fluid ] 1.1716322e-14 2.6239891e-01 0.0000000e+00 -[walls ] -1.5251689e-14 3.6416011e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -1.2836954e-16 1.9899936e-04 0.0000000e+00 -[maximum ] 4.7531423e-16 6.1509732e-03 1.1754944e-38 - -Completed cycle 6100 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 5.5511151e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -2.6333102e-15 3.9680000e+00 0.0000000e+00 -[fluid ] -8.0040141e-15 2.6239913e-01 0.0000000e+00 +[total ] -2.6575964e-15 3.9680000e+00 0.0000000e+00 +[fluid ] -8.0283002e-15 2.6239913e-01 0.0000000e+00 [walls ] 5.3707039e-15 3.7056009e+00 0.0000000e+00 Velocity - x y z @@ -954,25 +519,11 @@ Velocity - x y z Completed cycle 6200 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -3.7261860e-15 4.0320000e+00 0.0000000e+00 -[fluid ] 8.3752449e-15 2.6239931e-01 0.0000000e+00 -[walls ] -1.2101431e-14 3.7696007e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -2.1163626e-16 1.9899960e-04 0.0000000e+00 -[maximum ] 4.0592529e-16 6.1509829e-03 1.1754944e-38 - -Completed cycle 6300 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 3.3306691e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 8.8817842e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -1.7832957e-15 4.0960000e+00 0.0000000e+00 -[fluid ] -7.9589113e-15 2.6239945e-01 0.0000000e+00 +[total ] -1.7624791e-15 4.0960000e+00 0.0000000e+00 +[fluid ] -7.9380946e-15 2.6239945e-01 0.0000000e+00 [walls ] 6.1756156e-15 3.8336005e+00 0.0000000e+00 Velocity - x y z @@ -982,25 +533,11 @@ Velocity - x y z Completed cycle 6400 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -3.4347525e-16 4.1600000e+00 0.0000000e+00 -[fluid ] 1.0148132e-14 2.6239957e-01 0.0000000e+00 -[walls ] -1.0491608e-14 3.8976004e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -8.3266727e-17 1.9899975e-04 0.0000000e+00 -[maximum ] 4.1633363e-16 6.1509894e-03 1.1754944e-38 - -Completed cycle 6500 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 6.6613381e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] 1.2073675e-15 4.2240000e+00 0.0000000e+00 -[fluid ] -5.7592819e-15 2.6239967e-01 0.0000000e+00 +[total ] 1.2559398e-15 4.2240000e+00 0.0000000e+00 +[fluid ] -5.7107097e-15 2.6239967e-01 0.0000000e+00 [walls ] 6.9666495e-15 3.9616003e+00 0.0000000e+00 Velocity - x y z @@ -1010,25 +547,11 @@ Velocity - x y z Completed cycle 6600 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 1.4432899e-15 1.00000000000 1.00000000000 Momentum - x y z -[total ] 1.1692036e-15 4.2880000e+00 0.0000000e+00 -[fluid ] 2.4320823e-15 2.6239975e-01 0.0000000e+00 -[walls ] -1.2628787e-15 4.0256003e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -3.3653635e-16 1.9899986e-04 0.0000000e+00 -[maximum ] 3.5735304e-16 6.1509937e-03 1.1754944e-38 - -Completed cycle 6700 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] 1.6514567e-15 4.3520000e+00 0.0000000e+00 -[fluid ] -2.1233015e-15 2.6239981e-01 0.0000000e+00 +[total ] 1.6132928e-15 4.3520000e+00 0.0000000e+00 +[fluid ] -2.1614655e-15 2.6239981e-01 0.0000000e+00 [walls ] 3.7747583e-15 4.0896002e+00 0.0000000e+00 Velocity - x y z @@ -1038,26 +561,11 @@ Velocity - x y z Completed cycle 6800 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 3.3306691e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] 4.2015003e-15 4.4160000e+00 0.0000000e+00 -[fluid ] 1.0408341e-17 2.6239987e-01 0.0000000e+00 -[walls ] 4.1910919e-15 4.1536001e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -3.5388359e-16 1.9899993e-04 0.0000000e+00 -[maximum ] 2.3939184e-16 6.1509966e-03 1.1754944e-38 - -Completed cycle 6900 -Writing velocity output at step 7000! - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 6.6613381e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] 5.2770288e-15 4.4800000e+00 0.0000000e+00 -[fluid ] -5.9327543e-16 2.6239991e-01 0.0000000e+00 +[total ] 5.2700899e-15 4.4800000e+00 0.0000000e+00 +[fluid ] -6.0021432e-16 2.6239991e-01 0.0000000e+00 [walls ] 5.8703042e-15 4.2176001e+00 0.0000000e+00 Velocity - x y z @@ -1067,25 +575,11 @@ Velocity - x y z Completed cycle 7000 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] 4.8086535e-15 4.5440000e+00 0.0000000e+00 -[fluid ] 3.2959746e-15 2.6239995e-01 0.0000000e+00 -[walls ] 1.5126789e-15 4.2816001e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -2.5326963e-16 1.9899998e-04 0.0000000e+00 -[maximum ] 3.6776138e-16 6.1509986e-03 1.1754944e-38 - -Completed cycle 7100 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 4.4408921e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] 5.5788707e-15 4.6080000e+00 0.0000000e+00 -[fluid ] -5.4262150e-15 2.6239997e-01 0.0000000e+00 +[total ] 5.5858096e-15 4.6080000e+00 0.0000000e+00 +[fluid ] -5.4192761e-15 2.6239997e-01 0.0000000e+00 [walls ] 1.1005086e-14 4.3456000e+00 0.0000000e+00 Velocity - x y z @@ -1095,25 +589,11 @@ Velocity - x y z Completed cycle 7200 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] 4.6698756e-15 4.6720000e+00 0.0000000e+00 -[fluid ] 7.2858386e-16 2.6240000e-01 0.0000000e+00 -[walls ] 3.9412917e-15 4.4096000e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -2.2204460e-16 1.9900001e-04 0.0000000e+00 -[maximum ] 3.9204751e-16 6.1509998e-03 1.1754944e-38 - -Completed cycle 7300 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 4.4408921e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] 1.9151347e-15 4.7360000e+00 0.0000000e+00 -[fluid ] -2.6645353e-15 2.6240002e-01 0.0000000e+00 +[total ] 1.8769708e-15 4.7360000e+00 0.0000000e+00 +[fluid ] -2.7026992e-15 2.6240002e-01 0.0000000e+00 [walls ] 4.5796700e-15 4.4736000e+00 0.0000000e+00 Velocity - x y z @@ -1123,25 +603,11 @@ Velocity - x y z Completed cycle 7400 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] 2.0781987e-15 4.8000000e+00 0.0000000e+00 -[fluid ] 9.5444486e-15 2.6240003e-01 0.0000000e+00 -[walls ] -7.4662498e-15 4.5376000e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -1.0061396e-16 1.9900003e-04 0.0000000e+00 -[maximum ] 4.7531423e-16 6.1510007e-03 1.1754944e-38 - -Completed cycle 7500 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 1.1102230e-15 1.00000000000 1.00000000000 Momentum - x y z -[total ] 3.8268000e-15 4.8640000e+00 0.0000000e+00 -[fluid ] -4.6108950e-15 2.6240005e-01 0.0000000e+00 +[total ] 3.7504722e-15 4.8640000e+00 0.0000000e+00 +[fluid ] -4.6872228e-15 2.6240005e-01 0.0000000e+00 [walls ] 8.4376950e-15 4.6016000e+00 0.0000000e+00 Velocity - x y z @@ -1151,25 +617,11 @@ Velocity - x y z Completed cycle 7600 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] 4.4651782e-15 4.9280000e+00 0.0000000e+00 -[fluid ] -1.7937041e-15 2.6240006e-01 0.0000000e+00 -[walls ] 6.2588823e-15 4.6655999e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -3.4694470e-16 1.9900005e-04 0.0000000e+00 -[maximum ] 3.5388359e-16 6.1510013e-03 1.1754944e-38 - -Completed cycle 7700 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 5.5511151e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] 1.2698176e-15 4.9920000e+00 0.0000000e+00 -[fluid ] 2.7408631e-15 2.6240007e-01 0.0000000e+00 +[total ] 1.2732870e-15 4.9920000e+00 0.0000000e+00 +[fluid ] 2.7443325e-15 2.6240007e-01 0.0000000e+00 [walls ] -1.4710455e-15 4.7295999e+00 0.0000000e+00 Velocity - x y z @@ -1179,22 +631,7 @@ Velocity - x y z Completed cycle 7800 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 3.3306691e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -3.0878078e-16 5.0560000e+00 0.0000000e+00 -[fluid ] 1.1622647e-15 2.6240007e-01 0.0000000e+00 -[walls ] -1.4710455e-15 4.7935999e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -3.0878078e-16 1.9900006e-04 0.0000000e+00 -[maximum ] 2.9490299e-16 6.1510017e-03 1.1754944e-38 - -Completed cycle 7900 -Writing velocity output at step 8000! - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 1.2212453e-15 1.00000000000 1.00000000000 Momentum - x y z [total ] -1.2420620e-15 5.1200000e+00 0.0000000e+00 @@ -1208,25 +645,11 @@ Velocity - x y z Completed cycle 8000 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -3.7470027e-15 5.1840000e+00 0.0000000e+00 -[fluid ] 4.5657922e-15 2.6240008e-01 0.0000000e+00 -[walls ] -8.3127949e-15 4.9215999e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -1.2490009e-16 1.9900006e-04 0.0000000e+00 -[maximum ] 3.7123082e-16 6.1510019e-03 1.1754944e-38 - -Completed cycle 8100 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 4.4408921e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 7.7715612e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -3.4694470e-15 5.2480000e+00 0.0000000e+00 -[fluid ] -3.3029135e-15 2.6240009e-01 0.0000000e+00 +[total ] -3.5110803e-15 5.2480000e+00 0.0000000e+00 +[fluid ] -3.3445469e-15 2.6240009e-01 0.0000000e+00 [walls ] -1.6653345e-16 4.9855999e+00 0.0000000e+00 Velocity - x y z @@ -1236,25 +659,11 @@ Velocity - x y z Completed cycle 8200 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 8.8817842e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -2.1267710e-15 5.3120000e+00 0.0000000e+00 -[fluid ] 4.1876225e-15 2.6240009e-01 0.0000000e+00 -[walls ] -6.3143935e-15 5.0495999e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -2.8449465e-16 1.9900007e-04 0.0000000e+00 -[maximum ] 3.6776138e-16 6.1510021e-03 1.1754944e-38 - -Completed cycle 8300 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] -9.7144515e-17 5.3760000e+00 0.0000000e+00 -[fluid ] 4.4408921e-15 2.6240009e-01 0.0000000e+00 +[total ] -1.4918622e-16 5.3760000e+00 0.0000000e+00 +[fluid ] 4.3888504e-15 2.6240009e-01 0.0000000e+00 [walls ] -4.5380366e-15 5.1135999e+00 0.0000000e+00 Velocity - x y z @@ -1264,21 +673,7 @@ Velocity - x y z Completed cycle 8400 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 3.3306691e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] -2.7061686e-16 5.4400000e+00 0.0000000e+00 -[fluid ] -5.7662208e-15 2.6240010e-01 0.0000000e+00 -[walls ] 5.4956040e-15 5.1775999e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -4.5449755e-16 1.9900007e-04 0.0000000e+00 -[maximum ] 3.5388359e-16 6.1510022e-03 1.1754944e-38 - -Completed cycle 8500 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 4.4408921e-16 1.00000000000 1.00000000000 Momentum - x y z [total ] -4.8919202e-16 5.5040000e+00 0.0000000e+00 @@ -1292,25 +687,11 @@ Velocity - x y z Completed cycle 8600 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 3.3306691e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] 2.2724878e-15 5.5680000e+00 0.0000000e+00 -[fluid ] -5.3325400e-15 2.6240010e-01 0.0000000e+00 -[walls ] 7.6050277e-15 5.3055999e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -4.1633363e-16 1.9900007e-04 0.0000000e+00 -[maximum ] 2.8102520e-16 6.1510023e-03 1.1754944e-38 - -Completed cycle 8700 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] 5.3082538e-16 5.6320000e+00 0.0000000e+00 -[fluid ] 7.2511441e-16 2.6240010e-01 0.0000000e+00 +[total ] 4.7531423e-16 5.6320000e+00 0.0000000e+00 +[fluid ] 6.6960326e-16 2.6240010e-01 0.0000000e+00 [walls ] -1.9428903e-16 5.3695999e+00 0.0000000e+00 Velocity - x y z @@ -1320,26 +701,11 @@ Velocity - x y z Completed cycle 8800 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] 2.4286129e-17 5.6960000e+00 0.0000000e+00 -[fluid ] -3.6429193e-16 2.6240010e-01 0.0000000e+00 -[walls ] 3.8857806e-16 5.4335999e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -4.1633363e-16 1.9900007e-04 0.0000000e+00 -[maximum ] 3.4347525e-16 6.1510024e-03 1.1754944e-38 - -Completed cycle 8900 -Writing velocity output at step 9000! - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 4.4408921e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] 1.5820678e-15 5.7600000e+00 0.0000000e+00 -[fluid ] 1.2212453e-15 2.6240010e-01 0.0000000e+00 +[total ] 1.5612511e-15 5.7600000e+00 0.0000000e+00 +[fluid ] 1.2004286e-15 2.6240010e-01 0.0000000e+00 [walls ] 3.6082248e-16 5.4975999e+00 0.0000000e+00 Velocity - x y z @@ -1349,25 +715,11 @@ Velocity - x y z Completed cycle 9000 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 3.3306691e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] 4.4686477e-15 5.8240000e+00 0.0000000e+00 -[fluid ] 2.2204460e-16 2.6240010e-01 0.0000000e+00 -[walls ] 4.2466031e-15 5.5615999e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -4.4408921e-16 1.9900007e-04 0.0000000e+00 -[maximum ] 3.9898640e-16 6.1510024e-03 1.1754944e-38 - -Completed cycle 9100 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 3.3306691e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 1.4432899e-15 1.00000000000 1.00000000000 Momentum - x y z -[total ] 5.0098814e-15 5.8880000e+00 0.0000000e+00 -[fluid ] 3.8719028e-15 2.6240010e-01 0.0000000e+00 +[total ] 4.9821258e-15 5.8880000e+00 0.0000000e+00 +[fluid ] 3.8441472e-15 2.6240010e-01 0.0000000e+00 [walls ] 1.1379786e-15 5.6255999e+00 0.0000000e+00 Velocity - x y z @@ -1377,25 +729,11 @@ Velocity - x y z Completed cycle 9200 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 3.3306691e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] 5.6343818e-15 5.9520000e+00 0.0000000e+00 -[fluid ] -2.8727021e-15 2.6240010e-01 0.0000000e+00 -[walls ] 8.5070839e-15 5.6895999e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -6.4878658e-16 1.9900007e-04 0.0000000e+00 -[maximum ] 4.7184479e-16 6.1510024e-03 1.1754944e-38 - -Completed cycle 9300 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 4.4408921e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 6.6613381e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] 6.3664352e-15 6.0160000e+00 0.0000000e+00 -[fluid ] 3.5492442e-15 2.6240010e-01 0.0000000e+00 +[total ] 6.3837824e-15 6.0160000e+00 0.0000000e+00 +[fluid ] 3.5665915e-15 2.6240010e-01 0.0000000e+00 [walls ] 2.8171909e-15 5.7535999e+00 0.0000000e+00 Velocity - x y z @@ -1405,25 +743,11 @@ Velocity - x y z Completed cycle 9400 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] 7.0742023e-15 6.0800000e+00 0.0000000e+00 -[fluid ] -7.1782857e-15 2.6240010e-01 0.0000000e+00 -[walls ] 1.4252488e-14 5.8175999e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -3.8510861e-16 1.9900007e-04 0.0000000e+00 -[maximum ] 1.9428903e-16 6.1510024e-03 1.1754944e-38 - -Completed cycle 9500 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 6.6613381e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 3.3306691e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] 9.0830121e-15 6.1440000e+00 0.0000000e+00 -[fluid ] 8.4307561e-15 2.6240010e-01 0.0000000e+00 +[total ] 9.0934205e-15 6.1440000e+00 0.0000000e+00 +[fluid ] 8.4411644e-15 2.6240010e-01 0.0000000e+00 [walls ] 6.5225603e-16 5.8815999e+00 0.0000000e+00 Velocity - x y z @@ -1433,25 +757,11 @@ Velocity - x y z Completed cycle 9600 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] 9.7907793e-15 6.2080000e+00 0.0000000e+00 -[fluid ] 4.4061976e-15 2.6240010e-01 0.0000000e+00 -[walls ] 5.3845817e-15 5.9455999e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -2.2204460e-16 1.9900008e-04 0.0000000e+00 -[maximum ] 3.6429193e-16 6.1510024e-03 1.1754944e-38 - -Completed cycle 9700 - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 3.3306691e-16 1.00000000000 1.00000000000 Momentum - x y z -[total ] 9.0448482e-15 6.2720000e+00 0.0000000e+00 -[fluid ] -9.9815989e-15 2.6240010e-01 0.0000000e+00 +[total ] 9.0621954e-15 6.2720000e+00 0.0000000e+00 +[fluid ] -9.9642516e-15 2.6240010e-01 0.0000000e+00 [walls ] 1.9026447e-14 6.0095999e+00 0.0000000e+00 Velocity - x y z @@ -1461,26 +771,11 @@ Velocity - x y z Completed cycle 9800 Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] 1.0633855e-14 6.3360000e+00 0.0000000e+00 -[fluid ] 2.9594383e-15 2.6240010e-01 0.0000000e+00 -[walls ] 7.6744167e-15 6.0735999e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -2.8449465e-16 1.9900008e-04 0.0000000e+00 -[maximum ] 3.4000580e-16 6.1510024e-03 1.1754944e-38 - -Completed cycle 9900 -Writing velocity output at step 10000! - -Scalars - total mean variance min max -[rho] 64.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 +[rho] 64.00 1.00000000000 1.1102230e-15 1.00000000000 1.00000000000 Momentum - x y z -[total ] 1.0928758e-14 6.4000000e+00 0.0000000e+00 -[fluid ] -1.1171619e-15 2.6240010e-01 0.0000000e+00 +[total ] 1.0998147e-14 6.4000000e+00 0.0000000e+00 +[fluid ] -1.0477730e-15 2.6240010e-01 0.0000000e+00 [walls ] 1.2045920e-14 6.1375999e+00 0.0000000e+00 Velocity - x y z @@ -1488,23 +783,35 @@ Velocity - x y z [maximum ] 3.5388359e-16 6.1510024e-03 1.1754944e-38 Completed cycle 10000 -Writing velocity output at step 10000! +MPIIO wrote to dist-000010000.001-001 +- dist aggregated 0.029 MB in 0.000 seconds +- dist wrote 0.029 MB in 0.010 seconds +- dist rate 0.003 GB per second +Writing rho/velocity output at step 10000! +MPIIO wrote to rho-000010000.001-001 +- rho aggregated 0.002 MB in 0.000 seconds +- rho wrote 0.002 MB in 0.009 seconds +- rho rate 0.000 GB per second +MPIIO wrote to vel-000010000.001-001 +- vel aggregated 0.004 MB in 0.000 seconds +- vel wrote 0.004 MB in 0.009 seconds +- vel rate 0.001 GB per second Timer resolution: 1e-06 second Timer statistics Section: tmin tmax total - Total: 32.172 32.172 32.172 32.172115 (1 call) - Time step loop: 0.001 0.004 32.043 0.003204 (10000 calls) - Propagation: 0.000 0.000 1.340 0.000134 (10000 calls) - Propagtn (krnl) : 0.000 0.000 1.301 0.000130 (10000 calls) - Collision: 0.000 0.000 1.361 0.000136 (10000 calls) - Collision (krnl) : 0.000 0.000 1.305 0.000131 (10000 calls) - Lattice halos: 0.001 0.004 17.145 0.001715 (10000 calls) - phi gradients: 0.000 0.000 0.011 0.000001 (10000 calls) - BBL: 0.000 0.003 9.637 0.000964 (10000 calls) - Force calculation: 0.000 0.000 0.002 0.000000 (10000 calls) + Total: 3.886 3.886 3.886 3.885617 (1 call) + Time step loop: 0.000 0.001 3.842 0.000384 (10000 calls) + Propagation: 0.000 0.000 0.778 0.000078 (10000 calls) + Propagtn (krnl) : 0.000 0.000 0.764 0.000076 (10000 calls) + Collision: 0.000 0.000 1.779 0.000178 (10000 calls) + Collision (krnl) : 0.000 0.000 1.760 0.000176 (10000 calls) + Lattice halos: 0.000 0.001 1.171 0.000117 (10000 calls) + phi gradients: 0.000 0.000 0.001 0.000000 (10000 calls) + BBL: 0.000 0.000 0.026 0.000003 (10000 calls) + Force calculation: 0.000 0.000 0.001 0.000000 (10000 calls) phi update: 0.000 0.000 0.001 0.000000 (10000 calls) - Free1: 0.000 0.005 0.123 0.000012 (10000 calls) -End time: Mon Jan 31 12:19:31 2022 + Free1: 0.000 0.000 0.006 0.000001 (10000 calls) +End time: Fri Jan 20 12:01:32 2023 Ludwig finished normally. diff --git a/docs/tutorial/test1/vel-00010000.001-001 b/docs/tutorial/test1/vel-000010000.001-001 similarity index 100% rename from docs/tutorial/test1/vel-00010000.001-001 rename to docs/tutorial/test1/vel-000010000.001-001 diff --git a/docs/tutorial/test1/vel-00010000 b/docs/tutorial/test1/vel-00010000 deleted file mode 100644 index ef0b07245..000000000 --- a/docs/tutorial/test1/vel-00010000 +++ /dev/null @@ -1,64 +0,0 @@ - 1 1 1 -1.457168e-16 1.990001e-04 0.000000e+00 - 2 1 1 8.326673e-17 5.710002e-04 0.000000e+00 - 3 1 1 -1.179612e-16 9.310004e-04 0.000000e+00 - 4 1 1 1.353084e-16 1.279001e-03 0.000000e+00 - 5 1 1 1.318390e-16 1.615001e-03 0.000000e+00 - 6 1 1 6.245005e-17 1.939001e-03 0.000000e+00 - 7 1 1 2.567391e-16 2.251001e-03 0.000000e+00 - 8 1 1 3.365364e-16 2.551001e-03 0.000000e+00 - 9 1 1 3.538836e-16 2.839001e-03 0.000000e+00 - 10 1 1 2.359224e-16 3.115001e-03 0.000000e+00 - 11 1 1 2.498002e-16 3.379001e-03 0.000000e+00 - 12 1 1 3.469447e-16 3.631001e-03 0.000000e+00 - 13 1 1 2.012279e-16 3.871002e-03 0.000000e+00 - 14 1 1 1.977585e-16 4.099002e-03 0.000000e+00 - 15 1 1 2.775558e-16 4.315002e-03 0.000000e+00 - 16 1 1 1.665335e-16 4.519002e-03 0.000000e+00 - 17 1 1 1.665335e-16 4.711002e-03 0.000000e+00 - 18 1 1 7.285839e-17 4.891002e-03 0.000000e+00 - 19 1 1 4.510281e-17 5.059002e-03 0.000000e+00 - 20 1 1 1.769418e-16 5.215002e-03 0.000000e+00 - 21 1 1 2.706169e-16 5.359002e-03 0.000000e+00 - 22 1 1 8.673617e-17 5.491002e-03 0.000000e+00 - 23 1 1 -4.163336e-17 5.611002e-03 0.000000e+00 - 24 1 1 2.428613e-17 5.719002e-03 0.000000e+00 - 25 1 1 -1.144917e-16 5.815002e-03 0.000000e+00 - 26 1 1 -1.040834e-16 5.899002e-03 0.000000e+00 - 27 1 1 -1.804112e-16 5.971002e-03 0.000000e+00 - 28 1 1 -1.977585e-16 6.031002e-03 0.000000e+00 - 29 1 1 -1.457168e-16 6.079002e-03 0.000000e+00 - 30 1 1 -1.561251e-16 6.115002e-03 0.000000e+00 - 31 1 1 -7.632783e-17 6.139002e-03 0.000000e+00 - 32 1 1 -2.983724e-16 6.151002e-03 0.000000e+00 - 33 1 1 -3.295975e-16 6.151002e-03 0.000000e+00 - 34 1 1 -3.608225e-16 6.139002e-03 0.000000e+00 - 35 1 1 -2.775558e-16 6.115002e-03 0.000000e+00 - 36 1 1 -1.977585e-16 6.079002e-03 0.000000e+00 - 37 1 1 -2.879641e-16 6.031002e-03 0.000000e+00 - 38 1 1 -1.457168e-16 5.971002e-03 0.000000e+00 - 39 1 1 -1.075529e-16 5.899002e-03 0.000000e+00 - 40 1 1 -5.204170e-17 5.815002e-03 0.000000e+00 - 41 1 1 -2.914335e-16 5.719002e-03 0.000000e+00 - 42 1 1 -2.046974e-16 5.611002e-03 0.000000e+00 - 43 1 1 -4.232725e-16 5.491002e-03 0.000000e+00 - 44 1 1 -1.526557e-16 5.359002e-03 0.000000e+00 - 45 1 1 -2.636780e-16 5.215002e-03 0.000000e+00 - 46 1 1 -1.873501e-16 5.059002e-03 0.000000e+00 - 47 1 1 -3.365364e-16 4.891002e-03 0.000000e+00 - 48 1 1 -2.567391e-16 4.711002e-03 0.000000e+00 - 49 1 1 -1.630640e-16 4.519002e-03 0.000000e+00 - 50 1 1 -7.632783e-17 4.315002e-03 0.000000e+00 - 51 1 1 -1.977585e-16 4.099002e-03 0.000000e+00 - 52 1 1 3.816392e-17 3.871002e-03 0.000000e+00 - 53 1 1 6.938894e-18 3.631001e-03 0.000000e+00 - 54 1 1 2.046974e-16 3.379001e-03 0.000000e+00 - 55 1 1 -2.012279e-16 3.115001e-03 0.000000e+00 - 56 1 1 9.367507e-17 2.839001e-03 0.000000e+00 - 57 1 1 2.428613e-17 2.551001e-03 0.000000e+00 - 58 1 1 7.632783e-17 2.251001e-03 0.000000e+00 - 59 1 1 -4.163336e-17 1.939001e-03 0.000000e+00 - 60 1 1 9.367507e-17 1.615001e-03 0.000000e+00 - 61 1 1 -3.122502e-17 1.279001e-03 0.000000e+00 - 62 1 1 8.326673e-17 9.310004e-04 0.000000e+00 - 63 1 1 9.020562e-17 5.710002e-04 0.000000e+00 - 64 1 1 -2.081668e-17 1.990001e-04 0.000000e+00 diff --git a/docs/tutorial/test1/vel.001-001.meta b/docs/tutorial/test1/vel.001-001.meta deleted file mode 100644 index b5a14cbec..000000000 --- a/docs/tutorial/test1/vel.001-001.meta +++ /dev/null @@ -1,16 +0,0 @@ -Metadata for file set prefix: vel -Data description: Velocity field -Data size per site (bytes): 24 -is_bigendian(): 0 -Number of processors: 4 -Cartesian communicator topology: 4 1 1 -Total system size: 64 1 1 -Lees-Edwards planes: 0 -Lees-Edwards plane speed 0.00000000000000 -Number of I/O groups (files): 1 -I/O communicator topology: 1 1 1 -Write order: - 0 0 0 0 16 1 1 0 0 0 - 1 1 0 0 16 1 1 16 0 0 - 2 2 0 0 16 1 1 32 0 0 - 3 3 0 0 16 1 1 48 0 0 From d69eba7d2a1699a85c4eadb09716044461c5bf7f Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 23 Jan 2023 10:53:33 +0000 Subject: [PATCH 230/244] Remove duplicate test --- tests/regression/d3q19-mpi-short/Makefile | 22 --- .../d3q19-mpi-short/parallel-sqmr-st1.inp | 85 ----------- .../d3q19-mpi-short/parallel-sqmr-st1.log | 136 ------------------ 3 files changed, 243 deletions(-) delete mode 100644 tests/regression/d3q19-mpi-short/Makefile delete mode 100644 tests/regression/d3q19-mpi-short/parallel-sqmr-st1.inp delete mode 100644 tests/regression/d3q19-mpi-short/parallel-sqmr-st1.log diff --git a/tests/regression/d3q19-mpi-short/Makefile b/tests/regression/d3q19-mpi-short/Makefile deleted file mode 100644 index 65c989ac4..000000000 --- a/tests/regression/d3q19-mpi-short/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -############################################################################### -# -# Makefile -# -# D3Q19 short MPI regression tests -# -############################################################################### - -include ../../../Makefile.mk - -MPIRUN_NTASKS=8 - -SER=${LAUNCH_SERIAL_CMD} -PAR=${LAUNCH_MPIRUN_CMD} ${MPIRUN_NTASK_FLAG} ${MPIRUN_NTASKS} ${MPIRUN_EXTRA} - -default: - @echo "TEST --> regression d3q19-mpi-short" - inputs='*inp'; \ - for file in $$inputs; do ../../test.sh $$file "$(SER)" "${PAR}"; done - -clean: - rm -f *new test-diff* *meta diff --git a/tests/regression/d3q19-mpi-short/parallel-sqmr-st1.inp b/tests/regression/d3q19-mpi-short/parallel-sqmr-st1.inp deleted file mode 100644 index c9d7df780..000000000 --- a/tests/regression/d3q19-mpi-short/parallel-sqmr-st1.inp +++ /dev/null @@ -1,85 +0,0 @@ -############################################################################## -# -# Active paritcle (squirmer) smoke test -# -############################################################################## - -N_cycles 50 - -############################################################################## -# -# System and MPI -# -############################################################################## - -size 32_32_32 -grid 2_2_2 -reduced_halo no - -############################################################################## -# -# Fluid parameters -# -############################################################################## - -viscosity 0.1 - -isothermal_fluctuations off -temperature 0.00002133333 - -############################################################################## -# -# Free energy parameters -# -############################################################################### - -free_energy none - -############################################################################### -# -# Colloid parameters -# -############################################################################### - -colloid_init input_one - -colloid_one_type active -colloid_one_a0 7.25 -colloid_one_ah 7.25 -colloid_one_r 32.0_32.0_32.0 -colloid_one_v 0.0_0.0_0.0 -colloid_one_m 1.0_0.0_0.0 -colloid_one_b1 0.05 -colloid_one_b2 0.05 - -# Constant body force on all colloids ("gravity") - -colloid_gravity 0.0_0.0_0.0 - -############################################################################### -# -# Periodic conditions / boundaries -# -############################################################################### - -boundary_walls_on no -periodicity 1_1_1 - -############################################################################### -# -# Output frequency and type -# -############################################################################### - -freq_statistics 50 -config_at_end no - -colloid_io_freq 10000000 - -############################################################################### -# -# Miscellaneous -# -############################################################################### - -random_seed 8361235 diff --git a/tests/regression/d3q19-mpi-short/parallel-sqmr-st1.log b/tests/regression/d3q19-mpi-short/parallel-sqmr-st1.log deleted file mode 100644 index ac46c0767..000000000 --- a/tests/regression/d3q19-mpi-short/parallel-sqmr-st1.log +++ /dev/null @@ -1,136 +0,0 @@ -Welcome to Ludwig v0.7.33 (MPI version running on 8 processes) - -The SVN revision details are: 3210M -Note assertions via standard C assert() are on. - -Read 24 user parameters from pmpi08-sqmr-st1.inp - -No free energy selected - -System details --------------- -System size: 32 32 32 -Decomposition: 2 2 2 -Local domain: 16 16 16 -Periodic: 1 1 1 -Halo nhalo: 1 -Reorder: true -Initialised: 1 - -System properties ----------------- -Mean fluid density: 1.00000e+00 -Shear viscosity 1.00000e-01 -Bulk viscosity 1.00000e-01 -Temperature 2.13333e-05 -External body force density 0.00000e+00 0.00000e+00 0.00000e+00 -External E-field amplitude 0.00000e+00 0.00000e+00 0.00000e+00 -External E-field frequency 0.00000e+00 -External magnetic field 0.00000e+00 0.00000e+00 0.00000e+00 - -Lattice Boltzmann distributions -------------------------------- -Model: d3q19 -SIMD vector len: 1 -Number of sets: 1 -Halo type: full -Input format: binary -Output format: binary -I/O grid: 1 1 1 - -Lattice Boltzmann collision ---------------------------- -Relaxation time scheme: M10 -Hydrodynamic modes: on -Ghost modes: on -Isothermal fluctuations: off -Shear relaxation time: 8.00000e-01 -Bulk relaxation time: 8.00000e-01 -Ghost relaxation time: 1.00000e+00 -[User ] Random number seed: 8361235 - -Hydrodynamics -------------- -Hydrodynamics: on - -Colloid information -------------------- - -Colloid I/O settings --------------------- -Decomposition: 1 1 1 -Number of files: 1 -Input format: ascii -Output format: ascii -Single file read flag: 0 - -Requested one colloid via input: -colloid_one active -colloid_one_a0 7.2500000e+00 -colloid_one_ah 7.2500000e+00 -colloid_one_r 3.2000000e+01 3.2000000e+01 3.2000000e+01 -colloid_one_v 0.0000000e+00 0.0000000e+00 0.0000000e+00 -colloid_one_m 1.0000000e+00 0.0000000e+00 0.0000000e+00 -colloid_one_b1 5.0000000e-02 -colloid_one_b2 5.0000000e-02 - -Initialised 1 colloid - -Colloid cell list information ------------------------------ -Input radius maximum: 7.2500000e+00 -Final cell list: 2 2 2 -Final cell lengths: 8.0000000e+00 8.0000000e+00 8.0000000e+00 - -Initial conditions. - -Scalars - total mean variance min max -[rho] 31193.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] 4.3288984e-13 0.0000000e+00 0.0000000e+00 -[fluid ] 4.3288984e-13 0.0000000e+00 0.0000000e+00 -[colloids] 0.0000000e+00 0.0000000e+00 0.0000000e+00 - -Starting time step loop. - -Particle statistics: - -Colloid velocities - x y z -[minimum ] 2.1404693e-02 8.3210720e-19 1.1207995e-17 -[maximum ] 2.1404693e-02 8.3210720e-19 1.1207995e-17 - -Scalars - total mean variance min max -[rho] 31173.00 1.00000000000 5.0838962e-07 0.99225554160 1.00513736852 - -Momentum - x y z -[total ] 9.0238927e-13 -7.2951583e-14 4.4450019e-14 -[fluid ] -3.4373444e+01 -6.8389738e-14 1.9095836e-14 -[colloids] 3.4373444e+01 -4.5618443e-15 2.5354183e-14 - -Velocity - x y z -[minimum ] -3.4235679e-02 -3.4140000e-02 -3.4140000e-02 -[maximum ] 2.1216647e-02 3.4140000e-02 3.4140000e-02 - -Completed cycle 50 - -Timer resolution: 1e-06 second - -Timer statistics - Section: tmin tmax total - Total: 0.766 0.766 0.766 0.766271 (1 call) - Time step loop: 0.013 0.019 0.750 0.014992 (50 calls) - Propagation: 0.001 0.004 0.116 0.002322 (50 calls) - Propagtn (krnl) : 0.001 0.004 0.114 0.002288 (50 calls) - Collision: 0.001 0.007 0.216 0.004330 (50 calls) - Collision (krnl) : 0.001 0.007 0.216 0.004313 (50 calls) - Lattice halos: 0.001 0.008 0.261 0.002613 (100 calls) - phi gradients: 0.000 0.000 0.000 0.000000 (50 calls) - Forces: 0.000 0.006 0.002 0.000038 (50 calls) - Rebuild: 0.000 0.006 0.041 0.000817 (50 calls) - BBL: 0.001 0.004 0.064 0.001287 (50 calls) - Particle halos: 0.000 0.003 0.026 0.000528 (50 calls) - Force calculation: 0.000 0.001 0.000 0.000002 (50 calls) - phi update: 0.000 0.000 0.000 0.000000 (50 calls) - Free1: 0.000 0.005 0.004 0.000029 (150 calls) -Ludwig finished normally. From d75034b34e6d84f7ba4bd78fa7784ff8874070df Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 23 Jan 2023 16:14:30 +0000 Subject: [PATCH 231/244] Use util_fopen.h --- util/capillary.c | 5 +++-- util/multi_poly_init.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/util/capillary.c b/util/capillary.c index 59e17c947..a5954c0bf 100644 --- a/util/capillary.c +++ b/util/capillary.c @@ -14,7 +14,7 @@ * Edinburgh Soft Matter and Statistcal Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2008-2022 The University of Edinburgh + * (c) 2008-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -29,6 +29,7 @@ #include "map_init.h" #include "symmetric.h" #include "fe_ternary.h" +#include "util_fopen.h" /* SYSTEM SIZE */ /* Set the system size as desired. Clearly, this must match the system @@ -545,7 +546,7 @@ int capillary_write_ascii_serial(pe_t * pe, cs_t * cs, map_t * map) { cs_nlocal(cs, nlocal); - fp = fopen(filename, "w"); + fp = util_fopen(filename, "w"); if (fp == NULL) return -1; /* Header comment */ diff --git a/util/multi_poly_init.c b/util/multi_poly_init.c index bf4d626a6..a4200ec57 100644 --- a/util/multi_poly_init.c +++ b/util/multi_poly_init.c @@ -28,7 +28,7 @@ * Contributing authors * Kai Qi (kai.qi@epfl.ch) * Kevin Stratford (kevin@epcc.ed.ac.uk) - * (c) 2012-2021 The University of Edinburgh + * (c) 2012-2023 The University of Edinburgh * (c) 2020- Swiss Federal Institute of Technology Lausanne * *****************************************************************************/ @@ -42,6 +42,7 @@ #include "../src/pe.h" #include "../src/coords.h" #include "../src/util.h" +#include "../src/util_fopen.h" enum format {ASCII, BINARY}; @@ -330,7 +331,7 @@ void colloid_init_write_file(const int nc, const colloid_state_t * pc, const char * filename = "config.cds.init.001-001"; FILE * fp; - fp = fopen(filename, "w"); + fp = util_fopen(filename, "w"); if (fp == NULL) { printf("Could not open %s\n", filename); exit(0); From 978e068d6e3abda58c62fe01c5032b9ef0729d3a Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 23 Jan 2023 16:15:02 +0000 Subject: [PATCH 232/244] Replace assertion failure by controlled exit --- util/extract.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/util/extract.c b/util/extract.c index 8d35e9b22..f13763dec 100644 --- a/util/extract.c +++ b/util/extract.c @@ -746,7 +746,8 @@ int read_data_file_name(const char * filename) { int ns = sscanf(tmp+1, "%d.", &ntime); if (ns < 1) { printf("Could not determine time from %s\n", filename); - assert(0); + printf("Please check the file and try again!\n"); + exit(-1); } else { return ntime; @@ -1470,7 +1471,7 @@ int lc_compute_scalar_ops(double q[3][3], double qs[5]) { q2 = s*s + t*t + (s + t)*(s + t); q3 = 3.0*s*t*(s + t); - /* Note the value here can drip just below zero by about DBL_EPSILON */ + /* Note the value here can dip just below zero by about DBL_EPSILON */ /* So just set to zero to prevent an NaN */ q4 = 1.0 - 6.0*q3*q3 / (q2*q2*q2); if (q4 < 0.0) q4 = 0.0; From e2cfb47555a48aa21e6584017c258e03c1e4df89 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 23 Jan 2023 16:36:34 +0000 Subject: [PATCH 233/244] Attempt again to fix return value alerts --- util/extract.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/util/extract.c b/util/extract.c index f13763dec..a5a51d135 100644 --- a/util/extract.c +++ b/util/extract.c @@ -1611,19 +1611,18 @@ int extract_read_single_file(io_metadata_t * meta, const char * stub, int indexd = site_index(icd, jcd, kcd, meta->cs->param->ntotal); for (int nr = 0; nr < nrecord; nr++) { - double datum = 0.0; - int nread = 0; + /* Place at correct offset in the full array */ if (meta->options.iorformat == IO_RECORD_BINARY) { - nread = fread(&datum, sizeof(double), 1, fp); - if (nread != 1) goto err; + double datum = 0.0; + int nread = fread(&datum, sizeof(double), 1, fp); + if (nread == 1) *(datatotal + nrecord*indexd + nr) = datum; } else { - nread = fscanf(fp, "%le", &datum); - if (nread != 1) goto err; + double datum = 0.0; + int nread = fscanf(fp, "%le", &datum); + if (nread == 1) *(datatotal + nrecord*indexd + nr) = datum; } - /* Place at correct offset in the full array */ - *(datatotal + nrecord*indexd + nr) = datum; } } @@ -1632,11 +1631,6 @@ int extract_read_single_file(io_metadata_t * meta, const char * stub, fclose(fp); return 0; - - err: - - if (fp) fclose(fp); - return -1; } /***************************************************************************** From 018d99de648af59e70069002a987b3e83478715f Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 23 Jan 2023 18:00:48 +0000 Subject: [PATCH 234/244] Add LC tutorial --- .../liquid-crystal/blue-phase-visual.inp | 88 +++++ docs/tutorial/liquid-crystal/stdout | 367 ++++++++++++++++++ 2 files changed, 455 insertions(+) create mode 100644 docs/tutorial/liquid-crystal/blue-phase-visual.inp create mode 100644 docs/tutorial/liquid-crystal/stdout diff --git a/docs/tutorial/liquid-crystal/blue-phase-visual.inp b/docs/tutorial/liquid-crystal/blue-phase-visual.inp new file mode 100644 index 000000000..06a34d2a3 --- /dev/null +++ b/docs/tutorial/liquid-crystal/blue-phase-visual.inp @@ -0,0 +1,88 @@ +############################################################################## +# +# BPI from initial conditions. +# +############################################################################## + +N_cycles 10000 + +############################################################################## +# +# System and MPI +# +############################################################################## + +size 16_16_16 + +############################################################################## +# +# Fluid parameters +# +############################################################################## + +viscosity 1.666666666666 + +############################################################################## +# +# Free energy parameters +# +############################################################################### + +free_energy lc_blue_phase +fd_gradient_calculation 3d_7pt_fluid + +############################################################################### +# +# Blue Phase free energy +# +############################################################################### + +lc_a0 0.014384711 +lc_gamma 3.1764706 +lc_q0 0.27768018 +lc_kappa0 0.01 +lc_kappa1 0.01 + +lc_xi 0.7 +lc_Gamma 0.1 + +lc_q_initialisation o8m +lc_q_init_amplitude -0.2 +lc_init_redshift 0.83 + +############################################################################### +# +# Colloid parameters +# +############################################################################### + +colloid_init no_colloids + +############################################################################### +# +# Periodic conditions / boundaries +# +############################################################################### + +periodicity 1_1_1 + +############################################################################### +# +# Output frequency and type +# +############################################################################### + +default_io_mode mpiio + +freq_statistics 1000 +config_at_end yes + +############################################################################### +# +# Miscellaneous +# +# random_seed +ve integer is the random number generator seed +# +############################################################################### + +random_seed 8361235 diff --git a/docs/tutorial/liquid-crystal/stdout b/docs/tutorial/liquid-crystal/stdout new file mode 100644 index 000000000..07073743d --- /dev/null +++ b/docs/tutorial/liquid-crystal/stdout @@ -0,0 +1,367 @@ +Welcome to: Ludwig v0.19.0 (Serial version running on 1 process) +Git commit: 728176d0efb74c2522b413a82024a5b1701465d8 + +Start time: Mon Jan 23 15:12:45 2023 + +Compiler: + name: Gnu 12.2.0 + version-string: 12.2.0 + options: -O2 -g -fopenmp -Wall + +Note assertions via standard C assert() are on. + +Target thread model: OpenMP. +OpenMP threads: 4; maximum number of threads: 8. + +Read 21 user parameters from blue-phase-visual.inp + +System details +-------------- +System size: 16 16 16 +Decomposition: 1 1 1 +Local domain: 16 16 16 +Periodic: 1 1 1 +Halo nhalo: 2 +Reorder: true +Initialised: 1 + +Free energy details +------------------- + +Blue phase free energy selected. + +Liquid crystal blue phase free energy +Bulk parameter A0: = 1.4384711e-02 +Magnitude of order gamma = 3.1764706e+00 +Pitch wavevector q0 = 2.7768018e-01 +... gives pitch length = 2.2627417e+01 +Elastic constant kappa0 = 1.0000000e-02 +Elastic constant kappa1 = 1.0000000e-02 +Amplitude (uniaxial) order = -2.0000000e-01 +Effective aspect ratio xi = 7.0000000e-01 +Chirality = 1.3500000e+00 +Reduced temperature = -5.0000003e-01 +Initial redshift = 8.3000000e-01 +Dynamic redshift update = no +Liquid crystal activity No + +Using Beris-Edwards solver: +Rotational diffusion const = 1.0000000e-01 +LC fluctuations: = off + +System properties +---------------- +Mean fluid density: 1.00000e+00 +Shear viscosity 1.66667e+00 +Bulk viscosity 1.66667e+00 +Temperature 0.00000e+00 +External body force density 0.00000e+00 0.00000e+00 0.00000e+00 +External E-field amplitude 0.00000e+00 0.00000e+00 0.00000e+00 +External E-field frequency 0.00000e+00 +External magnetic field 0.00000e+00 0.00000e+00 0.00000e+00 + +Lattice Boltzmann distributions +------------------------------- +Model: d3q19 +SIMD vector len: 1 +Number of sets: 1 +Halo type: lb_halo_target (full halo) +Input format: binary +Output format: binary +I/O grid: 1 1 1 + +Lattice Boltzmann collision +--------------------------- +Relaxation time scheme: M10 +Hydrodynamic modes: on +Ghost modes: on +Isothermal fluctuations: off +Shear relaxation time: 5.50000e+00 +Bulk relaxation time: 5.50000e+00 +Ghost relaxation time: 1.00000e+00 +[User ] Random number seed: 8361235 + +Hydrodynamics +------------- +Hydrodynamics: on + +Order parameter I/O +------------------- +Order parameter I/O format: +I/O decomposition: 1 1 1 + +Advection scheme order: 1 (default) +Gradient calculation: 3d_7pt_fluid + + +Initialising Q_ab using O8M (BPI) +Initial conditions. + +Scalars - total mean variance min max +[rho] 4096.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 +[Qxx] 2.6766783e-15 6.5348592e-19 6.0000000e-02 -4.4966058e-01 4.4966058e-01 +[Qxy] -1.7414342e-13 -4.2515484e-17 5.0000000e-02 -6.0000000e-01 6.0000000e-01 +[Qxz] -1.7414342e-13 -4.2515484e-17 5.0000000e-02 -6.0000000e-01 6.0000000e-01 +[Qyy] 2.6766783e-15 6.5348592e-19 6.0000000e-02 -4.4966058e-01 4.4966058e-01 +[Qyz] -1.7414342e-13 -4.2515484e-17 5.0000000e-02 -6.0000000e-01 6.0000000e-01 + +Momentum - x y z +[total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 + +Starting time step loop. + +Scalars - total mean variance min max +[rho] 4096.00 1.00000000000 1.1873015e-07 0.99954734972 1.00092184763 +[Qxx] 4.9988623e-14 1.2204254e-17 2.3690483e-02 -1.9415614e-01 3.7077120e-01 +[Qxy] 1.2498655e-06 3.0514294e-10 1.2311460e-02 -2.5292886e-01 2.5292886e-01 +[Qxz] 1.2498655e-06 3.0514294e-10 1.2311460e-02 -2.5292886e-01 2.5292886e-01 +[Qyy] 4.2478154e-14 1.0370643e-17 2.3690483e-02 -1.9415614e-01 3.7077120e-01 +[Qyz] 1.2498655e-06 3.0514295e-10 1.2311460e-02 -2.5292886e-01 2.5292886e-01 + +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 1000 -3.9774699091e-01 4.0960000000e+03 -9.7106198953e-05 -1.1691716227e-04 1.9810963320e-05 8.3000000000e-01 + +Momentum - x y z +[total ] 7.5484757e-14 1.9921564e-14 8.6632090e-15 +[fluid ] 7.5484757e-14 1.9921564e-14 8.6632090e-15 + +Velocity - x y z +[minimum ] -1.4701045e-04 -1.4701045e-04 -1.4701045e-04 +[maximum ] 1.4701046e-04 1.4701046e-04 1.4701046e-04 + +Completed cycle 1000 + +Scalars - total mean variance min max +[rho] 4096.00 1.00000000000 4.6553718e-08 0.99977184250 1.00086831792 +[Qxx] 6.6287527e-14 1.6183478e-17 2.2471881e-02 -1.8275815e-01 3.6497222e-01 +[Qxy] 1.1806448e-06 2.8824335e-10 1.3246483e-02 -2.5408531e-01 2.5408531e-01 +[Qxz] 1.1806447e-06 2.8824335e-10 1.3246483e-02 -2.5408531e-01 2.5408531e-01 +[Qyy] 4.9220775e-14 1.2016791e-17 2.2471881e-02 -1.8275815e-01 3.6497222e-01 +[Qyz] 1.1806448e-06 2.8824336e-10 1.3246483e-02 -2.5408531e-01 2.5408531e-01 + +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 2000 -5.5568105238e-01 4.0960000000e+03 -1.3566431943e-04 -1.4963554775e-04 1.3971228321e-05 8.3000000000e-01 + +Momentum - x y z +[total ] 1.1259743e-13 6.9371592e-14 -1.2420620e-14 +[fluid ] 1.1259743e-13 6.9371592e-14 -1.2420620e-14 + +Velocity - x y z +[minimum ] -3.4079499e-05 -3.4079499e-05 -3.4079499e-05 +[maximum ] 3.4079499e-05 3.4079499e-05 3.4079499e-05 + +Completed cycle 2000 + +Scalars - total mean variance min max +[rho] 4096.00 1.00000000000 1.1891808e-08 0.99987627458 1.00045877831 +[Qxx] 7.5990384e-14 1.8552340e-17 2.2528101e-02 -1.8670958e-01 3.6638554e-01 +[Qxy] 9.7292797e-07 2.3753124e-10 1.4500379e-02 -2.5425133e-01 2.5425133e-01 +[Qxz] 9.7292794e-07 2.3753124e-10 1.4500379e-02 -2.5425133e-01 2.5425133e-01 +[Qyy] 5.0604682e-14 1.2354659e-17 2.2528101e-02 -1.8670958e-01 3.6638554e-01 +[Qyz] 9.7292796e-07 2.3753124e-10 1.4500379e-02 -2.5425133e-01 2.5425133e-01 + +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 3000 -5.8631199901e-01 4.0960000000e+03 -1.4314257788e-04 -1.5652472821e-04 1.3382150323e-05 8.3000000000e-01 + +Momentum - x y z +[total ] 7.8544810e-14 6.6554401e-14 2.0507901e-14 +[fluid ] 7.8544810e-14 6.6554401e-14 2.0507901e-14 + +Velocity - x y z +[minimum ] -1.0128932e-05 -1.0128932e-05 -1.0128932e-05 +[maximum ] 1.0128933e-05 1.0128933e-05 1.0128933e-05 + +Completed cycle 3000 + +Scalars - total mean variance min max +[rho] 4096.00 1.00000000000 3.7638450e-09 0.99992102854 1.00025282973 +[Qxx] 5.0440776e-14 1.2314643e-17 2.2646312e-02 -1.8901570e-01 3.6805468e-01 +[Qxy] 7.7177158e-07 1.8842080e-10 1.5237839e-02 -2.5491204e-01 2.5491204e-01 +[Qxz] 7.7177156e-07 1.8842079e-10 1.5237839e-02 -2.5491204e-01 2.5491204e-01 +[Qyy] 7.5404561e-14 1.8409317e-17 2.2646312e-02 -1.8901570e-01 3.6805468e-01 +[Qyz] 7.7177158e-07 1.8842080e-10 1.5237839e-02 -2.5491204e-01 2.5491204e-01 + +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 4000 -5.9533844104e-01 4.0960000000e+03 -1.4534629908e-04 -1.5889815334e-04 1.3551854253e-05 8.3000000000e-01 + +Momentum - x y z +[total ] 5.4789506e-14 2.2586100e-14 4.8166332e-14 +[fluid ] 5.4789506e-14 2.2586100e-14 4.8166332e-14 + +Velocity - x y z +[minimum ] -4.1750499e-06 -4.1750499e-06 -4.1750499e-06 +[maximum ] 4.1750496e-06 4.1750496e-06 4.1750496e-06 + +Completed cycle 4000 + +Scalars - total mean variance min max +[rho] 4096.00 1.00000000000 2.1746640e-09 0.99993890489 1.00017368530 +[Qxx] 8.5920002e-14 2.0976563e-17 2.2706267e-02 -1.9007091e-01 3.6907450e-01 +[Qxy] 6.0578950e-07 1.4789783e-10 1.5612592e-02 -2.5564114e-01 2.5564114e-01 +[Qxz] 6.0578949e-07 1.4789783e-10 1.5612592e-02 -2.5564114e-01 2.5564114e-01 +[Qyy] 7.5670906e-14 1.8474342e-17 2.2706267e-02 -1.9007091e-01 3.6907450e-01 +[Qyz] 6.0578952e-07 1.4789783e-10 1.5612592e-02 -2.5564114e-01 2.5564114e-01 + +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 5000 -5.9863397160e-01 4.0960000000e+03 -1.4615087197e-04 -1.5983992669e-04 1.3689054714e-05 8.3000000000e-01 + +Momentum - x y z +[total ] 3.7858605e-14 1.6511098e-14 7.5797008e-14 +[fluid ] 3.7858605e-14 1.6511098e-14 7.5797008e-14 + +Velocity - x y z +[minimum ] -2.9906821e-06 -2.9906821e-06 -2.9906821e-06 +[maximum ] 2.9906821e-06 2.9906821e-06 2.9906821e-06 + +Completed cycle 5000 + +Scalars - total mean variance min max +[rho] 4096.00 1.00000000000 1.8537234e-09 0.99994649881 1.00014830023 +[Qxx] 9.2737627e-14 2.2641022e-17 2.2720266e-02 -1.9054998e-01 3.6958539e-01 +[Qxy] 4.7460893e-07 1.1587132e-10 1.5798741e-02 -2.5622743e-01 2.5622743e-01 +[Qxz] 4.7460892e-07 1.1587132e-10 1.5798741e-02 -2.5622743e-01 2.5622743e-01 +[Qyy] 6.4818037e-14 1.5824716e-17 2.2720266e-02 -1.9054998e-01 3.6958539e-01 +[Qyz] 4.7460895e-07 1.1587132e-10 1.5798741e-02 -2.5622743e-01 2.5622743e-01 + +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 6000 -6.0007050180e-01 4.0960000000e+03 -1.4650158735e-04 -1.6024692701e-04 1.3745339659e-05 8.3000000000e-01 + +Momentum - x y z +[total ] 4.6358750e-14 1.1338153e-14 5.3148458e-14 +[fluid ] 4.6358750e-14 1.1338153e-14 5.3148458e-14 + +Velocity - x y z +[minimum ] -2.6098404e-06 -2.6098404e-06 -2.6098404e-06 +[maximum ] 2.6098402e-06 2.6098402e-06 2.6098402e-06 + +Completed cycle 6000 + +Scalars - total mean variance min max +[rho] 4096.00 1.00000000000 1.7837614e-09 0.99995000295 1.00015513473 +[Qxx] 9.5390672e-14 2.3288738e-17 2.2710872e-02 -1.9076358e-01 3.6980903e-01 +[Qxy] 3.7209849e-07 9.0844357e-11 1.5894003e-02 -2.5664144e-01 2.5664144e-01 +[Qxz] 3.7209849e-07 9.0844358e-11 1.5894003e-02 -2.5664144e-01 2.5664144e-01 +[Qyy] 6.6819393e-14 1.6313328e-17 2.2710872e-02 -1.9076358e-01 3.6980903e-01 +[Qyz] 3.7209854e-07 9.0844369e-11 1.5894003e-02 -2.5664144e-01 2.5664144e-01 + +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 7000 -6.0077482798e-01 4.0960000000e+03 -1.4667354199e-04 -1.6043136217e-04 1.3757820188e-05 8.3000000000e-01 + +Momentum - x y z +[total ] 4.6736920e-14 4.3451354e-14 3.4430792e-14 +[fluid ] 4.6736920e-14 4.3451354e-14 3.4430792e-14 + +Velocity - x y z +[minimum ] -2.5050081e-06 -2.5050081e-06 -2.5050081e-06 +[maximum ] 2.5050087e-06 2.5050087e-06 2.5050087e-06 + +Completed cycle 7000 + +Scalars - total mean variance min max +[rho] 4096.00 1.00000000000 1.7696231e-09 0.99995178283 1.00015893040 +[Qxx] 1.3872854e-13 3.3869274e-17 2.2692791e-02 -1.9085658e-01 3.6988921e-01 +[Qxy] 2.9206192e-07 7.1304179e-11 1.5945498e-02 -2.5691889e-01 2.5691889e-01 +[Qxz] 2.9206191e-07 7.1304176e-11 1.5945498e-02 -2.5691889e-01 2.5691889e-01 +[Qyy] 8.0783256e-14 1.9722475e-17 2.2692791e-02 -1.9085658e-01 3.6988921e-01 +[Qyz] 2.9206194e-07 7.1304185e-11 1.5945498e-02 -2.5691889e-01 2.5691889e-01 + +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 8000 -6.0114751217e-01 4.0960000000e+03 -1.4676452934e-04 -1.6051724498e-04 1.3752715643e-05 8.3000000000e-01 + +Momentum - x y z +[total ] 9.2814645e-14 1.7565810e-14 5.3797244e-14 +[fluid ] 9.2814645e-14 1.7565810e-14 5.3797244e-14 + +Velocity - x y z +[minimum ] -2.3978661e-06 -2.3978661e-06 -2.3978661e-06 +[maximum ] 2.3978668e-06 2.3978668e-06 2.3978668e-06 + +Completed cycle 8000 + +Scalars - total mean variance min max +[rho] 4096.00 1.00000000000 1.7698110e-09 0.99995276741 1.00016129105 +[Qxx] 1.0697378e-13 2.6116645e-17 2.2673503e-02 -1.9089555e-01 3.6990432e-01 +[Qxy] 2.2940256e-07 5.6006485e-11 1.5975166e-02 -2.5710243e-01 2.5710243e-01 +[Qxz] 2.2940255e-07 5.6006482e-11 1.5975166e-02 -2.5710243e-01 2.5710243e-01 +[Qyy] 9.6797828e-14 2.3632282e-17 2.2673503e-02 -1.9089555e-01 3.6990432e-01 +[Qyz] 2.2940257e-07 5.6006487e-11 1.5975166e-02 -2.5710243e-01 2.5710243e-01 + +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 9000 -6.0135629004e-01 4.0960000000e+03 -1.4681550050e-04 -1.6055792150e-04 1.3742420996e-05 8.3000000000e-01 + +Momentum - x y z +[total ] 1.1061291e-13 1.5033114e-14 6.4958455e-14 +[fluid ] 1.1061291e-13 1.5033114e-14 6.4958455e-14 + +Velocity - x y z +[minimum ] -2.2976943e-06 -2.2976943e-06 -2.2976943e-06 +[maximum ] 2.2976948e-06 2.2976948e-06 2.2976948e-06 + +Completed cycle 9000 + +Scalars - total mean variance min max +[rho] 4096.00 1.00000000000 1.7738728e-09 0.99995335884 1.00016289211 +[Qxx] 9.9741040e-14 2.4350840e-17 2.2656195e-02 -1.9091069e-01 3.6989322e-01 +[Qxy] 1.8020178e-07 4.3994576e-11 1.5993350e-02 -2.5722498e-01 2.5722498e-01 +[Qxz] 1.8020177e-07 4.3994572e-11 1.5993350e-02 -2.5722498e-01 2.5722498e-01 +[Qyy] 1.1015051e-13 2.6892215e-17 2.2656195e-02 -1.9091069e-01 3.6989322e-01 +[Qyz] 1.8020180e-07 4.3994581e-11 1.5993350e-02 -2.5722498e-01 2.5722498e-01 + +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 10000 -6.0147920369e-01 4.0960000000e+03 -1.4684550871e-04 -1.6057746167e-04 1.3731952956e-05 8.3000000000e-01 + +Momentum - x y z +[total ] 9.5225910e-14 1.6577018e-14 3.3185260e-14 +[fluid ] 9.5225910e-14 1.6577018e-14 3.3185260e-14 + +Velocity - x y z +[minimum ] -2.2137265e-06 -2.2137265e-06 -2.2137265e-06 +[maximum ] 2.2137269e-06 2.2137269e-06 2.2137269e-06 + +Completed cycle 10000 +MPIIO wrote to dist-000010000.001-001 +- dist aggregated 0.623 MB in 0.000 seconds +- dist wrote 0.623 MB in 0.006 seconds +- dist rate 0.105 GB per second +Writing q file at step 10000! +MPIIO wrote to q-000010000.001-001 +- q aggregated 0.164 MB in 0.000 seconds +- q wrote 0.164 MB in 0.004 seconds +- q rate 0.040 GB per second +Writing rho/velocity output at step 10000! +MPIIO wrote to rho-000010000.001-001 +- rho aggregated 0.033 MB in 0.000 seconds +- rho wrote 0.033 MB in 0.004 seconds +- rho rate 0.008 GB per second +MPIIO wrote to vel-000010000.001-001 +- vel aggregated 0.098 MB in 0.000 seconds +- vel wrote 0.098 MB in 0.004 seconds +- vel rate 0.023 GB per second + +Timer resolution: 1e-06 second + +Timer statistics + Section: tmin tmax total + Total: 75.512 75.512 75.512 75.512480 (1 call) + Time step loop: 0.006 0.029 75.372 0.007537 (10000 calls) + Propagation: 0.000 0.003 4.998 0.000500 (10000 calls) + Propagtn (krnl) : 0.000 0.003 4.952 0.000495 (10000 calls) + Collision: 0.001 0.005 11.115 0.001111 (10000 calls) + Collision (krnl) : 0.001 0.005 11.034 0.001103 (10000 calls) + Lattice halos: 0.000 0.003 5.350 0.000535 (10000 calls) + phi gradients: 0.001 0.005 10.632 0.001063 (10000 calls) + phi grad (krnl) : 0.001 0.004 7.090 0.000709 (10000 calls) + phi halos: 0.000 0.002 3.486 0.000349 (10000 calls) + BBL: 0.000 0.000 0.008 0.000001 (10000 calls) + Force calculation: 0.002 0.009 19.590 0.001959 (10000 calls) + Phi force (krnl) : 0.001 0.005 8.005 0.000800 (10000 calls) + phi update: 0.002 0.010 22.341 0.002234 (10000 calls) + Velocity Halo : 0.000 0.002 2.482 0.000248 (10000 calls) +BE mol field (krnl) : 0.000 0.003 4.929 0.000493 (10000 calls) +BP BE update (krnl) : 0.001 0.004 8.286 0.000829 (10000 calls) + Advectn (krnl) : 0.000 0.002 3.901 0.000390 (10000 calls) + Advectn BCS (krnl) : 0.000 0.001 1.859 0.000186 (10000 calls) + Free1: 0.000 0.021 0.103 0.000010 (10000 calls) +End time: Mon Jan 23 15:14:01 2023 +Ludwig finished normally. From 6217bd55044e9a3811284cbb52399219ebaf4b78 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 23 Jan 2023 18:01:15 +0000 Subject: [PATCH 235/244] Prevent repeated config at end --- src/control.c | 13 ++++- src/ludwig.c | 53 ++----------------- .../d3q19-short/serial-rest-c01.log | 10 ++-- 3 files changed, 20 insertions(+), 56 deletions(-) diff --git a/src/control.c b/src/control.c index 6b8870629..a43654045 100644 --- a/src/control.c +++ b/src/control.c @@ -8,7 +8,7 @@ * end Edinburgh Parallel Computing Centre * * Kevin Stratford (kevin@epcc.ed.ac.uk) - * (c) 2008-2019 The University of Edinburgh + * (c) 2008-2023 The University of Edinburgh * *****************************************************************************/ @@ -34,6 +34,7 @@ static int freq_shear_meas = 100000000; static int freq_colloid_io = 100000000; static int rho_nfreq = 100000000; static int config_at_end = 1; +static int nsteps_ = -1; /***************************************************************************** * @@ -90,6 +91,9 @@ int init_control(pe_t * pe, rt_t * rt) { if (freq_shear_io < 1) freq_shear_io = t_start + t_steps + 1; if (freq_shear_meas < 1) freq_shear_meas = t_start + t_steps + 1; + /* This is a record of the last time step for "config_at_end" */ + nsteps_ = t_start + t_steps; + return 0; } @@ -112,9 +116,14 @@ int is_measurement_step() { } int is_config_step() { + int t = -1; + int isconfigatendstep = 0; physics_t * phys = NULL; physics_ref(&phys); - return ((physics_control_timestep(phys) % freq_config) == 0); + t = physics_control_timestep(phys); + isconfigatendstep = (config_at_end && (t == nsteps_)); + + return ((t % freq_config) == 0 || isconfigatendstep); } int is_colloid_io_step() { diff --git a/src/ludwig.c b/src/ludwig.c index 9e024be2c..7c1b4d22d 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -941,7 +941,7 @@ void ludwig_run(const char * inputfile) { } if (ludwig->psi) { - if (is_psi_output_step()) { + if (is_psi_output_step() || is_config_step()) { psi_io_info(ludwig->psi, &iohandler); pe_info(ludwig->pe, "Writing psi file at step %d!\n", step); sprintf(filename,"%spsi-%8.8d", subdirectory, step); @@ -1063,56 +1063,9 @@ void ludwig_run(const char * inputfile) { /* Next time step */ } - /* To prevent any conflict between the last regular dump, and - * a final dump, there's a barrier here. */ - MPI_Barrier(comm); - - /* Dump the final configuration if required. */ - - if (is_config_at_end()) { - { - io_event_t event = {0}; - lb_io_write(ludwig->lb, step, &event); - } - - sprintf(filename, "%s%s%8.8d", subdirectory, "config.cds", step); - - if (ncolloid > 0) colloid_io_write(ludwig->cio, filename); - - if (ludwig->phi) { - io_event_t event = {0}; - pe_info(ludwig->pe, "Writing phi file at step %d!\n", step); - field_io_write(ludwig->phi, step, &event); - } - - if (ludwig->p) { - io_event_t event = {0}; - pe_info(ludwig->pe, "Writing p file at step %d!\n", step); - field_io_write(ludwig->p, step, &event); - } - - if (ludwig->q) { - io_event_t event = {0}; - pe_info(ludwig->pe, "Writing q file at step %d!\n", step); - io_replace_field_values(ludwig->q, ludwig->map, MAP_COLLOID, 0.0); - field_io_write(ludwig->q, step, &event); - } - - /* Only strictly required if have order parameter dynamics */ - if (ludwig->hydro) { - io_event_t event = {0}; - pe_info(ludwig->pe, "Writing rho/velocity output at step %d!\n", step); - hydro_io_write(ludwig->hydro, step, &event); - } - - if (ludwig->psi) { - psi_io_info(ludwig->psi, &iohandler); - pe_info(ludwig->pe, "Writing psi file at step %d!\n", step); - sprintf(filename,"%spsi-%8.8d", subdirectory, step); - io_write_data(iohandler, filename, ludwig->psi); - } - } + /* End of time step loop. A barrier, before closing down. */ + MPI_Barrier(comm); /* Shut down cleanly. Give the timer statistics. Finalise PE. */ #ifdef PETSC diff --git a/tests/regression/d3q19-short/serial-rest-c01.log b/tests/regression/d3q19-short/serial-rest-c01.log index 1f9693313..00e63193c 100644 --- a/tests/regression/d3q19-short/serial-rest-c01.log +++ b/tests/regression/d3q19-short/serial-rest-c01.log @@ -100,6 +100,12 @@ Particle statistics: Colloid velocities - x y z [minimum ] 4.3620293e-03 3.0155764e-03 3.0155764e-03 [maximum ] 4.3620293e-03 3.0155764e-03 3.0155764e-03 +Writing distribution output at step 20! +Writing colloid output at step 20! + +colloid_io_write: +writing colloid information to config.cds00000020.001-001 etc +Writing rho/velocity output at step 20! Scalars - total mean variance min max [rho] 262094.00 1.00000000000 2.6522509e-08 0.99854657671 1.00149980641 @@ -115,10 +121,6 @@ Velocity - x y z Completed cycle 20 -colloid_io_write: -writing colloid information to config.cds00000020.001-001 etc -Writing rho/velocity output at step 20! - Timer resolution: 1e-06 second Timer statistics From 5761f9a9e0d496696b411a1b705125067496c395 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 24 Jan 2023 16:04:39 +0000 Subject: [PATCH 236/244] Move tutorial material to html --- docs/tutorial/Makefile | 25 - docs/tutorial/colloid-test1/colloid-test1.inp | 100 ++++ docs/tutorial/tutorial.tex | 514 ------------------ docs/tutorial/tutorialFigs/arrows.png | Bin 21915 -> 0 bytes docs/tutorial/tutorialFigs/cog.png | Bin 27337 -> 0 bytes docs/tutorial/tutorialFigs/colloidCoords.png | Bin 51880 -> 0 bytes docs/tutorial/tutorialFigs/colloidInit.png | Bin 34009 -> 0 bytes docs/tutorial/tutorialFigs/colloidSystem.png | Bin 50681 -> 0 bytes .../tutorialFigs/colloidsPipeline.png | Bin 53167 -> 0 bytes docs/tutorial/tutorialFigs/contour.png | Bin 71029 -> 0 bytes docs/tutorial/tutorialFigs/glyph.png | Bin 26718 -> 0 bytes docs/tutorial/tutorialFigs/pipeline.png | Bin 30832 -> 0 bytes docs/tutorial/tutorialFigs/system.png | Bin 136363 -> 0 bytes docs/tutorial/tutorialFigs/thresh.png | Bin 27423 -> 0 bytes docs/tutorial/tutorialFigs/velProf.png | Bin 5312 -> 0 bytes 15 files changed, 100 insertions(+), 539 deletions(-) delete mode 100644 docs/tutorial/Makefile create mode 100644 docs/tutorial/colloid-test1/colloid-test1.inp delete mode 100644 docs/tutorial/tutorial.tex delete mode 100644 docs/tutorial/tutorialFigs/arrows.png delete mode 100644 docs/tutorial/tutorialFigs/cog.png delete mode 100644 docs/tutorial/tutorialFigs/colloidCoords.png delete mode 100644 docs/tutorial/tutorialFigs/colloidInit.png delete mode 100644 docs/tutorial/tutorialFigs/colloidSystem.png delete mode 100644 docs/tutorial/tutorialFigs/colloidsPipeline.png delete mode 100644 docs/tutorial/tutorialFigs/contour.png delete mode 100644 docs/tutorial/tutorialFigs/glyph.png delete mode 100644 docs/tutorial/tutorialFigs/pipeline.png delete mode 100644 docs/tutorial/tutorialFigs/system.png delete mode 100644 docs/tutorial/tutorialFigs/thresh.png delete mode 100644 docs/tutorial/tutorialFigs/velProf.png diff --git a/docs/tutorial/Makefile b/docs/tutorial/Makefile deleted file mode 100644 index 3b86244f4..000000000 --- a/docs/tutorial/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -############################################################################ -# -# Makefile -# -# Makefile for documentation -# -# make -# make clean (probably the most useful) -# -# should both work. -# -# Edinburgh Soft Matter and Statistical Physics Group and -# Edinburgh Parallel Computing Centre -# -# (c) 2014-2015 The University of Edinburgh -# -############################################################################ - -TEX_SOURCE = tutorial.tex - -defaults: $(TEX_SOURCE) - pdflatex $(TEX_SOURCE) - -clean: - rm -f *pdf *aux *dvi *log *toc *out diff --git a/docs/tutorial/colloid-test1/colloid-test1.inp b/docs/tutorial/colloid-test1/colloid-test1.inp new file mode 100644 index 000000000..3ca0825ea --- /dev/null +++ b/docs/tutorial/colloid-test1/colloid-test1.inp @@ -0,0 +1,100 @@ +############################################################################## +# +# Colloid simple configuration output +# +############################################################################## + +N_start 0 +N_cycles 1000 + +############################################################################## +# +# System and MPI +# +############################################################################## + +size 32_32_32 +periodicity 1_1_1 + +############################################################################## +# +# Fluid parameters +# +############################################################################## + +viscosity 0.1 + +isothermal_fluctuations on +temperature 0.000001 + +############################################################################## +# +# Free energy parameters +# +############################################################################### + +free_energy none + +############################################################################### +# +# Colloid parameters +# +############################################################################### + +colloid_init input_random + +colloid_random_a0 2.30 +colloid_random_ah 2.30 +colloid_random_no 10 +colloid_random_dh 1.0 + +############################################################################### +# +# Colloid-colloid soft-sphere potential parameters +# The soft sphere is always needed +# +############################################################################### + +soft_sphere_on 1 +soft_sphere_epsilon 0.0004 +soft_sphere_sigma 0.1 +soft_sphere_nu 1.0 +soft_sphere_cutoff 0.25 + +############################################################################### +# +# Walls / boundaries +# +############################################################################### + +boundary_walls 0_0_0 + +############################################################################### +# +# Output frequency and type +# +############################################################################### + +default_io_mode mpiio + +freq_statistics 250 +config_at_end yes + +############################################################################## +# +# colloid i/o +# +############################################################################## + +colloid_io_freq 1000 +colloid_io_format_output ASCII + +############################################################################### +# +# Miscellaneous +# +# random_seed +ve integer is the random number generator seed +# +############################################################################### + +random_seed 8361435 diff --git a/docs/tutorial/tutorial.tex b/docs/tutorial/tutorial.tex deleted file mode 100644 index 0d6878c73..000000000 --- a/docs/tutorial/tutorial.tex +++ /dev/null @@ -1,514 +0,0 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% -% tutorial.tex -% -% Introductory tutorial for new users of Ludwig. -% -% Edinburgh Soft Matter and Statistical Physics Group and -% Edinburgh Parallel Computing Centre and -% Department of Physics, University of Strathclyde -% -% Contributing authors: -% Oliver Henrich (oliver.henrich@strath.ac.uk) -% Fraser Mackay (s1026487@sms.ed.ac.uk) -% -% (c) 2008-2019 The University of Edinburgh -% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\documentclass[11pt,twoside,a4paper]{article} - -\usepackage{amsmath} -\usepackage{moreverb} -\usepackage{lscape} -\usepackage{epic} -\usepackage[pdftex]{graphicx} -\usepackage{bm} -\usepackage{hyperref} -\usepackage{color} -\usepackage{listings} -\usepackage{mathtools} -\usepackage{enumerate} -\usepackage{float} -\usepackage{hyperref} -\graphicspath{{./tutorialFigs/}} - -\usepackage{geometry} - \geometry{ - a4paper, - left=3.1cm, - right=3.1cm, - top=2.25cm, - bottom=2.25cm -} - -\setlength{\parindent}{0pt} -\setlength{\parskip}{\smallskipamount} - -\newcommand{\inputkey}[1]{\framebox{\textbf{\texttt{#1}}}} -\newcommand{\e}[1]{\cdot10^{#1}} -\newcommand{\beq}{\begin{equation}} -\newcommand{\eeq}{\end{equation}} -\newcommand{\beqa}{\begin{eqnarray}} -\newcommand{\eeqa}{\end{eqnarray}} -\newcommand{\com}[1]{\textcolor{red}{#1}} -\newcommand{\plag}[1]{\textcolor{green}{#1}} -\newcommand{\cur}[1]{{\textit{#1}}} - -\definecolor{terminalcolour}{gray}{0.96} - -\lstdefinestyle{terminalverbatim}{ - basicstyle=\small\ttfamily, - columns=flexible, - backgroundcolor=\color{terminalcolour}, - xleftmargin=0pt -} - - -\begin{document} - -\lstset{style=terminalverbatim} - -\setcounter{page}{1} - -% These redefinitions are just compressing the spacing a little. - -\makeatletter -\renewcommand*{\section}{% -\@startsection {section}{1}{\z@}% - {-1.75ex \@plus -0.5ex \@minus -.1ex}% - {1.15ex \@plus.1ex}% - {\normalfont\Large\bfseries}% -} -\renewcommand*{\subsection}{% -\@startsection {subsection}{2}{\z@}% - {-1.75ex \@plus -0.5ex \@minus -.1ex}% - {1.15ex \@plus.1ex}% - {\normalfont\large\bfseries}% -} -\renewcommand*{\subsubsection}{% -\@startsection {subsubsection}{1}{\z@}% - {-1.75ex \@plus -0.5ex \@minus -.1ex}% - {1.15ex \@plus.1ex}% - {\normalfont\normalsize\bfseries}% -} - -% Table of Contents -\pagenumbering{roman} -\title{Ludwig User Tutorial} -\author{Fraser Mackay and Oliver Henrich} -\maketitle -\tableofcontents -\clearpage - -% Sections -\pagenumbering{arabic} - -\section{Introduction} - -This tutorial document takes new users of the \texttt{Ludwig} code through the process of how to obtain -and build the code before outlining some short tutorial exercises where simulations -of simple example cases will be run and visualised. -It is assumed that the reader is familar with UNIX-based operating systems and has -a basic knowledge of hydrodynamics, complex fluids, and to some extent statistical physics. -This knowledge will be required to make sense of the input and output involved in using the code. - -The system requirements are an up-to-date version of a C compiler as well as working installations -of a text editor like \texttt{vi} or \texttt{emacs}, the visualisation tool -ParaView \\ -({\color{blue}\hyperref[ParaView]{https://www.paraview.org}}), the MPI-library -for compiling the parallel version of the code and \texttt{gnuplot} for simple visualisations. -The instructions for compilation of this tutorial and the full documentation assume a LaTeX distribution -and \texttt{pdflatex} are installed. - -\section{Access and Compilation} - -\subsection{Obtaining the Code} -\label{sec:getCode} - -The \texttt{Ludwig} project is currently hosted at our central repository at GitHub:\\ -({\color{blue} \hyperref[GitHubLudwig]{https://github.com/ludwig-cf/ludwig}}). -\smallskip - -You can either download or clone the repository. -The following command clones the repository and requires a working -git installation on your local system: - -\begin{lstlisting}[style=terminalverbatim] -$ git clone https://github.com/ludwig-cf/ludwig -\end{lstlisting} - -A copy of the \texttt{Ludwig} repository will now be available in your current directory. -You should see: - -\begin{lstlisting} -$ ls ludwig -CHANGES.md Makefile config src util -CONTRIBUTING.md Makefile.mk docs target version.h -LICENSE README.md mpi_s tests -\end{lstlisting} - -A full documentation of \texttt{Ludwig} can be obtained by running -the command \texttt{make pdf} twice (for references) in the \texttt{/docs} directory: - -\begin{lstlisting}[style=terminalverbatim] -$ cd ./ludwig/docs/ -$ make pdf -\end{lstlisting} - -This tutorial and directories with input files and sample output is contained in -a subdirectory of \texttt{/docs}. It can be compiled in the following way (issue twice for TOC): - -\begin{lstlisting}[style=terminalverbatim] -$ cd ./ludwig/docs/tutorial -$ make -\end{lstlisting} - -\subsection{Configuration and Build}\label{configbuild} - -The following section outlines the configuration and compilation -procedure. The configuration step is based on the GNU Compiler Collection gcc. -\begin{enumerate} -\item Go to the top level directory \texttt{/ludwig}: -\begin{lstlisting} -$ cd ludwig -\end{lstlisting} -\item Copy the (parallel) configuration file \texttt{lunix-mpicc-default.mk} -to top level directory -and rename to \texttt{config.mk} (both steps exectued below at once): -\begin{lstlisting} -$ cp config/unix-mpicc-default.mk ./config.mk -\end{lstlisting} -Note that you may need to specify the correct path to the MPI wrapper compiler -in this file (assumed to be \texttt{mpicc}). - -For production runs you may want to use thie compiler flag below instead of the default one: -\begin{lstlisting} -CFLAGS = -O3 -g -DNDEBUG -DNSIMDVL=4 -\end{lstlisting} -\item Issue \texttt{make} in the top level directory: -\begin{lstlisting} -$ make -\end{lstlisting} -This creates the executable file \texttt{./Ludwig.exe} in \texttt{/src}. -Note that more than one thread can be used in the compilation process, -which can lead to a considerable speedup -\begin{lstlisting} -$ make -j 4 -\end{lstlisting} -If you want a serial build, clean your directory tree with -\begin{lstlisting} -$ make clean -\end{lstlisting} -use the conifguation file -\texttt{config/unix-gcc-default.mk} and include the extra keyword \texttt{serial}: -\begin{lstlisting} -$ make serial -\end{lstlisting} -\end{enumerate} - -\section{Tutorial Test Exercises} - -\subsection{Test 1: Poiseuille Flow of a Simple Fluid in a Cavity} - -In this section the Poiseuille flow of a simple fluid in a cavity, is outlined. -The example is a step-by-step guide for performing a short run ($10,000$ time-steps) -with \texttt{Ludwig} code, followed by a short introduction to the post-processing -and data analysis procedures. Before starting, the code should be compiled in -parallel and the executable \texttt{Ludwig.exe} should be copied into your working directory. -\begin{enumerate} -\item Copy the input file \texttt{input} located in the directory \texttt{/docs/tutorial/test1} -to your working directory. Note that the input files in this tutorial are modified versions of the -reference input file \texttt{input.ref} in \texttt{/src} with a limited set of parameters -for each run. -\item Run the code in your working directory by issuing the command: -\begin{lstlisting} -$ mpirun -np 4 ./Ludwig.exe input -\end{lstlisting} -\item You should now see statistics being reported on your standard output and -output files being created with names of the form \texttt{vel-XXXXXXXX.001-001}. -These files contain the components of the velocity vectors in Cartesian coordinates at each lattice point in the simulation. -\item The first step to the post-processing is to get hold of the \texttt{extract} executable in \texttt{/util}, -which is created during the main compilation of \texttt{Ludwig}, and copy it into your tutorial working directory. -\item Post-process the raw data by issuing -\begin{lstlisting} -$ ./extract -a -i meta_file_name data_file_name_stub -\end{lstlisting} -replacing the filenames with those of the present case (i.e. \texttt{vel.001-001.meta} and e.g. \texttt{vel-00010000}). -The command line options '\texttt{-a}' and '\texttt{-i}' give output in ASCII format and a lattice index -for easy visualisation in \texttt{gnuplot}. -\item You should now see files named \texttt{vel-XXXXXXXX}. The first three columns of these ASCII files are the spatial lattice indices followed by the Cartesian components of the velocity vectors. -\item To plot the velocity profile of the system, you can plot the $y$-component of the velocity against -the spatial $x$-coordinate on the lattice with the command: -\begin{lstlisting} -gnuplot> p `vel-XXXXXXXX' u 1:5 -\end{lstlisting} -By plotting the data in each file, you should be able to see the velocity profile converging to the final profile as shown in Fig. \ref{fig:velocityProfile}. - -In the directory \texttt{/docs/tutorial/test1} a reference file of the final -output at time step $10,000$ can be found, in addition to a file containing the standard output of the code. -\end{enumerate} -\begin{figure}[h] -\begin{center} -\includegraphics[width=0.8\linewidth]{velProf.png} - \caption{The parabolic velocity profile of a simple fluid in a cavity after $10,000$ time-steps.} - \label{fig:velocityProfile} - \end{center} -\end{figure} - -\subsection{Test 2: Spinodal Decomposition of a Binary Fluid} - -This section outlines the second example, the spinodal decomposition of a binary fluid in -three-dimensions. -The example describes the post-processing procedure for this system and a brief guide to -the visualisation of a 3D system using ParaView. -As in the previous example, the code should be compiled in parallel and the executable -\texttt{Ludwig.exe} should be copied into your working directory. - -\begin{enumerate} -\item Copy the input file \texttt{input} located in the directory -\texttt{/docs/tutorial/test2} -to your working directory. This input file contains the parameters required for this run. -\item Run the code in your working directory by issuing the command: -\begin{lstlisting} -$ mpirun -np 8 ./Ludwig.exe input -\end{lstlisting} -\item As before, you should now see statistics being reported on your standard output. But -in this case two sets of output files are being created, one containing the velocity and -the other compositional order parameter ($\phi$) data at each lattice point in the simulation. -In this example, these data are in binary format. -\item You are able to use the same exectuable \texttt{extract} from the previous -example for the post-processing with different arguments and the \texttt{-k} flag to produce -files in VTK-format: -\begin{lstlisting} -$ ./extract -k vel.001-001.meta vel-00010000.001-001 -$ ./extract -k phi.001-001.meta phi-00010000.001-001 -\end{lstlisting} -\item A bash script like this one below can be used for convenient post-processing of multiple -raw data files and time steps (with appropriate start, increment and end in the \texttt{for} loop): -\begin{lstlisting} -#!/bin/bash -for i in $(seq 1000 1000 10000); -do - tstep=$(printf "%08d" $i) - ./extract -k vel.001-001.meta vel-${tstep}.001-001 - ./extract -k phi.001-001.meta phi-${tstep}.001-001 -done -\end{lstlisting} -\item You should now see files with the extension \texttt{.vtk} corresponding to the velocity -and $\phi$ data ready to be visualised using ParaView. -\begin{enumerate} -\item In Paraview open the files containing the $\phi$ data. -\item Click on the group of files in the pipeline browser and apply a contour map using -Filters/Common/Contour or in the tool bar: - -\begin{figure}[H] -\begin{center} -\includegraphics[width=0.8\linewidth]{contour.png} - \caption{The contour button in the tool bar.} - \label{fig:contour} - \end{center} -\end{figure} - -You should now see a three-dimensional rendering of the binary fluid. -\item Open the files containing the velocity data. -\item Click on the group of files in the pipeline browser and apply the glyph filter using -Filters/Common/Glyph or in the tool bar: - -\begin{figure}[H] -\begin{center} -\includegraphics[width=0.8\linewidth]{glyph.png} - \caption{The glyph button in the tool bar} - \label{fig:glyph} - \end{center} -\end{figure} - -\item Change the colour code to GlyphVector Magnitude. -\item In the pipeline browser, you should see: - -\begin{figure}[H] -\begin{center} -\includegraphics[width=0.6\linewidth]{pipeline.png} - \caption{The complete pipline browser for the visualisation of spinodal decomposition.} - \label{fig:pipeline} - \end{center} -\end{figure} - -A 3D visualisation of the system should also now be shown: - -\begin{figure}[H] -\begin{center} -\includegraphics[width=0.8\linewidth]{system.png} - \caption{A visualisation of a separating binary fluid using ParaView.} - \label{fig:sysVisualisation} - \end{center} -\end{figure} - -\item You can move through different frames of the simulation with the arrow buttons. -As you do this, you should see the coarsening of the binary fluid. - -\begin{figure}[H] -\begin{center} -\includegraphics[width=0.4\linewidth]{arrows.png} - \caption{Control buttons for the visualisation in ParaView.} - \label{fig:changeFrame} - \end{center} -\end{figure} - - -\item In the File menu you can save a screen shot of the visualisation or save the state of the system in order to reload the entire visualisation. -\end{enumerate} -\end{enumerate} - -\subsection{Test 3: Colloids Dispersed in a Liquid Crystalline Fluid} - -For this third example, the system being used is a that of colloidal particles suspended in -a liquid crystalline fluid. -The example illustrates the post-processing procedure for a colloidal system in addition to -demonstrating the ways to visualise liquid crystals in ParaView. -As before, the code should be compiled in parallel and the executable -\texttt{Ludwig.exe} should be copied into your working directory. -\begin{enumerate} -\item Copy the input file \texttt{input} located in the directory -\texttt{/docs/tutorial/test3} to your working directory. We see in this input file that -there are many more parameters -than in the previous examples. Specifically, we note that \texttt{colloid\_cell\_min} -must be set if colloids are present and must be at least $2r + \delta$ where $r$ is colloid -radius and $\delta$ - here set to 0.5 - catches any colloid-colloid interactions. This is shown for our example - where this value is set to 8.0 - in Fig. \ref{fig:colloid_int}. - \begin{figure}[H] -\begin{center} -\includegraphics[width=0.5\linewidth]{colloidInit.png} - \caption{A diagrammatic representation of a colloid in a 32$^3$ system showing that the value, \texttt{colloid\_cell\_min} must be set such that it can contain the entire colloid plus colloid-colloid interactions.} - \label{fig:colloid_int} - \end{center} -\end{figure} -\item The following steps outline the preliminary stages required in order to initialise the colloidal system and for the visualisation of colloids. Firstly, and as before, compile the \texttt{Ludwig} code in serial (see section \ref{configbuild}) and go to the directory \texttt{ludwig/util}. There you should see the files \texttt{colloid\_init.c} and \texttt{extract\_colloid.c}. These are the files required for the initialisation and post-processing of colloids, respectively. -\item In the file, \texttt{colloid\_init.c} set the flags for: -\begin{enumerate} -\item \texttt{NMC = 0}, -\item \texttt{periodic[3] = {1,1,1}} \\ for our fully periodic system, -\item \texttt{file\_format=ASCII} \\ resulting in ASCII output, -\item \texttt{a0 = 3.5} \\ which sets the radius of the colloids, -\item \texttt{ah = 3.5} \\ which sets the hydrodynamic radius of the colloid, -\item \texttt{vf = 0.02} \\ this sets the volume fraction of colloids to 2\%, for our system of size 32$^2$ with colloids of radius 3.5, this will result in 3 colloids, -\item \texttt{dh = 0.5} \\ to set the grace distance for interactions (note that this means that one colloid plus this distance is less than \texttt{colloid\_cell\_min} which was set in the input file. -\end{enumerate} -\item Now issue -\begin{lstlisting} -$ make colloid_init -$ ./colloid_init -\end{lstlisting} -this produces a file \texttt{config.cds.init.001-001}, which you have to copy into your working directory. -\item Next, run the code in your working directory by issuing the command: -\begin{lstlisting} -$ mpirun -np 8 ./Ludwig.exe input -\end{lstlisting} -\item An \texttt{extract\_colloids} executable is produced in \texttt{/util} during the main compilation of \texttt{Ludwig}. -For our problem we need to set potentially different parameters. In the file, \texttt{extract\_colloids.c} set: -\begin{enumerate} -\item The system size, \texttt{NX}, \texttt{NY}, \texttt{NZ} all = 32, -\item \texttt{iread\_ascii = 1} \\ since the output is in ASCII format, -\item \texttt{cds\_with\_v = 1} \\ which will output the positions and velocity data of the colloids, -\end{enumerate} -\item Now issue -\begin{lstlisting} -$ make extract_colloids -\end{lstlisting} -and copy the executable \texttt{extract\_colloids} into your working directory. -\item For the post-processing of other data either copy the existing executable \texttt{extract} from -previous examples or compile in \texttt{/util} in the usual manner. -\item Run the post-processing routines as before using different arguments: -\begin{lstlisting} -$ ./extract -k vel.001-001.meta vel-00010000.001-001 -$ ./extract -k -s -d q.001-001.meta q-00010000.001-001 -$ ./extract_colloids config.cds00010000 1 col-cdsvel-00010000.csv -\end{lstlisting} -The \texttt{-s} and \texttt{-d} flags produce scalar order parameter and director field output. -\texttt{.csv} files with the colloid data should -be seen in addition to the \texttt{.vtk} files for the velocity, order parameter and director field. -\item Again, a bash script like this one below can be used for convenient post-processing of multiple raw data files and time steps (with appropriate start, increment and end in the \texttt{for} loop): -\begin{lstlisting} -#!/bin/bash -for i in $(seq 1000 1000 10000); -do - tstep=$(printf "%08d" $i) - ./extract -k vel.001-001.meta vel-${tstep}.001-001 - ./extract -k -s -d q.001-001.meta q-${tstep}.001-001 - ./extract_colloids config.cds${tstep} 1 col-cdsvel-${tstep}.csv -done -\end{lstlisting} -\item The system can now be visualised, again using ParaView: -\begin{enumerate} -\item The liquid crystal, scalar order parameter can be visualised by opening the files: -\texttt{lcs-XXXXXXXX.vtk} and applying the `contour' filter. By setting a threshold -minimum $1 \times 10^{-4}$ the order parameter in the colloids can be ignored, this can be done using the tool bar: - -\begin{figure}[H] -\begin{center} -\includegraphics[width=0.8\linewidth]{thresh.png} - \caption{The threshold button in the tool bar.} - \label{fig:thresh} - \end{center} -\end{figure} - -\item For the director field open the \texttt{lcd-XXXXXXXX.vtk} and apply the `glyphvector' filter. In this case, change the glyph -type from `arrow' to `line' and scale the colour of the lines by the $x$-component of the -vector. -\item The velocity field can be visualised using arrows as before with the `glyphvector' filter. -\item To visualise the colloids, open the files \texttt{col-cdsXXXXXXXX.csv}. This will open -a table, which can be closed. Next apply the filter `Table to Points' found in -Filters/Alphabetical/Table to Points and set the X, Y and Z Columns in the `Properties' tab -below the Pipeline Browser to be the $x$, $y$ and $z$-coordinates in the table via the -drop down menus: - -\begin{figure}[H] -\begin{center} -\includegraphics[width=0.5\linewidth]{colloidCoords.png} - \caption{Setting the coordinates of the colloid locations in the properties tab.} - \label{fig:collCoords} - \end{center} -\end{figure} - - You should now see points in the render window at the location of the centres of each of the colloids. -\item To fully visualise the colloids, apply the `glyphvector' filter and set glyph type to -`sphere.' In the properties tab, set the radius to 3.5, scale mode `off' and scale factor to -1.0 in the properties. In order to access these settings, you will need to click the `cog' icon shown here: - -\begin{figure}[H] -\begin{center} -\includegraphics[width=0.5\linewidth]{cog.png} - \caption{Location of the `cog' icon in the properties tab.} - \label{fig:cog} - \end{center} -\end{figure} - -\item Next, glyph mode must be set to `all points' and you should set the colour of teh spheres to `Solid Color'. Finally, you can increase the resolution of the spheres by increasing the `Theta Resolution' and `Phi Resolution' quantities. -\item In the pipeline browser, should now see: - -\begin{figure}[H] -\begin{center} -\includegraphics[width=0.6\linewidth]{colloidsPipeline.png} - \caption{The pipeline browser for the colloidal suspension in liquid crystals.} - \label{fig:collPipe} - \end{center} -\end{figure} - -The 3D visualisation of this system should appear as: - -\begin{figure}[H] -\begin{center} -\includegraphics[width=0.8\linewidth]{colloidSystem.png} - \caption{The visualisation of the colloidal suspension in liquid crystals.} - \label{fig:collVis} - \end{center} -\end{figure} - - -\end{enumerate} -\end{enumerate} - -%\clearpage -%\vfill\pagebreak -%\input{references.tex} - -\end{document} diff --git a/docs/tutorial/tutorialFigs/arrows.png b/docs/tutorial/tutorialFigs/arrows.png deleted file mode 100644 index f699b0c2033dbfbdf0491bc3cfcb1ae5f6fe78d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21915 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%0AuQ*^IC}K>*=b=k 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

Y@%v*OnJEO1MyK|Xvk=zAoV@?8T7l|Jw zSt50lq4nKrOFrAX&g+s4Y8=iVheRj#nJ0qiW=E_lUl@I3Ma+_>oJ2l$5af$ZS$#l) z7@Z9q0_3nxKYzI5sJ^KGzPy!w1KI7-V7=&I@vTHMzxlzHk30F&G7#08 zXS)!Ckkmt;ZyE|Ti-U_j*VCOm;!35$8F5RP-=`PR?|xz`5wCvcCUj@GvnX7h_So1q z{-e-C1YW`4`8+g`KW6e>3)N5gr#PA$lakmMI!R=UY9XR1tI_UYhA;v>1OAbta;L)0(gWtt#5pxpG7LiYGz(O;pwj!e3@@thG;V zr!P4KC{WSaZ8T%Td-LUJ=_c}j>Oc8%o9|@r6+=iE9}{|jgIb6Hhv_Evm&1$^1>|o#?QnJ?x`2R$&o#?@wCV3tq#u0G zC#>G^b{Wf%QIVbcN{x7mJ2SEs&c#|l=dE6=Up6Gm)jZ58NK-oz+0|IHF;%F2x~#2^ z(S5Mbj@wnurEWAi>6p6clNv8N@VR$2TgIiSu7;{uvhBxU%p-N$FB`=$`A5i7m@ve8%1>vy4NBO z!=0~@R4>Ivlq!YgE&e?li0V1_$xq`v_lIksScO!b!<^IUh|hB5K9=bQN_c_!BpGZM{1SnBDu3O{;48u3X!b>hFGADH3ocN z>P!&_cLQq?fVmx-%BA@-jM!FkjlK%t~G;U>+m3}6M~T_e-zL>Br_~lQqE-597JL7b;#uZT|?d4_x#j6r`~M3 zQ$eD=HT}WI@m+x>ty-BV1ZCn$Y?XzHe($3&Bm`;3Ay1iuSgHlJC6t(mTTyS|+G_ZM z-(e`XMM~#S@tZp02Sus*AF7MfHDI4AnS~q%qJ!h+5$j{s2A>)ucs2y#wvq1C4_jKv zc;p3E=ew|%orT1o>6UT7bzC(NGQHWX8jNk=($Z1kDx6^hI3(jFk<6=I}6U}f{jQ*W29gB!6%IDWHQ7lkW!1{1Q&y1VO* z)vRJ))0lLUWYLQPNPy$oq_Ek1h|4^}D)l%WWq4@mphQ4~McaRu`E{m!mN*k)CVO{}Jh#J|yeU zi&a@;Zjfd9;_n@jrRBM-cJ#}fWx9*)fWVw3Nl*CZ1^ zrP#bD+K<$Q)@ILW4DLJ^$efuLp>x^LmyJN(I!$)H>9yU7j|n9*I>o@F%n8+Vo*lzf z;Ha`7=-CwFhT^=i0HXgOG+ok2cO3L`%-cE`a-Lmhx>h9ilzv@OiHz`DmH%Cy z&vR~~*^vlQ&01FtSNQ4j=pCDl;ZyABsfEs1kMbV;a zS~EkS(su1u7(`SCz?!J8kTWd^J{{6kK0vhK!#^Nj}vdvXol-5X5 zE}mZx?!tafHj8JKK-ld9)*^lcJK&osPcjmpri(XwOei&JYo##~y&CU}t%)G?in1KE z*)<(JU}^^NeHDrcV0Li`{A>XwpYUi<59Mzwr(8Vu0c*Q3y-aW zx_zI4RlL)*M-A9rBgxU>DS95}+qCS#j_%6cJSZM!G8o#AwN{uz*$xO@xBgjWJ}sp+ z#x-V3^*>1W8H(#poOYpC8b4N6e1~Sz(x&Rz(grQp|CfMn%y~j_ahA9D|7+~V_UZpM z?1t@K19};8O-ALZtx#IN({iDezYv9ON7GH-ZTp|9>Le)Aa!}BfO?Llh+@>V7$cC43 zwM}v?xKeRD7a`0#@a-y}+z4;suc%`7Bm%5K3daC335k#-rTmRSZWQP-kd2`<9phx{ zE$*m=hQ0H6Dft;2Rie&?W2d2r<_5*c4iV(a_iiGW6-?i zA4O%eyFqP2WAIl}e|8~#Y~(C;k8Vm-v!K6U|LpI9;`=7;&C8(t8%X0I^1FzZmw=rK z@NLsx!Crqp{rneXn9YB3@$>6qKLLP?xuDpeM#lMr1upW{ZwGw&?;l7(v00Xw@~8Uz z{fiqO5C1~)&VJMgHb8h(w&&l;Ispb%PxSXyUqPN028D|W{eM{Xo-5a=i=#TN!LKXe OOH4@md7gll`@aGHc-Kq- diff --git a/docs/tutorial/tutorialFigs/colloidInit.png b/docs/tutorial/tutorialFigs/colloidInit.png deleted file mode 100644 index de345aceb75921a0a39611ff0de51e8dc3dd8d97..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34009 zcmeEugBUOh`;~>(j_5CmndC=)F?wsNOyO4BM8zhh$G!f2r4ZlF?1*bN-N#- zJ;S(vXJ6m>&i5CbeO)TdyicuX#l7ye-W?59dq{Ad>N*An27!{I+!G88tP=1i7xxdFQ#9{1jW#kBAdkE$YTDo|2*dU6{x~V zTi@Q@_E({X57!|#}WGh#stRj61GmaZc#4xIx zASBp9QOZtDH*6m{mE1^3p6*aMmFdR{QZcU$U&UZo@)E$mx61oGDXg$OsED6jB zw$Op_BZ-b5E-n^QWhxPqV*8QzRs&-*?o%ZXsL^-UJS~zr_z~+;J5`GuT0UAxO6H== zXom>fMDVifB?&xz*HlJXK%x?U6vZIKL}DToHMHCK#22qMi;*xUdFIenuGMJ(;|j)2 zr>TGf$huTN#In=7uUcH6eLwub3R_b0p4M}XaDi9Le}=K7J>=5G21jlf{>H6Xi9Rz` zup2qT`~+!#BuhkY(ZSl|_391&(H~+6r}XTdFEMy2rxJJjf5+ddCXOj&$&P0XNMpSb zl{#gdDf;R~3okXH{(XwFt8poL#MHNUJ4w7>byN`(KF!T*i@VabjJLQn%d2A`G3>IC zG`qpxD6TcoM;Mn{F3_M3g9t483el6N+tF4%%-Bd|KYqIHx?5;sKI?Qd4AbZvPP)_$ zeU5ZYe$2MQwnqAkq=BgQeWQo< zHI+h&$H%L(aulynEit4?+zLbDu# zEXZ$UJon85vGkm=VM8(^O+UISi<*ius{Y)!MW6u-eG^(NWvs@Y^-x(Fy$(a8wxWX5D zl_tws#aI74d(GMo+=mcpEp0Z$Nu>J4aV1nzzNC^!ON}J zg9>wKs;i`mlWDqxFxaIix2|*L@oFw3Pq3srw9?UDFg*;Fuk6?*IA zO}=XuLAs{g={)4oET-1!kgS9h6EYow@u>RG$YZMU_Kfie& z`T2>q;#Ib1{T@9MYQdrZ*hXxHO&db?DITJtc)N(yg5(h{Sy)b(Y?y%4!XC*yX|&w6 z;kaKn4$VpUlJ7;n&wiirKFup+Qr=I6=JpFkz1y5gICqjdSeKI@DN-vnD|IVrD}5+R zFq5y5n98M*Qe(yMkzcyL!@a||qq!rnlNb^c5*?ByQ-r1ctl{&caN6tT*R^l@kVUYU z+!TnxVM$F=(v_Q%vM$Oln0?ct34;m3+%&U`dbFRy-%C5^EqZs-b*jUJN>~tS_rp1g zEAq#ms6)4*3nNy78dR~VDt&Lt^AV$J!*nAO9K$JgFGEt^Kf-^>k&^kcg5$#zEvWG0 z1FhWRr|_o*ep1IVE^RR(F>hlkuj`w)K6?G-S&BYKXo;KV1B4=i2C-by4C8@IXfwaf zC^;^5M#LZ*N^_q0s&xNSa5K6e*_z%O8W1sGssPRzN0jYLQBO3CbCN-$)n*mR~rZQ>YLWiwI;j2770U8t0or z%ov0xExtbEHM6p~WvOHKe&Tm9ac55_`+!yXQ?9Xf1duh_M@5gIx9CTCF{EC zV1^&Bkbo+$fyuPp$*jug-N~}AWuG=$MF(d4#D-eB&YG?cf9_=JGpUt-!(y{)mGMm1 z+Pm|5y-UrazJ0?Q)x%pq@2l}vSj=+Smejv<4|WfAPatS#?Jw>ro?1b8{rJ=SLpwoI zZaf549^dRM6g4T)-C^BIKbma+8oF6MP&kvknz7i3sGaHi7`v(YV?WBgW3k4gCf8fv%hG52 zAYj>aoq11mY4_8_fM@PDo@77I*UGWVcB28w!N6o>J4SoZUQk3(uvGU)(O|X^T(^XE)AqKk9v%r7=)EnCbH@^Pq0S z`r#W+bxU>0L>WaT#i^8?aN6FAXGF!<-e|ohbn+8j7v*WZyQSwqKNPAx=$pFoLM!(` zY>wGq)yF78TR~nyav`lP8hKxU-Bo!dvaF|Uy6pQ#*v1FZrRbQ@nB+Ndt|D< z2GbrQvbULA)9jkCkejNo-evDg>DzY@?7!&G6QF*4zH;&P+#csCp&*^0sE&J70K5** zI&)6&YxeN$T+-Nx`@JP6{Zilc0!h0IxVVW^(GI+8IHjAtJ0^jEo<~f=i|J4f*}IpV zFjM?)?m>#z+R3WYn+JnoxuSu>-!|U5&i{O#cX%&wDP$(Rox*@y*vO@6Y;0>X;8)F$ zWm#G^T2<}r$~w0uj|LCbjr$&m^|fBxfQdmvqSk;_g^u`&EN2nNg-n;yQ{lsd-3L>i zh6`NpAGkSw_y2r8g2O??LANGWb{yF$dEoKDoqqGrx^>{q-Cy&jgFn*L<%PtzvcbWY${d0I&+kW|HoVvi8 zHyu@#`=*Ze+|SG$P0YEy?47{V7#Lz+_rZ_$=5EgzyzK29T%q&=K@b~xq3UeJ@evnaAihM^3ObS=B}nL)=qBLjt&f{d7qg$ zy1R)pF`*Xv&wuEBntNIQ*Gdkqe~$$Y$b+(||E{W@6JpU2B z#C5ga6)$ibG}dzJFz_!H>T1Bnse`}R(f^`;mK)TOi*CTckit-slZJU=Zsg$Aks(qd z-reA5XJ86r6CtZ-2kYm%%9bBK6O2;4${wm|-@I zqahmqc?1IsSF`Ki^DyEu!02x?%U1(S{*|sh3}u8 zfN@YoEL?<4@zpo~8U-nd`@2=t#KAHoQgCv9u6tMi>%$#diJ0zjcs+P^ZIbCntki&yMey|uW18g^6Ef3NY6hvGo zV9}pgTG^^c&i(XOWA~>}oIav}-&ZIfc-SooZ{VW$`IR9EipY;q#a&YQc-A86FncdU z$X3fly>-9bvG=g`P<+a^@&n0CJ|kK-3yJb3?#O%YsGW*C({1JoER?Ws%jQ{T;c0&&5K{DWV^vg76Y6HaZ1b#rv>WpVSl`8pvLQu2_?Uo;xAz>8I<;6VAlar*7ulDV2MKRT^#)| z{}ghS%SU4QrH0wT#uV4Mh(YzU_G_X`iLz!h4fd+q+84rD<4+I2V4=m@lY}87q&&TM z71JEc|MVawg;TrX*Rtfr56PvPK5pCUR*%jM%iKGM3xR=~Hpi!JNZ-b-rggnC%d2QP zOC@8-XeM(vFedV0x{i*HyjIrV^8p9fqSHp0$PUEBayKa!t7JT>)#?x{Svy@o2E^`{ z{k01#poumkox<4stj6=)`(I^=>VNHL+E%GPsf?gTUt}KgI8T4iWkiXsH(S)T2nd|3K~uZTpjqo00o85&cMjzh zC4QO0JRkO+nCsUP`2DqAc2X%g$CWNIFrBeO)JbXE38(kz<~w~i+pQxJfzoG=nLa;r zJeT4G261y#X(a<%X5A+YxHhV>&_LjT`6-t^&$slg!${0EJ+!Sf1sbZ&;>0RL#gc{K4H#AFgn0e_gk!$g5nUhx>)aoCnsxHKov80)@Rc9J z=6|1-vH>n<43`fBJe_{uy~J9A5JC;CLNOSuWR4 z7~;<@lnII$WK2|MQdjHzbyd1R8!VKvS{d>cjn7Z^N7@1dr-0wF;(3$fk(r)V)=!I6 zO#}W$J>XBQQO|Ok5qFouB)`j2&)0|A;hTN;|L&a&>|OtM^_Adky!38017OSrhUp?& z;=ey$A)r5M^ZK*+#m;t8Kqr3qA)U9JLINGFO&;0|F)#_hSb~ztFeyUpCiivLED=YC z;ac4`yD9$F;lhWp_Vt_>>undxttW`e@rnw+JTdPY6`*cz)SQfHXF`PyDWQT@;|0gz zmFd|4+k8OIWSBIH7=zpo;`mNeRBxB-{p{@#QPD}%)A z;`rw>yzu$;&D|*G=ZWF?6cG-k2G)i*oxV!rAiyC-hG0UD%_e`X#!MbKOET+|;2M>Ht?#{SJGZyu3B5*8A~qQa!U(~fgH~p@2Ce_zuzTRVh&uMs7wy(3@5HJ94sQUq zVo%yhhwZ}T)W`L%=PJch=)1#q!UrmGlQ(?H*N|sJ$R3flW7VmuXR=nO{s&VJwaRoD zS0Xsb|Hf)9a6!5+is^CLNhfP96I8PAJqGTqB0@aFRyUG>nssICLfg<=KG%DN())Nh zZM}gnjd0fgaE{+$_I=Z773SZ|k_MM0O_l}ei4JPyTo4m+2Tpff?ERlE>gB~)o(2md z*}riP4%U0DR85af!&v3~LqQZc=;haGhL1M?fWJ9bu5tdeF*@*|H}s7<*ch*4IzQ1d zvR6HT$??O!*2a9lKD?r3=gXI&bD+)I8wY}a{pTbIiS^RN)DU*|;DIa=?T$5+Co0vh zbU-NYdXzc_p3d-F^rvebn&bR64q*_ngr9Brs(zgP=mxx9Uy)Msb=sd>bIs<Y|nXb6NjC@8qo%>aMWCumribY zW7o%Q9B~mGc;`dK6a7p&``)8hP!L57OuAd7zAy-Zvq4KS8DOQi_aw8fBf!Js9C#8a zV><$hxTY-;-Nhd~j%`!_Aj~Eu?7}h~T7D2?T%Poe6txgNSSVoW1^ZpnC=O`p9Mk1A z)OZM+?f*>k9=P!}kBi^){K4ZiqJ>L1A<1!AD(KEeN)CJRjCutHGDMXCs zoadUsUpRI8QaoRUV_?s+1VP5A-@!J}Dl&DQmXi$L1D@7o<#Q4KnN$FB2g^?YCI?Sdts%#S? z4`+gwfA4~z9(@YV;o7PGQ=Sc?S;Yja?K5L8h>;rLCfdUWp@sn;gn_&4qmjGVdZcBz zh>a&StT1Bu)SowOC?Wjg-Lu)C>ZrlW_8O?c&mV}Rd{R#+Kp{kHd#hr>dY@vT1Kh6_ zz{0gZRMk;-v~wE-{A3CL2kQA8I^OM%J>F&mJ^W2Gw&68O`fq(f8{K8xa|fGUfAtKd zPUtuoB$W$f=sohr5p4^XShxv%$K>41}ghS%=$<9)?f6Zol@=Mp{@u za#qFW7>3zY?}mE*a|m{lDi+uPXZ^rPu`0E^W1l5efnIAiDc?UE!^G5=aX;BxC5ugF zRShYZ2eB)bm+v1`AEb|MhpQ=&b>HpR*GJ#*DY(tF5Q`vOT;sLB0gLti^pZb#+V*8{ z8~^W+3^wFdW!x<(23ahseMbuoHuc=6(Tfc;4x|>En=CgAsH)`PlhiP7osoOcMu><+ zt9sCx00{%s5UXLm?S591S#Q$yF-F-4+}e_ZE(#eT<7jBT2>fZ4*{RgBO#jgl1=bRh z=k5_cmN~O=^F`KRv6e1>sUFLV3(^8AUO9L%y$ZK(m4Cj=9S=V;Mr0fo?#r~Gfs@J{ z)$%9iTJ?{Z&e_16QBYOM^rtn(yHd+fW{*xJFn5L&gQUFiCd-gjQ)3*u7Mi8ENBo&! zR|0SmZdZEi$AyVt6`jx9`cWAa)B>yFIdb1Zqtq@#vph-ha5W4n6#D6|F#RiA+x+78 z$_byJgE=6!vU;zVzccGg9ROK<xQJ)%5xw6&g>p``cG>44F1VK^Ia$ruBG1z9%ye82Ucnw24;;m- zTS;9HtQ8&BYNL>R2d0f6xTfE$_5fOYi?FioqDgFjOrypIQF6DbcKYEPwHz}5+*6PT z^S+Ly4feNO8Ii(^9SZj_cjyPh>mlq{f(7^KJ%#JPB%_3Ulow7`QeXQP@zu^CJ=-E* z%YXR9?=Rp_1JTmP{Y8es?(N{h=l367oKzz9`c-x1bz`!TuXuN+h<3JJJ)#M);_-^! zBCGJ~x;~tnmzGC2wCUXKHUjg;Uki;ldqWA=lWiAMv$;4sHnDi(uivkMC2=yGpjBa7 zZl3qBtX{%NK$A|lr&4_jT(0BG_o3EE&9Y3ZT5Z**B~!Wf3E4h*vaFJO7c*yq?GCfU^i1&wp!#^}(uCeU8_z)!V6B%f6L?Ec-0(GV%6{Uxf*& z00l02v}-=zF`B&Xy>;vOQ0!9}PVhDCq_M#1sj-p=sLjbC?`A{o?SW0`P3t0VU#Xvd z7q7mX7~o2~CNLys+Mb;(+)s+t6-UV|?z87K5}56gCN;mm%`GYx8FY%8=_Otr+-yXx zPJXjH?fg0$?8l7=!Cp0fs6ESE%ZS@be}>`g-IUa5Z})Zhsq&^>+SaB$cob<$JSch>Xyxoeh~=k}l%zwG`sF31T}@cXexu);RKr@zN=A!-gv;D8`~G6Bsm=^d1PyG(2j=Q2 z8B2?`QeXNr+x2<6WkrqD$7`4aC-1(~3j<6bez|FlgdqJ?SBjGI?<0>|Q|CnHy=1qV zw%C*GBjy;PjL-+qz07E?T)|pqycAkr@{6t)Amm60tHD67 zm`W*N(F3R=Dh*3pCai8+&ukjke!l4G5nL=)Jf|ntk3!oRq1>12);`sJOV0(uTCx^= zYyN|Nz<#Mw@5%o9`ly962iA&|6G)W%KH<=3e<6%#{HPQTlItCkukb*EtmOgdZVzz@ zWa>b!Yw77cmFd4{P0$7k~OsU&{vKIz9QrmL`W4{2r~Hv+w=p=nJUK zgx^Bv8eQLjTvxThVQy{x)*Z8_rd@F_08>XXB9hG^fvLuEiBzRxgA~qfC&ywIgqp!( zrb^))lE*&a$W+-6)joeU>@RGjkQIn9xBa5V-!$1#WBMMT36i5vxMzylCPE( zF&P_H`hX<)b&d277vgV4Pb&OKxv(?QE2;W(?=FCqPMyQn04HV+Jd!z`NO;U#3x@I8 z(Vy@gJj)nWu)9mZ_b(fy2GZty4$T4h)^4)u(`zx`?e#D`(jxUdiTQ4&AP;au)$PQb z5T_THf_8gR9x;TA2(p0{G`TE4YCPR)7IFz5xQXqBCw1M6#qNvJ>o6rYGhFP;`((xQ z%>#(^7T)9Vwe!OTp2`mgR6pJrWA{rh*5;j;xvh?{Bw5nHn4ax;uRF+wP3AF z*$rr2xzkGs#4!9gY)wO5oTUwUzOBqJIEz2pU!19nn7*Q*bG!ajt~W59cDzw3=&U(I zq$MJV0PWZm$|NQMVAYLdf)%Ufa0H%==K1wBzvD}9Mh<9T=;_~s*D3UDZ!RrtpOMgF zQxRX;p(7uy8PFaQeWP6alDRyRWOvG zX7bk?B#P3o7|2|fZB-_2m&T#J4P2sHyN)Pau;a(M!p^pT3?2l_9erg@m@n@NWaSuKwuMtDA)OORFRS`D~tzHF#X-+I) zO&{@sCd(nZ#?|7i4!@fBvLD4>eGw}ung1;Pnl8hoj19TmBk)*;A@1_ZZtQ-DTW7*U z2-%MJypcR#?eOc@xUx3K>)y+f)&uGXp_f;H*aF)%o368VKR1=-!>c!EKkv^4sm_^XX??DUnKP`(>;p1|Ou$yJIXm#;cqi8A z?A3Pyp5l0|ox*AVonE$O6fx|cw|$kZYDnS~Mt5d3Gyp%YoXFjGAZ0*0{3P0DOpLhP zm(4=p{sIHNP1A$hiTK7RB_#jUP zD@e)&4FQ~ka)n@|C8%8B?5JDPA$a{{y)vyb@O+(Xd_un<OpEut=5|xYBN>a_MI_bu2uG+}rIJSWc8B#M>9Pxsau<9r4CmS~$K*f`Ls! zdg&gbuk4i-xED0pPH=-NN)#%mxLT5jJo~xRIOJc^d7{9HI(LOPhxDdUL;RV!zB6td zrGUaUB08Ei=-64Lu*K_LV6?k;Ze!ryX1!E;Qk-A9vg{mmI|L;rnE zYrwh2+Q(~IU2gLDGa%Wh8+#f;NbZ5!%o3o{iT1hwKbzDnH~~;3=GZ$Bcmpop^Phni zZ~QiGjX+7Jo(y;co5L<@$Co~iY> zxN&s5_RrT6=sAj0G`iSH)AyP$)#mX&-=BD`@kWiSp}s+e^Z4=Ryi+?td1KE(2#M7m z70OPmtlp<20XxAHqwVBYwdO=Q9)k9PM^C9xsLyE?M7UzxZiMh_|fn~WX+uV%D+nuE{PTd2Y>K=8Qkzwy%h`RfeT@c?l@naa$WZCrA}$y`7D{mS9g3rH2$g?H~sf$_@0HZq@A^evW|(d7hw3B_Ma6 zV`6eOMZCPDp;wmBgg%c;A6Je0`Z#>6#wZv!{bAAm_ClK!-SweW5CJP%QlblztnASY*g zNc5`-j{Ryp@bP&!4?x1AXyX!-Y17jqE}<}Rd;F`+8sG&ENB1>QKrpenyf*;7UXXEW z-9E^RI3~V_Jli#IJ3r_f!t~+38w}q!n#6Fr0TPUV8Nl1{lH;j`mSUG5UGZFB<8JZE zT>Ordtka>l48OE|abJ&Rdp&1Aiv<%1uX?@}D82G!e~OhM1~LnGp|S*0=R`|A#aNs- zNe_OpBGN|eI{IjGCuiI5VMWtgNtNo0iA%vV*MaaC-E&c6$_E5iVBqyVywm9#&-I54 zHs=(B&NQGn{RC}FZ?EikNZAyeg!A}GJG36JKxx{}HyxM5NY!xX$lYIGI=Vld6rEAd z zywNUSdRX5aaPE`AXR07Pl}}-d-CwFP;0n`afGB_sMb?|sVl#Xbz7+eau(|L28 zAjx(!zf>&)Ku6-r{A5}lrP`UcP6M?t?|7SO6^ogR4pQ}(AT#HfQCl6L#Cp8GkP+l` z6AKcB7P1?NOd9AdPaE{y(ob1Qshs}l=8AtM1Qk+u@ zL|Ecxr_SFcP#coXDL7b)6$XpvLjJZjSJ?Qfd}-2>d=7&^GFUxU^VybW6e z_2bT2Ua|&MS;7V636b_O^=h2Q&KumiHFEu?B|5VVUqSUz^l;X7&WW7Il@aph!qquZ z4bZ_tm;2Em2r`WT$20uk6F#qKeoO3lDcPl;ue+!Vc<6f`1DvpdQkd>6gK^g!FoB;D zwO$$c!T0v_Vnf}oW8%WmILiiuMj%@Uo{4j|-G;nyZ#VehQ!VGd z=EdF{N%_6Zxt-g*0cT#KN8dts-GW>3)fgPkDa2@IK@rOYa_N=%WOjTeI%M2YOvhFk zq5kwcN1mB?jufut#;DtK-pusM1I3)W?_UiwKF<4Y8+d;W1jXzW-_I?5*o)(BebtK( zuo=E)UpgqeEqCuW7V;8}VwD%5`po|FDGJ?#$_UXMxhLxKriBDLKn=t4>EV>5DOM;B zQN?L-U7SHdGZtx-KJqHz)d|25q@z`{zp74)t71Q-O*vL>a za$6JzS=xUC7Q`je!iConua(oj^k)e8Q(ZCaFrj;BPN&?770ix(@2y-xhXmwz@}*?= zk^|1*kP^&VxQsif{O$A^T|8;9nQJOaOmQ<#AU2x(=B9aiIjtvo^Nq%k`7orBnmSmk_=vW~aNMTKxn zU_Lj|=JV*T`6Ld=i$n*bZ2%YR0w~%Ycu5GXhte{e5fl$OqD0<*#{!4rHp8Q5%txcJ zHM#9vO#nq}@5vwpb>BM`h@Jc@+%lgPNC2cH3#jZHZcM=lIdTcY(!VDn(?3qqV$q@H zPsgZEc3np3BZW3R(^m71dLH-63(dR7#JnQV4C=Rw+%&L7l3m-to$deU37;0&WM)is z%mczkA|XMh+rlB2U*s{1?mhVJf)#b@oo#jQXlyKJG}eCaeB2}nPBIGOcM!TjmEs0tBbF9o1xc{6Nzl@X4%w|D2HI&uRbZnc z226SfxJ93Paxr92P+ZrL$|m4zIlAw`i$4GKGu_$tyN-u}6^%Y_2d2+Bq=4$SU^&zu z+<+P2_ds;k8j4V?{wxZ(So5Al#CN=tt{@E3`UfSeJQeVQTFAy?=)(kn}1R5r7TH7GQOEN+mB5jj{)*v*)jiALPCaJFk8+hwxH(=>GePHAVOcF2cc zZilE{EMyIp#0};=(07dpjS+gOM#Y3wD<*iuXdHeywvMC>M*)l_vA?cq+9zg|l2tmQ ztq5ucW}n0GMhYJ$>~&@8e9IrfR=`8vMzW~fXOv}NxICiLqkTkJ!n z=qT|0^$NW+UiyPGkSEyjB^1_taDrB=j&b=eEtLCx=fbu_=TO3?9#iB%mvZe3Z2DUP z5s;!?R-sq_WtlH2c6((&17L^bP{R9)uLB7=ICOIO9$n-pkv*6Bc>gkZT8le9Bv|2w zq0~K@O}!yx8`OVF#Y_cas>=WsSV|=JJE%;jnkhzqV;70tuTnz%7KhF&pDLkjr;;2a zLW7tA`S$nlEi5&(?^Ba)_~o}HgAn_&is~u=2?3v`P9&{IIx0iinTe?Z3>|+^B%M!` ztroM{dr(dxdttM+Gx4;hzs-VXQcWS~jO~X4C+?BdrOKXsoL0Eeh_6W+7O#uASNpbj zs0=5ivF@W7!MDbrxdL;wVUS73wsQ_9p-ufaGAi$MQrs`}?|qDUikFCd`e#%W+XWp$ zUBhAdgZPZF(ZQ0g995yST`CwLNkhaylS<3Y{9@b_pus5WHJuW1H??IaPu^~C_&(;& z!hPn0prA^$v#mV)(WQx7V3VyTP)g+~jMz*2nXiWmet$GUND`=c86XNg&GP#n&uw3j zs`7fT7zuFOJ!F=;D0DXA!97w$AE-w{NhO@P1TenI{l!#JZ=w z@Js_DPf1ODABO7O;!j}PKC`6br477RxyHPn3u^PxF(8JV|8`Sj&5YPu^F;k`m zVK#uUEvkLXN+wSQ({HeQ;k~=k3;OA_`X8GNm!kS86rR)E0lv1zN~rZM)3RjiVV$CK z(AgUnMPJ2_vCfSGpbZB)}JXZAig$sn*P z%(Vb1U9wvEEA-3LTAKz)*jI0Xu9+cYH3M0NAdP6s7 z8#Tb~-=-FEcnMlnVl$kDkQsefJvJs8Et(q-5Wj_J;=TzA6s??$00#XLsK#stPsSf<<8A zV}DT!I^nULkEX&xGdQ>Pw@JZMHn(G?U!%H)oI)}7w-s`KlVDMyeTu>c_p}^fI`tb} z%$F;tKUru+l$XA1ZK`b4pBEcZvgPyqvprqUIrgrEsNO%ayzGx@bE2zf zj=7@LY$Yq?$Ja1%Z>9uAz{jd|VF9|Q;ZRMc=U;7ajQ0!M4&nl?HXcuKAqHwc80rZ5 zopJfxsw?4QM+B6>D&ufGi)AB>tf8Xbf4ga&`*^ZjLaX{MSPD4ufF^=nE;|F;3*r6Y zx+P_P2{K}B3*7-t!RNM*5ngLa@^ud#qhTVni2b7i zfkerHGR19`4Qxyvv|r`<&W3xvfJ#ah&U#B+FgU0XWC`~&@6AR8UA=UeqcU*Abbw8* z<={Sixp-JuFF?^EIxISe;Ty}P)vmIfVM{Z@u2|tBunQwuAx@y;LiUg}at2a`4)=Pv z2z*k2P~&kmdAeMe81BL46qc8tFD>-dk=(>)LIcqkA$LBhfrol=5hQU3tc)&DYplYs zz$7FIYfd5~1bvv>8~&ho$M`k_tWT4K2KFUod)lfukc+rgHec-0^envjNA5idv-yZq zFzuCp)Q}Py<474#L3EtZ9s7at8!39jq`QLE46rLcxCp`ao(~N)u-U#0)r8(Q)y&Bd z&}f6I1qERuq5FMRT>+vh ziRV$}QX6~5Mf(m&~U^%cCHR6&Bf-MDNXQ;JYH`0D- z0ZBv3h`olE0BPO0D0aA7+3 zR(o)KeT5&LvKK%8m^k7dp%br@4l&icfDoVqHF92;N-i9-Ll=ut(;Y`tclI}QL|Y}- z6n6yWUH!0+K;L0mw!bhSXQAG2hc42O)X#$l2L>9B;y!6;K{4|@jx{67UmLa{)OcZ7T6;uvBeM@aJD2N&u`}_Y%4~_gHr1*iq zNpdbE!}ddGHU7r-%-6tm5*OH*-g|Zh%K-wof&W+RlZ7|k^ZP@I)eF3ZQdhhdHfx=hzBO6+E*;cGg zq~X{0ASKojFMRDm0Xj_}i!M1ZOR)MX<~y{BzJsrM$3h7vnPB_ZbcwlKp?6HQYIdW~ zEW0yv8K8n_FK;!wvjaG^#E0+|A?)4Nw9rQqv#}$b$2^Z%e_z3xLTBw$$#*OgKuG1m zMVJsmxhn6$tzNjdb02#$t9#*rmm^T|LW9|Z!X9|uGDesIO>3|JN`6vy4iQ&x8xnE0d^C@&*M#@+i!Jw{k)HWTce zdN!VEF@GfLIdZ0_4JpbhgFA+nrljZy*qQh5vk1k@9@Xmsy4$v2iq7;|`?7)|)Prm}|<0RzJGn=bYzL@JF zHI$JGy_{(*YEC{2oXi$ilOmEwLKa9xjidKH=tz@5dv%Gb24c{LkRJuD=gY3b#1sKx z!TEpG!y{Br+r789|8LF+TqlzZqRZ(s$lVwK(ET6b!J+^FP2Y-=;;73RzFoPzq17w4 z)O;Yy#^7pFx8qTxvNkp{k)5zK$X9e|6Eg7}W(DV@l0kHSj~#+2DgdO+?@$*KM1+Qf zL}m6JK~MsACYb#VHan@01Ryrf{;v@JR|x-qO>lI{6NTTl{3)xvvn@*=jR_zTs?%t| zZ1{KrBwkB53)U9CIou@1Nl|b+TTlRO6(6xoJ{=l}4k2sGAHuIuK7^;*{sOT;yVbUk z1z9h5L>Rn-wyfn|#UF|QVgiu;BY1H}i;$z$T+mQ#Ns^A|AR8l$re`Jsx^4BrctA zg`Vhp18B9-t+z=j(|@OiqI&>|m;`#DN*RRigBQnCYdkjfz}s4(G=9H7x%4nbDuQx3 z>?GC(faydTxB@MIz{VSQ=%3K%$j6C+NLQ6G2prY)X+P6)elTMWm?(ZgWK7kVE7w8+ z@5Vt7D&xzh^rz`heO6zOymMTznE4TqA?jLub~4eHih7Z>9CxvJ^MU)?Z4@0%6L9o> z=dHT`Bfx9SxVf?hGQ+O)<04eL^q<|Y_J`h4F}_CXzPNn4O40MKb)Rc>qSELkEB(eUc|kCz&1FHk$<_dEJM z?or=BTR@dB2iK$4P`g#*4s`}iM0V0aXYZswU&nokqKsy$jz{w@U>D!%FA6$u*u3nx z^~uIUzHrcM-e=Wr z9JCy6BQLgh5|Bp;1gACCAcugeWbsn>epK+l9-)KP?7qwu#7zuDy(kIt0l_uN_yJT8 zRwAS95}*uSZGuj>+uF{yJljg2wo_Tyfx-i*kz!1>c)L!n=&XZx$vIJt#o2BnO4ZYy zbCd;C5LtK^?R|&syp{Y#k0mchFnJdy_7@bbJUW?u&-oRq*i(RKJyDSZ3a)vHpmJa0 zGCIatndrf-h=GCq9QFSKXkj{>^UMS7%D3GDYj*&ll=_g~U(oH8CG9EGgrTqfFX7se zj%O8;pl1r+0?PS2pYeHm9&ER0cp@eIPkKO`8_x%y{gP(&X#x;67-WPtp)RHVnV~WI zFBa`@ftLUj!rdY4IKax(ZFUPH>5@j(CB<*H5C?nCI!B`DuYrcF^+%P>TTNf~%Lar6 zk4?hL8ui;&O&rGVgV)A66B=!rT2J0J-dRVz1YO&0RBrg8$)#OWoY8(7#n-xk*A2A? zbdUgC0P7s@)+!)i6^Gxv&z>Et5_I-Zq4PQ9_j8~dZ1u`w$tXeWNOD2KDSfwHva5lrZ%&jW8-q_*YoZKdY(4$&T7Y5Hhn|3_ezEf^87H; zJ}JR(iP@yUDs$`$J{(v5^ z@ziX9x_6A-SJh6>Tfrfsn{`9r1cFx$7gA9@+w^`SJcPVMMg*sn3$tP7bUSU^t^cRJ zYYm68`}$^@GEx~yi7?TV2$e_?W|CCG$P7ge9Tg!@gdAq5Cl#WDFo{tPW8@TaHlCi& z5f2R_rgA>ye3}gNt{eTY|8>2e-|Kom_{iLQ@3r<`d)@2a`?r2;Ixw61Q+}&;=7ZUp zX={4|sif+r?WjiZRfU2pf9dd_ttaWRezSg7>BwPT0d=}SjBYU9pBoihEoP<3J3Z3=Uc~TM477Q+{)zT(nzd$o5Yc_f1MxO&N%w@{E(_TtZuZ_v|)uj#8e5d zQp5xD8}3>u!x*-e|BTD1b<*puit(}}i#w-{*4eFb9zYO0xu0~ldfL`SoqO0&TBdSw zM{Y^yFY9nP0wEbOomXW2XgcyIN!d-)XKgh250kK&jo*-?0`pUKUzwWjt)-;w&SmM~ z!nS$ZdUZrl`KODfeuSnwJPM$@9H`2F%IWx>MEfiMOV<;lP?fBMt}is&Ki)N#Q-*A= z6<-vPbfq5fq}?y_w!#z=uRAv-wp&ea5|%7bc-4FW^Ny;hbMYFDJLiYbJyoqtFmTWx zPcwi2$i4i_se-mUx|y$CECd}6vqrPy4ubsG<>&tLec>D-r?;Cpmd*k0Cs32mF;3Yx)GPugw6nZtYNN~7)Ssx7Kvm}2 z<*&WYT6?^If!hF2@9nzr&svIMU z*BoGK1~Jy`-+wsyLCh&z&-$X~0KPYzyH|fHbDAi*H89aC5cJ1YSDpzh>Ti8K^irT- zyPFRvwU@j5=?}#TLMRN5z{sK*t&omUR(0;&;ntL=TsD@x?WgY7lWcvsVXOt05oZs; zBr(61OXhAZ#aeck{$AQsI{L{3ZZ%~_YE9IOHmM!Uc6`^B5g$V!%@`zeo;;^Z_2Ox? z1X@Gl5A9PbiZogj?d|L&Gj=*)ps1_1nL9g?clP7MJh)L9g9G%WyCWY0r+-GfmHd2> z2l++?rdJ^6I~lOHYBK=qlp1NhC83mpphbHscUEs0k^ZQJWf@&gx>c}0buAhjmI32v zys#T?5hoB7zU2t?oOg|nz>`US14T-9{==^UHtgnHoYa*Aw-w4QfMqn)HyFAV7o=X? z{lpU%F9~uG0>9Oj3F0-*9=3tI79F_w^^+f+4QAL}7h$!Ug#W^NMpT@aRcWc)p-2=8 zdN`-f7isspuDIjnBT-pN+kkw))I4=>yx(A%c2(S-++kk1m_B56_rTSOX+i5%7jB4f zfF4{PcmlXGmG7%*06IKk=+Hmj8T_lFae3GT=+H6(I>34$ll8^B(l7vSxcc)T6YWjv zIyIXI2)=ddMBm?tGY=Ki&VJ&y=otp=PvI3`Y*#PUQHd zS;z+sh)bm#Ci`KeM9^*46{iISwwK}6tH*x!SyOb7DwwYO9wOg5_L=dvd^p@a34wWB zldBd3&`)ZYL7lcfk1ae(?G^Mp8R+&`mv&m4XBBnQJsVGktfah|@d$R_xS3g;rB#kX z+bPxzB-Uyzal&KqH94SzdqF3Ac06lrB${2C6ODji^#sS+!|zdI2u$&a61*H~#sWe0 z@!WIC@On%iF5)XLL82C!1Q)%SB|{J@<>vv`?uDH6{b~&7r5Q=~{`gyMBgZ3{COz_- zGm-w57~CcCp+>>a`pQ^Eff68W3&eVdr`jUSc?R!I^wmrHpN|3gLFsTUWE@?siCEe` z7`^0txjPf`6B-rmyf~B|xR00Vy=M^+$FM0BVLdG#m^z0IOCywoe27`~(EW}OIt48^ z?O=HsBFY|^D7nwgV5aq>Q{d|NbQcAQ#d*A0-vgJl@>7?p+2(sX9>fbSawp&OUmWEb zOp1-?z*{bblWv{LFS6Cgvd(VCvRa;RS}%;AMbiymQ_23q7@X8fRK!eH8ZRdzK-CU- zKEl8(2v2>0c_yc za5g`Pa9K?2@9lJuAb(tgBfs@m(-6WOFeO}OhBbYgI)KH0Q-STFr9~aNpOECuo^A;* z8vrtwo?4S_rU)=dI-&ZzCf5%#lr;4dxDNzC6kz+|d+zvMZm=^%w;9xl=~4iYq(UB! zo-D-WGjfABs5tURRUEnGm;=VhSfAcasl!-mFjl@6cRbTOIROxVhA;R*V*ZS- zf=TXJigq#p(W81mwO(GjW$3zXW)QKoM@~NWA9+8suLof$7^^QR_j<=2qJH^k|8S%_ z#o1tFn_byt*&wG?zdr!X!H^_- zAFTnIF5B~b3<7=q4o4l|k@i*>gQ>s8Qh-^gc#>Q1&|Z-J!9b#Km`GA%VJHzDMy8lz zOM)NM(@8sQISSK+SlHvwC(8YO=?2t5U9}ayu7MxZ`ASw|yT8!_8vp~OuN-nJODvdm z?V?iXhm*S>>=z2MSmP*&P}s#?9;7aTw68yr?MVCu_}VX1S13oz`GKiC*NhQ|^xo;a zR4XsItsR(-~mI=d2-ygEl5nGVyMPc!p5O;fh12-(jUh=EOMg z^TswqlA7f~(n|Kj3qq(Ip^)1#w?)O7X5jezK0jM!{{=_PuMj6kZN1ry!)~caTz#ol zN&|fWHaYicibcuyk6_PhR+In9Ix9^es8%E@r2N>2VIfN>rOiY~SjWF8XIgfqev)hyghNJNphBM?6y9|dDy~MnkR)`Dg#q6D68&~yyi&`m8 z7TrLk6a^Qq45EQ*D5da?ii@f*uKJ?EFYfh4H@E2F7hi6R@AbuyW-*#u3}hE$_{Gnc z#V@zTkJiQS^~Ix##Z#Ka1FFTdxy572#S_`Z!|26x_{A%T|5vv!=O+X}B9Z`Pr%oEd za9yol^;owdsorPHK9XAQ5t6Lf$H#KwQ*2r%eBP^Ayl-_!IVt%1G*-P|h3GU{Wn)gJ>9EUeyLTFu#&KE%wGMHBgwZuW3 z94bZ3Z%O`V9Q#VLyPa{nSxeDaC`h%YXSSh~^&vg&SH3Yx?Fxo6qwvEF^F+k+D#@rX zdRPG@3No9dmSnL;oO@UKRas*=PY#ecs`3LwdBy_1Uoy4-YOBlxyjLkWYim-gLDMXYzv@7SzCG+y08_PYxe}Ru84TZqlYo7t5`mE zJ5OP7@6cA$4s(iKDeuIxUPm&Tc&DDA(H59zt5{oRG|Vu##*L2o0^_T5{ls{WND}kA z#mTzI4}1+JsG`!ZsaQ3Htwmv;(>iA52atKpKkkm<{5VL;KTaYO@xinXqGY1Oo14fC zsZI(=^NJ0cAp#MDol{M2O^e@Dbo3>??xEjRfT_-I6__Fqv07|EF_U0c%abRo(JktZ z0VFR!c9<9na|L^BP!KfVkxOV6mbLcw=Xqd`P*5a#?KblGoYt<Bh~sudsk;I=^>VQ%2u@ExPQdKT6$&#!zI8 zS@V6+2n19*gYH17k}st@mtfzxlJd_cNki{*L(pjXLcn+W;Og(ch0vR!xx|6n3%`RL z$3$`D45PFVM5MV!jtT^l*2yI7@4x>qJ6ZCE?^IVl-7+{b8L(1SIjG_wnm2f%I(BD> zf#vSiYpNANB1tTctQ+&nieUHr%pscsH@oM*{Qj0*am!YmY3~Tk8$FC`s}2MHU~{&W zL9-^TYnvU7!8(Rovcx$ZL(Kol`*)_(e=>KdO%b$O=t|2nCiRe|LTGs?A%u({+~W@c;>2|5w)}?6(VV_ zl|j&V99?c5^L#4ep z#7c>R$i7QCQVD;8N)=l`=@68=cJ^v=$lvYCm@v#HVie8!y6Jk5$T4gwmK7`#xy&k< zC3N2e*G+OPWE>{^#6>p)kPT24$g*ryTxu2UEE99$Hti@wnYo%274!85GH{U)k+Oqm z2*0w>_ib_KEg9~lKNv4=z3o8eayzIeEU{}l46IwxZiTsy-UDs_I(7nSivf+Kk2r44 z9?u^r3|i{@p$AJ%6o;)gZC&PlgGse8AQRwC0j2dLWx-MQ{OY_PB=XoNq|7|!9% z_jd7@jzswIX9lU}Ky_#ia5vklRWA1Tue5ks3*0Nc=bJmmEeGsQ&#Q*|3BV}dtIxD+ z&dLBhxvrW`9+P|e=j`dx&Z-N5$LYDfHS-`4un>L$wyMtd6p$uf0LHjJ-s^Y-JNFoX z)MP)-6cF0Fz)f7N$MI0O!I3AWx;wf-sXZ^$5lY=p5<>bFR$h0RGXl)we6z zDc<*hXY(U2zta<_nNkqOa!2NWq9Gau=6TEHjTN4)_8Ev~>ww-iY5?$(o>f{6NeB|`?(7*?U7{8> z!hZ&se7KGMAELJU%}Uo+D2*0EVW(I*-}bvf=E+cfX*|sL!vp1O@ok7zA*LmDoW{_3 zE^Zx`SOH~X=uj3d=FiBBlTRNr=mOQQQr}T&w$bcsmU}>-ZFqHuuyuwFXY_K0={o;G zKW-my_#CA=YW7JyVQzE0 zY1$%I`+pa8IA{{BE}Ju|X8R%u_(4_{s|zfIX0LMaYvtz6p0J5PlHB3(?=g|E1Eewl z;@1I<*T0RY0-Mvd0ht(HJ&a$lp()rO0Rd^Pi52HD9R2_b;G_k@;humQ{W)oJl&5Ef zP@Sa$y<;-q;yc-lMo0n9IMg6P=U}Ap{9JY)p)s0b<-K)9`2f>B2pdv=S@uv`$7)+Zoe^QVXm53#6F#8_J@gAHx*orWls}isfdrl`9V494P}JW`bp4{zI_^ ztEv&fk-gl$a*U9Nxq{ir9yNF0Q061PJ&Tt`>KKaUuKWf>t5Or?5@x$M1_-9P@)OeW z`{#U}P*5VcWy9v7Be)>5V1ycGbnKIN`Q=8l%QO5yR#&{ATN5N{_+>%qCS%LlztRC8 zTQHdu%Bc=2};Cq;5r-RrIQWwA8`8(1WvQx^^xje zW7jz;E)xKc9)moPU3#xiFZjjn!9qkY3_gA!5R`BZlo0Sk&3OfaNBWs%6HN#zKPB~- z-WA9~NU_QcP(Aj}?jJYbbotgDwr3uKg3h zV`T!wTW@j!;0~{wb}ygbK0>Hic*S+TqS&d{Tp(4k#|=Jj z+uMrOe(_ zL1x!_TfDiCfmfQ-`2FQyqIJNiE{!01se2uPZ@C9pb)EVWTdR2yn2TQvca(J-ZR-sP z+xFWrZzhnU_IOa%iF!(tAy2DybS;Hfowkbq4mAqI_eUU^qq-vXs$hnb6{z(X7^unw z1Jg#cIl+*IQ)chsOh28-2=t< zk|oUd?$Coiuf=~98ww?dtL}vu5Ehhnkj79s_2Y@#+H5Rq&-nr1vh3+eO>n!Y#ePDk z<_cWh{cP#_0atARa9Ira@}sl7dcOTLQ+YmxwOLZmaBg!D)Ik?pkF(Lsk6}1pZDuXg zz;*n(X*ZBpXLbSL^Z(dKQ_tW9DB2RSy_XvMd{+N2Q+O8a?B)$ba~FW?SJpcaYOZI#}{d7lM8 zoVNsTu~{k_`9_#;7HW=z|kzxxpi`d1v!ymMzKcj_5o zkp8~#{rUWIKkoZ4`0~rxYp`dXb3MUKOrU}A|g>z1Vf34t~nABU75Ol z6?h_{W0gZh^pr>m{7Tbfd@aSjo>uGVBF)*-a-u<4_>R);D->@)PmoNKnyi6rGB3s6 zqwi#X49~oMn7U7zgx5X6`Gm2Mt_gpP3}DE7l1gZm5bAPR~UI%F68B+%_GE zeWtfQ_iU_n+tnl<`u1v!Ub*ET01OTMP+g&gUHSLhM~%zb5D|&6{@>mbeSrLXOq6r| z-|PSJmPqx^zsIx4|NiR#c>5jn-x$ec|33ad-u^#^{ND-vKZg9@8T>zn{GS;R{ojZD z-x>Vx82`Urbm4I~?%kg?8q43lL_|LC`L-Vu5xtzCr@olqlf_X_37kd4exF?-`nO+# zhwarOD<@grPvieHlaDr0^+oPxz6LW16?pm%wNHjui7t<0>m4CM)O}5K4{9D=weK*g z$Yj-xO9?GbmMZEicBl~Qj}jvO09f1Q*+@OTnFo6xRrm`BK@Hxd&KYJas0X zzD!E-+qL`WH>Yn9eGiFS*m}qO%FZ<@TjBDff8%`GX|HC>j&C0m?zTgtt)G!Q-67JQ zY8n?NBBIw*!QNn#7x;sYWRcq zzkiW%+K+2@@G##&r`2xS>^U?Z%*1a`T_bX#-2*)CJF~c5v=OR3X7l3ruA2VlJPjGs zf8X`?1xoSwW3jD?sIPvTcWM=9Oj~!2?7#D{F5fY*GS*)_WT&l=WsBvz*DR>gJPYSz z%0?ofw;6eHr>Z`QSBUPsU8f=XWfXP#{Z_LW4Z^NAm#)U_k+miwRMp#aLwwXCivau3tuZ0GZO|0T{+$KR|2sHx)>5Zr{SC8X^AeK_B*o< zjcn12cI1Zc2wNBjS?7FYGL&PXED>&zm?3Y2SqN?torm92rGAgmgo2o~qL1$SdE~Bo zS(&&9Nb&w>9#M9QNE9C(S~8&i7!6V|7OR4AFefEDE>)H^9zgy5(=7Djtb;!(wNeT9 zyyO-IrbJu>1<@5DA*3+~s0DxGmtLtKj>%;_^>?Ii|Fhb+7*BKI)57_awYL`VNRdi9 z3%f98-V_>Z3yRd!eq0h+7GIVEVYfz^Ew=R809=7X?;++Hmqq!p3R*D+8vZ&jwEZ~} z0bw^b-fC_R{?f9`i@PEHM82R=i zZH+zV8|(bXu!X$D*7}wvFql+)mpOl&1-1G|enCa!D`tJag$GoXAfQ&4Tm*#%yr3QI zzc&@oi@Sa0HIdX{`UVZrw_{HhSB}rRBg_Wl23Gs(@zFVxf4|52`|a|oBL^UUGPEig_6&Hz(742yb= zo5F2S;FbiwG6mQyQfZO@R`g=G{op{_DavkdxMDjp;urjZ#1R2ZA>8FY?P1z^U~src zL44!`1B^rM{CiZ?#Ys|^x3~9BjU8KThxxMsLD#CM0kA_VwQFgLYz0n3!8qBk1J zFP032Kv*$bzH^>9`-#nl#-)C)&E_aFA_kdn2P8xiSTk24Bb6B1u-lP8nGM3}D!raC zPnK17czoZ{NU~d~b`VNhyeX{GEZE(63R8l?N-VzCV`ZZwUyy<1;L8`$P)PjzLo+n8 zAChMQrD4f?$x6+KIs6gI*IUD{%pBWMSlT)oS+(+?e>3<*tp$5)>5bghDjd>%Yt_c- z#%X@CTME;fa_&U9GL;Qp>3ZELj`=pNKpa$`A%2xQcwNNT*EiT2EGGedbD?*zZ za(0A)9zAyF!XS}9J<-Wcq-IV^pNww0BYJTQH>;klF1Kw);NC_92Ptvsa~=zvowuiW z8oFJMG=Lv~i*{bXPCmObi&JP-ohP&>kJolSS;5$1z%evRR#PA~jO*>&PzY3$% z5RvZ+OA(NxVnlDA z)EZm8{ji1AHDv`b_3~<^uYq^~Mypbm=U91GS(4y>QfPq}n)-9B1{1X|3iI`CIP#H- z?=iNeXz3|?sZSvma-e8c-&w10n z=rm@>W_E+znKLO_r#$1{2+ph_s1wqWDFXO8H!A#ULSHl;BZi`%@?5ylIh9ZwX;xA; zt!<5~0#5Jp=J7CZGcU#5YmbB2>vKMUl{-#pBe`@KY!d~0do8i#tx>D%s~rG{*wy<- ztB=WWz%`s&sY>)uEK7YlAvtg|4hs)hjVpkhkmZvfhrVsDuUq<0zC51!TSXY~PXz9X zLfoaVdn|K81qhL~8zLvciT&fb%t8`mY{fN}rJAP6 z%d;7A5{xVaC^1{YulrQm#C4q9vae4?Q+*Hj_gUnE-5=4s) zXnAl)!=LL2xO2v%{W~8Y2!v`}{Wk8sz==W7rQuR6*j zU*%#a)fE-w_g}ewM}!_9drf$7ZdxU$jQ1KKHxa z>QRCO%_HB{MOd?-a&xZrlb;@=MjX#h?lYw4Du$R|dveWJ6GB${%?g8nK=NEdp^9@S zIKq*w$-!%1ul2Eo+x{Q^g)6f$Q^ZPcdE#dAg;m86H-o~0X-~Q^cV(kX7Xw@Jj z^!9e|{WiQ}1G&j<4Dz8EN%A}w?&h?iaoj1F}+O77uUdyJ+4wIG># z6MolTXQ#zesT@b1s^={k%e_j6DuU3o(z_Sa$KxYLBl~nZw_g?RN^7u7xK!aBN5<2R z_aoDQIP+Wot7Ni|S}n{v2`oiCbrCZ8Q(a8<-fSK|Cjyj`D&V{H9*rAW70G@(QqpIO z$>S#vMs0U;)qPLX%d)(Ot2g7tTK{IHO;7Gs)bF;&t`-Ye?=78APfvd~KJArLhtOr; z6HA4iN3EKSZno6t?+ZC%x$B?b+1%2@fN7{(axfCn&GG( zYLGthG;3s!Q2hFx_az{Gd=RP7zw!`K*ic;3-m;-hlQWX4G?dlAfM2DF6-vv6-D?m= z!|vheCUpxV0{)*sZyjlCZ| z4D#_q8Ir{~_v(FdmlQoVJ7-Spr*SxqIu3Sh2?~CPynmuSBt@e%G*KdnJ3v<)yC(Ce z;TN%bQ>j=nW$Gx!n}2kRkc)lrj5l#8{;wN!`(#X*0vDIQDhHN@yZA%NkqWvhP0suG z?nxctS^M&gy+?yXOb(r1%Zwo)LdP!;5B`vAw_dr7)}svhIBR~Z;k-HZFu}_-k1E83 zu`wFMor_(<4f-b>7rRHS6C2`-KgL7kr~cmU;wNbqIkXaHZ5#z+BAXw9j=+W8&>$J) z^Dw9TCzs3b0bDaRJVR$kw`=#qq`O_Ds>j1eJ+po4N-37qls!*XL4lh`#RSnvjc2Hv zw{E5#9b!>`Z+!7LSFF39{yJU$*2kf4<~pmlTrB~0>K`IBVu! zt@0j^gVZ%Ybo76c=n{=jY0*s>e@_+sPB&ZhJpA>ZhTpJV>s;T>!+@`=vnI_Clkc{A zjP6QMjkc3CzkxWiiq1pbiv*F-oLh<9PA9H^6SMFovPwB35$yX`!Y2jpMZU-+KuWLt#JA&2JTk^y`okD z0kSfkzf)&=PuFD}KD3T{jht&smwI$kC(|VJF&#HWmAn=Sk=vAr=cSN8JUran`#Fr` zF2JJRERMU6rt#bbz`nS<1Rj*`p`%{E9*p~HTGH7_HUWLshi#oqN+!~xmX)Q z7<@Nhi`~x0|D3%!|I7(8vTHaJ3tgFuqC7n>Hf@_GYH-LuKd{mw$P9#sq~ox z)hDMr+h@DQ_Ivt%mCa}$DH9F~FCaoj9U-$VTG9M*VtEk=zTP7R|0ja%{ugTlT-m-M zlK8w0+LTU98Ffk)lfO;lRqGV5G4@r{nK{vT%ObeNGHw-K+U`(Mc2nbrFWyyh=Fn_t zF+BA12R1W!0+ZNbj__|Cg-d@cJW#F4H}bBH*Ba}m_^AbE6{kTckInf2s0ynPvMJx? zlf0H>cX}samqhq9L2Nc;JEV2W-&!k&9)x<_Y1i6mSr%vE)Qf`RcmpYBL|5}`OBRcC z7`g~|gJhbp{6VQcjA_zaY7b^yCyn=QTz=aJjJ7t`%WM*cVE&SB?ls^XLNx=a`f|8) zu7o`Ml9%dsDgg}97PkQM1%H3wR?B$PZc(QG4QZP=ShZiB+79xaIrN!vZP0x#NXyk5 z8tPa)K_e&Ix*w*<7zo{qeeH0`G2~o-`@*?1-xg1d+baC{M@oyOZoHGMM+nO@;e9md zH5b`RbH2GCg=GH5hO&^^*g41&q{?M8b$!#f+XeVU$mCZ~Uw0Ls9OG7`p^Z`hj zvuvdOMeVHUvY*}JB$2J+_<9;UvuaNq>%^NZB3t6C1%eAXp|hJ=G}+p=c2sgnA=uc9;qb;)f&h$>IP|w z5^7>OLq*0QAsBv_t_?KSTWCxF`M)K(I(0s>j00VBT0V@MWDS81Dh%ApZ8piOs?r`o zXJy*=3w}Gr%bC1`+yC7r`7B~obNp3jU5|A6Tc$_g-B7 ziXO18`DC^Iq-;^I?ol|3PkBHPZpahgvkZ#T=j@^5x!WoczUVZ4!zAL7hd`_6;DJxd z=D8v4V45{YK)z6Z_A}Y$Q`L?i*v+?gcy=LOg_z%yRJ&zg9;di@nV0~fpI(N-iXgTU&Cg$4zxO{A zZBEu2wHq4xLd8eirVhcNANB)Ly`%jEH5IMlx7jHm>YZ+ABmsysfJs}*)X+9^l6PvL z4l5lxSj`((^X43T594|rGa>$-TcXQ`m47bG^+ZGd2njadyK$wxkxs&*=gvGkoT2Lc zn1N4aV2n`LCqzzP56>hW2Zx7}HZ<-k9rux>o?qk3MshCfCc;@UI}&)x&jRxB6ytd$ zxO^#v;O*sIHdbh%AN-EiaaW^B)W@}I^eJ7l;O_vcu{g?Ga?-rXe9ZS)wOKTwHQ*3a z)@kUy-FIh}C)0J?D zOGb1E#GfHUlgZVr924PZCc-=juOoxo_(QrjZq%CljPFH?`^@U#OGSWw1B^iXt45`L zTne;9@I*nL>`YYYt^FLzrjnShHXAv0 zaDH65YO?dIYqdx`qYAd?#gK?*M$W~MG(QKxDSMehE4ncN+nU=b`b~9}-4FBj+V{x=*;)HgC1Fe$@B@m_nRJcH<}X&m7hF^roz8X6hVx0Mk}>e1D%7d9`St^3e< zhn#S(uQOL&Ot_mju}78NH!`t19m(!zQ;tlTiZ@JKOrn zpkkamJXZsg>SxVM!RQrY)Kuu#j6nOC&7aEGQ4GYlAS(J zTTY%e?nulW1`+Nyu-Q$hwqhTzpH{q2qml9_A=P@pD7M}A)MCJRc9~{`w)_PF?ko$V zj5@NuyC;$Mc}}p&)azl^g3k)3imcB!$7kC){+2Uog^F?2*!?EriuDyuMPjZdAid^f z#5@FIVZsqW0mh&I(PuhtE!z!SGCMSs1>-jF79nMhf3P#PR(Fv6{=^Sa3a2nz`&o9@ zg$GjLQMv*$Q#dJzF)-4CQ%>eKV<6$Ecoj%Wcub@OEnTDuhKDTtHcx~iem%9_FD-@) zt3JAQ$=xkIAJ=3;C9n6))a`3_Ik-GgqozZvQcSXJGO8%@V-zCMgrjzosV@WuJ&pzE z8pGc0&>Iafv}P|ZM~+6#YoRLHnF&MV8`3iTlWN!3|HB~+UUz{7oLSHEp`-cu!f1g; zltd|%wIo?e#q+MjNY_!SgE>5DauOm2)OoUhB&E5m38 zPsP_#-4xn>J2*L>oMYq!Z&qSoZ6B<7i(gbjhWL^z^28Bpj7!aV_3P09S4VJ`m2E-T zA08Ya?Fa%ihf){)Z@f7Z?V|5+-npzG{W3xozCg(f{4_ASy|L!$#h#7$Ukl$4Y+y8ym*0pnuH&9`E9H5o{%*4GInp}e#{*8C&ze8Rq}>eh z<$F}J^AQa=85F&q>yPfQ=6zQg!9q!QpGBa`gbG>^gL`jSVG=%_jKb2&U}3s?@&erx zHcN2|o?rfR?w{<=7Q5$3TtSj*utM0VOo(OK6wZqBa7AnjgHH&7Z1q#((Wgb zX?A<0ZU}n*O^G7wtI((#NTT<>Aa}VS-&K>w>2ms7zTGPcys zZ@<$?Kdqeo*u! z_QKR2jMQl?)bgma%{fcB8t1=?jCsTK{L+R#7JHi;YUp=vCGnjce1Fz(1TJ@f&smTK zTtj6;ko4@n&bYO2%)B)=?5;FdlXoYdN*?MkdCk>lc@ZBON?ARTuQcycZY@V{w=j5t z<(Qbh?kzp*5)6zUFRvyY?xA&KBRv8KH5&1kw}HTWjogvOgwmO1C=H&n7Jh8NzTvxV zQ59!2449e`=UM9iY5}g|ckL#4D2Tx<7}Mm?7^vMb!?mPj-$5QR8t6#7p|tOJ|<-uQr855BR$5s|OZf?xFNOQ9s2;lVL zd?T*ZSu%_!oMVuV`16zO^(ZBw_WUI?W__&=Qn&ESV(Ryhab|;vjv)sLplrP2yRqK0 z34My5=NyB6*$)h0%VnwP*XNj%P)KD}T0S;Bf4_TM>6pn?|zP(I|Zyl9a|Mil~{uw;vTk`DiIzm1!_)Io*a8^ zdd}tnIp>5M>P@zAd!^Pt*GzBr;)4#A31vRF8;kie##w-fI8frg`E1~ew^yIC^Ldp% zO;4_9;8Cka3!;a_jF5&8tMpmCpiAqqbRL6Sh|jrDGpdTEgvK>Ok&(akhW#f>;x^pK zNc*nL#0#gDxR&TXv%vNX-fLC7t`9;n8PRBPBZpZQaoiY*rLRseH(HzB&Ecirm|0o~ z?y>z<4{#0+<&I;50T=mM8-B^5#mAGw>N!sGE`tqGbLJF4rx%`rUxd83Cp`o599KUGD+;G=3l zKp+Iv5^7%OUaqx@yW@9+5>f_+Id@V3gRFcM%a^J zb*%3UrHs#URs3qtO=H@~)SVc1zs(cVrh_(fSi@-L`itJW=-$SqDFK`;xN@GeCD8hL zFBSPJ%UOo_b$dp$ z>BnOW3~p25p$Wtrr#okA5BrAd>}|hV4m+AhS9egUvn6@1ZRaGs2+K_i?Vc3FoyGA6 zW*`rwngPm9|C#HdGyX8);Z1^}`vwb>T=y)rlcPd|vn3RX^xH!`A_Yx#NZLfx53}&K zE+yoo6_ItC{h7>w4fZY+LX}2YE;A^x0Z68oCjM z>0BVMhh|Q<4^XS^zmh8nv7GuA0XB)4MtaH20 z?~!j@j9|q5pu*jP_W~U0rKuZ`yy!h&=_QVzHP#7YMbL-bCPLAxM!aGU6d7;{tQJb* zpjSLgp&*$3$|WII{_-%~>G_y+lgka2A#8;g2ZtAZliw4r>3>>0a2 zPFj0rs6Q7K0sJ#G5LvtmGR=PHvk~unGcna-k`3CY4H=Fp?to)ZHHs%3{Mp!`#E# zXD3~a2C$K-XB4DH{_O`8+9>2vHnR3jt$Lqo~K1qr#MLc8>ChX2?HAWZ(6oKWRhBE+lE4Dzz1gqH-=}D)#M@rh9uHir5H~=Tuxe@oNHHRIbi#BiVed z$^-59NGNw|40-A{S_EWK_3eTx^4GKO-hDSW}~X#1mGTG70Mq38v$o;v}b5 zlGbVqCCs_U>>q`x)4~mV7W6N5r92i8LWaN9sirVhwHUqQ!~4(E`pS$`LoHRO!`Rq; zU$#wq63SV3_tt^kaRLa5!MImUasTiNm4(o#Bzo-9swRNJtv#$v;peLYQG(0Gbhv~;? z5aLU<4WIMGY$^U@nBQj<79i{=%#CX7%;P{LC9J4IE-Q6cb+1K8qtldIb!i?%+tVC- zB}baX7e6#mwYI+AanrRQowUF7V-@rI7Iw@IpdX?U`L&o+JTo+oRd^jVbbO z{QdQ9tUY_|X1q}>$BHn;{h=_4ScqF*&aW(_Dco6gyJfrj%}P}jr2e=2pJws(jFF&+ zpYJm5_{_{E^A(#%+f$2Rm`u@uzhboVkei-h&83--rgxCmTG%AAus_JlY)lbabx=(1 zNJ_2rmcKy{T4>=#gB#6b?624jFxM;wB4KgQfFn*mbzR0~0x1CYp21v8R zpmyjOTNcf=0NF%kHZ#3!>x67jVDw2^hP&ce%6UZpFb$@UVqT5>@+0* zlXMaL4SR301~Ebr^3k)5q}OSKGFvlMoq6(ZQAeJlLun_Uwhn=-TN+`ZAk!x#%0t`~0LYH#s zeDE5ZL0rm~ML*GA6!MmP5A5%NfVWFG5KOgV`q6Hwd z845G4g^`n*bswHS_vXt+p+n#I{br4`NCepP&5O<(2d;#reG98xT~5*Fmi}>*Mi#%x zh(`Ao0V@V2$fF%mqdI~8PX?Qlo|_@`-YMC)UUhP2z|9s%SebMXR*kR^#RZ|*u|f*d z_Z|sDHWs6Fim%xThM-4rJ>R|=Jz(Y$b>ts`2l6+u<-O|OIi>S!u*9d8B#7;gUz2wF z+XK@*a!vl76pWm_6na6Jb|lm~XDb~rKdER09J1tPL=kZ>k6CDZG5@4{+PM&ICHX3Q zECl7SI+Y+V1kYJU|KwoNv(>Dac02WTKeHT}ij_osjp)~N)#Ue@Q;)_Ho^ zD!yQ4WV-w&m!VwFqO2^KC2%&`RssDXQGE0>A82uCqS*6i*4_QC8uMi5@ys>2zl0pv zf7G1|5Vmj`^`L|JBUI!+eV^@}TZ!#{JUk@qbHSZA=%+Ku?K3s0vBa5S^2mqjiO1Zl z{zsxIZs$hR9uXq_oX}?L` zF8;EyHTjt{`ys!L<725?k{>Q2#Mk-v`$fSn1SM~Bbr|fOrpe}lfPYC9000VLbzk=Z z%HQ`IhX*EdlJ>7ZF4#jW)VM4|ABRwp8r3!Hhe+0c2LzALBO7Hlq)+FSywBols6;?`zy35f1~xLq|R}5 z0M*G;gwTZbNqDjkhPffE-G+<}RBS3)I!R&^9?DQnzJ-&JkPvo6duz0nn2x*p>Xu&o znVZXm+TcxeXn$|S9@5eC(4Q^L*In53@@M89anpeGeTWIoHumBoZE%?@Ju*DV z$~lEhm>-ugm=$V1`?DMLizZ+x7i!a;z*mT+Pew9j!X%!X%(%Cd_uO;XddV85NNQz* zI|JDm*7NUlF8y`?bLMXRl&RNKQ`-29YhbAduYX`SL~p}QzWpD*PjlwkBSN9X7E@d1 zsC#lL{c8WoLNR^C`gE-tBZ>XWDubmR?Q8|975!TL4N8KXW@@`(0=C?GUa}C24*`yD z-x(MR>&C9=Oqsai?b(%Mq3dT|jpTj&nMqNTq7Nlzq?cCSvinP09{O5MRcs#?KV`~_ zs^YXCfAZQ56yIZAgsc^ap{V6(dmi!L=0L~6_qf!+dN?(S3zYamy=If66$P!eKI*HI z6ElS1DE5!Y$icss1wXJz&Ya~kM02}=25LdLtUNTm)g zaFUxs62OHyxR4D}IbYrafnByLoU=Y!n8yM{*+-n?{G7Po@_0z-`x!lV>m;A^y)19< zyMl+P$0nH@_GmP+Ns$iYBmI8wz5e4^IN!mLzl1D!!q0zXK9%MvCgDX)sD~xD980L> zq#rvFDvt<3L1c7)=fWT3$Z=FN-mBY1f24%!K0#9V78>FmDh$pT3Lfh;Um+^`>;oav zkA*ih9IY=ZV&E#N2FE&B6==hBTuy^T{N*`T{2tt+&ljJv0<<>`iuU($6gp=|F6`?kppJ>)PMr%p+1KwAc8^F?|D>SipgL}4S50AWrJlC5h99yXfA%UEp zz}b<-M5@>nOPhPs>A4t|=yI-~pNLWj*}=lnEQ3`IGoN+s`!b@)e#Ao^d)0iGq`@vn z1a{|MrEon1xYWi`Q}4Oy@bjv<@AG7KI~|}2vZ8b_~YC+5QQ%`lfPd=4)By5AhcW1nMFOjlw_wOn&)_sQg# za<#GLC8`Yf3rgtKfm7dFs=tL!pdY<+Z?C zwRvC64vN{;w6dzgmVk0&_Q5Y<+S-6<1XApGYa8dY-RSiPb?0-$%Sb4xBPKFo_dOaR zOjoLr5xvPU^t$AJ6(_QffxWma5w{)7EeZW;Cm!45x0-fwa`c#HC2ywIn^mi4WI0f! zH7pS879p<35j#?6EA1$GHNC7^ef50u%B5_*P$cOd&q|Vt2v?)^YaykuxsJbDDmgMj z{rt<^3(4`}at+;dQF<#|3rIp*B3UqZmSwC~QP)6$`Q&r5C);pC*3|=|lGk62leXC) z73`0Td~I7K@ScoM8m1I&9i*JB0L>*-(erN_eZB0I7eeBX z+KwJ(gJXNf$l|{aNS?9OH^b*o(zOt^0*@ zs4s%8M#}R8`NggjMX3vYi05asmE>PEq4=lZB;kFuK4xAZllU-I8CO3v&O$o%;THvz z1!W2snTHFoYL)7i+n?->7#A7L&_%Jhvb=V>QD8ol1(cbAMiC#!3%s1r*!z24Pu%L? zx)Rle5W=LYgZ|WrS>)*^ax&+_DyF z)85sQ@WEz%v&7!%fA4bRkUy*#N6TE}}0*pA}>`Ngkyl1f7tVlh#qWSnDfGW6G4K z-O^(^o)VgGJRbFi?`By^Q(LcUc{>zIQ#OG80KAGxb;zO+zBfUmb(kVLb8?tCob#Ko zRm$rp$S9*HEJ0mzrY0SfYkk$C^PTGDogs(kE;uvIv8Ec7L@75q+|T(}e|0Z;Eb9Cc zP*qbvk8!~IkIzk%JqR?LMMLdud2AAiQpWwE#5q7LDKwv#$4F3xG>4GN@3ki|3h9Dc=~w3aEVkkMAK`Y`nDIYsdIwiaV&;zz41(V!Xa|$h zJ%TJS9Lyjbdz+~zy)Xt73$7t{CAU~tJi{QIY7t5gA-?yAb?1faHd4PWOmRpg@;^@m zaGvB=zcQ>}jk;=V>3`Ny*siy{{^{wzlx%6^Mi#G$CgctXsR?F5k;$xy+n-FO)=0nN z5O;_@?k}jb0d)D$Hp#7BuS|_^Ws7m-F`~h+lbkg>K}0r+Je@F1d`_ z-&Y9nqrh$EDdgfNvvyBK@d6UL5Kxe^XP1i|~l&w1l>b{XGlaG>j$Hgi&;uV53~A+1+l3`85ij5Nyv zlWRan0^qJv395NqPENz=#&v^z5XL|RloQos9y(Z#hqo)QjDLr6zFF8iu3jv(s(k)!YUt1 zZinrQk}pyz2BX$0QUCUv=vUEQdClJ`bFa1DJJk{@czswOnIXF@9*q01_+Okf<``2g z^(VGzTnngx5hr7#@rzD+Ko^E*G2sNq9yn>ca-X-?+cW8lL!S3?#Ip?b8_MO!)GA(u ztL9wttRSP<^lWmM-!$)&PA6A+2P3+Kh6cK?Yf!cg;-Ya8!~*P_nssU8OemxfwX2kk zd8~@gI?b55(_5@o5^c>oW;uOqorpb|?6N^HrT*=0t4~O$)|iu;;?-IQ6PTJ%{EK^% z$UySDL8>4|%;u4f@vd;*!mxo|zd`%VZu>;ae)}xaH1Ct|(OY#bP-2(G?hzoH9J2cD znY!v3Ie(k6a>{0>i??y-Akz-T^d`K#_b=Ekzv^i!R#U>4$;=C-yj)#j|8dW9oUNd< z*0xT4c#0j*Up5mOZkB(r)k8_ol9?Fmo-cW!$WaiCfNcf2x)RcBHxZQ`OId1u@k~>8 z$8*_v^-5{yNq(tTGU3)LCdaqE)r7>fpP=llrOdGqHrgsT>BKvhnzR}x zNEmMGt$FPB*-}0M5Jaz&XK5orT8I^kt1Kf+<@pHvQF9vWk`6|5{XFaV|C#%r&wa6U zVmr~2caQ1;PQ}_LLeNx(S`@=`c3QPMan*R0c+g8i{3BO}Z{6$^cCXm3~?r7CU9psqBMWE5I zIma~h`q#e2aIxQ2;or8h&QSJr=p!!}PrB?%v@i0qS9yRb=nK2ZkIt?$XF+m5imfn@ zb-h;LDxHPWZMyc~HFS%I%0%2NM}{%2LpR$xn7#wP6z2xXe7i3GY?Lumkt-+~PzL}q z?lL;7qn!+{Vox+l61++u&JUWG=@9KAI0Kdw-}R6W!$LX`mRjqVtI0`1LU}l2RppOQ zx|pobKyoJXLnz#l3v=Ptk4ut@4S}rANKGZ7kZQ(w#8+5MGSx!;ipJ-S!AG6rhZ&sT zp1@%ARULt^q^ytMv__?#ioYyUfvy-IN0*CIxa0d`?x9gMMa^a$h$t90QaWjRj5;wY9LFY+>&; zGpKGq%aVDD)NB2rHFC z?~?acOmmHY!!Y1-(;W*L9uGd3q6PYb^xf_95#vC+B@YrbA@o8^J}ati1V zb}6V(hm=4VQk6?=mkWZ#fP`X9`akDouC?@8?-GAR90YVmS!XOrz+cOMnf97W3Q>oI z-{3^`$5BR3#ny5Fd@E(@W?ivu{2IVLH8vd2>7(jRR|Yv93vFY;g4S0DPJnVuY{!y5 ztThoKrq4;;*V|Yt2lE+m^uEgoRJ7yP@SE74)sn`VMKwzeg9tmsP_${&H~j!5kgf_I zy=>}&wPg#GNE$c)ai@)(V?esf!5?B1t(<5nZ()R@&Crkd#a6eySDeoRh(7=*XKt{h zJq=QeqN$PxT2n5YDZk45WF*l{&fbi&g5Hcx4*N0Jlc7cST&IRd0Wi-y$26W z&O>g^V|BSAD`N9rB&pH!0_B;l%bKAQW=L3Uk|%yz27H)S&cMee_mE>BkjxxKBb~}r zq|Xl~?#}q&ZBF=;ld1KFMzO3YyXEu_oG#`)=ylC%HfJj)_K7=1iDU#aVjyo?=G*z6 ztvz=6{(QxetE6in7A29+Z(%9ZX3yVK$o$_;q#dY1UwQE-OW_dc+I%i!{qsv53G?IU zKTLl9uND9^$OmctaVJzvS&1*_-fdRie#pP>p5j%U_&S(LpMQ67tm?e(;OB}w*Sw{` zMg6>McJ}6p*G1&&$}6clL$9tW_BB~BoUgZ<9=~*#(PzeucESw_wN<0tZLHGsj-S}W zhi6)-glr3Xc{!1)eeN6HPq>~`I4)~X@qCQU);-}uF?&OCbwTc8VJ;5n4PLpyHu0;v z*74Uk|5w3+t)k*6cl6mCh3ijL?_74H#PQOE_&ApJWu4ruF=R_B?PP?=DsWI+?IRH_ zNUU9Sg{mG(H?IN^U824`=1x6}q8yk&3nf|C*=M>h9pScuc)@w*wgx2f(d4CDU{`53 zx)DPu!9wI$5!}UY{M3{rs<5_O!HnJ@ylUv!v`7-Y{5kbozY5AZU`6aFe5`8ZVA0x4 zNV)T+(XsMyerJ**>%5gXqEf0uDDiEpz5hFTDc74C?%WJ0hQ21kk^I7z-3h#FxVf1P zFVQBpis3iURNYP*b`NH}of!kSf>r?$YG|l)yz`&x;&~A$XD^ioobR!872HBJa{dVS zVps$q2}75FFn5|2HR#@(;9FPOF*beLuuqdZ>YWQ=jg^IOX9*H(zu>1&%mu4ESOD4z zesV;VOy2HO1G?Lqb**&1O5<+|kX+--_Sn~-I%*pQ3Xh$eul(zD|L8V);>^Gx?PsW# z5#3|S4>VnWl^2adlzy8`ayE9<@tG4x)8^dc=Tyr3TG;u`xb4t)#FehC!rmJ|*DA_- z=fg>1nmm>v16Rq@@9Sty+?M2svtgAc_I2eva5;N<;bn+1iW82a;1xjV>F8o|e%3Ty zoNxYPVD*4LAF2fDlg^EB#V;(V(#1nI3aQ7;i_V0(tF65tuJ%})uo}+ic@K0fV(r1{ zvG!QZ)dPp?Y#ORME6;$YK)@@~Z`>Du`(-*c6~Ock-9@dF2 zp#R3qCkv>jT^7(oiAg&7+eBPVs7;+00lK6S1lcaD>i?EclC<x1Jm_6lh6FS#F}fV2fF zfxQWwXrL!rq<8B>p7})Ox<~4!SMjPxKj1`Ndt$lHR=hUG*G0{xg2}g1oeX zkspHp!M^pFZ_fTOm3TIwwEKg71?P7_W=Lv_vKAEox|8p6wcuF@=d~Xv6&m?(Kmn>R zjjOL3bE1NSgJ-<|Kf2yBF6y=W9>xQTh=hblcMRPf(#*in-AWEUbfbVYNDM=Fmvom% zcT0DPbW8sS&$)lktLNhjd<9qRYwfkyj<26eRy5mN`76;=O;FzL_K6o7C4x(^!a?1t z{jMav7&Q|Y*~)pYpk5b}-V2e@%HXJ*LG;}mkZmB1p2l|>7hzJZewq21zG$ix8+A&> zNK|@W9~Q7}m7QCEjMR~CZGXNkk5&XAstdFr1ms6lOaFs3QME#ra(;P<$ebb`st#PI z^>Fx|Me0fBgOHUs**Bj(ah?-W#ymz)`{1)726X@v`u8tKx8HUeqpXD#E44}9X^V1m zn{N>-j+;@;!-{3u}@ ztIEbPLr0%WzLrQ4&SXa0WSc^=Huo=|j}9}aH*y>L^^h(>nKBqiI+9bl{9=dsPp}iU zLZRuFA0rXka|sv1Kt^TIo zGF|MWVh}G8{yTdC!z*c?SE~6Qy;d38dLEO-WIU1Sb;U+yj1UXb$^%jHG(nAjFF>`j zmzDVZ;_`*~fC3EE&y)>on)XmXCFL6!cyAWXN4TvB%F7m&tZd#GT;VhfDMPQfT>!id z4+t1UeE~-YEc^M5TOYlH7!K;J2=o#BfVRZi5`^c)9?aHlczJ$U3K!J;?Mbl&v0QHP zxZj{F(CW`O<3Q9ut012RqI5NJb?trgDIn2LFO9|P0RUX6w&etUwTes~{qkUQVg1tT z8j}3PKEHPN4rMLlc6Uyki;!Oa8)e5-4V!QDM3FA3F>E@{8LurwpaL$a_)Js?vK-Hx z43z*y1Eig`M-G-I;Ip&S(|T-QY~^#C-f!vA?hnXU>1WhQ^64DP z4=&WjQ;e>Yxstl-bx9(kr=;0r=H=O)ry1uKs9O3pHJJ{sBT75iU%~m8#&cT?XyVF7 z{$|=dQ&9p!mgI}$~Gyw7Q0GIdUP*5G0J~JjW zv38SjYYr( zGUxIgq76z~{hpM?F9DR-u_cL4(01FCf^O4jo|8E<7ckoDG4tb(*~R`OmZLsmD1~bO zPh{d&3dP+en~chnW!d&&4^Ssc?6f`tMg)5 zP#|yR{UE9q*>a+Q= z8CIQt@oSe#`l@NbX|2IIf~B~yQfIIL4Sem1-?;XpiTG9CUPIL0!$w^*;AD1c7UUo#&=qA-lN&9BWzHm^2+3<>V#WX;pgJ!`_q>R zi5uh%1ovquuZe#xXd)#F!T@xrPl(+G4|5Ip0k3V|*V1jop2&5V&J_}Lj)WO|j4dJ_} zYm%K?NHMD4PA00?21U;V{>~0$fK0L*PU_yuG+XHnPVAX4DP>GN=b0?`RGV=`qa@ZgTn^t%??C7WsV zg|V)f64M)y(-RURVBGRYgNh{XOzIe4ae4G;LU`LCVf;ym>1-0`&*K(FrvXQ(n^_`f zEJ!t1t|y_@q%~0D3|DZ2W1m6&X3?O&HaGT!=};HzQAUo^dl!A>%v&h|K@)BF^6a(h z3lZ%*()~ih2zq!Px%kIs2?IXLmj60}heZS!2cGRaUKfmKmuxADQHMxPDon~S0*8}t zmG{7`xD&`YnnMfl#!Z^RksBjf_|igkf1pU`zoOlH+@32er9}3&^78u3VDH`@@7^Z0 z7e#TORW)73*>K;{#-nltJp&f0I~W~1G{z{m?07UBA9$r7-9Gea-HQs2irKezu6N6a z<*jG7%7Ew#t0Ku$>)IK<;$5Xytm|Fjc*>xCb6fI3e^A6%3{6N%gk;zf%qz z&x3CK{Iz$0n5HqOUnjgHl&+{_a8Bw{`dZ#OH3Q=3wc>e&auqsE;Z!AYX~Y{Lf-2{Qee8^DYr3UHt0CF@9(x8zunaTxg_6vudDTA$?_pa zy>OhWkW68$t!ylgI;YRkP_d{e7+>64$}y8862$AHf`)8AG0J4MK$mifpZ%@&ba(2F z&;#wi3(GX?iDZl-70R!#FPH~l9KaB>50u6^{F{y#(qgq(d}QNSiXG?cr|tm(w?$OL zoXe@4B-aPQY5k$_ba;zpvtGDxMH0pH3Fg6s?2`@WF74l-9a)@~=J487<^3p)nL3%$ zIO81{qJ;fzheR^*4DN{{Tq>DqbD6F;)6IvxGdJ@(b4=xAe*X1Gg0SYL0eqm3(TpKJ zz6$t3(9D)Pt57U&k3XtRcK7vb4w?LZ0z|LKajW!7!_~!MUK-Ev?>+b2unGKMx#xpT z1JewEYL(1NmD|MPcNRC_O((NLVEoL`&CmU?t!sngamCpNHD2HnAvGEzgM;W@XS{J7 z{L6^z>B+(4Xn9ppYCj=LIs%8y5vt=2_JqXO;P?PSv%>gs?6eBRI}tOh)YflnVaPSj zmFu5b@n)K-|8TV&r=dx^m%o^|FEdq>-sCDSY=@)qxqlN)5+Kc<^|9#N&-xuhKxZ^l zrc{3G_D(-raZJA3<0)&4%hrX{+(uqqD^ua`SivQP2Q`F|}7n7sshwF`qcc z@SBu$KZM-uITEQWp&Dk>&27DCV7164k)kjMMf;a2;RjO^MCqB>Jk9w?%uy3b;SlBM zUES)oPgBqtDa0CLewc33GNlzbJ1TVezULIrD#bJ4r!B{`tse>KM?`AhyY-Wr2YKn&&dX2qRZI_zX1hdMIFlc z6OWZ?X~B3W=j^JZQzE|ZFz5AJp{sE5>=)P-D~TJigkTfHbmnVCR`@3 z<>ohfE^jwC&8+DHwJn2mZ$?z1>C#>#tA zjzU?QJA6HUwsMc5G*gT&%Cu<1f7iwSvA{d0-B0~*JNrvnt{`babM8YS8eZ*F z8ohtzq~s0E_GC7^8w7LQ1;937;=u!9RHUsF2IC5aAEOm@7zdYzO+*3)oSYeFx)6MT zG7wSr-tngk(f77me1AcO8*)S^oF1Tzzn7N|%{J9`72(jU0ew%7P=W4;bC z79%J)IDv&YksFeYz5d;r%+^$zlrTS5^)yy85vE-?VRh%Y$(TfU_01{(k>4#9q7}Zk zQyVE$6yT)y5Et7je+DjKqqB$Gtfk>JNRO3t(}GRfMKm104ARhi)MSI!c7uV45k5U1aYJAr1P@^uD`@o+YBT~Y4y7?&B* z)vs9fZ_unaBo@*^KbuU5E)**CKqB{S;8?1>@XB2=L>J80RL)_W^mEpDW!1Qr#B_5j zs@mG2rE^=Gg*6$`B@45n|D?>a$HI^=NeVry!YPAa0Pz^0c0#U!PM6|W?EyHXZ&@9B zk>gbcor<+_w5B5iok!2KJDm(NxwwUeA?wMH8LeDj?X!m-|6C;jC*XC;-yE4%%bG(n z9huV^vRzc9o~sB)>*-8QXN3Il3}4SKqzDOrMaB@m{))ArF!P^Rh9!NG%F!ixEMvUA zLua=e!2C(m?Ieq+T*$TWUF+qH0-Y1>hvjSU^7Wj#yOgc5JoyWzq5bJWIKJr&s3!iO zUzD=`+G@_?Fm55T9+)|7pTEyK@qPaht0@7AxJ@Co7*@yISr6&#emk^4g-{7fwQOae zR**W)tv620Kg>Ct*fTj3#xO+*Q8Sx$H+;e0*3LCeN5A~6B6u*8)q*BA*J~ga;x8GJT z=8)uUN)*8_yZxES`7|mbEKFh*2#U1URU*=)&aXo68X0^)8?t|D_!8qq{tgXi4n#$f%i7RxG3s*I5*&9 zRG6BvRYZTXBgclXL8fvai~%o<9ypf}lLQ##q#XYoUaBnnO|wAqodjKCf~reOta?_? zNMS^l`BaCxiA-K`aTsr+N`eC6>mZ3NY-?87O3ipaSdZ*sOW8c`(;T2>ardK$IA_0( z>Y^3s3**Zi_*CM{DC2n5{ti*`^wntES+=X~ikLQ;;JExLJQp9>h-{Ak@7I1%9=MORU+AJW` zz3iwAk2S~VC2`y`#wF()juprC0aXY%r*K6_A2kZ*0QDFkd-}pjMH>+lR&csGTE>_5 zKfFPIZ?cLqiL9^BSko;2K<@K^s!clPIER@{B4&~1HL2K<-pZM96MImy)li-~PAmXF zC}|vYEaw{+l)`JTp~2HWEfB37V)8Pkj(_4*(S8olgF|ck3`G`G8E4aH{cW=+FS=`!`uJ0 zi9bD^$$^Fjzz?o-Dl*YRV{$8iCzd_7u%xJj)DO-GuOd_fcZuXOJNo9SiCHx8}n=WbwW6bN71nQILAa$gA z2Z1E+68;Tz{;~egj1-CGx7BntCDH5l*Ysud7|<@Q7ckhO%vxjmu#oMSq?Gb&gcblh zuo!>}gDD7aAnPAhCrT&Qp%RD0?Zx`v{^d~(D)>uD4 zLvohVE+xqS_*l~7enC1v&}odZyN8C~v7wb#zPj@0=~X=)jdIe%geA&i!#3S--hU`j zK0ZQLnyg$_1oKfs>{wWLs{}27Gj>qZ*NbJ9?#n?SIuIQlAOzw&=%k;XkWL*>ZM8n^ z^=H6LgNa1VsQu=ZuPepMIgU6G)r{7=hev;#K1drewju&pks%2wSzdI!^VA(T z4T}Ue7XI(5#;V* zd29dlG^~?0Pr93ymNqXh&usE^t1Dpf{DJpDgnv9Df|^o9iZcoYNfPF)e{F635+jiY z+$0XHu!@8<_!JsmVJxnb4IakZang}06ys4ELsZ<^vJU-9dqI@He8FXT1WT1f39_!bq8Y+;FF5FmI~L8?}oZv zP2Oafx*qF^Ej>e`H=0(=QF6WI4wUPOF`iamoD(75NA0rh(4hj)M?zEHRJR>2ME~xR z%|?gMC)#$Ma?WSJkK*Y6+R`k`T`rg>#4p_Y^pv6zHiLCYTmQOA5^hHIW;iIKb0F{+ z-?Ku)HB#!q2A&eiyeI~IX$_bh`#a&sMKMwV$I^y|+`>P{<2k)V+yawxEpPp(laOOl zg<5wQ0Cw9tsxQfLpUX-)hJaTxCQ1(k8U_>b3V_WqmV;LGlIcz|=IiA6N;3R{58eKV zJ8De8Vte*#O9p@KCaH#-j45Xh_MNc-EpkigcbHSAZV|s36E;NzA8>!`rkGswH)$wC zmhy4ic!1cgH;;w}2=Px^#m41f@+V1~W73NlbCRO=$>thwepZ6n-F=ggSh!?Q?QVFe z$-CkAaejr*IWlWBZVouTnU`1lzZJZzf6nJ_Q4gWbjpWc#gSz4sW4`o3JRoF0Dhg7# zPnA^euSIE4$yv5|tSNIoY0Bt37lv8}Pv^Yf?~7!pVw4&FtgZx0|A=v*IbEU6h=;)r zW*eJ<;nl5z0DBf7CQE3|Aav3(#Hj2CmfxA=bTV+x{`y*|wN(9!{jdZDG^AM1G4f-1secSe8{nEc-LU;R{tQ+5{D?A{k~g%|jtt}; z4yH6Xp;9mh8pr+F%12h6Mwf*VWroED4^$=o2`5a4Pq8DILWh5(KW+u#kdIpO@9W)l zv9LAk{F<|)jSk?`N~L>F9P$&GEQK(F^}tGdyn#mUlvhaVMG*FXL>XdDHPH-*%hV~y z{Ubio>qTNyz^b%9V=`zE9oUk+`jYOkf3)ThXlt_314cUTZM(AhWF&k!%%V>T=l1xS;3CRf+G(Iv+yx|HcMUyiy&tK z`{x;89;N9Nh6IRU)J{GJ-VOlpuO|wBS!Q%^^~i!;xMA9Kzb!_xlo2R zIU16v(py)N1^?m+x0Oc*>^=t@)!zcKYB3w9hpfvL$!ZO@n!_xXu0(XoJt}y9iKgp_ ziEBd7BZ$z@TzMSf!i*1X4l>UK7+$Q8(O7(`xu5scZVM2leU*pyWCvbFs%rS}F%IHh zj`y(3=%Igp`w+zvWaY9(q|8`M6dGuhBrJ|_!G%cdhvbhfO;^tYZ7 zn2+_=k)6l~W1o;&>t0Ht&`S5*>l}7R(@A@SC4-d~N-s;7k2V8JDL}e7S!GFa{`S~3 zvn4?mruAr+R3T@6__-|LY>rR~TFGBe^vw+*vGoOeI6HT`xLrr1K_&?ayJY=grbMYa zUA%Z87BKxtECvjfnibSCfQn10P`%mOyFg|BIyMwh)5TM+jAL_&wC+}4nev5jM|{4_ zZVk!kqiEjJDII2BV*zkgw+z#AVp!R}d?08dnr^({FESJ+H|D_bY=0`p(kkLmAgOCi z%UoL^eXrS(EoAh8LYq|*>{Rh+d4p71{T934IZb$8DEYr34Y63eplzBH?PZUtC6tYNkXds zu(J5)qKcu1-E4UH!q=8fJ`4tY4G-f*Dc%H*=8pdLEA_!27K1G^&6Cz7V_)ZDdP&=L zR5J~dYXa4)i831^9lSv?gIk%?0*O1EO&oM_;5h%`p;n3Suv$Ucf03`SrXgps8H&F% zhb!u7^7~XR*ziwyaWUp^Jca+&0u*O6hB-bqSg33C_Y2`8^}PLNRsyzJlD^8-GDy?era(mSb*wKg#_y5BKQTC3Rj+OZMqQaJmibksfu-xMU`$Pg&f; z&CT^^Xnz_N1KN*Te(y`3*ZC=w$%G)Qa+a6&x|?E=_CE+eR_1vz{)mCn)6Q1nGz=#S z6E*@A<1niTY~F0 z@nwiUAU*5vk+CV)uvAamK_J&1AO@-ws>K;PW*YmAtT&>Ri#zl`XI|7?6oO}@_j^;G z-tOLJze11A+x9YH6|zCGtp?S#W&=9{6`9AxpB!MJ;18JOTh=!_a`GjoKbi#{4|Du(Pr@5$V@^ZX z^7CExAfZ7c%Bo$lC?)C3#`qPki+2+^5kqesIWaNL00O|cyH(Mb1hu&yi3@Qahj%qv z&1}w%MhGVMH}nDKrZBhg>lo(FE?j5f zo~1dx4J2(AdSbLF0ynW|FA;It)7?!(0SuN~&$S_S9sI7`l1i9fb7iM*1%w`aNt=z< zDZK^<-i_cWo`CfuqgZCJ_lLM@ypr$gq+D+~pv83pwi5?+X`DGgQK6;+U;-rJKQb7v zT6(^D*qx~AM=)x$%V#Q!8u5#6cNoXnW;#Bc;G<9{v-oMzlrz*4E1~0g}k!rT$8gjh5o=njX>9@ zn9DI~Vxi9CMo3@(Spy4$^NM$H03H>4uK?0R+Oi`vrC%sNh{#Zzy?*bUNANr8_3*QD zemIU4_X(4E35F1T!K7Yt4R?y|@J@|<9BWB)4gM3gLKp#Kr~*X5cZ{~*N8ke0*N76e zCxT+!-@{!Un^tFgS(M-z=CYCTeRwnMf`91H((9@>G@(BaVu>y3o$yRNMIh z5joUkJe^6MQ`-ODGHndt`bq`$ebO&u#P1Dm_(+O+{Osdrl|d>pi%PjSDZ{S;uQ^_J zff@~n;-J=a+iL!a9FU;0I_)u5u4ePtdp@RMATm4_@y6@8b3I6?j2mC=9maWyr7^uM~os_1D#}Svua7MgTkBgusy- zHtNiIQ#ih5YL6w>q~6$;_(iV`PDazQc#I6Mg7sG3=>4DN15H6n%Bs1fIXd*V*vFS7 znYk3f0`GyVm&ANRJjYXsC_-DJQ2FiAw14PY1EuPaG$Y;jlRK!6+E}W`Du*EnYbX#^ z?293peRsLI|JJwFDx5^2x~Gg2=yiUK+DXG3^X98%!1rTfYzC&v=^tEBUXw+@Cx;Ks z0dK#;B|TA2(zyD1C6muv`e7-~rYH8%x=(LqeU@`(`)Kj%2ed z-mhlv2?6@>bCY*!b;K8MJpyn{R^ab#UYMpbYdmzxam8-yd!XX>3mlfyBlxDMSWy-b z^?$tl=*as}*4HVMW?cLY8myU8_XNPaj$%_{(ilMN-;>KC`qpCw*9$GwJ&sDnU6K(W za?j#;To?5gWl=5R7u}y*O0Xr){~q<>;YSF{Jn82H0g(S*qUg)Oscs>V?lyl@7O=x` zWgf?eEvpMCOvSTLl3bH250JZC7x{h7fW`_kT=e6QyOH=Gnw2ReX(Akh3#F@@DbPLr zT^!XJ@wElbvZ>Hv>)0s(^ddayW26FM20rh{?gqOOn$9Y92|2I$U5$5k@ID@z97TnHcQCLF1? zY*XvuII&CDrPGKLkl8xq+{W!Ue$!7+4E#?$-2V=eKpXH1BbHCg!^=Eghc*R*sjIIapey6L*2 zJ8n}XLt~eULR}kwmew`|L;xQK?wn>u$JfSk-Ic>b1s5oxr0f9{l+>d z2&Snl+JeFWtLGy-*tXb4ntlaCsifpvolkUDnDv*Ihu^mjTgHmTq9VO&&=NYK?jRIP zYxp9Cs=|&sb0RzFAhhht<0ctbM|4X6l}_c~Wl>cUW9?kV?Wb_lr(2gWpp+(Gylfkg zG?^gu2;XFOv{b!oMLzQ+&^y`cQhL+=vBxgMDJGnGSrZV?o#a_8nT$|b&=6aMp*pT) zPdFvl@X##~f^>OvI85<+Rdh!OrKDawhel}{$UgaY9IvN*gcb0Wzp$9|vzlc58)Yg~T9MS8li zkoT|1aHJ+04BqkLa0W;~lbw+)>Y7k#I#cj$tQjTZ>U_PXvR9)2p3j%lQ?d3JOeSYznfycuc&SyuPH8!xzs))Oc|{y8}IF%vd1G+GZOe~RFT z79BH!e

A?7{0R^iwPaoadK03b`1KZPHDDu&y5Jc4jEuOByWpjEVg|Tf(@yD)c{E z-yM3ImXbuw6T+Q_La9f@nco`i{ofn?JulKD0t(ZPho^nDp_QGN238E%bp@xW}vs ze}Jt(%zB*7_i=B6jtx8xx}~w)knTPK-xyI*bEbDaZ7h+JBiCX24Anda6Erj^<$LVl zrg8>}{D{f#&$NuDHWsnA*&cRxh7p1dOERCxL5=Io)j{oDwY{i*1+#~O$9&PjVf#+h zn;dctJPp0X8H%ed0`kT^`>zS40-Dqcz?Y4PHZe?6!MmRQuSl6Hoi9ekZof~Cw$-^T zeA`&-{DfHLG-f_t<271O!@>490bV*577m0s+M@~|_luE{_wGYJ`>>*LFw=h0H+h6S zllHt)eb96=*F8U<0_=IJMpN`D@y|*1^fR)g*c|kS36Jj>S|Wdr#lDg;&9IGHM|-I~ z`n&D0GL{#bhFq@ROZwj0-u9x4nK1qFJ{^L56}#D-YV{*Y723J(=p-Zb)?1lW5(C2X zB8lsVJ+8!1G2dDT@;y{0yujF>Y|^MkMh#D3eYDy=Ek}G%A$ljh?Qw;iRp<6VY2LTI z{V>e@IlUp-~nj^Fo{lzqhADJ#Ql-zHbi; zi6yqX5B^*^BJ{D|QM8yI9AIWE|Fi3tLJv-Vjx?OYN61|T%{8lYL?@Ea5U-XOHWU~aC9b( z_k~N2-7Yh#uap`yh-jN%cjPaN`K|ae~{&4pd{?GgByB;Mm=n>-o zBX={xKzNgjz%4n}%EgdVdF!83n-}2$FTR=)L*_Uii)4FSt~v_T3ffVT2rl@qk`Qz3 zEbq{R!$7k-!rTvE5F?k;d?9l&Nsq~Vo>Rl&54UDHK^W=x#}^wrXMgRO1_5H<0*$Aa zyD>nXa)m*CKkC(4Ha7gFSKz)`go(OjiFH5FtSy2h){f>42FYBP+l+l~tv3qCqx+>I z`n#4sdtg!MeXx7DKsd6#LLm0wz5)g~Ov{-PEQm{)>(k7e!gqwHK*@-wf#NIRJ0(V><&ORe|3%S1i=RL)0`$k<`9Np-A)rA|bBheqLJls^?mcewHUl4a6)F2i;4QcIi&RC49>+a^rW8WUQ5tg*0~A%;zKOVcjdf zw_uclkoKfIko{R0Ss<}a`RgLG!*7!h4D2N(F61g#XHU*^ffaLw__S0Yhzby#?Xz#!9u;3r(f_49K8- zNoiVQ-#@iZ1=aAfKQ}{@O355&KKEpPce{L&mZJCPmueO*1a3B3y-~K0pQ$>Ts0URM zM%!V`-ww%1Yd`vXLPhL!&v$l17cYgm=}4s%ak1z)P;6v~Qzb|`km}9Sv-?;-5$nyQ z|6VER{KhKzNU}WTar+OF_R51UQlH+5BXD#a|2dYo!(^kZBp7FOi4Z*RiQ8?VZiSA4 z%P_1kwmF{rM;(~kk8nRMLZb)g1xsIUqex3aZV}^nT(r0&JIE&o*nW!K^cmQPZP=tw3dgy2vEYB+VI8I;iJLC8A zMXEGM_#31qw}U@>J+@S+4!_3MjCk!1##_~z>&g1TJ^KJ*LsiS`R{JbsVawbb&TZa}EO$asp* zv;rX}GM|I4L#WP;8>3ch$VQ%v{fS|OQPy)3x8cxQ;ce13NpH_~v?pNgW% zE2TZ?Huo0nNK}me@bM&?ZdjFtw7!%e<5!|hvJ|1!`tfkG!r+xK_m63$>2`|9rQ|C~rNB@#v> zIz8$-^v?##_#5PDA6?mf+x>*<5pCYd$huHH3yQCuj;%2@zxnGxK~HY6A>d$S~wCB|!T0Si|*k z>C4bfiBpK|BHN}jW#k|(HnYr0v7e|m4+=J90^q1F7Y8YXzkgPPr2=eJ%9@~&z@kg=1mt?CUHek4rRM6>Og z4x>rFVkSb{LKB)>?F6)kA*dSyZxIhRM^YcZ_O~Y{-3(aV+ zu7WgZcP>^t5-zM6Z3EChs}$H15!Sw(wL|oYmCrs@f!m~ zQ$rOjQH1*aZmwYWS?y~azdqR4yGt;N#gB1-!-lG%l5ux?yDVTw2HaAjFZn~Cr1G7M zgzOIFpDm}}$JC)D{f*hDH-~#L0Ab~wm1KzEA=Q@~ld+@wbE0Gue1m}y?6|CaZzJ<& zgf|x`x}?i1T*X5=K8zyWoNgU!iG$*37z3Ep@oj&+&>|=2Tk96_x}sL2La1DY$t`pI zm>RrxKLzAW1w62Eo5~7Do{ts)SFQbIJ zITyb8Y#AOJ2Ymn?ZfJWCz{RNFe;UJW<= zWFNBaliMScUbfwp$&J^E6V|%#Q!Jqze+;n5X8SHp3~&v>|YITqBse# z?F0aVD@kBSFDFbFy)0(IYJTn!Z=PRS0*(D8-9A~*=*WV?_c*85@~b)-vR&z`B>7Qx87}?r^XAMkBa|r@DM}G+occh}U^8 z{O#eigUJS{F?%L^(N<#xPx-K?yVZ4ShbqhN^ZPUZL%3duh#uiC!=aSsN|v#>+uPqs z{AeP!)m{mH30;>O9j1sq3!W3&GuFpgVSk(Y#e}KQ)h;8q=)%&x|fQZa0Xc}Rr$giQGxUQ3txYXds+P&hmag&Ei)Wvj*uUcH#gA z?=90@Hc(NG9{V_5I>=M#7gZ9G*F?wDA5P@Ajkqy%()oIrW|(|)dHBPJMQ?jWdd#<9^|EPoh%g_bnmyrZ%iWV! z*Do1F-;)F`ygc5UoQ$MX=}k%IwrjZh8Zh4};)S)z(-&_xcjzds(&V!HL>3K`9uDnO zTvOd8!c{s^d522G9&_>x4|G-Vo}gQ0;*up=Qoxm1uu0flk9PC!T-^8QorrtVb5(&4 zhaG}VpiKenU;3Sh9GpzeFws}~^fcFZ)h{AGU|Q;PQCFuquBI3r(}=PW?-NHFx3@!H zF*aLQpuH*ni3kK8__IY!C;MrEjum%N3pa|%!q$j*?U^ycQH91CB1R0%j<#!Ssq3%z ze?vUB!VN`4p-o`Q!>rY>UlSv5Y{L622;_MuGNe<}didxrxFNqHPEWcu*LdlJCn6|@ zIWaG=bga*>oeDl_KgvtOw;l^Ch8P@RC#P+(`9{dlIw1)M?^%Df`(qi(4F0zrXh_d8lgeuMD0SEJc z-GK;Z8ag&-ZUrraNJ*lXuOc{F_rD=KJ9Lz9Wh56k)cflzcWd>hNF0K%7Z?&VdOqXY& z#sk%Bwn}=kGk6eBy0+PSH2q%4_0VHA3#hAtNx500ws0`lyi#KDlzU*<;e%ib35Hi7 z*T2$c?^UdzjG9Z*)kS<@FEpoL3MN3XBI#MgOo|GCo<3S)z@quzbiSuiVEyS&T4-w^LdWMc9=l(2NW`Al`7Ph^*#I zxz+Q$CD+<_vPU+G@;yX8jsXv({8UEZ(%X-nI!~V%*xSp4N$M!4 z7+8Qo|4FFi*?L7(0|&(2>c(T`Zb-gkkIu2mW{VO%2iNzh`0CyQ3ukeWW}K$|c=33O zWXx3e8ErBQ!gLP!>%|9hGik_;zUPc=))n?u1*S?yQ+=?*R!pv8PJWXM9E&$Tt2ZY| zyWX)3i47%=7_7!)miu_h7h7xY>koN-x0dSa^)8PROr@6Jh)A$+m9tm65VU~)W@z#l zz)ffruW;})lv$wBpwA1OJ%rvn|jz+ z9=mphRwb-c{k?_)?6`f!Zdv`4fjH@ZwE*&Q{y`-lBDA>x9g77K3*j*FDT-QQs2??< zbG6JL>h0%pm%aH#x-&+DN1p=VuMo1iT54JNi+Q5k>4|3zb5i_frwlcRutijVwxmShn!^ZK@C(&BzfpMkNx6NdjGmX+<+K zj60{l!Qy&maof@Vl^fd8_d7XPl%eI4A8SZlvIv#=i+D7P;}a zq4QQ4mcTencCD{E@CEJ-_SNKk3SM@Z)a;>D#RqIs`DE^ea+tVHR&L#MpG4xJG=uxs zWSs($=4yi&k7M+rJ*@z0O%~Koh;rSYRc${(=!_F$OVQs>>`Xxj@A$L}fTp4+af)p-uEZWIR|p#;WufB4-lkAlCXGxIYo@1~}gVBI>7Dw}??tJ3tB7cK(O z(63Ng(T~%Pl3tuAD3dGcQuzvgB8DeieqYdZ{CY~BN}ov!6n5h|l5*3+CUdvCh#aPR zdpk7NC0YDjY2Q6R#|ZxQY1l@h(+zA&5h)V~eZJ(+T~sUi&(h*CKS%G>7%LA+WR3yV z3mEh=cMUlAd_ZNT6j8LhZ8W;snEp2lh1f+gFGrqNvh%Sn79DoFCx=56{EDv8@p7d76I8|iO>%@@VfRU`3(h3DfR-CaPw`*3?uR9k`#sS$-GG85RY{y~<;8!$=G?7_aY2 z8ibD|$Bg(|E-c=wGZjPk8$u{l2-QME#O#7miJaY4Le%3wUv&2(=_=CjNs>!)#hUnq z@hRd6Z*?`>K+yE4XJkeyu(-FpxFWoI8m>B3KO;N0dA@<0FKA z7;xXx-|(w$OQ)j1T!F0ivP;x_FNO5|;%hP-3hY5;?pqP|kJG1PpI`3$j$~WToL=sE zmdBNhg1ZMpF7Jc6XvA^WpJ=&fp3?jWC1%BlIs$gSm$}l(2lF;+aBfJCp8x&mFgqXA z%r*-1QF)kQj1a6#=M$Fj3aYAJdoN>;^=v(s?m~gE-bhp#QO|HZ(k_)>ny295er^Jl zuhv^$JrbuvD~jwhY>0x(q4VA+GIe||3Z?q z_lBzW-*m5%_~l2Ll56DQ-g}Dd|FH5W2%o5G|Mby2{6fy8WNL}(j5)oRnt0mGjq(ZE z8ml)emnhe~A!&AF35xT)y->J1&ri2L-8=;(@`ShThJ6deMr+HlyzcR;bmWHr;1$CM zpqX>51wF$Nu8q`h5D@MG45WYfOc+&C7vUUUk^e!TAK`t7@vg>b!B)M|a?95SM2d)t zsCH3nM@Zwa^uy$a-)rgPWaA120slXNi&b>3 zHzH@#vnV1L+c&7Ta&5MuS?AC-_CR3ZU`T`W0gF8O_~VDQwqhf&oBq++b^d7T;0l6H zu`LN1xDA_uiTH#aDt_gBP4H{oM(s3t3PzN9-p8c}Hg51{7xZ^rLVG#D5)%G7t9>Nm z$(zR+FN@zMj^*mqh@2>h8~5cd?Ine5gi49JAFT7kHX|0NCXY*JExtS2HPngwKrqF; zCMMERkVaks^MKrdNz#v;_pU~cwi)RnO>UkiTXW6mSG6`tepex%1?DcceHv(>!*3*S z+s~HiV`(s&&w@Cv#5&*B+0WdNE45U4432bN2&Fk~>sX`66z^{lvqC5I zg#1>VRBw5&BHE^R&U`9=>pevh8hO6RJv0||U7zvoBm4blcWEYacNow(X0RiK<=*U! zz6BH6NeNqwN4K7H_t%SIgic0In!7#|nEPkGL3;H;F>$(_FGV3o$tjh`IO0-1z$u

(QLgDU8o=rIrBc75Wu84>%XxmD!VDqjV+A{dCZLpDZPy7T1Gu^#E1&YOpx^ z-14HaikT@a+bB}C330?^e!IC01Mo5-9pfg1gi{Q9JUKQvn6JArS?RzMGklnyA!a}% z%VIiFP0h`PPrkOtlM3AQXU7kU>2(@@Y)E@MoN_qvdH0gEIfc*qhr?evllK4B^%hW3 zMeq9X0Z~dq1XQ}aB%}qAnxSJ51S#nj5Tv`Mhd~-)02xxc5fN#SZjkQo{to``{o?+= zJ8Q9KEzZoGeRjR^Jnyp;nc|%<`4y5W3h%v4#QY)1D&wAhSm4`&%G3>HYQsD%4Y`l4 z-Sb`fm_YoLiPLg$P9FaWI~{-i*QFl)MzYj>hh}E(ztsA4I#%tOIcmvhIi_j7;NJff zopF$XP1#ZQ$BBXoD@BwALF+sZARkBE4hKs2nhmbJdbZ(%K22v#x`Pcm@!?%vK0Y-| z)xDz1R%2$vp($8DP$i=oK06sx)z;Sw-Q|Mz69Y}QDQfg~>aaTFWwP?^ttp>x@9JMj z+z{K2dplk}KHU=c1$|9oHl2iAh$e(y}TRgB*@UFcA;=_ysv*mz{;F z>V~baY5&Gpeaka~ z*57$&yJhJdI<_@UT+Fddw4<-md>BP%2R@G5Pq;sjNtVvhesfZn`8s4MIPwDXz6dyr zl#=OTJlhM~`MO>$f{rJ$5uUYySDH`*+chS%aXlyw7iV z@RZJ8XZYP>JJ=8f@sXFyNKne5W_)L2$I-4FRy@v7~eo5I7Qi`)3oq>SyQ_S7Amo-8h!lfq`rMitVn9FRc=yTe`JbOE61(H`M;SNOI=Z%-Rdc!( z7Emn98Pvq6ZL@WE_3rpgq0VC|Ea0h~`N+nxcZU71n44qrrElSN@6G4m%y~xNf$*ss zX=fT>66UGt&xs<@Dj6})#i26pIMaxBq4z|;CVk0w#BD7H_l~Y3O@KcZ>OQeDZ{L-X zkzIe=(HUw==G)+=DCDTA@Mgl=XOC5x%PFGi#&6mU{_#x30;un9{UV!{@xst19)t~!oevW%rZBVjL@fNYU zD^WVC70FuB9QIy8PmvHHvD(H6vJP0#&LoSSB%WG3UT| z|DrW|;LFF$D%+d0MVcIbE`-+tcG1bUM8^RKUiE9%ci$a*!xOCER&`|T?RC7-_o~Ln zXu3;}z-JLwflS8;nDI$U2!E%gjmdcSK+NI8L+#=%#vUi<`WX=$Zc^g98NdUc)B-ol86 z;V6~;S6lwnfTE~V0@Y_RVQxS`?YIbkv$8dLEf+zmB!^GFcb>R8uAw6SVg*Eif?FR! z*EdQ8r@gJ`o|QBc0M+1`O$GMenczuB`v>!`vqI-N)m3&=BgM7_k6JH$_IBHVy?KAr z)}4m=wnTILnW_S%%@q*Fxm{Z%`Amsd-RiP>dbvyPbV+XSrRiByVW+2?njIkJR#>Ai zh4^iP!$BAvXE=N-Lt(|)(l?0DkCg3RnKvtDHW#Ber$^i)m-z#pBEbB}WU=@GGhiFR zB-1tZswZ*z4+AiLyltMddkp~gDorZ7GwHUyzu<3L(nj zr@()ssR>6QD8-yDZv9>hPfR>}hSoecS89}fQ7il`>6v$i3SwFE$L{GlgO0wdaYk|d z6Ir@PGHPsWY!TrR8D5sLvq*kVZdtJCihTkS`QaP&&oHQNuh21zPmpv5XyN|<^9&~{ z%f7Ep%S%7W%KlwRDc%{)$!36THeIh5RZA2XK17#HJQSJ!}u`H zsMuX?FYCOor;ArzMQ$6Mlr4tX1esIRVQQK%7%BI=iEl>wjqW;0VE;7F?H+c5E094- zKKpm%J#UHk(2V}wct$!%p?P=~%e+HP#a72?(bVk4U(pBfU>I2GkPGTkdyCP4vAO;( z;X(<|QXZhsco+4M@|lYK>Xx=pFKrtCd@EVwVVY&nET7{qBixw@;Yn&LI)?|l>`;Gy6*#cn6^&M`^dRn(Q2pmLy?S`;%#ia!;Jz|-CA=um}!N_$EPJE zJj24sl4J!}1td-yE0zI*V3d;A*MHqS*KYjRCV)d4Oz`OFIoC8?X-~w`A5f}%K{E}v zRKL9>yj6Prp9mwLw4QdvrlF^Rr<&#>6kfiUKTN7DoD0$2rCH^#tRCk$Nqd;g&nMlu z;etJ~-6{0EkV3b{#dwn|NOaVB%Rwv8NhH--eF{A64_&fz^K=xPgbXef)`GfE-))Gz?j4tA{i(oJb zrRdWuGPaR+jn7SV7yIJ+sTTy~$6m&)8rhZWL3APURD&kQp%YHYiedt-PSNk7H^Tm~ z*cwJ9M@K;HD@4AO8Sgt0;9{raHJX%noKa5&oG3m^*OjQ&*U~aGlcgbyw2=!_N4U5? zdjLdwUsQ$Fsl_E3L9hsE0054j!v@%Fh^!6UuY6;J!GaTF!EJc3tn69&8@|lKX>ym7 z#~l-ig8QOiu>fcrK=0W~F~6CBF`B96+ZsmTC4&5C%-0ZR{e+*dbj?RL5irrP`k^m_ zq#Ormp>zE#&-k(RJk%dMvDnwzA1t=^dmZ>J1ADD!n<+1B)g=U(3-qiM!2TBv#4uM)*NCnZPuT%+|Sg^(NxovQInc?DCO05 zS+Y2X-6oPHFWI0am1*%Uk1E(j>i)dY$hrgLO`z}LfhO+p?k}K>N^fV+db_^Gzlsmz zRkY`EHfpmRENhy2o$NVU9dx)A5Ua7^t3I_}(LMEXbFkKO=y$EKFvn6_>?6bbGcPmx zSL0n>DIWmLKDP*lM^d32-Is8%#D3?`bH?-eEpNc#gy1$;^L`iBV-~O=xQym(1wR0w ziP=r@ZZYl5h7dm02~2|q7oUV>s{4sXE`O?{}Z9KYk$ud zECF=3o01T)`SOOFot?fsoKfepQ^Nf-hVRMdTuhenEOPjFvqt!X=FU^f3pB~q3<+Zf z2&)g&fyD1{a4ExySFSXAFcft-1#|Y>yhdC%Jmd zNrORy(17@%tZZDca^@;`aRJH~y&A-f6k&lwGx2y#W}qc3flZC0E$vi@SfUv z3kbQkqr-B*RU;EAO*ikr@bn?#jV3WQm)d2N>z)g}1{Fav7$N!u=}L%(zhO1~IWCBk zz_suPd50j#cu&lkAx(v8rZ_C)9v7{Y8y;B1H4jPK4?6PKPGC4;z7I@oyuTx1d1ZZO zQ+)-Rvg3RlK;r7C+cZ%xCLw9re8%4-=_ExJ`;XxSu6MHpH2fhcWH<-|oxGnK3!rSe zt!99Xq=!wy1A%uQ3j%<{Y+p2*uR09|0}cPmhl{HwUm``Nk%0m^_rAAMip;w3*885y0pG?4eueQ=9{V<9q%DNYPXak)%}y2 z&z~OxnFE0xYqt?$JgN-e+u3p$peX=gt#ZZ=I8Rja{Ep@S4G+A26vpD`D~%=W+AarH zB8Q{Lf!Ym@;3f~rA8ynb((&aFlgi1zD~LbPH=DRNGkuOQ+8G9l&onv<$R2%#L`jRF+KnXp!!PiNPi2O`F#EW!F2MJLll;<)@wE zWB?PS4TG(fD#PPAI^&8hfQV?<}8m1bl^lk!tnh0#IMs<~D3q2FFwD6{5BxNKnp zKC?XBR&QnL=YLUS@!L&*`}Qr6kWN9JhOndCdT%W-g8(&XK4^YSv9SoHJj)cZI(vFd=Ex#B;$w}%nEtEkDSP4P^nF><2tGZh z7*6K0?BrvQv+_0#*?7aj+iyQMzKG{_*2HeoCHD1vAr^j6^|L=34+C(!RMygK<)zOS zdlnD!+F-{;fxtJg!yeJRwWkGeH0g+fYVZ>zTh zEIAX$8ziJVMQ=)|V&!QFvCguqIc4Jmlw?h_q8=N~2!g)M$5e2({Xs68MW+8TxgZ`S ztMXPvQ%WW&Pr*}O(t{~e*6}3|rxSs>>Hy$JOpwC#pQS?Y#%={~(dM#)`dx2}5!C$= z|C#4en#3@KK$a$ayc;~a1*(}92(2qpC%dmi_8NMy>2{6wx5s`iPm*MGE%mnuj#?(1 zRNvM7X8Kv-VJ$;!Lqmfh50<2fj@3*ZW1&Q*kEj{mQ;l+A29|$?O;8cq4?m~-jn6j*(F2wJqlvbP`(9vXL1^p5wv<@=O#cVo2j2ABNUEeD+te$m!^x8B2JTeR&;yKs<(ilg64D+%^M*Y`5!y zOcrRNmEK4&|4#jfrvSg#?ue=o5q8MyuMW~`Kj;74Tw`vI_1~xQxE;@sAOA4maMk`F zjf)L9m`1hY>A^GhVxbkMn+Z-uwu^cX(J8Yb-|(|8*fH&l#Sr7Q?ieQcTfP7^VwkcK zvx7u^&#n7GZIZY+UMZ}c0i&=qbMGJN7hYSTp;_`ow9+WDSxyZc8Vy)Z91>;YfWP^O zK?BN&f-ap>Ripoa6s!Vhj9I_FJkG*r$-%TI!v>UQ(_~p$!f`Z<9tMLvN+LBnXrMe1 zY$SDxDII;UeYk$UyeN!0!o*FiE5mOLxJ$GwLVNaAGI(6c29dNpjC4#6qQ-A!3&099 z!y6!k={&E$nI=rmI)cg8tJuHgkFCKi7Et=(iS9wKtry?jix)kdjy0SPA`KofLkNRm zvHzk4;xO0v5L;?v(G_??~8G!^Pt^4h+CjueS~? zyJ(=Y?f?{DMsN6VZ9fHFr?HRwx~6WrjHrE-9m~G$YKTa@6&&fMs77&N;vxb zeI%p}C@`K8^Qa*L%s!rLfWDbiS@|YN=KoBV;3rA*=Vao|M6zlBO9naE$c!Fc97lJE zN7MsfQiDd)cbc$cC0{_5rOu53;7{cfAiK&fOeB=Px5EY_F&!Fxrxc>Z=6>B0j911U zkg(j&28Ak+$TY_92sC1C=F~9acMy4CkB~MxZR(h*Tn`pHO%rXoMcoxuXn`P6!vbe; zxb*iSKc@4>k8#j=|1l62t*q$t-(vz5jb7Q7n{5S;T6TdsCm=X2m_LLFU^{t`Ex2VZ zGGZUo{e_pEue_W5)~wq#x_%1GD|x+~V7^50G15xMA>TLdTAxj@!!mU!*#y=_^9tZr z^LrMs7_gQbq^Cp@|3$iRva|cQcSR>qa20(koZeH&FN$84wj5&J!Ebbz!<#O%DBQVv`Y3;`^K?UAV}}lJM6a3 zH_)F-T&@Y!0xFWb;7dz}X=yDjFAIQX2P}m^V;}pzOddQ#Y$(e}-m`XkZX%46@ZA03 zsT(2Sh-mnMSjT+v`z`kK6y9{!e5j?(mFqy8tAO@R910a13JN~_oOfU$MioA!64mmq zK_V+EY!**48KpOLk6rhB*af!|OlX*l>eoHYko?#Ez$ixP+(prk900T$o-s+?89oi{l(Yh;ZDg<^p+P{@Zp zUi)t@0Cq@g%QqFb!ZU|Xf@VbcYlhoy3wgwv#wguamAl}Xa#x#mCsv5%TA58L8D=tO zm8>|ONLvB6l#buhEzn=iG?9r@up{YSpXLV>a@9%DC%dSv#UvnEUNRYbkDUPeV&BIF zyYqUepFabl`n3*|DDl-xI|PiDfn__VZ?6sH;_ zgCH~WTjb*)D41B1I0r4j-ycVpCK=K5^x4oAh?XvrGm1%ri1SQ|ReW&Mej>ua&^Ejx zDnT>HJ@;Wy>sQ0ltgM5Mf%h^pZMv>O@fQjN|Jbj0Tg1yk4Ek2y2{0rPec~7%ODnv* z#AP`+(bNM??y+*-()YyV2X&Z`psH7yKx}tbFb=k)i_6LP_w8j)WyiA1wJN>$eQo;* zPGv|61_nkJLA}8m-rdfp8nX0rM6{U&S6sVZ1b~URNRn)dfx6cdXBdn`Mh%QPqXJv! zyYAYx&=H%BiBI9)Pc8_MtU6YLJV?lgSEq(pZi6`c6|d<@0ZwA%I4D^qh(%g zV>=3DsIy-(VbC0nU84QC?L2(0p$>}MO!R=dL}1hvp2ddB0;OJrh;Tyu6Iu?!!^_I( zgMvJ|*~pPAm!VLf-e+E4Cp5_Urhap_+}xGM4u9}V*eQx#8eqVxHw9?}QMz3bdhx#^ zvZKA26PRS)zM3%Bz`nmM*AD#Q4%K8e65yi!pX*{6PL3>&rF?s z?kkO~dGPve2oyjci)5r$GMOr|QqBkSTvgJMZA}RlyrzKyL~PvL+^nqP?ni4agB&+T zbD*fk`XsSIDbKlZdQ+uA%^>xslDvM#zq-a$4RR6cZeAN@g z-KJP7!3f6Z#^M~dGvj)LNUS5IFH>*u&RV_AM!kecCgj`8M{0zUc!KJG;Y>4p#%z{x zmcJI10G>PpbC7o#O7t*Td&m*UBxbpf8swu+yASB5lXG>(QAF{0ADx4tiSA1Qx7}=? zXm>bKScAA9q14Y<7M0(ZaJaw|V^EB<^-#5Ntv;(E3t3V4>e{A6b#U2USd){4b_q56 zMBl}-nkn+|gqr0o2vscP6y$s3H!8p0shM3pRLT$@`>X3R_3{22eg1#sxTnBmwYAq} zX4Cd$@8%k2UYRqNZcz83@$$VaPc^d>4j9;PtI!)_Pc9T`@1VXJFVYT^ z_J1X%9pZ#Dlb(hNkTXp$9CkViag-~s#W6snVIxD#Dl;!nxLAotKXq+%JLu7gQdsk~TB%yltc(Fpa$^+jxA^uA-c(U!p$>k{Io3*Ea;s zd`zm+78V9y1+-fcT!O4+?;+Wbe<$zwrGLd;8` zcuM|hRnRC8^~py3Nm*i<{c}%1jY^X^hxaKGuK)6QDB=AD80{LcAXzSWDWw?lhYD^l zOnV50i4n%BkRurpNoTzN0^>O#Qyp4qxB5tCQ_-!k!E?Ms0u+o{p2dKB)~h&`KdxPt zxQt=itkN|2v7_+6f$e3WtME0z{?o<8vlc^^oqQ11zN@KOsZHuv@$}_fAK(8RnNn49 zWy}c#O3i}^kHq-|=3HvIuX@)6e*D~SGwL*Gk=o;q$|J=X!O_^b4HCN@$W)%F86#7C zetx#k^IDB zksGkLU7E72DsTOq7|+{U5+XSlt`25E*KnORWpF)1q*@HWH8gns-LUruPVMv()}YCL z*-1R(Ua19;#bA$Hp>N{T<)@PR0eU(J5}5%~A}LvX>X@XY{a+F{S}sd#%n@2#w>Ef} z6R)vqJ%)R6C9XZ&`=o5IDMfK`aSL9Cywt%Dp}*R;s5@FX^Z^THo4i8q62F>6-vW6k6*-_)!=ylGXa*RCeg-pPy*UVWy0tC{)zW*k;Bq zXSmj?=7Z>jH#L}0WM>^4tWUm6qQT2U$!GhT+UEif@SdpoIQjWh_ThRnSY_a1tNZS7 zOo5u>RHZzbDno_JX)IHfhu?M9M|f&rbQdgoZMqJrvYm?rE%{gVzy#ITX!Y;`1_t=b zxYyTv-XwDorZoC7Of5r%E@j#CBV}w54INGdLzp@lo?*L~oQzs=QYGDH;Ex7&t~j;; z5Cf9#hnF#wQz$?rwo~wW2hiti0`a>GFnkZfp`%hPPao!^HV(=|T6Pwdrr>(pyB`Rq zQP;ih>FjrmaBG{r<@7QcX`Opy>J?BSZK`&zU=Mk z`Sf?T#Z)dzIqilkK)H3BP^yB*ea@Q8!|B9d#A*2sC4T!Fk$?179THDQE1U@_mH0FL zC!M%j85c}{e2UdiDm&%cM}H9T%1n^A5D8%Xt*d^C=}TZ=|3w|Ag^>KDcVXs(ZGUCi z?s%y^LLs01A{T_{WMJ(oKD%L$1h2ggIJ8>$%-ZdfA?H5zqhhX7Z*=*wEmTG=S<7BU zD(eb=yB6QdKV3{0|Fq{&pw;`rcD9A8%4XnbZ=P2s|76G%EJ_3IfYA*d0G1;ZfSh-b zZgfX{A-M4h_3m|@bKeQ}J4Khgz7Qsm5V}f?)Lm^jr0#9wpN=}QX~)lF4?k!&sHOq; z$!}yox`w}S>E|d<29dw&yO(l2c2^X2Um;ns_x`DghFN0JnQovICND4VQ!RaoOI+Fb z?|Im&D3qoe-9wQH3I|A>iLjZ*1}Nt$%YsmThU?D`Ch{k>tCr`g?42LL<>OiSIR7^m zy2nO-`)@I@`rSe3;C}<9$$h4%PQ#KVT)Tcu-4Fl=hvd=JqQ>jtB@#Ln>dpT1E*0kB z&OZ@C%`J$VeMhtZrWs>;P&ydH9L)7jr2I_@7)ZavcY?&zn3)_{#+6g2NubWwv!?jfpR{o zGN8N%l6SfkseKNfv@&t8EYd3_NuUtXEEf5DKEI# zxb8SB+)Y=A^4=yd_bQ8lqu<_drpI@~QQX6O`>ETh?$9aONV3AqlhrzB+Yc}3abWDr zU-9^y*VO^lsBgji*Z{sSubn8!yK%eTV8`&2y>)yw)?08RN9PuA^CDZ`*z{c%uofxy1-XC81Zs?TFMqL z<79k$ihdeTX!FmG_SL_j&jjKwU#)T)!ULSdk()fSLu*>y?UCMjD>jl3X1w zMtGC;BPEf0r{O#%gX%iLC=SSj1W7;iO9_Mn1e+1Ankm?D%?R|=FaKmwlvQwM@!-5q zGr+Yb$qJ$>rywg6mnD`-?Hk#X3o!Y*jabxWZ4YoZR3ZBrnImqwhMW6^Y1=OT9R4ww zsYPb)W3%Q~ycb6?-gkF9yl2@D7y#O(->zC-4u|)j?+izpV$ir#$NQsD39%0@8nHlf z)zUue5$h22F7;fg+xJkRf4fr}|T~ z&(He8ao@gpb}~MUrj5XbFLPM6&}D!@C<{{hSkHw3jv(I$$tF0gc$N+ZKd24Y6Wj|M zOlK!u?Y3|NW5xrCB3bOO`|GMNQdKk)IKfTA?|0R#5E9f#{Abc}*;~3wk4Dq^U(1%~ zEocvE(MAa=;V36QN@C#B4qY5kweQ}>N>5a<`6#*iYfMZLjTl%4aD(Pv-0A|K%I(dG z!hTuVW0lkIYM)U8SDPqh2BNP3DvQv^skPTp5&F<2o8!2B0PE?onB1My&Z1Vve`J3~ zh4jc=!mK}5X-oJRQ18ZrZ$VU(FmsjGmmz8zdt0FiZh;~J10Ob!Yg|j7B!(c5r$pZk z!2n?(&H`YZfh?3wTdQw8K=BCgeAWo}$Jqb6jHQuI{l`{*?(sdqVfSqPQ^K89hdUKw zq{Y66#Q$ZPrId@jGOSj(SuFhfnTazmBribF(wcIQrIl!_pOIJW@54iafW;%h|L&WS zhdOud`H;;g)Syj&g8ZZhb4bmu=Esiu?G~{2@HamavX|Au>?071PX`gsuL9D>Id2Xz0^;Q2@9*ptD-R-Pns zKUGr+opG@(Nj69{eLSA7#jf7jxPU>YlslSHDVa9buvC{~SB&-7BiGmWEs3V@*Uxxr zas+|!&0WWSBh#Yb-N1E?!-P|;4zep^q`XMy<3X_|6N|CRJ32v2woK~xcce3=lcjao z(8n|WRTSoE>GwMFBQX+>Vm@afCZ*yH+nX_>{byL89tNB+;C!UOG3NsVt;B{#qdZ!r zIx(UfmkNEU%Hl$Dj4eos`n#c})-VyOqaF<(L5~gj&XyX+@eJOHyA+E^)6!R?_1_Ut z>ST=XJ>3c0|FtDVX^dnt!|@T?bgcf8 zrS&uoVLokQ&mt`%3}zhMF8(lNiibHOnbSREn$dYp*@8fE&5x;FuUiMXT66A!c@N0; z1p1S_6=9`19ZX9inBcGde`OQIY$GN~WQ9kB#(I;feHMf25CP(}B19iQHaK4Y>GiZ< zQ2FF{OCT7tISc_o^y}vy=_1&W9g+2PCbaGUS}7xt!oa07S^Za^tNAeW6f8-4crXBv(e6bS6kE4jwE=iS_<7{1vdmG3@qC*OwiQO_vn&FPJEDf3Vg zQzID}hL)E<&v>NNe;N@PYa01l(tXgxPoqkK>fg_{fB%VM{e2c~i{^Xhhj6x}uijB# zQD&t?RxH{&n+=fCIp(!bnUEibf+({Oe3!()m!q{${}3l)HqH*-1Z|qZg@J;bDF60F zP9=9C!j2E6q5=aB70mx>Q4sHsF5*43jE@+i;U4$l=}#7XH(l2J50LA1-Sn?Kh!S{Z zG@t!v6GACzCuyqwbyA$kg~%s-U|FjLY3!07|Ir9iWqGONuKbXWpA{d@s*t9*>?w0v z`i0Dc2pk7`9CjjojDNIa_U}Yu0vs}i193vMoVXv{?`Tn0ZmaP+Sm|>Je&LVja{z_KL!ci4IO{bcUjE4J!}N8OmTuk&OQaB zR)VVj^CpBA|E|kF2t-{y96zM^_Yt~6XO*X5uwuwegMatsL3NoXiF-djn^XNz@VoOZ ztsd)$v_kh^zRE{JMC9hoCQ%&=Iza%Uxfg8~@wwl7P5KyY^121g<{B-C;XMqUSaMvcE!F zjR1?e;^Xariz@4}i?x9RSV1!wnyOb(0Lr43Ghga4Aw+I&8=}oN(_M3Yp;5*# zGyF{VviKPh6m%KGYAs1k+Tc8s)x1v28^sNVE|G>gG@e-MQE!Ap^KFRW#>vDJvE5e+ z;5e@rIs}e;b^h-WF;Q#6T|_)VsXRutBG8e>V>hg8?{l^EYyEh`G?L=QUqni=_xlsM zt%?7GfZnpe#iPiq5$(kD=Cf!MSd3e3QC`-m`3P$4KZ6pq-dsdlc!Luk_ z#NIFFZnHOQ;{sn*KmFd^Rq;M)5nScSgcC_kR@mJvs(=*&N8fa@Xp>KqYAAIOy46;F zQ6Q~HR^ZWJ3=FUlMc;{t9xA9Uezzax9yP7=-n-!-mk%MLznO3cCYi9YF8^)jM&KD{TP&3Ms?m8xf7uw(N4{VH$V0f5(Z|eHuxr(3b^CX)>(`P7=)J=1GpIKEF}q>Ey3MIJD2^Wysy>^Fp3P1E=uwMO|S>Vc5^O;{VhNK2M*3UxX z+u|QDM#6+{3XMoOTu4sJxvVrAB*Gik>6 zQ_ahqR`bbCOI}w2F?xVN~FiTX4;({f6n{s80OPBL*Ou2-x6IFeodV7)73W z8rWMA0ZQ1wabMR(m0J8T(xPsBdy#@UV#dO!%A8thkF-fAEovGp|AI$?tA zY&~c=x)~6aF}d{^njitY?o$u+b~xeGdO}}0`GU8)>pu@gmcv2L&7QKMp2+qJqJ%D> z!|}oe9sI#blN<}Lh&>VO!Hl-f|9tc+HX&=B^QTPqCWhL-7GX7%9p(Jsm#|Zh&qOl$ ziR7oA;(?7P`;ves|D4-;P&jBkBbhjt8SM`o_)}rJZW=#Qz(kpnbG8@sVIGqCT)RgL zyw}8^+W>AJ*7LF#b8v*wQVM3~Xd&?kcK3r}BzFTvNSmFcF8z)$9i1b=m{=aGbphlN zL_sjuOg_0|(lb6(6#U$6jhO^_U_= zPU+@~`(ZZt1O<@8nRIo^LgroyG&iyHFh*=O?&tPZ+RNlA4DGP*=W>h{ z_Bly&N?=HK&nt|$y)82PPHw@bHe+63uX+9#=7bC^8DAt7}+R z(pNrZll5-)xjFWaRv!xf`owIob%4?c{5QHrTxe+EoO~y3s}sWvORE!wSN^d9)sCsK zZCrkhcGI|uDLQ8(hu5wcX}uql`E!HQqd{bdN?7R8;sv~A93GYV@X4cCXe{(mcK0u2 zUU{F5~vG;R)_P?jeE72-eRk};BnWz?v4ZY7&vp1OSgEOfga#5v{S zQ)8JoW9K?m=w$qJG5Y~C6O&%;L2Fc+0cr7+33%2qcdwg^m9eo@%4m_5ge5nhfT`r} zXEha}s#E@`t~0a-`)kix*T);~^_}M*O7sUS3kvl*zb61FC)LLLUUPtN)#U6A^5Vgq z&vo1sUjXLu=16&n<;_%{LifT#R-Q@pc>S*7d|k9&b(jTp`sqfYseQ@Z@kG-L+qWtd z8ERTuH)G4YLV`R`AD*-87oA_uW_yhF%t;^!sTVDvDvQIy-oAsCsTmEg4an1cNUuk} z@U_aTqB}Si28c3|ZB9i*sW7mhNtElAf4j{;XC*kC`Z{eW7b(okdy|>rdT9|Eyr`-!(Hui$qx9Wc`Z|2y-vu@g?D+v0j64M3k-O?8MZLU_h12g0S z2eX`Er@GiSJDO7G&sz>BdbNl1N#%306$E3O$6D6M%R>qvWkjJHr2#3a*4_|u&`^W3 z#>?7$^6}^t%=s8K?FWQZUe?^h=!Zgxb|UgRV~{t4g}aX5ICdp5dY? z*D5Ftbi|gEo>f}H_JHM3z{z_alP!hO zC7k-mA|pwqaU~yDJaTdQ4BWynJatBG6S}< zuU5BLYYnj{GXUKG1d)EFsauJY5-VD@agcD1`zBwJ?*Hqb2=4JmB`yxZ0YV0y^(}cL zaM77@th`-eUFERUeJ4zUJFU;jT@o4{E$m+SYoCYf1u846%c`>0cC>g#Nu@v4VQ+7r zgndqfpy~hdD_+)GO?1DOTS9$ay#Ci_m=0)(2q|9JbEd}#*zAzAE2q7D0$SP+p1)62 zPKET{|KkDJf7gqSit=3P+)~mFk9c8M;Pj(aPPyhf(a82@`caY(@gs1ySbKu|t3N0E z1hyohwn4>+U1HcG--2;AJ2wnG-^6vhK82F|BV>akE41UUpV#~M*}*xC1PTf+-V5W{ z{llkaOXg~x-gl)aVbj6U{4Aek2_wME! z4l{?7r;mL|AJqyoG>^W?NaO1_-W{(OC~tfpp-b%Nf`YAV-ZMy@G(OQq239stxtfNNEp zOWa=nc*{&)zQbSUy#ADeCxtU(>#FR%38swN21OqD06y-0HOA`-P6lZstIfOl#~M^y zQ%)^eSMhm$lQva@gJnSxxEIWG)D9k>LDvT$jM)&A%WFx>X6b@M=mi^^XFh(s+!&#b zaNV_kGFzpP*mq66RcWd4A*~wLaO&zBNhQ1&8B`EO!C8BZ7>7KTbq=+tK1oxuOZoVJ zF3AD`-zLf)@Pt+1O7Xu}(@8RSr8V{JVfxPV=ahJ~v>>=)8uLfB^-E97ont8G40`D4 zEUaGZ2{Hjx>yO=>0;dq}jeW+o`ZHam5ut0&Xrz|lvH~P4K?gE`YMXDjH=$DbH3ETH z?ge4;(>B%D@-Cx1BOi#sBeFcbu8@aGyIh((Q$A}uRL$63*S{wP!Ra9X+;+_JrJ1(2 zMc;5wn*uJkmY$=>derR|3<6nC*!3X-*giVTxPu(ic9(Q-`Lj z%}%MxJD<)Z%2(eOjh3ng9D2_^2xzgZp0XMJap3}b4gesrp86$*kebiwqJM{>=|9Vh zNdi>)yC?T@_9AJqGcI#@!~pDONiesHRg2*do~ z<|gCm*)#B-F)HN4cQq0cm{tL2sSBm`9C(j|g~OqT!7<>HLz*f&UDMOi^WL)1iMno+ z$6fsKnRbcDVo9@$%t#f_0^RZiG!-~JU04IbXR4`ZQ=yy}5fMQp=wdPYg?7v;^=*Zl z^?qQXQS~fQ9t%`Y@95Y5vnh$0#}1+~ce4aoj~mvJ-ky{Mr<}!zh)vI03pRFhjl)+* zuYpk&SMU6?#u+=o#@e#m*Ny2Nl#eM%+|CaYKrNvCtyDL&@;2MV^NhWX2`iEwy8Tw`ellCI{c{#`ey21XuK3KmJ?Wckbo?aplgJ1^(w1 v(5(jl`9H3J-Z=pL&ny3a_}>*8o@)tJhtv727@uPB%K$`1S-M!t(EtAe_Rh1c diff --git a/docs/tutorial/tutorialFigs/colloidsPipeline.png b/docs/tutorial/tutorialFigs/colloidsPipeline.png deleted file mode 100644 index 471d3398e20af5bcaed63c8e5830ef88166f2894..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53167 zcmd?QbyQVd^fpR!=mRQjyt~bIX19eJJy?rxD4T=xRy5gjucczy#WqNYBOX1MhxE z$~~nfvk9gZWyB{4j3m=ib6_rfA*$wrDmhYvP^qyK2|BqJm6FmT!s1q2eCJ_)Cgq_T6)Iq7()pl=k-CTSpeP2=mHIJ8vKm zM5y=#CP48Imv{JtZ7t{U5j!qv_KWnT_yCf>f@xLAcLYXR4-PEe?`*M&!FeVA{Y0X= zs1*zhL4zS@!e3ULoXkXv6+)*4kHdJsL!#37A0!PbQFK=7z7s!LiE^r%se%u$oh>CM zF_WitfVr(g*%-AGIdtnj6%*$YD1@AaQ*u!gyyOZWK5TgIjnR@pg&UbPcj_$J;xLGS zjBwXs#xEDVEjj=;@AT}i5Yk~h4mq(v5fS0lh*b^Yc)Rv<1WC+I^0na5D7O9&Q0aQa zm9ezV=ow-=xI<0?kI?K3eUHc60<5#2_+Tcn`GgZ;SCDJ2}W%4%SaZ{A!Kgs*ML%4O1a;u+6%_ni;(UYgE3+zm!FSVn&> zimmhLH6lA=YhG&u#dTr~3IaDc(4`G|(rGYUCeTF^q4h})_zD9bFI*bt^Da+N1>x90 zrlzut>uFLf;Zvc?Hq~%v2iy5O?%f^Bsi+x3dx4FyXHSx4%;+xez{178AiV8l#KSdt zgOdUN0gqw1dm{MyO>mSUZP@-#kTQl|uuL#Xu-#`eFkaTvyq=Qxhf%>IPs{@ypg4J` zJyr4{PcEfx98`N|@lV@@C?P#$=F78f53&(Bes(n*+Qq6|(n$PdtlPIfKlCA(6j`U4 zV$yoV*QfO^_L`$5PeVlJC(J0Oz8nuWL!c8$);46sP=Pg#UrfwavGM|IGL|fPc|^{G zx+FMZ=579br6K8cc#Lh3ZT4S#H^11A=##mdFwyy;U&?U$eadW_03#NbG z8`6q$(f3KD-TnxSqQrZc%sJ@M{(J-7T8j6y}l860atbrFv7 zGGv;DC7j_~TrkzhQ-Z|6cQiV{ zMCPigkxfV`j{Gdd+KLH%Td}FeNj4~fHS*F0^%G)=n0ub$rtA@c7xJ#O!P}twZFirb znfYrOv%F>{jG!~Nd=1WsOMZFp1uy`jz5^W@U z6Z_PZ;7Jm1Sbb)FT79ZV;Ix#F0x506Q*BzNMD&M=U+CA8XBN-JYYF^a-e>|aS$IE85j|mA^r|Y)2Q~dTnHIv38v;HMx|F?MKOY^Eq2mBdWMm&828+1;D5Kgl~ts%#WiUKbe)Tf|P7SRQc7bDdf(La&ND z#}B6ZMzu>OijPG}rfSEt-U+ETYF|dX!CxF#>>2YJ%XjZlcx$3hRE$SVrsY-X73i6( zXe#rl@MyZ{?LA!Z6AC4lXH~T?S$xtg?E9&uK-d6Ma_tBsQowo7!;#St;F->6m}3k! zq2!)6gBr1!SeV^6e_>KT^&|N1}6XChe zD9N*}-Nl{rcC&J$a+7i^akJlH-sL+MO_LreD5Qx>aU*>UfxY?VtFq z8E?}Z@vR=VPYt?f?PG`xutLkm%Q_4OMTQ_r@D79y|0DlU{{YeMHogFcK#*9jSW%E2 z>Nc8l;C2wYc&5a9NG5K6XjiZurte)dVtQr$yW4F$xxZ?;tVsF+VK_${D!ZK13x)$MUi9iE! zPjUBeXJ0%cQWL9_&SaT;->sglzUd80f-#h3%nNin%Jsg=P#t_flW0*!oJ2Oqv|KGufEd)jss!$F!u-YM$|8d)cT3pBop zUIw9>(o)hQOR239aDEO(XZiK8;-2E!;;%cwJI#EnnXfWCkhsKBmXENTiL1<9*0k4B zR^2ul)=K(Gz@eD#k>C-vXYwulP3zTBgPpD6-k5?|kFbP?qO&~>Q??$r7oW_eRHY93 zY1~&in6Bpm%yXo% z6C+9|$~B6JP~@8KM`|cRK7mnh3EiH2?c>hWgH*T2&M#^mXX>8YeeYzStBkBHO{i5wY^?s~=iZTp?vFenDT9@5M*Rd(R!q+Ml zrb4F89=@lcvKBlPqP7f^j)TFB~)JwU`&!i0Noa@-z zW6e*h8EdOnys2+Cyc{F@DKG?EKhS|G?Y=P;Xr08gYTSOitu!StHMKuFQ&Wj(3+6f6 z&8lp4j$6t~k>2jI^(OZ2KMC;t=F94*d=6W`hQe&ob#Xb#Ir(0=hWiy(V_2pya6&Uj z<`)vjM_qYW9dwGkw{t~ot_y`;I=nk5>>5e#X6%lP!=Ydm6!xG#m4x>mCB@CXuUimE z_Sm}kE?Xcl6r9Be;a=YP;JmmIn|;a)Sq+>E=^)Z$;WlvkG(NsJ?YCLEvL->MM5d^j zSyt`x$*tB+afjb+VtcFC+HYz|AFsvlyY!cs(hNr)`=xZJqD$`6lS6?Scl{;idI1;v zufCsQqv((D9+7Ve7N3W8ik!F!xKix?+_r?=J=|P09$HCNmQ-#zA{5CND)D#^FTQSo z9n$zB`o3YMZ{74wyL2jST~(eCDbi1C{9RNYc+kj!Zi=qsmH_#9J3AhKR|BOtEH0ixALA(o@jd zGPDsaWPj{IF%YC=Pfijssl8d{@XwTAoCqgH-a)8&jPQ*R35>SSI8mM^su0q=x<%HH zR2bA@krc%^*sf0)g=xSR9M{od?}|uSgY-k4S5UCp)8lL#_vr~P0>gocy>JBhE6lIx zD9d@q0^>~U8Y3Rv`fC%EkOLqJ7!FUhoDmQR>27`yWuMdjKtMozW2yGS<%OaGzp=e7 zi;;=_OH&pPTL<871O!13e&E#B)WwL>!`8;mncqW*`qv%&!1+xxD>db>TU@M#s9z|m zP)gW4nNo7GaI&yb3u97JQVKemnDIZCl>YZ|;D18Y7A`Ih{H(0*?(Qt^94z)u=B!Wn z`1n}a*jd@xnSnc)ojvVbj69g_oN4}f$$#&WG<7z1vUG5z)FN|54UE&1H z2C1c_vKsIg>1JwxiMyFm#()0aoLixhtv%yHKoCWcl@wF+K-_ubDf(*SqI<~*9~?}X z`lWfGr0v!7?e{!2F>)1^k5fz66>TMKM~+gpt6$6xuBFTBmFZ7er$p$bax>eG+{JOg zyQ9jOiiVmXhIaP-)Dw@Nrfi@Mq2tGH+@WvwVV3jaq2G|Cb_H^!}|gJOTBuo8v_XX+^_w%wh%h(@~!K|2ah4Ot>?M zsg~mc{TY)_ip2E_fh_O=|BohH0!qX` z2d{FQ%g*!@Y0#O@?Pkh=2Xg+nVmM?Ti`;(=Gn2uP-|L8x2IY$Rwy5!c3|_2s^j6zF7k4&6zo)+T_Sfk6U~j2}f^w#sbK;XW}j#_oIrN44wrJ`x25MaH2<(Y@M8@ap>dZ07z_ ztH_S`UPCwil2*sU<;D)IT<|absnG+|<`tatmNcc0R6#%LFJ)!Y8D8wxEy+cLuMYZI zEv!#=W}vDCN@B>!$hpm4$ANp(706a_(GqG98NW;0`LOUMc6w40G7bhtfbj9SD%85H znQ+VExi(zwGPbs0Z&)4XeDvwD3=-{f1j_1C!y3dsD#QC4E6=-$nBh8s?aqZt80BW> z`{%5zRH!FCxCi@m(UDIN+}=+3O|_uCDc@Lv?$2sCiA3))+p`WuH=QVL7D%tn;&!IR z5jCOgj<*dL(jLnGGF298(J;|4v9RU02B>&IQ=cqgJ$#6>8U+%D{Xv8`DyV<$|9$=x z84&KVPAzt-cB30JCok`PuO;78G5GaqtHki`a}?jx1^3)Xo_h@&4)@8)abZ)f*Ql^1 z_%-@qUtgfKjEuzP@iw|E=aa2$Uy~f2Iy;L>nK<%PTQAtfF>e_)@!IZ^DQi~}FtOxH zqNIVH?lm308k+2-UcfQ)7n*<=Vk|C`B$qE&s);S$Upe9)NC((36gjC zsOX5u_f+|QOpuODP1v>152{T()ipTllBH33>cAp0%%z}u>_=*xQCy2bm)M^4*z8eS z@D*PfUML!jo#JBWKxH$`{)?zDZb=9(aRmGh3sJ4?^1ngJk$oTtI!7M4cBv<30Jd zqwTU*!eo~S^K4(jnXOBtN8&kVXNLy|4%e{Lr7&*G(J$}QI>PWe{0@2k8C%QjarxwVml%O5o>H40Vc!sdWA@e0of zPU?8>CwLy9YyTp6ez=H7#NP`i)i$JMdP~!?PC{hUHfq%?&K)xlwx+`)md)LYz5p+k zSU%p$MsWT02ZKdZJ-er)7HAj(4ig6`fZ^Xg<7d%z;Hosx8s|p)7NRpey zv?x889Mc=iY9G1e`68jppo>c8@#f0=T0C);;kar$EN(H*J3sfG+PS;N?zgJ#j!TD^ z#V7nOGt9EklP9CuQ)a!sp>nRw=BZbWA}79S5eBWNaEJFFK0Rs9Okv>Ir74XP8yc)K z32jMMn6^HjC0^aXd`Wn;3wH}ET@paL@TF*o8?%>(WYsHq`4~O^-Nv>`en zSKLCvf%LS4{@2hJ4o-`j;$4s)FZRjXJ7C2qFI05ZQaIu4uJPAa=VI2KZF^!|+dDJ~ zu|`iIA-;P3*11!`SpNAJ`-YCEQ}H4f{BJVt>31wUj%zS~j>b90ggAQ#{S>34zkvWM zNI*#bv;x4FU<33jz(@Bz^~Y`ZbZ`M}f?xf_3!ea*9;NwP!mnXcbb5yr@Oa{aJejy} zF1xer_nbTLX|`!_z+*^wLQCB4Jh%O@5Ee_y7x?&|EU)JQ?GA^ziFAQj5L$UQMBmIx z4K_Mn2#F$Ml~(6Ev>%U+1Q*tzSj|1lU)#+daZOVO26G1#E>WJNiQX{d{yZnHhGmz` zZqCco-d3vlI(UXowf_Ij&pa=q4#9`b9kiejK_f;Xe0l4){Y-NazRn;6Xd zwC$u_KTiU|?q)i9&(OcT1m-+hp{?3}sRf9_MH;;WU0pFGBXl+LP%`&9ZNS981_>A^& zxowf*B)~nhBQ8*FF5vTcx*ce2E|p=iti6{nJuNI7;C0`j`*hZgYO&G9#^KVb%3jq{ zIcHvOfOi4*>P9cg&Cr06X&WS+z)#;ESq~0cCx~3?M}P$h&$)Lt>RRy>y~b)&k2n$@tEEetd@F3~MT$x&LtTJC!d&z6>68pfRaefJr*+)~Bm6v+c zJj1wQb_PE{oGM9>)tuUCUOfl3q)s=YM z!p9-;SA_iqq87dx^P|vR$-R~Wuq$mqBE`-`P@$tS-Bg}LIabdm7)r%`TI@9YZC%Q*VVyV&y5hN||OWGA>2Kw3qjX^z)w z5Om)r1*k#cn6qGPYZlf1RhW)@2f0tj-Obmw`(mnSPvNXb#;6DOq%`~G_Bu0owb~Np zkgtZ*_EyF@hv+q-^u7MU@5!QTIqn9_j_;#OxM&yygb?f_kCT&@6%}tHjJeSVX ztgiN$ikSlU`5nTJ+~&*5TwQnJ<&O$?98V+J%lWudZ=gr}eBI?g@| zX2QFnEGpaAAy5+3)>_;A=(0~bnI_^l%$5$EBk`k;sZIQUj9cScwS#ipm?C3irAKt^lig|6h0G^zk*MiAJu?8oV}*He9A!&n zhz+hISv-9$Nq%&0y{ebF`kL-#{00rjx>372hq-QiQi$D~#0bxlYCi)Xyky7BRJu$h ztyL?kW78)AV)JiF^%5QX+2Ik~k2N5h$;UBW&-~K{zgb+Ih2;1+3~%2dsFTm zmyMp-A@25ro3I2NhCm<^3kbGjnl*YtikNjn&;y=>pj3>gU}eC`00`w(l>b9MzL{K5 zz^6X@R#<39v-hbu1a_>sNpV3Gqv+L+c1*Hd*vZA5V>?$J!fpGgk!`b6mt}8JBz1|6 zgcgzDhX)d>dF%>#+*(f~9A|st~bcrT)P1MBp5x2^>+7&g8AuebcZ zmqSO39=^xk%^_KEni zQBdC;)vZJaC87*4lMb)$_WzZo@kb|MgB^`2M}s|<+YVBGkLTv34Ihw#H1`Yp-yP?o zC;({;qu@3P^whe)1<%LNkx+R+mBsK^$9VLH+6_*0G$`bPZB1N%jklf@kRmm&!};$n zzM?4Xw_LISPYCxk3H|j18^E;N!m`M{wuf9V^*7bC@! z=oKF|v&sKMPyhWb%0*Z1wyS-Vi5swhFiKU9ES_|Gj&AIo?Y;&-3Yy_-#aolH{0xY$ z_puqskdWC10FL=PTl9kw&=PiN5^ezEeY1b8@}HhO?gFx*b;yRbAz)YilP36QxPp-g zWJp0@-$@uEgwEU+oLma9uy%)0DmH`;n?F5JBF5baZOHo_eBa;bc0S5q22i*mTP84o zHn!V|r?xy`nfxgHx;prm?EcPx5WL+{`L~Dj6u5c(Mc8tRj$ON`nYp<{b5m1JNJvO3 z*)o9LGnarRW!lf@HR@z|FETbW5seJ4|F}90?;R2W)~qfM9j@0&yCc*a$uCsN=U8Ub zn4?Q{?o5KSNNUUSIx+W?yWxVu-Pg(Um=Ac??JUSXbiKN*?Lk1>ITHTajATeWaCy_y zKlYas_-jy;I`*ie#PBPIaS>6o78nfK(A(Qv;_&b=H)URTGY@tl=Ds(N!EB%ShMHri zJ;8Zoe7%^~VgobdWomfOT~)SHXQwJ+{vmfNPF^cI7w1VY3-hI2C^_OYXycvytJkr) z<#ozW#PYg^{LSuliZc~oTDmWJsDY*$T2hvr?#k1GjM7W5@*b z-BzC;aUsVE9)bHBSj53`CxJ9m;z@_5p~B+L?;tb!$t&+BBKtHb8dU5ex4PL^830ll zi}vc zhO^k7r_EzAJodPa4?)iBVHRj>rZ%px#SV4hd)Y5WL((&* zmL5}PiS|M$F_33_8*1(|yA}%j)<<4<|LBksQ4^Ijs1SvjO-%!`YYchpXkvZh)2#4U3l|8XPo@rXNm1=~jTsLLD5| z-H?t8{lp@k)1qc0*0@eRqh7!(>aVLfCt$GHYcIh3fRFgXOz3OAB0o!HG*#X4x#7A{xklglU2gSj+IsLI_ziB%47ra|8+X;9kQgKrJw-}zl} zFNsX9~N*E4GIj*v6`Y%j00C&%n+QHb@rzE>QPX| z?}H^{6Me5+(;r)-4cWYj+UDM;-;kku*Pb;m=O%<{A%JjF-x&cV^43GuyA-F{M1Tc# zbM$=*6&-VD8f`U)nl|_l8NOTsRm0_fvPg_BVJ%UqSc7a1CuTt%TquZPP`fM1nPkSY zsNxUdYpId#A-5b|efmF|RQ#pM`V0Hm-9IqFUkAUi0&sLZi{Swt>uKgWmaW&AI2g_1 z6qKIk22!A{iL$t>52^*?fE2G|+|b=piO-}+j_fu^g;)U8amPzy+oFFG0WM6Ae7sXNEUtRG8-x}X{EF9KGbD@# z(iKgtqA3MpX@2dK#(Q}7B(rbAL`kSAD_y6xy6Zf1V5Qsf^VSMp36aS8=$`;2!24eK@*s3-Zgc1W)jN2wgZcccC&SG0y&#h5J6ZFklvN8jTsszoKBk$bsT z6r^*GHnUB9?CS#w`v_M6M5{%$`12UBC`Gw|f*Amixvpj^`~O&*C{pRKH-?9U*hq!E zI62`vT7{|CAuqk#w)#7x!P7~(w{eZin_nSV(Hk zr=o%|*WcfdR??>|FE7#3Nxg(v{!sF76#J3*mzW=Clv5VyZeb(`x*Iju6Hw1l-)f}{ z*t#jQxsvgn+{P|Ik>I9~fJIq}aXV5F6qW)3S8Xc6xb<)Jf_ekYM177YG#$-G-^rrU z{e{~^ktVWl;MSA>&*2u^eWm|DQKLqUM0}PbNQfgtS#)e{EC&%W06O&MlNbzUE%9%G z=060i1)vJH9O?fFn7Y(R=7%}B<> zIl&BCgusoVN<6Y(5YUp?@AOPJqPixXQY3SW>xkKJ&!TOyHl)5wSwWAl zJ#jzr=jQlWX!^~k?>!kbD6JqDr*s>0*GG|YT)2W(L&BplTqOU`Slj3T1JDhWWY^sV4|wgna%QG~BKb&-P?PSxU8_4Kp8^VZ ze3qV?ioR`eKoUpAP+O$yI5FT^ zJ~*qfS*^z-CbNwgSgZWy^PaAgA>ziZDB9L}GggI_w?KY}sJ##|%mrlCMtMm1+oA!Y z8I~jYI9=lzZhL8mZ2epN7QAtW9;}blsIk(Z2jkWjV~1BXQssvnv@gLUESR;C_k%+w zYaS{~FvK^IqwjWji6@0Iho-Mkgpy~%IXSI*(4oA(U4DVW#v-lCYzJYq*ui#!^c-hA zCMChX*8O2Qy&WkM&MTMVV*d{c@oq`ZBEfHjkI5~L zzTiNyB3|ia(68-r>sLGEV#fXUq)(S`s2rLI^356$WX=Gnatz~s*nh6JO&00i9{4U{ zU$m=+2H~goiy^1S#~~%0Y%2O$rw56|&J6n<-@k1IEPao3yGO5>Zj!26`C$@F3#%YQ z#cT?ZXO0>GU4CyIJ)2CvJhi0SqLatw?3dM&aG1@ExuB{k4JOp0WmkD!U>)lRL#m*m zwv+6Fc4v<%^U`;#3QM2~uD_yK1UGrbtt2Kk zAEHw=?#8=pN>@o@eo8Djer-o6(9-iFgsrwzNnRUJ%OQZc!P~W$0VSb=ci8 zjcKoV%V1QtNtFJlG-MTtBTGpjB*cC(Qdkjn0jBYkV&{!_YoYU9EAo0uJHjvpk|_W|m(|HECa@q+Sn zl$Ndqot?(~G{}q&CrrPP9zV^akr*~0X30qOfQ#UhAQ#c>>4UlWFFzHXmW~@sI?e~p zzNUdEb#3lBhVOzJc`JIBvW;&-oosRTehkVenw>$6oJp0*Fsa$ue?)Vn4-E1Vfhp#pT>k6Ll47L>1oANvcz=pCyn` zKao)COOK?7nbPyJD)YYS>ysS0aBIY#nHmZbA+4P6Y^f`nc;**g#VY~RLpheIBIy0UE24lqZ+#N z^XH{f0weYQDU7KmYE8;PrVD{Q)rvJoCmeJ<2>L?!=ivGJlapu!Eev2-r$$RQqlGBUmQDS)daC%HlR z8xX1p7Z3|P>~qy`pp&eWGmk4K7BC>GoGncDMp-f0y(1fP?Kz#1Xe!v7{4sel{y35k6ZA`T8tND7amTjXZ61pQY{&BW49;XB$UO`)RH)D(C? zfH{kx8o(UIX4q*K%Dq7>Uv)GuQUNI9 zmej?};{Cm{l!|eY2NKkznqAbY3ZW9G#Ovv_2349yimcQK9x>=bH2tc`sWyFbsfWra zSWics>&E-F`M~EbGnMkgAzUa<-iWhyq(?X{SB*zAbLMY#%Y}`z3x}s=F61D`lUxSA zI01x|J`gQh1Ke2BQQYIP$i-l82`}LUJ)d!R5aG7hnGM2U>|G}Y*H$dJi z7{I4j{x@|P#yJtX;@?S76+MhsGgK;#$jyhbJL#3SbKGn%$_){3_W8^@7Y?9M%$$o% zPu>xU+AdlYJilSe!5K*&4Aq@+OXll^u=FEwKcAS8Oc$ zv)J`lCf|3EPi=A(Uf_zIIv$k5DkI#fMXa|Wxp*!lNV}D~lj&bho6%|=UlJv}pv4a< z8>6LmkS~`^d@p=If}Y+kJb?d#3~4)8opFCocgr;{NiT??3x;|wGVF^us|fe|++-4g zf*o5<4T%&U6XEu}yJJ#S?Ax%FbERY-_rTwWdC%J78;X{5g7#I&s#H&Ql@{-Hi8|p< znndvFG>XA)_8;k$Ia+nR&eet_$`YRhgszf!A{umq1?hu+KX zVc{|GJ-)Y-D-kA+j!O!1J-SD6mxbzP^|xS{Zx?~uCp#+Fsk^<<6bbJUiZgp2q%=-g zg)CHS+_8Adz*<;GPW9t7DO}9q$GsjNxf|~7Yi9<2g;Q6+T+v#XITk5m6NvY( zzQIn`$UH9(`eI4B(Zj5lNN}026#^a^zDs+^%b_2KqOfSF25kTZSgp13M?_agr}TvP zY4Y2hlasd0)W!4#YRB0J;(l1dd2|RPx<^cp&*FS#XrlkZC?3PRcXKWW`8|UKE3FfS zK>;MXm>f6g6BOwd7WeBpK(HE~dS9)OH)Sj2_tKJSRWb0Ccd z?P>TjL+q!n{GsYtM6Us?-a1dYKMB`j+<&X%8xmyC{B>oQnKdG89W~F#!#x7!Y~oZS z)$M6NIaRssVq#iAbe`5hh{4hdw%)KzJBI6Hxgqfb7SrSrp=-AXq@>jSZeTg>}yME5d77MA7GWO0s?(hkD6 z%Vhj?bxAoH8J})7@TzlMt=Ve$nzxO(`OSX9mzpZMMDf}gr%;i%{rYT~*&1{F?YEDy zU|uhSnTM63w7gC%A8m#b9qZsuWqG@NIz!h2DAESFZnb(Tx2h@13;;dIq)`YH1xgo; z8+Z{DzZb^SoX8vSQN9n!ZSjtsdAzSyP$0j$T(5PaWj~@ec|SNfILGS6i~HjMiEsJv zs6rj3eFoi6M!>qFhecw^hiQMlC|oQnyQyg3IGGE1OTNGYLeaxgE zA3bOZf;@b@{wL12N!?H&l$CE100kOjgw>w~Wa5f_FMkpEA6Ed1YvH0Hd?@(~dD_m) zAN7Ld8ndc4m}UXl;`k0xDurQcZoRUyGiY=Dnr=pq22w$yFJA9x=T#@Pki;aA5o%9e zKd=6y7a-S@^BUNR`0{Qox(mq06M8!b@k0SgeH~mcFx2M;+VOn1eK}fmCH=LMwYl=c zZ=`W^vW$6C7>tW))c?4xK2N>lD^|Mvw2V#r8Hxo-qqgDx(oLjTIL7n0w|AFZk`;y>qJ z^zl+6tj$l-;x`P6_csTs^oDG)>M#VmC1%Z28KLEZ_j~R61M4f$BbR#^V4by$D+ONr zgQTo`=UR>q+)Na}t`M|l_;q*A8@t4CL~J7ZH1Db0nh&gs0ZK~KhGSj><#k3PBG6x=*kb1{CDHI$yM-XCcd{0l$awW60+~cd#!1a7W@s4fokdNVF{noReS*#N)^r?|zykaLZHgSEu z7ngEl4&B7jqhA+i6KQ3oM+LP^sSOVJoSdZ9`#FruZLSv|LxP1;eWxwQ}d&IM1 zic~+PW?O&zq+Ei`GSKul0-e9f8wY)LJVvf>Hzgf%bAE+lZMDcIOEA^+-SqJ~mX<&O%QeUkX)Yc0U_5re(FG;j|Q=`o^B@X}CZV*MDBC zho(4`3K9!mE*&W4^x#Kk*g0_=B!|4om%9^={ z17m3ssa87C-y9&y3XHunL{_6iUP)Q`vEb;Nd+f7%Eldta+A%1DGjZ3?# zxabktO>BE*M8ywa-h>9xccK$u6S@oZ`-vA{q}xuCr2AT32i$ULDyQ3ur=?SAO!s zuTIHwu?l)&q&mmxt7V}ULJ#)4+j}CMvNNjQH46oq@Z?gV)8ren)1V*)KKwUR=W(;4 z0?l}93*8~8pEq-N>-wg)>TumH6V~=sM2ryFj`53Y$I+=6;kJM62t%|zwn~dskd+Pa zJ=?V0?6tw-thB|lupo=LFZiPkIg#QaN7CZrVmXrlV1OQ;m-}1S(*h_O*+>~IUiaiA z+m_zSI1O#FHiz}Nqc^D)ik97bA%Y;YXoVwQ`05n*)jrYP)u+Lfyd@ZYLWf8F+*2Iv z&)54EAgkcCcBO&D1uyl0(=?R#;KWVw# z>o{k~@<7s_G4OCbM&`A9OBzvhshTcfxHJ);6wqTe3dgk2&x)S=P^>V0yz53>S(fdt zKn)&}1_9Xt;@!f8(U)2@C^Hw@!G^xfpAXa`T#lN%+#0i2GW3%!ztiXX4{EU|T{}k) z#1HSa;BV}@Gs{IxnXj794fUXJHe#bzCx?qWUCWKzx5!LN^Dbl?@8?|CUjW6z$jdRj zwW>rDX%V1;Ntw_v?xWRj7>4Ldo6x#_4r~qt(V!4je5Lx&_|OoK=k2tCFIX18hsUN! zy&hU5Cqpui;M2^)4ft`BMr&(mB~AIgvx6~4w1EsLHm9Ax5%%RTO-@h8^u0dc$OW2;E(marpXgbdXo!B> zLjQ5#HZlSGz=9tb?OLg$lxN|2x;VlLGqXv@TXlBl2@&?}XN7ttu&ZXjX5u&|i&{$V zw%q+(D%~HSP$&bcU3`QesCr17$klcI#y5!AvxJvDcUC9PtxQoBABo{IPX?sV7z;MZ zZGO=xb?@OL0yZ7eD=d5@p~MP>IMP?22>tPq&Q00=b9BQu+l>ruZu#Sz`t%U|OwNE* z;bt{H|8F%0Sjho-mUiH)jBuT}n3#~EbS^BfF`zb`>RUndVAUDT%W&fqiYIbK#5t@y zNhrC%VvU;8GJ$?B6H6NK@&A@ScotDa(bhkwJdu4+%V9Z6%lWlf0{n^$*h}9SCJMO1 zJ+be^VTL@GkN4koFj76-et^^;Kxe_6lfUzg!y#)YtkuCm5<`O*%`Y!>O3wKog+{{eiMG6u8?@SH+?C^jt%4p)NUdq4z77dO?c~7-VrhXSQ((KqzYO5V(_cG#)P?+u&@F;qCa08K zs4ilCyv+TTbG4UkmXi$2X@Y1K|F>ytB2_ev45>x{DzM)}-3{!+3iup|ij~veelX-Z zoO@_^L;nkpzYM>|4&!&Y3id{q=p1C!mpX zUNf`KO~IlD1TS?T3W@yOct)zG7A!hu(&as2(O@^=(y&*^ zcd!R5MCYf;J4p+I=CjI($*?3kad7uzd)Ww_Q`=`Rv+z1PypqJ7>=MFmRC1#}3XyiY z4hnD*b~j>Og+TTFhLr$&o0CQ&NX%b4~lQi*Ou#Qu`#rs*eVtkTr$a~+w3iD(|sBAZ8rD#;B>O7Kys4y)Mu6~ z47t(XpXHOrb*$6M<>zauUEyklIB+pV^wlUeiaeTZu!(4`!tCp5b{>r$dq>+0}^!q$7S9skV$xNT|l0kwtZOfG>t z#NCex+Mx$)J}>Hh_CN0(BGo1j>kxj(cI3p`IPf){`Y7$?+psNPl+4#{B{pGGRIS^+ zw1;WB6Zb9t;h?qaR<8~$e-V;qAAIjO*%OSp!K3&4=WT-W<)B&yAQf-EEsRnL(94OmQEI!W{bM6U1a{#tl_Adrwo^N`CI>c);SW zq5|IOQv&Q)fALyV4RS;4^>pg??rc`5LPVKsOnoq$OceuWO?&jHQP$RaZ@z{4ecWsw zZD-AWOAgG9rj?u2aTu=HhfS@2g7>fAg6aPKq)6U!As2RBW>rlYSy?OzK`UmS=8aOS zTE<`kg20bPMn)>EtTUZmEzNzkOk0Re#i!3kU==S+QgiKFl6?=kpk)^ra&n z=fA88r4&)67MLWBpuIa+K=;yQFyZhK{1f*xQMRL>8-0<1$G?F*aH0$BwhcfWi1b6? zsV_EilR6E<4WRP+wd?jjU`7uBGcn8Y@${g+S+2tB>*?&WO>~FL?PN1eH^@L512kV* zL=Qm`pytO1$$Gh%hjd6_Li)xFM%(d1;y&UTqg+?Vtltg+*u&uO{fm;}CIiF(8uM+Z zZx0V3>PNS0E^+iKrnmu(swF$*9|T zZM%)?6R=zfLlNp*iGVhaUl6JLqaQ-uehNUYTOhZt`lEbgxWT6@|7Vfw|JO8v;1JcB zO_{Z{;bo=fh)TC#lJ1xxpN?bQ^{Yd-7jIA=y%PPu*!$|RDF1g&L4-k+6r@um6ltWp z1f{!%25F=_1!Urhf5LXQ45t9!V2mo7D+s;yoV21<$MiEpkz{%90?dI>u zAEyQj_q_K){s=cC9;s2I+>!yh?&Q9iIe zkS}o3p#zAkAFP+!{=E9m56t9^3UKVbYIJ5Pj(mOCjIf@>CVG*&Yek{ZPk&mwKg#yq zQ;}&y(}1a9g6C|=_Y;eG0fZmbdP0U8m8B;}L&5UmGq)o8b8~NZ?PhNzuA1GY?n7SJ$ca;@v@&aaj|1+ZR`_ho-3(j!)g4c;+7inV|mZ_hnF3ZJ~1+^Tgp?XbxWy>X9Y zf6Thc5>*zt@XU=q2VK+P#tnuJL@xk+9;2YKz2@7SUb)!BIynr7S^|dHQ>9E+ozYhJ z;LhKQhI}48>j%9s?NUCTtQx83WBX`HvMgTUHk3m{APm0D#B%KJ(J;sUwi&&=lM*s; z>|saixLJKPG*%v%fovmwTC#-~UOj#|btjppvO;WJMtkDsilOXMN(I@<6|95a{EN@Z z$)WUxLU6H>${S2S9HP}<$iNSXOs5Bhrx;;%!rLZ`5i1=0wLt=aLI&>MBtu?vE#vqx zQ^NfZ5#ht-UoqJDiOh_YyK+ioVj;hHBL`I=rG<;jSXkaL9>vEQq< zxui^Ksrp|18OmD4o{_zG*xV*ws&rtQI-ewn6uDpYmp52IMyw-c{W;Ri0vo1TL%k5d zvzLSs0H_J~(>vjRF|;kX5GX@vsBt#;Q^3TQX_>eX+uA1ds%);&O=o?0UY__uGx#-_ z(K$Tn73er+BJ%dx!-Q_Ea+yrZTH#bNtmrp>Rb{!)`lZc!iIFYi`W~RUf| zZB-8Y(#*$5de$E1IzI!tIBzKN_1^!~XFjjUAI$_=OI>XaKPZ75AgRzcbDri(PC zQ*DZm>5+OJV}wX*G?1c+1)sbQa*fBOVlgjqD^bj)a5Lp~jP*jHllQiT z&0)Vjc$rKiQPTxy7rjCHJ19EvpqXb3#JUcj3<q_$t(zRfNSbOxxyYKL*_Ggiz0kTB=L6g$zrx)kMD6mJFjd|5qaDR4Os$E%Y ze_}F^?(llOS#$;-QT@gsee9`7FA0|$cE5W*Xb=;1Y;?yQ|UXulYUfqVvaC$o0pkD$3D$e2ap@RD^d_MESF&^G?>_fc4 zS9iTu@CB5h5q>tX>VS>Gy<*mNeX!`h6X7+)qo6E^ZK(tQ<@wzx3vYD+H@$xc} z#P2Y{l>|Mxx%NppiwEUCe!GFPEG5UO-=nlKPa;?&dqU|=0ICpOl?XW!M=I=z!ai(u zG6IMAKbc`iBKB7qzU>_=-2y*(A{MC_xWM4m0pD@P#i2ii8pS}ai-Xg!`eg$!H>uh8 zK0d^GPXq4iJ5{EMTD+{ZW{bY=9d0c(vt$%yx#!%Q&353usUA|8j9yQ0rkEZU9@tTAhhrrW-NlR}Qmm@h@BS8Lr&okuTWx|d;WAHq1auv_%g3iZ=jXlKie>=l*+?v6 zSVZ;3JGdaL+$e0yWDS+e2^JSnerABtEs0tDPBfRF#xtZ&T` z5!{_|8Kd~=@XLpC7KeIOZ@Bw}I@pVogH5StG+z~? z+G}wGVD(%$jqOa>;^uR2Ve7=wIW(v{#@4`^+Kh!O`h$n`>~x82GpktlLl>!oQiW|{|c!U3`oNfNVs7K>bt(Y15T?d*#O-w>ml}S%M<#1W${rQQ|(rN zKobiW7H_UAD){P&$`?@y(AXsO_Tj4$m)vn!Yu!-Z^!v1;BWbj7hmeetJCijhV+1ct zl2LA`I%w_P|8$p{z5Nyu6}@EZZ_-09Bf?pAHMmR8A)Hmg6}L`n8YLO>G?clQ6rqg) z69SQ)5XW!rlQ1W>f@!cY6!Y+GEp^Fx_}jNMvEUvX1|%dl<$Bj{wq=Uv(Fx1j%08df zz${#Dy|P1!WPFrD=R1692?+^92PWOk<67Wgygl~P$Kc9)HqtXjt>$YxlFi4&+2IV8Nv z#Y?vGr0&Wq;FNX$0>UQme5QF^yKPhHIX_KbV;r?Tef@G;vNZnMn#b{b*!g|`qgnsv zi0E1WtGzWt9*RenD1acL@as5spQo@hzWY+g6Z5t8`OGHu6fZ(&;Ye|oE4Vt(wHq_f z^0-o29M_ zw2SdW^5Uc^F&()Zsyc1Oi^ArJpqQyl3mG~m{W{1x;y`gxupKOK3#lUJY(!i_0rA}<*RF-pFA!VO}yar8>{Qau=qxUBG`<}x?A9NYd-=v zzJ!X92FIVs%s9w>vaq$b@^Dp@`CHsGV@l`fzBkN?w3a$5br_hyiHmB+P>9Yr_$oc` zVbbC8F~#S5vR9qtj%|V6?vC#Mz%@a4^V@3j*GqXA*&^Couqldpe!Sx$DjMdPDxUYp zdU{RRbvSBge4F=;Mkqokb2(8$zT&2zLcI4+Tp3>qP9^MiVVe_w3wu9c&tlsA6lsco zs9K<^cZei_ckwbW({(*fN@mVvqd2%>>2fXjTtqeuyD{A-*cZ%*`s7E;10W?)$OBRc z!i8c69DrZ8udm(61W5ZXB>=~C8tnI5mABxBfTHLL7?s|>d*@f!pZHWvOjI;&weN8* zW6sEX7lJd3Y{*d+lRgaSOyqu9yIsG(+s0NgU7Wl<@Tn(50UezNg;tn>%JLeE?ZA_g z0m&>%vwuhj1|Ta@+l$8g@0q89?)Qv1Ym7g#o69MFI`Z|46ArReiN@s0d4|XG- z4^VM%2|_)^wA>PuU@_vxDkV2JgO?N}*i^jhRCQtUXAMBKq1*gVp;ZLPqD zPtn_RdZK&u5RwcF{N(w97ZPAU=&y*|ckM^U9hX85oLzmEU6X-qF*|ZHpi$4Yug1Gy zJ1svP0Kbt(9jYvcx})jd9RX)iWEhhyh+L2w2L*^f10K^q7WG!J_QK?7x$hi?dN1DE`=$vcctb9E0Co&`AG68@XdfF*MMX1=fY%aQ#6NG>9QvZkCIrkshX z%rYgNlye0-MzqUU%kemu4>xbF4CiGP;gmumoBBb10yv%9{uOLbf|bTTeHIZge7nMQ zF>rBT##qWLPos#6o}0lE^Y)A^a+}&`St(8r#IIlArnEOCkWZ6CR7(_vk zP1p$khez-umPL$lXgmfG9DDJigrbb%0kO<(pyIN?&umVG071N!X+Xjkp|=%px#9%` z7kCVb$sXq@j92?`nJod@W(6pxbfOKs2&HYZWSG}aL_gO8L1R!EiC`C1U^T9| z<~JU;BNM>YkA4<_GJ{CHpAvzbe==Xvw0>-W=bQqf`cbbjfk~R$L6n*KqA)Lh>e3c$ z5Jw{%^i%5UX91`&L^uf_IRD%oEeK5_3*l`9&l^x$jQRo-WWgf6P5mQF^PR@tDSAkslB2d)mp2A%UtHe$ z=vGZo2~QSC>%YTDu7w|U;9*0G*~CZs`ETtTf{K3g5Ha{#Zh-b5=2!lQLIsHX-y=TN zFLnl*Hl6Sm$NVMDaQyNgOEX;L5kst$^e5BCqw*FrUD%lEZZ9AZ*G+^Ktz zP_{?7Z`d$a!BbD$GVWi{KZKVUs4w|;qYiIJaR3s z)3L>Q#r$Bl^I4!_e>u{80Mp775U2@NdIrE zH>hAS_gyjKwFnZv9uK?Z?G-&bfenj}$i@+x$3>S1EmZQ5+y~`3(N=a*zw`Uduj_EQ zb^i?+jy(-qkl+O3JOGXccwfjGyk&#k0P!@gM zXWRZz#@2?pq{C(r#D5w&J|C-Yd&mdF?t*uoyiGXAfo9EKzGQ2A!ok`939?0z2H6s# z)AaW7dEt3m*Mjc>S^d|?I041!`8l2TEbS^3^)`~!j_?e+bm$hq2AE&$@~g*78slmj zBS)Kk*rVlVBw?hVdZs~5(NCeIL;dc$iU)lv6FO`+>k4^}3BZwGI=MwLem>ug~ z@_*PcXvp}h*rbA{2tFM-S}LPUk0EL zP7`gTi5n8%}1_-~th3(Xcu+~CA4B96#dWo4^q`r3$k{53vP z2+IrmrpI1K$Pm|=cDk5*r0>Hh8Q**&xWGU$)esHQl2>9za=!(tP&Li=;umU2jUduK z-zGvyv7?>k(_u*<6De$rDI+$Vu1J&KtL%{ORZ`})b$y!6&{Z;TP3GtjCr&BK)m1F^ za=csXwIp9)Temj3NL}&wYA9v&UnPPc%puI?eIp@)|44H&3TQ4|{u7#u24~dKNi=uI z#ZO-*&$9rTrv-5^6`jcoiRdf!mVsV8xtww~gYlYaldfj(Io1$iZgQ%(1dlz|mMGJp zK>wQ)@7m^=maAF#jM;#Uo0m(nJvgV;yF!Wv5Wp`RmRw2RwjZJs?x`B;J(`*! zITLu=tL0yKL<_S}X(Zd65v0GKrWP_EmOc{{0mK#)fZ8B#k^vVT6sZ$*5CU}8G$Q-( z8)yO2f56%PQdR6mK67+*OyGSMys$s9j@=dT6rj6a%elt=KIA}`uf$)RhmaJ2^KhQ> z7v}+?s3<3q{hx6j_Om9R&bi)nHaDlQYz(PjzdRmysP=ZU2XXW}$0wKBC)#hF4K53N z9lzv@@S3XJKI4?|1-&V zv5bM~j4^I^{lpCg$%{)uhL&}&m)eGhUNzB&+{vv%N`{(VL1|DVS5%(<#oJUEMU)A; ziIFcTL{*#E#1VE4Zu{;}2Mu2{Jo*~3-K8iYe+4V6$Btu#nnQ^;_2~86VNFAn6mBOM zEdwwEpVRMTw`BCoj6J(u%UsDUSKyiHf<>KIBBH+hD7JJR767)b((Ygww=t zvvJ%*h$O-gB8jds)pTa-?O$Sp*fW8#wOSBh{ z-0|EEZuwgW8kaSPDH`^b(61HLoE)3h4YDR%)2K1*?O#NY8uT)dR{dqqd@q1_GffJx zW)oLH8BxNN;y zQ=7h&L{#9F$KovTn2bT+fke6^$k{5%#2?;6`9q5!aQ_0|AKk-6hOE;yH9J<?R}3 zl7amQpNwjVt!BaB>vO&E$_w`wB-O51+oxC7nPO_1cx*E@wA)xg{eIcaH02Dth6a1PiYsK%#W%u?jH8jUo-2_AqGLirTLZPJ7 zx<>S3{wrzyBHmd_D}X!Q@>o|$uqWrXqqLF&?}>cJ#WgcKg(linH-}}XN4(z$tUbp@XZ;>q1%-g-*kdcMk}tGSz2xz7 ztggO`v^cgM#!m_}5a|TP`OAKe#PfF84D(jNW#LgkI$%;cnDAEO!kh2%>btWYic-T* zd3DEd4?#7_8A7EyocFAW1olK@!y-GF;-$M1y^f^ccn`RH;zb6H#Lt#J5VTipH^BYM z)`=yV4ZR%@*0cAUzyRbEh^)q6CK6!o1HGso`dn`p5|JA+{LprY5M4`ZM*L;KV}9iAd{sg|l0kj7 zPju_=<>1!PiPSu#i1{?fW~8HHN8pSgHgE#OhMr%<2K%A3G|IfSMV0>D_!>a~aaZS8_p`$h1p7C0ht(3+5p{4FgoL`Vw~BmYmO1)D#l z1trb@Us~`75BRUtGpGS&^#8&Hy_TiS*h<8bJx4AmGC3u!h8#VD$eG!3VW}Yw-c-O-{OYQ88Id9==<&l4!Ex#`TJ4ABeaetg|>_$JMO+8MSMtBBJsPmkgj7~CtBb2rXgbN3|)3^O}}R{0KrdV3`@ORm)VkB#$!#0 zR*(AjI~rr?m++Ed1)*vw7RT8bs-g9=SKf0eIuikpJl>%c_xy#OBmr`+7GI*D5@%un zLb($Dr+LW&D+=Qb6+PqogsmB!1fD7_lNwT6TQ?WIywp^Rw2#lr8{|~+is^$XH=>}m zRmipp-6baPr2SHxEhB7oLkJP70^H|+L2t5B#VrsIpJ=w?$vHhtimz;p8$ zxD-NUR!!&`J5*uGj_<-aAoC}}C*?O^Q%esDt$C!LG{SgJJO}Egy;9crVFic9)Z;Js zxQ-<_j7!d?p@bSU`OYnc7HF?9^*;M9hOZ-`Rg*707R=i!Ne6dFO{cMM&k^~jGP609 z8gW|Ud~@Cr6PoW6+rlEPS#%gw_Qb2YRauJZEAE?wr-_x+7dE zgLDWlGlTS#eE2j`SrvF!3%4@&qq7b5$^GdM=v}hMC{ZLMfo9XF+wD~PtYzFNi`ayh;+dL1KMM54CZs_L>Zh{f zCKXlz4-P?NAF1ol+dB!*?syEn9R8fXYX8vO)03Zrni_c%HYm*#-dRgz6WoF~0d?ZM zI&n*0nqAhf@9gs<=y5{3JW}x^#>chk$hpykYJhuz7Rb`}%`2V=IZOZ-uD0g0>Lf_B zG(T{A*}lOZ6veBcpLX6XSX}_V+iG(-VHVX~bE_1Kfi1$u+TsYm!2h{4V#;G_lm9zY){7^Z7zW zf0HQVrD!|VLI4e5U$CSVRmzF}{(gnTepKCBJE{?5!)4MAqX~Xs)=u7w+B%5%clCu#j81mHxg?`A zL%MyEVAAE{X{P=3uez$TIp)$(Kql^1uSoFS^wJ><(#uaR+c>H!>Xo!E)^4-Ws%*?^iS9NK(t6;taZ+VpwX5y*F9(+Nr_x+aiqIdMbdgr7;*T+OZ<9 zFE1Zp-jr~2kcZ>35|O`)>h+8U?AG0xeACt$W|~&wTU&C)Vhe>!kHyc|uc|QnRMr#T zWy_di&&dt6oeZ(l+(NU|FX`5}L1v|U)A4=8pG^Ur9UP!L{~enk#JS;j<@f>=)%>I@ z{(OPO6vSUCHZ(GVLYS51sm{;Nj_0g$kLD@@0>_-5kLuIXI(_2m#A#-We#bD-EhVC& ztMTX|O^5r#^z!%brV#ni4YfxXKxer<-?mD!m%$4R@kHueObqudK0fzV*G@E$l6+h8 zbp@*`sAcxcg?XewrchBXK@NRyvaKUyF~P%8>^n?i*v>(m=eSBSj9Nc*B8#Ff2d5Wfau%mJHd; zIc=#k>SoZaO)#SIX1}RqErdzn#zlGZJL~ae=ZW*%9o|Sw`kjMO0ohM9?=(4uRcs{j zI^A|QGNR!S5~AQW2^AE&(7k=0=COJ!MM$d%Bi%XdhjINmQ~D#gh)EiI^B+tS zRntS^R1T$~advZa{bhA< z6eSfE(gpnTR8An=VB$`^m|gY?o#%7|x#S0rk;gA~5T zyh+(n33|UEmAC^XXD>99pw%w*@XVYd^+%V-P70}oLjw5IX-t;U>FKn6l*=DnulpT( zhvsaFl~3F%*FL%>={^fTz8$T#8b6qo6f(6-T&&$+EnZvo~fAS#CZkCg!23=wj@*zVICH*Q>C7fv8` z-njAe=}ypD3`f~3>Qv({LcVo^6Po@|(Rf6mEl8a9&NdQmnl4W zf(r|6`8qtufSRe<6H>Q1mu6U~JN#&T%M%Q@?TKN?5v#Z^C@LD|3dW*PyIi8U z&ixPT*Bp|x0zLG#nuyU847DWh+MpaU*H2h zqH|q=M+neMN=Ps`k1c)=rWi|)f2{e9D#vvn?47l=EpS=AxYrUN>J*s)IS=gd6BgcE z)Vt5{Fe*wdoCn+*k8Nx7?Ae;;>(27?b%vs6jMYms7sFszs|{7%bo`PhsRbXZ^qV?^ zHuAw2t9YTou++j2jm=8-YDoguUlbEn1_~;8E@Xez#SjApTlfYN@UlR9+iKF#=UTjqi^{HK^2Hq) zSy|O8j}x2rh!WjT`{1kn`|aiBZ0(7*)j|uBQRK7c z9{9ZWU%#TaK1xg(9lf%@Fl`^Sgkp6HC&9myj~B#m8W|emE9Xbw3w~ZMbh(7qkvsEE z;sVdV!|b+-roUa~OiB)OaSfL=8JjR``raxN^R35JwKM})yE~9NjSE5hw$hGHJ6!+g zbX2ujEyYI1c97~vMdkg|UIGz=GU$lgK-|mv9`XYe@~97iX{>k9MU1dyK$N~WkbxIr zd7%BQ=z{3T=tcz1e3?+?d>tT9DT~bLq0jbZgj~Jw3)Ri`9=!Apkuu7(2nZPBlN&8kdLjZbBQMh zzQV^yK2;?b>~7V4r=4@!jDcWuqJWR4Fhkr)O9iUj^}Jj?sJOp^JnqcMjWY)+ME=hZ zw{A&;`1^oAecs%UfoTDhJa-U7M~r_+&JR--kKOj zCqCCIrQM7>?q)AoBT9Cmw#a1*;+9aAJJ`5|bTTqaP42pfquVL46-7UH(p7?QD}7+T zhei7Bd?vL>fz;Es+8TC;Sk3R&?*aM>7a&I8^*L!;>1c^oBza3fR_+4yASN zQOW6Z)sn{ja6^Oi7Y)Q@_p@GEM;zVA%$_O~v?1kn{4nMbtE_x(Dez~f0!#c6my*g{ zW~G1^7a5B%ALr)|2Htm%Y2lFt)i)Tb1D7VJ%{m@v)E07lbA(cBLj7luH;~Dwghdz9 zr9~Hz#5n)FEgS`0bk{X$jhAY-_VHgA#a>rH0)9vC-~HANbidA}2UNSh>BPUco^YBJ z`de&Q{{M%*%s-AU@@#`jSJUmxER>eEJv1c5>mFTrYVL=(L)!2DS^FEv);PcfD$xAP z2^ol#1I-XzfBJ;w*s;IQ*8U!V|5|!%yi`16#-!S(Z;iF19ey`9i7-@h%wLT1xx{PV zTPo}nel4ea>U3DtyT(9}d97o=lN%6aFx3%RakD_E$?QtqsN zQB_?pd)JAK}x5ODo>d zL&0py&sX(;<4{n;CA-jVRO)mi4MiwcKUS`u`v-QMm{eC z&!6skMV(h!@vFB)4O-gxE@6(My@hWOTXwBoo|67M)qUdCcElQ|v9v%FyPH>8w7M$Df`mS_9thPRO@xG@M&~Si-aq~y%<$$(Dlm8u7gnLOOYmVaJF?tfWlq>71RRVZdvI18R09G37< zvlZESNx7)XwBtpZAxoTE~NQ?LVq0Te@c=CBV`-xlkavskW zPn4EItM5%akGo@OnNPg_V{cp35eo^vi8$P5caYHij&OkU>m{En^}{RP@Uz2NKLm<^ zV_Joe9z8NyY{sCte?O<3VjC2u<&%tuCRDF-n2oE>847~tjdwy^m zV}8`xNQsV-H>&-qSHKYqzsEFawBnDs)?ySE9a3V5f=Yv=L2^EvfZw?CQ;7JrF=zGC zhH?Gdf{U__tH$;4L=6nm7r)C}ryLonjx8n%ZsLyS99EDA0D5-Cl|x6%vxtxo#k!39 zHdrFE*J)USK9(m~J5-=ADHe`h>snhl(z|>Dbactpi_65;_o2@C+#W|n=CIw)S|e;F ztBVhU#}~J9BDJwZl=dX1mgZ8gK7BoFyzM78#*Y1T#vRqOLd{}0xdKvjdW<*5)}-G= zM%DY0)#n(^HjkRYD0*>6#_}bFu2sd>gRH?avZR&Iu_T8ZW7<$%cqP&RoNFpSU17a) zIcKmG&&;-zJUEYL+k*>s+tP1-r}vwj+W_CC(X6HyG3MyHvVl!@&qjfbVqsru(L`zD zQP(nR)w=qOVj)y1Ni&vDW9)LK75g*?Q+)<{%*}1YqT=TM zy~c-G84o%yVw!S%eagw5Zy#Bc3>a8iV}baFPv&Uz74c`mk#^xJKjhulCH!2qA3 zcRZkC!NEmNG>r|UJOzVHep`V1h{z*9zmV2xsUxD^<)kHpxh8IH*_5A)3iA2GG7EZiN4;2sr;3&u`3Y@&*ib5c^xc~-M#3jBgIjP z8ckU{rz#$JQ@QBT%>YxP+jEff+#})2>R^3vWYq_=zQjy1@*?fbiCxo6kyuz1aoz`5 zrJZd`rTR+d;r)9DN#y<|w~nw2owe%C8eW`uTYi5sW6J~=G_!j4BH}1Uhi%Hx*>LH= z8pA@dWa8+}Kq9YwgTs{&y`I9p-y+@E*TsX;iQog~vbd7~NcYfrcLPkZaE4$;EX@Mk z)H|%Kx(D8Z(q%Hs&3}3|7xE(1D4;o8^|cLF$db}|ZMBE_n`D>1Rm0fST7wUU@Xru= zikyd2&hf6+>X4fhzryMpe%0u;J~dKJSLa*qoyI_RV$F+--a0u6l$m0waFKESE3Rb? zn0`eej#a`9c>c;cg|zPNq4@R3pQsa|%dKb>DsJk2RpwaDEmI$4qckuMF41;I&oLkd zuuLDz&&IJ1@x}~5O-*HQT`{p~^{W(xDi1BKL|IeVSoJPiYDv|F%XMH&in5JqzKxUI z`=%|ux(&HllDWK^l+cBjQu7wo!D;q`e$yffRm@VIEdVYDjP=l*M!(Mpk zLkRj^1ucphvC`=cDviyVC|f7L>S+$X7iHexS%dV8G1MVc|4h;`5H&eQv!b*{m2H8E zsHwQ&cSO{L^?)Y3N9~8pkz=QsmR=Ia@)((Jw~w0tWzlP>+eK_~kP* zpZMnVoYuJ&mrj<<&Pj^Hfv;Zn1BA%nPiNk>bKX2aGi3ARoZ21M_MoTdQ197!s)>ae zTfMRQ%49y0;qqB7RMV@#4grnVb;n+EsYc^qC9@K_CT`QTN1aOK>?xo>O&E18gxMIqE*SZmu%!OtDozom@-Z z6ze>}p4jWHp0pCUNyn@vact_*2{#+RtIp#C7YkMI)5y>ef|`?bVy{!0>1tqHJGgvk zgKp(mZaocixCeiT84Iohqz23vcmhbu(RaCAqy0=sz z?k*vV*Qf4Th6CY>p%`4#gu#o%IcwBvqmTcbY)m{#s`YFmz<15SxY1i*q67KXg|PUq zDUBrfF{MuqcN)ePQ=^TTs&^{0G&RQ-&od^+jO+>@fXA<{Qm<55S-a=bS#tlTVC%ki0D<5& z*0nDB1euz9g@uC52%g!ZZeQN}8p8G=}jTXcuEMY=M(VAmzswo~h zj3knTD!G?4aCL^c=Fg<6L!Wn+`rqGI?I9H%7Y?|a`fD!JXn?%2zBbr5umu+g#c+WR zQCLKszc*(!MkNoGo&3h}z3qA1idL(MQjr2wEb_wBv0yIC{&+t<#?Ddi^i$0hhW2&y zM1i%jzI4Xx?^W{*pQXXa9n$d(HbGYBx_CZ^!v$-_ct_>!}w{dMEm zoCMaF1Th`vgOHH2xP_R_2i8Ky?~hQvV1?Vu&wiRXCim|ME@7f8K#y;7d~weYW$UF3eOdKcrs5dQ$^hHQ)R#7}V*d1L z&c@zP(amY~3_pSB98FG6h5~ZLDmbs}M)hvZNoi4X*7~4_%VvyrO>?H}AuN^t+#|6w zkB~%p0h3X1TmS`nw2==F@Cnv;S>a0NJ&g}&*mUQ{`kgdmKLG{^^;800P`1-%pMiKm z@7sQZEnmkUe(2_JpA_g72kDhQT@*A-s^lrh0_?0TB1WU$qh!BycT0=s{`XxN6E?9C}4TFcC&o<75qfn zoYGyg*o-ABg6t{cEhOm|RhpJF+#?AQ9R*1rzX_eH#QXzBVCUYaq{_`Pzw`@CD?_9G zwWh*1{8AX~25|j%1|~<*MM|YY*qNIUAVt3{EH32t>H;CKV<3Pp1XH8?q1_q%=WPKQ z;EXKUr)&YQ<++kSZ4DxfOA|vtFWk=n;OMWn*A)@zO;kk#Ey^s^oqyu5k3j5*tPX8# z_UZ=AW_+nt@-Hal!w(Rnbmth&)un+Pi|a2mh3GW*9lIXnWU=exY9KFb4?7d|4=ppVY! z1i#|_&+>>hOmK1(-Cc%U%9t@SPL-@5-#8>DUy6Z>ezJb!i$9>DrmkM#SXi!OFMN3` zbX1}$RO}pFxPD9+PRc5?QvDy<}C}7>Us{Daw{K|wpgV}teGZkqC_lYt|g~E?lV?0(hYae z_f4l1?{{F$DJ3~;DhxM66pl{lrko#n_NoYKnHk-}z3reXC*vc+I=DeaNa9FXwEacG zpmLZiI5}^|m#Qc3clIel< z@RJo47EJ^0!mks8{rga*<$xUBMHRFynZ{ycW7!<$Uq^+7DNjatolo>0eEm9R=w0l` z_lT6mC8z3t|4F^xgQOXUk*(8iJNV=6h=r)Q2~Hg#g0b(sOerp`GS#k%~cWI zz2N&#cc@MeF2)?*I+R36bZH$GR%7hA(a)M(yZ{>-;j1y?2EyWiWW z-Jw0Hd#V*>RDDcaKszz(yK~k1WNfvF!(S+MW<}{MSFcGTe3hAn(^$&gIKfE zF-A2(%lTlwq)D&M`R_i%TAMx-oz37A9fOd*JvVvbVDoA^2K>fpW<~Ft&hWKu?3e`U}pwB;Blm( zF7sYBhN7Ys2d(A}ky?&UqS&6Z-JMp?+a+p}b5uu1$EdGQWD;+g>kmB^YrRmXs3Lze zc8rYzu}oEe86pBN6jt?c%x8V3PS0DigO)m1!&ai?#J-rcAqyn)p&mB*V>yFFnWrKV z5k$3xpS^SR`&zp5Q>zbh7eCpLEpd#x#rwSNoCqqnjxxXSi>`T85Ahgkxbh^*7t*Sc zw)c$_1TXj|3)!f!S5Wa7-DXSP`XGblM2RDIYbGCJt9px2H8VO8AI#@ohfnW5EWWY)ES_<9`XR$&XBuV>`TKil|tHb+wx zoMv#Rb$EYHGo{4UM4;hFB_`H8|D4&Y3EwOAQqkTusHqJ$(9BM=bCQpBM&_SilpjTG zfxg~@yruZz_^Vhxg$4K~*lSQkKXr39?o2xgYL>%!C2|wOZc4?PDfPL7KC`5Jo+`Jm zM)S=~lUOIOhP;H=;G2P~QGaq|b^kZNz%l`G#JLf9kNp-q>B<5yi$)Tx!sg_9D`m(a+(1!aK3{xxWZ_wNs6E{JIPLv6F-EXsdjG(( z)0#Dz*n*Hb?2<0BGVZEy)Qz`2d(x;$F6%sid9H+e|KSz`0&+-=;K@sUVLrqLO@*h! zad*SUcT{+Mu3|%NdK>wI7CaiP`?@dDk96-(4M3QsSXqst4ciEj-9dNW)BcV>hcp1M zP$uOS0(ggo!+1Y(|8pN)&)?gFoN;J1uqByneXo<^em@_qG2^t4eUSbkEiHpw%FBaR z3OAUL3a00AvBX-uqkKnZeiExm$|8iRS#57AN)~^-xC-vFJ(0fO%M2-W-N=e(1PZ}A zbSPf=R%%&S^ED~{69-T^p^Hmpuk}Izh+3Wgx(99&l4I>9zJz?N_6h3|g1Gq`Dz(g= zUH4jY@eM8$ajl9yf@pEfi|K2}*;Hxz1<7e{@8s8(tp_f0oa6)rc(b}T*~X2*o@nNA zchrDcJwp4bD-SP68P#nn%8=XZY_M8YM&sACqvEC^EriHrrN3_#bPr)0))Bis2SR-K zZd+`AOcRciQ_3 zywhB#Iot5T-re4V3xGvcaQmH!hKH8BY^tsYN|@22cwkL`2wOrTQC3vfE+{xr*A8=~ z#8x=_QwO(q2jMOjby6~H@Q->hP4Nb$eRst_Kizy~`D{>Y;qtR>8|pxgV|$gYV7lw6 zcOny`ThkF`U#Q2kqmT?PhM>!0zk^;#MKbtGm%Ur-vbToQrWK1^taMUt>i~bf)a>#0 zMOmGB?4}K&!2xLWy7_ZlqAIvpO{8(V?&`PK2v(}gH{Ur_;we7HXdh&h zXONWAmc1@Y5L3NsvxF$~yY8la;*vt^#;w#`_WivSjSW@(oS0^Qh^kkG%D? ziwWPb2iNMY;^E;HoC8T_J5_Wc9i_K^cn_0qB_ud)F9BIKj*^tACe8<()W09Y`^6jl zsUJc^Mn4j=^QPkA^;Ke+s`p}$K?F0U`PDUm@(|iU!W6Vj5^FBpiBn^7|D1~@L^43m z3RJ%das4~UWB!DNDbzzdfi16d3krrizkO3`UrY7TW3^3N<1Zx>j=FQtr zJ1EMklLz2~99{3a&5DW&{EK6#<%QE;Iidi*|GNMV8>9GH0RNRU0%@5D$=Nfd^SdDt|aw6zYHRc9#5dpA~ zDG@cqjWi2fe@y>nmOrMO?-uW4Do}Hzmf3OA{wvFFCWf+<0Jb%LTnf_m!lK+t3dYlB@YQMg=on^xI-+{s_ERG}a#Wxct>jNf1cvS2Kvqy1g~ z#(MFet$&vsQPx#}^4|Yb1N~pBf4lUFsP_|(%p9SVz_tOH-E##$HE%OkJw@^H<4Ul1 zWMqZr2MfzWG)m4<(YA#`}RJG!h$_XoY`Wy&iB##&*>q_$%20IpzXM z8XDcd&PLy3ySDr(+Wu{^N6jg#0HHU;xNYcFs~Ehuj&e@iF(d+JN;e`V`x{09S{nf;U04Rly5GW26nu z{w0r|B>NN@`p4-)xB(lPd>DTg&rq!87+?r-)|1)r6W|p^)VXQ0Cg6HbS;?JS-f$DN zD)?L@7XBl-VNB1wuc_N_<=NtUtAOr+EZZI-e|_AN06V`~{oS+b0EjO+|X##m+y z_nAWd|Nr0r-v2%4o_p>+=ifQf`ON1`pLyna-p~8Byx)&B_zo{}m_7rUg4c{G`h(aS zn37_>S_6KrD3$sH^~DRQFWCoUIvR{Gj`vfG=Wg9=q`g+3veHK~@*VXfL`Knmad8zP zbN4Jk>RRX`K-{*P%_cu`(7A2u()1{`!4PrAA>Zz0>%DJuW_Iyvj-rde7es(Sk}oQL zfT_pvE)&;q^?jND(ek|;54eZLA7q1{qas$908(mxXDi7Yx|ZSOv9`EPZ-9>2UJva+q&ZEc&;EF!DZx{iO>hy~vnI-tc9qLzvTdKisWye;?f zLEeNE^@)d)Q@BL5TQ}(` zb60S$Pyf!faJ)uv{0wUSH&`1F@5%)ag*9@-Ded$}arSA^<-ED=U4fsU{v+mn$+)yy zv$^+(rhtGvcS8u5sl0dWbuyY4ne28hVt3kDA{L{{?!cH7na3Rmf89UTU*y$`Q8tD4 z`K7f!Gw?n=93MJu5RXmw(523~x_n-ldhcwZzTf0s$9zal0o!D~b*6$x<96lbqKiq1 zdHOC$7qUdD#9V3P-1ves<119T5ncZ?=YVimXVe{X!}^R`Xmt#-bYjTK)UzIrWwhN^ z=BvC_w4o!BdoyX4rX!L!akSWh*dU+==T(i?_y-ox=HT;&Q z`BJz0X}k}vxurudUojQ*9(XaH_;IrQW7+9bZ! z$bLXg%xPS7?w$UpsjE)oD>ro!1RnS{(QJdViLmWa+L3zhFj9+#&Sx<O=ccd^*E^xsg-dKO#z`@os*|5x91M~ zEy}t0RXGgo$aD7{CHikya~<3r{Ju9!$%ZaW#(nB51&4CTU?9=j$8#m@5z&=6lwIpd zi=^YV7DM1Y5U##doAwOkqC47Xq*F?z>PSXtKb&O56nQwWLeDEXQ&U~xQ#wIHeNPf7 z672MLRkX;MZH|fCx9@5ZW9WLfS&*W=>nd6qjX_R>k$w@d^W!~ zMFuCSaBb##H%?*aIx|zfV;vds-vpy>9nbHzAjeLl%rzCl2X!X|$CXt-GOu&+jkW?27Xg@6cJj!WjhY~tp&vw>|uvcYkQ1ty&T1YYOoFR{bxJa#0YamNNik{J=;r0s6Ih@VnQZArNIDZwEV7b(^` z^3`2^No$n2&>uZjbB1U=ZO2Fm{!XIg&0;##Q%iBjY~@O|aCm#p**q<^{E3i=!5mS| zEBKG4O!`sjZ~mNgx5d-Lj_$ax^0%Um@5x)Xv`AONsMYy0Cc;mD6a%jbuJJx;>t*jx zlmpKxr4~{vbklvFNBx5nBrf0Jr7){wijf1CxOY){40C-uB;ZX8WF)an&Ia=HjUj=e_8yWm?XT00c2><~VwIqcxxExVxh*tw2d_^sb^!9F!}azUqIO04RH2)^{Y zIr`aK?BSa>iwHbC33=Wlg^J^F;m(!lZ!Equ5!111^Bw^edLBpJDq5L4+ukU9p7E9h zQ*<|YIpA(tne@-;@KKAFS7z(eK7{izND)rZvsszK3A)DRb*u>iiDxsO>x^U5F&OL` zr8834nUas4k3j=H@l{_G!w9iN6rAiwaeXN<(_LD2$xEwnZBlZ(O%otIg{W{xx*L`d zgj{W2$Buk`X|yV{%&o`wu0$ErXP(z|NoJV?R=J!sN%R{H4c;ez2^&pIutcVoONE;4 z^X-DfRipX^JokdY(wb}j3}!)nMGJx5EJ}PdrH%zmLt0N$C;d@mYo7Ki-zit)a&`Ax zx5x*fkwC1Jah!Y=$$bi$yx3V>YrlVs0}iGR#7aEPBC+t6ze2qy1(v4#J~@w(;BifD z9V&(Z8RRiPJ7O5DqPwhrNFQE*DH4!zte^l^3g=2XBL@mI9>$&(6T>54Vv{;_N0B{M zbFCMqOm{jK@$-n-G5n?k1N#WkQuVHc#5=NQaOpeVsBLSaw%d+!xh2IaHP2Hk0 zLLTU%u5_2%G9NCjI~SiEV6FaJ1d0;C29Q4i;Qr;UtnHI>&a8X zupW59%U(M-{~avj&wo>+Su2BEu>mcS$eoqCVNM_+Ka*41 zx%>BxL5cr>SzDAS0l&NP-vYZb04f%E?`e7D;`dPR+2|Gi?lPQVJDYUb*6UB24Nvr$^RbMz5CDdC<_W*ox0o6xTXp6bTS8$)#l^zcAE&t9ytNb+ z5KW^Q{O}3#{;?UMU~ZloyDK4k5maHE^;lLQ%UynTiMx8f`{lE|MU;Bs-9t)s7cP3K zo8uqul!!m6SJAHxm5mR`vr~6;^ZeeQ6Y2D9RIl%KsZ+N@!&C|_v}ELP{^7;)ftDqo zz7WAF#K^hPkt$|r$;5%Trjx~C&R=_O1w9-cr*?|=r6{<)e{^lUlyQDe*hKjxG8nd+PvxB59Z9a;*v=X zjppLLAZt3)N_v#;qVroz3z`Q}vRJ!7fT6Y|Tirs?(qae@A$M#(*vG#WInsTf)9m{r z2XAl8g*xaL0gjKh$!6BD9EI1bc=XN4cc1=Bh230Axe%vziot=#Qc6@MmbTAb#cj<| zsl1r$|MGmk$H|3n5%ODwQAVD7;)b{+>W@+g>I0ZAHLR&q^3ioSrEk^Fb3SduS`}*p`XQ={`WU^$9UhoKzZVxq8!z0~-EDMb0jC1hsZ%WJsnUVG zqHeylwy5dTTENRQBYdys)A#{14kt@dl}G$1hcaRk0!YU9%9M-^>D*{PiCis&>rBpq z@jVL^%CLL@ts^o-S==T}N{{lc7nI(@F^L>F*Tvk`*2#R0q16Bmv&gNIrZW81EScOn zY1L4#Lhg!HEY)rI%U0z@6R?VheXPSXL_ktH)z27*sECf}PI zhq_y>TH>9Z*>=T|#S}^#EOIG{D%w3b)8o$mCEz?!=0%V9kNd^_j8Pb;E(9Z+*D3TT zpcL{VwHBSqUcqu_Nb=sY_^Rp884sVX^Y<#7q?a`)o&0f%PiW(gW-Q2X1yL#YLy{dA<|Or6BPf3JaO>4(0#^%l@p zZR?cV7u10P`bX_PV?BMW;Jet}84s;6-*KFJoLdSr_7X38xfR!QAu~pYUbjAvcdb_D zU$YU%X6nmL?`C&Yn~zl^JPn-iO&~}B1<{wHkNQ5@)!po+>j#tz%OquS9gak!E7Bj_ z1$%R5AIq+$EH;_;zFz!RuYw2OcmZxt%H@kmMK&HS?|IOXBqfa>;lrkfWz5`3p{F88 za#Z?U!wD3&ek~^O5X3=5gQd-4U{+!V?$OttZdebNWCK);)+0Fpth%Ud%?XCQXR$ zK56d3L=a%M)Q@o3id#;Td$mwuEhuZId?g%51tUnek0*?douHAhgyog-f!hi0QQzy2$Fvn}O$w@|Ljqb8+c6{3k?VC~$+AStXS%szBs&dTc6u$dY+UW>kt+%bKWmk6o`Yy|1$sfSM zGf)=;V(B*vULsp$)pAeyE8s|m)`}mJTA7*|MI)-9DbxVYTxmv)yvz3$ z#si)5=Uke38L8O=no&Z0#nHsTjL<8V2)>}!mg?cvRv!s@V!GMt^=hh&UpiH0X}2R} z1*cXLy>K70rH7)3y~l9T<1T4EAWD`m6D4L3*6}2yj4@^CT>+edH0fpeb64%3(1tGV z`#v{0Hl}9Vm9IVL+<(^PHhgpx7|ZR2pSHN69H~Vi;}+PoMM*rxbOo1!Lw#v-SKSeS zeEgcpbeSR%mkYxKI882fXVI3z@?l;=Jocy3v+G5^JRbXc?72d{|E}#f!Fq4+S(;8Zfwl&_0;7f1OymIBpFGJJVrp*hK`Ap#J zwx+~hd|`(=jG6BQVynOPzeYG-=tbc}04FwL_>Z!r7Pg^fgi#0c?$~8n!tUF|#z6p5-8s zQ5@kGC1+`3HDlG($X-?Y?tLDEt|>+5-Oqcwsg`{2ENxWT8_gb9cPt2>SXm%6Yu*aW zuVXSp??ZMv?lp!JUY{jz{>1E3aZ`7^mtcFZHOz~#P&NA(S*L7t8(A?Kgsul`q06fr zE1St5cwC4genKZPX`+M5!TO@DlrNP-EUYP4`= z`JST~wa6^rnaWjI;6xV6I5(pDvfSVVP9SL&QyCXH^y#lC;$XO_y3H^-AE#Fcr}Uw} zzjbocXvWrG|7)*`q&aqXhx&!W*KHP7vgI>Ri~9Uc!^O_4T&vy(iXOJ{kg!*=`7S@g z_n;-YHsift_^3Cr%6RB=aOD#D#<{la+i4y*;wx{T@YgM@$3kAdG9zg| zUM0a+)C~ZmcCANw=44n^&h47}R~GGDa_gK&R}tDYww3RpXlJ(G61V`6L|ZlA$ZGEs zcTmY=F$*3V>i6hcRC~^NLGJ=vXui7PAfK_DS~aFQAYAMdkQMjV3KkcFlM$12FHA0p z=m_WUJ6{yROM1m7n!TK~uSme%)+%eP%1~hP)9x)l*3f^l5lCj0?wy=V{>3Z`Op8|V z+(iRyhXbOyNk8gWb|1elNM3?{dh#?lTCg;KIRT!Fr@e4YG*FcMY;!+R#WSy7yN|(# zuW_VUcp-Hbu05*UmnettlUu>!2^z~k>g1ak-I$Wq2odFC(uQ(;|4Ryn>r4{VrEq!` z!O&Ei55%=LsjY&Zp5>wY;n}K9icvY<$OX`s29z@#>9w;z|F!^)-F2Spqm%P=3478nhbP!;3j!O&dNd+0lxx?Rt|1ANP9m8WRihvd5_F;3K_JkXW$y#5^` zDlULC*#C&UP^TxP0)fs)E?0Qr6aJZ3$rrX8Ri4R4$eweUBKYfbT;FqJ!t%7pF9ak4 zU~042qXtn{aPObP{*3WUs}^t81T|}^pZdM^7!jU_SC^>k%ks&PZm56TrHMSzAY@3V2+Yd*$CL*J#5XG56y5CskeTWgj*FdtfQ8(B{hA7+V8&+T*x_bj%tugiVOFnK zh90rW^wr`oLvzaMLgW)QUC|h3D$VtX*~A>XLiJ;1nsva+xMwa$%_w&_NwK*;;UD^m zg$n^(mCaM;zVF7xI(;$-3*5qnUzy`PXitK#Fr~pRFDXv(qMX-NRd%uprDy(dPm!Kz ze%1TwgF~**-eRh*P!HfwWN_@i7M)yw$wHJK_Bngrs(5AD$97s)_taPMYtr+tXp-TL z)}@UVtx1K^s0E>%oqTTXKI+0ZX*~$S!8eTRNyaRWe7sm~k^CL9T*X63o&D%$Pt30U zHZB%B@$~>+R^ZggtB_yOJAeiIQajKH78k0AvIg$K6>Uubg19li7Kzur)4)@(FwmPA z%w#O&-1x99z|?=H|6R_x5cW-*ghE)SJ3*^ySK|kOJsa(Tu|145@w)(|Sqr**gK+k& zs0|}w;N4vg0ixXY4lB>_`tO9}$zdxue$=Ns(tbqtMsVWeE*B`A zOy=vy($a<-eX@Z0%T5zIamr;pluq^-p;AsYBl%KoKRvYcKN~rOD8Ekw0LSM5^5HsFaa?hv-qwzR<|*PRVVnT9Ci}VKj{FsCti0X*sA==l{enVM!qrlo;!0Xh-n#A9V` zJ=}w+5x$Qw!YuWjerD8C_ePShi=;ko`F6XbV-KzaxVDYUGLEc(%JZ8JWJ1iY$Q9;J zEtES|LBQUAd$0NZ_j#eo87dOfZ0nrIgIz3g1la=M+K$HY-{edqwD~Gv{>^VBba~c) znM(Nn^l!4_VJR#;SGKIPcK0C(373wxHW)oJyy8E$*NV$t*m8x)kz`T`6U zIlK?Ck8VmZ+Tdk47aPqr?l8NyUbrwN1SjT!Z#Z!i_Tt+2#u~BYgRIR#qi|;gcsj03 z;lJ01@$~+)cS81uglu19&sh}60c~*UcH&7mdV?ssbNT_;$2oNta(!7;(pBB)Bjbre zpN!YkRd8QO+D{Tl{{Iyc=>H6TMVt$YdJbucii$y{n;9DB#>RGPd%RzKi00y|6fpnI z{q1iS1kkAO8%aZfhITBS1xy&@x`sc~s&ZWC9Zh{XKZlVnkW4AMzU zkM5AGjsDz0E!Olmi{G&VsJFMP1{}Cbs&b+^Y_0Q+n8#L+;f=#?UY_iD&&>%FZjN@Z zSTe;wLolE=i)otj zBEEV!aZu6yO>Ek-#9~;Ei%|6AqY7A*w@T`fMQ4))9e53LyJJ8#R-ujusKdZ>NxQx5 zAWYfr8o4&He_UVR_y_>qu`OR;v87pJ@O+_q2EEgT2tqy4qR4rgJyQC&>%Tk$OhO@5 zA(jX&L(lfuyFROvjRCKdH*kYddsAa(<$IvWp?q-@xf1u{lpdqz7D(_Jwr!`%V_zQh zhITx+h&R77bO88RoY*WZeOL&@>C3`>#QMw% zY83pVblY2Y8aLzN;Q_@J>Wc>Wse!HNt1_b&M_;_D@p$#KS_1w5cWX6y4>;%E|3dVz zGXmhi#(6|1pJf=m^^wZ)Pm=J=vYpJa?p8T7gao9g+oRRWc6H=X=X)#s)Gs8(D%#NJ(hDFOZ8Vo;W>BI!RUiHtNw#O1Pj%Bn-m z0IkEez?a2Hh#&H8;z;EwUrH$hez4hRkzKLqu$#*IIoUaRS}T+&0Vv;&mkx)0S0_>l zY~Q*e!Ts>=3}uL4&qS`9ubDNUdjyDZdO$}~(>%+2O(v3rcp=>V!0X*4 z*w#NdZ!mUL&6cnYk=g4NXD6heGGw)ePrQX?k0uwPm+WP8{4tuG*~9Wd@Qz9QXySN2UwXUjnj zWA)Xb00G(M+Ov4~OK$|f|4R?Hj=>JBR_P&j9^l6p{LwfYh?gRFsAx z1JBf4#ykxh?ENjYjmaL(+KY_2Ol%z(6y=+S4uq;Tzh_T%gC); zqkFA8K7FR{icwrpMY*cRPW;~ch{iwn4$z8d5E5L-+UyzzPpq%Sny#(gz$MC@?@{{D zTjnW{6gaDc_ZX~|teOdBLnG{~VJeG#D5x5(tr}`l*V7sND90_ghFV<${}L&zm_nQW zQ5z{*Fzel6ozaQIwjRqfT(B*c2L;SgnQ<(DFPC540D%FqXE;ozxa&p5bVZ&$=Q0N< zSpN8g(2|5v($DBT5s=%F1w+l?83NjU*HpC{=K1G}(|b=%N=*Ubl{6@7iCWcAibeY@rp(s^to}g zfx*09<@T#@ZtYyFGQJa@m6?;*-p&B=NQIv>Gti?TY}d0oZK+xXRd%i)1pCL&y%HxX z1%dIu`BuetM4)#W<~2^bpNCA;AE&>WZ;;r<2TFIf{wcshLd-p6v(!A=#PN?wCue$; z7mIdIDY%H6hoL}wvx1&rQI-2wirl9EmBSlZ103F>%K@8tRM`nT2W&>iQswuxsE5a= zFy)gx(vPcOcQ58PSCGEcKF1fI^}%s1xfyHxsabik{5|DkIkg+Z7G(0%?A1zNqNtr9KobN??ydw$uO{b=ZPNs?7wATt>ak$=iVgA%MPmB zw7=vHlY|_LRML10Nij}T%6X35(EbPomstwvcqZ}7>s_-)lYds3{lnD4rVBZ)R3BUY z!JzIh-j13K7E!!glEu2k(;7B8#s^1k1Qtb*7>yn90*!5){!Qnqp!}ar&}^<- zTPLVRM@K_hrNd}H#;m}CVBqVqus1;oqC;6tGe_68kPtfxBytm^ zN2l;cGIHow%d-N)3)PH@-o7%K|EcF&Uy8xBorw0?uzR~f7@c>*_uN8db&6Y z$`e(h;W1N5xI5H%pv~8QqoEvV62*G>EEtEoHgH`08^0=Nu?|aa8p*E}@#N1h+IA9- z%Cok$`SHTXZ#c@4RlOFI^lu7@5xojtV@KU6#(I~=N!_|v1J-l%O+lQ%gfS-07Z;Ko zrvgroT_2qXM}s`0`On6B=D1^DxAnb%!-fB~(Hkf!dI~_Rlt{jP{`b4`1L(s4H*_P2 zu=76<2A4kh5qc%p@eQz)h=J83b_n(dmg8qRG@np&%ZxVOY%{wC2H&r`W4M!Tl$2=|Q|>L&8QL z0ZNVmg~+d=@!NsFY0Hy?;F4m~OJ%JMKgjjHJ#ha$<9@D^p#NT~|Fsy$sl%yJlKe-I11&5oPP34dO~)YR>ApV*%1uGyTT=*m zmf9N4hj^m?h){EZAZ917J}T4vBN(e}T|ourcwYAA6HM9!{^)4woz2&{68t{^Ef0?N diff --git a/docs/tutorial/tutorialFigs/contour.png b/docs/tutorial/tutorialFigs/contour.png deleted file mode 100644 index 03fff1906889afafbd8088620d943eadcdaf8bf6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71029 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�te z&qdwk=Z-hG^#D=bXT6BZTwUln>wn|@xOxtCNWRudM-WZdH04|QvJOv~zIKn4`ls;A zHo&EihE4-S?K(+Swzh7E%mmxxZ|NAMahaJnJN(v+iGdNoq!C2Mtl1NG47Luj(^N@* z+ki)mO|l0d29F=pMYn0tCD@rH#dKXqq>e)j{>549?T~qGWV7`4QC~-TX&<$XHmtG! ze*vTR$$gq}tjtB@?dp|Vj1`UnP^Agw?Jljc{_YOM_|l&HM_VR=k|dXH>a)!_QD3N! zbjs*>_T5@9=6rNdCjp0Z%<;I(B<<}uXqYNVx#bw<28ql9{OFEZC3VsC`nGCU80o)6 zd5W11H{#s3F=`#3C8Z^s#iVR=S}5zotT}hA`!2P~cG1T=E3#w8`+j!uoseD%urFR$u zRrIYQhyp~$xV7%qS=W|{#4W+|O=IEu#g74qzY0S%U5Pesysu{|{XT!?_vZVyY-3^q zeThsqGemtVe+&$e#tibaw$HBhKL8M^%pjxbY8W|xJ&XeUO?_^6iXs3qg#hpjjbkPs zgc&-#ZY)pJ2ymCif~1pkK6zr6@}yXrwDvfxHq3=;2lcW{c7i{~*26!{TZNRUL@ zSPoKMrJukyWd^n3aGr7w?KS6Ubbe;uT6jcOlcg`FNE=}vW-iGw96N$iud7^m74CQ+ zTE6VJSr$89jMfPdbr2e{Q|2rCwfVDdc-eVg^?81e9#g;nqThHw9usXj^pzkn>nrt- z(M<0xXUBN_ih+hH_6>e__5LJvSHWjZo>H|nn6=O!Y`GKE*h?hOs^T$2^_fG)g z?e9Vx0P!X%rXAh=af%D0hl`Uxl6Eyo+60jX45)3CF}xfB;)fsqCJMsoNebiR1iggN zi5OM!&?)R&0gv6}Qjbu3F$@?HUH;*p|7&>s;5$4rL<~`e4%)|$A7E_thFgE}Hz>&~ z2WG1E&1vpx9aN|LrozJOb@M>u5yFhAO zf8>eE2DN+355r(@Gae&qGsb9O&`%l*!iEIO2nb^>;yx&>uSv8y%M$Y;Zq4AuuaVMW zYUQOH5hR-HuHsY~zsvF_0P(T)axaP?dO_0j8n!6Lrqy+4E1qt;whmqzijl;a-qX+) zfld=CS5+*ojb#A)%mQuNr=nN*gAYGqUa|3LVCmH~ufHNVG?fo zBD1 zJR!|=wT1&>tTXhTC;EzKEJhGXZ)+urXjfC`GhCfprQT@)gK&e%xGzO8(@nDFFy*kJ{oBdL5y=4vI(?mZ{DJT57YEqyhcsyAd@M#GcNJ=ZZbK#-uc!ZBH=W5 za2k#1xFF^Hg%L#NoNS^-vS;h`0OGxS_af+0fm+KaKy-N#KvZYBsH<`WF&hDnqZ~z) z)_RaN7L+#`7z*w@&Q*}7BG)+gnqI%YL5iL%F9y)C;Ak8b7xsSR+GH7Us)k!%tubUUbUfsABA;NG|%6Na_?${<+2JutA-5+h9xR zJ_jFCwqQ-Vk**j!bo{AjStCFcEFu4Wm+^D-Z~ESMn~qC*IRV59*(^KLj0}1=2!PJ@ zUWv_VZs2Ux7O{5msFy^iG|%Sk+h0d#zxQl2B0HF#$2K)$t$Z`>*|L&*07<(KQ@KXT z3k|PqRKs}Gab-QQzh8r2=B6HEtCHn~o*cxPH8?OBohxRlJejy39!~uTIGjak?gF^6 z8L3P69HxWn2x?4(g=Q%maEEz_b>{9YqEi=J!eA?@rWbC7Yga#tJgnovsJ8(N@0Ae~ zZ37#@jWWf?X+*81Z_@qPyXXS}qM0blbW0SJ`O2`YKi=j2I{vckkB$a-*uS4&yoRef z$g%Fdc^}!SIWsd8f2VG-@uPa{RpaQ00pi<75Pe7iqKeI}4<|A8oVxWvGeMpw#{2x8 zURGTM5rEh(KqP|LK_tg{`vizfL=cUF4Gl4c142+jJ57TV{eJxEzvCXe@UGuyZY8{z zG;!F)7*gQsooZ$fbWrCud~Q69p1+KQX$b%P5C1Q%xWA`^BuzrbF7nLlgv-ApEv6%U z^qc=F43Zk+WVcHK#F_i24iIHb6@+Pq3YYJ1e-T!y^SG8Op>2ek%wAHTwrP3%h*aMd zQY0_@3M2g^CMZ7_mC8DGlyi6g0r0m8XdIyCi^w0|Q4)iQ7Sh#plQ|L*)bSlFXfZ0y znXze4b4NcliPVF364BLwTA5^GjI}Vpq~F6da~U4Pm>urNg^6LV!5Py{jT*YYzparX zxPljdVl@K9mK|!PI?sozmu{xCKhi7pI=yJHy$OH4DL^#E^wIQpVYz7{?EL@Co%>T< zS9XODh?jr{i8m5R2qXkXmSkH_CRNU)DwX7a&JU@nq$)KYyF9W+k*o)jfaWDY4Gj%6 z?|k36eV~W2XJi?pxmD0j(|!BibI(0{uf5k^yP-lcHGql0M-4RH3p*&81^^=2m+tG* zvKT9XP<;g;8c{zKAgN&>+g?TB(G(Y<$0x?y^&K)2^hcRny&4kX)bsDA%5H%7y zcm=W*bM#?)lYbgkcb7uj0CM41R4iM;m8)~#s_ToJ4XY$*jy{zr z=b>wEpm4j(db`D%zP*hdWNoXzKuvWE3e?a$b7TYS7#mq4C1{dsWbn#>MFuAWhy*b% zbAKz0WER_ug~5s@fRGtPfuYW+k8XSnN7Z`{gr}}<7X8W(@jAa$6Qajgnb2@P^P+AmkU?>hHy#&RyfcGtkXT7ee>D<);X(o z&^#x>h6<0eB&G<=JbCsotnIxG2T;2k0f#Leuua<0EPyK8K>2~67mT+WBOj7ODm1{Aorp6-uWp7(bqhd`2P6G%HX@0us*mcmL?#~ zvY#0p+Q3xWe|*y!|N6Q6PVaY(HmD&bc@2P}y6YO65Jj@uo4f0=KJG#@HI>%0(`L)e zVU09itpwM_&u2ie0uXuaG(e1iu=0BLoV8NmxQEX}_OLud0K80~x_u~$<_%sMj+txM zU&q;>v)rROnm~gd_P~@yW!XknN|4p~X;%%-&dtw4X?{ya9!2FG0r__7 zBT14dY_r3@wyA?Hk$tyxsGXqf5LrGMDcYZv)rBzk?oC+QLdCeb6dHS|zi!L)I}89d z)iHMo-quo9#B5{?3G?H(8IZK4#byEj?d@K;xKH3AHyEz9kNa9L)} z)OHy>PQo~j-C2*}U%MWTuj_-sKm&YY8H0V=R_%vO5VMEA??aBu&$$11e`8Pl9@jkY z(Q3cd{cC%)LE0mKl0EXX9#=k}kH;qP^Vq~$9%r&Ir_b}F14K_=!pG1Z6`oLZR>Df=(q|^M}SE4 z5W!B}Q9U4=dj)!=0P*eYLTJXJ8UN&N7(~QKidL32L}OpEW} zqrZO3Sd>FIqD)_W@l_OCF>>uf?e-S{5vqvud>Ek><*?1_J6<2M*u^jS8^63a-M4*@ zCMy|4DPICadzER-?CmK1YHneQpvA+mT$&H<*K?usT2_zQM8KsHXU|MxO(laUKzz4I ztqlP28j851}=5tUZ&||Lcl1AKzjs>u6X|w+++Dg>B z#I_Z*5~+hu)HhBPdbkq-Drn&VL;-oqAUf_l0E(Sr1t4w^Dmm;SvW`fNDyGo>&a2IZ z(chVwnP@~co<>@=N7|*I0*H=f0*KW#qIe%+(6w#8N3iGq+>Zu7*)^a0UY!b!xUZXy z6d-D#{qD?O{WL&KHdN=qHQNlLj)^wZ?X2%r=Zc`j3|H)x`uZ_sh)rxLZDyc7N)WL{ z&khc8!-dOX&^8x{#Iz7FJrKZAW4|;v8MBDa7j3KHu#*f_8+{9HZy|jw1}kFkwG2pE z|HRuX8@7pcLI9(~Z>h#6_EaacP*@JrsBfFil)=*vP3?zNs} zbbB(}_McVpbdO`ER5vP0PJHr3%)Se%1W>=$=Jl%H99ISHtP^`lt<~eF--dG2dT6=| zm9rNyGuRY4f~U>&5EdZXy8ygEL%&wg;yoqBR$#fdM6jA*OXF@!=xU~Y7xwPvEm}~q zcDkLO5B{BHG*9YVeJ!pN+6A6hk9=k;vg7vn&0-4vtz(OSpE{@brDycZV{&bB9ZTnN zdJMDkImwj9^WYo=zfuzoC>VHLdAEqNCS^fk2NiYv8G&IZwxczxNk7gU@_p%hfrNa& zKYl-%!OHVvFc|;#_3Zz??)(*7Ds#WN`O9(IKrSI3|Lk3~PwK#2GGpO5j`3|KZc z0>q7AUHU9RUqxROGP3-@9-Fe2e4qD{!NWD^#&dI12*JG!8`ywDfLuNPMkDm>3nV^v zNTBQ*+#x94OaP~k?6CnA71pPwC!;pN@=iXK>M6D)nb6#Y$PMuGI$K?Z|%Xl-uUR>{?E^A+kDhsr4k08`?+*4rTzJM#sKldD5j!Y5wRz$flbKQgHFiV z3y`)SJCXFi_kGL^?nwsm7$AQB?<~Nb7%`$uaQ+Jm)*#L!yN#k34!gArwE&kcC>tX< zFV;uccgdeMoF{7#U4&xcHfekNI(3slRH#Pa_os*dO9t^d6fOiJSQHd+cuSPVY}v@f zKZiCHRPAkK(QonZ1Ry%5E~*Pem27@IgJEA;UktUEX&&AWRR9IZT_`R4YdDxSsJ6D! z?xOo9&SEz*;((b~-^4nVMzV^zE*#ZMI{9R7SPZsl=dlhD-yq^7SU$qQC{}aC!h_YJ zFq|y%MQEVSh?9^>v`xl-@eSFbb;cH2P(Q}910dB3XrcB*df+iQ)WJt+WP6jS%LF|A|}s{x|)N2Asrqekq}QuONqb+pb=tjf{)ic0h0*M(wq_>p-aRGq1u@#Is-X6ab#cD9yP{*NF2~-4xju)B3 zGS4%WT(XG#e|c&-th^|M=EF;2{MP;0Pt$wY%>~%?OMs}blUXy)2Z3SIZ!~ued$;j z-RNi~ ztD9ZqGaJ6_91aoSdL6!h^mTat`cbHDKMI}KQR*W|P>aw^X$cAZnF8zl#V~y5x6qF# z$S_>y?=pcZ03zZQ+w?0mNMY|YZE7d$v1|cbNGVQZTd6uwVU{_}vWHCb%9cgs4>-3n z_uUrjytcC}xFV?WieTnEEjPM)1_6kpQJ|)l>_-8L+S5P&E)+LrLu((cYp#%`M9mmx zDkUY*Wb?r6Z&*VtO?dL+yKq1+)3zZTPFOST zEtDWMFfZ#FC+@8bAhL$BW^Eu|Q^e+43%Ws2bZ_M-o52H6Ce4&mkmCd1r$VEW9^oLTpn%A^bu^1YN6aq`eS}3 zeNI-y2ASp!=H4bj(|rP6wt{MEH){yIew=Uj&Z6 z=LA5;*Eq%l!Qm$VD{_DZG%OnWSl`A7RNjnsL0}_2Qok_;gFLUZ*fP6-s0<9EhYx=r z*YZ~8!41?uts6BMQ_uR!DDq=Aj{pTA(ZJxs{B&3aEN%fv_F=#owAcUj9#+WlBKN2O zvxoau!5GK(PpD_VS$rDSE-Z!vdS*9JiX>B~?IK{QfuDMV8)z)lQ6dxpB5Vu%z48H> z!#%DGO4%FC;bNf}c2-cgr|nVag)3on=x+G*?t^Hl3{);qFKW4j=ch1^y^_6GJ%?X= z4pv{61Q3^(m!lYrYp}sZ*I(Ch0osRLQ_mq=3$oG!!C_kCwGpS!(Z3xF*JkZd0*M}r zwT1#pYa`u{woJR`{{)iyz(2NqRDhTcC^=0IqEVD;BkM^CARUnE&4KtOTXwo#3eEwt4v&b z`_FUU5s8DuBCsPAz2C@8i zkU@0f#S}FeQWZ@niE2(^-1nMD@ApBYy(Eo~C`t)25L!vp)gYyd$js&55yovdEae{) zDTY=_w6h7&m?MkW2CrQw;0V<@f%qEdx=|1$mT({gA~)22uvn^sYJa~vXi67*#))vT zc#*+`e?Sy!lbWfT(0K)M7l0yIDrOO5b^<3qs!)k%156tXM~|EK^l z$viW5hXo>*t2vyRzSz86nyK_jsDYCz=1(IIR1H+Oy)6LZiWx+*)npKF z+!zaEpM07DM2hRB;Krl@gNtjObmpc z(QXuqD*zD(BT}Dfwp=E|Z|&4hfgXSar5g{aKg;1j5nXrASzB@*0MT_$fH*r#(`N#f zuI(B~4XH*x0mMWFl}=_@0RcZ zCa0c+7t`NU_e)vJH5n$@)@Eu`5nF3-B;Z3P#yYM8Znmv`vWGj+Cd>4fEG+?c3t@e# z6dJa2VQlDL`0^kBh5Pn$y!HZ{_ut;f*NThC`|t7t-l@;$G#2OxfMsgvmpBs)7)Dwv z`xW4LAINU7{@MFg;Ns`BYX;>7ak3!n6CCT}0EMY_*F|=tCm18q>bK(P$r{Lp3Dvc7lK6nOV^p~T+e1q1DfWtn3EwL#c*0)S?vO9TN8z__`_v=B7b4%pVj zx>H}9pUa1Dzy0sHX3;(xX}twY-aA(s}aDaujc&S z*jF>>)bnP}sr`>RU`?+|<^;&*oT*{|5j7|Nj`s59j~@KmbWZK~&Ve=bIc?mY;dbWO?sB zJV=6UwwoQZ(z6;#GrRxse%NOvJzA~Qn(3MDriXWh^4?`uR%TY|eQ(r7CD0^Df&hpP zASxm=!`&12o^yW3-IgSfAlh32Gqc1vG+uAxfs)hAC4}ak*EiEn4+S2p;9!qu zJ&E?V_81r%jgisG=a*C`T%}D%QE$}a^o(}5m7=?MIQqxtqP4v% zDwU(y+ggtO^+$2Mw;Y!jF2~IqzlgE1Dcau-T^fJZ?EZiAi|5}R{P!=O@4u;?;$0la zE^WN?&F69V{ugo3*opR`*611QithgIh(>GdEtcc7+!KSt*J5<`gD7=$#o_L19PF&e z(cx~il)9sT{7MW@e-OR>gV7eJQ9Ig+qaE72^)&W3m!kqb+q;LOg|^m?52Ai@9Bp00 z(K&iE&f2@Ucm}phm_CH zacs(ERv9bz?tc}VjF*1;$|deS!kFu0{B?D9GbZcl`Tpf!{{MKgay$Cwdt&tRXbg@H zMR#W}eWN!z+k2t~`qs)d`qqAIt!_qT|0phB=KbILMf491M&qm&$Mtd?ORqX>!~HV* z=ifDsqQd8`#jV(WuoImpeQ{;(qxk59U&YAqIQ*{j`M5kPAIA3fdTea0#M;_&;%T!p zm$=`J7#tj>U2l(W^30Z&R`^jl?%eqzo-W=`G@YJah?$v%7#tYkJ$I*f{G8a|vBRaH zZzGP54rtG2EG<7t@4>cyeCs#xqRBWpsnX8Xc>44~JXyROn@7u0Wjxlq$`PIPZ|Huu z(-oc7!I=4YE+%e_M`@5T-8hNkBgW+6Q5+x8FAr<1^=KS19;$7;x4sw~nu>+_>xpio z@K9yK>yFXyXyOZBOFR4X-rim;FE2yGJzp_`nvTv_r0gr^*Q?c`(t8a0y#5G|LRC`&uixM z^4RlS<)i22`*e18@_xo*U|=Af>v{Qj_us^#9BwpD)4TuEpMDqj9)A(NGhMOp@ue6Z z9gWsTdu%Um#*?p~@~+#GT%Eu6%joGHNIZRS^I5DvyGI|`L9Vv&tiv%jF`HJ$w{0$Q zk2ZLLVqQs~bOidPeKRj=7xIe6=~<%T39?t&>iB!|@S9j!e#{sjkMXI?F*bcUx_Sm< zXKN)s``!N?`$vm0er+VCuFgb1@At;iOzFglkfTV zwvJzh4Q)6$+>iB*rFgLTb=-gUdBjj7h8KonbYeV)dnRJ2V=RU{$D_N{kFL@d_0w9E zYX`A=vKc$a>#!t;3HV z7kU)B!s!`uy{#qM&c~;1J57AHk#ww+S~aTZ5Y-dpBKxOjjc93aiBeZ7I(j;zvyZj6 zlh2HAI-2^G=NH#-dqy9FZ~HHQALqw#uk;;yO!}AZ>p2&{pPn?Lw$JC?h>lthybbw2 zG96B=;}cBqA&M!_a-@O-@xzx$v6IUYT_gB%@)kN+B(dL#Ak zKi8$D^;zOKmE$Aifc)hUo_`W&w7CPluDiP@J#SA>Z*-vFN_SH5NmxgPpAO-GR=L=q(LKXB&DWI6?V)Slf;LldagRZpQ90Yk4E~R`;X) z_#}EOBXR5MZ{u(N_V3fZ)k&T$Jx0bpOXL3LjZfn0)elo!eC{_zJL&d)i~Ul%tgNh{ zv#ch5K07-b)6<1+nfvYcT_^wU@zPQ`b8v7FJ3Bk{`K`p~eNSQ#dA$8ZnN;XY=lqZ7 z@I0SKnW8LGhK-MpCz+CEg+<-?X86xT^@_*&)@RIL76anD0b;3*L4}fiTI0TKTN(cCJX5Iz zNaV$Ib#^hJyOIz)J4L|PPofUR5h3v zFBM#ZS_Dw&h*lUyItJmezb!y~{N4aW>1L-rDj&ps!1UdFU&Q9#N}RMFN9S-UhUNyM z)Y%a`kM^Rv(;D4f(=j}8HF^ie>A-D(KNN!H`%$Icy<=BmWd0Y?JBV@(ZQ5F3s8v8V zAaQ&3K5bu#v(|!Ur_vK3(9+fwEuBMg);$)drJ-n?9L4FulW0F#jN$QCuGb%3D4(qf zr^1lW0b=b8fOu4m$~KDVRwG)Ex?&&!#1G#OfY{1#s?)o60cShCF*G<96AM#--iZWW zdb;}nh&=$rcDf?~alaC~2_UXUPgh@jc()*1sd{V^~)n8362Q2@0--`Y`~J_tZu z*@*IfIWAwm9v|HLq)-f@%rU?)(ds!U1RSzoO(5I-N^CE0$Ij#3=&1I@{N#d9V9?DJ-rZfbC(khk9W=e zs2L!7AJ6p}MgeIG{|r{&bf3!radGiM8h>{lejXhotuc8G!xqopg0XOa75Y5hh(@g| zZv65eV)n`>(cRM*CzXASTu(7_-H!6XR-ja1AnlJKj4h*M$PEkv8dH?9?HMGZuYx0K zU-Kvc)br7{)u7=qa#h3D?#^bcuRn{`<;Q8iw|^L5wGb0C%A>J3z{vT<@Bg3J-G35; zGrch~HyS;CeQ|bj7F*9YW8?8=%#2^gc=oq(TI=?ce?TzgU2n z^+MmZa_Rp4uVZQTaqL%il3a^+oCob~&tFjsou#eRir&C?82i&IPVysQQb5_t@y%P$ zIpbIX5Pu7Rh<298S^)&*-wl6FzZc-~91JE9*#r(z?iw}LW7Y$-JPakhZ38ibp<;Ao zGJ3l;0K*?!Sz^c-b?`0wCpoyk$L zwDcqa#8dd_jh_)9N+Wr%vcT2Pb8~YEAWEYjyV%#IwOCmH{Kr)qVL0{igLHS{3M>B6fQ15jP3okIPI#%&;p9oaBq}1p~DWHtgh7^ ztHt1K42{hsMPTjOoy4TtdWHds2I?pc4j!!HD^|N!J1TZ=-vu*>Bw$Gyd2oL7j zXk3|n&j8{+Uh};kgv1Dz?ui&>9UL8wK9sz!5&-e686fUpeP2b9?v0xteG+pE3joBP z1Q6?Z5cMJ!05N+}-O~Ou6mu+6YmZlBZ)q&7Wd%<$j#@o<#Q;9^%n~ z7|`dH2;t8MK?ym0OdEJlp z~!3o1&v*RKD|LJ&1X1 zUpz;ls1`y>58_5)v8PR1jz9VMH!(YVsSwn(_tB%fL}2_emiHgTQOA1JIu7xU0}zh| zh~3dqc^M!c!`t>_8?b%4&v3@zqqkQ@*Y{q+Xw=_75L1(Lh36BnpdfskeNGzqUgXc; z2M|3j1GCLdEZw`i>0SH13PA>R(o-RFJO`}e9q73 z@6y4otLLc*Xhh92QrW2I@ofS``Gj)LG5XEzKgEMbUq`JSuj>dvn&*X`M|l^(wsnYZ z-W@Yne;s2}SBP}zB_ad`1!Lgm+QX-4QkM8~f zFbNMG?ufn-qMo`sF;KKbbst%N*oe8QtMSo?7|)2VP+@;J0I_JA@3hP_Zfvf^y@y}M z?T3Gevms>cr9tEZp2Obp8e5#d)y&v+*%gK_in zFXFd<^=}ieqTA4SF8KGw_%FtN@f1yjeopc4+bQ~_*B3;*$;D^PV_76xq-zOu@bN22Mj7$;-Aks= zpC3{59l$p9R7ZP8wA4TS(|?Hvn_om_csn{5&OL}4G!j6h? zz9%831tYK%qG%(G_1rvsbeqt!dvSnJ>YKtkJ~4nd5Fm4mMP(PIr&59jGckSXBdl_b z*xh)BGPQvh5$&PWM`$Wu6~axsu$;BQ#LocaRcXGx7~4zVM750dvu7ka5blloaU9`= zY+x~M8N8adwG-UrSW7z_tI^lB9sQGdE~g0h>&JTEf;UYsqH9Hfh|7JO+aB?pF`krr z6@Z^T#8f*)1Lk@FK-}+&-p=6|Mll?jB9v%+B>IO30f>0Zp94g~eAjnkV`(jgQC|P> z!EutF{J%FiJX_zWH$`~2Hh)(heIYJk`YP*Fh@yxhC@RXkgJ03JDvwt*HDzTOmO znnKmE&g!96P#@qy++HPA1AsVp>2h5A-~#~S0OI5{c@LBKHF;b0x*BfE_Edlp4^S;dhQ5lmx zi0=CgSs1GTDtiQG3SwoH-ap^D*HSU?SdUX+yC|r$Aokq6N@wrw@%b3<>nhmGyw_uz z!J(gu*%>XgCi|)jd z{^YNI^fvd^AAREYoZxj`?elt$ZUYdPHy;tC0M8$4iS}M1wmMP90V=j39nV-0D7x=9 zt$f$Q%9rVmdasLS%a=V9;GDJvpZ2pyJP$J!6#9>C0TKm=*@M{NJ+PuX>eZ@Od5`;q zWD-!p$d94E(HJIbYIxXCe+**0w-zEN+UrCf@cRIUb5obzJV3OqEh+lzi_d@0Sbdm4 zp~jEt>4g+Mq;CDHMH*f#1&#e&^cl%{{%)QsgPh-u{sp03Jh+`9Hx@z;Ozf24bDo~OT< z{-Ex7{rbmgyec<}=Spui?Z3@G_DA`+K+G^ujfL`Hy>fc(-2bu9<2my6?d@&SlNR9v zn@KJR3|*B)uF8sLndCV>i*g`)36)98A|p~@n#$z?C^rfS2PEac>htnXXBGtL-EiEuDlo=j?9a#bzvyl5?n1<@IWW>U(W-uBWHwlX2p0_c>`NJzfTg3RR7g z3f2tz3OZ{sFTz8WQ7Q%kMZI|nXn*tgOkWAA#nhTY8ZG+cqB>XXr+G{Hg{a!OR}Yd&*nLs?>aR#i*aHW z*-f}CfXeqMt>4b#n0@iejzJYYLuEC7eew10qufqt`(#`6PxPj!2fa z51Ot-|K*=DzZ2uR3c5T({l|X&rj|VBd|mdw=scOOUB1mJ#va61eQhL5K9 zqhz?mcsYc=p@H*C2ayVfHnYB*kq2lp^jy)VMN9!2fR$EBHorn*w{w99&+rLY^a}5W@ zIz}PAqB?ENKb)Vtn)bb(&+;x7`LSb1jAE{!GC+Qz_s|HC*LjbgV;>T5K6zs9|7OBH zI>s_U)Cg!lQa(Neffm`-cgU|j#j-vhAbQin?bkr309 z_r!yk--}z{&9TqD{3Lk~o-IF$-NVgPcx7~cl-xq-bc}KdNNiNJ%nsoRn~p(3cOAGp zgw9B>Y8~q`jI(QyJg%W>yr`pug> z8Aa6qR30u-2;)KY4IV@vAhB}<=Fkfxrx1)@vl9xG)OKLtJ6Nd8eTdA-xN4CR0OABi zY6L~To7Y)>T9KLyfOu9xX)hyS3H$7$K+n(^MUV(58k#gG;T$09nFS#3Z|ue9@hNocDn=?kv-P=~y3x?MW5ZADN6-8<+4AqAsry^pmh#o9+;GQl&pfJN) z)JhmzMmv+GUGHFru}9}cY7S8pch+~w<1EJ%p!(9yD}*?soRR~Z@;8&`HHYU45(SF| zKol$|f06&NA(ccO^1}~v_-H7R#g#-j;yx#^84V9ey83CftH#<#{8p4+>Mx;XN zc=CBwt_>+RFWRl)#P%Z&K;b#Xn`{oa1&cNiDiozck-oHWEfrHT7u6y*<^(-kA>90A zDXKl&(deoqfOuwz=_>)^cJ!RW_vSy2G5CWaS^N8Vq2MhlnC}ZfRH!xsMEj>a&_#Zx z95SRwZ=wKGu&97eN;Z8oD=Us8R~OHB9C=-Zcb08}MCDr!AM!ZQmp{3EdEa~doB%O_ zEc%Uxq{9Qe!hl2#Hp?rE80Xq!kX$~!hjnuK%{_FjoluCUpj{-IrCi%4f`sA=qj=j;w!1r! z&req{sA0G`4(P4n2;eu2A(no&NnU&fLu4<8UdPEa<79Gb2Bmp~cY*@jq=2Ot_lExz z{fM@mw=4G*$KV0oT&|6S zI9e@7*I92&jb4g{Sv*C6?NJJ#sZ6^Rw?;w@aI0WpUL+rNuD{d8qLz5nyE&ll;T>)}o-7 z`iZF>7FtThY7Q{CZ4w2r4GoF<8t<5k8<#(g4?p@e0o@gfOKxqE&Vc;t;n^)(e6oTf zWuY3wDYepTr8Km3l$;7dB29@u{PTZ`rKfjL*i2%bOA7d%yI&Cn*A*Z9<{$BVTn2>J zk_XZI*WN>bDD9-X%DKv+9)u@P?-AOw6+^Rt!3m2abVQxx?8+`ei@d~vA*wnN=F&qr zsb0iQ63q1^TG_R|7pofipWPf&qRLzYNxt{&Gr}|0Q^;*cKZS0{Lu`<{a!hXG5rXrG zc>Zb^U>cAZR5UG>9}u32pdZ)=Af80q2&^2CXf9%{0@H@k#4ZY1Wgte{rV~K?;Db+p z<`C0!fQTjWtSUf6$RQ}Y+X*kjV>dd77jcFvX8=SCc$`Ad;{&|Qgy(H76LtWTSh#UH zCT6FwmKPzHg|I?UDXaiI0>nC@rU@uQ3l;voP2&0i@2!LY50$8zI=X=M?fSU_(ZMlg zD(I~sND6>xzL6fp=_vr>JM$o(#VUPE3%a3=R&<@8r}`}*R)L`6=C~KMJj0^AOn=%r zT8k6&pvF66WR@JPAqpq8cH%ie5hBF&Xd5ODKon$8%umJKb%5PKZ&C)NmEPB!!&!jC z3=-Mm!7lGn%?)6Eh6m9~xD({U3J`rRJq;>#?-f8K*F;4}B|vL&KZS<$yt))#1Ns9> zvWi3bTq#uGN>`bc08t+6?|Sw;M&M?cu}Yvk*}@>Ud4s}I2d9LW_TrJjs&A2+1Q0D! zQ+X)|k$di~VdQuqKpco`bGMoRqWOG)2ZY}H0uZxMw!QMcLUt5NQIfNiHl3-t^!?gz=?CkjCMj{JO4%oo5a@d5ha*5*<)Fc9@lbj8^082P~{ zz9{EU?>~yACrbcR#_fPP@t4V2pC_7PC!XH@eH;=gG{QT$LB&#YvPLN2kqjx&@@*}N zW{fZ2qqK4B;`p-f`yL&eoA5qm>(bie*d(&%q^(RPVBYa4A!bzH?4U55`E*7iX$U>u zK|wT~VlQHZYNwYMK8VZYW(fwmIx!A$-4rRn{Svr0orSTTyuU;UIF^gCeu#`WI;-es z-$e(X+x~2Em4$^HDC*bF(-n&9yPjuTf9m4<&tu~SfY^fvagu7JgM&PNPzmIk?IIUKQ4(p2HwuzV>xf)mIKZrSG zt-#PQS^KGDMc_S4NVxJsy`XrmpWf$+KJn_m(xi})6tN>DRNy5Y^&na~^i?$dt}A#8$MWIrtfF2U zXhS8C2-(Kl@jTA&awn9q9_MOfy~dASyxt1{@$NU#0YJI_tAAi14<;|x-a1zCr8`mH zS)u}=<<9$3{+yW*BjfXN^VVObaMQJ=2eEENE%WP8qUuyMRjKam>cguw8sk*08ppcf z@`r!=e`E3CSHz+laxk8(<%=k|t(`;h!EgUBJVsX%=)J zG!b@}^1h2G@?Xd5`ja^8JdLgaiZP7B1p1hgqEd^Ur5!>f^`=fkk4Zv~ijX2buDjc7 zgxF%Kgn748{k9#Cq5Qgx_q0x>(oTxDbd!_P!Pd%IzKgxp2b4o@jdJ&;sCCa1z`hry z%H!ycW%Ba2qick)(u$UC9koC@ZBXBral@IJ#(o@%{7v=eFu6)Y|rZ~uks*T z*-PbG0DXsClBespo2I#_;bjpZa#wDBhLwAKKs>+`R^B3Pd#{BM zKZ=12FT@RE_%&E`qC_Nsh$ZmZ(xc=VNB}W=5Z@U<#Dhq#t^m=gVf1W{k58T}Ih1wB z@+~;g;=PMdIyk~>Lx@UC4`Eg0!;Z|OTw=8>B@ZG1kwO7SJ1|Z12&(|ZQD`=EouU=} zgx27iu)s{R0H1pi1&Zdc7I~~FT>?akZdioLa^+pMff(zCUtGQwSFe4T1gw3?aLgqH zupy@MCPPer77yaxFOr}Z@L91=A!uSk&4F!7Y{N3lyJXXHwdNIZoI)vMkcWhtI zo;{1m*e~o)R=nM$ILOiIR@8b9Fw6iDJK)P0J4jyIbX4 zmUXt-MS146^6KX-eU@6lYOMrGh#0>n%U8Mof%FTebkxK01t2e1rGVALVE z-aPX&#;gEwpU}W+Z9Q5#DpVI8iYfSx;Lp6djTL0*AqC-1yOS|dV{Hk#B_*C?&dF}m zyL*inh@i2YO17p-yXIK^3c8CQlQQNgA0| z>J9B)S$!JI$k#23!j+61wi&Gl~Nx({5n04MSV1Wny;?TBoF0vvHbKt=T&~l;=A{`bH!2VH9I?- zau6LuF5gDid~J_!UVIN?q8wuCb(I@}#Af-S+>lRs53iqbl=8;s(O9Wm@cQ|DWsZF> zdl0?mdt0Q&2e)BbS`~6`WllC)v}>cX(3F5L6Vh{EMfhf-zr62DPyjslAm05NfH)Ud zDaYM|)u%=%jMGePFH?Dw9J@|jxpr(+dXn0^smOLIt`cHu80iLsVPo}4>>{9?4^hjT zid2t9Y6z2=pkt3t%)waB;>$n&w|M&C%jl$yDvv6Jl)N@qh}j+y7|3`<#eFICv^SoNb2 zo)DTOK-}9Y=18p5aa+g{k&B)Xvg%@ZsncR~5f;=6-OC#fsU-O)yhSH**m)^VdN1K! zJBkjbyXdSgQw@x~#UXM~W?|B!Fk*_;mtu*pG*a72;+cp9PKdH z@F3RqPy{FvGCnY!0OHKd0)o9i87Q>Ut*ksv;YU9YK%|^GAoV`;AgT~(?KZ?aD;+h$ zpA3bS5o(aBu{te zys!cbto6E@iwHn8#1x@bH9SsGMc9zjGBozdWd+C&l@P92HLqO#ASrdJuLGoV_=^HG z6{-0dLQL`Z73Fz)M^Cn?lFDE!*Pr7vU-vVxinP*K({( zPF{Er?N2HV0>lI6usSXgMbU}XvP5x)6ZktunGUKbPJcKPW7kHb)Qi`VbN2BdZa>~h zIa1dz5n{@`U@A;1%!an9ERQkefnMtZ+`hXirp|+?Z+%An>|p3(f-F{r|z zV9uUIujQgl8yTVS6hc`L=lGS*o}af_rnybynd@a4mw(IOdrkM1orX5~u2dG3n*v1d zR{)(?D=1#)r!GYsFyN8rbepQCx9@%#Tg z^bB+)uZiKK2fI}7#>*dd6n>@y=pogkN3cd%WTDERD}B%Psp4A|o;vk|N9zSp zx|KJ0uFovd&fmcE7TG8LUH+j5M^B~m^BT%(d*0OI{awA^%K))kfH*{$W=}C6Z2=IW zi033A*bESx_obWHEx=;&zWv4}x3$nQm8t3o%GXqgMkB1U7JjO}RwMPLicWH}&pV+u zlG9cQ%B6ol=@A-pHAEtr3mFZsv)@ukA=*wlq7#pKKdB{e!-H6~i(!R~wD6IA&YWQl zeok+7^MhX`fK}*07jHPzTbW_;F!{W5`)+Uz!KM}X?GTO^4Fno+2LW)V9%wXt^yE%F zeD-zhH8$dO5Z*LMg=P$d>MBlEVg6xJxmw^2$fkIqUhtDWbhcgSa=?79Jw)|Uu)ivw+$xJKsN*$V zUVhG>wTh|4keD_iSAny!HePqC4{hU`1>(yDe%;UB$AQxf5Zmz}Ui*|B#DUS2xL)4D z+DRVb5xH5mv19lK+X5QTP zR{@pg-S^($-e?b5p-=+aZJIQ zws!L^mZO8Jc%|wF3Ic^yW~dxEPUsGKh~URKTOkZ(8?O(s>VS z!KosyAin?Lt9Y_{CpM24skXVxe2yp4(gh&Ki|BGjn5oq^tz_C(h6&(F9le4V@fx8z z2_V8WC{lBR2k~f&P)X*H>mXcZ>MHYwQQ6M?i!&?sodZM#QIQh?!_}fhe#&tfs~m+O zDtma3sIErQI}+qcASVx03*7iP6ZfK+s1zJw-PUp~FtX2S37wvPHzB49Y$%eva27(+ zVyI_H0WDa*0E!Ctf}sj3HZ;&Qm>8OQF^8|gKgIxv$>L_EaSn8=tC*4Lyn6w%t zNhYlz73j!p1&BXKks9X-d%#5DzC&|E3IRh{&v_1#&nE5c`^BWk3Y{FX;j+U7G&PcOVvfT(5FX)dzmRiT{2 zKozdi?B&(*o4=FSNn1g<9z<7Vo8NgIb5jIvg3m$*6(G-Rc+AVIvhU^J{LbTC10$FB&H9Klp}-62e8%fi-?kQDDLXZ1pN$|D9)e z5g(KD_7nh!KzF~o5hBPG6PCKKaqwBA4MmUQT*!!CLZZ9K12lR-6+SKJo91TNPmdUH zJBB$QQk}F;d?g+>r>-!hx=h5tDW0|dA^6u6rSM5(IHWT99>wP@MAylr=~w8}w?1Mj z4*Fbvm(BNhlluY~Cu|oWDmNER(ID5*u_VB{qh!vma`JSuFRJ|xLlyn`z3d%MgvhlfX_u3@DbeeBorDc62LjbXid zyew$d#yIr7N|SF}?1%p5eLTl&C_nU0eb+hPcAfA0F257+*9;I($wBNUe6e#-fQanG zh$DT4dBs0HiRYiwHu)17u*Fu|rE8yW6$bumUX(ko2@o~`L;-h=>B`LgJOLawsJmcQ zQVXMLXf?{hoI_JV%sWgn899SFGQE>Vow1FbxD^j#Cl$r5p8GZdqJ8BM{;KELsV>$5 zdsa;SZ|gnfy#mCQm6h~v z^E4OoIpB-zS<5t6;*VL2faq@EIk$hc@0lG9#!g}xTD40sV&^urP{tr``J^Pld@DzUV6ikvhCf>fisHNZfXPpjdy?|A;*?|ZeTs4id7#Wr@ezr zDMEf>op^jh&yKNb8MY=+Y@uLH2OdYkq4bfdmpAXn?&4>$y-R4}=};U~8LQNHK*{9Q zXa}IT;^}B<^*798{^>&ggc@=S|g! zTTf9c$OF*wtfgAuB|y}AEVI(WsDKba2neR97Equj6QlC?x3y$|=p^js8d#{od_=?7 zd|#s!$}u$9JUA5+tLGU4c^kp8wDm9=gU2y=9WU$zN$})2v=GMAsL;Q5n23CXX)B=N z;4~ErFEi&oyfr|)`w-Jgnhg07;Ch+x?<2yAESja_>3j4!rDHdSuA=IoVJd*$By1Eo zh4(~-!Z0`iui?JiTO0H_s+ekV#>=5XYP7`RAxg(C;eiyNTHjPi3t%YAyj|{^ie3$TZtJ}@b5%z}x!bO9M$EfI9JVC|4KB;hgyM3(w(iMY-lRZ0F0X=jQKnd-La) zw;rn;5gg{{Rd#AE_L<%HoxY1DxBUcgze=BT>WgR3DBi{xZV*m3bK`HwufLS~mbr)r z+w>EnL7e;clulyXtYY0w9@Z#n7UQe}L}jgD1N&@RAbZnNe30K&P<(fH)?$NPy)AP0 zkC-mW+&lqhD|yHrU4!HvQmBdCL*>XibMhW;FERbaaSTJN%M`hAUN}8IneI(DdZWAQ z2G$wS7^32BuJ=O#^$ugmv8N|rzGnEGfK>xj0$z+adPjQaHTqWp5K$b@!C@M|yl;7s z3S=wR7-hgAz)~5swDcGnF^v@b;P_lm(UT)jD2!apy$h(cV2M26zMgooef@hVV1M?$ z&jH$tFY0^F{{EkH|FWO_IY6Wes2)VpECjSZa{==NheE(&ycR&w-_Cze#}&sQ4U6wn zTZ^%spQ;#l$OplpkvXiUBsj*aOrcf9cvw56?!hU#E9ocoS91CkE?q>aaDHLH z)$_=;&wRveyaenXo>BxKgiGT5(574(cMI!Pd@$Ma6;Tj@?SY1trd!HJ$`wa za8Zt30U(x{wuV9xgo-l#82!|CREf4bA)mK_zC1oM{M^u@5u_+!_9&j27IaK>9R{H@ z(hmfR;OOFBd^Fl5j(Ha~^y@Rc;C&OZFoO|)>Qa*T>LxymMve!J75lPdLdS^}OfBT+ z*!VdX`@XVbZEY<9I{Sry%xNwhhs8L0aRkX@n!%jdM_%q2_8eu6I!Wg3$`XI~d-=M@ zh?SbJov&F$2$2$l9qxz$_b-dyq^8z<>kEqh#B!V6F?$J02?U5 z{q0RIzElT6_8AZjZ7jB();z=#p^?1##1OOs8iu2lyn*FJ^@-qgykjVxgu~PcJrHOf?l2eu zh^GR?OYf%#vH5)nBvjy^;O%ivtkt!}I7CS-^_61c@&o{eqBT`Q6%GI^gzDAEPwMU- z!>T)n7iXMmrpysXJi6Xg*M`bB6Unx9VnJqYRXB2w^5(lA?4vp*7Z!`I`ZTfa>C5~->q?z6sGFZWL3MDe6hcM(ts%>r`0{&Zeicq{xJ&A&0kVXQCv~vdrKqB|h<)Ik51^}V} z4K`w!D2gZGrm{`ppVgxT5J#_IX`UPeK+_NDJEx43`U#dKljW(VR%MD0b0Y+WK?!+t zR1ilA?{3)-~aEkSHnA_3o$ooD%&(*T8$d?U2Qt8~`ICC#|%xj_$mFJ!S5)IEX zlE8l4hUJ<04Jbra6v@y#WbvG$&uIarMls+}rPX3Mt}0xo1zkC` z$95NBx==iGf6ZH;QM!1oEZ`k40z|`k6wK1v?=sEZzPx&!m%nTN?C*|ILy>%5$E?bL z0zX@s-xWagS(2he-`At8XH2iylP4;MC`?4hj1Xq(MD_OnDmlZ47!#5ql(5q}q1=L% z0gSU#6jqs<`yhpvwj1I|XS6@MplGk-LoK$^7rP^s~)O8A@pAiL} z^btYm7)50$$qzr!YpM58udDAe^=H`CpR0}O@xJ9x(TBg~hoAhF&*EzT&j4|aTvn@? zc25Ej@gR2eV!%lN2k%z;SAajy6D0bt0FQa=QBC{MQmv4K{FDCq(R(6U8Lt}5i`jqx z06+jqL_t&!@ftGb(mbDnL)Jz`&msB@`kNl#0lfEJ0Gf8eA=FErM}ei+$RJT8ZtdLy zMB5@Q>~nX|!!*rDH+*w}%GAnsb4M?DE#s8FC?Ip*$#tp>w^2}VXq>1Ha%HXZn`Fen zc5IU$yG_Oa0lc`s`t+|;u_EmKa7OQOxZ?gVa>=`L2DkxO4$x2MnVV zE4?|3nD7CFglqv0^^$36HRtXOAOtc@0I~HPARYq{kMJN?wx9gP07T!1%AEsOMc(i> zW6+PP`{aZ4#oz>1Z3@@40lX`K!~;V8DhDu#Qch;xhM>-MSd1Bmu*g^ET2t;MM#-Rh&*V9JieI@a~BapEfjQ74!4cnj|Y^8-1-^XO%&bxs1J*{>7QC%`es z&VnlD+zBYp$iXuqe2|=;VLY}YCbn$rFzm=mc6#Il8YZ4k1IFg& zuB5G=p0^DU6CwEHn0M+0EuKpV2E&&HAHJ&z<|(^|a9CKlnuM}pFn|2x|BSnjKPU8f zGdgC-jTkc=oUwJr=*3}M+hzQ16~M1|5@8N)+sTPLW4aX!3mA4=-N9?Pr_g7-pV7CN zSbK=@?{RYEh6xuRBoW@+94-C%y-GK8JGQpgQPhiR8srhPv&Hlw)8t851zC^X+XaY) z0wCQ~5X~Euzv<0U33Ytxb-i@yMxhYkg*9g>D+JABKpn~Jyb2(yq^g{&B*>?|ryfAT zqLmS)#XdmMupq;%3_%m@bz{^~{c=p052eE5`_mKYGw3z(-CBGg=av*+{m<&x`rK0Jzlz(pDsU7cAL3Ej^ zXC4!FTtw;gqF_(V-;AkQLO~fXt>>;oNi3D~_M#<%hJscn$lm2A-^Am)|C;In43OtO zL2-*-!oZcmdOIor)18f{vGVu}LILY|POhOK-(+fwximI3fXK7o_TsxLWP?SZ2;+v| zZozw<`_-{3pL2ZZezL!5L=h~i6bs~X^n~8V6bcG3(qrS8Pvg#hI){pBr z4KQp_Ov!iDhqB$(gV&qqFt^=zT!6wNXEE_hK_cTs4`RweeC|PfTRDgqZINfGqgiS6 zA=A<95^}5_;Jly81$nQsE01GCQmq4Uha!agRD&Oxp_1seUovkkMH@S5BTkNebcYqv zrk>pY6VrN}#D_O;5f*wC1EiI%iyYcQCcvkV2joo3bK%WFv>26r_FatI3rb$>Wq_!>FdS6S=XZ`<`EfG< z%VWf21(VsZmt{%O&u|#fCq3WD6}@|2*K@qKvc=ys@bq4`U3uVdf<%`rud>YYd*@a3 z8eUsDq2bf(tJ8V!mzVbfAm)d%p)x2}31Fi|!XoV4Xur2H9_#+=E}kzVXaA3&>C;`Dn^p!=tzbAUWclC@8rp1?9~ zcxYPzM*?muFdl6@NFk=XR6wO zlkhxrHbF`Kj{%5(lROiJFibYX*X>5}Du8%#(L!<3I;;Y)y!<3q@UU)Cfas9Qk0+h` zq-IuARl2hZ!ub$OXbV;RMhP{dBB$Xq?SzopjyfgakGBDwYsV?CVQ?1VK3tT6cRG&- zc|3xf8X=L5G6BrwM0B0;{0i+maC|J=es34FhXlWJ{`mTI7!$Pz?TQyb>j@CuN zj$ueD_>P@#VK~+v{O;{ zz4}fKH5I`6y?IVkgouvuKJz5Wg@7u?g3su2FMAL-QCtmo{`l5!l3>N_(+9N&^^2l2ZVc8 zk+XUXCnz8`P2r*x9(qnsH&V8s?nPTt&*P5@ZIwu$RXm7Gco2`sX&;@2Z(aKhm9Pg> zbcIz*ch)d40!TH$4&pt$dE?`_!ZZ-_MgfxJAk+NfE^l|w`}zFJ2fZ!wU%iQrT@_>B zcN;luKA+1DNl^ zYNcLAhrn}>VfGxdZ6G_Ot!XzIAa+{i{jC+LxuC8Iwa{-pfwHv+kD?;8z@j?>LJispJ#E9w zAw5rFSVt78*;)J((;84os|yA)io1Pu8!%2T3mzbxa(G?o>>5#I@a@pK1+VmJKhqM- z#8u{Eymjl>Ng>FCD?j_|Y<~zqlqUAw>|xDdY;}Dx)_BL86e6m&lPA`Tz!1z<31Or9 zW+!y&8Ux6T(C2y(I;Z5)F=a<}oA9+Ygg&305359YsRco5fX5b!JXk`y3omRBMh7i& zgY;*W3I&-;LJvws0yz-K!axFg6%&284Z#J>7U0`{{ESE0VVmZ*(`4_YOE06`^PYgu?aqkjDkB%ylqlPiwtk#YNBN zKElpt(a5y2LQWMSG(M|BP7A%jS|BDJjW|$&dKYx_`HDVQOuL}c=y+TKBzA&wW+W%YXmOgZKhKEIeV}&;C-pFX#!Mt0UA^SS^sGqM=3I>UPQNh(+GK z$QAmVO3Lyw00CO61nb?9Zce)*F!woM*P~H9*N^{eJ8Zi$N7+WQy29365RaE1WMS?Z&7bpr-uk=O6A=0g%0ySeq2GI4Rx(sZv>ZFW zvmEvOcezLhd7TCTL(81zXPNO)C6uh6iQ7kJsroxMpYrq12)!#aQT+}%O1sQCY;m%Q zxesIh#%~En9ZF*+(UtxvH{jAuOYxz|gQ5cJ4qp6Krlx|gDp$w3&NP#2J0G!g6mM~l zO{T4Ke%)>&3#Rcvn7`yy5RO^<*}Ght`-}Hg5iaB15h!L)pgcuSpdLUCExv2VkmEvL zg@w1v!ZF`UD1jDu7>tgU1%|9@1jjv!=)JAMQAC2P!J?g z^j`rO3uDij01?pjwx_ws?Q)y@iJ`_RbpYMWxe;xv^JS(`Qw)ekySOMj?%=h%!*o+e zu8wIf1c*=W#+@(z2Xi&=$MvgJh+nvpJbTK`Y)DMG{mO*I zw}|wD$I?+S{ujmtd6*G;R(v&7*u_3ypgFXgON_ivx&-a>SfD0FRZGC&}`& zL)AFvm22;9rHuf^5klKK@Bk8rU#6;N8Sftvy}Wn?7WsE=R5xr-tKQc>B2CS)V}L-D zgN-|JY@wF{jvvRMFhB@!8-RfDOUoml?IGx?gq0GXQARsSsvahcbq3|+GSd>M6zD;7 z`Kf~JhXBNMqvCF= zB9U`c3S~rwv;_lkt~+xP@vy;oEd-+%M=zoPQ|A3DW^&QaH@~MPQ3k2vWCcE_lF-Vo zHFV@0;2aU6xO+qyeadPdb&xdQUy1gib1NIPIo$Q)^&AXd?`o z&=$j01b3E-PlX{6`nK7AqJhWps?f6VgCN!LEomo^(u=8Q)3C^#KWCu{d4$Y9^R_on zo$tP)&+T|T;(47mMDN4~g|butRcx%VI$?XEVQz2ntOY3f;Q+CChTO(XFYj$gkRVao zSzJTwaR#l@Pj82P&gaoft-@~Tke2KWTEDx6Z|@h!iqD^ms0s8G?zT-_TXgi^j^0iDHa(zsx37_OJ!o1y4dke0{D@67Mr z!*FNOD94iYE&7f9NGc>!B)Uj{NhA1E$B>VYGi8i zD|mYZta(YK&L5h#Uc+m7uVLg&zmX{#(gE9<7c?FI+Pg)Y3gG!&0I}3Vv&)`-tp2Q=aP4TV8$| zUw!?DBv&=g3A(1IiON9^8ZBaf(`$FkT143&6W`M;GfLPsnK-t~Rm>9## zj2xuSc~+sG_W$^rXai$frv2lIq!e= zpQu22FXm^9fmmSxI{3wQgfGCA+r^3C7S$u4Wscu!IM!G)l;05B(Jo*g(gsZNrEF ziI$t`AG9nvp++cP-R&r>cntMe8HQ1KzBHz2wCP9!Uxi<#Lr^2bwOqO$C7&%@rlpPE zGebQD(q2D%a$b{6KlwDDPdT8{WIn^@mU9jjVORn-8K;U?wT^eC1>;T!^Mv&h=7AkR zi*g(xq%a(u?W4FHJ71WUi17kJFGG6<@b+5-VsMCkgua$xt>Fr*{JsP==4@#Aa9;r{ z|4{&@5E(*UEE8}3M5bGjLy;@P{bVxy=nE)t8Ig%Ldg%<+b>6kDZKeZ2kv6y0&{+YQ z;m3x;`pgCYEh;U(P^1Q>;UdJe*@IX-n(ygcQ%XF*A_xKnjSm(PNg+n$SPSeGZXPcH z@ScV*Yl!f9?0bgfzH6^eGl6|JpV7W0FjEc*_!PLlcWER*bj?vMSsBWL{M!JcaxBZN zZ`=BvJRo1gdpCOweMax&brkl>LKOnHt||$Id~0sayWTGh6OK*i@G~EAg|hm)(2uMw za3T|kk2B2>`Tm{o8s|scTD~3o5>8W;#(bx&#O8OAF8UV# z*slbK@{gRWD=+r`@(uZr#t|z9+Xv)#dfbl~H+mQi-&TG&hV;tV7hUWRwncB2g=RGJ z$-BL##Z#P*PtS~fTJOI1_qrOp+_#^}Uo}3s&DTvs`a9_1Guzh;y`RRjGvOSL+%MDg zJ5Kx2UrRGr@Aqv0v11S;DpNfP5T7pzf581y1I~eA0&(a29O(5-c{#;UZP6IRKF#H~ z(3{{;RjySdBGs@vs4#C~H{0fvMus*@W9up;(C4+zL16+z?5nc~4bx|ia?9TWkaBIO z#b|%W08#o#OTmJ2z^SA(R@87XW(il;`Nz?abam>i`%`a45I3|@30xh>LhKmZ=TzwCMH6u%uA&z&?6(o8s zy@vu!4S-p0c#NyRXMmW;N1=-p1#a>?_CC&~=zYD1hRXar`I_HFzxNU#`mi=mA*nF7 zVeTvZvOu+H3L~=+$=LxwV2j+vO_Z<$6bLLMfq%}@Ws_kRpPY`$utc+%EnW#3HGIXrmeymeH;I-xu<^a-K^+WG-*!zj^Rw2L|T z&X}aVv4`??a7svIF*$KAQLe{uz}ys=yzO%gx%e(*^n$}Y2i6uLhk9-l3LdKmNsFBM zX)^yV`n7bO16>1GH$|Q}J3SU3_&OP4Wm0c>F+5zosZ9j?-gkp_>h+u3)ewV{9KO zAQm<|AtGWI1Mczu8kL)|C(_?$r!U173ez}l1z|>3yvxORB(IVeI~UzDbXh?z`p)IU zjzQ-D)lj10L_jR0v5#oXD}Wt!3TT`BTV>j%s7$J1&>UI%B4Fla{;kK->1PCmj!O+* z=64yH;d}C0_QB@mRs2gHVUZENX@Y{eIV(>x&oKrb$FWN@e`vn$kJs^&pjI1<(!Gu(clulsh$vg?xbitjxqrauU>00U)&cSDjhM@42tzo1VpE;#cUK^Fw zjR4We+GolEty4jqa|0ZThA=Q)Q<`}H3=G}lm*Rfj+|@$}xlRC)3fW!cASMr@ z-t2P&-J77>chOi{IyUV~f5ac^2U2Ofw$WH@fpTgHo||`Gragc?}f!StN*I2$TQR%O?^>^R5vm8U=)z3 zLBbgY97}JLPA@*=t3OFo$BxFlEFhJj&`HA?+Yw3~nv+~DlHp`7y(eJ$ih&HTwkpP7?t6+c<$yKYV z9YbRJyB+=IbDOX8ZTp@tn`rhRHl72-As7V~HS)u%oA=_B3D~XL)D4q3#M5<1QH#>R zWK3QrXAlqKK0>X`l-wuM1ROzRsKc ze9>7?84#8zehPDcjK^##b}en*PVVIZVUmVX4k4(?DLf_a!U5!beNH}GCr{Bl0Q-VW zmPvAgO311B%vq1sO8Ko7rVf)ov&hIkpb&s~20(11*o#bLf?N~nXJy2s6d<@%s9rTN zekdJYhwjaEu@7qvb`n}Gbt}vvxkWhCK2wC$&X}%)_O|sA5=l4*S#v30A($fMlqC6n znDjgXS?`aEMUhv+v>1$!q?o}}4K375m~vWB2>dLw6)@WWvr;h(h!#kzlxTG>guCPM zT(H5A-iIZRbNhop!w@!=&Sm3*~ zU8O(|auIUMR0H%67d?1}#!3_0q;xrX7UgM9N|WTScgS^+KccDT!=+Eg{wv)B!fYp2baxS)g8 z`{*5vuU67LTJh?*Sn%x_FGl&0GW-C?F7k&<$mKe~vC4JoMD~;@S~fyoGhbN`iOR1) z$}u7R-{oR|5$MU2HE@}ysgX;AmtbT7PoI2vWOzJnQ#1mA%kh}?9;fvvz#)bM=n#0>s;QK8vNbhrtMo);_$L@c9yT4cbcdjg$vi z=#ujDGzq)!`20Bx<#kQdLb1iF$ZuRS4KU ziC4+*{g>`hY~9cQln37b1sd{5)(o&9S{M4?AWXNikv?^ zfr2#owqPqqKMEWLZEhWN@^rV-D@TiXj^I!)qWiWtkArV(r@y%ry3q;zrI;T#%PHlK zp{zbf^Yy;%TKT*83LqAPt9Z2L#`?Vqy^XSw63XxF+%mv72N0)|2hoYy8x*N=9>RlV z!apgDf@;MJ#4CeUuGCBE+HofHo&NP;Mk3S8{I>*2_T+O;j)1Tv+y7m^B|5g zSpSj$V(w&awM=QD)N`e>xky>$?Va_g)Q$+HWL~CWl=u+}SpXQ(-+8Bmc)%EnN?_~* ziW?{h#_=~OPwskpzFM*LjN&ealp0zZTddoJgPh<_M}eQ2U7$kOt>m#a{PImMuLBU9 z?{7c0oeFy`x=YKCna<%pKyfh@E2xprD&8=0ySLlP&B^b@s;RY8ChSD978;R(I%bNH zyfYYj8Rn|zW<7a{wzf8s0wP_th6)A^*U&0wNMtd#WctM%Z+ZMRKkNIpuc5^8ukxd3 zFHIqmfZ|3}s!Rq;e=*P1g!?|i&N>iiPAj8uv2x+Hs~;sA+GZ2wZL^A!9yYC}t^z5Q zDc_GuQ&QXjPj0PT=d~4t@+HTng6@5$o&7BdkaJIjhMOg@J~=eW~TXrHt1Mb|+5a7@ z${mHcf>&#=LSC7qkXHfFvuA(MgXnJ_=Xw67rP$x3Szi6kWApKD?brUEm4_TT<2|yM zGJ6f*=l+&?hGSJ=vI+m!C~HxtRlH}FDie$|1w|(^wTr6YZH&>%A!ClP&<1i8iYNXD z9^^Rqn<0dFFix3R`_#N^m2C8)R?aJ#>x+z5bGK03Nd?a>^D7Pj+{cL)n1fH|qD(($ zKDF=5F`;pIcb7RMsc7wdkt*r(GW&zPL<4|4UZag(Irjx#`Pp;-VMM1k!@NZs<;XpjsDQ+^|~NL5kK>A@w>mkj;{g6aRCf1eL^>v^oa zu~bB_i#iDdy-e{iEEssZIfySj1`SXi$fws+!L?`fN2{10Btx;Grh-KE3yq6g$bv28 zVw$3Z3;9lb*G4Uk;+;1Vq#Hd&nx%g8A|!6Ub=prPyCpKy(!|Iems z;En-PXD+T?ocBKdb!-U?%|+CMr_7LVn{TKLkmh<3^)$-w1(km1n30|u{<6H#u=qpA zlGn%p)v!|U;j_p~1bbdfW2X1`A?XQ@0bta|>^X$SqsAZ%|&O&)>F^+ea7!Xt@_2t3*tWMg?97_YEb4j38 z%R5v$U89;Q;enLxcG6#q)bvoG#$q--#bmkbbnKMF-H$wiS7DrUlnws)a%4Qp~6 z-ZG}7XeXqytsf9?Vgo@CB(HhNL-GZZPd51i$wOY|b%0=yr#Nv8I~WAEq*%+$ z$kI@&sXhPSIXqp>t;uG0Rd za2{a9p?D%#NS(yEqkk{ySwuwf#?v>4ffu;vLm7u3DXM&2BfHX1EM}qu`#BaZMj9A|2_>m_3@> z?v1DmyaVizJ177|67kexFmfCvpdx#eLo|>mBIiorBS&bHtjN6Dr%bM6;aSie+XuTY2H_yJILiDlCkt(TtH6CJ|L=bbXi__IVRpFGOiW+ zL)m57i{3CP+>u)m&0llm5nW}4Z&kV{Ml}WsyaoqL@*Mh<6FC+wM4F=<=HgE%+UQY? z9!wj~sNtiC7{b#q0Ei&cmT{7yUl9$fh$BuzaAA$nu2p_GA}|n#@;1p)eOzdgcO`O( zEF$taX`V=%V4)v1StGx@eM58VDbq%&jgi)g9zgdVwt+}&bSU2?4ZCc^VN=JeLo13D zs-2Nd1Mh=hrEypRiIj~|3`MiBrP%t-u~;@8*nG*rrC__j^X1^c0gQo-Oo2yqL!a3; zp&96;rjsJFjl&Qb7kvAzvq5*%F1qumR=j(RKuY9Vj#AQ!nZk%^E9A0?HeXe3wmdJt zBW)m3p5@4REICBh2@XqAUU`>vENc9ScZvF63BKUD*JK&w)`(@pck9d?95sQb2X>qJ-9> z3_d)hhr|R(cUToeqhAD!Iw#Fm-BiYcB9V(s5-+3YnGDXS>eG1W@o#&}FYaqN$B0@P z#^3AvZ@h1kxKp?E`f54{DNxsMTnl57^bR5{VIaylfez^hhvD-FKlxdBm|>=O?))w= z#Q;YU;~2t*>vHm04Q(zXhjxV^WRx<}`cOnEvzB5+^?TO02razC7jI0RVu@Y^?V8nZm7a4^X zCkDzbk&pDwr&k`D8(iiEyLxAMFuV+*i~)!uP)Yy-7()&(FiHRdoBClOk}kx4339f` zKgxr*a}UY^n6r;?2di4SroLaj%RFp(=fF%niS~!{3P()wtd)B!GG-nrqNqM~lA$l2 zu~qu6`t+YXs}YqyP|D?b&7-T)Nmo~%^lp7~vv>QNGEg2VTL<|`=7Wq!lzaI6Nkzcu zSIiVHtZ#@88F&h#2-;?uF3sU+z^D@qDcZRzI>0c5ccY}=m6FVF6n7TBJ5jnc>KGse zgHCOVXp_!S7Zmz342cp#Tg){_8|OJ(1OK93Ctt(pq-`>PAMG+TNDhmB8O#P;8@bTP zhj^j>KXG53!uUd`V$FNDyS{(p9(E}6RXC*JMWp1eidQoNYh)UsaI3yiXVsT$sRJ?& zUd@19@s6TJB6na)1_O0vKjSb+k}2-ml$X`4Uvh+1u!Bqh%5-_sOX1x1s@!+7`VK19ZRf+zY?^Vx*!9XiSG4G zc)>k6jOfry=~0E%bhT;Zh9ko6)2B zHW0JEnH~&dARR53ktS|kxql}{@qF@19_1w|O-B_`J}yRobS;vozJ{+@Nhu94p-Kx- zwNbykS4u5iSdsVdsTBL8XL|2yi1Uv>{#o=nL7=wbd77kw2sY3<0v+M^Tcu##QZx~d zJAxmkiGas220Ik)3z~?Xhz!h~RKSXUp?tuwQPP>P7A8x?5Mv33UrW(&{hrq?xPc4( z2N5>lMw%nW4t2y}fjNRBN*8@)m`(u?C|F|LU<7dvo`XU2rXpSp+;d7D?@3iA&6oCH z%YMQR`XPeBU<6{g-R7BsBRGHfhK!<1<_XBF17FZ#V+sTYPiAJs6fzn0U(ZrQ;`paVkQ^01%-GAXEkeku41xrUwuO zIF&5WMy+sNUj`zcx|rwXf4}biUf#%8N8V6w3Vj)*3_|eo?&x3L7V2Rdf_(fW02&1E z6DwD0KhEf5#vgz9zl=o$%5MIo6>@vw&DKJ&(j-i7n$rN~~0^cWf_?>&eE zC9+J~P{~p!|G&N)b)ex8^+6d$LBFeh65%~vQHsH;(so}GQ52(Jl5=2LjpK-DKq;rM z4(qo(%ds#Ln2f{TV=12+we-l7<1(t>5pEx70GG>=nt&igNN$DOxHX(K?$8o61EVA* z2wa#31LZwDrjf6nLz}~xF#t*J!9$$PmG;AYXgNhi^e2g^Arbi$l%-G<9AZ(fc4%=1rAI`4^ML_Ztq4sLAm#xqzA1~(ByBCoVLhBQaPES^z# z)pnZo9{1UW8+8fAKuRf|W1Kb^+TaO~CUp%OL&;+FQG`uJC1_hmrIWb&Lyr3BJ#qh>``{~~ge#j>13*nCkcQEC&1Uc800>Cj=WF0tGw2*BYI_Wu1WwDVx z`vW)RB;rsCqm+6wCLYVVK#F&yL24g`6yC?^h4&uZRsZ~j-rFB)l;!tiATGVGZXb_( z*MF_Syl&j-EourRrdB|44Z2f%rOmSP8@dGJP+obkmf6B_O>YP~LebMSJu>cy#FPFV z>DwsLQlN95+MEZGb{UZ~ZK)6R1-%euq>aMkleRQvk#JxDD&{DR!B*5pL%~kq?Y*z-S?gKrzV{uhETcE@Sf9eRac_+BY}-l#;^JZ9 zvt7?eHF9q6uZ{%u5h_i{T?MicKscUd>y8N_3mYnm(TMGNm*al_&xu?EymfaVy?T@dcLSgPS(2thc?=j=&UcWnYVRtgUf>A0QS8Ps)k)BY zgHS@uv-we#F-MCkeEzulWoP&)N?NmF^V;$Y^jaW3R8Vf*e470r;3=q$o*h%tcBRu? zpTX1=%&wkpo;5Xa5RdPrPADu~bbu!>HIzT*wM(qT9Y3Ui2I;muZUxTyN9v_Kb&;jO z>Dycc2wbJ_O}>yB_!cO_OLcX|NdRvB;@h-?R8_vAotR z-@6{__u-?!nfPk`ix^cM zwd`i(ET{4$a7+1J4EmR$1;ubycHEy(NBq~|6V1@MUO{8yF=lPk0=FbB$@dExUEaI! zDE&6=jN702I3u|34Og*l-1Sr&u#ae|x`C@f>0$ses|uw$1ul=RrM*?#gaO6EYPq{D zna|J8@@eq{<(T42PC{nVWJy4pN;4r@ZTM~~@F>eyhU{D`l@Mngbc)Iv_;d*!JyV## z3Vm5HlFh(Xw#bJxa{+5}FCK+H7Hk77vXrUbp|KDRiS%8L{G5YsFA6u60SF4ho?N4G zXPTrB6U+Vbl-8H8ndwCCI~ftK+Ay@ov{M~;T}e~a68KC(oD${rx<8W9?W9h6N>O`3QD4w=jpE@yI$=ko>?TRmjViP0`8{V6Tk_V?< zJv4atvY>{x87wq`_L`5Yp6c!sh6^18qTzl) z?(FW0)a97lv({X?LXTZbAb~BS?vkNG(n7nbT$7;~hgfLB&Har;kl~Peam=+48Q;LF zy0y%1R^;0XP>Jogu~$&j=M7^*vznPqV*Iaeji^qvL9~m7E)Fkj z#mGu7<`me7U1xFh%V#ltMqTfJBjRKrurW8{2=A_WZ^`6~*Oz`g+ctMxK3SWS$%+## z`fCTxvF#{wjcLew?zTgsDFPzQQxg#iE~k!pl|~J3-DHjrY>%T;+nd|>J2tB$M(b5- zW)ep(XoWq*e0@nRzjAE)2;Z25Old}R61ufNNA6|t>~?Rajp=Z&=|c$(2LAmoYfr9^ zPQ`9NxRgK{%!)HhFk}fDjhGe~7qPf=t4beO}6vY0uOT5cGOoG z80yIDEw?L%qC5)h;5nWD{@?$+lyveip3YfEWI2> z)6PWihU<)%cFBP;=##poKmg;{%MTrUjo9}-AYRR@$dUpP*$7=eu8e~fI2-%bg_E{K zy$1e8`m3yVdc2my0g%);nEH}_C%blimRqWKRA%KLagV=GcYRfdR2|HSu2KsXwXf2n zOVR5z`W$;jL24vIlSL3A_ytL$xlFyG?80FSq5#vuxDd~k@>)b8eTl@8lpO`og8=eB zOhP<_=QG8}SO*Cm&=#NPio#FGQrq;8l`arlYlC^u)G?5&Fq=;YI$`)4Cz}i{@Qgs5 zO$R}7VeXE>IGY;cN8uC!j3;vJ}_Imqqg1^cUJn^2Lo~Wl$RNR0mg6X$cTdO? z^o;A@@Da$)hPl-9ob4E)reQqwG{AJ}w&?|ba-);@ta%hEI&GSaI%!t^D&! zSRC1iK*=4(_4UugLi&Ho+`c{6Q6-ydNCvtl3}9@trIQ3yhFkQm()Q8UR0c)g^)(Fk zruNwbf3NCeUh0zctAtc)M!9gvZK9?1Aa0SbPi3lSyVh_u=F)M?6w&2_9B>-t4=tp< zoY{B=w)^vkmlhXiVC9nHtFSKHmobdfUwj3n=*Y*p4A>)aOU1%-UsF+fvbeni<8A#% z^~)Y^=3Iwe_Xe_1J=sNz%ejdqCg`$)S><=UH9=lL&p1jW9DJ`={l2B>uEdYm0zq~c zZyA-`*H2WVE`HFXPS+u+DQ0ty%h+wRRp6od+k~ywIm|UXPan-UU$)AP+6$m>bJzzU z)k!0+3cT-Vwd@o&VAFC@gn7=-`|9TE-4UT$IYjgu*NeV46=5Hgd~*gWHNOI)sO@ZX z-g0dm%af+)znRC_^gq{DhQ*o3>VI-;wW?&u6T*_@&CyrKpR{hdJheSD9*n-s>^L^VOR>hDvOcU#LEvkNR z?KH?1{JnKDXK!|Org=k}))rI26lL?CT0xvVmyGQeP8WP5SbCIL*;U4+BOZ!_Ft<3x z2HEMYwA7$4T0G`yu1waV#LbN-S}JGqn6fK+;4VeRlcr37<#M5BJV0 zPru6~OTouR;L%AQkb8W|jlw-;&*8?m8n!a|ef8GfE;fqR6sSg)K@BD{T8i>~0^g1W zQEO?J?vo9OJgM_ocj4{cYs+J0P7cD1saYdNUqX>b!txkg5I z?g#r zWVbz65oc(j6&UZ-W1Oy{12}(j`TJ1vvF}DzWB`5pKfOB$6fiRVoR~wG0;yqL86A_t;UhEydZXbNY&ai#~6V?Qzwt+Spy63=xIB{B_O^F?R zxEh3V*0NK!nyR}FLaPn^#!)Ae``l88dhQ|Tpq>jv9&W9b*OJ|Pd4BrRxL?xman&cN zJ|tqOAa*Q|WLYPGPQ$&mS<>l}_a_U28Zw;zo$$X&d}07Rwdbp$Xgf-FIs+wr`bSV9 zS3ZgKHF3#wBmb5>kLl68N6!gcYNxsWN5p3TA=18K0~O?-O1gQtS{AcI(9++URGnC~ z0H2LoCaP3;X4lqv-T}^mx$T}zxJdG@wBS7z0j@`jy!(=M`>s|y`%QxqfR3iSK6l^! zEvs6pG-Y?ETo8lY*%gP!lDptLYfWDDuQl0pL`KV`zH9v5qKpk40{ow61V@SU{MJLnp+(SN($oW_m;nfk%{<;CFLjnzYhDgtUf|Ds=mg7Qv3M3wtqrM<(w_05gF%4q<^pY tPs_Od#l950zx~s%zoPxc`M(*Aa7tm40h8;6z-h_O9)z|byi^kz_%9)?SnB`) diff --git a/docs/tutorial/tutorialFigs/glyph.png b/docs/tutorial/tutorialFigs/glyph.png deleted file mode 100644 index 6182a6eacf00cff2c761643cd6a7f9c426d0dc53..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26718 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 ~2B! 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 diff --git a/docs/tutorial/tutorialFigs/system.png b/docs/tutorial/tutorialFigs/system.png deleted file mode 100644 index 19da2cce95e74c2701970f3fa2bf71c4bf3cb9f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 136363 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

k%@y& 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%0DP7*15kR05JfE_ac3Q%C142lMz`ZW zF0o0T!>NF(Y3@;3wL!7!`CsKxseDn{3I+JAt`qloS)AX?ZGT~w;!Qz^_=Sy({XEGj z+iY5;#bk3@p7YXy30SFB^7)PtMvfYwN0n({@;N;&cXn${G590NzwjXLa_0BF(Kgv_ zaN69Lx8IWA`-p>m`j^wIw9boj^;c25v-w8r6gN>KJl^ZeS`Bww|ADiGB>(IP!nbB2 zTx$@+lh60}?yf}sX)9$5`EH7hU66C6APIZo*@ZfQeGu8v^vH}i#-h1gWsX4uiYa5r zag?4xikW0#&2ObF$8Wvk?dD*hYp|kgka$Oo^2jt2r9S}5A```q5=GeIPejQJf{Y{$ zrvD@Q)&cWxiN3F7NH@J-ME`Frzz2MF^ggj*EC3^L;asi+?Hr+0KgF3LFHqMmrpOX5 zHwZnAugH%W1tlv@GXDLrb_tDvKd00EkAs|3f!C`C-N*^y4<=+k@WS)bcTq`#(pqc` zSC1-t^{A6XGNe0J;YVCP$Ks=5%SW8@DGF&unxoUb)mm)HwRwdbzl%pI&r}!y@d{=CKT?LhPcc z&m^%R@m*I*x;}{&e>6l)LHaYF2w#b8b4GYL)J4bRK30A6;w$cz$CaT7oO+-#`A4Fy zz(cX&RJ&Zoi}tK{j;4>jQymWxangaPB$`SXN~BaE(J2AuCJ2efi;jIP3I$EDN_3oH zfba7IBa#2>)j)!O6Lvkf`(X6nXRq67 z`((SNW+OE_F>4YD!cD|0pVuh@>2jVPOE!^68>Mz_E!^EzM!`>w&MW(4XS^Fi81!{s zfA}PXQZ^#dQXsySW7*{<<(-}V${ajB&BSF7yy!fh1IG>Rrd>6EN^}JS#NC;*b;Q1K zWv5+MIu6cK=9NIjfJ5Q|xnv?tPcmIOI^8&?Ccp!CLTdv^ah8GJEQ%`m8P*enJ?%45DcsDjYkH9&DzhrkMBI{DNQN`WogYpiLpG<0z>@fGtMe% zx0#9_iPH~lBEe(=N}<3tx52|3eIV)mkjbV+&PvVhlBFyIS8KJj_`&3Z1ha_5a%qb; zJB~ysX|2XCrQK@UWTD>7j8=#dw$dD}IdmGke?~+mL?~o{f%;j?OaHCli&qeQ24`-Y zVI0hFY!xtE^&7ELCQp6XWsA5nOc*d6NBgd*@9p}Bp1}HN*!vvc*Qz|XvCRzb6{6Fb zt&<~X?~QrO^9f2CANS?$t!DSEmcNm{f57XV48Pvt0%+?>oT}?NnN8${r3qrmzj#-^ z(7-%oT;$E*6pt6>d~9BLVNr3Dka;%KZ1KQ6_;WbvH&-rxkUgQ(ZFJB3^(`>f^3-l76g9B#LvV864R}1kt&uHIpixl22*I(DDe7JB;I&7c1?7MX|$32I6 z^#7}$ew7q?XY#C?1O#Y#NnnZ5YZJJ_1(113V#fdA6 zJ6~?$M)Er%12f^2sU<$wD(8;{_l=2@#VWJyEzziSj(i{zo&wErJfq1TwquZ~WaGl! zy2o`e=1b$vvpr)J16kzU}3)13Li1UZTbWEA#-Nl$vC7IujEp#X;{_f|61^ z+@2!>d1YmJu@v1E45*hTNa3}Qr1T63aG8D>YfPbL zhfRxRCErZsVzcVDE<*^Vxu_XHteJ@M<86Nye25VS2F7c)xcR3~JoBIY-y_DSA=!S@ z4z}h!lUxj{Kyk|8TF$`1!->y90>E7{$f$kZjx=EQU(2S0b3Yfnuv4b5Q!f2BaR?$c zY=1Y&f|^#iLAE1c2MO&BFP0pvuWu`R?sj%AFOToV*X1KR7mMbJ*^g&G^dqzC=RD{I zILS#pKID}$^7|V-?rcgDYIa&qboCD05eTevQ5L9;4F?%cWmEqm26boE#MOqX%Q-4G z8hU%kq$fv20~^pFKKH-l#(U_i*}Uvye(aZtXfOyEz@ue=|A{|DmbtdT7Gp7LgK#=` zP^9l`_sCyPkFfmF-IOu{LoaTYW11_0?AvARIQHM(A*$c-zDe5N&UcAk$?IpLO=pTe zcRlkL0*jAmjHeh3^e70-b@(te-R|VC#-A02H+Ipa|5vBD`!iTqvisAU$3vGvO$jA1 zC7Upmm`mulQ6deZ>>C;O&fWefzW2sUwI4J0i%*imq9gHFp z1cg@~o<|FI9wF27PM6$E`s!nea~lK;6LDeKsp-?y?6m!B_GVEVJWj@afyp0AzALs+ znJO}+!lGpoP(sxZ-`e$Onoihl>+8nN&9crwG~mb^{TDn->U{O%mc3qc3bFy)gwOCW zLtKLH9#}x6_aQ=tWHMONJ6{ci+^Bh~KAvy9wf{S4N!Qd^f_JbRhc!J=hrN4KNve=D zWUDvhZyUjM@EVlWzT2D%mfNsR75{-(;R)d>FpK=$nB@r?39-2xk|WqE;eG0@R}x; zAuwAn!mx*aMIjaYlyD@C5Pj98E&JQr!cCD}D70Ss3UYK!X|dfG4v;hG*#G6tcs;+^ zQf$D;;Tu97 z8Eh5?8cD>n<|nu<!R z0wC*`Q`e$kL4Y7eIk!Qzk7kxAwQy={IB`1l8Lulezsp@fPcm@81(uubS4tcaYp({k zSiZjm1&e;sy#JB=!aBp+b?=c-Mw|De5)X!Qdl)macIkYu<%YHe=N;kb-B#qKyTGf` zC56E3l|*}#Y;8yB$l9QS1Tw-4?yZ>zgNe7siAuc<>}hyFLUGAy3w>-&2`<)S%DSe5-GagE zB6uvW4oZ33AfUzr@VMI3*pqo?kLCXdYc=~Tl_3-TX1w>5Lyx``8_mot>>d7qoxmg# z+qFbt4+j&O|K51$8x^T}_&xuNmmjY^H$ZN_Z@Q1^8DoR0NLzNiy@aOB0s&Z1p$8)N z)+p0{`=H#{4e@hi;sYe`_t9x0ggOQ=WCh*w%jJ#~xyT z-|!J0*mzuuDnwVdF?(5lWHi9We_m zdsS5_F~tBM0ti9@e$G3H_cHmtfpaT*I*81}SGJ6dNODOc(-J=5!1jAg7TJ!K!>>0H za71?Vl3;&&J_|H}*c95nbjo%=k2Yjc@^d`W%-P};d!h90_<*R)Atv+e3HQ11S)L*8 ze;DJpPlPljyNmAoQJ&UFP1rwJ>85Gh(r3zTX|)jG!Xl-qjfQBp_9^?&D1VEhu{r}n zB&3IuYHEen&30)PP5|-QwN}ts+;WMPDFt;FziCue7Z5jQHJN$#;nVhH;hh;e+uFZ-}V?u%E zpGh@a#)ifBmjy-NhkQc=|JV_^jimt}#`B2zf6K6kEiK8?h8b14&QuW)PbGo}^ zIo$7tWaJJ=a1gN3fMbR%X^?=&VXpd|JaffKYBTCKHpzJ`dE8RN7qn6R7(~twCg%l; z$c1JDXo2t&gQpf9xAQuv{gx3QiPk%+=GZw&p-I!Iy42b8VhP&O;Yb1Eep*GkU}mt z4O?+g15x`sL--}Q(KxWSW9czPuuX=JEMm?Uoz7i~N!sBsGv>h!GNGzvZ+*XdDJiLc*#n6Z zWsHy8>9vJ^HVoX4Ahe@NGVX%RAz7&jX{iaVmvQV2NyvaPvYw(y1qsWfR#Xwv^5gg8SBEg8zihLyUZ8SI?h<=jMPsT3)vO(dp#q;?%*2g{hdL^*V^J=e#xeln%P+>;Q+){Z z8UxXYkJnuiOoIZQ)u0|LZ&p^h6)mwC4dP*2sAx0nB2$8-HcvsJOmxn(N-1lMM@O-5 zd!D%MS3dWANEKq%J4{y#AG@3jC?TuY&d=g zF`RSeUMLT0w1Y$2G`Tm9mun+z04sm$iZ8>(9ifFpZO*l@b}w3`3 zhtr}_b03Tjhcr|!&7zUYt8fUJ6W7hEEGoryqkEo9)W#(h05q4~U->F6v*)k>>+hof zmoQBI+aW4RUjkF}lD-gtay;9J347^H;HegU-|y{w^oS$FdA>1@Omq;zrGcZ~6E(v# z77pbHE4QaNQZYw3eAvzB2uVnh!!6;?Z)Diao_k}8+|BF)jx%hPp_y5mdfR!Q;#j)e z=&Q@)7u1IGgYDOAx|jru^$!ppVF#WNIcvvn@oU;#I~HGEuLT2h*@ zyf0-4fO%UBD`tFHDA5xS#C^^^W+H=4m_LDw#Q%FqsNRZeBIh=q)gaTqA9gJMCJrf= zd6U%xaCLh)&YzYy{H3a_Ao5N|MB0xtS@~#}qf1Q6*P$dA%1;T4QkNrh%^QuwNkaXQ z6d+7v7U{?3dq5?4x)!?&86e7Cdz)7Mr63G1H+%T$mXOQ!Z_tW(@+VzYnfO!R4gNrZ zDy>$QGHvo+zmONY6vmkjR9EYPf=n(@_C-xiMJ*`K=<|+o3L4wI1F)In+xh%!?$}R< zmwM@=C(bpPzt}TMC)k_7!~dVujf2)Qr76e5NG29dyVp9%?tZA^LWa(I@YPU>pG0Jt z5}VaObJ4hfy=Eo5@{54i#Fi>>Z?>`pQhs!b%=^b`NH+6`(6&-}=$=?b`}AxXuv74IMNEA!ux&fR6=b9|qt(!trp z6}s?%1~lO^H_yjSfeEZy zo$oPAEkV#+G6rw_{C^O3_Zoktgeh`7US)%1;NyB|o4*N?O5znVNoJ^^GWV-&_<0XR zRwJl0{;710*6EApjxaBM?D~Grn~*@|v5OwVpX#&CR*%qUUYsvg3C$3?o9c-Eui>?j zVk_9_dJ;|avC5$F;l~CMh^PZA9n$v5OO0k(EoH1SJAH`>BO29=WwUt${O%EB(iCJO z!8 z+!ZFHCW=I`cn9lOXbSreyi646f6|xU$~wR+c(&$W@T?8iCDA3z|aYm{*dD9khrVMMsTOwuHTH;RYqC|E^~d9~1sk_LQ577Sp{ z{=1|xvOgh-O|M&XSe4CBDW66`jRVWnDCNiv*ZDYKpswLoU(LtMYq4`)z!6pMjFfqG zH}C-n(9@Z3Jacii(l);;l4qG_{T(+~HsIFPI#6U5k;%wM+Hjov?deQ0pUgTG?=1bh zWf@4ZiY?8C1h5m2Lxka`=;PG-KBQTj)=DBVnLQrYB|ol|vce#>VFBScVADN@n3ovL z4PJ?#b&>|xUnG|^o@v1a&y*Ap)Ai;&K_(58a8}OjA5?cU(_W1}xYDUpo~;Grm+q)< zYcj9%jZgKtNx9n3Zcy6T4UlMeds$PI$(qh;?j)9*>Y-qs^aYz1SR4m0C6z54`#8ra^Re)= z&%8GHUj0$1r2FpCnVY*#&x-^Fz&gUqVX+hlk4>>R|63=TiLDq0$|7XZ1Ab=hNBLulEH3`% zTHzrog^ate(&T9~X=B_6J}XuEg!K0&OuW3sKYskE`2o(HK>_|=y+?SD00kikd^lZO z+K+4HK1gRT<9=EhGV6SfuB2^U(+*w0L)+HtegFW6{%=PypRmcsE3DcJRY<40Xg;yu z!Xuxq`ft_y@VfRF&PZzgO1@|O(Nx#oRHw5q_G)(DoK!ymNex6$d`pbIU-&bq|C0TD z-6@HJri}+%V@HC7-`^=m3`itD<%6lMn=XBTcWg_ z9L8;bwCEz$urQba_HKsvj^7VuP8Xn5W$;9Sy5@(wqrYciuKWO zB{sUOVS{wFiUss{jm{vLhq1epVS_DM3^EGe#QO{4xi26RUcgI+A(%YQj2LO(8r=&< zBGD=W1ohs-{*|H%d${h*%_IY&QJln>VU|?EHMs_2+A=qUz#Fr>Tt^smT<)Kz88jXV z20=9^@gxX3_2C@+L=A)`Y_nfkcCAdGSG+4sO)@@_vO^3oDukynsCT>=TX2<{_+w_8 z*_1VP;N%o^2oId7S9k8TH3h|D;!V?jm!u%%tFyi8r>i;*d>TcOIlS@bN4zQAq;P>X39nasle= z&+S6IW%kM~y1!uIn(-PFLdefgSYKb??%Wgjvfd!AprD}c=2k9I)g<3}mL9x1-KgI{ z$IHRNLrVIIlyvmO5#6}~Nx#eLJJSg90JTF|i!o4V;JX8DO-l0#*dif?-uUkgHa9lt< z35t`H1r%8s`!$tWm6f4Dj^8Q-p}cmAL=?n;rG$7f7-k|$A<2^jIsm!P#{7?a7gV+mZ)_defC*$Czqbi43`b&|`1K+1!R77BvCM5~MjGFhjj z4;?r~dlx?gi~if0M`cDItmhIp9@A2i)!|~-Yt3u{xc7+fpoJk=H;ZB3&0v{EWd7tf z=%&au&!+>3OL$-`K*pUI+&r7S8m9AMZru(Z^3xo1JkWzWesna`ro0fwU^ ziZR}2y*ge%T@b%4k_+_4VgqUb3rtfljL>XFTr-PF#Ujy{xgW>p3EII`v9Yw<{X@Oq zn~PI<$@sZ7^yan(au#UCdV;9IY^JAYP`2;u`<2$CFy`WGV!)yGA9~p`J&zWFqzEDG z-PgV%NK53M)*s3AAU!h(9YPOm*#Bb<+#_E}Wn6dMEkpgPIg@L{x!IPIL6lrX-d_a^m9L;Sw*!hxfWuF&L-zR1Kv0;p~sZeAa$C*RTh2IQb2OO9Azn0M3 z$0Rn--Ew42R(SgAC|hQTEB_U zg(A=5dRNkKW^gb@zcz9^e&X2$0O)nnV#2DJIrL>8KIf(C)6stFGQSS-Ej6?#i2!6j zWJ2xW1G?$^PHBZU@+x68qT$G5wcaGPDCFPrQEh$h9uhZ9W8(#6q~IFD7vX_Qe4HKo zE>3xdi2o`+BOr}sozRrKy6yB z->tIXs$hI-L30b6&PfidFgemb==|_IP_k+xBccKKCyOmZniI=OS#l%ixgaXva=5pc zI(b2fSwx7GioYs8(b>L~chC-nC`i(LlgBp0vv$k2g#|{sk~c8^yTe&{fYvmh>Mf4J z4gEqRoF9&Y=Z)N9|FG(L;!fpeJM)vvG4%I5XAtfXcH!&=2w3+kP3fS^Zn9PHxc=J!_it zs1v%Mdmc`saQg+hpZy}IsB*@+JgB906;sA-z1KZWK;TuyY5n_;uWgnigqIyyYySb) z{1YX4w5D2=e9vrhl;z@`#<3^pJ|Q4q!VyZ-6o!y^6xd;x&!2r>b;J_*9k;6rl%Q)g zdLJx;CvoH!bEO$4?02lKyF*dtXm~Y_?Hlv}Hg8(qACnc;cxq^}83|GRBAa#+WfqWD zDG23$)3Q}IZad)5a%QS+eset4`yCMu@p3$^`}E)XUS&{?7`yKmn>XlvUe1Q}s}H7f zU(Qc9UNS7-_tiJp&NOJvJTi7%8%!0bbSjQh5Vd2ugxN;kU4DLx!Ihq=Zq*H;mgRp}oH>dH zS*F+F)(%FEkv*rG7o(TLNlB1Q^tuCw-Ce5}`u_tk+-P%65S5JA zs9wW`J8W(B%u!5pm0v*0Q$|H4y!|8UEAZ@5wn5||z%Ei^u6w%fE9rK@ty-zwz;%C4 z$m#Pscbd3knR!)ULPhC&DO%lbPiyhE5!h`Ib_(7+`2F@3vqX1%NI{N`KT8Y-x#jhY zJFkn>6_m+owBMb>ICbHB|I~z9dvDFPqFE-Vi5$U1eCmtSLnm-z`#qG`*+RwtTfb)(u97ivR33_2?J8^D9xA%htU4xW)}KN^Z9^iPy(ite4>U*lL4A89 zv~^Bi5`VnkRK3&-LzzH2_%-K0%voY$LqmgMt0)OryX(Oq@-4_&Im6|26VvzW+0Ida zYqN?VV0UD@yjRm(R+d2rA%9tO#^u>uadtUW+;!i|M;~%e)UY~nd z{g=?CrKEH@Vj7mF@rW3JfH&`F4muH72uN>?CZkC^_Z!XDFVfBdYfDMPf|XT$1l-BX zCHiMVKVdh^CMhu6x1FzMHX#Qha>U!#qwX9}<$+pc%T+WVoKdp)^4h7XpwcuYjiY4^okHZ0HfR2@89x+i3p&pu^;Q zvg^)HKWOn^jAhn%*opAwbC|oF8o9Hrh8V5iE{23NuaNr{F}+z7bHZY(TirK|E-hc$ z-Zs`dCB?PQ-{Ze^IzxQwdC!USIcPO~#il&VPM_t%jYU>^iYGlCG99n6UazvLxM9vQ zXa2Y@(Z$5&(qqU0kG56uOmz}1OzyIdeFg&qLP-Qg&a~m;tlN0bq|{ryyh`r)6`!qFmVV4-ugO!ca-P*mfBRMPDMP(@=BV>p zf8A?9s(?q;^QtxO_N&am;O+q4T^sda3C(+Ism<}c8B4739J>k@O8W;Hu{_F1{e*^x z!`d8Lx^jg~zN-af0q^}%)Z=W$NJ19VNhs#4spex+u4L(G1I5$h8FHAU%!ps zfq{1Wob0RZq9U7>BV+FopQv;H!Od}a5)x9<9?q1_dmjF-hh@9_eQ4B&;?PiX?aJxV zgnb~rhKyh@vBfPWR4(H>7B|jH^nBS}L9DO&?lLpsh0NcZPg!i#Tvp#G?8^s4o>ZOj zFXevhZhw&fjE{9=EUzs%wl6^^Z%bZd-)QpYGJZVvD?x5QJaUhJsdJK)IO^wLB|geeXx z;SyCTcAE(xs4NPdZQSX|TU?BvU9|XaUWu~{XzS|Y;^O)oH@xf>e<1l}Kp*AAZlhzf)zr&aW-b%lm>MuG3i;)AZP18A6iw3nqQC>Uwl>DWSI=2b_|L z9k|~wxAau`;$JYbq4{IsME4Z@Fv7yGBPsNQgQNQ0%yZlqAc_<7YP(?L22KN`P-h_) zw7M1LnOW-5!O?7HUy*jV^|NKqiPYQC*Nd_R-`SHJM#j`yx3wm^LXE8Mytbxxtr1<# z8Fm(Za$&LfcydY!V`H?uBDN8zLK_8?xH(c%r{)pYS8umc78m$s?}9FW z|MJM9I)u2kYCk26-@1NFyiB80aS9q^tGZQEozyzzY8ZR_4h8Y|LshsB)E|T((*Yq-R-NTZm`;`d~w@+f8C>!kbF(Z@0W|l-Qfz*s;xFN zHP@+}2e;%KYFo}HbKfSfCcq#<$nXk|H_1vBt zZw?jx8mbF!oGO$O1Kz(lxhS}W#qx@tZ+mRy6;d^0)@FSo!=qC&6eHyBBC9ckOx|SJ z%=8{K-+SL2t`Ak~=@Bd)+W5Bkhw4|Wq~K^U=1p1IG4R=K?6MVBQkM%nKHd^xeBphJ z@?W??0&DAvG*j1AVbl| z58L53BXH9nxJU(qOdu}+8D+>e&z;3@`4b-1^RX*EA}XI=!Za$;T^Q&p4iB^Q z%@FVa)Fl>hBhbb3@*esj?o*-@`vt_OE0F2vyQ#x@N#m`*1guz8usEaf1%;>pKjsQd zef5cJ#S6%*3y2{aSa=j50_GRu5QiK0v!^JDzEYZ+XGz8MNRbE3&&PpV$9B>yGeSor z1rU=$#KzMLHjZI%1k6Vr*#MOtWG4xZ@&ftb^2YkUG&rDzN&#PPqbn>X(r%1`G3AqE zevt|Y$)>WhNQ#+~M?|5=0h3}hM@^2xFvMR!UT>XCq0$I+M5=5N@=>7)=^RNRpnZ84 z$thR0!6k0j3%)_qT-?}{r<@TK`0O9-?gebU9wbn)0laBrx|%yQspBw{`^t5tMNL|L zutGZh|9-F%*F59DO9qgl0n5gc>k4qnu|l%UYK})AP~ic>*m%S-;i_B+zWJQI2yw9> zSJE&kz(GTDzWzN<05sncZw8cjU+Nu0abPGk!0Ebc4?_w*?zW`d%!vn1m;NRQ>#T(ajq&ZH1RRvKhb zv4D^mq!5`3T0k*##%PuMq;vFY@PQa40Q~5|4ub}O%AO2H9Pyyv{opGB0qK!RYOcPA zN$z4|Jc}F-$>7cc0hA~>s2nECL7|~F)(cgjw`egtQAjv4_(-pTVK(^_0zHQiXLIAo z$#JM@b}day(uN}=bakV7Ve|++5R&&muO;>RGI`bcq3W)hN?hsH_tb>!yCCl7#^v;0 zd;`DM+hSU-UkKeiHPmnz_8FKQX*K-UxWoT1;?m;6)YH)%`^lSmtvX{1%?ab-`-%9EvRPs&n4z zlQHJDZ=-Ll>@*vsM4C5qgq9FrkA%;TE5Bii|KZO?!Fp}giy8=6zS*C?N6hJH{F|lm z9of5}-=tI`3OhA5(MO507w!mU=L2%iW6A1`SIcuB_!r#(EN(bFp@}j>qjbyP0K~b{y%f_q1Zu zBuRA6`SNgCK5z4Qq-y8U9P3#-;d*?SQfF%sB?-Qqsz>eOrJeBBUNnJEqqeQ)mzp^% zownV=(DPiZi16m3^YxsxwI5+l%|_&C0It87llCgfc0b{@c$ES$5-)KT}1E|SPULc{;;xzm53)9uH6-q?83#4l*Jy7?7~ z@5);kwB-N};o=#FOu;oxGr9G@YEj5waQete)i!bAeShz9cQ)g!zp8pr+xz^Shj~%` zwv)G~@1?hTI>c}rb4aMJ?{{{I0Z_?!(ExzBG;ZL8qIM(OC z@H8}QSF|4dz4Le16nk7>VCCd|TdrOZ2Qy_bm!;k66ztBqx72%rg?*BAL6())*V?(l z(t9+Ue~+HyV_foh?oY2{S*zL6c^w_mcjMJLW?rvN|ME8Zw0Joa&6~~bEkI8GIg6SK zvDNM?)05Tg;-CY?{D>X6aq`fke2PF|D0;iZIRx; zjvigQaN)t_e$Q9EmvUnA_CBpEmi=XkdQOsc(HHg7>Xh&6#P!8K&jDrIa&E3q@%wTd z-O|4O>RoA?mGe`Y`^&FajHY|z{(owc%)XwxJE$u3?xc)xo+r;$+u40t7M~7#J8dX(@4K7#IXb=ySkpMCe~XRR%2>m^b)e z#KaV(#l$ES9c<0MSee4WNQEb;BLU<4@iW1BnAlnraLS)-1911@vTvwJt%7Jo-(g_~ zfJilgb}WS&qClrt;^TEN)oMqe*VoUY0DvX|>IdLTo?m%IhU;jGZ`)GS&4??#>&_6& z539kE;b93?m_Dp6)LeM)Vd0h5DL7a#65WMAq^(>a<-JV z>e=}b3}Q^8-+=KG|Cm02XYW8E{2HdbzaMW0UeqGgBRA!)30n}R>K7(9f?t@d-LE&6 zGIqspV&WINBpyBu!THIV)&%dvypwk0Lgn3OkB$q5AMNc0%9DFO>wdvqs zCR!>NGADQy%De9yk;4BrepsHoyILpj)AeSAL(P26%jov)YFs=ESxP77hp!>*@3i8$ zbQ*q@5@ln{1>c5Ia8qF$afgjwG%0%`x1~{{gW?x&9L3x0hGF1gaP8)OvM~=uhcL~% z-3P0LwBKC?Ut1uEi14aKs|0i9Y(vK3KDvk}3yw^@(K~xxz7zgvEMYZq3)_y_DI1V*U%LncUX;EI(pY6q&7T=9+_Ci zRj&ZV1-DduG`^Hj^>39h zM?32!9M|5?wWL>RLMH*u(YG84l4i8`I8R|8yI^v<-(jGeWT2&Cp1nk|;$90TX9PtU z(1e~sUMnE$21y1H2ibIxVq#=U<@S~3Uql3naF_?!jiKef>Z_3r=6H~>vQz0>z`AJ{ zqVVk_HD6o!{Wc4R3)0hSU=ywQKrIG&*Klficj1LSBeFw1%cRN3H=vmp{mj*tt0p1| zc`}S@ti*Vl$JPds{vI);uR@)}DkWmC+SEXui6%{08<%mTDhrC8&-uAjZ9wuI7G)i1 zopqd)wPQB8n^c%e3I)tW##Ps4oBcCk~DicPi+Bn7#j2Dqdm95 zZ<+J2H#b~$SCCisr|Y^!mZP6D2x9 zVfld+>Bl$JOU4W9`Wx<<0zo`L;~PSLJKiV^-?s}nb^>NN+8=L9;SPV(Rlc%9ayG!2 z!$@t%vcpb)l_W~v2z%20Yws0XJB%Af2qpq0<@=y$3(EU2WTW6YYSu9NjhG`UP9|(KYw;p7bTQ2mG;weoi3aZ(I&UGe1pZ)?_yPa9ZOp$(U&| z2}XDtXft7T9WQrpXQHl*v+Stkds7;@ONS{0(RbI}Z@s+u+K?+c)6OPuxm%FPe}l0s z5J&^?+M_V#q-gSR%&=u%;{{~|i3M@ltzKfU;Dn1Kjz{jjxiQ7&i02J$Om9qSOmYjD z19-`i(8Nk<(J;j!F~oJzZO6+<5lc5q_e!fvx8}u|0IG%OGf70{=}`LsTPWwO=N#v% z=UnIC0zd)b0coG|;M5K4J7j`MQOZ!%aXs)t-WB6=fskku3MzXI)#lN?Xx!AyUDs0fZWBj(@FfnjQQn1W|S2aPZd`uEZ(cUjYyOmEGYXi zJt;pi2TSR%JEC4@%c+nfW?}I@w;LcOVBaC;(IhszA{; zZIk&qV3?rI7+CD2DljcIO)|Y*+zezZ6jrA$N-4f8v7ZJ_*Oz1{d&>3hNjT~AhqfiR z1^R@nl*j{lR4~*=fov+PxhvViKeF>VK2qx$CrbS6QHrub>|r-W;HoBKB6=X=Awrqu? zRoMGiTY<2?Z`rf`6M-CBJ`Yz~lfQc^pTQ4fOcRO^b7o_P>?RgwgytG1jk9O|SlxZy z?}jbPl+wois@m)uAv&9go6U46bgXnlbcdfuQoPu?xjwV&8ZB7eFUd_Z&y|jqwjZ_e z4KED}j<)nX{(3du(M>gIR0AlW{km_FVyOAWy&I*@p?X8xy8hs;B;hW5{qSV zU4yf~bD(n!S|{C5eqa9l&a~Smq<>RAMug`%tt{8N{uq7H)8(rRr3=8N%*FPI<(Tg} z;d++T0v#3|l|+j~PoXmPcbcNmdOh_r$C7=xaoVOjy*YihO0G&)O_d|aG2w!Rz&Yt% z(l+a^E^@hWIjT-fqmP55!=UTp>2*?booC_$>&-6CFx@%bN%HMn=UCuz)o|`&{C>(t z({#<^;O~fI)y=Cg)2@wbm+DM+Z8vj|(`%n?<3s98zO9S)*ZI=xaV}_u(f-uK{O0sSIEjzUl z!`!FLx25VtX z5oe!8xZGFWs2&7}tfkuGn&0d89Ad4N+{c@{kt4JtoFfSEMV@KTl0vY5VjK3C(VocG zGj}JQC%G^?YSi1$Hz?cecXKXQgR0pE`3BjZzup{%=!dH3mF;Ql*H(Mr*(VrG4^Qir z91f@sq7IT*AnQ-oCN|YO9XC5q&gvE>D^{5bnYOxl--J}6u@Fb`lrP+xs`TU&CkQ8a zmN*qBs(Q~2Eqm3asxCJITxI7IN44*?ZEaDPW`M?8DpeVctp*R1q!59Tr=4@{C;8(H zQvvV{%Gc(@oJ09pf!W#9iTS!}SZhq4%j3-IX2+P-%tVR99&1k`&%tYd?+tG@ABDT8 zo#(M9Ya|`?_hj$+G@QeH3Tu(Sq%OZ7OCMiaj+>fr=H0T>F7Z6f7O{FR6f&~QJ1^`R zPw0Ku3yMJ_XA=~5qq-4)>A#GRSAW=Hfe+I;pU zx}wU{?0IZgQyoekKHOYi2+X_ct+F%4 zQ$3=hZ1od(nS6wZY{V3Gexd{hkvKIG@pqaQjD_^s$rv&WK>GSR4u#5-O$O^$LE~CD z9)tr^#|*Q94~L0(`fj>1MN}@hb?bn15Uwz=(;`0N-Eg}eeguj>dr(Ygr>!$AMIGFk zDzBhmt-IUpA-dExIt=}}iLG!r=K7OQ$z`VFyan>D<}>m)B+K=dF?=p)qJV5CrRfL* zgHQYC3oETma|QzgoB0K(;iU0dj^EhUn$^(6*2t9A&Dsvy8U{wtjUW1GZR%u5;bv`R z)EP2?YR z#7!NI9lqE(eX+Hn_(Rvw$ky3Oh>GgZi~jlfn@>}>FaP}}8^`|=3o0PnpBgp}R(7_3 z(uQ^w{8P%W_{GiCN<;jMwW*CG^c}(+>>oY|{?*~%s{Z?x|I$_SU%GO!|7*{Gsrj#- zf^2^z_!o)(R@YypP<;ub2(tZC_rfS1#<8bRbC7%yR{%oqaDQwA{d|Oe-~HDe`pocS z6-kT)dbg4m{|I!0J<4!T*O7ESZ8krgzfn#7oI2j7U2QVolNeRCJy-n{8425qQHd7R zMM>FD9n-?nzZ={lNdXIo_$H=nUJqtbV-27eMDvXWRknI&ynKAf;=0cKH05N!Z&%%X zYf*0W@>0$=SO3LcL4Em`sxw%%!DiWUq|EY0?{5B<8Wx-4-!K1n6b1!3R47{hs{f%D z78?@|2&|<327~_eV~E_kWU3isI{QMI|im&+VXpR~fWKQK?<{KS@V}$&FB07?zrE z0Qh&6K}!k?4aontQ1W*_bHIGwLlYPO>#i)TuxL;v{(ozWT?}fmAxsJk`aiNa$wPHE zXJN?5Xq20wA^Q(Q`4>U?;NYgt|3lH=G8L-ys_{o4uuDRqD)FDRRB(Re7=4wH`}jw1 zv+to6HOjp!Dh!KqF=CPahwS-uqJZwH$mD=OMk)US*9Jln#*~zFHE0%>6!;g$Y80TH z%$pC8{OM^RkF^GG@AQFwk(PfkxQ^-{3Qp$p|AB`m;Bu7gzCYyumzgOJke43J6|@d+ zHeir?3D36ck2>WX;k4=0{0^Vl!Vgk^j2YeF@u)f)inmHFkYO`Jd_vp#PSUK|0nB z1R?~5hyP)0ZGrYT(#D>Z=l@`0Elacy4{v#S`B`?C>@UOl^NCSh>`W&*?k~Fz{V&Ah z<8j8?q0B>F5L2J-k7E6k>Hnht=|9rf)z$s~=pVvcGVg9YPAkbK`}bi&jTj;zOZyM= zyv`K0v9S?FLONFZ2mpwBrG))4kx?cLd01`@@;vMKJZmZ5KS1s03rDFAbaHXIXUp*X z+X6dp{ExI}v_^jJCP^|IB2tO@9b*%nFem&2&lGNO?(Xieq$IVHuki3-A*NJ+dCk!A>WCLo}HS!NEq?oGwfP0AJ+ZLS#nximwor+kE9h88jg-n zPaA@W{zhRKD_oOyc@418a@6pD(bdO~AOBCfs;#TLWAly^A|fKHZphD<$m`w2($v&s zh$H^n{Cia2To*lLgZW*_tAmKb-oVz&nl#n`|Em0B=rv?qe0)SFyD>66sC#uK{pDU1 zJIH7Ly!>h3j$o;mX;+i6Yz5?>gwjws!B?R$Xx&B@uUdnwyOq3 zQoU^G|BEI;AQ0UDNs~W+cKhysJnA1BswJ6TT(tb>yNKXkk+-ZU+dY)K{x4q9-QE3v z^NOXVBk(1~wTY=|zfysG2KK&7GaxZ32`P4QDzvtaM>?m*JVqgdH;AEp)h=S|#4azW z^84QKuaXj7##s><13V+VLztP#MU%uUuO!qc2Lz+ zJJ7UPj5GEf;88WD5cxaqD###SUF`xGZ#p{MvGr;#e)e@sNO*X?YcbB9lH{?rebjpC z2_hlfi}$FnGhCX5&~Z>IZJ?s=Mhp$39W}3vJ4}o(u<~a*Id2>q3~2Wrx9GH_y|rw~ z{ki-!E!LZmq<7CFz2Sfs;lvvU5WZbArWzb97I*k-EMuMhCdzxr^)dwD3UfbJuQt0ZDPyyw0kI=x;TM#fK_v-;)y<%&w{ zF_@ds;mr#=qpZuCctEF`{9aj=P$)E~5C*^F^J-UTXSc_xYHFs?3TpeG3O$+jGqN^l znJB+~AM||QkVfkAWK8^GeC-DCptO>>9UR$dd7}`0#bl7(w%;T0l%IhraQVr`((?P{ z{!zeB>&GP$<6Fx-(hzy=bRoTs%_Dn!K18wD;pLdk7GsLc#E?4qo#8-wc|%y{sm*Pj zP2Z{by)I<0x{r9+BhTpH`dz|n&EYE>*pMMYm3@3iU$E68UJy62#7BN_HBTdjI_}!F zGM#R10a1IuZWiU>zu?fX#6G=MTVYZ+DhmS`URyETkI@Yl)wTKj#*R+(i{{GEy=7D_ zsM@0^91U;Lanmb8n)v~#wcyUg@9Ye8V07;hV_IN&iM1i{ZP~ZNf~x?3UF*5=UvV$o=jor}MfL*-`lg?DMQ`ls@nxl0d4Txsd#28(2DgzuBY&3fNn zXJ8x%iapeLxD}iGofDIVAcc%5IJM9eI5OV_n-0E;?HoGg*ZxX- zMB43bbDTj!M=q~?W%XOb2!n@zgOox-LWy>baK0DQFI0fCyFDIez;#1LeC4v6 zTvz8e-|lLRZt?UApALuGVY4?6A7buu*BiwxTvQxnwqNgV8&e56dy4y9uCHS8sekZ* zoz>%~F33w9Smg!dZc4`nb4wqkkV(g3A|-+H+(MB%KNUdKg;>_?*NHmp;jPCm(Vg2T z^2qSN?n}vbrs9K@V)*dK?e22YV;QMN(zvnG#VTv(IVDWM9`bXJYtq?-Ev$BRL@}U} zOgsyWv|b&;9T;zOB_VjV@+At-onHmPD`!_9KeE^k=r$G7i)+qzAU9cf?w3%Yp;?Fw zk0u5@TN8h3-V@mU3R_g(lZwAET1RRwi@*A%4xzT?`r68r6e!H!R`}^tr_D{_Rsk84 zY*J~g-H+MUL5;#`Poo1fU$#>JNENzrat+tYjEHrUf{bbGPU6QXN_!_fzha z7neFXV;R0^;>@gAdZA5U*mb#8IuQ=4*s4d6?2?KJND!YQD=Bb+yZq@XLH$`5ef+L7swdYo zpG=KE#{J@Rq(ZCNaSamLZP0g~*`2^p^wao@0j6MjKscmQM(%gu#@OpjTY1uX>V14Q zVEEKSDnj}C@Z;T?R2s_1za~GqNHshhAaKne8WV;;NEaGkg=Aw40f9jwyno!3U$wkA z`OMo+Ef@|DPX+6Zl?Wc5zdedNBO{|(Qmt^~eqdR}hCei1-6lO)DlAM+ue^3;WTe~0 z-P41H4Yb_$zrHasvg)@9+V`t;6-##SPZrSr^jXfW#mz)iArZE02H)4Q$><#@jXP}M zA;)~!SbmzHapV3lBmE2@7N+A|(a4b)$zBQ-IoAx2xr&m{lg~tu)c#e4VBX2e_o(c->gI+h{!*($wXyL-RBxx zB{=Hx5S^tS^nA2(;mDx5is$5xq=-{96C^~QNp}sb4b+c8){QsnI%DI^WC?!VyDKIp z$dA~@M^KWNzG$;ZcfQpxmguoJ%LsG?8@*adiFO{oM-|A7y#T)YCU2o$>PH{$k(ifU zF*)_%Cbbu&!Qyz$Y*)A29(Obhub)u=(XG;i%)k4>aMSJkevl26lK8q(;-S@<$ zI?WMvp5cm$zUu03GeRv0v-qy8xO7gzd_^jDq3G`R`i!>hS1<1*8G~K%$e2Ouv zOM0_2>xjNd4mA&vVJy&hmr~}D;T6x3b78$DJ93qg7a~QYdT3t%l1&yByHQp^VB_Ag zq~Ln`oPwLNCWX^<-SZ$`|KYb|)$?OBHkqub@8{u9zx<kR~xG8`>(D_o9Nq8@%mcOD>T?^bNy0`blC0d*@c@AUx%n*knXg;NgP86uh{ zZHV3KM2}dX9w%2{Ebrp$Q7$^4@rME>SdEuvu9po2S<3M;+ctZbU44PPs_N}H1`fsT zr_)I8S1_pRP3^O89+GW!FV1`h5NFqLX7*90e&poP%ciqO$DJv|`K*+H-DJ^S$!fN8|EL zyy}aA9!q9NGB#Xln~QQsN$XG=qCJF{I!gme^rUT-LjuO4r+Iu^=C=DQbmK2%`o>0> zUYjjzNm_3P9X*iAc!6}g{CUM~f|9*D*dlYN(YgYLO%G4>FB1-5`6-v0E_$&mGT~GA z6rOLH?s@38d2LlZuW*%6YqX{=TQ<+_J7t*tiugE?@b9`lS*&cvVT$Yk9^B=?q>#0H zB95hApO6ZiyK|M5w!}}thiQ77@Cn(~OGsL6TC6>j4{pG@o=D_XH~zU{^?>ro*A9K> zre{t{s6|?gwcb-Io!b)NdRpVh$^s&VCq!d;)2vzLt}K*v?Mx#7PBN$(La+OU=SdLZlRqP33iB zGD5#lfTwrV7S9xh0?e?WPD@>jowWDwF?*+V2e(gNYbOfQc+tl^nXoq`bvvLK9<3ZSzfx2fI%1CO{8gV=POGHBOa5p!Kly1{oVP zXUrYKlRz`j+&AyCg$*L<^K(8aDk}MteFVJHyMr!@cm*Y&#Y9s>(G*5+Mt$`$8H8eq zqpW1O&9O)c#gW8&Xb=;EXmMn&He4dPyS1f*rgJyD9HrXB5?e!fyRh1EUb)`@W@xHU zpJS+oj*C?0i@8RpE9x|>)o2)?NCkh1nwLvi?B_nQEG(kKw0Q2&E!=eEGODYL2gt?S z06NcFA^jXdGL6e+Nabx~915-Qh?`tqWUUnMFxuzMN7|p*@}qs2MPztiKVH>I9crO` zO|cP}rR#Oqtu(37Y(>87%GJXTYmd4%e_&)bIEg|6zE{OdmbqGn*rS50jxdM^FZRt@k;@Ltn>F()UdhbLj8!Nfv0Dl}Ldc(13G=+_ z1_mA`v!3I`54vtDHD4{5F`tF={D!A(MK}e~r}>RlXXOrKuYaL8W@N?1Xv%u;Ebn>|OGaMz3JMR$+ z8q#9a3Qy;}JiOJJi*6njFrBS_byhg`%=e6!gX;!2H`(s#g@&R(io985NZiAVl@tb{ zro@>@^YfD6IGXRcS|cfTQCgX{Tr-@TcT(eZY1nKNIYD0UaldhDkEdcG-jtRgYrBt) zjj)&M-CmKem2{;(Ikk6j%d_zLl5&=QxZl-y`d%M){QC1CYaF;}=TP&rxex8S9c0EM zNb3MTBsyt!*@*doc&&&_aEw9q7IO1k(ibBFA)tzg0>*H!VUVe`C)PK(I8%{gLps7V z&!0V~`+V7!p_9Ae&AxJ@KKt5zC##LMYJS%EQJN`Gvd{w<>0wVkb7p{gLj6R~e$soh z;Zu4{aA>LVj=&qwtOOD{;d7r$?yc??m2#)=)BP5?A>C8k#Ej9cu%N+%b8v*n3LP8) z0qn|V`M_xUBA?Ii2YoFu>}Nb zEPiZKgrbjgkx~^Gy7EOb4~@?b4h`4Q%RRvEKpR%UH(p>wPTQ51RXIUa4&MjYv90Cv zep_jq{+>@WKI?u#l9An1E6iLcmf3!-cV7Wh%Go7}`gP*Q3d(Pp0k2dm*A5w%Gw9Ev z_jN7`_dW;h26zDHmMg`hb%S29a91d=b<^h)uIN(fdb3MM=WmdTX?rkC5Xt)FTm%IX zutnJ#wf}ER4kLuqPNj&Re9!GXA~6^@>b_2qt2ot)~BVj zBJpv%CvMA@`%Bp_x%c{u1)j0;y#Pj(f)1i=&4b{*w=%lPIREABl5$@O8FT(iboNXA zbO%@dgL2H-enn`{vH%Do?q1SeLjmsUP@-pCy}!}R+55K(Bj5$*f9rnlV-e;pyMWA z?3&c~!YL~qwrHJ5{S}(p$v(mb(?)`c6*$=`=fl#Lo{Nla5ok=e-D))Xp{S}Huujy? zyP)BzyfGb5#qcT}g#UwlXJ>2B=XjNji!~DD@*Vx?m!k+=w&N9fd*OSJ;w-izk0USNb#JVEl}%h=nRU$N$-bTW>px0u<;Fbw zORd^VP#N3hraNPOebNf`2^Q;Bud95>bhRb#F(p-)ikD}}{rM9$7!*BPB@7Omq4T+_ z%aWQt;8%#yIn&+)+gzw}Ui~<`DzrX)Ns9IZma@>wXln~!crN*6FU{`6Y_zDMYmd6 zA==;qZQ6c&dmGl2^shu;t<7w~axxscF91cCr<8@*y`LNaV8Lg3GO(x)`vU?5zAKuK zySz|pfswKF_@~bML=Xxs)>~-+z)nZ3ys)sO z1hw2ug=Bgu(p8sRQ#yQFxeyvc`rcfzN#nZ;LabvMBytN^!OqE*oyl=dc(X;!cs993 zgdBs3F^$>TBrakJ99HiW@0ZVKaz*0L$rqmYQ!Ki}1V^=EN?7U$!#S-;r*G)q zbSNjP*e?fNT@l70nQz+9i*UZd28b4Y zEvEcn)txA@DBHziiboF&IFFbfR$0A6ExPYBY7w(>{k5AaQ+ix_7_{h;7xVa-vLLXab8VTYUl1( zsv4qXzDE(*(bG}U7i25jsH-RP#u{{0l{G}xs{29X7{ciFhbpgVxv=`^XXBNk= z{*!fLUP6q@9+eV3=cYI@wpt@r{m}UHT+cbs1NeKQkSv4+&3p^jBjf-;N1Nk&U4Jdj z%R-u%K5;IC?CQGdv1b(u9OaS3k0y5B)P+1oGD_>~prFQUgExZKhw^V`pY^sTS3T4w zLL(Y*@jId%M4WC}7`8JNyfwEfaW?I>QBOSSufePkSv6bCCZ~RhprmsAq6^$BgoHjO zV(uSD%NLH}#eB}9dCSa3K&`e<9hNpN3R@8`FHBUEacfvEtM!t!BqeFr=tFG2M$Cf; zuvK1s<+H;(mO@`)r!S@;@40Yh8%}*xz4mqqSGi9&ET0byv&r77qS|LoKQa_ZiteV^ z9{K2@>p;$wV^_rA;7(o@5<=5%9ld&5NF(?F{`vANLDSR%2KWhgCoqOM2J^Ur-?r&O zML%AK^=pxwo$UCUi+-`!KC}<*caij20)gQuz1&TPP&`xc&wvPGR9|tEKE5Dt7tt zOz5h8xV9ADYiQ^?M6X;0y~znfgH_oi*3UZUxK-}%m1ke8M@HnIh}G_{Y#&p;yWW^* zVyDC*49?F7vPo;OV0Y72&arWyuDI5<2RJ=nHYeWexprpoj+8c?jVxBkxEa`qn&^B2 z^?_6yBCLu-0mYDIQtXxIb$aohdkOBJN!qM^MKmk{lHskh8mykEG%K^YMnmust)7kQ z*M|#uVnR{aWP6P?oOLE;eT_64M~H6?uofB(#xE)Vg0ckxN+8gH1V`5a=D@X*z3g&oE8B8vR_~s)d;u$qg~n!xB_?a~8s3&%6g=+A zD-_=OZn%^-R$q-`eVx(`_{JqSi;m7*pen?8R7B|7A|FYtDr=bHXhKn% ziOX1UUMO`U!M9$07Fw+P{pRacA$tVrp0|<^Tn6>o8Y43k_H(~Wjjysb7AvW5@%d#z3yQIS!Y%v4;>m&XT^Mkd3$@MIknSA%|)EhzM&FC z?(kI<%1Fz3`-lM_~-r=r4I|Er+$v4A11Y_!Y2cPqN zmN4px6qxlh`B(0mqiN##D#u;Mx$Qc(=YM44^+vyFy;D#+S)Z|f+-<|PXy=@jAQjE|Kz<%c z;*a45D0BLE7Yd*y4+3T!0ex@z4)VLl8*#nzK-$9RF?3|#+xA`n6vOHnLM|kk=+QFG z>B>f*;`%5r%6;17SQ1r^ivb>Knk=){YDo%m!LyEh>VZu8dU;NO{j9 zq$m4F2isQi=1T{@~K9Q zt-WK$^X`f7;G3Y`Pd#`mj!xR`jK{jNeX~NfeUSg$t$(mu+2@KLu#2)epPz{}%yOmN zrbXlExTcw%I4}hiA=#mcUtqY#;jpCTDYV8i;2BpWgPQSX>moR&6c> zxL@s9cgPAc0E;CJVo)Eab7@(y$Z&F>Dl6-zc={o-vj$7!_ToapK$mpDiyp8}bE3d> zCme`@5~Fz`-RbSvmHUb1`@KQ-=H@{fdEAA9yfn`Ie9)b;EstCDn;Y53g)}?e{9bce zx6K|Fs(9=T@q+~|LKds~a5 zgN7-Zfq-72c|N2M;K%7f2mIcztGJqOmWh3(R#Fw3TtA;^?k#62mk30jA@sBvVAql9 z&QGiRr^nqbxmA!jl3m+ej1L}JkR&j8b64iktvS6*;F;-EpR!=5FBmCk(QWJC$?+E% zULgItwBlTY)cKXGew0flTia!Ya`#=2n8oq}F^lSqdjwCN;)dWu&OcKui2g{Rs4$tt9eSXHfK4yG1ySY5+SVUH9y>TUJ) zg-KQ5;LHL_*48(wI*rXA56Mp`r3)-odD%K0jtMzZH-8o;(SWj}gn4;>5=T@)qs>Jm zg~X|7Wxljw^1u+GNjIPg?MIxI$sia0M_IRe!RL84se7f1R{N{GJ;!$JDNE+Zs3_;S zleG&ZfX_A8-YK@*>W&tqaO}d%bQIh-U*TyH)t7Ff!Cp=J3j4$|1aR3WTsxbq;3dD| z{5e6GfZ0We_40vCz4_>L&l=19mdMS>>-mPGTx#p#gBC{J*oc!?(VWZSgG0moflu2k zHoCKDFHRlq=NnS@4EA&(R2waQg%(s_p-NG6vrbN-vA}DHn}K0*OPlh<-BsZpN#axg z6R(eEa!;&c-C%6)!?qXW&@2t-kXxz#lVS{LWL---iAzM%L+gV#> zTF%drp{7;4Z%*RLJqRzNOXZd@Fk1ZWyFctC!Y7t3&{P+rko^e0Bh;VD{Ul`Yt=amq zgn^q|lBT%1L2@f%^hI<_7sE@<#6!NSkKlQ~LOh>E1~&y0iO&x9M%#C@l_oknDjMVE zHby@|Yt$F!sVT>;D(E6kR@}pSZ}vDa**9IlNn)d>c~E@CbCmkei3V8G)2nB^r1gY1 z1gp{4Qc|Hvejf4&(9xOGQiWGvqFK1{7@0v54TDP{;*7lPLR23fG$a0SchLhsn-&K7 zRfv7@y{CroW-^`weZOVBZZ<2PnU&6 zIG~-8&0xew3=z4K16NAO`@T4`!*T@@?S+cmudAx6yZV+C{JL*dpSC|)7tzM_m5e4q zHmcxie*fV^rc%gD8mB2Jl+0$ko%}hF#OOGQ9D9D*O=_z>YBWrzYJ)RY+tmik{f3~8 z;XDwHE$ryIJv6R&pq7{8D8yDjF+ZN3{KydEV+NC`Wl%#a|JXnFB7q2nQXyQCN45Z)B1k>+N5d_ZcyENy4inX9nefuah3!u*6v%!R{4MfZ(b4TFwClO zTIWmY?2_{cQr^2au;QmA@oSpc4A8_bqX|*D`usR{o+Tj1^!~k+wCu9IXgK@O*15ub z$D&&Ruz8ubiPe8ey=KZuuWNS?0=e#^C1t5L54${Wy9!;}G3_bj6OGJkHo=lsmo$qF z%uc=IIXaRVdsy57CFLFIlr=PnE%t6ZNPEnmbH<`4CAu~u0?C#6#)>)XByVDXBG~0F z^G%kyI{IIxQDQaoMdZU`a9W?cKE{^8-#3q>CDMCnEktWb_?%O7|PIDTx}TbntZ z+7-=G;d3RK@o6mn^gu7DpetrGKZ?As+`1^}+x)f`9D{4imx5_S_MGzU6F*oxkr#w} zSFrixylJQ27L*xZHrn~w_AA(k*g@ugDVQLvv@n_8!6Eyatv}pDgg*5;ZfG5iS55u& zd)_tclWxoT5_L|6o-4R-sD&z)?eTt*ipz{PTrN_5Ub|$J!1k#XH#!^bKD3wYL4oh^ z%i*ODD@!Vi$HxO;tWDa=?65cgGjHu@U!X@?;knm8z`Ykt!m_?MH5F6W zQbTgz;CVwYg`IvmrSrUA?)#XugYoNFne5kOq~#HO>dGCG_3Raom(swxyKsF;*yGAd z4=pdw((Y&qBu7B{;gG?UU%seh+((2<^?@>SQqOl&vuHyqCtCfSz&u7rW2_A#pXhHHRJd0E-D6oDz66*}nxwR&t~CWAxijjDc@Fk4L4 z*v)h~>-HNUGIIF2ty1i@#joP)c2@SL-biF;Etsu!sstA-fB+PbXb7yLlh zduj5@xuPabB;GMG7T?=k-|vjG-rJP1(q_M#?xK3dUC|FvkB3qm0pNqwD=JG28-gZC zr$~6$&ONv50QfHc3i0lE^M%$!KMS1~JAP6e>^7VP$P~l+;&LQwQG-LxhOhml-0=ki_|e)V37eEz9#+sR_L#`xAC3kC?Xe zn)|SNMQk4Q7etD`4aPMKTI#_SAt0o-$IlErYd~RkIj4DT+SbYJ>cJMp7f8E^)cw=V zlWlB&|3Y-m8{30}n0_#!Ti5*)`U*SrI0f!YJyJ}9lGB)s+$5nSujow2-4&TDdS~uL& zX;}M4*m+5Di@1d;VW)^*spK2{9>sBo`$A<>#X!9#cll5(XN_VB5q}?dnUHAm%v;yv zQIU(Q(Cx;e9lplh+$4a4jAGz8t$Hbr5Z}kFlK$}2Op`abbncCj`OT5~17CRgBfU1p zPi2H;*`OIt9=7-M^W*AO_wjv&ugrv_%GLAp@r@7d{cPKP1(mUP;Pd(RO@in2TW{F3 zga&ceviF|ReUuFfqUUxn zzp#wFeB!0f2ZpCW*p!#PO~O$Sw?8G^qihw+Stwv#0Rw=EPklcTPuc6p)cz(UM$a`sJY z#bziII47s0qWDYUkL->LV%o7!?|}$1R#w}P4LPyyIo|tb9=A!wb+)RigigPt5EO;z z_rNhl2!!;bSn%#Cl_DCv+gs>y93Bt%%f2-=GymXiJ6809>2_G3JK31Ipq^^xf=m;8mCAuJ4ZNpmHYM_(ex^-DGXx3PVX zD&^IiUm@b?ywmaT*$X|ufNQ?@%WswJCx3Z92|x&$f{|3g$Pkv)f@60Ng?JrlL#2ei z%Tl;l$?Kw5)s^Q)<6NAM;vIp|A4#R{v8IJQqcu%&?tP#%uh*%= z57r5|^6?m_#;kn$uuL((Jae-mG7a=Z1NKipYx>Ue>#Z!a!z2lTG9m9=nal$xP~7kW&Q-G#Y#c@u~**# zqrS^VsgEk>xeb04Yi#E=8ToppXhyd!bF8gD?sGd+@kJbvp7Ql_{BU8kndB`*lW*6H z0et%?3oy$!50iV3MF0V*P6)3MdezUUMbRuY={_6_3DlCBv$aSKu<8}X;(>2t*g(Rk z{8yD7#=nm7ywQEHm|m#vs@nXsHxe+upDqzki3tjv3O3cA>s*ltL`j0{jT`dFt(I8N zf5-QV%j1$ca8S;z@y!e8J$*IOYMBBd?`qy@=FtCy&cdVIH@3ymv+P{U$1~5SePvJ8 zgMHbQ3Zz)L)L8g*4scKC3!^JJPtk|pGe?67^jLZo><-;c7R~`|Op_0y-wEd)A7fAZ zG*?^}lK>tOg|9D_#}s~+9>(foNh_r^XgEp9(F)Hvi%wuPoC4>oWViNLufn_jKz2CI zS&&0%CGd#O$n;e2xHl>FS(8;b3@x+Qo8h?HRle%khI8nGibKzrEev>cFzX#vb#WnwE))>4_gWhK2VYXn}+@@E*FVRnUd;hRxtT(Cr*KAp{l{4KJID z-$Q8avsm|D`lo~lpCBl7bN&Z(+2sCU-RAP#O@2a=gTFv=djbmFxIe%}dAWrG*S`QJ(c>HZrBX_1-#nj zY)ty5ozD{EOgkm?1-YMh8SCo`U$q(Rp!6%qDa$r^DhhR7Jxs%;*`6=PoPKkF1Rvbl z@lL~KE!C(`T$Eh8jquj78!5H;OOWoF@lFK@7s%}94ah@Rqu0B8x2#&1|AE1O9b;$G^40W-p3Io2K@xEPF{j}p?gwAvvPFca4f+}4;U*J37xTEV}p}V z??)z;9H68^gziDxcefBMAWiLKB}~Pr^;Ih`^Bnuw_SdjcMjUTT)e43bM%^3rW8 z@Dc0b`cftT-A%vnQeLYWo3~P9K9rOK@~qR-#p>xg`M+w!9!%*ctlvUD)a89bk!NI` zeTqC>MPQ6(h70Fr!c>aI-^9WTdoN&spVg!D|4KRUXt>(0{|AvG(W69-(Si^mYSfrf zGJ1(_5FvUHF&G5VMjbWLTM)f>Mu_M}mqfJa1fz^z-Xr&O=e>XHeV#v_^;>J!a+W#k z>~mdnzSrK@-urw`E0w*N+S|Emlq)P1JWJ%u7vmPL=SIwRA7?JqAe#{%BIL*pfAVJj z6hV({DTP)%-Rt4jCN=J9emyumyn7JbLR(qD`A~=*vQ2Ye%1!$I`v4b_j zr(rhK0^pF-Vx?4eS?0buu1a!w;N%%$7H(9bXCK#uT2*d2*uhm7`G$`<3iMbxTFA*M zYcX?nFE6cnl^7-wlhay(cqCZwk#RX$h7&O^nLT(Jr0-vhHe^MK7r07PygVZudZMR2 zAer8QSsuGKq8FNX$!({&%0;#P(RjW1N^r z?>z^f>i0PJvEIy7Rbm%QTX7x3X}y7ZRt)U`@R{#0mM%$){mOI50nDok|W zcW=Gj<|5d1mVw&xfy)zv_pum((5Rr*#_XX7c9wlZijQGBml-u=B7l3+lMSfPl0wdVOl9Qc0yqWfxXmAB`UMhUj@=s{HtGR>^gDHn zOLQP|z!~?H9ymV%K7r8{2QCP-nPf>{kCS~9Nc8TZq}cI@jg1W=UQ8EnEs;`11<()m zUS-#)Wx z>+&`mb*G5ury`3Xhv<501@ zPGXc?HM-P#VOiV+fBalff(#a3Otj+ zb95x|Bb7p^6lWI0Hqq|7Yp3txvQr0AhgUNs4p4gp1=0=j-Ou>e-^|{;AqLXL#>Lg! z6Ki_QCs)t0)4-Nwc}E&~9O}EX&b}{AZx6H;wW#}kWY)J_r(>YGq5&2*wI`W>7PqtE zfRmxQr7RoYIW{Jbis%(qA?44+FUZLnQiN!~Eii6rCRc>Oig_&q|zaRX?W=p$O?=9Y(2N|ST<5; z1JEw7S{$@1H3#)*%QC#|p!Tv-WaO~omF$k&lKqIHE3hKj7la^zQp4TAMy#V*V)Op3oHb8vkIb`T@m}4Zi7H^|j?#h@mr`1Vo*a+%QotJV z)BaS+i4tS~h>O_x<_ls=I&OLN^ou;&6pWtBgFERN=7nsh6U>c(up`IyptJ3_3Ha%_ zWA74Ek>LH7{j>FFQd7+LRUc}<^~G&2+eb6PP#Ngs>>RnVusRPME}(Va=XOX^#UM==wr z3A{TjN6xOIgZS_Z2<}Xd9G^wV?7KQdE2qj4~6Jn}K zn-CRS4d}0EL+UQ}9M0!RAkrm0pH(?}RC`j*!_J7OixoB1ne_%g6>a$NEFp$pSs z75JqzXVRv88|Yp@kVu(>*wxL#LWVlFpf)~=Q~@I^qrm-xBYy zedE-Lwg&7?<*zan!Ok=AcC9LDUnaiW#jo);PkxFyuj*TdBLz{nJRn;fq3NIzhL`(!X65lMnlqayZ9JOZY4Zbs<)0XQ+SoU%u3&O+``0& zw$NR5F|(}OQ!_Lf4t^I}e477DV4wsO5bksEaoKI^um)5V*or#JI!T-K+&y}oRHDwJ z`N`va#=WW|Rso!0*g!~s6v6*WR~tSzJCP zv;DD^+zZQKIyj+V%x&-ZXvX^qtj$KtxDs5bC}R=86?E-J7!=$18BVf3|B~d|&{Q!K zO2xAZohsp;xcpoY=k4~c^G28>gV*;KYh!oK;!5qF5DEhN-3f(Arl^W02b_IYst}0S zEWK@qe1E06&cc%gU5G)^sXxVge?23-T+OcU@1?S-D^24P2GHG^~zCkp%_BzeH)r zcjYn=G(-7IPg{}#ku9}^hIALAm$*KpE-TmG;dYsrDiwXhk&c*Zs`D);BSwjiyR~iH zfG0KPp|)NZ_-SrNtMrtah&F?0I`yM-)usE;s_OgzN-p6oKvG1HZo^NOy_MV&F)X26 ziEn4^ld9}5@9XwNaT!IB?ak=>y;ScL)A!wAb8nV8YsNy1UJnbP-o>m5Oam|4WtLTI zY^x>J)kg>~Tqh-$Pz>!Im*%H&`>cu91}eBpHyz~R^pW%uq&kZQ54gH)X1#Y4reg{U z8hd)#AClkOlV2Dum0Kd?YjkRk^$&Vs;b4#UcuTi$<1=hhM7{Gd=80rp)w`1Ux1sxm zNhMpNQyEs%SioJ6jBUioRVE49mgZTeZtdp2_x?WNe&-e?`TeJAenRMmT;l@gHJ3Y* zy2l~SBsj8JQNf&oT>O2v$L5(9`jVA*nQeXx+&f6M=x$O0xkK0D8gy&Xh2r4oDu`#M zWpiWXxN4(TYH5+LS?|7a18!E4D2pych$||h^LqB&8ndEf-Xu4ba(@ri?Sj*4P0@>aerXr7}5eB>c)Zt{kNfO4p(311U&SX?4jYbop_ScA!Us3p* zr&}7RE-nurwZt5kK$=9wi0~2{Hi%{U841Gu1uk!}0g~NjYsn`KnjJ_>XRbN(9+YmO zv}<}ikKXAIjSleU_$F~Rp4X6l#U=lhgukMktt6$2&&mRnKeJ)IbQUgVnz_t>c zr^paeI&embqQVZO4N_W27n=DBRg(Dz2V){QPMc=dlm)G9EcDLUaNxN$1ColI07_ef z-BtL*4?GGHl(xV&BC4f!F5=Odd~w@^*YU0(sqLfr_>g;osNTNZ%IV^X{rGI3v#E2g z@((&4!#+pDTA6N17?JkAfu+kJlR&yk#)==STd<}hK~1L9hu%R4O+U82l|awH6D|DW z1cqo?(^tdA4qY~N0y*Wt|BIX(lfMrpl=u~)&`xBjS zaAE15i>O2Pe6rY4!>()NUyAmn{eKj_(HSa8O!WzL^97oG3efgv-mk52r<{z|m^;M4 z1{;A;0AlpR25inOVMrj6Y5i{04fkoOf0W4$7Ok>v_S zjtqxNs-_92zGQo=Cv_r-(>C(JL}pupwRaOseAwPE^vwd@IMoUlFi4oMGLy$9qnW2VF^2vT1^@jMsfY@VbF21l?*S!XcOlZZIsw>Y)q5J4!zrp z`L0I#k&4Aio|5WkKz#w0q!^+_dc9Vs&xGv|d&^Abz>0DOFBA&_cKyybXbQ=0uhCcozYKW%+#Dp8Z94bIzI{M35*_DNs^ z5Ro?-9|x4z@hprC_vz}ES`vb|l+y!>a9Y%HHsXG@*0hIyaZE+1lSB*Zlx&`i!YCxay)D&F>Lfy}+bpbl zvNMzO9jspcPGDshpGT`tKff=n5xJI+^WlUD?BEVPUo07BZVGkOEyN##70`i4<~n{m zWA>X}?54a#Mx>y7mPt!=uYOo{M64aGe=Q>@?~$csL7r|I8Cf3LO3mO9U@#w4Q8nk{rxRH?q%ePL0P zKEh_W*^gu%u2MeFZGXhI>2yOFZR;yN~G05l4}P0QO_s+q4oiIQ5UaK6i86h6hENB zcckO~5;jyqg#CF_4oHE`3f=J)A*S-4Ont@&yaI6XrSk|;%lkm2#)ELNm_XzM)|>hG zbliTe)Y~aQSkGwvm6&RDbTlhX_D!8uZa(1#cDAiNx2-q4PcVMqQo)Y?iIwzue5}4x zt_R;0gAq;F7i}1JM%Za-$A@6=g}zm%ys2H}GERsdVy;!M&KnUIS`wX|>jOtCuIWni z36eAPE%Hz^dP=`1r0HuVwzuO|JT$kQ~g|F=jH~<<;@)d`B^MHQt#fh0$RGwJFVt z_^T4`nqB(z*eOd1{~}8gE(xk&quV^2`G&e_uG&fKGs8aH1FS_e^+}1V^8;Az`9+)0 z{x}2~%b@Agx&7DY2KyDO@1RHzGOo9E*~3t7i-fnA?bX)KGR9be8k5oV%g1~fdD^V{ z+Z@*DOBhLs!zN4O++hHa;M^>)J#&NsYcKogC7E}g5HQ-xR*f=88#A7Mk2J%baamHG zD{g%`3^HuhIy_oa>&mc%!9zRWTPFLKm^AIo1BEu%wBzc|!sN#&Y^Us#pIBhEJQi-c zrxg6F5kBcd+0@mmGJO#3p-29yY8Mec{}}L>nLaHpHv+AI4*oTd)AbS!(xdy!&Dojj9)c|&5VZ^N%tn}p2q$*G)#8PCm#k)lZ+VJuGP;wj>MzeGQvQd4p(VVw{5eV%{V zQ*-(<941tabtUVRt&wufAjR4G#7-4qP`3rSvk0HdEaAy9fW*nWkV{M(`?;y^;MJxx zhjr=2_d;MYjjf5GSN6yPT`z;~aGcbS>Ql*&SLZU5#?M`4#;I%r9+=3W%Ui${tj_e9 z(VW*!kxP8dV7>=k66$V_o(j7|D}oSnhTuK9`Fpi&iB4C)u)_H=(JL#1)7Li$eul3=lltYEwzB{o6y(zBDlv>Y3zs6d<+mh(2nUKq4d6 zhO&FIq;BB$X!MaUgH8HEsp8V02*ku~vJaGo*w_c%Hz;5Vk~4&nds+JSFHk&(yHx7v z8m3g)g|jtn4-kOP*N)YYClii{r)(h6@4ap_p8Ew>b1~KO0WG4o{vYpLwS7p@vN0eQ zcu2K+sMfC>JtWC1o{KI~S~_`j)yQhpyn=nPfKKEa*LjQP#)bw|axA+=wW81Mt=UKL z5g7F@^q^edYr;&cSV=2RA#n>kQ84OaR-%=PCG(9Gfr^{Zwyh6F#_g4Ee_8FG6cf*V zY2o^dF5E)t*H9-j!j;1SbhogPb)l|?X*_{s>RQ=nFQUjtdmW+IKrAP?yQb)zFIt*Z zuL@11tewlU+vNP_AW!7$jm5=ZtzTEZ-gTcl0h;o#=n0#|!GSon#I9hU&Gw96Fj1kt z?=_^!j!qFZrPF~@Nctt3f=W7>Tnzd#vRD5jIkgb8B&&y~zkG6G?HM$abhk2O#AMPE z+M`Ljm99TG=TCE%Hh*8p7PjkZ+psZ(wN+9$N3U+vL(=XhNpcK1CLPuzOF9K5rpd$t zXj(pMK?#-2e2XC#;d*k?co-*@K0dBU5B!DeZ8hlAr|Gz$g2qvGlujY0iKRwA>(IuGAui<)n2hBtW_3o zE@yv4?D&H~oUc20D;RKnH&f|_8}BNKyeqk3UUXZT{G;RGS!T9*NUUw~u%dcW6Lhys zUMiirE%Eu-PRpUHc8K?7j9TGJpwP`oh9VR+t->O%6HKX5<`^We-Ft7iN($dg&6l;- z<6QWo+tiN_pbk%aBC(KJVq&U0vk>!(%#6t;h$!%YV=r}tG623~*0yN0J1LNeCu3k0 zD5C^86YGKr4TuSN>9}Rs8Yu`6%ClJ+=5|2xhn5~Kv!9azQa8`wRl5icuNcx3Q!R1O zyq0Tk_B!tOvS9wG7mD9bM@NTs1TSEi2gUS5RlosRx{^d!VaKFmkr}eMuQa`SY)3x} zHfh}R4S0>P7Bks2#$IVBhWyXUI`^5*H$i3GZV1i9>(BZ)DP8F-(LrG}WqE1ZJ`^ z&G1prkp>Q*g3npk-GaX^rQ?4p)WJPCwV@z!Kn4mGBIn;HDWK$J|L|G7{6UIO9()e+ z;R~3~EP@5zCcx}4r4sPnhz2c zJcHz`)74Fi1B0J2_`iZtV64J|a@5AFDl)^5PY!pyjFIzKo8*Do0-f{qiwfZ9uWZCr z&*qlw9UI>as?Ub2zZjNlrzg01^Nn?$sM{lb=H7?e$w^vynxBBYg*rK76VooiMZvyH zm+zvS5FnfeR2jB!Rtwvn%gRAY**dD3fmFsxE3mO?a4T6NKOI4ajYBvePu--M2wV~p1MIe*w&1kw8QGE-|@WNjOj?KlM|02K06YmD2Sgs-c)_I zT4jKkh;WFOYfqcv-cBrV{FK3k0lk6zpi@nY(spa?7g8RHbp2{4W=WD6k}i|ouCKG~ z%{l6Vni&X267D2GJu2byMN9O2j7;goo6YxvT0%DwZF2(|lF?@?YLhG!q_yi4)BBY`y zYri2nRvY{}@(f-R8n+*%UnPx(SEq0CqbS&QXpmE*`6p%aTFF~*)f92L?X12z|6@)A zwvF+1OB`Z%d~B!TmoJp<4IyQdNo*v&jLRrl3^E<1mHWsK8on z_{OK}D|dsu308u8Wx!tw^>47R#dyHl_}`Y~%77>+e~@wEc^`Pl$_L7uq#I>hAR6WG zu$Z%Ib`bA7MWhW>*-d%vvy0u@Si7NeIj-ay2(}dDB>pAzfV*92eEGPK6X$lx0d4{^ zXRy`2GeorS^*B@h4d+$b_cza$6Vx3monfV>cmrYFzmwXJ>p-zLf$EaRX3ZNx>%42U z-|`+Q$I>im+!sI!a1w29d9&=*C_Nwp0s)x$Gajo2pq%^_e)Jt!7GZDkdvY^C{3gEf zzn8w*i=_{Kc$CkG0WxK=+CU`jxtF&zs@n$d+5EMe91|at< ztlU3PCo>h{KU#ru95u_6Y1UB?xA1A`XK&hePI+b^{*1c(E=IP+Y|*b(^IBve?_gB_ zh~f>y{Pz~Y;^LzMA&NwB&8+96hRKWYu1Mh~uAo2#F2S82XgT-~OP&0n7mfWQW0A*G z$0eQYEtGUTjZ3Gaxe^1q=NqI<9+h0QWXbuDK)HdCJCu|${qFe?$S^yjOU5(yA@1+r z7Y?o)H!--_&-J-Y;xVE^W*Cc7nUWnVp|pIeNx^OVQ^RM^yTpzCLDD+tCtG`)CJ9jA zrB{Ks9m7Wb>DCeWlG2TOSv~+UE1H{sL&ggWURqL%XIp0I@Fjcxkn#<|^Erk3r#DQJ z%i_BW^+V@(oqRkSs89G*-zgG<)$crZoAskAw=FCx`nm2QmvYfbqGMjBf{?ssJCvRC zT+zE8Rm|dr`DEa1oKl|yQOGSW3l7eXwq7WQRic;f87)sAgMW7iz^BTew!PKazh_@D zsBXqCP_GIkD-eEtA2L9C&Y#v5K@<1QL+4sdIVT2GdJdroM^MmC>#>W}RA2S03_;wWEUNTv`w6KkMig zNA5vq4gr`R_}!(OGzasDScY~_ z6%UUb?c~%P0nQqk-Ciey;dXSvNsneIwe@^IY!ROGyJ8!_*(oD*(blx6p9=I%Fg+VJ zUvGCW9Dg)*+(Ebb!V6FZBT)27(0Q9-9lnPAV*l>t!x~(zm>rZq+Uj!$&v_5jciY}&3H~z}dy3GtgJhx{1v!egT5nfg^ z?TTjiUT1g(2Ky+g;>(uHG zWOOD)23V*js_l2YUoHNHidCk?hf3A-&l$WvJ0c`a={^7eoMEJHO#cLA0&i;K{woWH zH4eCcT3DL&is`8ObuhZaBp$cL((?K?^M=Gf5t%@`c5%F4Rsra>o27wK|IHAk0T`u! zF+?%Qzk!`Ck9HrOW8%;-*oO$gQ zeD%+CFadV4EwTZ>AH9|*K<^0;YI(#uFS!5pto|RW>19=55o~$yM0zpz2Vim_2_T#Z z1f5@BaHg{Vje&jRhz~dz%g+=S7srNh$jCwL9s&u1vO!geKZs!R&t)xPKmi zz<`}=zKdr6{O6xz1SJr)h_J?AEKL9B6HEpS8FV=@=-*D!BNuso(zq?8RD6 diff --git a/docs/tutorial/tutorialFigs/velProf.png b/docs/tutorial/tutorialFigs/velProf.png deleted file mode 100644 index 5c3fae52c10a1220ce8b5a778ff3d1dbee9b4348..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5312 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 M8=4uE>N|)37vnd3!2kdN From 26ca03843be73c4058e7f53e95e22028a84168df Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 24 Jan 2023 16:06:12 +0000 Subject: [PATCH 237/244] Remove old input.ref file as misleading --- src/input.ref | 610 -------------------------------------------------- 1 file changed, 610 deletions(-) delete mode 100644 src/input.ref diff --git a/src/input.ref b/src/input.ref deleted file mode 100644 index a9a2f4118..000000000 --- a/src/input.ref +++ /dev/null @@ -1,610 +0,0 @@ -############################################################################## -# -# Ludwig input file -# Example. -# Please see https://ludwig.epcc.ed.ac.uk for details. -# -# Lines introduced with # and blank lines are ignored. -# -# The file is made up of a series of (case-sensitive) keyword value -# pairs which should be separated by a space: -# -# keyword value -# -# Components of vector values are separated by an underscore, e.g., -# force 0.01_0.00_0.00 -# -# If a given keyword does not appear, or is commented out, a default -# value will be used. -# -############################################################################## - -############################################################################## -# -# Run duration -# -# N_start If N_start > 0, this is a restart from previous output -# -# N_cycles number of lattice Boltzmann time steps to run -# (if it's a restart, this is still the number of steps -# to run, not the final step) -# -############################################################################### - -N_start 0 -N_cycles 100001 - -############################################################################## -# -# System and MPI -# -# size NX_NY_NZ is the size of the system in lattice units -# grid PX_PY_PZ is the processor decomposition -# If PX*PY*PZ is not equal to the number of processors, -# MPI will choose a default (may be implementation-dependent). -# -# periodicity Periodicity of MPI Cartesian communicator -# -############################################################################## - -size 64_2_2 -grid 4_1_1 -periodicity 0_1_1 - -############################################################################## -# -# Fluid parameters -# -# fluid_rho0 mean density (default 1.0) -# viscosity shear viscosity [default is 1/6, ie., relaxation time 1] -# viscosity_bulk bulk viscosity [default = shear viscosity] -# -# isothermal_fluctuations [on|off] Default is off. -# temperature isothermal fluctuation 'temperature' & -# beta = k_B T in electrokinetics -# -# ghost_modes [on|off] Default is on. -# force FX_FY_FZ Uniform body force on fluid (default zero) -# fpulse_amplitude Amplitude of time-dependent force -# fpulse_frequency Frequency of time-dependent force -# -# distribution_initialisation Default is fluid uniformly at rest -# 3d_uniform_u uniform velocity -# 2d_kelvin_helmholtz -# 2d_shear_wave -# -############################################################################## - -fluid_rho0 1.0 - -viscosity 0.1 -viscosity_bulk 0.1 - -isothermal_fluctuations off -temperature 3.33333333333333333e-5 - -# ghost_modes off -# force 0.00_0.0_0.0 -# fpulse_amplitude 0.0_0.0_0.00005 -# fpulse_frequency 0.00004 - -distribution_initialisation 1d_poiseuille -distribution_uniform_u 0.0_0.0_0.00 -distribution_poiseuille_umax 0.0_0.0_0.01 - -############################################################################## -# -# Free energy parameters -# -# free_energy none single fluid only [the default] -# -# Otherwise -# -# free_energy symmetric -# brazovskii -# surfactant -# polar_active -# lc_blue_phase -# lc_droplet -# fe_electro -# fe_electro_symmetric -# -# symmetric_lb symmetric with 2 distributions -# -# fd_advection_scheme_order 1-7 -# sets order of finite difference -# advection scheme -# -# fd_gradient_calcualtion Scheme to use for gradient calculations -# 2d_5pt_fluid -# 3d_7pt_fluid -# 3d_7pt_solid (free_energy lc_blue_phase only) -# 3d_27pt_fluid -# 3d_27pt_solid -# -# fd_phi_fluctuations [0/1] Cahn Hilliard fluctuations -# fd_force_divergence [0/1] method for force calculation in -# electrokinetic module -# 0 = gradient of chemical potential -# 1 = divergence of Maxwell stress tensor -# -# Note: only parameters for the currently selected free energy at -# run time are meaningful; you don't have to comment out all the others. -# -############################################################################### - -free_energy fe_electro - -fd_advection_scheme_order 3 -fd_gradient_calculation 3d_7pt_fluid -fd_phi_fluctuations 0 - -fd_force_divergence 1 - -############################################################################### -# -# Symmetric / Brazovskii -# -# A symmetric bulk parameter (A < 0 for binary fluid) -# B symmetric bulk parameter (B = -A for binary fluid) -# kappa surface 'penalty' parameter (kappa > 0 for binary fluid) -# C additional Brazovskki parameter (C = 0 for binary fluid) -# -# mobility Order parameter mobility M -# -# phi_initialisation spinodal [default] -# block -# patches -# block_X -# layer_X -# bath -# drop -# emulsion -# -# noise magnitude of initial order parameter noise [default 0.05] -# phi0 mean order parameter -# -############################################################################### - -A -0.0625 -B 0.0625 -K 0.04 -C 0.0 - -mobility 0.15 - -noise 0.05 -phi0 0.0 -phi_initialisation spinodal - -############################################################################### -# -# Surfactant free energy; set -# -# In addition to A, B, K, of the symmrtric binary fluid, there are the -# extra parameters -# -# surf_kT bulk surfactant parameter -# surf_epsilon surface term -# surf_beta non-linear term -# surf_W entropy term -# -# surf_mobility_phi fluid order parameter mobility -# surf_mobility_psi surfactant order parameter mobility -# surf_psi_b initial uniform (background) concentration -# -############################################################################### - -surf_A -0.0208333 -surf_B 0.0208333 -surf_kappa 0.12 - -surf_kT 0.00056587 -surf_epsilon 0.03 -surf_beta 0.0 -surf_W 0.0 - -surf_mobility_phi 0.15 -surf_mobility_psi 2.0 -surf_psi_b 0.01 - -############################################################################### -# -# Blue Phase free energy -# -# lc_a0 -# lc_gamma -# lc_q0 -# lc_kappa0 -# lc_kappa1 -# lc_xi -# lc_active_zeta -# -# lc_q_initialisation nematic, twist, dtc -# cholesteric: cholesteric_x,y,z -# cholesteric finger 1st kind (CF1): cf1_x,y,z -# CF1 with random fluctuation: cf1_fluc_x,y,z -# cubic: o8m (BPI), o2 (BPII), o5 -# hexagonal BPs: h2d, h3da, h3db -# amorphous BP: bp3 -# active_nematic -# random -# -# lc_q_init_amplitude scalar order parameter amplitude for initialisation -# -# Typically BPI o8m amplitude -0.2 -# BPII o2 amplitude +0.3 -# BPIII bp3 amplitude +0.05 -# simple cholesteric twist (z-axis) amplitude +1/3 -# -# lc_anchoring_method [none|one|two] (default is none) one: Juho's method -# two: explicit surface free energy -# lc_anchoring [normal|planar|fixed] anchoring boundary conditions -# for solid (if present; defualt is normal) -# -# lc_anchoring_strength the parameter w1 and w2 in the surface free energy -# lc_anchoring_strength_2 [Default is zero = 'free' anchoring] -# -# lc_init_redshift Initial value of redshift. -# -# -# lc_redshift_update [0|1] Allow dynamic cubic redshift adjustment -# (default is none). -# -# lc_init_nematic Initial director vector [default 1.0_0.0_0.0] when -# nematic initialisation used (doesn't need to be -# unit vector) -# -# lc_init_bp3 Specifications of BP3: No. of DTCs, radius of DTCs, -# environment (0=isotropic, 1=cholesteric) [default 2_3_1] -# -# lc_dielectric_anisotropy This value should probably remain unchanged. -# -# electric_e0 External magnetic field appearing in free energy. -# -############################################################################### - -lc_a0 0.014384711 -lc_gamma 3.1764706 -lc_q0 1.1107207 -lc_kappa0 0.01 -lc_kappa1 0.01 -lc_xi 0.7 - -lc_Gamma 0.3 -lc_noise 0 -lc_active_zeta 0.0 - -lc_q_initialisation o8m -lc_q_init_amplitude -0.2 -lc_init_redshift 0.83 -lc_redshift_update 0 -lc_init_nematic 1.0_0.0_0.0 -lc_dielectric_anisotropy 41.4 - -electric_e0 0.0_1.0e-6_0.0 -electric_e0_frequency 0 - -lc_anchoring_method none -lc_wall_anchoring fixed -lc_coll_anchoring normal -lc_anchoring_strength 0.0 -lc_anchoring_strength_2 0.0 -lc_anchoring_strength_colloid 0.0 -lc_anchoring_strength_wall 0.0 - -############################################################################### -# -# liquid crystal droplet -# -############################################################################### - -lc_droplet_gamma 2.586 -lc_droplet_delta 0.25 -lc_droplet_W -0.03 - -############################################################################### -# -# polar active gel -# -############################################################################### - -polar_active_a -0.1 -polar_active_b +0.1 -polar_active_k 0.01 -polar_active_klc 0.02 -polar_active_zeta 0.0 -polar_active_lambda 0.0 - -leslie_ericksen_gamma 0.3 -leslie_ericksen_swim 0.0 - -############################################################################### -# -# Colloid parameters -# -# colloid_init: no_colloids [default] -# from_file see also "colloid_file_stub" -# input_one specify exactly one -# input_two specify two -# input_three specify three -# -# input_random random selection with additional keys: -# colloid_random_no number -# colloid_random_dh 'grace' spacing -# -# For each type specify at least two radii and a position, e.g., -# -# colloid_one_a0 2.3 -# colloid_one_ah 2.4 -# colloid_one_r 12.0_12.0_12.0 -# colloid_one_isfixedr 1 -# colloid_one_isfixedvxyz 1_0_1 -# -# colloid_rho0 density of all colloids (default 1.0 as for fluid) -# -# Other quantities default to zero. For random initialisation position -# is ignored. Full list matches the structure in colloid.h, including -# -# colloid_one_type: inactive bbl [default] -# active Include active terms in BBL -# janus two-sided wetting in binary fluid -# subgrid No bbl ("unresolved particles") -# -############################################################################### - -colloid_init none -colloid_type inactive -colloid_rho0 1.0 - -colloid_random_no 5 -colloid_random_dh 0.4 - -colloid_random_type default -colloid_random_a0 2.3 -colloid_random_ah 2.3 -colloid_random_v 0.0_0.0_0.0 -colloid_random_s 1.0_0.0_0.0 -colloid_random_m 1.0_0.0_0.0 -colloid_random_c 0.0 -colloid_random_h 0.0 - -# Constant body force on all colloids ("gravity") [default is zero] -# Uniform magnetic field [default is zero] - -colloid_gravity 0.0_0.0_0.0 -magnetic_b0 0.0_0.0_0.0 - -# Colloid-colloid lubrication corrections - -lubrication_on 0 -lubrication_normal_cutoff 0.3 -lubrication_tangential_cutoff 0.0 - -############################################################################### -# -# Colloid-colloid soft-sphere potential parameters -# The soft sphere is always needed -# -############################################################################### - -soft_sphere_on 1 -soft_sphere_epsilon 0.0004 -soft_sphere_sigma 0.1 -soft_sphere_nu 1.0 -soft_sphere_cutoff 0.25 - -# Lennard Jones -lennard_jones_on 0 -lj_sigma 2.3 -lj_cutoff 4.6 -lj_epsilon 0.0003 - -############################################################################### -# -# Walls / boundaries -# -# boundary_walls X_Y_Z [0 for no wall; 1 for wall] -# Must be consistent with periodicity above -# boundary_speed_top For use with built-in walls -# boundary_speed_bottom For use with built-in walls -# boundary_shear_init Initialise shear flow (z direction only). -# -# boundary_lubrication_rcnormal Normal lubrication correction cut off -# -# porous_media_file filestub If present, the file filestub.001-001 -# should contain porous media data -# porous_media_format [ASCII|BINARY] file format [default BINARY] -# porous_media_type [status_only|status_with_h|status_with_sigma] -# determines type of porous media data to be -# supplied -# -############################################################################### - -boundary_walls 1_0_0 -boundary_speed_bottom 0.0 -boundary_speed_top 0.0 -boundary_shear_init 0 -boundary_lubrication_rcnormal 0.0 - -#porous_media_format BINARY -#porous_media_file capillary_8_8_32.dat -#porous_media_type status_only -#porous_media_io_grid 1_1_1 - -############################################################################### -# -# Output frequency and type -# -# freq_statistics N Output diagnostics every N steps -# freq_output N Output field state every N steps -# freq_config N Output full configuration (for restart) every -# N steps (can be large!) -# freq_phi N phi data output frequency -# freq_vel N velocity data output frequency -# freq_fed N free energy density output frequency -# freq_shear_measurement stress profile accumulator -# freq_shear_output stress profile output -# config_at_end [yes|no] write full configuration at end of run -# [default is yes] -# -# io_grid NX_NY_NZ Cartesian processor I/O grid. Default is 1_1_1 -# -# phi_format Override default format for particular quantities -# etc... (both input and output) -# -# distribution_io_grid decomposition for parallel input/output -# distribution_io_input_format BINARY or BINARY_SERIAL for single serial -# input files. Output always parallel. -# -# density -# rho_io_wanted "Yes" if density output wanted [no] -# rho_io_freq Every so many steps -# rho_io_format ASCII or BINARY [BINARY] -# rho_io_grid 1_1_1 -# -############################################################################### - -freq_statistics 500 -freq_measure 200000 - -freq_config 5000000 -freq_phi 100000 -freq_psi 100000 -freq_vel 100000 -freq_fed 100000 -freq_shear_measurement 1000000 -freq_shear_output 1000000 -config_at_end no - -default_io_grid 1_1_1 - -distribution_io_grid 1_1_1 -distribution_io_input_format BINARY - -phi_io_grid 1_1_1 -phi_format BINARY -psi_format BINARY -vel_format BINARY -fed_format ASCII - -############################################################################## -# -# colloid i/o -# -# colloid_io_freq frequency of output (time steps) -# colloid_io_grid io grid for colloids -# colloid_io_format_input ASCII ASCII_SERIAL BINARY BINARY_SERIAL -# colloid_io_format_output ASCII BINARY -# colloid_file_stub a string used as stub of file names -# -# Note that the output is always parallel. A SERIAL input file must -# be a single serial file. -# -############################################################################## - -colloid_io_freq 1000 -colloid_io_grid 1_1_1 -colloid_io_format_input BINARY -colloid_io_format_output BINARY - -############################################################################### -# -# Lees-Edwards planes -# -# These parameters set up a number of equally spaced planes -# with constant velocity. -# -# N_LE_plane the number of planes -# LE_plane_vel the y-component of the plane velocity -# LE_init_profile set the initial velocity profile to be consistent with -# desired shear rate. (Only valid at t = 0). [0|1] -# -# LE_oscillation_period -# Integer > 1, switches on u = u_0 cos(2\pi t/T) where -# u_0 is maximum plane velocity set via LE_plane_vel, -# and T is the oscillation period -# -# LE_time_offset Integer offset to allow equilibration etc. CARE! -# This must be <= N_start and fixed for given run. -# Default is 0, ie., no offset. -# -############################################################################### - -#N_LE_plane 2 -#LE_plane_vel 0.008 -#LE_init_profile 1 -#LE_oscillation_period 0 -#LE_time_offset 0 - -############################################################################### -# -# Electrokinetics ALWAYS 2 SPECIES FOR NOW -# -# electrokinetics_z0 valency species 0 default +1 -# electrokinetics_z1 valency species 1 default -1 -# electrokinetics_d0 diffusivity 0 default 0.0 -# electrokinetics_d1 diffusivity 1 default 0.0 -# electrokinetics_eunit unit charge default +1.0 -# electrokinetics_epsilon permeativity (ref) default 0.0 -# -# electrokinetics_init gouy_chapman -# liquid_junction -# uniform -# point_charges -# -# electrokinetics_init_rho_el electrolyte concentration -# electrokinetics_init_sigma surface charge density -# -# Also important: -# temperature sets Boltzmann factor beta -# -# electrokinetics_rel_tol relative tolerance in Poisson solver -# electrokinetics_abs_tol absolute tolerance in Poisson solver -# electrokinetics_maxits maximal number of iteration steps -# electrokinetics_diffacc diffusive accuracy in Nernst-Planck equation -# This parameter controls the adaptation of the -# number of multisteps: 0 < diffacc. -# A value = 0 deactivates this feature. -# electrokinetics_multisteps number of fractional LB timesteps in NPE -# -# fe_electrosymmetric has a number of additional coupling parameters -# for the binary problem: -# -# electrosymmetric_epsilon2 additional permeativity to set contrast -# electrosymmetric_delta_mu0 solvation free energy diff species 0 -# electrosymmetric_delta_mu1 solvation free energy diff species 1 -# -############################################################################### - -electrokinetics_z0 +1 -electrokinetics_z1 -1 -electrokinetics_d0 0.01 -electrokinetics_d1 0.01 -electrokinetics_eunit 1.0 -electrokinetics_epsilon 100 -electrokinetics_init gouy_chapman -electrokinetics_init_rho_el 0.0 -electrokinetics_init_sigma 0.0125 - -electrokinetics_rel_tol 0.0002 -electrokinetics_abs_tol 0.0000000003 -electrokinetics_maxits 1000 -electrokinetics_diffacc 0 - -electrokinetics_multisteps 1 -electrokinetics_skipsteps 1 - -electrosymmetric_epsilon2 100.0 -electrosymmetric_delta_mu0 1.0 -electrosymmetric_delta_mu1 2.0 - -############################################################################### -# -# Miscellaneous -# -# random_seed +ve integer is the random number generator seed -# -############################################################################### - -random_seed 8361235 From 1ccf0681ddc4c2878cb7fcdc4817ee58c8201015 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 25 Jan 2023 11:15:22 +0000 Subject: [PATCH 238/244] Add missing memcpys --- src/ludwig.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ludwig.c b/src/ludwig.c index 7c1b4d22d..12dd4abe9 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -7,7 +7,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2011-2022 The University of Edinburgh + * (c) 2011-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -904,6 +904,7 @@ void ludwig_run(const char * inputfile) { if (is_config_step()) { io_event_t event = {0}; pe_info(ludwig->pe, "Writing distribution output at step %d!\n", step); + lb_memcpy(ludwig->lb, tdpMemcpyDeviceToHost); lb_io_write(ludwig->lb, step, &event); } @@ -923,18 +924,21 @@ void ludwig_run(const char * inputfile) { if (ludwig->phi) { io_event_t event = {0}; pe_info(ludwig->pe, "Writing phi file at step %d!\n", step); + field_memcpy(ludwig->phi, tdpMemcpyDeviceToHost); field_io_write(ludwig->phi, step, &event); } if (ludwig->p) { io_event_t event = {0}; pe_info(ludwig->pe, "Writing p file at step %d!\n", step); + field_memcpy(ludwig->p, tdpMemcpyDeviceToHost); field_io_write(ludwig->p, step, &event); } if (ludwig->q) { io_event_t event = {0}; pe_info(ludwig->pe, "Writing q file at step %d!\n", step); + field_memcpy(ludwig->q, tdpMemcpyDeviceToHost); io_replace_values(ludwig->q, ludwig->map, MAP_COLLOID, 0.00001); field_io_write(ludwig->q, step, &event); } @@ -968,9 +972,10 @@ void ludwig_run(const char * inputfile) { stats_rheology_stress_profile_zero(ludwig->stat_rheo); } - if (is_vel_output_step() || is_config_step()) { + if (ludwig->hydro && (is_vel_output_step() || is_config_step())) { io_event_t event = {0}; pe_info(ludwig->pe, "Writing rho/velocity output at step %d!\n", step); + hydro_memcpy(ludwig->hydro, tdpMemcpyDeviceToHost); hydro_io_write(ludwig->hydro, step, &event); } From ca94cf573b1999fe789e6f8a02fd927b06c843c5 Mon Sep 17 00:00:00 2001 From: not populated Date: Wed, 25 Jan 2023 11:57:07 +0000 Subject: [PATCH 239/244] Remove meaningless assignment --- src/blue_phase_rt.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/blue_phase_rt.c b/src/blue_phase_rt.c index 9b1c31ad6..488e9f635 100644 --- a/src/blue_phase_rt.c +++ b/src/blue_phase_rt.c @@ -341,8 +341,6 @@ __host__ int blue_phase_init_rt(pe_t * pe, rt_t *rt, if (strcmp(type_wall, "fixed") == 0) w2_wall = 0.0; } - w1_wall = w1_wall; - w2_wall = w2_wall; fe_lc_amplitude_compute(&fe_param, &0); pe_info(pe, "Anchoring type (walls): = %14s\n", type_wall); From c04cb7bd5845f7660377a7e4d6d597612a9f9403 Mon Sep 17 00:00:00 2001 From: not populated Date: Wed, 25 Jan 2023 11:57:52 +0000 Subject: [PATCH 240/244] Remove assertion --- src/field_phi_init.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/field_phi_init.c b/src/field_phi_init.c index 060666f3e..6628b2eaa 100644 --- a/src/field_phi_init.c +++ b/src/field_phi_init.c @@ -542,7 +542,6 @@ int field_phi_init_spinodal_patches(field_t * phi, int seed, int patch, int ip, jp, kp; int nlocal[3]; int ipatch, jpatch, kpatch; - int count = 0; double phi1; double ran_uniform; @@ -577,7 +576,6 @@ int field_phi_init_spinodal_patches(field_t * phi, int seed, int patch, index = cs_index(phi->cs, ip, jp, kp); field_scalar_set(phi, index, phi1); - count += 1; } } } @@ -589,8 +587,6 @@ int field_phi_init_spinodal_patches(field_t * phi, int seed, int patch, noise_free(rng); - assert(count == nlocal[X]*nlocal[Y]*nlocal[Z]); - return 0; } From 4fa9bf9b863c73bf3703019b1e3f84f38550f96d Mon Sep 17 00:00:00 2001 From: not populated Date: Wed, 25 Jan 2023 11:59:16 +0000 Subject: [PATCH 241/244] Tidy compiler warnings for set but not used --- tests/unit/test_fe_surfactant1.c | 6 ++++-- tests/unit/test_io_subfile.c | 3 ++- tests/unit/test_lb_model.c | 6 +++++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/unit/test_fe_surfactant1.c b/tests/unit/test_fe_surfactant1.c index cf681c51c..7a00737af 100644 --- a/tests/unit/test_fe_surfactant1.c +++ b/tests/unit/test_fe_surfactant1.c @@ -7,7 +7,7 @@ * Edinburgh Soft Matter and Statistical Phsyics Group and * Edinburgh Parallel Computing Centre * - * (c) 2019-2022 The University of Edinburgh + * (c) 2019-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -179,6 +179,7 @@ __host__ int test_fe_surf_xi_etc(pe_t * pe, cs_t * cs, field_t * phi) { __host__ int test_fe_surf_fed(pe_t * pe, cs_t * cs, field_t * phi) { + int ifail = 0; int index = 0; double psi, phisq, phipsi[2]; double fed, fedref; @@ -204,6 +205,7 @@ __host__ int test_fe_surf_fed(pe_t * pe, cs_t * cs, field_t * phi) { fe_surf_fed(fe, index, &fed); fedref = pref.kt*(psi*log(psi) + (1.0-psi)*log(1.0-psi)); assert(fabs(fed - fedref) < TEST_DOUBLE_TOLERANCE); + if (fabs(fed - fedref) >= TEST_DOUBLE_TOLERANCE) ifail = -1; /* No gradients, phi = 0.8 */ psi = 0.4; @@ -230,7 +232,7 @@ __host__ int test_fe_surf_fed(pe_t * pe, cs_t * cs, field_t * phi) { fe_surf_free(fe); field_grad_free(dphi); - return 0; + return ifail; } /***************************************************************************** diff --git a/tests/unit/test_io_subfile.c b/tests/unit/test_io_subfile.c index 09fe4eb0c..ef61b8398 100644 --- a/tests/unit/test_io_subfile.c +++ b/tests/unit/test_io_subfile.c @@ -9,7 +9,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2022 The University of Edinburgh + * (c) 2022-2023 The University of Edinburgh * * Kevin Stratford(kevin@epcc.ed.ac.uk) * @@ -237,6 +237,7 @@ int test_io_subfile(cs_t * cs, int iogrid[3], const io_subfile_t * subfile) { sz += cs->listnlocal[ia][ib]; } assert(sz == subfile->sizes[ia]); + if (sz != subfile->sizes[ia]) ifail = -1; } /* (File) Offset */ diff --git a/tests/unit/test_lb_model.c b/tests/unit/test_lb_model.c index 25a445bb8..9b06013b6 100644 --- a/tests/unit/test_lb_model.c +++ b/tests/unit/test_lb_model.c @@ -8,7 +8,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2021-2022 The University of Edinburgh + * (c) 2021-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -169,6 +169,8 @@ int test_lb_model_wv(const lb_model_t * model) { assert(fabs(sumcv[X] - 0.0) <= DBL_EPSILON); assert(fabs(sumcv[Y] - 0.0) <= DBL_EPSILON); assert(fabs(sumcv[Z] - 0.0) <= DBL_EPSILON); + + if (fabs(sumwv - 1.0) > DBL_EPSILON) ierr += 1; } /* Quadratic terms \sum_p wv[p] c_pi c_pj = cs2 d_ij */ @@ -181,6 +183,7 @@ int test_lb_model_wv(const lb_model_t * model) { sum += model->wv[p]*model->cv[p][ia]*model->cv[p][ib]; } assert(fabs(sum - d_[ia][ib]*model->cs2) < DBL_EPSILON); + if (fabs(sum - d_[ia][ib]*model->cs2) >= DBL_EPSILON) ierr += 1; } } } @@ -197,6 +200,7 @@ int test_lb_model_wv(const lb_model_t * model) { sum += model->wv[p]*cv[p][ia]*cv[p][ib]*cv[p][ig]; } assert(fabs(sum - 0.0) < DBL_EPSILON); + if (fabs(sum) >= DBL_EPSILON) ierr += 1; } } } From 5ff0defe2802b4a5ca6d6401134e1d50427fc79d Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 25 Jan 2023 14:10:26 +0000 Subject: [PATCH 242/244] Update to account for new output --- docs/tutorial/test2/extract.sh | 7 - docs/tutorial/test2/input | 31 +- ...00010000.001-001 => phi-000010000.001-001} | Bin .../{phi-00010000.vtk => phi-000010000.vtk} | 0 docs/tutorial/test2/phi.001-001.meta | 20 - docs/tutorial/test2/stdout | 1744 ++--------------- ...00010000.001-001 => vel-000010000.001-001} | Bin .../{vel-00010000.vtk => vel-000010000.vtk} | 0 docs/tutorial/test2/vel.001-001.meta | 20 - 9 files changed, 206 insertions(+), 1616 deletions(-) delete mode 100755 docs/tutorial/test2/extract.sh rename docs/tutorial/test2/{phi-00010000.001-001 => phi-000010000.001-001} (100%) rename docs/tutorial/test2/{phi-00010000.vtk => phi-000010000.vtk} (100%) delete mode 100644 docs/tutorial/test2/phi.001-001.meta rename docs/tutorial/test2/{vel-00010000.001-001 => vel-000010000.001-001} (100%) rename docs/tutorial/test2/{vel-00010000.vtk => vel-000010000.vtk} (100%) delete mode 100644 docs/tutorial/test2/vel.001-001.meta diff --git a/docs/tutorial/test2/extract.sh b/docs/tutorial/test2/extract.sh deleted file mode 100755 index a56a23d29..000000000 --- a/docs/tutorial/test2/extract.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -for i in `seq 1000 1000 10000`; -do - tstep=$(printf "%08d" $i) - ./extract -k vel.001-001.meta vel-${tstep}.001-001 - ./extract -k phi.001-001.meta phi-${tstep}.001-001 -done diff --git a/docs/tutorial/test2/input b/docs/tutorial/test2/input index 2da94a908..da4e0899d 100644 --- a/docs/tutorial/test2/input +++ b/docs/tutorial/test2/input @@ -28,7 +28,6 @@ grid 2_2_2 ############################################################################## viscosity 0.83333 -ghost_modes off ############################################################################## # @@ -36,16 +35,15 @@ ghost_modes off # ############################################################################### -free_energy symmetric +free_energy symmetric -A -0.00625 -B 0.00625 -K 0.004 -C 0.0 +symmetric_a -0.00625 +symmetric_b 0.00625 +symmetric_kappa 0.004 -phi0 0.0 -phi_initialisation spinodal -mobility 1.25 +phi0 0.0 +phi_initialisation spinodal +mobility 1.25 fd_gradient_calculation 3d_27pt_fluid fd_advection_scheme_order 1 @@ -64,7 +62,6 @@ colloid_init no_colloids # ############################################################################### -boundary_walls_on no periodicity 1_1_1 ############################################################################### @@ -73,13 +70,15 @@ periodicity 1_1_1 # ############################################################################### -freq_statistics 100 -freq_phi 1000 -freq_vel 1000 -phi_format BINARY -vel_format BINARY +default_io_mode mpiio +default_io_format binary +default_io_report no -config_at_end yes +freq_statistics 500 +freq_phi 1000 +freq_vel 1000 + +config_at_end yes ############################################################################### # diff --git a/docs/tutorial/test2/phi-00010000.001-001 b/docs/tutorial/test2/phi-000010000.001-001 similarity index 100% rename from docs/tutorial/test2/phi-00010000.001-001 rename to docs/tutorial/test2/phi-000010000.001-001 diff --git a/docs/tutorial/test2/phi-00010000.vtk b/docs/tutorial/test2/phi-000010000.vtk similarity index 100% rename from docs/tutorial/test2/phi-00010000.vtk rename to docs/tutorial/test2/phi-000010000.vtk diff --git a/docs/tutorial/test2/phi.001-001.meta b/docs/tutorial/test2/phi.001-001.meta deleted file mode 100644 index 211752a37..000000000 --- a/docs/tutorial/test2/phi.001-001.meta +++ /dev/null @@ -1,20 +0,0 @@ -Metadata for file set prefix: phi -Data description: phi -Data size per site (bytes): 8 -is_bigendian(): 0 -Number of processors: 8 -Cartesian communicator topology: 2 2 2 -Total system size: 32 32 32 -Lees-Edwards planes: 0 -Lees-Edwards plane speed 0.00000000000000 -Number of I/O groups (files): 1 -I/O communicator topology: 1 1 1 -Write order: - 0 0 0 0 16 16 16 0 0 0 - 1 0 0 1 16 16 16 0 0 16 - 2 0 1 0 16 16 16 0 16 0 - 3 0 1 1 16 16 16 0 16 16 - 4 1 0 0 16 16 16 16 0 0 - 5 1 0 1 16 16 16 16 0 16 - 6 1 1 0 16 16 16 16 16 0 - 7 1 1 1 16 16 16 16 16 16 diff --git a/docs/tutorial/test2/stdout b/docs/tutorial/test2/stdout index 0e9bd75cb..3149abfb1 100644 --- a/docs/tutorial/test2/stdout +++ b/docs/tutorial/test2/stdout @@ -1,20 +1,23 @@ -Welcome to Ludwig v0.15.0 (MPI version running on 8 processes) -Start time: Mon Jan 31 12:19:42 2022 +Welcome to: Ludwig v0.19.0 (Serial version running on 1 process) +Git commit: 4fa9bf9b863c73bf3703019b1e3f84f38550f96d + +Start time: Wed Jan 25 14:05:11 2023 Compiler: - name: Gnu 11.2.0 - version-string: 11.2.0 + name: Gnu 12.2.0 + version-string: 12.2.0 + options: -O2 -g -fopenmp -Wall -DNDEBUG -Werror Target thread model: OpenMP. -OpenMP threads: 16; maximum number of threads: 16. +OpenMP threads: 8; maximum number of threads: 8. -Read 25 user parameters from input +Read 23 user parameters from spinodal-test2.inp System details -------------- System size: 32 32 32 -Decomposition: 2 2 2 -Local domain: 16 16 16 +Decomposition: 1 1 1 +Local domain: 32 32 32 Periodic: 1 1 1 Halo nhalo: 2 Reorder: true @@ -35,7 +38,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.25000e+00 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- @@ -51,9 +54,9 @@ External magnetic field 0.00000e+00 0.00000e+00 0.00000e+00 Lattice Boltzmann distributions ------------------------------- Model: d3q19 -SIMD vector len: 4 +SIMD vector len: 1 Number of sets: 1 -Halo type: full +Halo type: lb_halo_target (full halo) Input format: binary Output format: binary I/O grid: 1 1 1 @@ -62,7 +65,7 @@ Lattice Boltzmann collision --------------------------- Relaxation time scheme: M10 Hydrodynamic modes: on -Ghost modes: off +Ghost modes: on Isothermal fluctuations: off Shear relaxation time: 2.99999e+00 Bulk relaxation time: 2.99999e+00 @@ -75,7 +78,7 @@ Hydrodynamics: on Order parameter I/O ------------------- -Order parameter I/O format: BINARY +Order parameter I/O format: I/O decomposition: 1 1 1 Advection scheme order: 1 @@ -94,170 +97,34 @@ Momentum - x y z Starting time step loop. Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.7075230e-13 0.99999597100 1.00000224425 -[phi] -5.9892076e+00 -1.8277611e-04 8.4736999e-05 -3.7913090e-02 3.4553839e-02 - -Free energy density - timestep total fluid -[fed] 100 -1.6003803788e-07 -1.6003803788e-07 - -Momentum - x y z -[total ] 4.5555920e-13 1.6264767e-14 -2.2329361e-14 -[fluid ] 4.5555920e-13 1.6264767e-14 -2.2329361e-14 - -Velocity - x y z -[minimum ] -2.8195285e-07 -2.7984482e-07 -2.7511604e-07 -[maximum ] 2.9871396e-07 3.2025753e-07 3.1333488e-07 - -Completed cycle 100 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.5232260e-13 0.99999670920 1.00000335066 -[phi] -5.9892076e+00 -1.8277611e-04 1.1045141e-04 -4.1119558e-02 3.9049863e-02 - -Free energy density - timestep total fluid -[fed] 200 -2.1166939546e-07 -2.1166939546e-07 - -Momentum - x y z -[total ] 4.3795523e-13 1.0769163e-14 1.3211654e-14 -[fluid ] 4.3795523e-13 1.0769163e-14 1.3211654e-14 - -Velocity - x y z -[minimum ] -3.5782155e-07 -3.3708348e-07 -3.1246038e-07 -[maximum ] 3.3883987e-07 4.4844919e-07 3.5427676e-07 - -Completed cycle 200 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 3.0397906e-13 0.99999675733 1.00000529794 -[phi] -5.9892076e+00 -1.8277611e-04 1.7194304e-04 -4.8845813e-02 5.0039839e-02 - -Free energy density - timestep total fluid -[fed] 300 -3.3293730614e-07 -3.3293730614e-07 - -Momentum - x y z -[total ] 4.9548213e-13 -3.0576236e-14 -1.5668022e-14 -[fluid ] 4.9548213e-13 -3.0576236e-14 -1.5668022e-14 - -Velocity - x y z -[minimum ] -5.6663924e-07 -5.1289673e-07 -4.8532584e-07 -[maximum ] 4.8566893e-07 6.7468130e-07 5.0790183e-07 - -Completed cycle 300 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 7.6316731e-13 0.99999636660 1.00000847477 -[phi] -5.9892076e+00 -1.8277611e-04 2.8490523e-04 -6.5339489e-02 6.4690253e-02 - -Free energy density - timestep total fluid -[fed] 400 -5.5467543213e-07 -5.5467543213e-07 - -Momentum - x y z -[total ] 4.7468973e-13 -2.8997638e-14 -3.1391556e-14 -[fluid ] 4.7468973e-13 -2.8997638e-14 -3.1391556e-14 - -Velocity - x y z -[minimum ] -9.2606434e-07 -8.3892858e-07 -8.2672027e-07 -[maximum ] 7.6166467e-07 1.0701992e-06 7.9513744e-07 - -Completed cycle 400 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 2.1176394e-12 0.99999553363 1.00001371173 +[rho] 32768.00 1.00000000000 2.1063151e-12 0.99999553363 1.00001371173 [phi] -5.9892076e+00 -1.8277611e-04 4.8718426e-04 -8.6230872e-02 8.3870553e-02 Free energy density - timestep total fluid [fed] 500 -9.5113387392e-07 -9.5113387392e-07 Momentum - x y z -[total ] 3.8016118e-13 -1.9591967e-14 -1.6861512e-15 -[fluid ] 3.8016118e-13 -1.9591967e-14 -1.6861512e-15 +[total ] 3.8015077e-13 -1.9664825e-14 -1.8908486e-15 +[fluid ] 3.8015077e-13 -1.9664825e-14 -1.8908486e-15 Velocity - x y z [minimum ] -1.5213472e-06 -1.3906223e-06 -1.4460630e-06 [maximum ] 1.2441757e-06 1.7638482e-06 1.3140333e-06 Completed cycle 500 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 6.1979311e-12 0.99999409447 1.00002234909 -[phi] -5.9892076e+00 -1.8277611e-04 8.4874129e-04 -1.1293117e-01 1.0881489e-01 - -Free energy density - timestep total fluid -[fed] 600 -1.6589269429e-06 -1.6589269429e-06 - -Momentum - x y z -[total ] 3.4618142e-13 -4.8721444e-14 2.3113456e-14 -[fluid ] 3.4618142e-13 -4.8721444e-14 2.3113456e-14 - -Velocity - x y z -[minimum ] -2.5105422e-06 -2.3296874e-06 -2.5656221e-06 -[maximum ] 2.0817873e-06 2.9398285e-06 2.2041674e-06 - -Completed cycle 600 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.8600232e-11 0.99999122790 1.00003787823 -[phi] -5.9892076e+00 -1.8277611e-04 1.4957928e-03 -1.4699277e-01 1.4101269e-01 - -Free energy density - timestep total fluid -[fed] 700 -2.9231794916e-06 -2.9231794916e-06 - -Momentum - x y z -[total ] 3.7717052e-13 -3.0152963e-14 3.8882092e-14 -[fluid ] 3.7717052e-13 -3.0152963e-14 3.8882092e-14 - -Velocity - x y z -[minimum ] -4.3643705e-06 -3.9379776e-06 -4.5252998e-06 -[maximum ] 3.5199722e-06 4.9355771e-06 3.7450094e-06 - -Completed cycle 700 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 5.6160854e-11 0.99998636169 1.00006512538 -[phi] -5.9892076e+00 -1.8277611e-04 2.6530835e-03 -1.8994335e-01 1.8203992e-01 - -Free energy density - timestep total fluid -[fed] 800 -5.1763760514e-06 -5.1763760514e-06 - -Momentum - x y z -[total ] 3.8025139e-13 -2.0813212e-14 3.1585845e-14 -[fluid ] 3.8025139e-13 -2.0813212e-14 3.1585845e-14 - -Velocity - x y z -[minimum ] -7.7112587e-06 -6.7001329e-06 -7.9264262e-06 -[maximum ] 5.9961651e-06 8.3053065e-06 6.7454790e-06 - -Completed cycle 800 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.6718404e-10 0.99997685440 1.00010968285 -[phi] -5.9892076e+00 -1.8277611e-04 4.7125431e-03 -2.4288645e-01 2.3433704e-01 - -Free energy density - timestep total fluid -[fed] 900 -9.1605026959e-06 -9.1605026959e-06 - -Momentum - x y z -[total ] 3.3962416e-13 5.0827398e-15 1.9002161e-14 -[fluid ] 3.3962416e-13 5.0827398e-15 1.9002161e-14 - -Velocity - x y z -[minimum ] -1.3605055e-05 -1.1431695e-05 -1.3743350e-05 -[maximum ] 1.0254963e-05 1.3911689e-05 1.2316458e-05 - -Completed cycle 900 Writing phi file at step 1000! -Writing velocity output at step 1000! +Writing rho/velocity output at step 1000! Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 4.7816684e-10 0.99995964285 1.00017786147 +[rho] 32768.00 1.00000000000 4.7816617e-10 0.99995964285 1.00017786147 [phi] -5.9892076e+00 -1.8277611e-04 8.3323309e-03 -3.0584417e-01 3.0376499e-01 Free energy density - timestep total fluid [fed] 1000 -1.6085949744e-05 -1.6085949744e-05 Momentum - x y z -[total ] 3.2119793e-13 4.6830595e-14 1.4668822e-14 -[fluid ] 3.2119793e-13 4.6830595e-14 1.4668822e-14 +[total ] 3.2087180e-13 4.6438547e-14 1.4672291e-14 +[fluid ] 3.2087180e-13 4.6438547e-14 1.4672291e-14 Velocity - x y z [minimum ] -2.3844024e-05 -1.9451814e-05 -2.3457745e-05 @@ -266,170 +133,34 @@ Velocity - x y z Completed cycle 1000 Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.2734758e-09 0.99992961011 1.00027173542 -[phi] -5.9892076e+00 -1.8277611e-04 1.4546646e-02 -3.7942017e-01 3.8365780e-01 - -Free energy density - timestep total fluid -[fed] 1100 -2.7759431109e-05 -2.7759431109e-05 - -Momentum - x y z -[total ] 3.2598924e-13 4.5886905e-14 3.1225023e-16 -[fluid ] 3.2598924e-13 4.5886905e-14 3.1225023e-16 - -Velocity - x y z -[minimum ] -4.1187553e-05 -3.3030063e-05 -3.9121286e-05 -[maximum ] 2.9730061e-05 3.7671092e-05 3.8507659e-05 - -Completed cycle 1100 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 3.0611935e-09 0.99987885973 1.00038214337 -[phi] -5.9892076e+00 -1.8277611e-04 2.4805304e-02 -4.7217263e-01 4.6920074e-01 - -Free energy density - timestep total fluid -[fed] 1200 -4.6487867780e-05 -4.6487867780e-05 - -Momentum - x y z -[total ] 3.2741865e-13 3.2782804e-14 2.3765712e-14 -[fluid ] 3.2741865e-13 3.2782804e-14 2.3765712e-14 - -Velocity - x y z -[minimum ] -6.9320212e-05 -5.7326406e-05 -6.3236647e-05 -[maximum ] 4.9546848e-05 6.4683951e-05 6.4734192e-05 - -Completed cycle 1200 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 6.5221646e-09 0.99979872526 1.00049859044 -[phi] -5.9892076e+00 -1.8277611e-04 4.0771304e-02 -5.6361095e-01 5.5385116e-01 - -Free energy density - timestep total fluid -[fed] 1300 -7.4451116718e-05 -7.4451116718e-05 - -Momentum - x y z -[total ] 3.0911038e-13 -5.0480453e-15 1.1438767e-14 -[fluid ] 3.0911038e-13 -5.0480453e-15 1.1438767e-14 - -Velocity - x y z -[minimum ] -1.1192839e-04 -9.5461422e-05 -9.8364928e-05 -[maximum ] 7.9992116e-05 1.0695713e-04 1.0320197e-04 - -Completed cycle 1300 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.2381934e-08 0.99967056513 1.00075184277 -[phi] -5.9892076e+00 -1.8277611e-04 6.3693889e-02 -6.4643005e-01 6.3147635e-01 - -Free energy density - timestep total fluid -[fed] 1400 -1.1241463288e-04 -1.1241463288e-04 - -Momentum - x y z -[total ] 3.4230604e-13 -3.5440401e-14 4.9064919e-14 -[fluid ] 3.4230604e-13 -3.5440401e-14 4.9064919e-14 - -Velocity - x y z -[minimum ] -1.7030817e-04 -1.4974956e-04 -1.4632369e-04 -[maximum ] 1.2303755e-04 1.6784643e-04 1.5344713e-04 - -Completed cycle 1400 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 2.1323726e-08 0.99952245400 1.00104155252 +[rho] 32768.00 1.00000000000 2.1323733e-08 0.99952245400 1.00104155252 [phi] -5.9892076e+00 -1.8277611e-04 9.3456275e-02 -7.1598586e-01 7.0179497e-01 Free energy density - timestep total fluid [fed] 1500 -1.5839325740e-04 -1.5839325740e-04 Momentum - x y z -[total ] 3.5459136e-13 -2.9878877e-14 3.6807363e-14 -[fluid ] 3.5459136e-13 -2.9878877e-14 3.6807363e-14 +[total ] 3.5452891e-13 -2.9583974e-14 3.5988573e-14 +[fluid ] 3.5452891e-13 -2.9583974e-14 3.5988573e-14 Velocity - x y z [minimum ] -2.4021427e-04 -2.1783912e-04 -2.0674707e-04 [maximum ] 1.7751775e-04 2.4625330e-04 2.1190198e-04 Completed cycle 1500 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 3.3567838e-08 0.99936467043 1.00127692931 -[phi] -5.9892076e+00 -1.8277611e-04 1.2795908e-01 -7.7039086e-01 7.5884040e-01 - -Free energy density - timestep total fluid -[fed] 1600 -2.0764082960e-04 -2.0764082960e-04 - -Momentum - x y z -[total ] 3.2753314e-13 -6.6457256e-14 4.1570913e-14 -[fluid ] 3.2753314e-13 -6.6457256e-14 4.1570913e-14 - -Velocity - x y z -[minimum ] -3.1103588e-04 -2.9863543e-04 -2.7548567e-04 -[maximum ] 2.4352880e-04 3.3344295e-04 2.7531399e-04 - -Completed cycle 1600 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 4.8378821e-08 0.99924274225 1.00135954647 -[phi] -5.9892076e+00 -1.8277611e-04 1.6365546e-01 -8.0976437e-01 8.0306698e-01 - -Free energy density - timestep total fluid -[fed] 1700 -2.5465038414e-04 -2.5465038414e-04 - -Momentum - x y z -[total ] 3.3881578e-13 -3.9593329e-14 4.8325927e-14 -[fluid ] 3.3881578e-13 -3.9593329e-14 4.8325927e-14 - -Velocity - x y z -[minimum ] -3.6977684e-04 -3.7603175e-04 -3.4478297e-04 -[maximum ] 3.1486237e-04 4.1384234e-04 3.3063352e-04 - -Completed cycle 1700 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 6.4429195e-08 0.99911660566 1.00130264123 -[phi] -5.9892076e+00 -1.8277611e-04 1.9710202e-01 -8.3556704e-01 8.3457392e-01 - -Free energy density - timestep total fluid -[fed] 1800 -2.9568815377e-04 -2.9568815377e-04 - -Momentum - x y z -[total ] 4.1325277e-13 -3.2578107e-14 6.2370248e-14 -[fluid ] 4.1325277e-13 -3.2578107e-14 6.2370248e-14 - -Velocity - x y z -[minimum ] -4.2250139e-04 -4.3775053e-04 -4.0569514e-04 -[maximum ] 3.9741321e-04 4.7160991e-04 3.9922188e-04 - -Completed cycle 1800 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 8.0103727e-08 0.99900321957 1.00129560595 -[phi] -5.9892076e+00 -1.8277611e-04 2.2626608e-01 -8.5608999e-01 8.5432419e-01 - -Free energy density - timestep total fluid -[fed] 1900 -3.2969781596e-04 -3.2969781596e-04 - -Momentum - x y z -[total ] 4.4905399e-13 1.5144136e-14 9.3203223e-14 -[fluid ] 4.4905399e-13 1.5144136e-14 9.3203223e-14 - -Velocity - x y z -[minimum ] -4.8820353e-04 -5.1059547e-04 -4.4955466e-04 -[maximum ] 4.6181670e-04 4.9815574e-04 4.6269428e-04 - -Completed cycle 1900 Writing phi file at step 2000! -Writing velocity output at step 2000! +Writing rho/velocity output at step 2000! Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 9.3823653e-08 0.99893767334 1.00137892040 +[rho] 32768.00 1.00000000000 9.3823674e-08 0.99893767334 1.00137892040 [phi] -5.9892076e+00 -1.8277611e-04 2.5069554e-01 -8.8036232e-01 8.7050296e-01 Free energy density - timestep total fluid [fed] 2000 -3.5743943403e-04 -3.5743943403e-04 Momentum - x y z -[total ] 4.5986825e-13 3.0964814e-14 1.2033430e-13 -[fluid ] 4.5986825e-13 3.0964814e-14 1.2033430e-13 +[total ] 4.5944151e-13 3.1353392e-14 1.2011225e-13 +[fluid ] 4.5944151e-13 3.1353392e-14 1.2011225e-13 Velocity - x y z [minimum ] -5.3161790e-04 -5.6909095e-04 -5.0341812e-04 @@ -438,1406 +169,313 @@ Velocity - x y z Completed cycle 2000 Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.0492001e-07 0.99892873392 1.00163561841 -[phi] -5.9892076e+00 -1.8277611e-04 2.7091264e-01 -8.9673509e-01 8.8495338e-01 +[rho] 32768.00 1.00000000000 1.3051301e-07 0.99890852341 1.00167805823 +[phi] -5.9892076e+00 -1.8277611e-04 3.2569962e-01 -9.2428349e-01 9.1809411e-01 Free energy density - timestep total fluid -[fed] 2100 -3.8030380903e-04 -3.8030380903e-04 +[fed] 2500 -4.4510185331e-04 -4.4510185331e-04 Momentum - x y z -[total ] 4.0342382e-13 -7.6050277e-15 1.1809997e-13 -[fluid ] 4.0342382e-13 -7.6050277e-15 1.1809997e-13 +[total ] 2.8494221e-13 -2.3113456e-14 7.2431644e-14 +[fluid ] 2.8494221e-13 -2.3113456e-14 7.2431644e-14 Velocity - x y z -[minimum ] -5.8967138e-04 -6.0804985e-04 -5.6840897e-04 -[maximum ] 5.6966821e-04 6.1567386e-04 5.8090958e-04 +[minimum ] -8.5241659e-04 -7.1624144e-04 -7.4971797e-04 +[maximum ] 8.1087399e-04 7.4995229e-04 6.8135338e-04 -Completed cycle 2100 +Completed cycle 2500 +Writing phi file at step 3000! +Writing rho/velocity output at step 3000! Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1352758e-07 0.99898207379 1.00167026489 -[phi] -5.9892076e+00 -1.8277611e-04 2.8777788e-01 -9.0719587e-01 8.9508313e-01 +[rho] 32768.00 1.00000000000 1.4747265e-07 0.99885588882 1.00199412998 +[phi] -5.9892076e+00 -1.8277611e-04 3.6981970e-01 -9.4938370e-01 9.3667633e-01 Free energy density - timestep total fluid -[fed] 2200 -3.9961992143e-04 -3.9961992143e-04 +[fed] 3000 -5.0286105589e-04 -5.0286105589e-04 Momentum - x y z -[total ] 3.6047207e-13 2.3706731e-14 1.1840529e-13 -[fluid ] 3.6047207e-13 2.3706731e-14 1.1840529e-13 +[total ] 2.8778022e-13 -2.2634672e-14 2.0197038e-13 +[fluid ] 2.8778022e-13 -2.2634672e-14 2.0197038e-13 Velocity - x y z -[minimum ] -6.6396259e-04 -6.3847100e-04 -6.1683250e-04 -[maximum ] 6.3849451e-04 6.6893210e-04 6.3196324e-04 +[minimum ] -1.0282932e-03 -7.1849432e-04 -8.7258355e-04 +[maximum ] 1.0152072e-03 6.4125082e-04 8.7321311e-04 -Completed cycle 2200 +Completed cycle 3000 Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.2014658e-07 0.99893494343 1.00153722889 -[phi] -5.9892076e+00 -1.8277611e-04 3.0211710e-01 -9.1348347e-01 9.0405194e-01 +[rho] 32768.00 1.00000000000 1.4907844e-07 0.99885952314 1.00191229627 +[phi] -5.9892076e+00 -1.8277611e-04 4.0526490e-01 -9.6120764e-01 9.5210827e-01 Free energy density - timestep total fluid -[fed] 2300 -4.1640588236e-04 -4.1640588236e-04 +[fed] 3500 -5.5257952580e-04 -5.5257952580e-04 Momentum - x y z -[total ] 3.6675524e-13 3.4243441e-15 8.3603263e-14 -[fluid ] 3.6675524e-13 3.4243441e-15 8.3603263e-14 +[total ] 2.6295632e-13 -7.4593109e-15 2.0363572e-13 +[fluid ] 2.6295632e-13 -7.4593109e-15 2.0363572e-13 Velocity - x y z -[minimum ] -7.3291753e-04 -6.6486556e-04 -6.3693388e-04 -[maximum ] 7.0366358e-04 7.0933731e-04 6.6499627e-04 +[minimum ] -9.7366374e-04 -7.6956770e-04 -9.4056566e-04 +[maximum ] 1.0244738e-03 7.7837299e-04 8.6954592e-04 -Completed cycle 2300 +Completed cycle 3500 +Writing phi file at step 4000! +Writing rho/velocity output at step 4000! Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.2556538e-07 0.99890191577 1.00142456663 -[phi] -5.9892076e+00 -1.8277611e-04 3.1459222e-01 -9.1773801e-01 9.1218611e-01 +[rho] 32768.00 1.00000000000 1.3122334e-07 0.99886357464 1.00183815212 +[phi] -5.9892076e+00 -1.8277611e-04 4.3468078e-01 -9.6092085e-01 9.6447995e-01 Free energy density - timestep total fluid -[fed] 2400 -4.3139276720e-04 -4.3139276720e-04 +[fed] 4000 -5.9406770779e-04 -5.9406770779e-04 Momentum - x y z -[total ] 3.5708936e-13 2.7814556e-14 6.5999289e-14 -[fluid ] 3.5708936e-13 2.7814556e-14 6.5999289e-14 +[total ] 1.9894156e-13 -2.2200991e-14 1.9278329e-13 +[fluid ] 1.9894156e-13 -2.2200991e-14 1.9278329e-13 Velocity - x y z -[minimum ] -7.9328336e-04 -6.8204057e-04 -6.9062259e-04 -[maximum ] 7.6220286e-04 7.3621463e-04 6.8036355e-04 +[minimum ] -9.7476646e-04 -7.9646965e-04 -8.5686130e-04 +[maximum ] 9.2862332e-04 1.0382664e-03 7.6923673e-04 -Completed cycle 2400 +Completed cycle 4000 Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.3051302e-07 0.99890852341 1.00167805823 -[phi] -5.9892076e+00 -1.8277611e-04 3.2569962e-01 -9.2428349e-01 9.1809411e-01 +[rho] 32768.00 1.00000000000 1.2245017e-07 0.99895760272 1.00182578005 +[phi] -5.9892076e+00 -1.8277611e-04 4.5845272e-01 -9.6980835e-01 9.7250800e-01 Free energy density - timestep total fluid -[fed] 2500 -4.4510185331e-04 -4.4510185331e-04 +[fed] 4500 -6.2818015585e-04 -6.2818015585e-04 Momentum - x y z -[total ] 2.8481384e-13 -2.2443852e-14 7.3167167e-14 -[fluid ] 2.8481384e-13 -2.2443852e-14 7.3167167e-14 +[total ] 1.8778035e-13 4.0332321e-14 2.0664026e-13 +[fluid ] 1.8778035e-13 4.0332321e-14 2.0664026e-13 Velocity - x y z -[minimum ] -8.5241659e-04 -7.1624144e-04 -7.4971797e-04 -[maximum ] 8.1087399e-04 7.4995229e-04 6.8135338e-04 +[minimum ] -9.1929130e-04 -7.5542037e-04 -6.8538634e-04 +[maximum ] 8.3393075e-04 9.3944716e-04 6.2402757e-04 -Completed cycle 2500 +Completed cycle 4500 +Writing phi file at step 5000! +Writing rho/velocity output at step 5000! Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.3482637e-07 0.99888249619 1.00195068492 -[phi] -5.9892076e+00 -1.8277611e-04 3.3579870e-01 -9.3311267e-01 9.2395856e-01 +[rho] 32768.00 1.00000000000 1.1881696e-07 0.99889323555 1.00187450842 +[phi] -5.9892076e+00 -1.8277611e-04 4.7933845e-01 -9.7406728e-01 9.8253696e-01 Free energy density - timestep total fluid -[fed] 2600 -4.5788029540e-04 -4.5788029540e-04 +[fed] 5000 -6.5828010521e-04 -6.5828010521e-04 Momentum - x y z -[total ] 2.9035455e-13 -1.9116653e-14 1.2768606e-13 -[fluid ] 2.9035455e-13 -1.9116653e-14 1.2768606e-13 +[total ] 1.8543500e-13 1.1128598e-13 2.3412522e-13 +[fluid ] 1.8543500e-13 1.1128598e-13 2.3412522e-13 Velocity - x y z -[minimum ] -8.9660599e-04 -7.3285507e-04 -7.8827216e-04 -[maximum ] 8.6550454e-04 7.5294004e-04 7.2054486e-04 +[minimum ] -9.0143402e-04 -7.2983090e-04 -8.4081680e-04 +[maximum ] 8.0902548e-04 8.7873874e-04 6.3512283e-04 -Completed cycle 2600 +Completed cycle 5000 Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.3791803e-07 0.99884886192 1.00178520479 -[phi] -5.9892076e+00 -1.8277611e-04 3.4512745e-01 -9.3984796e-01 9.2975812e-01 +[rho] 32768.00 1.00000000000 1.1250327e-07 0.99886179286 1.00151630863 +[phi] -5.9892076e+00 -1.8277611e-04 4.9708837e-01 -9.7884833e-01 9.8958402e-01 Free energy density - timestep total fluid -[fed] 2700 -4.6990915587e-04 -4.6990915587e-04 +[fed] 5500 -6.8392653381e-04 -6.8392653381e-04 Momentum - x y z -[total ] 3.5043843e-13 -2.2874064e-14 1.2582990e-13 -[fluid ] 3.5043843e-13 -2.2874064e-14 1.2582990e-13 +[total ] 1.1755874e-13 1.0758408e-13 1.3027426e-13 +[fluid ] 1.1755874e-13 1.0758408e-13 1.3027426e-13 Velocity - x y z -[minimum ] -9.2449734e-04 -7.3498427e-04 -8.0221295e-04 -[maximum ] 9.2003070e-04 7.5176682e-04 7.6625030e-04 +[minimum ] -8.2213503e-04 -7.0830584e-04 -8.4736395e-04 +[maximum ] 8.2549795e-04 8.1944510e-04 5.9949725e-04 -Completed cycle 2700 +Completed cycle 5500 +Writing phi file at step 6000! +Writing rho/velocity output at step 6000! Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.4052704e-07 0.99886228699 1.00156820311 -[phi] -5.9892076e+00 -1.8277611e-04 3.5383191e-01 -9.4468637e-01 9.3347334e-01 +[rho] 32768.00 1.00000000000 1.1076608e-07 0.99894037489 1.00208778944 +[phi] -5.9892076e+00 -1.8277611e-04 5.1149067e-01 -9.8490788e-01 9.9555033e-01 Free energy density - timestep total fluid -[fed] 2800 -4.8131459723e-04 -4.8131459723e-04 +[fed] 6000 -7.0571261526e-04 -7.0571261526e-04 Momentum - x y z -[total ] 3.1362413e-13 1.1195905e-14 1.4585555e-13 -[fluid ] 3.1362413e-13 1.1195905e-14 1.4585555e-13 +[total ] 1.4304530e-13 -2.9119068e-14 1.3411841e-13 +[fluid ] 1.4304530e-13 -2.9119068e-14 1.3411841e-13 Velocity - x y z -[minimum ] -9.6171328e-04 -7.2638476e-04 -8.0661498e-04 -[maximum ] 9.6572562e-04 7.3381316e-04 8.0794983e-04 +[minimum ] -8.4709557e-04 -7.0717499e-04 -7.3331802e-04 +[maximum ] 8.9175487e-04 8.1115474e-04 6.3780458e-04 -Completed cycle 2800 +Completed cycle 6000 Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.4367666e-07 0.99886659498 1.00174717660 -[phi] -5.9892076e+00 -1.8277611e-04 3.6202596e-01 -9.4780821e-01 9.3563292e-01 +[rho] 32768.00 1.00000000000 1.1084627e-07 0.99898377327 1.00166425147 +[phi] -5.9892076e+00 -1.8277611e-04 5.2379254e-01 -9.9109526e-01 9.9629639e-01 Free energy density - timestep total fluid -[fed] 2900 -4.9224983337e-04 -4.9224983337e-04 +[fed] 6500 -7.2581938148e-04 -7.2581938148e-04 Momentum - x y z -[total ] 3.2281122e-13 1.7340296e-14 1.4955051e-13 -[fluid ] 3.2281122e-13 1.7340296e-14 1.4955051e-13 +[total ] 3.4885289e-14 4.0554365e-14 2.0015239e-13 +[fluid ] 3.4885289e-14 4.0554365e-14 2.0015239e-13 Velocity - x y z -[minimum ] -9.9692693e-04 -7.2141695e-04 -8.2570924e-04 -[maximum ] 9.9848375e-04 6.9442672e-04 8.4221101e-04 +[minimum ] -8.2336157e-04 -7.0720608e-04 -6.9614013e-04 +[maximum ] 1.0109515e-03 7.5302914e-04 7.0049373e-04 -Completed cycle 2900 -Writing phi file at step 3000! -Writing velocity output at step 3000! +Completed cycle 6500 +Writing phi file at step 7000! +Writing rho/velocity output at step 7000! Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.4747264e-07 0.99885588882 1.00199412998 -[phi] -5.9892076e+00 -1.8277611e-04 3.6981970e-01 -9.4938370e-01 9.3667633e-01 +[rho] 32768.00 1.00000000000 1.1217043e-07 0.99899239320 1.00183086300 +[phi] -5.9892076e+00 -1.8277611e-04 5.3499311e-01 -9.9696624e-01 9.9575691e-01 Free energy density - timestep total fluid -[fed] 3000 -5.0286105589e-04 -5.0286105589e-04 +[fed] 7000 -7.4531744328e-04 -7.4531744328e-04 Momentum - x y z -[total ] 2.8751654e-13 -2.2599977e-14 2.0206406e-13 -[fluid ] 2.8751654e-13 -2.2599977e-14 2.0206406e-13 +[total ] -1.2468845e-13 1.1073434e-13 2.1945640e-13 +[fluid ] -1.2468845e-13 1.1073434e-13 2.1945640e-13 Velocity - x y z -[minimum ] -1.0282932e-03 -7.1849432e-04 -8.7258355e-04 -[maximum ] 1.0152072e-03 6.4125082e-04 8.7321311e-04 +[minimum ] -8.0170847e-04 -7.2572061e-04 -6.7040516e-04 +[maximum ] 1.1229630e-03 7.1666215e-04 7.8619143e-04 -Completed cycle 3000 +Completed cycle 7000 Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.5130260e-07 0.99886345967 1.00215725787 -[phi] -5.9892076e+00 -1.8277611e-04 3.7731822e-01 -9.5070784e-01 9.3787398e-01 +[rho] 32768.00 1.00000000000 1.1807686e-07 0.99896815919 1.00221569442 +[phi] -5.9892076e+00 -1.8277611e-04 5.4603071e-01 -9.9796395e-01 9.9605037e-01 Free energy density - timestep total fluid -[fed] 3100 -5.1325502587e-04 -5.1325502587e-04 +[fed] 7500 -7.6572970465e-04 -7.6572970465e-04 Momentum - x y z -[total ] 3.3717820e-13 -1.7399276e-14 2.2839022e-13 -[fluid ] 3.3717820e-13 -1.7399276e-14 2.2839022e-13 +[total ] -1.2130921e-13 8.4602464e-14 1.4322918e-13 +[fluid ] -1.2130921e-13 8.4602464e-14 1.4322918e-13 Velocity - x y z -[minimum ] -1.0380846e-03 -7.0991436e-04 -9.1252013e-04 -[maximum ] 1.0168212e-03 6.0423947e-04 9.0619660e-04 +[minimum ] -7.8584377e-04 -7.3065420e-04 -7.5341686e-04 +[maximum ] 9.9198296e-04 7.7251001e-04 8.4368548e-04 -Completed cycle 3100 +Completed cycle 7500 +Writing phi file at step 8000! +Writing rho/velocity output at step 8000! Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.5403362e-07 0.99888266425 1.00230544795 -[phi] -5.9892076e+00 -1.8277611e-04 3.8460207e-01 -9.5429555e-01 9.3945552e-01 +[rho] 32768.00 1.00000000000 1.1368020e-07 0.99901572281 1.00145313471 +[phi] -5.9892076e+00 -1.8277611e-04 5.5731993e-01 -9.9567927e-01 9.9533746e-01 Free energy density - timestep total fluid -[fed] 3200 -5.2347310258e-04 -5.2347310258e-04 +[fed] 8000 -7.8595081742e-04 -7.8595081742e-04 Momentum - x y z -[total ] 2.9630465e-13 -8.4411644e-15 2.1490101e-13 -[fluid ] 2.9630465e-13 -8.4411644e-15 2.1490101e-13 +[total ] -4.4037690e-14 2.8914371e-14 1.0579385e-13 +[fluid ] -4.4037690e-14 2.8914371e-14 1.0579385e-13 Velocity - x y z -[minimum ] -1.0248215e-03 -7.1329700e-04 -9.4048576e-04 -[maximum ] 1.0096630e-03 6.2405656e-04 9.1406696e-04 +[minimum ] -7.6264147e-04 -7.6103495e-04 -8.0149803e-04 +[maximum ] 7.9192302e-04 8.3626939e-04 8.8450782e-04 -Completed cycle 3200 +Completed cycle 8000 Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.5447081e-07 0.99887119416 1.00239065551 -[phi] -5.9892076e+00 -1.8277611e-04 3.9170339e-01 -9.5740503e-01 9.4439205e-01 +[rho] 32768.00 1.00000000000 1.0847466e-07 0.99899940362 1.00139971215 +[phi] -5.9892076e+00 -1.8277611e-04 5.6842250e-01 -9.9361592e-01 9.9495612e-01 Free energy density - timestep total fluid -[fed] 3300 -5.3348188801e-04 -5.3348188801e-04 +[fed] 8500 -8.0501259763e-04 -8.0501259763e-04 Momentum - x y z -[total ] 3.0552297e-13 8.3266727e-15 2.0995358e-13 -[fluid ] 3.0552297e-13 8.3266727e-15 2.0995358e-13 +[total ] -1.1227477e-13 4.3166859e-14 1.3571436e-13 +[fluid ] -1.1227477e-13 4.3166859e-14 1.3571436e-13 Velocity - x y z -[minimum ] -9.9692717e-04 -7.4171687e-04 -9.5192752e-04 -[maximum ] 1.0123090e-03 6.7332795e-04 8.8843920e-04 +[minimum ] -7.2344387e-04 -7.6275390e-04 -7.5475345e-04 +[maximum ] 6.9129608e-04 8.7755944e-04 8.9323749e-04 -Completed cycle 3300 +Completed cycle 8500 +Writing phi file at step 9000! +Writing rho/velocity output at step 9000! Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.5248306e-07 0.99886121313 1.00220173809 -[phi] -5.9892076e+00 -1.8277611e-04 3.9860237e-01 -9.5980904e-01 9.4855860e-01 +[rho] 32768.00 1.00000000000 1.0467954e-07 0.99903220711 1.00146282247 +[phi] -5.9892076e+00 -1.8277611e-04 5.7909691e-01 -9.9227756e-01 9.9518788e-01 Free energy density - timestep total fluid -[fed] 3400 -5.4320227640e-04 -5.4320227640e-04 +[fed] 9000 -8.2287647705e-04 -8.2287647705e-04 Momentum - x y z -[total ] 2.7239322e-13 -6.6752159e-15 1.8843260e-13 -[fluid ] 2.7239322e-13 -6.6752159e-15 1.8843260e-13 +[total ] -7.8055618e-14 6.6880529e-14 1.0365667e-13 +[fluid ] -7.8055618e-14 6.6880529e-14 1.0365667e-13 Velocity - x y z -[minimum ] -9.8713261e-04 -7.6033120e-04 -9.4727910e-04 -[maximum ] 1.0215926e-03 7.2472550e-04 8.7886703e-04 +[minimum ] -6.4495937e-04 -8.2567761e-04 -7.2841974e-04 +[maximum ] 7.9770894e-04 7.4888333e-04 9.1277396e-04 -Completed cycle 3400 +Completed cycle 9000 Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.4907845e-07 0.99885952314 1.00191229627 -[phi] -5.9892076e+00 -1.8277611e-04 4.0526490e-01 -9.6120764e-01 9.5210827e-01 +[rho] 32768.00 1.00000000000 1.0559961e-07 0.99906126354 1.00179575984 +[phi] -5.9892076e+00 -1.8277611e-04 5.8861452e-01 -9.9436481e-01 9.9601783e-01 Free energy density - timestep total fluid -[fed] 3500 -5.5257952580e-04 -5.5257952580e-04 +[fed] 9500 -8.3860798056e-04 -8.3860798056e-04 Momentum - x y z -[total ] 2.6305694e-13 -6.6162353e-15 2.0400695e-13 -[fluid ] 2.6305694e-13 -6.6162353e-15 2.0400695e-13 +[total ] -1.1291662e-13 1.6268931e-13 1.5311710e-13 +[fluid ] -1.1291662e-13 1.6268931e-13 1.5311710e-13 Velocity - x y z -[minimum ] -9.7366374e-04 -7.6956770e-04 -9.4056566e-04 -[maximum ] 1.0244738e-03 7.7837299e-04 8.6954592e-04 +[minimum ] -6.2631535e-04 -9.9990504e-04 -6.8582361e-04 +[maximum ] 8.7149203e-04 7.1574939e-04 8.8027954e-04 -Completed cycle 3500 +Completed cycle 9500 +Writing distribution output at step 10000! +Writing phi file at step 10000! +Writing rho/velocity output at step 10000! Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.4550994e-07 0.99886689467 1.00199228518 -[phi] -5.9892076e+00 -1.8277611e-04 4.1167190e-01 -9.6131767e-01 9.5522618e-01 +[rho] 32768.00 1.00000000000 1.1595536e-07 0.99911095563 1.00274736900 +[phi] -5.9892076e+00 -1.8277611e-04 5.9731595e-01 -9.9174620e-01 9.9739837e-01 Free energy density - timestep total fluid -[fed] 3600 -5.6160991815e-04 -5.6160991815e-04 +[fed] 10000 -8.5399505820e-04 -8.5399505820e-04 Momentum - x y z -[total ] 3.0295558e-13 -1.3846563e-14 2.0440247e-13 -[fluid ] 3.0295558e-13 -1.3846563e-14 2.0440247e-13 +[total ] -2.1944599e-13 8.5025736e-14 1.5690227e-13 +[fluid ] -2.1944599e-13 8.5025736e-14 1.5690227e-13 Velocity - x y z -[minimum ] -9.7627892e-04 -7.9065320e-04 -9.2266455e-04 -[maximum ] 1.0137057e-03 8.5106238e-04 8.6382815e-04 - -Completed cycle 3600 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.4224885e-07 0.99887267041 1.00193339317 -[phi] -5.9892076e+00 -1.8277611e-04 4.1782167e-01 -9.5993932e-01 9.5802405e-01 - -Free energy density - timestep total fluid -[fed] 3700 -5.7030517223e-04 -5.7030517223e-04 - -Momentum - x y z -[total ] 2.6881622e-13 -2.4698993e-14 2.2523997e-13 -[fluid ] 2.6881622e-13 -2.4698993e-14 2.2523997e-13 - -Velocity - x y z -[minimum ] -9.9586301e-04 -8.0775523e-04 -9.1639905e-04 -[maximum ] 9.9062030e-04 9.2146853e-04 8.5869874e-04 - -Completed cycle 3700 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.3882076e-07 0.99886071121 1.00217583160 -[phi] -5.9892076e+00 -1.8277611e-04 4.2371270e-01 -9.5967246e-01 9.6049541e-01 - -Free energy density - timestep total fluid -[fed] 3800 -5.7863504929e-04 -5.7863504929e-04 - -Momentum - x y z -[total ] 2.2918126e-13 -2.3338970e-14 2.3475666e-13 -[fluid ] 2.2918126e-13 -2.3338970e-14 2.3475666e-13 - -Velocity - x y z -[minimum ] -1.0013199e-03 -8.1011543e-04 -9.0231714e-04 -[maximum ] 9.6408176e-04 9.7457416e-04 8.3646770e-04 - -Completed cycle 3800 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.3483909e-07 0.99886528900 1.00206048881 -[phi] -5.9892076e+00 -1.8277611e-04 4.2933467e-01 -9.6033089e-01 9.6255914e-01 - -Free energy density - timestep total fluid -[fed] 3900 -5.8655093315e-04 -5.8655093315e-04 - -Momentum - x y z -[total ] 2.3254662e-13 -5.8227728e-14 1.8556337e-13 -[fluid ] 2.3254662e-13 -5.8227728e-14 1.8556337e-13 - -Velocity - x y z -[minimum ] -9.9460579e-04 -7.9559461e-04 -8.7783218e-04 -[maximum ] 9.4319841e-04 1.0134694e-03 8.0724000e-04 - -Completed cycle 3900 -Writing phi file at step 4000! -Writing velocity output at step 4000! - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.3122333e-07 0.99886357464 1.00183815212 -[phi] -5.9892076e+00 -1.8277611e-04 4.3468078e-01 -9.6092085e-01 9.6447995e-01 - -Free energy density - timestep total fluid -[fed] 4000 -5.9406770779e-04 -5.9406770779e-04 - -Momentum - x y z -[total ] 1.9892421e-13 -2.1951191e-14 1.9268268e-13 -[fluid ] 1.9892421e-13 -2.1951191e-14 1.9268268e-13 - -Velocity - x y z -[minimum ] -9.7476646e-04 -7.9646965e-04 -8.5686130e-04 -[maximum ] 9.2862332e-04 1.0382664e-03 7.6923673e-04 - -Completed cycle 4000 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.2876740e-07 0.99884351557 1.00168593641 -[phi] -5.9892076e+00 -1.8277611e-04 4.3977434e-01 -9.6203559e-01 9.6703030e-01 - -Free energy density - timestep total fluid -[fed] 4100 -6.0128847152e-04 -6.0128847152e-04 - -Momentum - x y z -[total ] 2.1061972e-13 -9.6068986e-15 2.2249216e-13 -[fluid ] 2.1061972e-13 -9.6068986e-15 2.2249216e-13 - -Velocity - x y z -[minimum ] -9.4801376e-04 -8.0172997e-04 -8.2518101e-04 -[maximum ] 9.1698257e-04 1.0304225e-03 7.3410692e-04 - -Completed cycle 4100 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.2705821e-07 0.99885978852 1.00200454110 -[phi] -5.9892076e+00 -1.8277611e-04 4.4465925e-01 -9.6423379e-01 9.6915714e-01 - -Free energy density - timestep total fluid -[fed] 4200 -6.0828227333e-04 -6.0828227333e-04 - -Momentum - x y z -[total ] 2.1338487e-13 1.0217521e-14 2.1267710e-13 -[fluid ] 2.1338487e-13 1.0217521e-14 2.1267710e-13 - -Velocity - x y z -[minimum ] -9.3368757e-04 -8.0106728e-04 -7.8398941e-04 -[maximum ] 9.1114694e-04 9.9898848e-04 7.0392153e-04 - -Completed cycle 4200 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.2556747e-07 0.99891675142 1.00211437371 -[phi] -5.9892076e+00 -1.8277611e-04 4.4937845e-01 -9.6657162e-01 9.7071726e-01 - -Free energy density - timestep total fluid -[fed] 4300 -6.1507481459e-04 -6.1507481459e-04 - -Momentum - x y z -[total ] 2.0751109e-13 7.1748163e-15 2.1598348e-13 -[fluid ] 2.0751109e-13 7.1748163e-15 2.1598348e-13 - -Velocity - x y z -[minimum ] -9.1137936e-04 -7.9411351e-04 -7.4706526e-04 -[maximum ] 8.9460779e-04 9.7518127e-04 6.6365371e-04 - -Completed cycle 4300 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.2402180e-07 0.99895883906 1.00201758884 -[phi] -5.9892076e+00 -1.8277611e-04 4.5396780e-01 -9.6840360e-01 9.7179277e-01 - -Free energy density - timestep total fluid -[fed] 4400 -6.2169732216e-04 -6.2169732216e-04 - -Momentum - x y z -[total ] 2.1744759e-13 4.0325382e-14 2.1540755e-13 -[fluid ] 2.1744759e-13 4.0325382e-14 2.1540755e-13 - -Velocity - x y z -[minimum ] -9.1329632e-04 -7.7956479e-04 -7.1505624e-04 -[maximum ] 8.6735228e-04 9.5505581e-04 6.3452630e-04 - -Completed cycle 4400 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.2245017e-07 0.99895760272 1.00182578005 -[phi] -5.9892076e+00 -1.8277611e-04 4.5845272e-01 -9.6980835e-01 9.7250800e-01 - -Free energy density - timestep total fluid -[fed] 4500 -6.2818015585e-04 -6.2818015585e-04 - -Momentum - x y z -[total ] 1.8801627e-13 4.0384363e-14 2.0650148e-13 -[fluid ] 1.8801627e-13 4.0384363e-14 2.0650148e-13 - -Velocity - x y z -[minimum ] -9.1929130e-04 -7.5542037e-04 -6.8538634e-04 -[maximum ] 8.3393075e-04 9.3944716e-04 6.2402757e-04 - -Completed cycle 4500 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.2089187e-07 0.99897281852 1.00162602217 -[phi] -5.9892076e+00 -1.8277611e-04 4.6284426e-01 -9.7089152e-01 9.7461036e-01 - -Free energy density - timestep total fluid -[fed] 4600 -6.3452462819e-04 -6.3452462819e-04 - -Momentum - x y z -[total ] 2.3095068e-13 6.8799133e-14 1.8211474e-13 -[fluid ] 2.3095068e-13 6.8799133e-14 1.8211474e-13 - -Velocity - x y z -[minimum ] -9.2629448e-04 -7.3820236e-04 -6.8483897e-04 -[maximum ] 8.0071471e-04 9.2530850e-04 6.0898780e-04 - -Completed cycle 4600 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1954037e-07 0.99898031696 1.00145998123 -[phi] -5.9892076e+00 -1.8277611e-04 4.6713653e-01 -9.7175838e-01 9.7682632e-01 - -Free energy density - timestep total fluid -[fed] 4700 -6.4070401052e-04 -6.4070401052e-04 - -Momentum - x y z -[total ] 2.4174759e-13 1.0457260e-13 1.8505336e-13 -[fluid ] 2.4174759e-13 1.0457260e-13 1.8505336e-13 - -Velocity - x y z -[minimum ] -9.3096935e-04 -7.4937634e-04 -7.1287911e-04 -[maximum ] 7.9886606e-04 9.0784834e-04 6.0111420e-04 - -Completed cycle 4700 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1882286e-07 0.99899383106 1.00144226380 -[phi] -5.9892076e+00 -1.8277611e-04 4.7131854e-01 -9.7247601e-01 9.7889614e-01 - -Free energy density - timestep total fluid -[fed] 4800 -6.4671154431e-04 -6.4671154431e-04 - -Momentum - x y z -[total ] 2.1695840e-13 1.0247012e-13 1.6521853e-13 -[fluid ] 2.1695840e-13 1.0247012e-13 1.6521853e-13 - -Velocity - x y z -[minimum ] -9.3155057e-04 -7.4764220e-04 -7.6292132e-04 -[maximum ] 8.0263686e-04 8.8719566e-04 6.1369457e-04 - -Completed cycle 4800 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1877355e-07 0.99896921832 1.00174059772 -[phi] -5.9892076e+00 -1.8277611e-04 4.7538568e-01 -9.7306785e-01 9.8079359e-01 - -Free energy density - timestep total fluid -[fed] 4900 -6.5256660086e-04 -6.5256660086e-04 - -Momentum - x y z -[total ] 2.2795307e-13 1.0426382e-13 1.8927915e-13 -[fluid ] 2.2795307e-13 1.0426382e-13 1.8927915e-13 - -Velocity - x y z -[minimum ] -9.2100134e-04 -7.3696988e-04 -8.0931406e-04 -[maximum ] 8.0952614e-04 8.8239399e-04 6.2694070e-04 - -Completed cycle 4900 -Writing phi file at step 5000! -Writing velocity output at step 5000! - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1881698e-07 0.99889323555 1.00187450842 -[phi] -5.9892076e+00 -1.8277611e-04 4.7933845e-01 -9.7406728e-01 9.8253696e-01 - -Free energy density - timestep total fluid -[fed] 5000 -6.5828010521e-04 -6.5828010521e-04 - -Momentum - x y z -[total ] 1.8569521e-13 1.1214987e-13 2.3381297e-13 -[fluid ] 1.8569521e-13 1.1214987e-13 2.3381297e-13 - -Velocity - x y z -[minimum ] -9.0143402e-04 -7.2983090e-04 -8.4081680e-04 -[maximum ] 8.0902548e-04 8.7873874e-04 6.3512283e-04 - -Completed cycle 5000 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1827394e-07 0.99885087488 1.00182029676 -[phi] -5.9892076e+00 -1.8277611e-04 4.8317219e-01 -9.7495583e-01 9.8416532e-01 - -Free energy density - timestep total fluid -[fed] 5100 -6.6382764111e-04 -6.6382764111e-04 - -Momentum - x y z -[total ] 1.9838645e-13 1.0829185e-13 1.9867788e-13 -[fluid ] 1.9838645e-13 1.0829185e-13 1.9867788e-13 - -Velocity - x y z -[minimum ] -8.7667232e-04 -7.3692769e-04 -8.5101086e-04 -[maximum ] 8.0219565e-04 8.6660386e-04 6.3746759e-04 - -Completed cycle 5100 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1698797e-07 0.99885303779 1.00175691762 -[phi] -5.9892076e+00 -1.8277611e-04 4.8687448e-01 -9.7552005e-01 9.8570435e-01 - -Free energy density - timestep total fluid -[fed] 5200 -6.6917203555e-04 -6.6917203555e-04 - -Momentum - x y z -[total ] 2.2169419e-13 7.3975548e-14 1.8730503e-13 -[fluid ] 2.2169419e-13 7.3975548e-14 1.8730503e-13 - -Velocity - x y z -[minimum ] -8.4972983e-04 -7.3891432e-04 -8.6731588e-04 -[maximum ] 7.9090218e-04 8.5096605e-04 6.3439658e-04 - -Completed cycle 5200 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1532656e-07 0.99882934542 1.00168229999 -[phi] -5.9892076e+00 -1.8277611e-04 4.9043091e-01 -9.7667525e-01 9.8714720e-01 - -Free energy density - timestep total fluid -[fed] 5300 -6.7429355544e-04 -6.7429355544e-04 - -Momentum - x y z -[total ] 1.8618787e-13 8.0418311e-14 1.7169252e-13 -[fluid ] 1.8618787e-13 8.0418311e-14 1.7169252e-13 - -Velocity - x y z -[minimum ] -8.2180389e-04 -7.3300320e-04 -8.7961984e-04 -[maximum ] 8.0052955e-04 8.3697347e-04 6.2644181e-04 - -Completed cycle 5300 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1375438e-07 0.99882846582 1.00155549529 -[phi] -5.9892076e+00 -1.8277611e-04 4.9383471e-01 -9.7787938e-01 9.8845497e-01 - -Free energy density - timestep total fluid -[fed] 5400 -6.7920222496e-04 -6.7920222496e-04 - -Momentum - x y z -[total ] 1.6568344e-13 1.0248399e-13 1.1162946e-13 -[fluid ] 1.6568344e-13 1.0248399e-13 1.1162946e-13 - -Velocity - x y z -[minimum ] -7.9812112e-04 -7.2209643e-04 -8.7287723e-04 -[maximum ] 8.1392674e-04 8.2679630e-04 6.1447007e-04 - -Completed cycle 5400 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1250329e-07 0.99886179286 1.00151630863 -[phi] -5.9892076e+00 -1.8277611e-04 4.9708837e-01 -9.7884833e-01 9.8958402e-01 - -Free energy density - timestep total fluid -[fed] 5500 -6.8392653381e-04 -6.8392653381e-04 - -Momentum - x y z -[total ] 1.1741996e-13 1.0647386e-13 1.2993773e-13 -[fluid ] 1.1741996e-13 1.0647386e-13 1.2993773e-13 - -Velocity - x y z -[minimum ] -8.2213503e-04 -7.0830584e-04 -8.4736395e-04 -[maximum ] 8.2549795e-04 8.1944510e-04 5.9949725e-04 - -Completed cycle 5500 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1161248e-07 0.99893643871 1.00148518330 -[phi] -5.9892076e+00 -1.8277611e-04 5.0020138e-01 -9.7958450e-01 9.9051068e-01 - -Free energy density - timestep total fluid -[fed] 5600 -6.8850147400e-04 -6.8850147400e-04 - -Momentum - x y z -[total ] 1.6442403e-13 6.2803929e-14 1.5001195e-13 -[fluid ] 1.6442403e-13 6.2803929e-14 1.5001195e-13 - -Velocity - x y z -[minimum ] -8.4075132e-04 -7.0212255e-04 -8.1536956e-04 -[maximum ] 8.3567873e-04 8.1231527e-04 5.8711097e-04 - -Completed cycle 5600 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1098654e-07 0.99897236584 1.00157146059 -[phi] -5.9892076e+00 -1.8277611e-04 5.0318654e-01 -9.8051476e-01 9.9199165e-01 - -Free energy density - timestep total fluid -[fed] 5700 -6.9295542311e-04 -6.9295542311e-04 - -Momentum - x y z -[total ] 1.4722945e-13 6.8684641e-14 1.2451151e-13 -[fluid ] 1.4722945e-13 6.8684641e-14 1.2451151e-13 - -Velocity - x y z -[minimum ] -8.4593427e-04 -7.0475552e-04 -7.8586595e-04 -[maximum ] 8.4642936e-04 8.1132530e-04 5.9901918e-04 - -Completed cycle 5700 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1056258e-07 0.99896540654 1.00165689324 -[phi] -5.9892076e+00 -1.8277611e-04 5.0605600e-01 -9.8205776e-01 9.9340852e-01 - -Free energy density - timestep total fluid -[fed] 5800 -6.9730189620e-04 -6.9730189620e-04 - -Momentum - x y z -[total ] 1.5173626e-13 3.3226893e-14 1.0627610e-13 -[fluid ] 1.5173626e-13 3.3226893e-14 1.0627610e-13 - -Velocity - x y z -[minimum ] -8.4315615e-04 -7.0670258e-04 -7.5673755e-04 -[maximum ] 8.5782939e-04 8.0826592e-04 6.1186614e-04 - -Completed cycle 5800 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1045573e-07 0.99894217163 1.00182041879 -[phi] -5.9892076e+00 -1.8277611e-04 5.0882043e-01 -9.8352008e-01 9.9460120e-01 - -Free energy density - timestep total fluid -[fed] 5900 -7.0154899700e-04 -7.0154899700e-04 - -Momentum - x y z -[total ] 1.1122700e-13 -3.5017128e-14 1.3543333e-13 -[fluid ] 1.1122700e-13 -3.5017128e-14 1.3543333e-13 - -Velocity - x y z -[minimum ] -8.4616123e-04 -7.0754159e-04 -7.3456956e-04 -[maximum ] 8.7113672e-04 8.0123197e-04 6.2209844e-04 - -Completed cycle 5900 -Writing phi file at step 6000! -Writing velocity output at step 6000! - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1076609e-07 0.99894037489 1.00208778944 -[phi] -5.9892076e+00 -1.8277611e-04 5.1149067e-01 -9.8490788e-01 9.9555033e-01 - -Free energy density - timestep total fluid -[fed] 6000 -7.0571261526e-04 -7.0571261526e-04 - -Momentum - x y z -[total ] 1.4304530e-13 -2.8144154e-14 1.3440291e-13 -[fluid ] 1.4304530e-13 -2.8144154e-14 1.3440291e-13 - -Velocity - x y z -[minimum ] -8.4709557e-04 -7.0717499e-04 -7.3331802e-04 -[maximum ] 8.9175487e-04 8.1115474e-04 6.3780458e-04 - -Completed cycle 6000 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1121398e-07 0.99895338347 1.00227342998 -[phi] -5.9892076e+00 -1.8277611e-04 5.1407788e-01 -9.8622464e-01 9.9622722e-01 - -Free energy density - timestep total fluid -[fed] 6100 -7.0980943393e-04 -7.0980943393e-04 - -Momentum - x y z -[total ] 1.1735751e-13 -3.6932263e-14 1.0242848e-13 -[fluid ] 1.1735751e-13 -3.6932263e-14 1.0242848e-13 - -Velocity - x y z -[minimum ] -8.4528576e-04 -7.0579044e-04 -7.2881594e-04 -[maximum ] 9.1323160e-04 8.2607054e-04 6.4961319e-04 - -Completed cycle 6100 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1148151e-07 0.99897832727 1.00223507832 -[phi] -5.9892076e+00 -1.8277611e-04 5.1659131e-01 -9.8747885e-01 9.9661849e-01 - -Free energy density - timestep total fluid -[fed] 6200 -7.1385365736e-04 -7.1385365736e-04 - -Momentum - x y z -[total ] 6.0045718e-14 2.2301605e-14 1.4784007e-13 -[fluid ] 6.0045718e-14 2.2301605e-14 1.4784007e-13 - -Velocity - x y z -[minimum ] -8.4144322e-04 -7.0469359e-04 -7.2152507e-04 -[maximum ] 9.3539236e-04 8.0928451e-04 6.5921214e-04 - -Completed cycle 6200 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1151883e-07 0.99900916388 1.00206686245 -[phi] -5.9892076e+00 -1.8277611e-04 5.1904149e-01 -9.8868847e-01 9.9673480e-01 - -Free energy density - timestep total fluid -[fed] 6300 -7.1786624559e-04 -7.1786624559e-04 - -Momentum - x y z -[total ] 8.5556562e-14 1.1383255e-14 1.4651128e-13 -[fluid ] 8.5556562e-14 1.1383255e-14 1.4651128e-13 - -Velocity - x y z -[minimum ] -8.3204427e-04 -7.0551546e-04 -7.1403335e-04 -[maximum ] 9.5849442e-04 7.9647170e-04 6.6941483e-04 - -Completed cycle 6300 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1128266e-07 0.99901640963 1.00185784990 -[phi] -5.9892076e+00 -1.8277611e-04 5.2143956e-01 -9.8988268e-01 9.9661381e-01 - -Free energy density - timestep total fluid -[fed] 6400 -7.2185709930e-04 -7.2185709930e-04 - -Momentum - x y z -[total ] 5.3193561e-14 2.2419566e-14 1.7262927e-13 -[fluid ] 5.3193561e-14 2.2419566e-14 1.7262927e-13 - -Velocity - x y z -[minimum ] -8.2147115e-04 -7.0596855e-04 -7.0651851e-04 -[maximum ] 9.8339487e-04 7.7790730e-04 6.8288476e-04 - -Completed cycle 6400 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1084627e-07 0.99898377327 1.00166425147 -[phi] -5.9892076e+00 -1.8277611e-04 5.2379254e-01 -9.9109526e-01 9.9629639e-01 - -Free energy density - timestep total fluid -[fed] 6500 -7.2581938148e-04 -7.2581938148e-04 - -Momentum - x y z -[total ] 3.4795083e-14 4.0793757e-14 2.0064506e-13 -[fluid ] 3.4795083e-14 4.0793757e-14 2.0064506e-13 - -Velocity - x y z -[minimum ] -8.2336157e-04 -7.0720608e-04 -6.9614013e-04 -[maximum ] 1.0109515e-03 7.5302914e-04 7.0049373e-04 - -Completed cycle 6500 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1040085e-07 0.99899534558 1.00167255864 -[phi] -5.9892076e+00 -1.8277611e-04 5.2610302e-01 -9.9235185e-01 9.9580841e-01 - -Free energy density - timestep total fluid -[fed] 6600 -7.2974215204e-04 -7.2974215204e-04 - -Momentum - x y z -[total ] 1.2150003e-14 7.4742296e-14 2.3056210e-13 -[fluid ] 1.2150003e-14 7.4742296e-14 2.3056210e-13 - -Velocity - x y z -[minimum ] -8.2461601e-04 -7.1514377e-04 -6.8463728e-04 -[maximum ] 1.0407215e-03 7.3705369e-04 7.2067010e-04 - -Completed cycle 6600 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1022796e-07 0.99904059936 1.00161884010 -[phi] -5.9892076e+00 -1.8277611e-04 5.2837139e-01 -9.9364396e-01 9.9516623e-01 - -Free energy density - timestep total fluid -[fed] 6700 -7.3362364642e-04 -7.3362364642e-04 - -Momentum - x y z -[total ] -4.3402781e-15 1.2884138e-13 2.2222502e-13 -[fluid ] -4.3402781e-15 1.2884138e-13 2.2222502e-13 - -Velocity - x y z -[minimum ] -8.2369286e-04 -7.1904593e-04 -6.7910907e-04 -[maximum ] 1.0706496e-03 7.3050793e-04 7.4008277e-04 - -Completed cycle 6700 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1058757e-07 0.99904075345 1.00184536738 -[phi] -5.9892076e+00 -1.8277611e-04 5.3060219e-01 -9.9491027e-01 9.9506099e-01 - -Free energy density - timestep total fluid -[fed] 6800 -7.3749174717e-04 -7.3749174717e-04 - -Momentum - x y z -[total ] -6.2144734e-14 1.2908771e-13 1.9770297e-13 -[fluid ] -6.2144734e-14 1.2908771e-13 1.9770297e-13 - -Velocity - x y z -[minimum ] -8.1946292e-04 -7.2315050e-04 -6.7767174e-04 -[maximum ] 1.0983487e-03 7.2220875e-04 7.5708733e-04 - -Completed cycle 6800 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1133268e-07 0.99903494247 1.00189496476 -[phi] -5.9892076e+00 -1.8277611e-04 5.3280571e-01 -9.9604965e-01 9.9545548e-01 - -Free energy density - timestep total fluid -[fed] 6900 -7.4138381873e-04 -7.4138381873e-04 - -Momentum - x y z -[total ] -1.0977330e-13 1.3334819e-13 2.2782123e-13 -[fluid ] -1.0977330e-13 1.3334819e-13 2.2782123e-13 - -Velocity - x y z -[minimum ] -8.1174889e-04 -7.2684792e-04 -6.7302108e-04 -[maximum ] 1.1191141e-03 7.2025669e-04 7.7165852e-04 - -Completed cycle 6900 -Writing phi file at step 7000! -Writing velocity output at step 7000! - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1217043e-07 0.99899239320 1.00183086300 -[phi] -5.9892076e+00 -1.8277611e-04 5.3499311e-01 -9.9696624e-01 9.9575691e-01 - -Free energy density - timestep total fluid -[fed] 7000 -7.4531744328e-04 -7.4531744328e-04 - -Momentum - x y z -[total ] -1.2449070e-13 1.1047413e-13 2.1943211e-13 -[fluid ] -1.2449070e-13 1.1047413e-13 2.1943211e-13 - -Velocity - x y z -[minimum ] -8.0170847e-04 -7.2572061e-04 -6.7040516e-04 -[maximum ] 1.1229630e-03 7.1666215e-04 7.8619143e-04 - -Completed cycle 7000 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1308083e-07 0.99898799072 1.00187754362 -[phi] -5.9892076e+00 -1.8277611e-04 5.3717462e-01 -9.9761442e-01 9.9595345e-01 - -Free energy density - timestep total fluid -[fed] 7100 -7.4930197673e-04 -7.4930197673e-04 - -Momentum - x y z -[total ] -1.1435297e-13 1.3564497e-13 2.1664615e-13 -[fluid ] -1.1435297e-13 1.3564497e-13 2.1664615e-13 - -Velocity - x y z -[minimum ] -7.9142336e-04 -7.2012375e-04 -6.7968814e-04 -[maximum ] 1.1052106e-03 7.2155136e-04 7.9498949e-04 - -Completed cycle 7100 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1407121e-07 0.99898020091 1.00180699588 -[phi] -5.9892076e+00 -1.8277611e-04 5.3936141e-01 -9.9800086e-01 9.9603545e-01 - -Free energy density - timestep total fluid -[fed] 7200 -7.5334297806e-04 -7.5334297806e-04 - -Momentum - x y z -[total ] -1.2037940e-13 1.6938187e-13 2.3266111e-13 -[fluid ] -1.2037940e-13 1.6938187e-13 2.3266111e-13 - -Velocity - x y z -[minimum ] -7.8788069e-04 -7.2248780e-04 -6.8807574e-04 -[maximum ] 1.0711998e-03 7.3196110e-04 8.0037954e-04 - -Completed cycle 7200 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1523475e-07 0.99895745319 1.00165517902 -[phi] -5.9892076e+00 -1.8277611e-04 5.4156233e-01 -9.9816306e-01 9.9600457e-01 - -Free energy density - timestep total fluid -[fed] 7300 -7.5743502496e-04 -7.5743502496e-04 - -Momentum - x y z -[total ] -1.1567830e-13 1.2500764e-13 1.9442781e-13 -[fluid ] -1.1567830e-13 1.2500764e-13 1.9442781e-13 - -Velocity - x y z -[minimum ] -7.8212896e-04 -7.2610133e-04 -6.9621146e-04 -[maximum ] 1.0372762e-03 7.4433453e-04 8.0968361e-04 - -Completed cycle 7300 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1671767e-07 0.99895399147 1.00203809286 -[phi] -5.9892076e+00 -1.8277611e-04 5.4378402e-01 -9.9814154e-01 9.9604854e-01 - -Free energy density - timestep total fluid -[fed] 7400 -7.6156877786e-04 -7.6156877786e-04 - -Momentum - x y z -[total ] -9.9937419e-14 1.1715628e-13 1.6189480e-13 -[fluid ] -9.9937419e-14 1.1715628e-13 1.6189480e-13 - -Velocity - x y z -[minimum ] -7.8172958e-04 -7.2882879e-04 -7.2222331e-04 -[maximum ] 1.0187738e-03 7.5698762e-04 8.2642794e-04 - -Completed cycle 7400 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1807683e-07 0.99896815919 1.00221569442 -[phi] -5.9892076e+00 -1.8277611e-04 5.4603071e-01 -9.9796395e-01 9.9605037e-01 - -Free energy density - timestep total fluid -[fed] 7500 -7.6572970465e-04 -7.6572970465e-04 - -Momentum - x y z -[total ] -1.2126064e-13 8.5095125e-14 1.4329163e-13 -[fluid ] -1.2126064e-13 8.5095125e-14 1.4329163e-13 - -Velocity - x y z -[minimum ] -7.8584377e-04 -7.3065420e-04 -7.5341686e-04 -[maximum ] 9.9198296e-04 7.7251001e-04 8.4368548e-04 - -Completed cycle 7500 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1837500e-07 0.99900396637 1.00215838451 -[phi] -5.9892076e+00 -1.8277611e-04 5.4829605e-01 -9.9763747e-01 9.9597996e-01 - -Free energy density - timestep total fluid -[fed] 7600 -7.6987187907e-04 -7.6987187907e-04 - -Momentum - x y z -[total ] -1.0206419e-13 6.0333682e-14 1.5123666e-13 -[fluid ] -1.0206419e-13 6.0333682e-14 1.5123666e-13 - -Velocity - x y z -[minimum ] -7.8747662e-04 -7.3151322e-04 -7.7568442e-04 -[maximum ] 9.5584850e-04 7.9471494e-04 8.5853875e-04 - -Completed cycle 7600 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1761317e-07 0.99903173518 1.00196991662 -[phi] -5.9892076e+00 -1.8277611e-04 5.5056319e-01 -9.9716421e-01 9.9585746e-01 - -Free energy density - timestep total fluid -[fed] 7700 -7.7395401887e-04 -7.7395401887e-04 - -Momentum - x y z -[total ] -6.1433497e-14 8.7100466e-14 1.4827722e-13 -[fluid ] -6.1433497e-14 8.7100466e-14 1.4827722e-13 - -Velocity - x y z -[minimum ] -7.8546216e-04 -7.3447149e-04 -7.8730448e-04 -[maximum ] 9.1169722e-04 8.1920927e-04 8.6722532e-04 - -Completed cycle 7700 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1640822e-07 0.99899761579 1.00175270272 -[phi] -5.9892076e+00 -1.8277611e-04 5.5282203e-01 -9.9654741e-01 9.9570067e-01 - -Free energy density - timestep total fluid -[fed] 7800 -7.7798227665e-04 -7.7798227665e-04 - -Momentum - x y z -[total ] -7.6987028e-14 8.1337714e-14 1.4568555e-13 -[fluid ] -7.6987028e-14 8.1337714e-14 1.4568555e-13 - -Velocity - x y z -[minimum ] -7.7932239e-04 -7.4553930e-04 -7.9874951e-04 -[maximum ] 8.6520133e-04 8.3329245e-04 8.7289014e-04 - -Completed cycle 7800 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1507548e-07 0.99899061012 1.00156138190 -[phi] -5.9892076e+00 -1.8277611e-04 5.5507312e-01 -9.9593554e-01 9.9552356e-01 - -Free energy density - timestep total fluid -[fed] 7900 -7.8198024118e-04 -7.8198024118e-04 - -Momentum - x y z -[total ] -8.9837859e-14 4.8620830e-14 1.2380721e-13 -[fluid ] -8.9837859e-14 4.8620830e-14 1.2380721e-13 - -Velocity - x y z -[minimum ] -7.6935654e-04 -7.5443734e-04 -8.0366148e-04 -[maximum ] 8.2491021e-04 8.3808624e-04 8.7921970e-04 - -Completed cycle 7900 -Writing phi file at step 8000! -Writing velocity output at step 8000! - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1368022e-07 0.99901572281 1.00145313471 -[phi] -5.9892076e+00 -1.8277611e-04 5.5731993e-01 -9.9567927e-01 9.9533746e-01 - -Free energy density - timestep total fluid -[fed] 8000 -7.8595081742e-04 -7.8595081742e-04 - -Momentum - x y z -[total ] -4.3628295e-14 2.8720082e-14 1.0567588e-13 -[fluid ] -4.3628295e-14 2.8720082e-14 1.0567588e-13 - -Velocity - x y z -[minimum ] -7.6264147e-04 -7.6103495e-04 -8.0149803e-04 -[maximum ] 7.9192302e-04 8.3626939e-04 8.8450782e-04 - -Completed cycle 8000 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1234074e-07 0.99902811615 1.00156722930 -[phi] -5.9892076e+00 -1.8277611e-04 5.5956374e-01 -9.9533306e-01 9.9515123e-01 - -Free energy density - timestep total fluid -[fed] 8100 -7.8987950230e-04 -7.8987950230e-04 - -Momentum - x y z -[total ] -1.6858043e-14 5.2440691e-14 1.1581361e-13 -[fluid ] -1.6858043e-14 5.2440691e-14 1.1581361e-13 - -Velocity - x y z -[minimum ] -7.5789317e-04 -7.6524721e-04 -7.9356831e-04 -[maximum ] 7.6494656e-04 8.3645259e-04 8.8832292e-04 - -Completed cycle 8100 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1109720e-07 0.99902556421 1.00149748229 -[phi] -5.9892076e+00 -1.8277611e-04 5.6180130e-01 -9.9491953e-01 9.9497064e-01 - -Free energy density - timestep total fluid -[fed] 8200 -7.9374963577e-04 -7.9374963577e-04 - -Momentum - x y z -[total ] -3.7948811e-14 1.6948248e-14 1.2415416e-13 -[fluid ] -3.7948811e-14 1.6948248e-14 1.2415416e-13 - -Velocity - x y z -[minimum ] -7.4923006e-04 -7.6716283e-04 -7.8177069e-04 -[maximum ] 7.4561450e-04 8.5943908e-04 8.8951876e-04 - -Completed cycle 8200 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1000665e-07 0.99902524954 1.00143157663 -[phi] -5.9892076e+00 -1.8277611e-04 5.6402503e-01 -9.9454227e-01 9.9485565e-01 - -Free energy density - timestep total fluid -[fed] 8300 -7.9755148706e-04 -7.9755148706e-04 - -Momentum - x y z -[total ] -3.5690201e-14 3.7615744e-14 1.2459131e-13 -[fluid ] -3.5690201e-14 3.7615744e-14 1.2459131e-13 - -Velocity - x y z -[minimum ] -7.3839413e-04 -7.6707062e-04 -7.6798312e-04 -[maximum ] 7.2754483e-04 8.7033021e-04 8.8827934e-04 - -Completed cycle 8300 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.0915719e-07 0.99901876733 1.00137625434 -[phi] -5.9892076e+00 -1.8277611e-04 5.6623113e-01 -9.9410641e-01 9.9490914e-01 - -Free energy density - timestep total fluid -[fed] 8400 -8.0129742135e-04 -8.0129742135e-04 - -Momentum - x y z -[total ] -8.2728963e-14 2.3401420e-14 1.2650991e-13 -[fluid ] -8.2728963e-14 2.3401420e-14 1.2650991e-13 - -Velocity - x y z -[minimum ] -7.3370095e-04 -7.6542862e-04 -7.6045611e-04 -[maximum ] 7.0551971e-04 8.8040619e-04 8.9052074e-04 - -Completed cycle 8400 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.0847465e-07 0.99899940362 1.00139971215 -[phi] -5.9892076e+00 -1.8277611e-04 5.6842250e-01 -9.9361592e-01 9.9495612e-01 - -Free energy density - timestep total fluid -[fed] 8500 -8.0501259763e-04 -8.0501259763e-04 - -Momentum - x y z -[total ] -1.1223661e-13 4.2670728e-14 1.3529455e-13 -[fluid ] -1.1223661e-13 4.2670728e-14 1.3529455e-13 - -Velocity - x y z -[minimum ] -7.2344387e-04 -7.6275390e-04 -7.5475345e-04 -[maximum ] 6.9129608e-04 8.7755944e-04 8.9323749e-04 - -Completed cycle 8500 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.0783685e-07 0.99899031345 1.00151210915 -[phi] -5.9892076e+00 -1.8277611e-04 5.7060258e-01 -9.9306844e-01 9.9499819e-01 - -Free energy density - timestep total fluid -[fed] 8600 -8.0870793886e-04 -8.0870793886e-04 - -Momentum - x y z -[total ] -1.2350190e-13 6.3161282e-14 1.1849202e-13 -[fluid ] -1.2350190e-13 6.3161282e-14 1.1849202e-13 - -Velocity - x y z -[minimum ] -7.0820369e-04 -7.7146775e-04 -7.4943049e-04 -[maximum ] 7.1853298e-04 8.5849542e-04 9.0575437e-04 - -Completed cycle 8600 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.0717245e-07 0.99899400283 1.00155748094 -[phi] -5.9892076e+00 -1.8277611e-04 5.7277057e-01 -9.9245564e-01 9.9503929e-01 - -Free energy density - timestep total fluid -[fed] 8700 -8.1237290353e-04 -8.1237290353e-04 - -Momentum - x y z -[total ] -9.8893116e-14 1.1114720e-13 1.1555687e-13 -[fluid ] -9.8893116e-14 1.1114720e-13 1.1555687e-13 - -Velocity - x y z -[minimum ] -6.8988652e-04 -7.8197513e-04 -7.4370669e-04 -[maximum ] 7.4386671e-04 8.2957745e-04 9.1675204e-04 - -Completed cycle 8700 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.0638883e-07 0.99899884002 1.00154726589 -[phi] -5.9892076e+00 -1.8277611e-04 5.7491762e-01 -9.9176833e-01 9.9508333e-01 - -Free energy density - timestep total fluid -[fed] 8800 -8.1597345547e-04 -8.1597345547e-04 - -Momentum - x y z -[total ] -1.0135989e-13 1.0841328e-13 1.0930146e-13 -[fluid ] -1.0135989e-13 1.0841328e-13 1.0930146e-13 - -Velocity - x y z -[minimum ] -6.7071073e-04 -7.9407470e-04 -7.3671334e-04 -[maximum ] 7.6434501e-04 7.9990066e-04 9.1859799e-04 - -Completed cycle 8800 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.0552985e-07 0.99901203624 1.00147462605 -[phi] -5.9892076e+00 -1.8277611e-04 5.7703033e-01 -9.9161911e-01 9.9513278e-01 - -Free energy density - timestep total fluid -[fed] 8900 -8.1947951561e-04 -8.1947951561e-04 - -Momentum - x y z -[total ] -6.9996092e-14 8.9071112e-14 1.0633161e-13 -[fluid ] -6.9996092e-14 8.9071112e-14 1.0633161e-13 - -Velocity - x y z -[minimum ] -6.5903188e-04 -8.0839659e-04 -7.3096442e-04 -[maximum ] 7.8144397e-04 7.7351185e-04 9.1529459e-04 - -Completed cycle 8900 -Writing phi file at step 9000! -Writing velocity output at step 9000! - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.0467953e-07 0.99903220711 1.00146282247 -[phi] -5.9892076e+00 -1.8277611e-04 5.7909691e-01 -9.9227756e-01 9.9518788e-01 - -Free energy density - timestep total fluid -[fed] 9000 -8.2287647705e-04 -8.2287647705e-04 - -Momentum - x y z -[total ] -7.8118068e-14 6.6759098e-14 1.0364973e-13 -[fluid ] -7.8118068e-14 6.6759098e-14 1.0364973e-13 - -Velocity - x y z -[minimum ] -6.4495937e-04 -8.2567761e-04 -7.2841974e-04 -[maximum ] 7.9770894e-04 7.4888333e-04 9.1277396e-04 - -Completed cycle 9000 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.0401151e-07 0.99905788673 1.00151887676 -[phi] -5.9892076e+00 -1.8277611e-04 5.8110843e-01 -9.9296434e-01 9.9524799e-01 - -Free energy density - timestep total fluid -[fed] 9100 -8.2616253867e-04 -8.2616253867e-04 - -Momentum - x y z -[total ] -7.7896023e-14 6.4663552e-14 8.5091656e-14 -[fluid ] -7.7896023e-14 6.4663552e-14 8.5091656e-14 - -Velocity - x y z -[minimum ] -6.2906105e-04 -8.5011866e-04 -7.2561621e-04 -[maximum ] 8.1405920e-04 7.4002049e-04 9.0326334e-04 - -Completed cycle 9100 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.0372185e-07 0.99908039910 1.00155927298 -[phi] -5.9892076e+00 -1.8277611e-04 5.8306174e-01 -9.9355670e-01 9.9531696e-01 - -Free energy density - timestep total fluid -[fed] 9200 -8.2935618367e-04 -8.2935618367e-04 - -Momentum - x y z -[total ] -4.9970444e-14 8.2767126e-14 1.1077597e-13 -[fluid ] -4.9970444e-14 8.2767126e-14 1.1077597e-13 - -Velocity - x y z -[minimum ] -6.2122356e-04 -8.8121278e-04 -7.2109381e-04 -[maximum ] 8.2840245e-04 7.3205821e-04 8.9066925e-04 - -Completed cycle 9200 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.0388821e-07 0.99907705781 1.00158871036 -[phi] -5.9892076e+00 -1.8277611e-04 5.8495981e-01 -9.9401425e-01 9.9550604e-01 - -Free energy density - timestep total fluid -[fed] 9300 -8.3248300181e-04 -8.3248300181e-04 - -Momentum - x y z -[total ] -4.9016347e-14 1.0072498e-13 1.2430335e-13 -[fluid ] -4.9016347e-14 1.0072498e-13 1.2430335e-13 - -Velocity - x y z -[minimum ] -6.0956654e-04 -9.1665049e-04 -7.1353905e-04 -[maximum ] 8.3736863e-04 7.1944775e-04 8.8511924e-04 - -Completed cycle 9300 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.0452053e-07 0.99907409930 1.00169077732 -[phi] -5.9892076e+00 -1.8277611e-04 5.8680856e-01 -9.9429721e-01 9.9573388e-01 - -Free energy density - timestep total fluid -[fed] 9400 -8.3556215469e-04 -8.3556215469e-04 - -Momentum - x y z -[total ] -4.6972842e-14 1.5321078e-13 1.5682594e-13 -[fluid ] -4.6972842e-14 1.5321078e-13 1.5682594e-13 - -Velocity - x y z -[minimum ] -5.9448804e-04 -9.5619302e-04 -7.0195213e-04 -[maximum ] 8.5203877e-04 7.0940296e-04 8.8403322e-04 - -Completed cycle 9400 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.0559961e-07 0.99906126354 1.00179575984 -[phi] -5.9892076e+00 -1.8277611e-04 5.8861452e-01 -9.9436481e-01 9.9601783e-01 - -Free energy density - timestep total fluid -[fed] 9500 -8.3860798056e-04 -8.3860798056e-04 - -Momentum - x y z -[total ] -1.1226090e-13 1.6231114e-13 1.5262791e-13 -[fluid ] -1.1226090e-13 1.6231114e-13 1.5262791e-13 - -Velocity - x y z -[minimum ] -6.2631535e-04 -9.9990504e-04 -6.8582361e-04 -[maximum ] 8.7149203e-04 7.1574939e-04 8.8027954e-04 - -Completed cycle 9500 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.0712909e-07 0.99905500291 1.00188685122 -[phi] -5.9892076e+00 -1.8277611e-04 5.9038515e-01 -9.9418449e-01 9.9636205e-01 - -Free energy density - timestep total fluid -[fed] 9600 -8.4163812744e-04 -8.4163812744e-04 - -Momentum - x y z -[total ] -9.8133307e-14 1.0275461e-13 1.6339360e-13 -[fluid ] -9.8133307e-14 1.0275461e-13 1.6339360e-13 - -Velocity - x y z -[minimum ] -6.4378469e-04 -1.0482407e-03 -6.7496878e-04 -[maximum ] 8.8261274e-04 7.1818850e-04 8.7230391e-04 - -Completed cycle 9600 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.0910492e-07 0.99906140512 1.00202844923 -[phi] -5.9892076e+00 -1.8277611e-04 5.9212999e-01 -9.9376125e-01 9.9673551e-01 - -Free energy density - timestep total fluid -[fed] 9700 -8.4467624785e-04 -8.4467624785e-04 - -Momentum - x y z -[total ] -1.5886251e-13 1.3459373e-13 1.7739976e-13 -[fluid ] -1.5886251e-13 1.3459373e-13 1.7739976e-13 - -Velocity - x y z -[minimum ] -6.3985227e-04 -1.1011704e-03 -6.6898585e-04 -[maximum ] 8.8958912e-04 7.1514177e-04 8.6494082e-04 - -Completed cycle 9700 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1149351e-07 0.99907868514 1.00242566335 -[phi] -5.9892076e+00 -1.8277611e-04 5.9385955e-01 -9.9313502e-01 9.9707358e-01 - -Free energy density - timestep total fluid -[fed] 9800 -8.4774207100e-04 -8.4774207100e-04 - -Momentum - x y z -[total ] -1.9828236e-13 1.1137966e-13 1.7126231e-13 -[fluid ] -1.9828236e-13 1.1137966e-13 1.7126231e-13 - -Velocity - x y z -[minimum ] -6.1950455e-04 -1.1561307e-03 -6.6385462e-04 -[maximum ] 8.9649353e-04 7.0528462e-04 8.5974716e-04 - -Completed cycle 9800 - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1404064e-07 0.99910460953 1.00266023194 -[phi] -5.9892076e+00 -1.8277611e-04 5.9558559e-01 -9.9236566e-01 9.9730945e-01 - -Free energy density - timestep total fluid -[fed] 9900 -8.5085111878e-04 -8.5085111878e-04 - -Momentum - x y z -[total ] -2.1282975e-13 1.2553153e-13 1.6732449e-13 -[fluid ] -2.1282975e-13 1.2553153e-13 1.6732449e-13 - -Velocity - x y z -[minimum ] -5.9822672e-04 -1.2050207e-03 -6.6125896e-04 -[maximum ] 9.0251580e-04 6.8800372e-04 8.4879996e-04 - -Completed cycle 9900 -Writing phi file at step 10000! -Writing velocity output at step 10000! - -Scalars - total mean variance min max -[rho] 32768.00 1.00000000000 1.1595536e-07 0.99911095563 1.00274736900 -[phi] -5.9892076e+00 -1.8277611e-04 5.9731595e-01 -9.9174620e-01 9.9739837e-01 - -Free energy density - timestep total fluid -[fed] 10000 -8.5399505820e-04 -8.5399505820e-04 - -Momentum - x y z -[total ] -2.1913721e-13 8.4966756e-14 1.5647553e-13 -[fluid ] -2.1913721e-13 8.4966756e-14 1.5647553e-13 - -Velocity - x y z -[minimum ] -5.8662322e-04 -1.2361346e-03 -6.5612134e-04 -[maximum ] 9.0382317e-04 7.0361355e-04 8.3541062e-04 +[minimum ] -5.8662322e-04 -1.2361346e-03 -6.5612134e-04 +[maximum ] 9.0382317e-04 7.0361355e-04 8.3541062e-04 Completed cycle 10000 -Writing phi file at step 10000! -Writing velocity output at step 10000! Timer resolution: 1e-06 second Timer statistics Section: tmin tmax total - Total: 114.180 114.183 114.181 114.180596 (1 call) - Time step loop: 0.009 0.025 113.333 0.011333 (10000 calls) - Propagation: 0.000 0.004 8.376 0.000838 (10000 calls) - Propagtn (krnl) : 0.000 0.004 8.217 0.000822 (10000 calls) - Collision: 0.000 0.002 8.508 0.000851 (10000 calls) - Collision (krnl) : 0.000 0.002 8.391 0.000839 (10000 calls) - Lattice halos: 0.001 0.008 21.612 0.002161 (10000 calls) - phi gradients: 0.001 0.009 22.505 0.002250 (10000 calls) - phi halos: 0.001 0.008 17.626 0.001763 (10000 calls) - BBL: 0.000 0.001 0.024 0.000002 (10000 calls) - Force calculation: 0.000 0.002 8.851 0.000885 (10000 calls) - Phi force (krnl) : 0.000 0.002 4.629 0.000463 (10000 calls) - phi update: 0.002 0.014 34.963 0.003496 (10000 calls) - Advectn (krnl) : 0.000 0.006 3.704 0.000370 (10000 calls) - Advectn BCS (krnl) : 0.000 0.010 3.178 0.000318 (10000 calls) - Free1: 0.000 0.014 0.801 0.000080 (10000 calls) -Warning: key/value present in input but not used: -(Line 67): boundary_walls_on -Warning: key/value present in input but not used: -(Line 44): C -End time: Mon Jan 31 12:21:37 2022 + Total: 115.875 115.875 115.875 115.875126 (1 call) + Time step loop: 0.009 0.039 115.332 0.011533 (10000 calls) + Propagation: 0.001 0.006 9.455 0.000946 (10000 calls) + Propagtn (krnl) : 0.001 0.006 9.391 0.000939 (10000 calls) + Collision: 0.003 0.016 40.850 0.004085 (10000 calls) + Collision (krnl) : 0.003 0.016 40.756 0.004076 (10000 calls) + Lattice halos: 0.001 0.004 7.288 0.000729 (10000 calls) + phi gradients: 0.001 0.006 13.647 0.001365 (10000 calls) + phi halos: 0.000 0.002 4.657 0.000466 (10000 calls) + BBL: 0.000 0.000 0.011 0.000001 (10000 calls) + Force calculation: 0.001 0.006 14.466 0.001447 (10000 calls) + Phi force (krnl) : 0.001 0.004 10.636 0.001064 (10000 calls) + phi update: 0.002 0.010 27.739 0.002774 (10000 calls) + Advectn (krnl) : 0.000 0.003 5.793 0.000579 (10000 calls) + Advectn BCS (krnl) : 0.000 0.003 5.535 0.000553 (10000 calls) + Free1: 0.000 0.039 0.503 0.000050 (10000 calls) +End time: Wed Jan 25 14:07:07 2023 Ludwig finished normally. diff --git a/docs/tutorial/test2/vel-00010000.001-001 b/docs/tutorial/test2/vel-000010000.001-001 similarity index 100% rename from docs/tutorial/test2/vel-00010000.001-001 rename to docs/tutorial/test2/vel-000010000.001-001 diff --git a/docs/tutorial/test2/vel-00010000.vtk b/docs/tutorial/test2/vel-000010000.vtk similarity index 100% rename from docs/tutorial/test2/vel-00010000.vtk rename to docs/tutorial/test2/vel-000010000.vtk diff --git a/docs/tutorial/test2/vel.001-001.meta b/docs/tutorial/test2/vel.001-001.meta deleted file mode 100644 index 2218fe04a..000000000 --- a/docs/tutorial/test2/vel.001-001.meta +++ /dev/null @@ -1,20 +0,0 @@ -Metadata for file set prefix: vel -Data description: Velocity field -Data size per site (bytes): 24 -is_bigendian(): 0 -Number of processors: 8 -Cartesian communicator topology: 2 2 2 -Total system size: 32 32 32 -Lees-Edwards planes: 0 -Lees-Edwards plane speed 0.00000000000000 -Number of I/O groups (files): 1 -I/O communicator topology: 1 1 1 -Write order: - 0 0 0 0 16 16 16 0 0 0 - 1 0 0 1 16 16 16 0 0 16 - 2 0 1 0 16 16 16 0 16 0 - 3 0 1 1 16 16 16 0 16 16 - 4 1 0 0 16 16 16 16 0 0 - 5 1 0 1 16 16 16 16 0 16 - 6 1 1 0 16 16 16 16 16 0 - 7 1 1 1 16 16 16 16 16 16 From 31b37e3beb9028b4193257bc1d779304d55e78f4 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 25 Jan 2023 14:11:42 +0000 Subject: [PATCH 243/244] Add comment --- src/phi_stats.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/phi_stats.c b/src/phi_stats.c index 12df44b8c..4d742a2d1 100644 --- a/src/phi_stats.c +++ b/src/phi_stats.c @@ -11,7 +11,8 @@ * floating point sum. * * There is also a version which adds a correction for BBL - * (specifically for the case of binary fluid). + * (specifically for the case of binary fluid). This might + * be updated and relocated. * * * Edinburgh Soft Matter and Statistical Physics Group and From 440392f43666a471789d878c5d5602c98a0736fc Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 25 Jan 2023 14:11:57 +0000 Subject: [PATCH 244/244] Version 0.19.0 --- CHANGES.md | 10 ++++++++-- version.h | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 2195d372b..6194290a3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,10 +3,13 @@ version 0.19.0 +- There has been a significant change to the way that i/o is undertaken. + See https://ludwig.epcc.ed.ac.uk/outputs/fluid.html + It is the intension to replace completely the 'old' I/O mechanism + by release v0.21.0. - The extract_colloids.c utility has been updated so that it takes only one comand line argument for new I/O metadata. -- LTGM.com analysis has been retired as the service is closing. The - two outstanding recommendations are covered by CodeQL notes. + - Input associated with the choice of force arising from the free energy sector has been made more general. Specifically, input keys `fd_force_divergence` and `fe_use_stress_relaxation` are replaced. @@ -22,6 +25,9 @@ version 0.19.0 - The LaTeX tutorials document has been replaced by html tutorials at https://ludwig.epcc.ed.ac.uk/ to reflect new I/O and to add a number of new topics. +- LTGM.com analysis has been retired as the service has closed. The + two outstanding recommendations are covered by CodeQL notes. +- Various minor updates. version 0.18.0 diff --git a/version.h b/version.h index 791a2701c..ff1f27c8f 100644 --- a/version.h +++ b/version.h @@ -5,7 +5,7 @@ * The version is MAJOR.MINOR.PATCH * See, e.g., https://apr.apache.org/versioning.html * - * (c) 2014-2022 The University of Edinburgh + * (c) 2014-2023 The University of Edinburgh * *****************************************************************************/