From 0b2edad5fdb4b5918d4e8c316d451a4ca02ce4b1 Mon Sep 17 00:00:00 2001 From: not populated Date: Fri, 27 Jan 2023 17:25:04 +0000 Subject: [PATCH 01/97] Fix for first touch and message --- src/model.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/model.c b/src/model.c index 4ff5e8b0..f75e1ba4 100644 --- a/src/model.c +++ b/src/model.c @@ -10,7 +10,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) @@ -113,6 +113,7 @@ int lb_data_create(pe_t * pe, cs_t * cs, const lb_data_options_t * options, if (obj->fprime == NULL) pe_fatal(pe, "malloc(lb->fprime) failed\n"); if (options->usefirsttouch) { lb_data_touch(obj); + pe_info(pe, "Host data: first touch\n"); } else { memset(obj->f, 0, sz); @@ -509,9 +510,11 @@ __host__ void lb_data_touch_kernel(cs_limits_t lim, lb_t * lb) { int kc = lim.kmin + (ik % stry)/strz; int index = cs_index(lb->cs, ic, jc, kc); for (int p = 0; p < lb->nvel; p++) { - int lindex = LB_ADDR(lb->nsite, lb->ndist, lb->nvel, index, 1, p); - lb->f[lindex] = 0.0; - lb->fprime[lindex] = 0.0; + for (int n = 0; n < lb->ndist; n++) { + int lindex = LB_ADDR(lb->nsite, lb->ndist, lb->nvel, index, n, p); + lb->f[lindex] = 0.0; + lb->fprime[lindex] = 0.0; + } } } From 6e08367531f2e9a7ff6bd3f62f72c04bd1ccb374 Mon Sep 17 00:00:00 2001 From: not populated Date: Fri, 27 Jan 2023 17:26:23 +0000 Subject: [PATCH 02/97] Add option for first touch --- src/hydro_rt.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/hydro_rt.c b/src/hydro_rt.c index fed227c0..86228594 100644 --- a/src/hydro_rt.c +++ b/src/hydro_rt.c @@ -5,7 +5,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2018-2022 The University of Edinburgh + * (c) 2018-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -104,6 +104,16 @@ static int hydro_do_init(pe_t * pe, rt_t * rt, cs_t * cs, lees_edw_t * le, opts = hydro_options_haloscheme(haloscheme); } + /* First touch option (all or nothing) */ + + if (rt_switch(rt, "hydro_data_use_first_touch")) { + opts.rho.usefirsttouch = 1; + opts.u.usefirsttouch = 1; + opts.force.usefirsttouch = 1; + opts.eta.usefirsttouch = 1; + pe_info(pe, "Hydro data: first touch\n"); + } + /* 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); From 40040ad859d88a7d46c6e4396513e878035fa3c6 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 31 Jan 2023 10:48:19 +0000 Subject: [PATCH 03/97] Add first touch option --- CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 6194290a..79edc828 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,11 @@ ### Changes +version 0.20.0 + +- An extra "first touch" option has been added. For details, see + https://ludwig.epcc.ed.ac.uk/inputs/parallel.html + version 0.19.0 - There has been a significant change to the way that i/o is undertaken. From fe3e5ff3bc397677ac7d728417a4bf668e68a10f Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 6 Feb 2023 12:27:56 +0000 Subject: [PATCH 04/97] Allow time zero for gradient at time zero --- src/leesedwards.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/leesedwards.c b/src/leesedwards.c index c986e727..d0a98f5a 100644 --- a/src/leesedwards.c +++ b/src/leesedwards.c @@ -872,6 +872,7 @@ int lees_edw_buffer_displacement(lees_edw_t * le, int ib, double t, double * dy) assert(le); assert(ib >= 0 && ib < le->param->nxbuffer); + if (t < 0.0) t = 0.0; tle = t - le->param->time0; assert(tle >= 0.0); From 95c03aaf3882348ec28b3e4217aa040c5cf8ced4 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 6 Feb 2023 13:09:23 +0000 Subject: [PATCH 05/97] Refactor fe summary and add at time zero --- src/ludwig.c | 185 +++++++++++++++++++++++++++------------------------ 1 file changed, 99 insertions(+), 86 deletions(-) diff --git a/src/ludwig.c b/src/ludwig.c index 12dd4abe..025288c3 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -182,6 +182,7 @@ struct ludwig_s { static int ludwig_rt(ludwig_t * ludwig); static int ludwig_report_momentum(ludwig_t * ludwig); +static int ludwig_report_statistics(ludwig_t * ludwig, int itimestep); static int ludwig_colloids_update(ludwig_t * ludwig); static int ludwig_colloids_update_low_freq(ludwig_t * ludwig); @@ -520,29 +521,7 @@ void ludwig_run(const char * inputfile) { pe_info(ludwig->pe, "Initial conditions.\n"); wall_is_pm(ludwig->wall, &is_porous_media); - stats_distribution_print(ludwig->lb, ludwig->map); - - lb_ndist(ludwig->lb, &im); - - if (im == 2) { - phi_lb_to_field(ludwig->phi, ludwig->lb); - stats_field_info_bbl(ludwig->phi, ludwig->map, ludwig->bbl); - } - else { - if (ludwig->phi) { - if (ludwig->pch) { - cahn_hilliard_stats(ludwig->pch, ludwig->phi, ludwig->map); - } - else { - stats_field_info(ludwig->phi, ludwig->map); - } - } - } - if (ludwig->p) stats_field_info(ludwig->p, ludwig->map); - if (ludwig->q) stats_field_info(ludwig->q, ludwig->map); - if (ludwig->psi) { - psi_stats_info(ludwig->psi); - } + ludwig_report_statistics(ludwig, 0); ludwig_report_momentum(ludwig); /* Main time stepping loop */ @@ -985,69 +964,7 @@ void ludwig_run(const char * inputfile) { if (is_statistics_step()) { - lb_memcpy(ludwig->lb, tdpMemcpyDeviceToHost); - stats_distribution_print(ludwig->lb, ludwig->map); - lb_ndist(ludwig->lb, &im); - - if (ludwig->phi) { - field_memcpy(ludwig->phi, tdpMemcpyDeviceToHost); - field_grad_memcpy(ludwig->phi_grad, tdpMemcpyDeviceToHost); - if (im == 2) { - /* Recompute phi (kernel) and copy back if required */ - phi_lb_to_field(ludwig->phi, ludwig->lb); - field_memcpy(ludwig->phi, tdpMemcpyDeviceToHost); - stats_field_info_bbl(ludwig->phi, ludwig->map, ludwig->bbl); - } - else { - if (ludwig->pch) { - cahn_hilliard_stats(ludwig->pch, ludwig->phi, ludwig->map); - } - else { - field_memcpy(ludwig->phi, tdpMemcpyDeviceToHost); - stats_field_info(ludwig->phi, ludwig->map); - } - } - } - - 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); - } - - if (ludwig->q) { - field_memcpy(ludwig->q, tdpMemcpyDeviceToHost); - field_grad_memcpy(ludwig->q_grad, tdpMemcpyDeviceToHost); - stats_field_info(ludwig->q, ludwig->map); - stats_colloid_force_split_output(ludwig->collinfo, step); - } - - if (ludwig->psi) { - double psi_zeta; - psi_colloid_rho_set(ludwig->psi, ludwig->collinfo); - psi_stats_info(ludwig->psi); - /* Zeta potential for one colloid only to follow psi_stats()*/ - psi_colloid_zetapotential(ludwig->psi, ludwig->collinfo, &psi_zeta); - if (ncolloid == 1) pe_info(ludwig->pe, "[psi_zeta] %14.7e\n", psi_zeta); - } - - if (ludwig->fe) { - switch (ludwig->fe->id) { - case FE_LC: - fe_lc_stats_info(ludwig->pe, ludwig->cs, ludwig->fe_lc, - ludwig->wall, ludwig->map, ludwig->collinfo, step); - break; - case FE_TERNARY: - fe_ternary_stats_info(ludwig->fe_ternary, ludwig->wall, - ludwig->map, step); - break; - default: - stats_free_energy_density(ludwig->pe, ludwig->cs, ludwig->wall, - ludwig->fe, ludwig->map, - ludwig->collinfo); - } - } + ludwig_report_statistics(ludwig, step); ludwig_report_momentum(ludwig); if (ludwig->hydro) { @@ -2374,3 +2291,99 @@ __host__ int ludwig_timekeeper_init(ludwig_t * ludwig) { return 0; } + +/***************************************************************************** + * + * ludwig_report_statistics + * + * If t = 0, we need to compute any relevant order parameter gradients + * for the fist time. + * If t > 0 we use the computation coming from the time step loop. + * + *****************************************************************************/ + +int ludwig_report_statistics(ludwig_t * ludwig, int itimestep) { + + assert(ludwig); + + if (itimestep == 0) { + if (ludwig->phi) { + field_halo(ludwig->phi); + field_grad_compute(ludwig->phi_grad); + } + if (ludwig->p) { + field_halo(ludwig->p); + field_grad_compute(ludwig->p_grad); + } + if (ludwig->q) { + field_halo(ludwig->q); + field_grad_compute(ludwig->q_grad); + } + } + + lb_memcpy(ludwig->lb, tdpMemcpyDeviceToHost); + stats_distribution_print(ludwig->lb, ludwig->map); + + if (ludwig->phi) { + field_memcpy(ludwig->phi, tdpMemcpyDeviceToHost); + field_grad_memcpy(ludwig->phi_grad, tdpMemcpyDeviceToHost); + if (ludwig->lb->ndist == 2) { + /* Recompute phi (kernel) and copy back if required */ + phi_lb_to_field(ludwig->phi, ludwig->lb); + field_memcpy(ludwig->phi, tdpMemcpyDeviceToHost); + stats_field_info_bbl(ludwig->phi, ludwig->map, ludwig->bbl); + } + else { + if (ludwig->pch) { + cahn_hilliard_stats(ludwig->pch, ludwig->phi, ludwig->map); + } + else { + field_memcpy(ludwig->phi, tdpMemcpyDeviceToHost); + stats_field_info(ludwig->phi, ludwig->map); + } + } + } + + 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); + } + + if (ludwig->q) { + field_memcpy(ludwig->q, tdpMemcpyDeviceToHost); + field_grad_memcpy(ludwig->q_grad, tdpMemcpyDeviceToHost); + stats_field_info(ludwig->q, ludwig->map); + stats_colloid_force_split_output(ludwig->collinfo, itimestep); + } + + if (ludwig->psi) { + int ncolloid = 0; + double psi_zeta = 0.0; + psi_colloid_rho_set(ludwig->psi, ludwig->collinfo); + psi_stats_info(ludwig->psi); + /* Zeta potential for one colloid only to follow psi_stats()*/ + psi_colloid_zetapotential(ludwig->psi, ludwig->collinfo, &psi_zeta); + if (ncolloid == 1) pe_info(ludwig->pe, "[psi_zeta] %14.7e\n", psi_zeta); + } + + if (ludwig->fe) { + switch (ludwig->fe->id) { + case FE_LC: + fe_lc_stats_info(ludwig->pe, ludwig->cs, ludwig->fe_lc, + ludwig->wall, ludwig->map, ludwig->collinfo, itimestep); + break; + case FE_TERNARY: + fe_ternary_stats_info(ludwig->fe_ternary, ludwig->wall, + ludwig->map, itimestep); + break; + default: + stats_free_energy_density(ludwig->pe, ludwig->cs, ludwig->wall, + ludwig->fe, ludwig->map, + ludwig->collinfo); + } + } + + return 0; +} From dc70f65475d7fea395904de2887f45fda272a098 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 10 Feb 2023 13:34:06 +0000 Subject: [PATCH 06/97] Replace timer free1 by diagnostics and adjust step timer --- src/ludwig.c | 11 +++-------- src/timer.c | 4 ++-- src/timer.h | 4 ++-- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/ludwig.c b/src/ludwig.c index 025288c3..2600802e 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -874,9 +874,7 @@ void ludwig_run(const char * inputfile) { TIMER_stop(TIMER_PROPAGATE); } - TIMER_stop(TIMER_STEPS); - - TIMER_start(TIMER_FREE1); /* Time diagnostics */ + TIMER_start(TIMER_DIAGNOSTIC_OUTPUT); /* Time diagnostics and i/o */ /* Configuration dump */ @@ -980,7 +978,8 @@ void ludwig_run(const char * inputfile) { stats_ahydro_accumulate(ludwig->stat_ah, step); - TIMER_stop(TIMER_FREE1); + TIMER_stop(TIMER_DIAGNOSTIC_OUTPUT); + TIMER_stop(TIMER_STEPS); /* inclusive of diagnostic/io */ /* Next time step */ } @@ -2111,10 +2110,8 @@ int ludwig_colloids_update(ludwig_t * ludwig) { TIMER_stop(TIMER_HALO_LATTICE); - TIMER_start(TIMER_FREE1); if (iconserve && ludwig->phi) field_halo(ludwig->phi); if (iconserve && ludwig->psi) psi_halo_rho(ludwig->psi); - TIMER_stop(TIMER_FREE1); TIMER_start(TIMER_REBUILD); @@ -2126,13 +2123,11 @@ int ludwig_colloids_update(ludwig_t * ludwig) { TIMER_stop(TIMER_REBUILD); - TIMER_start(TIMER_FREE1); if (iconserve) { colloid_sums_halo(ludwig->collinfo, COLLOID_SUM_CONSERVATION); build_conservation(ludwig->collinfo, ludwig->phi, ludwig->psi, &ludwig->lb->model); } - TIMER_stop(TIMER_FREE1); TIMER_start(TIMER_FORCES); diff --git a/src/timer.c b/src/timer.c index 3f71da60..a25ab2ab 100644 --- a/src/timer.c +++ b/src/timer.c @@ -10,7 +10,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) @@ -90,7 +90,7 @@ static const char * timer_name[] = {"Total", "Poisson equation", "Nernst Planck", "Lap timer (no report)", - "Free1", + "Diagnostics / output ", "Free2", "Free3", "Free4", "Free5", "Free6" }; diff --git a/src/timer.h b/src/timer.h index 28067c18..b712076c 100644 --- a/src/timer.h +++ b/src/timer.h @@ -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) @@ -102,7 +102,7 @@ enum timer_id {TIMER_TOTAL = 0, TIMER_ELECTRO_POISSON, TIMER_ELECTRO_NPEQ, TIMER_LAP, - TIMER_FREE1, + TIMER_DIAGNOSTIC_OUTPUT, TIMER_FREE2, TIMER_FREE3, TIMER_FREE4, From 651daf5f7ecefa339abd3e8f2e7bb95c18867f34 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 10 Feb 2023 13:34:56 +0000 Subject: [PATCH 07/97] Add free energy output at time zero --- tests/regression/d3q19-short/serial-actv-s01.log | 3 +++ tests/regression/d3q19-short/serial-actv-s02.log | 3 +++ tests/regression/d3q19-short/serial-anch-cn1.log | 3 +++ tests/regression/d3q19-short/serial-anch-cn2.log | 3 +++ tests/regression/d3q19-short/serial-anch-wn1.log | 3 +++ tests/regression/d3q19-short/serial-anch-wn2.log | 3 +++ tests/regression/d3q19-short/serial-anch-wn3.log | 3 +++ tests/regression/d3q19-short/serial-bond-c01.log | 3 +++ tests/regression/d3q19-short/serial-bond-c02.log | 3 +++ tests/regression/d3q19-short/serial-chol-fld.log | 3 +++ tests/regression/d3q19-short/serial-chol-n01.log | 3 +++ tests/regression/d3q19-short/serial-chol-n02.log | 3 +++ tests/regression/d3q19-short/serial-chol-n03.log | 3 +++ tests/regression/d3q19-short/serial-chol-n04.log | 3 +++ tests/regression/d3q19-short/serial-chol-p01.log | 3 +++ tests/regression/d3q19-short/serial-chol-st1.log | 3 +++ tests/regression/d3q19-short/serial-chol-st2.log | 3 +++ tests/regression/d3q19-short/serial-chol-st3.log | 3 +++ tests/regression/d3q19-short/serial-chol-st4.log | 3 +++ tests/regression/d3q19-short/serial-chol-st5.log | 3 +++ tests/regression/d3q19-short/serial-chol-st6.log | 3 +++ tests/regression/d3q19-short/serial-chol-st7.log | 3 +++ tests/regression/d3q19-short/serial-chol-w01.log | 3 +++ tests/regression/d3q19-short/serial-chol-w02.log | 3 +++ tests/regression/d3q19-short/serial-chol-w03.log | 3 +++ tests/regression/d3q19-short/serial-chol-w04.log | 3 +++ tests/regression/d3q19-short/serial-chol-w05.log | 3 +++ tests/regression/d3q19-short/serial-drop-lc1.log | 3 +++ tests/regression/d3q19-short/serial-drop-lc2.log | 3 +++ tests/regression/d3q19-short/serial-drop-lc3.log | 3 +++ tests/regression/d3q19-short/serial-drop-lc4.log | 3 +++ tests/regression/d3q19-short/serial-drop-lc5.log | 3 +++ tests/regression/d3q19-short/serial-init-bp1.log | 3 +++ tests/regression/d3q19-short/serial-init-bp2.log | 3 +++ tests/regression/d3q19-short/serial-init-br1.log | 3 +++ tests/regression/d3q19-short/serial-init-br2.log | 3 +++ tests/regression/d3q19-short/serial-init-lcb.log | 3 +++ tests/regression/d3q19-short/serial-init-lcr.log | 3 +++ tests/regression/d3q19-short/serial-le2d-fd1.log | 3 +++ tests/regression/d3q19-short/serial-le2d-fd2.log | 3 +++ tests/regression/d3q19-short/serial-le2d-lb1.log | 3 +++ tests/regression/d3q19-short/serial-le3d-st1.log | 3 +++ tests/regression/d3q19-short/serial-le3d-st2.log | 3 +++ tests/regression/d3q19-short/serial-le3d-st3.log | 3 +++ tests/regression/d3q19-short/serial-le3d-st4.log | 3 +++ tests/regression/d3q19-short/serial-le3d-st5.log | 3 +++ tests/regression/d3q19-short/serial-le3d-st6.log | 3 +++ tests/regression/d3q19-short/serial-le3d-st7.log | 3 +++ tests/regression/d3q19-short/serial-le3d-st8.log | 3 +++ tests/regression/d3q19-short/serial-muex-st1.log | 3 +++ tests/regression/d3q19-short/serial-pola-r01.log | 3 +++ tests/regression/d3q19-short/serial-relx-bp1.log | 3 +++ tests/regression/d3q19-short/serial-spin-c01.log | 3 +++ tests/regression/d3q19-short/serial-spin-c02.log | 3 +++ tests/regression/d3q19-short/serial-spin-fd1.log | 3 +++ tests/regression/d3q19-short/serial-spin-fd2.log | 3 +++ tests/regression/d3q19-short/serial-spin-lb1.log | 3 +++ tests/regression/d3q19-short/serial-spin-lb2.log | 3 +++ tests/regression/d3q19-short/serial-spin-lb3.log | 3 +++ tests/regression/d3q19-short/serial-spin-lb4.log | 3 +++ tests/regression/d3q19-short/serial-spin-n01.log | 3 +++ tests/regression/d3q19-short/serial-spin-n02.log | 3 +++ tests/regression/d3q19-short/serial-symm-dr1.log | 3 +++ tests/regression/d3q19-short/serial-symm-dr2.log | 3 +++ tests/regression/d3q19-short/serial-symm-pat.log | 3 +++ tests/regression/d3q19-short/serial-tern-st1.log | 3 +++ tests/regression/d3q19-short/serial-tern-st2.log | 3 +++ tests/regression/d3q19-short/serial-tern-st3.log | 3 +++ tests/regression/d3q19-short/serial-tern-st4.log | 4 ++++ tests/regression/d3q19-short/serial-tern-st5.log | 4 ++++ 70 files changed, 212 insertions(+) diff --git a/tests/regression/d3q19-short/serial-actv-s01.log b/tests/regression/d3q19-short/serial-actv-s01.log index 16bc5a0a..6e2dff6e 100644 --- a/tests/regression/d3q19-short/serial-actv-s01.log +++ b/tests/regression/d3q19-short/serial-actv-s01.log @@ -97,6 +97,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 0 -2.7821403250e+01 4.0960000000e+03 -6.7923347779e-03 -6.9444444444e-03 1.5210966656e-04 1.0000000000e+00 + Momentum - x y z [total ] 5.6843419e-14 0.0000000e+00 0.0000000e+00 [fluid ] 5.6843419e-14 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-actv-s02.log b/tests/regression/d3q19-short/serial-actv-s02.log index c06598df..23e3ffed 100644 --- a/tests/regression/d3q19-short/serial-actv-s02.log +++ b/tests/regression/d3q19-short/serial-actv-s02.log @@ -97,6 +97,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 0 -2.7821403250e+01 4.0960000000e+03 -6.7923347779e-03 -6.9444444444e-03 1.5210966656e-04 1.0000000000e+00 + Momentum - x y z [total ] 5.6843419e-14 0.0000000e+00 0.0000000e+00 [fluid ] 5.6843419e-14 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-anch-cn1.log b/tests/regression/d3q19-short/serial-anch-cn1.log index 8e657895..beffeccc 100644 --- a/tests/regression/d3q19-short/serial-anch-cn1.log +++ b/tests/regression/d3q19-short/serial-anch-cn1.log @@ -133,6 +133,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_s a f_s/a +[fe] 0 -1.4626035050e+01 2.6056900000e+05 -5.6131140119e-05 1.3520000000e+00 1.0140000000e+03 1.3333333333e-03 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-anch-cn2.log b/tests/regression/d3q19-short/serial-anch-cn2.log index 78c91c4e..871249d4 100644 --- a/tests/regression/d3q19-short/serial-anch-cn2.log +++ b/tests/regression/d3q19-short/serial-anch-cn2.log @@ -134,6 +134,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_s a f_s/a +[fe] 0 -1.7124366154e+01 2.6056900000e+05 -6.5719122975e-05 1.4909235568e+00 1.0140000000e+03 1.4703388134e-03 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-anch-wn1.log b/tests/regression/d3q19-short/serial-anch-wn1.log index 600a6ebf..a66cde76 100644 --- a/tests/regression/d3q19-short/serial-anch-wn1.log +++ b/tests/regression/d3q19-short/serial-anch-wn1.log @@ -116,6 +116,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_s1 fs_s2 redshift +[fe] 0 -8.6959696682e-03 2.5600000000e+02 -3.3968631516e-05 3.0749616130e-04 5.0104380201e-04 1.0000000000e+00 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 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 c21f5d07..643c7c97 100644 --- a/tests/regression/d3q19-short/serial-anch-wn2.log +++ b/tests/regression/d3q19-short/serial-anch-wn2.log @@ -117,6 +117,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_s1 fs_s2 redshift +[fe] 0 -8.6306600774e-03 2.5600000000e+02 -3.3713515927e-05 1.0567663416e-03 9.5798326283e-04 1.0000000000e+00 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 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 da1699b3..e2d6ce2c 100644 --- a/tests/regression/d3q19-short/serial-anch-wn3.log +++ b/tests/regression/d3q19-short/serial-anch-wn3.log @@ -117,6 +117,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_s1 fs_s2 redshift +[fe] 0 -8.6543552119e-03 2.5600000000e+02 -3.3806075047e-05 4.9250383870e-04 2.9895619799e-04 1.0000000000e+00 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-bond-c01.log b/tests/regression/d3q19-short/serial-bond-c01.log index f27a7194..2a98dfdb 100644 --- a/tests/regression/d3q19-short/serial-bond-c01.log +++ b/tests/regression/d3q19-short/serial-bond-c01.log @@ -120,6 +120,9 @@ Scalars - total mean variance min max [rho] 65484.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] 0.0000000e+00 0.0000000e+00 9.2965928e-01 -1.0000000e+00 1.0000000e+00 +Free energies - timestep f v f/v f_s a f_s/a +[fe] 0 -9.3481842103e+02 6.5484000000e+04 -1.4275524113e-02 0.0000000000e+00 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-bond-c02.log b/tests/regression/d3q19-short/serial-bond-c02.log index 4cc8eae1..16bc3cbe 100644 --- a/tests/regression/d3q19-short/serial-bond-c02.log +++ b/tests/regression/d3q19-short/serial-bond-c02.log @@ -117,6 +117,9 @@ Scalars - total mean variance min max [rho] 65484.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] 0.0000000e+00 0.0000000e+00 9.2965928e-01 -1.0000000e+00 1.0000000e+00 +Free energies - timestep f v f/v f_s a f_s/a +[fe] 0 -9.3481842103e-01 6.5484000000e+04 -1.4275524113e-05 0.0000000000e+00 + Momentum - x y z [total ] 9.0877306e-13 0.0000000e+00 0.0000000e+00 [fluid ] 9.0877306e-13 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 bdcf5638..c5333faf 100644 --- a/tests/regression/d3q19-short/serial-chol-fld.log +++ b/tests/regression/d3q19-short/serial-chol-fld.log @@ -110,6 +110,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 0 -1.4685971349e+00 4.0960000000e+03 -3.5854422238e-04 -3.5854422238e-04 0.0000000000e+00 1.0000000000e+00 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-n01.log b/tests/regression/d3q19-short/serial-chol-n01.log index cf8e7501..28143ebb 100644 --- a/tests/regression/d3q19-short/serial-chol-n01.log +++ b/tests/regression/d3q19-short/serial-chol-n01.log @@ -139,6 +139,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_s a f_s/a +[fe] 0 -1.7252339949e+01 2.6056900000e+05 -6.6210255055e-05 4.3835625600e-01 1.0140000000e+03 4.3230400000e-04 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-n02.log b/tests/regression/d3q19-short/serial-chol-n02.log index bdb06932..8252beef 100644 --- a/tests/regression/d3q19-short/serial-chol-n02.log +++ b/tests/regression/d3q19-short/serial-chol-n02.log @@ -147,6 +147,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_s a f_s/a +[fe] 0 -1.6858825082e+01 2.5899400000e+05 -6.5093496690e-05 8.7671251200e-01 2.0280000000e+03 4.3230400000e-04 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-n03.log b/tests/regression/d3q19-short/serial-chol-n03.log index cd123b75..1cc0b951 100644 --- a/tests/regression/d3q19-short/serial-chol-n03.log +++ b/tests/regression/d3q19-short/serial-chol-n03.log @@ -147,6 +147,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_s a f_s/a +[fe] 0 -1.6858825082e+01 2.5899400000e+05 -6.5093496689e-05 8.7671251200e-01 2.0280000000e+03 4.3230400000e-04 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-n04.log b/tests/regression/d3q19-short/serial-chol-n04.log index 46cdee31..73dd14c8 100644 --- a/tests/regression/d3q19-short/serial-chol-n04.log +++ b/tests/regression/d3q19-short/serial-chol-n04.log @@ -147,6 +147,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_s a f_s/a +[fe] 0 -1.6858825082e+01 2.5899400000e+05 -6.5093496689e-05 8.7671251200e-01 2.0280000000e+03 4.3230400000e-04 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-p01.log b/tests/regression/d3q19-short/serial-chol-p01.log index 3d83d3ea..e008d9b7 100644 --- a/tests/regression/d3q19-short/serial-chol-p01.log +++ b/tests/regression/d3q19-short/serial-chol-p01.log @@ -141,6 +141,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_s a f_s/a +[fe] 0 -1.7464619353e+01 2.6056900000e+05 -6.7024931413e-05 1.5407341537e-01 1.0140000000e+03 1.5194616901e-04 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-st1.log b/tests/regression/d3q19-short/serial-chol-st1.log index 61f8ac8a..8d0cb47f 100644 --- a/tests/regression/d3q19-short/serial-chol-st1.log +++ b/tests/regression/d3q19-short/serial-chol-st1.log @@ -153,6 +153,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_s a f_s/a +[fe] 0 5.9309580698e-04 3.2663000000e+04 1.8158032238e-08 3.7847344338e-02 2.1600000000e+02 1.7521918675e-04 + Momentum - x y z [total ] 4.5329018e-13 0.0000000e+00 0.0000000e+00 [fluid ] 4.5329018e-13 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-st2.log b/tests/regression/d3q19-short/serial-chol-st2.log index 3da66f93..2f536625 100644 --- a/tests/regression/d3q19-short/serial-chol-st2.log +++ b/tests/regression/d3q19-short/serial-chol-st2.log @@ -153,6 +153,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_s1 fs_s2 redshift +[fe] 0 9.2262326809e-03 1.6332000000e+04 5.6491750434e-07 1.7943162655e-01 1.7943344133e-01 1.0000000000e+00 + Momentum - x y z [total ] 2.2665203e-13 0.0000000e+00 0.0000000e+00 [fluid ] 2.2665203e-13 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-st3.log b/tests/regression/d3q19-short/serial-chol-st3.log index e48f5d19..95fe3d75 100644 --- a/tests/regression/d3q19-short/serial-chol-st3.log +++ b/tests/regression/d3q19-short/serial-chol-st3.log @@ -116,6 +116,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 0 -2.4456131969e+00 3.2768000000e+04 -7.4634191799e-05 -8.6978571499e-05 1.2344379700e-05 1.0000000000e+00 + Momentum - x y z [total ] -4.2632564e-14 -2.6093711e-14 0.0000000e+00 [fluid ] -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 ccb69bb3..4d0d0202 100644 --- a/tests/regression/d3q19-short/serial-chol-st4.log +++ b/tests/regression/d3q19-short/serial-chol-st4.log @@ -116,6 +116,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 0 -2.4456131969e+00 3.2768000000e+04 -7.4634191799e-05 -8.6978571499e-05 1.2344379700e-05 1.0000000000e+00 + Momentum - x y z [total ] -4.2632564e-14 -2.6093711e-14 0.0000000e+00 [fluid ] -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 12a12cd2..00a393bb 100644 --- a/tests/regression/d3q19-short/serial-chol-st5.log +++ b/tests/regression/d3q19-short/serial-chol-st5.log @@ -116,6 +116,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 0 -2.4456131969e+00 3.2768000000e+04 -7.4634191799e-05 -8.6978571499e-05 1.2344379700e-05 1.0000000000e+00 + Momentum - x y z [total ] -4.2632564e-14 -2.6093711e-14 0.0000000e+00 [fluid ] -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 aefb7726..310dcc2d 100644 --- a/tests/regression/d3q19-short/serial-chol-st6.log +++ b/tests/regression/d3q19-short/serial-chol-st6.log @@ -116,6 +116,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 0 -2.4456131969e+00 3.2768000000e+04 -7.4634191799e-05 -8.6978571499e-05 1.2344379700e-05 1.0000000000e+00 + Momentum - x y z [total ] -4.2632564e-14 -2.6093711e-14 0.0000000e+00 [fluid ] -4.2632564e-14 -2.6093711e-14 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-st7.log b/tests/regression/d3q19-short/serial-chol-st7.log index 17d4746a..016c0e87 100644 --- a/tests/regression/d3q19-short/serial-chol-st7.log +++ b/tests/regression/d3q19-short/serial-chol-st7.log @@ -152,6 +152,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_s a f_s/a +[fe] 0 5.9309580698e-04 3.2663000000e+04 1.8158032238e-08 3.7847344338e-02 2.1600000000e+02 1.7521918675e-04 + Momentum - x y z [total ] 4.5329018e-13 0.0000000e+00 0.0000000e+00 [fluid ] 4.5329018e-13 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-w01.log b/tests/regression/d3q19-short/serial-chol-w01.log index 9cf9056e..76fb7770 100644 --- a/tests/regression/d3q19-short/serial-chol-w01.log +++ b/tests/regression/d3q19-short/serial-chol-w01.log @@ -123,6 +123,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_s1 fs_s2 redshift +[fe] 0 -1.4668275124e+01 2.6214400000e+05 -5.5955029006e-05 3.0987550720e+00 3.0987550720e+00 1.0000000000e+00 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 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 1b328687..29c0c153 100644 --- a/tests/regression/d3q19-short/serial-chol-w02.log +++ b/tests/regression/d3q19-short/serial-chol-w02.log @@ -123,6 +123,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_s1 fs_s2 redshift +[fe] 0 -1.4668275124e+01 2.6214400000e+05 -5.5955029006e-05 3.0987550720e+00 3.0987550720e+00 1.0000000000e+00 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 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 5dced4a8..531098c9 100644 --- a/tests/regression/d3q19-short/serial-chol-w03.log +++ b/tests/regression/d3q19-short/serial-chol-w03.log @@ -123,6 +123,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_s1 fs_s2 redshift +[fe] 0 -1.4668275124e+01 2.6214400000e+05 -5.5955029006e-05 3.0987550720e+00 3.0987550720e+00 1.0000000000e+00 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-w04.log b/tests/regression/d3q19-short/serial-chol-w04.log index 07b7fccb..fd22ee53 100644 --- a/tests/regression/d3q19-short/serial-chol-w04.log +++ b/tests/regression/d3q19-short/serial-chol-w04.log @@ -118,6 +118,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_s1 fs_s2 redshift +[fe] 0 -3.1260163080e+00 6.5536000000e+04 -4.7699223450e-05 4.4267929600e-01 4.4267929600e-01 1.0000000000e+00 + Momentum - x y z [total ] 9.0949470e-13 0.0000000e+00 0.0000000e+00 [fluid ] 9.0949470e-13 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-chol-w05.log b/tests/regression/d3q19-short/serial-chol-w05.log index 080cd504..23d16406 100644 --- a/tests/regression/d3q19-short/serial-chol-w05.log +++ b/tests/regression/d3q19-short/serial-chol-w05.log @@ -118,6 +118,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_s1 fs_s2 redshift +[fe] 0 -3.1260163080e+00 6.5536000000e+04 -4.7699223450e-05 4.4267929600e-01 4.4267929600e-01 1.0000000000e+00 + Momentum - x y z [total ] 9.0949470e-13 0.0000000e+00 0.0000000e+00 [fluid ] 9.0949470e-13 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-drop-lc1.log b/tests/regression/d3q19-short/serial-drop-lc1.log index 6923b15f..5aa44a45 100644 --- a/tests/regression/d3q19-short/serial-drop-lc1.log +++ b/tests/regression/d3q19-short/serial-drop-lc1.log @@ -129,6 +129,9 @@ Scalars - total mean variance min max [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 +Free energy density - timestep total fluid +[fed] 0 -1.4702479172e-02 -1.4702479172e-02 + Momentum - x y z [total ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 [fluid ] 4.5474735e-13 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 e58deb09..c75e1f03 100644 --- a/tests/regression/d3q19-short/serial-drop-lc2.log +++ b/tests/regression/d3q19-short/serial-drop-lc2.log @@ -124,6 +124,9 @@ Scalars - total mean variance min max [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 +Free energy density - timestep total fluid +[fed] 0 -1.4296432148e-02 -1.4296432148e-02 + Momentum - x y z [total ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 [fluid ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-drop-lc3.log b/tests/regression/d3q19-short/serial-drop-lc3.log index 95679d9a..d47eecb8 100644 --- a/tests/regression/d3q19-short/serial-drop-lc3.log +++ b/tests/regression/d3q19-short/serial-drop-lc3.log @@ -127,6 +127,9 @@ Scalars - total mean variance min max [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 +Free energy density - timestep total fluid +[fed] 0 1.4297372559e-02 1.4297372559e-02 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-drop-lc4.log b/tests/regression/d3q19-short/serial-drop-lc4.log index bf5093a9..0f2a4a2a 100644 --- a/tests/regression/d3q19-short/serial-drop-lc4.log +++ b/tests/regression/d3q19-short/serial-drop-lc4.log @@ -121,6 +121,9 @@ Scalars - total mean variance min max [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 +Free energy density - timestep total fluid +[fed] 0 2.8628468640e-02 2.8628468640e-02 + Momentum - x y z [total ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 [fluid ] 4.5474735e-13 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 4c3d6e2d..39322e99 100644 --- a/tests/regression/d3q19-short/serial-drop-lc5.log +++ b/tests/regression/d3q19-short/serial-drop-lc5.log @@ -121,6 +121,9 @@ Scalars - total mean variance min max [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 +Free energy density - timestep total fluid +[fed] 0 1.6905959169e-02 1.6905959169e-02 + Momentum - x y z [total ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 [fluid ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-init-bp1.log b/tests/regression/d3q19-short/serial-init-bp1.log index c4bf36bc..41934a49 100644 --- a/tests/regression/d3q19-short/serial-init-bp1.log +++ b/tests/regression/d3q19-short/serial-init-bp1.log @@ -95,6 +95,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 0 1.3742090041e+02 3.2768000000e+04 4.1937530642e-03 4.1667013235e-03 2.7051740705e-05 8.3000000000e-01 + Momentum - x y z [total ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 [fluid ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-init-bp2.log b/tests/regression/d3q19-short/serial-init-bp2.log index 8e7e46c4..3e444428 100644 --- a/tests/regression/d3q19-short/serial-init-bp2.log +++ b/tests/regression/d3q19-short/serial-init-bp2.log @@ -95,6 +95,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 0 5.3918275382e+01 3.2768000000e+04 1.6454551813e-03 1.6347824504e-03 1.0672730938e-05 9.1000000000e-01 + Momentum - x y z [total ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 [fluid ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-init-br1.log b/tests/regression/d3q19-short/serial-init-br1.log index d1124e8e..3f32957d 100644 --- a/tests/regression/d3q19-short/serial-init-br1.log +++ b/tests/regression/d3q19-short/serial-init-br1.log @@ -105,6 +105,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 0 1.6357908149e+02 3.2768000000e+04 4.9920373991e-03 4.1667013235e-03 8.2533607554e-04 8.3000000000e-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 diff --git a/tests/regression/d3q19-short/serial-init-br2.log b/tests/regression/d3q19-short/serial-init-br2.log index 219cb486..8ff61340 100644 --- a/tests/regression/d3q19-short/serial-init-br2.log +++ b/tests/regression/d3q19-short/serial-init-br2.log @@ -105,6 +105,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 0 8.4309097066e+01 3.2768000000e+04 2.5729094563e-03 1.6530748961e-03 9.1983456029e-04 9.1000000000e-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 diff --git a/tests/regression/d3q19-short/serial-init-lcb.log b/tests/regression/d3q19-short/serial-init-lcb.log index e2d9aaad..1922f425 100644 --- a/tests/regression/d3q19-short/serial-init-lcb.log +++ b/tests/regression/d3q19-short/serial-init-lcb.log @@ -97,6 +97,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 0 -2.3437999391e-04 3.2768000000e+04 -7.1527097751e-09 -7.4628096480e-08 6.7475386705e-08 1.0000000000e+00 + Momentum - x y z [total ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 [fluid ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-init-lcr.log b/tests/regression/d3q19-short/serial-init-lcr.log index bcf503ca..a92c51ce 100644 --- a/tests/regression/d3q19-short/serial-init-lcr.log +++ b/tests/regression/d3q19-short/serial-init-lcr.log @@ -95,6 +95,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 0 4.2023235107e-02 3.2768000000e+04 1.2824473604e-06 -7.4628096480e-08 1.3570754569e-06 1.0000000000e+00 + Momentum - x y z [total ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 [fluid ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-le2d-fd1.log b/tests/regression/d3q19-short/serial-le2d-fd1.log index 236fd67c..67ecb50b 100644 --- a/tests/regression/d3q19-short/serial-le2d-fd1.log +++ b/tests/regression/d3q19-short/serial-le2d-fd1.log @@ -90,6 +90,9 @@ Scalars - total mean variance min max [rho] 4096.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 [phi] -1.1802440e-02 -2.8814551e-06 8.2680383e-04 -4.9977721e-02 4.9988335e-02 +Free energy density - timestep total fluid +[fed] 0 -2.0247427840e-05 -2.0247427840e-05 + Momentum - x y z [total ] 1.5099033e-14 -3.3306691e-16 0.0000000e+00 [fluid ] 1.5099033e-14 -3.3306691e-16 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-le2d-fd2.log b/tests/regression/d3q19-short/serial-le2d-fd2.log index 740972ac..a17b1f02 100644 --- a/tests/regression/d3q19-short/serial-le2d-fd2.log +++ b/tests/regression/d3q19-short/serial-le2d-fd2.log @@ -89,6 +89,9 @@ Scalars - total mean variance min max [rho] 4096.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] -1.1802440e-01 -2.8814551e-05 8.2680383e-02 -4.9977721e-01 4.9988335e-01 +Free energy density - timestep total fluid +[fed] 0 5.8277975965e-04 5.8277975965e-04 + Momentum - x y z [total ] 5.6843419e-14 0.0000000e+00 0.0000000e+00 [fluid ] 5.6843419e-14 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-le2d-lb1.log b/tests/regression/d3q19-short/serial-le2d-lb1.log index 53c82b93..198e1b0c 100644 --- a/tests/regression/d3q19-short/serial-le2d-lb1.log +++ b/tests/regression/d3q19-short/serial-le2d-lb1.log @@ -92,6 +92,9 @@ Scalars - total mean variance min max [rho] 4096.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 [phi] -1.1802440e-02 -2.8814551e-06 8.2680383e-04 -4.9977721e-02 4.9988335e-02 +Free energy density - timestep total fluid +[fed] 0 -2.0247427840e-05 -2.0247427840e-05 + Momentum - x y z [total ] 3.1225023e-17 0.0000000e+00 0.0000000e+00 [fluid ] 3.1225023e-17 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-le3d-st1.log b/tests/regression/d3q19-short/serial-le3d-st1.log index 156a97ce..b54b3202 100644 --- a/tests/regression/d3q19-short/serial-le3d-st1.log +++ b/tests/regression/d3q19-short/serial-le3d-st1.log @@ -90,6 +90,9 @@ Scalars - total mean variance min max [rho] 32768.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 [phi] 1.1826252e+01 3.6090857e-04 8.3228431e-04 -4.9999491e-02 4.9993448e-02 +Free energy density - timestep total fluid +[fed] 0 -1.0291179264e-06 -1.0291179264e-06 + Momentum - x y z [total ] -2.1316282e-14 -1.2861240e-14 0.0000000e+00 [fluid ] -2.1316282e-14 -1.2861240e-14 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-le3d-st2.log b/tests/regression/d3q19-short/serial-le3d-st2.log index 5392e86d..4a13a7a6 100644 --- a/tests/regression/d3q19-short/serial-le3d-st2.log +++ b/tests/regression/d3q19-short/serial-le3d-st2.log @@ -90,6 +90,9 @@ Scalars - total mean variance min max [rho] 32768.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 [phi] 1.1826252e+01 3.6090857e-04 8.3228431e-04 -4.9999491e-02 4.9993448e-02 +Free energy density - timestep total fluid +[fed] 0 -1.0291179264e-06 -1.0291179264e-06 + Momentum - x y z [total ] -2.1316282e-14 -1.2861240e-14 0.0000000e+00 [fluid ] -2.1316282e-14 -1.2861240e-14 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-le3d-st3.log b/tests/regression/d3q19-short/serial-le3d-st3.log index 9f4aacfc..aca3bfc1 100644 --- a/tests/regression/d3q19-short/serial-le3d-st3.log +++ b/tests/regression/d3q19-short/serial-le3d-st3.log @@ -90,6 +90,9 @@ Scalars - total mean variance min max [rho] 32768.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 [phi] 1.1826252e+01 3.6090857e-04 8.3228431e-04 -4.9999491e-02 4.9993448e-02 +Free energy density - timestep total fluid +[fed] 0 -1.0291179264e-06 -1.0291179264e-06 + Momentum - x y z [total ] -2.1316282e-14 -1.2861240e-14 0.0000000e+00 [fluid ] -2.1316282e-14 -1.2861240e-14 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-le3d-st4.log b/tests/regression/d3q19-short/serial-le3d-st4.log index b791ad62..22444160 100644 --- a/tests/regression/d3q19-short/serial-le3d-st4.log +++ b/tests/regression/d3q19-short/serial-le3d-st4.log @@ -90,6 +90,9 @@ Scalars - total mean variance min max [rho] 32768.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 [phi] 1.1826252e+01 3.6090857e-04 8.3228431e-04 -4.9999491e-02 4.9993448e-02 +Free energy density - timestep total fluid +[fed] 0 -1.0291179264e-06 -1.0291179264e-06 + Momentum - x y z [total ] -2.1316282e-14 -1.2861240e-14 0.0000000e+00 [fluid ] -2.1316282e-14 -1.2861240e-14 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-le3d-st5.log b/tests/regression/d3q19-short/serial-le3d-st5.log index 8ab73a5a..304d6f40 100644 --- a/tests/regression/d3q19-short/serial-le3d-st5.log +++ b/tests/regression/d3q19-short/serial-le3d-st5.log @@ -90,6 +90,9 @@ Scalars - total mean variance min max [rho] 32768.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 [phi] -1.5507344e+00 -4.7324657e-05 8.3289318e-04 -4.9998133e-02 4.9995155e-02 +Free energy density - timestep total fluid +[fed] 0 -2.3211354094e-05 -2.3211354094e-05 + Momentum - x y z [total ] -2.1316282e-14 -1.2861240e-14 0.0000000e+00 [fluid ] -2.1316282e-14 -1.2861240e-14 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-le3d-st6.log b/tests/regression/d3q19-short/serial-le3d-st6.log index de509d61..8f7b670f 100644 --- a/tests/regression/d3q19-short/serial-le3d-st6.log +++ b/tests/regression/d3q19-short/serial-le3d-st6.log @@ -90,6 +90,9 @@ Scalars - total mean variance min max [rho] 32768.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 [phi] -1.5507344e+00 -4.7324657e-05 8.3289318e-04 -4.9998133e-02 4.9995155e-02 +Free energy density - timestep total fluid +[fed] 0 -2.3211354094e-05 -2.3211354094e-05 + Momentum - x y z [total ] -2.1316282e-14 -1.2861240e-14 0.0000000e+00 [fluid ] -2.1316282e-14 -1.2861240e-14 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-le3d-st7.log b/tests/regression/d3q19-short/serial-le3d-st7.log index 3fec4316..5651bdb6 100644 --- a/tests/regression/d3q19-short/serial-le3d-st7.log +++ b/tests/regression/d3q19-short/serial-le3d-st7.log @@ -95,6 +95,9 @@ Scalars - total mean variance min max [rho] 32768.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 [phi] -1.5507344e+00 -4.7324657e-05 8.3289318e-04 -4.9998133e-02 4.9995155e-02 +Free energy density - timestep total fluid +[fed] 0 -2.3211354094e-05 -2.3211354094e-05 + Momentum - x y z [total ] 2.0816682e-17 2.4286129e-17 0.0000000e+00 [fluid ] 2.0816682e-17 2.4286129e-17 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-le3d-st8.log b/tests/regression/d3q19-short/serial-le3d-st8.log index 5438ce31..2a27d7f2 100644 --- a/tests/regression/d3q19-short/serial-le3d-st8.log +++ b/tests/regression/d3q19-short/serial-le3d-st8.log @@ -90,6 +90,9 @@ Scalars - total mean variance min max [rho] 32768.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 [phi] -1.5507344e+00 -4.7324657e-05 8.3289318e-04 -4.9998133e-02 4.9995155e-02 +Free energy density - timestep total fluid +[fed] 0 -2.3211354094e-05 -2.3211354094e-05 + Momentum - x y z [total ] -2.1316282e-14 -1.2861240e-14 0.0000000e+00 [fluid ] -2.1316282e-14 -1.2861240e-14 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-muex-st1.log b/tests/regression/d3q19-short/serial-muex-st1.log index c112cf6e..b9547fac 100644 --- a/tests/regression/d3q19-short/serial-muex-st1.log +++ b/tests/regression/d3q19-short/serial-muex-st1.log @@ -91,6 +91,9 @@ Scalars - total mean variance min max [rho] 16384.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] 1.6263778e+04 9.9266224e-01 5.8870003e-03 -9.4336416e-01 1.0000000e+00 +Free energy density - timestep total fluid +[fed] 0 -1.5496142043e-03 -1.5496142043e-03 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-pola-r01.log b/tests/regression/d3q19-short/serial-pola-r01.log index 3847d4d7..cf7f8d95 100644 --- a/tests/regression/d3q19-short/serial-pola-r01.log +++ b/tests/regression/d3q19-short/serial-pola-r01.log @@ -88,6 +88,9 @@ Scalars - total mean variance min max [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 +Free energy density - timestep total fluid +[fed] 0 -2.4688264984e-02 -2.4688264984e-02 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-relx-bp1.log b/tests/regression/d3q19-short/serial-relx-bp1.log index 41921288..a5e00e01 100644 --- a/tests/regression/d3q19-short/serial-relx-bp1.log +++ b/tests/regression/d3q19-short/serial-relx-bp1.log @@ -95,6 +95,9 @@ Scalars - total mean variance min max [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 +Free energies - timestep f v f/v f_bulk/v f_grad/v redshift +[fe] 0 1.3742090041e+02 3.2768000000e+04 4.1937530642e-03 4.1667013235e-03 2.7051740705e-05 8.3000000000e-01 + Momentum - x y z [total ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 [fluid ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-spin-c01.log b/tests/regression/d3q19-short/serial-spin-c01.log index 88d700c4..a33e1aad 100644 --- a/tests/regression/d3q19-short/serial-spin-c01.log +++ b/tests/regression/d3q19-short/serial-spin-c01.log @@ -120,6 +120,9 @@ Scalars - total mean variance min max [rho] 261110.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] -1.1543906e+01 -4.4210890e-05 8.3500524e-04 -4.9999763e-02 4.9999902e-02 +Free energies - timestep f v f/v f_s a f_s/a +[fe] 0 -6.0796618987e+00 2.6111000000e+05 -2.3283910607e-05 0.0000000000e+00 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-spin-c02.log b/tests/regression/d3q19-short/serial-spin-c02.log index f09b5847..69d211a8 100644 --- a/tests/regression/d3q19-short/serial-spin-c02.log +++ b/tests/regression/d3q19-short/serial-spin-c02.log @@ -123,6 +123,9 @@ Scalars - total mean variance min max [rho] 261110.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] -1.1543906e+01 -4.4210890e-05 8.3500524e-04 -4.9999763e-02 4.9999902e-02 +Free energies - timestep f v f/v f_s a f_s/a +[fe] 0 -6.0796618987e+00 2.6111000000e+05 -2.3283910607e-05 0.0000000000e+00 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-spin-fd1.log b/tests/regression/d3q19-short/serial-spin-fd1.log index 998c2e65..1c4a4e5f 100644 --- a/tests/regression/d3q19-short/serial-spin-fd1.log +++ b/tests/regression/d3q19-short/serial-spin-fd1.log @@ -87,6 +87,9 @@ Scalars - total mean variance min max [rho] 262144.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] 3.1484764e+00 1.2010484e-05 8.3289934e-04 -4.9999916e-02 4.9999705e-02 +Free energy density - timestep total fluid +[fed] 0 -2.3227909424e-06 -2.3227909424e-06 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-spin-fd2.log b/tests/regression/d3q19-short/serial-spin-fd2.log index 65c08e47..7b726bca 100644 --- a/tests/regression/d3q19-short/serial-spin-fd2.log +++ b/tests/regression/d3q19-short/serial-spin-fd2.log @@ -97,6 +97,9 @@ Scalars - total mean variance min max [rho] 262144.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] 3.1484764e+00 1.2010484e-05 8.3289934e-04 -4.9999916e-02 4.9999705e-02 +Free energies - timestep f v f/v f_s1 fs_s2 +[fe] 0 -6.0642209766e-01 2.6214400000e+05 -2.3133167178e-06 0.0000000000e+00 0.0000000000e+00 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-spin-lb1.log b/tests/regression/d3q19-short/serial-spin-lb1.log index 1f376c60..ac447cd6 100644 --- a/tests/regression/d3q19-short/serial-spin-lb1.log +++ b/tests/regression/d3q19-short/serial-spin-lb1.log @@ -83,6 +83,9 @@ Scalars - total mean variance min max [rho] 262144.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] 3.1484764e+00 1.2010484e-05 8.3289934e-04 -4.9999916e-02 4.9999705e-02 +Free energy density - timestep total fluid +[fed] 0 -2.3227909424e-06 -2.3227909424e-06 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-spin-lb2.log b/tests/regression/d3q19-short/serial-spin-lb2.log index 60f21935..c15753b4 100644 --- a/tests/regression/d3q19-short/serial-spin-lb2.log +++ b/tests/regression/d3q19-short/serial-spin-lb2.log @@ -88,6 +88,9 @@ Scalars - total mean variance min max [rho] 32768.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] -5.9892076e+00 -1.8277611e-04 8.3100414e-04 -4.9989121e-02 4.9997941e-02 +Free energies - timestep f v f/v f_s1 fs_s2 +[fe] 0 -7.4302952403e-02 3.2768000000e+04 -2.2675461549e-06 0.0000000000e+00 0.0000000000e+00 + Momentum - x y z [total ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 [fluid ] 4.5474735e-13 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-spin-lb3.log b/tests/regression/d3q19-short/serial-spin-lb3.log index ba969270..06439124 100644 --- a/tests/regression/d3q19-short/serial-spin-lb3.log +++ b/tests/regression/d3q19-short/serial-spin-lb3.log @@ -85,6 +85,9 @@ Scalars - total mean variance min max [rho] 32768.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] -5.9892076e+00 -1.8277611e-04 8.3100414e-04 -4.9989121e-02 4.9997941e-02 +Free energy density - timestep total fluid +[fed] 0 -2.3206300630e-06 -2.3206300630e-06 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-spin-lb4.log b/tests/regression/d3q19-short/serial-spin-lb4.log index 02616f61..3aaa208d 100644 --- a/tests/regression/d3q19-short/serial-spin-lb4.log +++ b/tests/regression/d3q19-short/serial-spin-lb4.log @@ -85,6 +85,9 @@ Scalars - total mean variance min max [rho] 32768.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] -5.9892076e+00 -1.8277611e-04 8.3100414e-04 -4.9989121e-02 4.9997941e-02 +Free energy density - timestep total fluid +[fed] 0 -2.3206300630e-06 -2.3206300630e-06 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-spin-n01.log b/tests/regression/d3q19-short/serial-spin-n01.log index 7a3bb418..20b89690 100644 --- a/tests/regression/d3q19-short/serial-spin-n01.log +++ b/tests/regression/d3q19-short/serial-spin-n01.log @@ -87,6 +87,9 @@ Scalars - total mean variance min max [rho] 262144.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] 3.1484764e+00 1.2010484e-05 8.3289934e-04 -4.9999916e-02 4.9999705e-02 +Free energy density - timestep total fluid +[fed] 0 -2.3227909424e-06 -2.3227909424e-06 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-spin-n02.log b/tests/regression/d3q19-short/serial-spin-n02.log index 0e78eac8..62cd8118 100644 --- a/tests/regression/d3q19-short/serial-spin-n02.log +++ b/tests/regression/d3q19-short/serial-spin-n02.log @@ -87,6 +87,9 @@ Scalars - total mean variance min max [rho] 262144.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] 3.1484764e+00 1.2010484e-05 8.3289934e-04 -4.9999916e-02 4.9999705e-02 +Free energy density - timestep total fluid +[fed] 0 -2.3227909424e-06 -2.3227909424e-06 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-symm-dr1.log b/tests/regression/d3q19-short/serial-symm-dr1.log index f4297f6e..9b70d7cc 100644 --- a/tests/regression/d3q19-short/serial-symm-dr1.log +++ b/tests/regression/d3q19-short/serial-symm-dr1.log @@ -88,6 +88,9 @@ Scalars - total mean variance min max [rho] 262144.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] 2.2740611e+05 8.6748547e-01 2.1958671e-01 -1.0000000e+00 1.0000000e+00 +Free energy density - timestep total fluid +[fed] 0 -1.5105107994e-03 -1.5105107994e-03 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-symm-dr2.log b/tests/regression/d3q19-short/serial-symm-dr2.log index 49add1a3..3e3a70ea 100644 --- a/tests/regression/d3q19-short/serial-symm-dr2.log +++ b/tests/regression/d3q19-short/serial-symm-dr2.log @@ -95,6 +95,9 @@ Scalars - total mean variance min max [rho] 262144.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] 2.2740611e+05 8.6748547e-01 2.1958671e-01 -1.0000000e+00 1.0000000e+00 +Free energy density - timestep total fluid +[fed] 0 -1.5105107994e-03 -1.5105107994e-03 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-symm-pat.log b/tests/regression/d3q19-short/serial-symm-pat.log index f7f3b5e1..3409e168 100644 --- a/tests/regression/d3q19-short/serial-symm-pat.log +++ b/tests/regression/d3q19-short/serial-symm-pat.log @@ -87,6 +87,9 @@ Scalars - total mean variance min max [rho] 262144.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 [phi] 2.1006400e+05 8.0133057e-01 3.5786932e-01 -1.0000000e+00 1.0000000e+00 +Free energy density - timestep total fluid +[fed] 0 -1.2299442998e-03 -1.2299442998e-03 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-tern-st1.log b/tests/regression/d3q19-short/serial-tern-st1.log index 47340da8..3fa93f95 100644 --- a/tests/regression/d3q19-short/serial-tern-st1.log +++ b/tests/regression/d3q19-short/serial-tern-st1.log @@ -103,6 +103,9 @@ Scalars - total mean variance min max [phi] -4.7000000e+01 -2.1759259e-03 2.3281934e-01 -1.0000000e+00 1.0000000e+00 [phi] 1.6571000e+04 7.6717593e-01 1.7861702e-01 0.0000000e+00 1.0000000e+00 +Free energies +[surf/fl/tot] 0 0.0000000000e+00 5.3155555556e+00 5.3155555556e+00 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-tern-st2.log b/tests/regression/d3q19-short/serial-tern-st2.log index ae18debe..5b978c82 100644 --- a/tests/regression/d3q19-short/serial-tern-st2.log +++ b/tests/regression/d3q19-short/serial-tern-st2.log @@ -103,6 +103,9 @@ Scalars - total mean variance min max [phi] -4.7000000e+01 -2.1759259e-03 2.3281934e-01 -1.0000000e+00 1.0000000e+00 [phi] 1.6571000e+04 7.6717593e-01 1.7861702e-01 0.0000000e+00 1.0000000e+00 +Free energies +[surf/fl/tot] 0 0.0000000000e+00 5.3155555556e+00 5.3155555556e+00 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-tern-st3.log b/tests/regression/d3q19-short/serial-tern-st3.log index 4fde8b2a..f0524652 100644 --- a/tests/regression/d3q19-short/serial-tern-st3.log +++ b/tests/regression/d3q19-short/serial-tern-st3.log @@ -103,6 +103,9 @@ Scalars - total mean variance min max [phi] -4.7000000e+01 -2.1759259e-03 2.3281934e-01 -1.0000000e+00 1.0000000e+00 [phi] 1.6571000e+04 7.6717593e-01 1.7861702e-01 0.0000000e+00 1.0000000e+00 +Free energies +[surf/fl/tot] 0 0.0000000000e+00 5.3155555556e+00 5.3155555556e+00 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-tern-st4.log b/tests/regression/d3q19-short/serial-tern-st4.log index 1cd71997..0fe41848 100644 --- a/tests/regression/d3q19-short/serial-tern-st4.log +++ b/tests/regression/d3q19-short/serial-tern-st4.log @@ -118,6 +118,10 @@ Scalars - total mean variance min max [phi] -2.7000000e+02 -6.7500000e-03 6.7495444e-01 -1.0000000e+00 1.0000000e+00 [phi] 1.3000000e+04 3.2500000e-01 2.1937500e-01 0.0000000e+00 1.0000000e+00 +Free energies +[rho/phi/psi] 0 -4.0000000000e-01 -3.0000000000e-03 1.6500000000e-01 +[surf/fl/tot] 0 -2.3800000000e-01 3.2824083333e+00 3.0444083333e+00 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-short/serial-tern-st5.log b/tests/regression/d3q19-short/serial-tern-st5.log index 2dbc9650..529d50b5 100644 --- a/tests/regression/d3q19-short/serial-tern-st5.log +++ b/tests/regression/d3q19-short/serial-tern-st5.log @@ -117,6 +117,10 @@ Scalars - total mean variance min max [phi] -2.7000000e+02 -6.7500000e-03 6.7495444e-01 -1.0000000e+00 1.0000000e+00 [phi] 1.3000000e+04 3.2500000e-01 2.1937500e-01 0.0000000e+00 1.0000000e+00 +Free energies +[rho/phi/psi] 0 -4.0000000000e-01 -3.0000000000e-03 1.6500000000e-01 +[surf/fl/tot] 0 -2.3800000000e-01 3.2824083333e+00 3.0444083333e+00 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 From abee9c37d80f43757c9b18cdaa425e7c404d7184 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 10 Feb 2023 13:50:26 +0000 Subject: [PATCH 08/97] Add missing ncolloid initialisation --- src/ludwig.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ludwig.c b/src/ludwig.c index 2600802e..e156ee64 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -2358,7 +2358,9 @@ int ludwig_report_statistics(ludwig_t * ludwig, int itimestep) { double psi_zeta = 0.0; psi_colloid_rho_set(ludwig->psi, ludwig->collinfo); psi_stats_info(ludwig->psi); - /* Zeta potential for one colloid only to follow psi_stats()*/ + /* Zeta potential for one colloid only to follow psi_stats() */ + /* There should be an explicit option. */ + colloids_info_ntotal(ludwig->collinfo, &ncolloid); psi_colloid_zetapotential(ludwig->psi, ludwig->collinfo, &psi_zeta); if (ncolloid == 1) pe_info(ludwig->pe, "[psi_zeta] %14.7e\n", psi_zeta); } From d265b7955d65b94e0cf2be3040e758223ab4c1ba Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 10 Feb 2023 13:52:11 +0000 Subject: [PATCH 09/97] Free energy report at t=0 --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 79edc828..006664b5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ version 0.20.0 +- The free energy is now reported at t = 0 for the initial state. - An extra "first touch" option has been added. For details, see https://ludwig.epcc.ed.ac.uk/inputs/parallel.html From e5a9455aa8a1fcc728f4c6a95b92a052f8033b7e Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 10 Feb 2023 15:09:33 +0000 Subject: [PATCH 10/97] Avoid uninitialised warning --- src/blue_phase_beris_edwards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blue_phase_beris_edwards.c b/src/blue_phase_beris_edwards.c index 0c25b047..bf33cfdd 100644 --- a/src/blue_phase_beris_edwards.c +++ b/src/blue_phase_beris_edwards.c @@ -574,7 +574,7 @@ void beris_edw_kernel_v(kernel_ctxt_t * ktx, beris_edw_t * be, int status = 0; double q[3][3][NSIMDVL]; - double w[3][3][NSIMDVL]; + double w[3][3][NSIMDVL] = {0}; double d[3][3][NSIMDVL]; double s[3][3][NSIMDVL]; From be22615cfa87bfc99847dfdb65de654fae1cb8a2 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 10 Feb 2023 15:10:09 +0000 Subject: [PATCH 11/97] Add coverage via code-cov --- .github/workflows/codecov.yml | 21 +++++++++++++++++++++ config/coverage-gcc.mk | 26 ++++++++++++++++++++++++++ mpi_s/Makefile | 1 + src/Makefile | 1 + target/Makefile | 3 ++- tests/unit/Makefile | 3 ++- util/Makefile | 4 ++-- 7 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/codecov.yml create mode 100644 config/coverage-gcc.mk diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml new file mode 100644 index 00000000..2997aafc --- /dev/null +++ b/.github/workflows/codecov.yml @@ -0,0 +1,21 @@ +name: Codecov +on: [push, pull_request] +jobs: + run: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Test + run: | + cp config/coverage-gcc.mk config.mk + make serial + make + cd tests/unit + make test + gcov -abcfu tests.c + - name: Upload + uses: codecov/codecov-action@v3 + with: + gcov: true + gcov_include: tests/unit/tests.c diff --git a/config/coverage-gcc.mk b/config/coverage-gcc.mk new file mode 100644 index 00000000..b8138f15 --- /dev/null +++ b/config/coverage-gcc.mk @@ -0,0 +1,26 @@ +############################################################################## +# +# coverage-gcc.mk +# +# One for coverage +# +############################################################################## + +BUILD = serial +MODEL = -D_D3Q27_ + +GCOV = -ftest-coverage -fprofile-arcs + +CC = gcc +CFLAGS = -fopenmp $(GCOV) -O2 -g -Wall + +AR = ar +ARFLAGS = -cru +LDFLAGS = -fopenmp $(GCOV) + +MPI_INC_PATH = ./mpi_s +MPI_LIB_PATH = ./mpi_s +MPI_LIB = -lmpi + +LAUNCH_SERIAL_CMD = +LAUNCH_MPIRUN_CMD = diff --git a/mpi_s/Makefile b/mpi_s/Makefile index b1098c63..c063f437 100644 --- a/mpi_s/Makefile +++ b/mpi_s/Makefile @@ -32,3 +32,4 @@ test: mpi_tests.c $(LIBNAME) Makefile clean: rm -rf mpi_serial.o $(LIBNAME) a.out + rm -f *gcno *gcda diff --git a/src/Makefile b/src/Makefile index c2f9154c..49a66450 100644 --- a/src/Makefile +++ b/src/Makefile @@ -99,3 +99,4 @@ util_sum.o: util_sum.c .PHONY : clean clean: rm -f *.o $(EXECUTABLE) $(LIBRARY) + rm -f *.gcno *.gcda diff --git a/target/Makefile b/target/Makefile index f8660266..ba18e152 100644 --- a/target/Makefile +++ b/target/Makefile @@ -13,7 +13,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: # Alan Gray (alang@epcc.ed.ac.uk) @@ -62,3 +62,4 @@ testmpi: clean: rm -f *.o *.a a.out + rm -f *gcno *gcda diff --git a/tests/unit/Makefile b/tests/unit/Makefile index 5805fcb7..35a2e14c 100644 --- a/tests/unit/Makefile +++ b/tests/unit/Makefile @@ -12,7 +12,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) @@ -61,6 +61,7 @@ test: clean: $(RM) core *.o a.out + $(RM) -f *gcno *gcda #------------------------------------------------------------------------------ # Implicit Rules diff --git a/util/Makefile b/util/Makefile index 21db9b47..2be135c5 100644 --- a/util/Makefile +++ b/util/Makefile @@ -11,7 +11,7 @@ # Edinburgh Soft Matter and Statistical Physics Group and # Edinburgh Parallel Computing Centre # -# (c) 2016-2021 The University of Edinburgh +# (c) 2016-2023 The University of Edinburgh # # Contributing authors: # Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -71,10 +71,10 @@ polarizer: polarizer.o clean: rm -f *.o colloid_init extract_colloids capillary extract \ coll_squ_subgrid_init multi_poly_init polarizer + rm -f *gcda *gcno .SUFFIXES: .SUFFIXES: .c .o .c.o: $(CC) $(CFLAGS) $(INCL) -c $*.c - From 29131c45fdc8c744dde401291be711de539aca82 Mon Sep 17 00:00:00 2001 From: ludwig-cf <40301532+ludwig-cf@users.noreply.github.com> Date: Fri, 10 Feb 2023 15:33:45 +0000 Subject: [PATCH 12/97] Update codecov.yml --- .github/workflows/codecov.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 2997aafc..423ceae5 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -8,12 +8,12 @@ jobs: uses: actions/checkout@v3 - name: Test run: | - cp config/coverage-gcc.mk config.mk + cp config/coverage-gcc.mk config.mk make serial - make + make cd tests/unit make test - gcov -abcfu tests.c + gcov -abcfu tests.c - name: Upload uses: codecov/codecov-action@v3 with: From f341fe6efaed9c02e9c77097fc5b705ea23f43f9 Mon Sep 17 00:00:00 2001 From: ludwig-cf <40301532+ludwig-cf@users.noreply.github.com> Date: Fri, 10 Feb 2023 15:37:44 +0000 Subject: [PATCH 13/97] Update codecov.yml --- .github/workflows/codecov.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 423ceae5..bcaee0e5 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -8,12 +8,12 @@ jobs: uses: actions/checkout@v3 - name: Test run: | - cp config/coverage-gcc.mk config.mk + cp config/coverage-gcc.mk config.mk make serial make - cd tests/unit - make test - gcov -abcfu tests.c + cd tests/unit + make test + gcov -abcfu tests.c - name: Upload uses: codecov/codecov-action@v3 with: From 8ba5108b686c12a9d10794082a2cde28f21c03ac Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 10 Feb 2023 16:09:39 +0000 Subject: [PATCH 14/97] Add coverage report --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 8d77e019..213574e5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ version 0.20.0 +- Added coverage via https://about.codecov.io/ - The free energy is now reported at t = 0 for the initial state. - An extra "first touch" option has been added. For details, see https://ludwig.epcc.ed.ac.uk/inputs/parallel.html From 9aa0f86ac22210771df11cd614c2370e680bf72d Mon Sep 17 00:00:00 2001 From: kevinstratford Date: Fri, 10 Feb 2023 16:29:28 +0000 Subject: [PATCH 15/97] Update codecov.yml --- .github/workflows/codecov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index bcaee0e5..590c9ccb 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -18,4 +18,4 @@ jobs: uses: codecov/codecov-action@v3 with: gcov: true - gcov_include: tests/unit/tests.c + gcov_include: src/*.c From 34a112485134d0224470aa419b98ff09759f3213 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 10 Feb 2023 16:54:06 +0000 Subject: [PATCH 16/97] Use recommended uploader --- .github/workflows/codecov.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 590c9ccb..742c7b37 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -6,16 +6,14 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 - - name: Test + - name: Build and run the unit tests run: | cp config/coverage-gcc.mk config.mk make serial make - cd tests/unit - make test - gcov -abcfu tests.c - - name: Upload - uses: codecov/codecov-action@v3 - with: - gcov: true - gcov_include: src/*.c + make unit + - name: Upload results + run: | + curl -Os https://uploader.codecov.io/latest/linux/codecov + chmod +x codecov + ./codecov -t ${CODECOV_TOKEN} From 762a10aa4929875a6fdff7be61de4e64af28f9c2 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 10 Feb 2023 17:14:20 +0000 Subject: [PATCH 17/97] Add gcov for src directory --- .github/workflows/codecov.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 742c7b37..a1adf4d1 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -12,6 +12,8 @@ jobs: make serial make make unit + cd src + gcov -abcfu *.c - name: Upload results run: | curl -Os https://uploader.codecov.io/latest/linux/codecov From 92197f22b35fe4bd1d52f9ee3ccfcd88f0831476 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 13 Feb 2023 11:22:19 +0000 Subject: [PATCH 18/97] Add free energy at time zero --- tests/regression/d3q19-elec/serial-elec-do1.log | 3 +++ tests/regression/d3q19-elec/serial-elec-do2.log | 3 +++ tests/regression/d3q19-elec/serial-elec-do3.log | 3 +++ tests/regression/d3q19-elec/serial-elec-dr1.log | 3 +++ tests/regression/d3q19-elec/serial-elec-dr2.log | 3 +++ tests/regression/d3q19-elec/serial-elec-eo1.log | 3 +++ tests/regression/d3q19-elec/serial-elec-eo2.log | 3 +++ tests/regression/d3q19-elec/serial-elec-ep1.log | 3 +++ tests/regression/d3q19-elec/serial-elec-ep2.log | 3 +++ tests/regression/d3q19-elec/serial-elec-gc1.log | 3 +++ tests/regression/d3q19-elec/serial-elec-gc2.log | 3 +++ 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 | 16 +++++++++++----- tests/regression/d3q19-elec/serial-rest-ec2.log | 10 +++++++--- 17 files changed, 67 insertions(+), 8 deletions(-) diff --git a/tests/regression/d3q19-elec/serial-elec-do1.log b/tests/regression/d3q19-elec/serial-elec-do1.log index 7dc44473..560e1e24 100644 --- a/tests/regression/d3q19-elec/serial-elec-do1.log +++ b/tests/regression/d3q19-elec/serial-elec-do1.log @@ -133,6 +133,9 @@ Scalars - total mean variance min max [rho] 1.9251200e+00 4.7000000e-04 4.7000000e-04 [elc] 0.0000000e+00 0.0000000e+00 0.0000000e+00 +Free energy density - timestep total fluid +[fed] 0 -1.0518099887e-02 -1.0518099887e-02 + Momentum - x y z [total ] 0.0000000e+00 1.1368684e-13 0.0000000e+00 [fluid ] 0.0000000e+00 1.1368684e-13 0.0000000e+00 diff --git a/tests/regression/d3q19-elec/serial-elec-do2.log b/tests/regression/d3q19-elec/serial-elec-do2.log index f786c929..abc9b44c 100644 --- a/tests/regression/d3q19-elec/serial-elec-do2.log +++ b/tests/regression/d3q19-elec/serial-elec-do2.log @@ -133,6 +133,9 @@ Scalars - total mean variance min max [rho] 1.9251200e+00 4.7000000e-04 4.7000000e-04 [elc] 0.0000000e+00 0.0000000e+00 0.0000000e+00 +Free energy density - timestep total fluid +[fed] 0 -1.0518099887e-02 -1.0518099887e-02 + Momentum - x y z [total ] 0.0000000e+00 1.1368684e-13 0.0000000e+00 [fluid ] 0.0000000e+00 1.1368684e-13 0.0000000e+00 diff --git a/tests/regression/d3q19-elec/serial-elec-do3.log b/tests/regression/d3q19-elec/serial-elec-do3.log index c67762ca..2afd21f7 100644 --- a/tests/regression/d3q19-elec/serial-elec-do3.log +++ b/tests/regression/d3q19-elec/serial-elec-do3.log @@ -133,6 +133,9 @@ Scalars - total mean variance min max [rho] 1.9251200e+00 4.7000000e-04 4.7000000e-04 [elc] 0.0000000e+00 0.0000000e+00 0.0000000e+00 +Free energy density - timestep total fluid +[fed] 0 -1.0048099887e-02 -1.0048099887e-02 + Momentum - x y z [total ] 0.0000000e+00 1.1368684e-13 0.0000000e+00 [fluid ] 0.0000000e+00 1.1368684e-13 0.0000000e+00 diff --git a/tests/regression/d3q19-elec/serial-elec-dr1.log b/tests/regression/d3q19-elec/serial-elec-dr1.log index 98f40d07..37bf1188 100644 --- a/tests/regression/d3q19-elec/serial-elec-dr1.log +++ b/tests/regression/d3q19-elec/serial-elec-dr1.log @@ -135,6 +135,9 @@ Scalars - total mean variance min max [rho] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [elc] 0.0000000e+00 0.0000000e+00 0.0000000e+00 +Free energy density - timestep total fluid +[fed] 0 -2.0073826936e-04 -2.0073826936e-04 + Momentum - x y z [total ] 0.0000000e+00 2.2737368e-13 0.0000000e+00 [fluid ] 0.0000000e+00 2.2737368e-13 0.0000000e+00 diff --git a/tests/regression/d3q19-elec/serial-elec-dr2.log b/tests/regression/d3q19-elec/serial-elec-dr2.log index 8abcb078..ca382985 100644 --- a/tests/regression/d3q19-elec/serial-elec-dr2.log +++ b/tests/regression/d3q19-elec/serial-elec-dr2.log @@ -135,6 +135,9 @@ Scalars - total mean variance min max [rho] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [elc] 0.0000000e+00 0.0000000e+00 0.0000000e+00 +Free energy density - timestep total fluid +[fed] 0 -2.0073826936e-04 -2.0073826936e-04 + Momentum - x y z [total ] 0.0000000e+00 2.2737368e-13 0.0000000e+00 [fluid ] 0.0000000e+00 2.2737368e-13 0.0000000e+00 diff --git a/tests/regression/d3q19-elec/serial-elec-eo1.log b/tests/regression/d3q19-elec/serial-elec-eo1.log index bd4ed9ee..f6d8e141 100644 --- a/tests/regression/d3q19-elec/serial-elec-eo1.log +++ b/tests/regression/d3q19-elec/serial-elec-eo1.log @@ -109,6 +109,9 @@ Scalars - total mean variance min max [rho] 1.0000000e-01 0.0000000e+00 4.0322581e-04 [elc] -1.4224733e-16 -4.0322581e-04 1.2500000e-02 +Free energies - timestep f v f/v f_s1 fs_s2 +[fe] 0 -8.8160138392e-01 2.4800000000e+02 -3.5548442900e-03 0.0000000000e+00 0.0000000000e+00 + Momentum - x y z [total ] 3.4416914e-15 0.0000000e+00 0.0000000e+00 [fluid ] 3.4416914e-15 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-elec/serial-elec-eo2.log b/tests/regression/d3q19-elec/serial-elec-eo2.log index cc29cd10..e6856dcb 100644 --- a/tests/regression/d3q19-elec/serial-elec-eo2.log +++ b/tests/regression/d3q19-elec/serial-elec-eo2.log @@ -111,6 +111,9 @@ Scalars - total mean variance min max [rho] 4.0000000e-01 0.0000000e+00 4.0322581e-04 [elc] -1.0165480e-15 -4.0322581e-04 1.2500000e-02 +Free energies - timestep f v f/v f_s1 fs_s2 +[fe] 0 -3.5264055357e+00 9.9200000000e+02 -3.5548442900e-03 0.0000000000e+00 0.0000000000e+00 + Momentum - x y z [total ] 1.3766766e-14 0.0000000e+00 0.0000000e+00 [fluid ] 1.3766766e-14 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-elec/serial-elec-ep1.log b/tests/regression/d3q19-elec/serial-elec-ep1.log index f2eb0049..fb814029 100644 --- a/tests/regression/d3q19-elec/serial-elec-ep1.log +++ b/tests/regression/d3q19-elec/serial-elec-ep1.log @@ -129,6 +129,9 @@ Scalars - total mean variance min max [rho] 1.3381768e+02 0.0000000e+00 4.1153145e-03 [elc] -1.1347338e-11 -1.5376572e-03 1.9920319e-01 +Free energies - timestep f v f/v f_s a f_s/a +[fe] 0 -1.1349791372e+03 3.2517000000e+04 -3.4904177422e-02 0.0000000000e+00 + Momentum - x y z [total ] 0.0000000e+00 9.0252805e-13 0.0000000e+00 [fluid ] 0.0000000e+00 9.0252805e-13 0.0000000e+00 diff --git a/tests/regression/d3q19-elec/serial-elec-ep2.log b/tests/regression/d3q19-elec/serial-elec-ep2.log index 708519c9..46578c4b 100644 --- a/tests/regression/d3q19-elec/serial-elec-ep2.log +++ b/tests/regression/d3q19-elec/serial-elec-ep2.log @@ -129,6 +129,9 @@ Scalars - total mean variance min max [rho] 1.3381768e+02 0.0000000e+00 4.1153145e-03 [elc] -1.1347338e-11 -1.5376572e-03 1.9920319e-01 +Free energies - timestep f v f/v f_s a f_s/a +[fe] 0 -1.1349791372e+03 3.2517000000e+04 -3.4904177422e-02 0.0000000000e+00 + Momentum - x y z [total ] 0.0000000e+00 9.0252805e-13 0.0000000e+00 [fluid ] 0.0000000e+00 9.0252805e-13 0.0000000e+00 diff --git a/tests/regression/d3q19-elec/serial-elec-gc1.log b/tests/regression/d3q19-elec/serial-elec-gc1.log index da36116e..9e7d3c8a 100644 --- a/tests/regression/d3q19-elec/serial-elec-gc1.log +++ b/tests/regression/d3q19-elec/serial-elec-gc1.log @@ -99,6 +99,9 @@ Scalars - total mean variance min max [rho] 1.9920000e+00 0.0000000e+00 2.0080645e-03 [elc] -1.2878587e-14 -1.0080645e-03 3.1250000e-02 +Free energy density - timestep total fluid +[fed] 0 -2.6048547231e-02 -2.2387073046e-02 + Momentum - x y z [total ] 1.3766766e-14 0.0000000e+00 0.0000000e+00 [fluid ] 1.3766766e-14 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-elec/serial-elec-gc2.log b/tests/regression/d3q19-elec/serial-elec-gc2.log index 90524806..021ff92f 100644 --- a/tests/regression/d3q19-elec/serial-elec-gc2.log +++ b/tests/regression/d3q19-elec/serial-elec-gc2.log @@ -112,6 +112,9 @@ Scalars - total mean variance min max [rho] 1.9920000e+00 0.0000000e+00 2.0080645e-03 [elc] -2.3203661e-14 -1.0080645e-03 3.1250000e-02 +Free energy density - timestep total fluid +[fed] 0 -2.6048547231e-02 -2.2387073046e-02 + Momentum - x y z [total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 [fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d3q19-elec/serial-elec-rr1.log b/tests/regression/d3q19-elec/serial-elec-rr1.log index 72115f69..2feb3bb9 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr1.log +++ b/tests/regression/d3q19-elec/serial-elec-rr1.log @@ -125,6 +125,10 @@ Scalars - total mean variance min max [rho] 3.2726000e+01 1.9230769e-04 1.0000000e-03 [rho] 3.2726000e+01 0.0000000e+00 1.0003057e-03 [elc] -1.4922959e-15 -3.0566084e-07 1.9230769e-04 +[psi_zeta] 0.0000000e+00 + +Free energies - timestep f v f/v f_s a f_s/a +[fe] 0 -5.1748931944e+02 3.2716000000e+04 -1.5817621942e-02 0.0000000000e+00 Momentum - x y z [total ] 4.5402571e-13 0.0000000e+00 5.0965010e-01 diff --git a/tests/regression/d3q19-elec/serial-elec-rr2.log b/tests/regression/d3q19-elec/serial-elec-rr2.log index 5a59df0e..700dedf1 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr2.log +++ b/tests/regression/d3q19-elec/serial-elec-rr2.log @@ -127,6 +127,10 @@ Scalars - total mean variance min max [rho] 3.2725000e+01 1.8867925e-04 1.0000000e-03 [rho] 3.2725000e+01 0.0000000e+00 1.0003057e-03 [elc] 6.5021773e-15 -3.0567018e-07 1.8867925e-04 +[psi_zeta] 0.0000000e+00 + +Free energies - timestep f v f/v f_s a f_s/a +[fe] 0 -5.1747350393e+02 3.2715000000e+04 -1.5817622006e-02 0.0000000000e+00 Momentum - x y z [total ] 4.5401183e-13 0.0000000e+00 5.0965010e-01 diff --git a/tests/regression/d3q19-elec/serial-elec-rr3.log b/tests/regression/d3q19-elec/serial-elec-rr3.log index bee9c984..97307eb7 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr3.log +++ b/tests/regression/d3q19-elec/serial-elec-rr3.log @@ -125,6 +125,10 @@ Scalars - total mean variance min max [rho] 3.2726000e+01 1.9230769e-04 1.0000000e-03 [rho] 3.2726000e+01 0.0000000e+00 1.0003057e-03 [elc] -1.4933259e-15 -3.0566084e-07 1.9230769e-04 +[psi_zeta] 0.0000000e+00 + +Free energies - timestep f v f/v f_s a f_s/a +[fe] 0 -5.1748931944e+02 3.2716000000e+04 -1.5817621942e-02 0.0000000000e+00 Momentum - x y z [total ] 4.5402571e-13 0.0000000e+00 5.0965010e-01 diff --git a/tests/regression/d3q19-elec/serial-elec-rr4.log b/tests/regression/d3q19-elec/serial-elec-rr4.log index 81f572dd..27707130 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr4.log +++ b/tests/regression/d3q19-elec/serial-elec-rr4.log @@ -127,6 +127,10 @@ Scalars - total mean variance min max [rho] 3.2725000e+01 1.8867925e-04 1.0000000e-03 [rho] 3.2725000e+01 0.0000000e+00 1.0003057e-03 [elc] 6.5746562e-15 -3.0567018e-07 1.8867925e-04 +[psi_zeta] 0.0000000e+00 + +Free energies - timestep f v f/v f_s a f_s/a +[fe] 0 -5.1747350393e+02 3.2715000000e+04 -1.5817622006e-02 0.0000000000e+00 Momentum - x y z [total ] 4.5401183e-13 0.0000000e+00 5.0965010e-01 diff --git a/tests/regression/d3q19-elec/serial-rest-ec1.log b/tests/regression/d3q19-elec/serial-rest-ec1.log index 685b6116..a533f0c3 100644 --- a/tests/regression/d3q19-elec/serial-rest-ec1.log +++ b/tests/regression/d3q19-elec/serial-rest-ec1.log @@ -125,6 +125,10 @@ Scalars - total mean variance min max [rho] 3.2726000e+01 1.9230769e-04 1.0000000e-03 [rho] 3.2726000e+01 0.0000000e+00 1.0003057e-03 [elc] -1.4922959e-15 -3.0566084e-07 1.9230769e-04 +[psi_zeta] 0.0000000e+00 + +Free energies - timestep f v f/v f_s a f_s/a +[fe] 0 -5.1748931944e+02 3.2716000000e+04 -1.5817621942e-02 0.0000000000e+00 Momentum - x y z [total ] 4.5402571e-13 0.0000000e+00 5.0965010e-01 @@ -142,6 +146,13 @@ Colloid velocities - x y z SOR solver converged to relative tolerance SOR residual per site 3.8552497e-12 at 100 iterations 1 multisteps +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 psi file at step 20! +Writing rho/velocity output at step 20! Scalars - total mean variance min max [rho] 32715.00 1.00000000000 5.7030058e-09 0.99976080725 1.00025312238 @@ -165,11 +176,6 @@ Velocity - x y z Completed cycle 20 -colloid_io_write: -writing colloid information to config.cds00000020.001-001 etc -Writing velocity output at step 20! -Writing psi file at step 20! - Timer resolution: 0.01 second Timer statistics diff --git a/tests/regression/d3q19-elec/serial-rest-ec2.log b/tests/regression/d3q19-elec/serial-rest-ec2.log index 2f77527e..151dbdcd 100644 --- a/tests/regression/d3q19-elec/serial-rest-ec2.log +++ b/tests/regression/d3q19-elec/serial-rest-ec2.log @@ -106,9 +106,9 @@ Input radius maximum: 2.3000000e+00 Final cell list: 11 11 11 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 electrokinetics files(s) psi-00000020 Initial conditions. @@ -118,6 +118,10 @@ Scalars - total mean variance min max [rho] 3.2726000e+01 1.8867925e-04 1.0413963e-03 [rho] 3.2726000e+01 0.0000000e+00 1.0436820e-03 [elc] 2.4008573e-15 -2.2857042e-06 1.8867925e-04 +[psi_zeta] 8.2502980e-03 + +Free energies - timestep f v f/v f_s a f_s/a +[fe] 20 -5.1748728127e+02 3.2715000000e+04 -1.5818043138e-02 0.0000000000e+00 Momentum - x y z [total ] 4.4153432e-13 1.9531615e-14 5.0965010e-01 From 40b825a0585f475eae07ae4b07a9492457af7b94 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 15 Feb 2023 15:23:00 +0000 Subject: [PATCH 19/97] Remove extraneous space --- src/timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timer.c b/src/timer.c index a25ab2ab..5141a120 100644 --- a/src/timer.c +++ b/src/timer.c @@ -90,7 +90,7 @@ static const char * timer_name[] = {"Total", "Poisson equation", "Nernst Planck", "Lap timer (no report)", - "Diagnostics / output ", + "Diagnostics / output", "Free2", "Free3", "Free4", "Free5", "Free6" }; From e1c03cda024bea36d5d6ef0e6eb1e36dd2bbb3e3 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 18 Feb 2023 11:30:35 +0000 Subject: [PATCH 20/97] Avoid passing large object by value --- tests/unit/test_blue_phase.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/tests/unit/test_blue_phase.c b/tests/unit/test_blue_phase.c index 716b4739..7c9f81ac 100644 --- a/tests/unit/test_blue_phase.c +++ b/tests/unit/test_blue_phase.c @@ -44,7 +44,7 @@ int test_fe_lc_dimensionless_field_strength(pe_t * pe); __host__ int do_test_fe_lc_device1(pe_t * pe, cs_t * cs, fe_lc_t * fe); -__global__ void do_test_fe_lc_kernel1(fe_lc_t * fe, fe_lc_param_t ref); +__global__ void do_test_fe_lc_kernel1(fe_lc_t * fe, const fe_lc_param_t * ref); /***************************************************************************** @@ -959,8 +959,15 @@ __host__ int do_test_fe_lc_device1(pe_t * pe, cs_t * cs, fe_lc_t * fe) { kernel_launch_param(1, &nblk, &ntpb); ntpb.x = 1; - tdpLaunchKernel(do_test_fe_lc_kernel1, nblk, ntpb, 0, 0, fetarget, param); - tdpDeviceSynchronize(); + { + fe_lc_param_t * p = NULL; + tdpAssert(tdpMalloc((void **) &p, sizeof(fe_lc_param_t))); + tdpAssert(tdpMemcpy(p, ¶m, sizeof(fe_lc_param_t), + tdpMemcpyHostToDevice)); + tdpLaunchKernel(do_test_fe_lc_kernel1, nblk, ntpb, 0, 0, fetarget, p); + tdpDeviceSynchronize(); + tdpAssert(tdpFree(p)); + } physics_free(phys); @@ -973,7 +980,8 @@ __host__ int do_test_fe_lc_device1(pe_t * pe, cs_t * cs, fe_lc_t * fe) { * *****************************************************************************/ -__global__ void do_test_fe_lc_kernel1(fe_lc_t * fe, fe_lc_param_t ref) { +__global__ void do_test_fe_lc_kernel1(fe_lc_t * fe, + const fe_lc_param_t * pref) { fe_lc_param_t p; PI_DOUBLE(pi); @@ -984,11 +992,11 @@ __global__ void do_test_fe_lc_kernel1(fe_lc_t * fe, fe_lc_param_t ref) { /* epsilon is sclaed by a factor of 12pi within fe_lc */ - test_assert(fabs(p.a0 - ref.a0) < DBL_EPSILON); - test_assert(fabs(p.gamma - ref.gamma) < DBL_EPSILON); - test_assert(fabs(p.kappa0 - ref.kappa0) < DBL_EPSILON); - test_assert(fabs(12.0*pi*p.epsilon - ref.epsilon) < FLT_EPSILON); - test_assert(fabs(p.redshift - ref.redshift) < DBL_EPSILON); + test_assert(fabs(p.a0 - pref->a0) < DBL_EPSILON); + test_assert(fabs(p.gamma - pref->gamma) < DBL_EPSILON); + test_assert(fabs(p.kappa0 - pref->kappa0) < DBL_EPSILON); + test_assert(fabs(12.0*pi*p.epsilon - pref->epsilon) < FLT_EPSILON); + test_assert(fabs(p.redshift - pref->redshift) < DBL_EPSILON); return; } From a847427233165838b76eb8251a02150424ea46c8 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 18 Feb 2023 12:24:12 +0000 Subject: [PATCH 21/97] Avoid passing large object by value --- src/gradient_2d_ternary_solid.c | 24 +++++++++++++++--------- src/stats_distribution.c | 19 +++++++++++-------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/gradient_2d_ternary_solid.c b/src/gradient_2d_ternary_solid.c index cc8257cf..dc6b0ebf 100644 --- a/src/gradient_2d_ternary_solid.c +++ b/src/gradient_2d_ternary_solid.c @@ -11,7 +11,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2019-2021 The University of Edinburgh + * (c) 2019-2023 The University of Edinburgh * * Contributing authors: * Shan Chen (chan.chen@epfl.ch) @@ -26,10 +26,14 @@ #include "kernel.h" #include "gradient_2d_ternary_solid.h" +typedef struct wetting_s { + double hrka[3]; /* Wetting gradients phi, psi, rho */ +} wetting_t; + typedef struct solid_s { map_t * map; /* Map structure reference */ fe_ternary_param_t fe; /* Free energy parameters (copy) */ - double hrka[3]; /* Wetting gradients phi, psi, rho */ + wetting_t wetting; /* Wetting parameters */ } solid_t; static solid_t static_solid = {0}; @@ -49,8 +53,8 @@ static __constant__ double wv[NGRAD_] = {w0, w2, w1, w2, w1, w1, w2, w1, w2}; __global__ void grad_2d_ternary_solid_kernel(kernel_ctxt_t * ktx, field_grad_t * fg, - map_t * map, solid_t fe); - + map_t * map, + wetting_t wet); /***************************************************************************** * @@ -98,8 +102,9 @@ __host__ int grad_2d_ternary_solid_fe_set(fe_ternary_t * fe) { k3 = fe->param->kappa3; a2 = fe->param->alpha*fe->param->alpha; - static_solid.hrka[0] = (-h1/k1 + h2/k2)/a2; /* phi */ - static_solid.hrka[1] = (-h3/k3 )/a2; /* psi */ + static_solid.wetting.hrka[0] = (-h1/k1 + h2/k2)/a2; /* phi */ + static_solid.wetting.hrka[1] = (-h3/k3 )/a2; /* psi */ + static_solid.wetting.hrka[2] = 0.0; /* not used */ return 0; } @@ -133,7 +138,7 @@ __host__ int grad_2d_ternary_solid_d2(field_grad_t * fgrad) { tdpLaunchKernel(grad_2d_ternary_solid_kernel, nblk, ntpb, 0, 0, ctxt->target, fgrad->target, static_solid.map->target, - static_solid); + static_solid.wetting); tdpAssert(tdpPeekAtLastError()); tdpAssert(tdpDeviceSynchronize()); @@ -151,7 +156,8 @@ __host__ int grad_2d_ternary_solid_d2(field_grad_t * fgrad) { __global__ void grad_2d_ternary_solid_kernel(kernel_ctxt_t * ktx, field_grad_t * fg, - map_t * map, solid_t fe) { + map_t * map, + wetting_t wet) { int kindex; int kiterations; @@ -209,7 +215,7 @@ __global__ void grad_2d_ternary_solid_kernel(kernel_ctxt_t * ktx, if (isite[p] == -1) { /* Wetting condition */ - dphi = fe.hrka[n]; + dphi = wet.hrka[n]; } else { /* Fluid */ diff --git a/src/stats_distribution.c b/src/stats_distribution.c index 547f0a13..40f5e5ee 100644 --- a/src/stats_distribution.c +++ b/src/stats_distribution.c @@ -11,7 +11,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) * @@ -32,13 +32,15 @@ typedef struct gm_util_s { int8_t cv[27][3]; } gm_util_t; +static __constant__ gm_util_t util_; + __host__ int stats_distribution_momentum_serial(lb_t * lb, map_t * map, double g[3]); __host__ int distribution_stats_momentum(lb_t * lb, map_t * map, int root, MPI_Comm comm, double gm[3]); __global__ void distribution_gm_kernel(kernel_ctxt_t * ktxt, lb_t * lb, - map_t * map, gm_util_t u, kahan_t * gm); + map_t * map, kahan_t * gm); /***************************************************************************** @@ -221,6 +223,8 @@ __host__ int distribution_stats_momentum(lb_t * lb, map_t * map, int root, util.cv[p][Y] = lb->model.cv[p][Y]; util.cv[p][Z] = lb->model.cv[p][Z]; } + tdpMemcpyToSymbol(tdpSymbol(util_), &util, sizeof(gm_util_t), 0, + tdpMemcpyHostToDevice); /* Local kernel */ @@ -233,7 +237,7 @@ __host__ int distribution_stats_momentum(lb_t * lb, map_t * map, int root, kernel_ctxt_launch_param(ctxt, &nblk, &ntpb); tdpLaunchKernel(distribution_gm_kernel, nblk, ntpb, 0, 0, - ctxt->target, lb->target, map->target, util, sum_d); + ctxt->target, lb->target, map->target, sum_d); tdpAssert(tdpPeekAtLastError()); tdpAssert(tdpDeviceSynchronize()); @@ -275,8 +279,7 @@ __host__ int distribution_stats_momentum(lb_t * lb, map_t * map, int root, *****************************************************************************/ __global__ void distribution_gm_kernel(kernel_ctxt_t * ktx, lb_t * lb, - map_t * map, gm_util_t util, - kahan_t * gm) { + map_t * map, kahan_t * gm) { assert(ktx); assert(lb); @@ -317,9 +320,9 @@ __global__ void distribution_gm_kernel(kernel_ctxt_t * ktx, lb_t * lb, if (status == MAP_FLUID) { for (int p = 1; p < lb->nvel; p++) { double f = lb->f[LB_ADDR(lb->nsite,lb->ndist,lb->nvel,index,LB_RHO,p)]; - double gxf = f*util.cv[p][X]; - double gyf = f*util.cv[p][Y]; - double gzf = f*util.cv[p][Z]; + double gxf = f*util_.cv[p][X]; + double gyf = f*util_.cv[p][Y]; + double gzf = f*util_.cv[p][Z]; kahan_add_double(&gx[tid], gxf); kahan_add_double(&gy[tid], gyf); kahan_add_double(&gz[tid], gzf); From 6e5aa4fd31d03dc2faecbd04153da18b82a28457 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 18 Feb 2023 13:27:30 +0000 Subject: [PATCH 22/97] Add smoke test for util_jacobi() --- tests/unit/test_util.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/unit/test_util.c b/tests/unit/test_util.c index 1fcfc03d..f812d2e0 100644 --- a/tests/unit/test_util.c +++ b/tests/unit/test_util.c @@ -28,6 +28,7 @@ #define STAT_TOLERANCE 0.001 int util_random_unit_vector_check(void); +int util_jacobi_check(void); int util_str_tolower_check(void); int util_rectangle_conductance_check(void); @@ -45,6 +46,7 @@ int test_util_suite(void) { util_random_unit_vector_check(); + util_jacobi_check(); util_str_tolower_check(); util_rectangle_conductance_check(); @@ -107,6 +109,35 @@ int util_random_unit_vector_check(void) { return 0; } +/***************************************************************************** + * + * util_jacobi_check + * + *****************************************************************************/ + +int util_jacobi_check(void) { + + int ifail = 0; + + { + double a[3][3] = {0}; + double evals[3] = {0}; + double evecs[3][3] = {0}; + + ifail = util_jacobi(a, evals, evecs); + assert(ifail == 0); + if (evals[0] != 0.0) ifail = -1; + if (evals[1] != 0.0) ifail = -2; + if (evals[2] != 0.0) ifail = -3; + if (evecs[0][0] != 1.0) ifail = -4; + if (evecs[1][1] != 1.0) ifail = -5; + if (evecs[2][2] != 1.0) ifail = -6; + assert(ifail == 0); + } + + return 0; +} + /***************************************************************************** * * util_str_tolower_check From a312c4d4dc09557b3d074cf6305cc39bb52efeeb Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 18 Feb 2023 14:07:04 +0000 Subject: [PATCH 23/97] Avoid floating point comparison alerts --- src/util.c | 45 +++++---------------------------------------- src/util.h | 4 +--- 2 files changed, 6 insertions(+), 43 deletions(-) diff --git a/src/util.c b/src/util.c index 87b8636a..d41af532 100644 --- a/src/util.c +++ b/src/util.c @@ -349,13 +349,14 @@ __host__ int util_jacobi(double a[3][3], double vals[3], double vecs[3][3]) { g = 100.0*fabs(a[ia][ib]); - if (iterate > 4 && (fabs(vals[ia]) + g) == fabs(vals[ia]) && - (fabs(vals[ib]) + g) == fabs(vals[ib])) { + if (iterate > 4 && (((fabs(vals[ia]) + g) - fabs(vals[ia])) == 0.0) && + (((fabs(vals[ib]) + g) - fabs(vals[ib])) == 0.0)) { a[ia][ib] = 0.0; } else if (fabs(a[ia][ib]) > tresh) { h = vals[ib] - vals[ia]; - if ((fabs(h) + g) == fabs(h)) { + if (((fabs(h) + g) - fabs(h)) == 0.0) { + assert(0); t = (a[ia][ib])/h; } else { @@ -594,41 +595,6 @@ int util_gauss_jordan(const int n, double * a, double * b) { return 0; } -/***************************************************************************** - * - * util_vector_create - * - *****************************************************************************/ - -__host__ -int util_vector_create(int n, double ** p) { - - int ifail = 0; - double * v = NULL; - - v = (double*) calloc(n, sizeof(double)); - if (v == NULL) ifail = 1; - - *p = v; - - return ifail; -} - -/***************************************************************************** - * - * util_vector_free - * - *****************************************************************************/ - -__host__ -int util_vector_free(double ** p) { - - free(*p); - *p = NULL; - - return 0; -} - /***************************************************************************** * * util_matrix_create @@ -815,8 +781,7 @@ __host__ int util_matrix_invert(int n, double ** a) { * *****************************************************************************/ -__host__ __device__ -int util_dpythag(double a, double b, double * p) { +__host__ int util_dpythag(double a, double b, double * p) { double absa, absb, tmp; diff --git a/src/util.h b/src/util.h index bdf64981..7c32eaa6 100644 --- a/src/util.h +++ b/src/util.h @@ -53,11 +53,9 @@ __host__ int util_jacobi(double a[3][3], double vals[3], double vecs[3][3]); __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_dpythag(double a, double b, double * p); __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); -__host__ int util_vector_free(double ** p); __host__ int util_matrix_invert(int n, double ** a); __host__ int util_random_unit_vector(int * state, double rhat[3]); From 1ed11067631040016557d771e6862b2bac5ed583 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 18 Feb 2023 14:28:00 +0000 Subject: [PATCH 24/97] Remove stray assert() --- src/util.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/util.c b/src/util.c index d41af532..716be929 100644 --- a/src/util.c +++ b/src/util.c @@ -356,7 +356,6 @@ __host__ int util_jacobi(double a[3][3], double vals[3], double vecs[3][3]) { else if (fabs(a[ia][ib]) > tresh) { h = vals[ib] - vals[ia]; if (((fabs(h) + g) - fabs(h)) == 0.0) { - assert(0); t = (a[ia][ib])/h; } else { From 0983a0afec4c99db1ac24c916c348726365ea65a Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 18 Feb 2023 14:28:50 +0000 Subject: [PATCH 25/97] Add test for util_dpythag() --- tests/unit/test_util.c | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/tests/unit/test_util.c b/tests/unit/test_util.c index f812d2e0..18699da4 100644 --- a/tests/unit/test_util.c +++ b/tests/unit/test_util.c @@ -5,7 +5,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2012-2020 The University of Edinburgh + * (c) 2012-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -29,6 +29,7 @@ int util_random_unit_vector_check(void); int util_jacobi_check(void); +int util_dpythag_check(void); int util_str_tolower_check(void); int util_rectangle_conductance_check(void); @@ -47,6 +48,7 @@ int test_util_suite(void) { util_random_unit_vector_check(); util_jacobi_check(); + util_dpythag_check(); util_str_tolower_check(); util_rectangle_conductance_check(); @@ -138,6 +140,40 @@ int util_jacobi_check(void) { return 0; } +/***************************************************************************** + * + * util_dpythag_check + * + *****************************************************************************/ + +int util_dpythag_check(void) { + + int ifail = 0; + + { + double a = 0.0; + ifail = util_dpythag(3.0, 4.0, &a); + if (fabs(a - 5.0) > DBL_EPSILON) ifail = -1; + assert(ifail == 0); + } + + { + double a = -1.0; + ifail = util_dpythag(0.0, 0.0, &a); + if (a != 0.0) ifail = -1; + assert(ifail == 0); + } + + { + double a = 0.0; + ifail = util_dpythag(12.0, 5.0, &a); + if (fabs(a - 13.0) > DBL_EPSILON) ifail = -1; + assert(ifail == 0); + } + + return ifail; +} + /***************************************************************************** * * util_str_tolower_check From e71f3d2dd3a10d0a8b2be44b0fa570836d3e4787 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 18 Feb 2023 19:25:55 +0000 Subject: [PATCH 26/97] Mandatory input file name --- tests/test.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/test.sh b/tests/test.sh index 9af14518..0f9e2a2e 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -18,7 +18,7 @@ # Edinburgh Soft Matter and Statisical Physics Group and # Edinburgh Parallel Computing Centre # -# (c) 2014-2020 The University of Edinburgh +# (c) 2014-2023 The University of Edinburgh # # Contributing authors: # Kevin Stratford (kevin@epcce.ed.ac.uk) @@ -49,7 +49,8 @@ function main() { stub=`echo $input | sed 's/.inp//'` echo - ${launch_mpi} ${executable} $input > $stub.new + ln -s -f ${input} input + ${launch_mpi} ${executable} > $stub.new # Get difference via the difference script ${launch_serial} ${test_diff} $stub.new $stub.log @@ -63,6 +64,8 @@ function main() { echo "PASS ./$input" fi + rm -f input + return } From bae7abd31e934b7b14552a25955d297da930343d Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 18 Feb 2023 19:26:08 +0000 Subject: [PATCH 27/97] Remove unused scripts --- tests/nightly-test.sh | 71 ------- tests/repository-dump.sh | 43 ---- tests/test-all.sh | 32 --- tests/test-gpu-01.sh | 17 -- tests/test-simdvl.sh | 23 --- tests/titan-gpu/config.mk | 19 -- tests/titan-gpu/input | 406 -------------------------------------- 7 files changed, 611 deletions(-) delete mode 100755 tests/nightly-test.sh delete mode 100755 tests/repository-dump.sh delete mode 100755 tests/test-all.sh delete mode 100755 tests/test-gpu-01.sh delete mode 100755 tests/test-simdvl.sh delete mode 100644 tests/titan-gpu/config.mk delete mode 100644 tests/titan-gpu/input diff --git a/tests/nightly-test.sh b/tests/nightly-test.sh deleted file mode 100755 index 91dab323..00000000 --- a/tests/nightly-test.sh +++ /dev/null @@ -1,71 +0,0 @@ -############################################################################## -# -# nightly.sh -# -# This is the script to run the nightly test, and is specific for -# local set up. -# -# Edinburgh Soft Matter and Statisitcal Physics Group and -# Edinburgh Parallel Computing Centre -# -# Kevin Stratford (kevin@epcc.ed.ac.uk) -# (c) 2010-2015 The University of Edinburgh -# -############################################################################## -#!/bin/bash --login - -# Despite --login we still need to get appropriate paths, etc: -. /etc/profile -eval `/usr/bin/modulecmd bash load PMPI` -export PATH=$PATH:/usr/local/cuda/bin - - -# This is the local directory for the nightly stuff -thisdir=/home/w02/kevin/nightly -cd $thisdir - -testdir=ludwig/trunk/tests -summary=$thisdir/ludwig-tests.log - -# Log file - -record=$thisdir/`date +%F-%T`.log - -# Start a new summary file which will overwrite anything present -echo "Summary of $record" > $summary -echo "Test directory is $testdir" >> $summary - -# Checkout the SVN (and send the report to the record) - -svn co --username stratford http://ccpforge.cse.rl.ac.uk/svn/ludwig &> $record - -# start via bsub (indy0.epcc.ed.ac.uk) - -cd $testdir -cp ../config/lunix-gcc-default.mk ../config.mk -bsub -o $record -e $record -n 64 -W 1200 -q normal -J test-cpu < test-all.sh - -# Note the need to wait for the first job to finish means -# that the copy of the nvcc configuation is deferred until -# the test-gpu script. - -bsub -w "ended(test-cpu)" -o $record -e $record -q gpu -J test-gpu < test-gpu-01.sh - -# Wait for the tests to finish, and clean up -# This includes a copy of the summary to a public location -# (the interactive job will wait for the main "test-all" job to -# finish before starting and itself will finish before the copy is attempted) - -# Using "-q gpu" as "-q interactive" not active - -finish=$(cat <> $summary -EOF -) - -cd $thisdir -bsub -w "ended(test-gpu)" -o $record -e $record -I -q gpu "$finish" - - - diff --git a/tests/repository-dump.sh b/tests/repository-dump.sh deleted file mode 100755 index b2c8b1fc..00000000 --- a/tests/repository-dump.sh +++ /dev/null @@ -1,43 +0,0 @@ -############################################################################## -# -# repository-dump.sh -# -# This script backs up the SVN repository via svnadmin dump. -# As we are remote, this requires first getting a local copy of the -# whole repository, done using svnsync. -# -# Could be, e.g., invoked by cron. -# -# $Id$ -# -# Edinburgh Soft Matter and Statistical Physics Group and -# Edinburgh Parallel Computing Centre -# -# Kevin Stratford (kevin@epcc.ed.ac.uk) -# (c) 2011 The University of Edinburgh -# -############################################################################## -#!/bin/bash - -copydirectory=/home/kevin/nightly/copy -dumpfile=/home/kevin/nightly/repository-`date +%F`.dmp - -# Make a temporary local repository in $copydirectory - -svnadmin create $copydirectory - -# Allow svn to make revision changes to $copydirectory - -echo '#!/bin/sh' > $copydirectory/hooks/pre-revprop-change -chmod +x $copydirectory/hooks/pre-revprop-change - -# Sync the new copy with existing repository - -svnsync init file://$copydirectory http://ccpforge.cse.rl.ac.uk/svn/ludwig -svnsync sync file://$copydirectory - -# Dump the whole thing to a file and remove the copy - -svnadmin dump $copydirectory > $dumpfile -rm -rf $copydirectory - diff --git a/tests/test-all.sh b/tests/test-all.sh deleted file mode 100755 index 676bf53a..00000000 --- a/tests/test-all.sh +++ /dev/null @@ -1,32 +0,0 @@ -############################################################################## -# -# test-all.sh -# -# Just a convenience to run all tests... -# -# Edinburgh Soft Matter and Statistical Physics Group and -# Edinburgh Parallel Computing Centre -# -# Kevin Stratford (kevin@epcc.ed.ac.uk) -# (c) 2014-2015 The University of Edinburgh -# -############################################################################## -#!/bin/bash - -make compile-run-serial-d2q9 -make compile-run-serial-d2q9r -make compile-run-serial-d3q15 -make compile-run-serial-d3q15r -make compile-run-serial-d3q19 -make compile-run-serial-d3q19r - -make compile-run-mpi-d2q9 -make compile-run-mpi-d2q9r -make compile-run-mpi-d3q15 -make compile-run-mpi-d3q15r -make compile-run-mpi-d3q19 -make compile-run-mpi-d3q19r - - -#. ./test-mpix08.sh -u d3q19 long08 -#. ./test-long64.sh -r d3q19 long64 diff --git a/tests/test-gpu-01.sh b/tests/test-gpu-01.sh deleted file mode 100755 index 4e666f19..00000000 --- a/tests/test-gpu-01.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -############################################################################## -# -# test-gpu-01.sh -# -# Only d3q19r regrssion tests at the moment -# Serial only, until a parallel test machine is available. -# -############################################################################## - -# This is intended for the nightly test, so this copy must be here - -echo "TEST --> GPU serial" -cp ../config/lunix-nvcc-default.mk ../config.mk -make clean -make compile-serial-d3q19r -make run-serial-regr-d3q19r diff --git a/tests/test-simdvl.sh b/tests/test-simdvl.sh deleted file mode 100755 index 44ea838c..00000000 --- a/tests/test-simdvl.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -############################################################################## -# -# test-simdvl.sh -# -# Serial tests only. -# -# Edinburgh Soft Matter and Statistical Physics Group and -# Edinburgh Parallel Computing Centre -# -# (c) 2016 The University of Edinburgh -# Kevin Stratford (kevin@epcc.ed.ac.uk) -# -############################################################################## - -# This is intended for the nightly test, so this copy must be here - -echo "TEST --> SIMD VECTOR LENGTH TWO" -cp ../config/lunix-gcc-simdvl2.mk ../config.mk -make clean -make compile-run-serial-d3q15 -make compile-run-serial-d3q19 - diff --git a/tests/titan-gpu/config.mk b/tests/titan-gpu/config.mk deleted file mode 100644 index 652801d7..00000000 --- a/tests/titan-gpu/config.mk +++ /dev/null @@ -1,19 +0,0 @@ - -CC=nvcc -MPICC=nvcc -CFLAGS=-O2 -arch=sm_35 -x cu -dc -DKEEPFIELDONTARGET -DKEEPHYDROONTARGET -DOVERLAP - -AR = ar -ARFLAGS = -cru -LDFLAGS=-arch=sm_35 - -MPI_INCL=-I/opt/cray/mpt/default/gni/mpich2-CRAY64/8.3/include - -#cray mpich -MPI_LIBS=/opt/cray/mpt/default/gni/mpich2-CRAY64/8.3/lib/libmpich.a -#dependencies of mpich -MPI_LIBS+= /opt/cray/pmi/default/lib64/libpmi.a /opt/cray/alps/default/lib64/libalps.a /opt/cray/alps/default/lib64/libalpslli.a /opt/cray/alps/default/lib64/libalpsutil.a /opt/cray/dmapp/default/lib64/libdmapp.a /opt/cray/ugni/default/lib64/libugni.a /opt/cray/xpmem/default/lib64/libxpmem.a /opt/cray/wlm_detect/default/lib64/libwlm_detect.a /opt/cray/udreg/default/lib64/libudreg.a - -LAUNCH_SERIAL_CMD= -LAUNCH_MPI_CMD=mpirun -LAUNCH_MPI_NP_SWITCH=-np diff --git a/tests/titan-gpu/input b/tests/titan-gpu/input deleted file mode 100644 index 19a11c92..00000000 --- a/tests/titan-gpu/input +++ /dev/null @@ -1,406 +0,0 @@ -############################################################################## -# -# Ludwig input file -# Reference. -# -# 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 1000 - -############################################################################## -# -# 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). -# -# reduced_halo [yes|no] use reduced or full halos. Using reduced halos -# is *only* appropriate for fluid only problems. -# Default is no. -# -############################################################################## - -size 128_128_128 -#grid GRID -reduced_halo no - -############################################################################## -# -# Fluid parameters -# -# 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' -# -# ghost_modes [on|off] Default is on. -# force FX_FY_FZ Uniform body force on fluid (default zero) -# -############################################################################## - -viscosity 1.0 -#viscosity_bulk 1.0 - -isothermal_fluctuations off -temperature 0.00002133333 - -# ghost_modes off -# force 0.00_0.0_0.0 - -############################################################################## -# -# Free energy parameters -# -# free_energy none single fluid only [the default] -# -# Otherwise -# -# free_energy symmetric -# brazovskii -# surfactant -# polar_active -# lc_blue_phase -# -# symmetric_lb symmetric with 2 distributions -# -# fd_advection_scheme_order 1-7 -# sets order of finite difference -# advection scheme -# -# fd_gradient_calcualtion Scheme to yse for gradient calculations -# 2d_5pt_fluid -# 3d_7pt_fluid -# 3d_27pt_fluid -# 3d_27pt_solid -# -# 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 lc_blue_phase - -fd_advection_scheme_order 3 -fd_gradient_calculation 3d_7pt_fluid - -############################################################################### -# -# 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 or block [spinodal] -# 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_q_initialisation twist -# o8m -# o2 -# -# lc_q_init_amplitude scalar order parameter amplitude for initialisation -# -# Typically BPI o8m amplitude -0.2 -# BPII o2 amplitude +0.3 -# simple cholesteric twist (z-axis) amplitude +1/3 -# -############################################################################### - -lc_a0 0.01 -lc_gamma 3.0 -lc_q0 0.19635 -lc_kappa0 0.000648456 -lc_kappa1 0.000648456 -lc_xi 0.7 - -lc_Gamma 0.5 -lc_active_zeta 0.0 - -lc_q_initialisation twist -lc_q_init_amplitude 0.333333333333333 -lc_init_redshift 1.0 -lc_init_nematic 1.0_0.0_0.0 - -lc_anchoring_method two -lc_wall_anchoring normal -lc_coll_anchoring normal -lc_anchoring_strength_colloid 0.002593824 - - -############################################################################### -# -# 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 -# random (see below) -# colloid_type: inactive bbl [default] -# active Include active terms in BBL -# subgrid No bbl ("unresolved particles") -# -# colloid_cell_min: -# This MUST be set if colloids are present: it specifies -# the minimum cell list width, and must be at least 2ah -# + delta, where delta catches any colloid-colloid -# interactions. -# -############################################################################### - -colloid_init no_colloids -colloid_type inactive - -colloid_random_no 1 -colloid_random_a0 7.25 -colloid_random_ah 7.25 -colloid_random_dh 0.4 -colloid_random_r0 32.0_32.0_32.0 -colloid_random_v0 0.0_0.0_0.0 -colloid_random_s0 1.0_0.0_0.0 -colloid_random_m0 1.0_0.0_0.0 - -# Constant body force on all colloids ("gravity") - -colloid_cell_list_interactions no -colloid_cell_min 4.0 -colloid_gravity 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 0 -soft_sphere_epsilon 0.04 -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 - -############################################################################### -# -# Periodic conditions / boundaries -# -# boundary_walls_on [yes|no] Use built-in side walls [default no] -# periodicity X_Y_Z Sets periodic boundary conditions in coordinate -# directions [default is 1_1_1]. Best to leave this -# unchanged -# boundary_speed_top For use with built-in walls -# boundary_speed_bottom For use with built-in walls -# -# 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] -# determines type of porous media data to be -# supplied -# -############################################################################### - -boundary_walls_on no -periodicity 1_1_1 -boundary_speed_bottom 0.0 -boundary_speed_top 0.0 - -#porous_media_format BINARY -#porous_media_file capillary_8_8_32.dat -#porous_media_type status_only - -############################################################################### -# -# 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_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 -# The following for particle data are under review... -# n_io_nodes Number of I/O processors for particles -# output_format [ASCII|BINARY] default output format -# input_format [ASCII|BINARY] default input format -# -# phi_format Override default format for particular quantities -# etc... (both input and output) -# -############################################################################### - -freq_statistics 10000 -freq_measure 500000 -freq_config 200000 -freq_phi 200000 -freq_vel 300000 -freq_shear_measurement 100000 -freq_shear_output 100000 -config_at_end no - -distribution_io_grid 1_1_1 - -phi_format BINARY -vel_format BINARY - -colloid_io_freq 10000000 -colloids_io_grid 1_1_1 -colloid_io_format_input ASCII -colloid_io_format_output ASCII - -qs_dir_format 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 -# -############################################################################### - -#N_LE_plane 2 -#LE_plane_vel 0.008 -#LE_init_profile 1 -#LE_oscillation_period 0 - -############################################################################### -# -# Miscellaneous -# -# random_seed +ve integer is the random number generator seed -# -############################################################################### - -#random_seed 7361237 -random_seed 8361235 From 9ba984fb258b9b0f762e0a301b00305e6d6dc461 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 18 Feb 2023 19:26:45 +0000 Subject: [PATCH 28/97] Move to mandatory input file name --- src/main.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main.c b/src/main.c index 796fa181..11a35d4e 100644 --- a/src/main.c +++ b/src/main.c @@ -4,12 +4,11 @@ * * Main driver code. See ludwig.c for details of timestepping etc. * - * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * * Kevin Stratford (kevin@epcc.ed.ac.uk) - * (c) 2011-2022 The University of Edinburgh + * (c) 2011-2023 The University of Edinburgh * *****************************************************************************/ @@ -29,14 +28,13 @@ int main(int argc, char ** argv) { - char inputfile[FILENAME_MAX] = "input"; + const char * inputfile = "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]); ludwig_run(inputfile); From baf45d864570a0474dcecdc77a7803c53e994ff9 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 18 Feb 2023 19:27:11 +0000 Subject: [PATCH 29/97] Adjust floating point comparison --- src/util_cJSON.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/util_cJSON.c b/src/util_cJSON.c index 8214bbe4..d8e8b453 100644 --- a/src/util_cJSON.c +++ b/src/util_cJSON.c @@ -564,10 +564,9 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out { length = sprintf((char*)number_buffer, "null"); } - else if(d == (double)item->valueint) - { - length = sprintf((char*)number_buffer, "%d", item->valueint); - } + else if((d - (double)item->valueint) == 0.0) { + length = sprintf((char*)number_buffer, "%d", item->valueint); + } else { /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ From b8f386902d319baad451218e28c2cf2e3756467f Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 20 Feb 2023 16:38:33 +0000 Subject: [PATCH 30/97] Move electrokinetics to field implementation --- src/fe_electro.c | 3 +- src/fe_electro_symmetric.c | 3 + src/fe_electro_symmetric.h | 7 +- src/ludwig.c | 58 +- src/nernst_planck.c | 59 +- src/psi.c | 732 +++--------------- src/psi.h | 81 +- src/psi_colloid.c | 3 +- src/psi_force.c | 3 +- src/psi_gradients.c | 95 +-- src/psi_init.c | 5 +- src/psi_options.c | 175 +++++ src/psi_options.h | 83 ++ src/psi_rt.c | 296 ++++--- src/psi_rt.h | 13 +- src/psi_s.h | 68 -- src/psi_sor.c | 119 +-- src/psi_stats.c | 3 +- .../d3q19-elec/map-elec-gc2.001-001 | Bin 1024 -> 0 bytes .../d3q19-elec/psi-00000000.001-001 | Bin 32768 -> 0 bytes .../regression/d3q19-elec/serial-elec-do1.inp | 41 +- .../regression/d3q19-elec/serial-elec-do1.log | 7 +- .../regression/d3q19-elec/serial-elec-do2.inp | 39 +- .../regression/d3q19-elec/serial-elec-do2.log | 8 +- .../regression/d3q19-elec/serial-elec-do3.inp | 11 +- .../regression/d3q19-elec/serial-elec-do3.log | 7 +- .../regression/d3q19-elec/serial-elec-dr1.inp | 39 +- .../regression/d3q19-elec/serial-elec-dr1.log | 7 +- .../regression/d3q19-elec/serial-elec-dr2.inp | 39 +- .../regression/d3q19-elec/serial-elec-dr2.log | 7 +- .../regression/d3q19-elec/serial-elec-eo1.inp | 23 +- .../regression/d3q19-elec/serial-elec-eo1.log | 7 +- .../regression/d3q19-elec/serial-elec-eo2.inp | 5 +- .../regression/d3q19-elec/serial-elec-eo2.log | 7 +- .../regression/d3q19-elec/serial-elec-ep1.inp | 36 +- .../regression/d3q19-elec/serial-elec-ep1.log | 7 +- .../regression/d3q19-elec/serial-elec-ep2.inp | 36 +- .../regression/d3q19-elec/serial-elec-ep2.log | 7 +- .../regression/d3q19-elec/serial-elec-gc1.inp | 20 +- .../regression/d3q19-elec/serial-elec-gc1.log | 28 +- .../regression/d3q19-elec/serial-elec-gc2.inp | 120 --- .../regression/d3q19-elec/serial-elec-gc2.log | 167 ---- .../regression/d3q19-elec/serial-elec-rr1.inp | 5 +- .../regression/d3q19-elec/serial-elec-rr1.log | 7 +- .../regression/d3q19-elec/serial-elec-rr2.inp | 5 +- .../regression/d3q19-elec/serial-elec-rr2.log | 7 +- .../regression/d3q19-elec/serial-elec-rr3.inp | 5 +- .../regression/d3q19-elec/serial-elec-rr3.log | 7 +- .../regression/d3q19-elec/serial-elec-rr4.inp | 5 +- .../regression/d3q19-elec/serial-elec-rr4.log | 7 +- .../regression/d3q19-elec/serial-rest-ec1.inp | 5 + .../regression/d3q19-elec/serial-rest-ec1.log | 7 +- .../regression/d3q19-elec/serial-rest-ec2.inp | 3 + .../regression/d3q19-elec/serial-rest-ec2.log | 9 +- tests/unit/test_fe_electro.c | 77 +- tests/unit/test_fe_electro_symm.c | 17 +- tests/unit/test_nernst_planck.c | 39 +- tests/unit/test_psi.c | 556 ++++++------- tests/unit/test_psi_options.c | 218 ++++++ tests/unit/test_psi_sor.c | 55 +- tests/unit/tests.c | 4 +- tests/unit/tests.h | 1 + 62 files changed, 1443 insertions(+), 2070 deletions(-) create mode 100644 src/psi_options.c create mode 100644 src/psi_options.h delete mode 100644 src/psi_s.h delete mode 100644 tests/regression/d3q19-elec/map-elec-gc2.001-001 delete mode 100644 tests/regression/d3q19-elec/psi-00000000.001-001 delete mode 100644 tests/regression/d3q19-elec/serial-elec-gc2.inp delete mode 100644 tests/regression/d3q19-elec/serial-elec-gc2.log create mode 100644 tests/unit/test_psi_options.c diff --git a/src/fe_electro.c b/src/fe_electro.c index 4c4294fa..85b0a8b2 100644 --- a/src/fe_electro.c +++ b/src/fe_electro.c @@ -27,7 +27,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2013-2018 The University of Edinburgh + * (c) 2013-2023 The University of Edinburgh * * Contributing authors: * Oliver Henrich (ohenrich@epcc.ed.ac.uk) @@ -43,7 +43,6 @@ #include "pe.h" #include "physics.h" #include "util.h" -#include "psi_s.h" #include "psi_gradients.h" #include "fe_electro.h" diff --git a/src/fe_electro_symmetric.c b/src/fe_electro_symmetric.c index 365425f4..35b6a6e8 100644 --- a/src/fe_electro_symmetric.c +++ b/src/fe_electro_symmetric.c @@ -95,6 +95,8 @@ static __constant__ fe_vt_t fe_es_dvt = { (fe_htensor_v_ft) NULL }; +__host__ int fe_es_epsilon_set(fe_es_t * fe, double e1, double e2); + /***************************************************************************** * * fe_es_create @@ -134,6 +136,7 @@ __host__ int fe_es_create(pe_t * pe, cs_t * cs, fe_symm_t * symm, fe->super.id = FE_ELECTRO_SYMMETRIC; psi_nk(psi, &fe->param->nk); + fe_es_epsilon_set(fe, psi->epsilon, psi->epsilon2); tdpGetDeviceCount(&ndevice); diff --git a/src/fe_electro_symmetric.h b/src/fe_electro_symmetric.h index f5b3df36..48b4b4de 100644 --- a/src/fe_electro_symmetric.h +++ b/src/fe_electro_symmetric.h @@ -2,12 +2,10 @@ * * fe_electro_symmetric.h * - * $Id$ - * * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2013-2017 The University of Edinburgh + * (c) 2013-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -49,9 +47,6 @@ int fe_es_mu_ion_solv(fe_es_t * fe, int index, int n, double * mu); __host__ int fe_es_deltamu_set(fe_es_t * fe, int nk, double * deltamu); -__host__ -int fe_es_epsilon_set(fe_es_t * fe, double e1, double e2); - __host__ int fe_es_var_epsilon(fe_es_t * fe, int index, double * epsilon); diff --git a/src/ludwig.c b/src/ludwig.c index e156ee64..e58e5f36 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -211,7 +211,6 @@ static int ludwig_rt(ludwig_t * ludwig) { pe_t * pe = NULL; cs_t * cs = NULL; rt_t * rt = NULL; - io_info_t * iohandler = NULL; assert(ludwig); @@ -362,10 +361,11 @@ static int ludwig_rt(ludwig_t * ludwig) { } if (ludwig->psi) { - psi_io_info(ludwig->psi, &iohandler); - sprintf(filename,"%spsi-%8.8d", subdirectory, ntstep); - pe_info(pe, "electrokinetics files(s) %s\n", filename); - io_read_data(iohandler, filename, ludwig->psi); + io_event_t event1 = {0}; + io_event_t event2 = {0}; + pe_info(pe, "Reading electrokinetics files for step %d\n", ntstep); + field_io_read(ludwig->psi->psi, ntstep, &event1); + field_io_read(ludwig->psi->rho, ntstep, &event2); } } @@ -454,7 +454,6 @@ void ludwig_run(const char * inputfile) { int im, multisteps; int flag; - io_info_t * iohandler = NULL; ludwig_t * ludwig = NULL; MPI_Comm comm; @@ -617,15 +616,13 @@ void ludwig_run(const char * inputfile) { /* Poisson solve */ - if (step % psi_skipsteps(ludwig->psi) == 0){ - TIMER_start(TIMER_ELECTRO_POISSON); + TIMER_start(TIMER_ELECTRO_POISSON); #ifdef PETSC - psi_petsc_solve(ludwig->psi, ludwig->fe, ludwig->epsilon); + psi_petsc_solve(ludwig->psi, ludwig->fe, ludwig->epsilon); #else - psi_sor_solve(ludwig->psi, ludwig->fe, ludwig->epsilon, step); + psi_sor_solve(ludwig->psi, ludwig->fe, ludwig->epsilon, step); #endif - TIMER_stop(TIMER_ELECTRO_POISSON); - } + TIMER_stop(TIMER_ELECTRO_POISSON); if (ludwig->hydro) { TIMER_start(TIMER_HALO_LATTICE); @@ -923,10 +920,11 @@ void ludwig_run(const char * inputfile) { if (ludwig->psi) { if (is_psi_output_step() || is_config_step()) { - psi_io_info(ludwig->psi, &iohandler); + io_event_t event1 = {0}; + io_event_t event2 = {0}; 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); + field_io_write(ludwig->psi->psi, step, &event1); + field_io_write(ludwig->psi->rho, step, &event2); } } @@ -992,7 +990,7 @@ void ludwig_run(const char * inputfile) { #ifdef PETSC if (ludwig->psi) psi_petsc_finish(); #endif - if (ludwig->psi) psi_free(ludwig->psi); + if (ludwig->psi) psi_free(&ludwig->psi); if (ludwig->stat_rheo) stats_rheology_free(ludwig->stat_rheo); if (ludwig->stat_turb) stats_turbulent_free(ludwig->stat_turb); @@ -1808,9 +1806,13 @@ int free_energy_init_rt(ludwig_t * ludwig) { pe_info(pe, "\n"); pe_info(pe, "Parameters:\n"); - psi_create(pe, cs, nk, &ludwig->psi); - psi_rt_init_param(pe, rt, ludwig->psi); - psi_force_method_set(ludwig->psi, psi_method); + { + psi_options_t opts = psi_options_default(nhalo); + psi_options_rt(pe, cs, rt, &opts); + psi_create(pe, cs, &opts, &ludwig->psi); + psi_force_method_set(ludwig->psi, psi_method); + psi_info(pe, ludwig->psi); + } pe_info(pe, "Force calculation: %s\n", fe_force_method_to_string(method)); @@ -1878,9 +1880,13 @@ int free_energy_init_rt(ludwig_t * ludwig) { pe_info(pe, "Parameters:\n"); - psi_create(pe, cs, nk, &ludwig->psi); - psi_rt_init_param(pe, rt, ludwig->psi); - + { + psi_options_t opts = psi_options_default(nhalo); + psi_options_rt(pe, cs, rt, &opts); + psi_create(pe, cs, &opts, &ludwig->psi); + psi_info(pe, ludwig->psi); + psi_bjerrum_length2(&opts, &lbjerrum2); + } fe_electro_create(pe, ludwig->psi, &fe_elec); /* Coupling part */ @@ -1898,12 +1904,6 @@ int free_energy_init_rt(ludwig_t * ludwig) { psi_epsilon(ludwig->psi, &e1); psi_epsilon2(ludwig->psi, &e2); - /* Read the second permittivity */ - n = rt_double_parameter(rt, "electrosymmetric_epsilon2", &e2); - if (n == 1) psi_epsilon2_set(ludwig->psi, e2); - - fe_es_epsilon_set(fes, e1, e2); - /* Solvation free energy difference: nk = 2 */ mu[0] = 0.0; @@ -1914,8 +1914,6 @@ int free_energy_init_rt(ludwig_t * ludwig) { fe_es_deltamu_set(fes, nk, mu); - psi_bjerrum_length2(ludwig->psi, &lbjerrum2); - pe_info(pe, "Second permittivity: %15.7e\n", e2); pe_info(pe, "Dielectric average: %15.7e\n", 0.5*(e1 + e2)); pe_info(pe, "Dielectric contrast: %15.7e\n", (e1-e2)/(e1+e2)); diff --git a/src/nernst_planck.c b/src/nernst_planck.c index 8a51a4b4..424cbfcf 100644 --- a/src/nernst_planck.c +++ b/src/nernst_planck.c @@ -57,7 +57,7 @@ * Edinbrugh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2012-2022 The University of Edinburgh + * (c) 2012-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -71,7 +71,6 @@ #include "pe.h" #include "coords.h" -#include "psi_s.h" #include "advection.h" #include "advection_bcs.h" #include "nernst_planck.h" @@ -188,18 +187,18 @@ static int nernst_planck_fluxes(psi_t * psi, fe_t * fel, double * fx, fel->func->mu_solv(fel, index, n, &mu_s0); mu0 = reunit*mu_s0 - + psi->valency[n]*psi->psi[addr_rank0(nsites, index)]; - rho0 = psi->rho[addr_rank1(nsites, nk, index, n)]; + + psi->valency[n]*psi->psi->data[addr_rank0(nsites, index)]; + rho0 = psi->rho->data[addr_rank1(nsites, nk, index, n)]; /* x-direction (between ic and ic+1) */ fel->func->mu_solv(fel, index + xs, n, &mu_s1); mu1 = reunit*mu_s1 - + psi->valency[n]*psi->psi[addr_rank0(nsites, index + xs)]; + + psi->valency[n]*psi->psi->data[addr_rank0(nsites, index + xs)]; b0 = exp(mu1 - mu0); b1 = exp(mu1 - mu0); - rho1 = psi->rho[addr_rank1(nsites, nk, (index + xs), n)]*b1; + rho1 = psi->rho->data[addr_rank1(nsites, nk, (index + xs), n)]*b1; fx[addr_rank1(nsites, nk, index, n)] = -psi->diffusivity[n]*0.5*(1.0 + b0)*(rho1 - rho0); @@ -208,11 +207,11 @@ static int nernst_planck_fluxes(psi_t * psi, fe_t * fel, double * fx, fel->func->mu_solv(fel, index + ys, n, &mu_s1); mu1 = reunit*mu_s1 - + psi->valency[n]*psi->psi[addr_rank0(nsites, index + ys)]; + + psi->valency[n]*psi->psi->data[addr_rank0(nsites, index + ys)]; b0 = exp(mu1 - mu0); b1 = exp(mu1 - mu0); - rho1 = psi->rho[addr_rank1(nsites, nk, (index + ys), n)]*b1; + rho1 = psi->rho->data[addr_rank1(nsites, nk, (index + ys), n)]*b1; fy[nk*index + n] = -psi->diffusivity[n]*0.5*(1.0 + b0)*(rho1 - rho0); @@ -220,11 +219,11 @@ static int nernst_planck_fluxes(psi_t * psi, fe_t * fel, double * fx, fel->func->mu_solv(fel, index + zs, n, &mu_s1); mu1 = reunit*mu_s1 - + psi->valency[n]*psi->psi[addr_rank0(nsites, index + zs)]; + + psi->valency[n]*psi->psi->data[addr_rank0(nsites, index + zs)]; b0 = exp(mu1 - mu0); b1 = exp(mu1 - mu0); - rho1 = psi->rho[addr_rank1(nsites, nk, (index + zs), n)]*b1; + rho1 = psi->rho->data[addr_rank1(nsites, nk, (index + zs), n)]*b1; fz[addr_rank1(nsites, nk, index, n)] = -psi->diffusivity[n]*0.5*(1.0 + b0)*(rho1 - rho0); @@ -317,7 +316,7 @@ static int nernst_planck_update(psi_t * psi, double * fx, double * fy, index = cs_index(psi->cs, ic, jc, kc); for (n = 0; n < nk; n++) { - psi->rho[addr_rank1(psi->nsites, nk, index, n)] + psi->rho->data[addr_rank1(psi->nsites, nk, index, n)] -= (+ fx[addr_rank1(psi->nsites, nk, index, n)] - fx[addr_rank1(psi->nsites, nk, (index-xs), n)] + fy[addr_rank1(psi->nsites, nk, index, n)] @@ -366,7 +365,7 @@ int nernst_planck_driver_d3qx(psi_t * psi, fe_t * fe, hydro_t * hydro, } /* Add advective fluxes */ - if (hydro) advective_fluxes_d3qx(hydro, nk, psi->rho, flx); + if (hydro) advective_fluxes_d3qx(hydro, nk, psi->rho->data, flx); /* Add diffusive fluxes */ nernst_planck_fluxes_d3qx(psi, fe, hydro, map, cinfo, flx); @@ -420,6 +419,9 @@ static int nernst_planck_fluxes_d3qx(psi_t * psi, fe_t * fe, hydro_t * hydro, colloid_t * pc = NULL; + double * __restrict__ psidata = psi->psi->data; + double * __restrict__ rhodata = psi->rho->data; + assert(psi); assert(fe); assert(fe->func->mu_solv); @@ -457,15 +459,15 @@ static int nernst_planck_fluxes_d3qx(psi_t * psi, fe_t * fe, hydro_t * hydro, fe->func->mu_solv(fe, index0, n, &mu_s0); mu0 = reunit*mu_s0 - + psi->valency[n]*psi->psi[addr_rank0(psi->nsites, index0)]; - rho0 = psi->rho[addr_rank1(psi->nsites, nk, index0, n)]; + + psi->valency[n]*psidata[addr_rank0(psi->nsites, index0)]; + rho0 = rhodata[addr_rank1(psi->nsites, nk, index0, n)]; fe->func->mu_solv(fe, index1, n, &mu_s1); mu1 = reunit*mu_s1 - + psi->valency[n]* psi->psi[addr_rank0(psi->nsites, index1)]; + + psi->valency[n]* psidata[addr_rank0(psi->nsites, index1)]; b0 = exp(mu0 - mu1); b1 = exp(mu1 - mu0); - rho1 = psi->rho[addr_rank1(psi->nsites, nk, index1, n)]*b1; + rho1 = rhodata[addr_rank1(psi->nsites, nk, index1, n)]*b1; flx[addr_rank1(psi->nsites, nk, index0, n)][c - 1] -= psi->diffusivity[n]*0.5*(1.0 + b0)*(rho1 - rho0)*psi_gr_rnorm[c]; @@ -523,6 +525,9 @@ int nernst_planck_fluxes_force_d3qx(psi_t * psi, fe_t * fe, hydro_t * hydro, MPI_Comm comm; colloid_t * pc = NULL; + double * __restrict__ psidata = psi->psi->data; + double * __restrict__ rhodata = psi->rho->data; + assert(psi); assert(flx); @@ -577,21 +582,21 @@ int nernst_planck_fluxes_force_d3qx(psi_t * psi, fe_t * fe, hydro_t * hydro, for (n = 0; n < nk; n++) { fe->func->mu_solv(fe, index0, n, &mu_s0); mu0 = mu_s0 - + psi->valency[n]*eunit*psi->psi[addr_rank0(nsites, index0)]; - rho0 = psi->rho[addr_rank1(nsites, nk, index0, n)]; + + psi->valency[n]*eunit*psidata[addr_rank0(nsites, index0)]; + rho0 = rhodata[addr_rank1(nsites, nk, index0, n)]; fe->func->mu_solv(fe, index1, n, &mu_s1); mu1 = mu_s1 - + psi->valency[n]*eunit*psi->psi[addr_rank0(nsites, index1)]; + + psi->valency[n]*eunit*psidata[addr_rank0(nsites, index1)]; b0 = exp(-beta*(mu1 - mu0)); b1 = exp(+beta*(mu1 - mu0)); - rho1 = psi->rho[addr_rank1(nsites, nk, index1, n)]*b1; + rho1 = rhodata[addr_rank1(nsites, nk, index1, n)]*b1; /* Auxiliary terms */ /* Adding flxtmp[1] to flxtmp[0] below subtracts the ideal gas part */ flxtmp[0] = - 0.5*(1.0 + b0)*(rho1 - rho0) * psi_gr_rnorm[c]; - flxtmp[1] = (psi->rho[addr_rank1(nsites, nk, index1, n)] - - psi->rho[addr_rank1(nsites, nk, index0, n)]) + flxtmp[1] = (rhodata[addr_rank1(nsites, nk, index1, n)] + - rhodata[addr_rank1(nsites, nk, index0, n)]) * psi_gr_rnorm[c]; /* Link flux */ @@ -707,12 +712,12 @@ static int nernst_planck_update_d3qx(psi_t * psi, map_t * map, double ** flx) { acc = 0.0; for (c = 1; c < PSI_NGRAD; c++) { - psi->rho[addr_rank1(nsites, nk, index, n)] + psi->rho->data[addr_rank1(nsites, nk, index, n)] -= flx[addr_rank1(nsites, nk, index, n)][c - 1] * dt; acc += fabs(flx[addr_rank1(nsites, nk, index, n)][c - 1] * dt); } - acc /= fabs(psi->rho[addr_rank1(nsites, nk, index, n)]); + acc /= fabs(psi->rho->data[addr_rank1(nsites, nk, index, n)]); if (maxacc < acc) maxacc = acc; } } @@ -784,7 +789,7 @@ int nernst_planck_adjust_multistep(psi_t * psi) { if (* maxacc > diffacc && diffacc > 0.0) { psi_multisteps(psi, &multisteps); multisteps *= 2; - psi_multisteps_set(psi, multisteps); + psi->multisteps = multisteps; pe_info(psi->pe, "\nMaxacc > diffacc: changing no. of multisteps to %d\n", multisteps); } @@ -800,11 +805,11 @@ int nernst_planck_adjust_multistep(psi_t * psi) { psi_diffusivity(psi, n, &diff); if (diff > diffmax) diffmax = diff; } - + /* Only reduce if sanity criteria fulfilled */ if (multisteps > 1 && diffmax/multisteps < 0.05) { multisteps *= 0.5; - psi_multisteps_set(psi, multisteps); + psi->multisteps = multisteps; pe_info(psi->pe, "\nMaxacc << diffacc: changing no. of multisteps to %d\n", multisteps); } diff --git a/src/psi.c b/src/psi.c index d29ae487..a5b9d47d 100644 --- a/src/psi.c +++ b/src/psi.c @@ -9,7 +9,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2012-2022 The University of Edinburgh + * (c) 2012-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -18,472 +18,222 @@ *****************************************************************************/ #include -#include -#include -#include -#include #include -#include +#include #include "pe.h" #include "coords.h" -#include "coords_field.h" -#include "io_harness.h" #include "util.h" #include "map.h" -#include "psi_s.h" #include "psi_gradients.h" -static const double e_unit_default = 1.0; /* Default unit charge */ -static const double reltol_default = FLT_EPSILON; /* Solver tolerance */ -static const double abstol_default = 0.01*FLT_EPSILON; -static const int maxits_default = 10000; /* Default number of iterations in Poisson solver */ -static const int multisteps_default = 1; /* Default number of multisteps in NPE */ -static const int skipsteps_default = 1; /* Default number of skipped timesteps in Poisson solver */ -static const double diffacc_default = 0; /* Default diffusive accuracy in NPE for constant no. of multisteps */ - -static int psi_read(FILE * fp, int index, void * self); -static int psi_write(FILE * fp, int index, void * self); -static int psi_read_ascii(FILE * fp, int index, void * self); -static int psi_write_ascii(FILE * fp, int index, void * self); - /***************************************************************************** * * psi_create * - * Initialise the electric potential, nk charge density fields. - * *****************************************************************************/ -int psi_create(pe_t * pe, cs_t * cs, int nk, psi_t ** pobj) { +int psi_create(pe_t * pe, cs_t * cs, const psi_options_t * opts, + psi_t ** pobj) { - int nsites; - int nhalo; + int ifail = 0; psi_t * psi = NULL; - assert(pe); - assert(cs); assert(pobj); - assert(nk > 1); - assert(nk <= PSI_NKMAX); - - cs_nsites(cs, &nsites); - cs_nhalo(cs, &nhalo); psi = (psi_t *) calloc(1, sizeof(psi_t)); - assert(psi); - if (psi == NULL) pe_fatal(pe, "Allocation of psi failed\n"); + if (psi == NULL) goto err; - psi->pe = pe; - psi->cs = cs; - - psi->nk = nk; - psi->nsites = nsites; - psi->psi = (double *) calloc(nsites, sizeof(double)); - psi->rho = (double *) calloc((size_t) nk*nsites, sizeof(double)); - psi->diffusivity = (double *) calloc(nk, sizeof(double)); - psi->valency = (int *) calloc(nk, sizeof(int)); - - if (psi->psi == NULL) pe_fatal(pe, "Allocation of psi->psi failed\n"); - if (psi->rho == NULL) pe_fatal(pe, "Allocation of psi->rho failed\n"); - if (psi->diffusivity == NULL) pe_fatal(pe, "psi->diffusivity failed\n"); - if (psi->valency == NULL) pe_fatal(pe, "calloc(psi->valency) failed\n"); - - psi->e = e_unit_default; - psi->reltol = reltol_default; - psi->abstol = abstol_default; - psi->maxits = maxits_default; - psi->multisteps = multisteps_default; - psi->skipsteps = skipsteps_default; - psi->diffacc = diffacc_default; - - psi->nfreq_io = INT_MAX; - psi->nfreq = INT_MAX; - psi->beta = 1.0; /* Not zero default */ - - coords_field_init_mpi_indexed(cs, nhalo, 1, MPI_DOUBLE, psi->psihalo); - coords_field_init_mpi_indexed(cs, nhalo, psi->nk, MPI_DOUBLE, psi->rhohalo); - - *pobj = psi; + ifail = psi_initialise(pe, cs, opts, psi); + if (ifail != 0) goto err; + + *pobj = psi; return 0; -} -/***************************************************************************** - * - * psi_io_info - * - *****************************************************************************/ - -int psi_io_info(psi_t * obj, io_info_t ** info) { - - assert(obj); - assert(info); - - *info = obj->info; - - return 0; + err: + if (psi) free(psi); + return -1; } /***************************************************************************** * - * psi_halo_psi + * psi_free * *****************************************************************************/ -int psi_halo_psi(psi_t * psi) { - - int nh; +int psi_free(psi_t ** psi) { assert(psi); + assert(*psi); - cs_nhalo(psi->cs, &nh); /* Swap full halo */ - coords_field_halo_rank1(psi->cs, psi->nsites, nh, 1, psi->psi, MPI_DOUBLE); + psi_finalise(*psi); + free(*psi); + *psi = NULL; return 0; } /***************************************************************************** * - * psi_halo_rho + * psi_initialise * *****************************************************************************/ -int psi_halo_rho(psi_t * psi) { - - int nh; +int psi_initialise(pe_t * pe, cs_t * cs, const psi_options_t * opts, + psi_t * psi) { + assert(pe); + assert(cs); + assert(opts); assert(psi); - cs_nhalo(psi->cs, &nh); /* Swap full halo */ - coords_field_halo_rank1(psi->cs, psi->nsites, nh, psi->nk, psi->rho, - MPI_DOUBLE); - return 0; -} - -/***************************************************************************** - * - * psi_nk - * - *****************************************************************************/ - -int psi_nk(psi_t * obj, int * nk) { - - assert(obj); - - *nk = obj->nk; - - return 0; -} + psi->pe = pe; + psi->cs = cs; -/***************************************************************************** - * - * psi_valency_set - * - *****************************************************************************/ + psi->nk = opts->nk; + cs_nsites(cs, &psi->nsites); -int psi_valency_set(psi_t * obj, int n, int iv) { + psi->e = opts->e; + psi->beta = opts->beta; + psi->epsilon = opts->epsilon1; + psi->epsilon2 = opts->epsilon2; - assert(obj); - assert(n < obj->nk); + psi->e0[X] = opts->e0[X]; + psi->e0[Y] = opts->e0[Y]; + psi->e0[Z] = opts->e0[Z]; - obj->valency[n] = iv; + psi->diffusivity = (double *) calloc(opts->nk, sizeof(double)); + psi->valency = (int *) calloc(opts->nk, sizeof(int)); - return 0; -} - -/***************************************************************************** - * - * psi_valency - * - *****************************************************************************/ + if (psi->diffusivity == NULL) pe_fatal(pe, "psi->diffusivity failed\n"); + if (psi->valency == NULL) pe_fatal(pe, "calloc(psi->valency) failed\n"); -int psi_valency(psi_t * obj, int n, int * iv) { + for (int n = 0; n < opts->nk; n++) { + psi->diffusivity[n] = opts->diffusivity[n]; + psi->valency[n] = opts->valency[n]; + } - assert(obj); - assert(n < obj->nk); - assert(iv); + /* Poisson solver */ + psi->maxits = opts->maxits; + psi->nfreq = opts->nfreq; + psi->reltol = opts->reltol; + psi->abstol = opts->abstol; + + /* Nernst-Planck */ + psi->multisteps = opts->nsmallstep; + psi->diffacc = opts->diffacc; + + /* Other */ + { + /* Unfortunately, "rho" is not available for the charge density, + * as it would conflict with the fluid density. */ + lees_edw_t * le = NULL; + field_create(pe, cs, le, "psi", &opts->psi, &psi->psi); + field_create(pe, cs, le, "qsi", &opts->rho, &psi->rho); + } - *iv = obj->valency[n]; + psi->nfreq_io = INT_MAX; return 0; } /***************************************************************************** * - * psi_diffusivity_set + * psi_finalise * *****************************************************************************/ -int psi_diffusivity_set(psi_t * obj, int n, double diff) { - - assert(obj); - assert(n < obj->nk); - - obj->diffusivity[n] = diff; - - return 0; -} +int psi_finalise(psi_t * psi) { -/***************************************************************************** - * - * psi_diffusivity - * - *****************************************************************************/ + assert(psi->psi); -int psi_diffusivity(psi_t * obj, int n, double * diff) { + field_free(psi->rho); + field_free(psi->psi); - assert(obj); - assert(n < obj->nk); - assert(diff); + free(psi->valency); + free(psi->diffusivity); - *diff = obj->diffusivity[n]; + *psi = (psi_t) {0}; return 0; } /***************************************************************************** * - * psi_e0_set - * - * Set external electric field. + * psi_halo_psi * *****************************************************************************/ -int psi_e0_set(psi_t * psi, const double e0[3]) { +int psi_halo_psi(psi_t * psi) { assert(psi); - psi->e0[X] = e0[X]; - psi->e0[Y] = e0[Y]; - psi->e0[Z] = e0[Z]; + field_halo(psi->psi); return 0; } /***************************************************************************** * - * psi_init_io_info - * - * The I/O grid will be requested with Cartesian extent as given. - * - * Register all the I/O functions, and set the input/output format - * appropriately. + * psi_halo_rho * *****************************************************************************/ -int psi_init_io_info(psi_t * obj, int grid[3], int form_in, int form_out) { - - io_info_args_t args = io_info_args_default(); - - assert(obj); - assert(grid); - assert(obj->info == NULL); - - args.grid[X] = grid[X]; - args.grid[Y] = grid[Y]; - args.grid[Z] = grid[Z]; - - io_info_create(obj->pe, obj->cs, &args, &obj->info); - if (obj->info == NULL) pe_fatal(obj->pe, "io_info_create(psi) failed\n"); - - io_info_set_name(obj->info, "Potential and charge densities"); - - io_info_read_set(obj->info, IO_FORMAT_BINARY, psi_read); - io_info_read_set(obj->info, IO_FORMAT_ASCII, psi_read_ascii); - io_info_write_set(obj->info, IO_FORMAT_BINARY, psi_write); - io_info_write_set(obj->info, IO_FORMAT_ASCII, psi_write_ascii); +int psi_halo_rho(psi_t * psi) { - io_info_set_bytesize(obj->info, IO_FORMAT_BINARY, (2+obj->nk)*sizeof(double)); - io_info_set_bytesize(obj->info, IO_FORMAT_ASCII, (2+obj->nk)*23 + 1); + assert(psi); - io_info_format_set(obj->info, form_in, form_out); - io_info_metadata_filestub_set(obj->info, "psi"); + field_halo(psi->rho); return 0; } /***************************************************************************** * - * psi_free - * - *****************************************************************************/ - -void psi_free(psi_t * obj) { - - int n; - - assert(obj); - - for (n = 0; n < 3; n++) { - MPI_Type_free(&obj->psihalo[n]); - MPI_Type_free(&obj->rhohalo[n]); - } - - if (obj->info) io_info_free(obj->info); - - free(obj->valency); - free(obj->diffusivity); - free(obj->rho); - free(obj->psi); - free(obj); - obj = NULL; - - return; -} - -/***************************************************************************** - * - * psi_write_ascii - * - * Returns 0 on success. + * psi_nk * *****************************************************************************/ -static int psi_write_ascii(FILE * fp, int index, void * self) { - - int n, nwrite; - int nsites; - double rho_el; - psi_t * obj = (psi_t*) self; +int psi_nk(psi_t * obj, int * nk) { assert(obj); - assert(fp); - cs_nsites(obj->cs, &nsites); - - nwrite = fprintf(fp, "%22.15e ", obj->psi[addr_rank0(nsites,index)]); - if (nwrite != 23) { - pe_fatal(obj->pe, "fprintf(psi) failed at index %d\n", index); - } - - for (n = 0; n < obj->nk; n++) { - nwrite = fprintf(fp, "%22.15e ", obj->rho[addr_rank1(nsites, obj->nk, index, n)]); - if (nwrite != 23) pe_fatal(obj->pe, "fprintf(rho) failed at index %d %d\n", index, n); - } - - psi_rho_elec(obj, index, &rho_el); - nwrite = fprintf(fp, "%22.15e ", rho_el); - if (nwrite != 23) { - pe_fatal(obj->pe, "fprintf(rho_el) failed at index %d\n", index); - } - - nwrite = fprintf(fp, "\n"); - if (nwrite != 1) pe_fatal(obj->pe, "fprintf() failed at index %d\n", index); - - return 0; -} - -/***************************************************************************** - * - * psi_read_ascii - * - * Returns 0 on success. - * - *****************************************************************************/ - -static int psi_read_ascii(FILE * fp, int index, void * self) { - - int n, nread; - int nsites; - int indexf; - double rho_el; - psi_t * obj = (psi_t*) self; - - assert(fp); - assert(self); - - cs_nsites(obj->cs, &nsites); - - indexf = addr_rank0(nsites, index); - nread = fscanf(fp, "%le", obj->psi + indexf); - if (nread != 1) pe_fatal(obj->pe, "fscanf(psi) failed %d\n", index); - - for (n = 0; n < obj->nk; n++) { - indexf = addr_rank1(nsites, obj->nk, index, n); - nread = fscanf(fp, "%le", obj->rho + indexf); - if (nread != 1) pe_fatal(obj->pe, "fscanf(rho) failed %d %d\n", index, n); - } - - nread = fscanf(fp, "%le", &rho_el); - if (nread != 1) pe_fatal(obj->pe, "fscanf(rho_el) failed %d %d\n", index, n); + *nk = obj->nk; return 0; } /***************************************************************************** * - * psi_write - * - * Returns 0 on success. + * psi_valency * *****************************************************************************/ -static int psi_write(FILE * fp, int index, void * self) { - - int n; - int na; - int nsites; - int indexf; - double rho_el; - psi_t * obj = (psi_t*) self; +int psi_valency(psi_t * obj, int n, int * iv) { - assert(fp); assert(obj); + assert(n < obj->nk); + assert(iv); - cs_nsites(obj->cs, &nsites); - - indexf = addr_rank0(nsites, index); - n = fwrite(obj->psi + indexf, sizeof(double), 1, fp); - if (n != 1) pe_fatal(obj->pe, "fwrite(psi) failed at index %d\n", index); - - for (na = 0; na < obj->nk; na++) { - indexf = addr_rank1(nsites, obj->nk, index, na); - n = fwrite(obj->rho + indexf, sizeof(double), 1, fp); - if (n != 1) pe_fatal(obj->pe, "fwrite(rho) failed at index %d\n", index); - } - - psi_rho_elec(obj, index, &rho_el); - n = fwrite(&rho_el, sizeof(double), 1, fp); - if (n != 1) pe_fatal(obj->pe, "fwrite(rho_el) failed at index %d", index); + *iv = obj->valency[n]; return 0; } /***************************************************************************** * - * psi_read - * - * Returns 0 on success. + * psi_diffusivity * *****************************************************************************/ -static int psi_read(FILE * fp, int index, void * self) { - - int n; - int na; - int nsites; - int indexf; - double rho_el; - psi_t * obj = (psi_t*) self; +int psi_diffusivity(psi_t * obj, int n, double * diff) { - assert(fp); assert(obj); + assert(n < obj->nk); + assert(diff); - cs_nsites(obj->cs, &nsites); - - indexf = addr_rank0(nsites, index); - n = fread(obj->psi + indexf, sizeof(double), 1, fp); - if (n != 1) pe_fatal(obj->pe, "fread(psi) failed at index %d\n", index); - - for (na = 0; na < obj->nk; na++) { - indexf = addr_rank1(nsites, obj->nk, index, na); - n = fread(obj->rho + indexf, sizeof(double), 1, fp); - if (n != 1) pe_fatal(obj->pe, "fread(rho) failed at index %d\n", index); - } - - n = fread(&rho_el, sizeof(double), 1, fp); - if (n != 1) pe_fatal(obj->pe, "fread(rho_el) failed at index %d\n", index); + *diff = obj->diffusivity[n]; return 0; } @@ -498,14 +248,14 @@ static int psi_read(FILE * fp, int index, void * self) { int psi_rho_elec(psi_t * obj, int index, double * rho) { - int n; double rho_elec = 0.0; assert(obj); assert(rho); - for (n = 0; n < obj->nk; n++) { - rho_elec += obj->e*obj->valency[n]*obj->rho[addr_rank1(obj->nsites, obj->nk, index, n)]; + for (int n = 0; n < obj->nk; n++) { + int irho = addr_rank1(obj->nsites, obj->nk, index, n); + rho_elec += obj->e*obj->valency[n]*obj->rho->data[irho]; } *rho = rho_elec; @@ -524,7 +274,7 @@ int psi_rho(psi_t * obj, int index, int n, double * rho) { assert(rho); assert(n < obj->nk); - *rho = obj->rho[addr_rank1(obj->nsites, obj->nk, index, n)]; + *rho = obj->rho->data[addr_rank1(obj->nsites, obj->nk, index, n)]; return 0; } @@ -540,7 +290,7 @@ int psi_rho_set(psi_t * obj, int index, int n, double rho) { assert(obj); assert(n < obj->nk); - obj->rho[addr_rank1(obj->nsites, obj->nk, index, n)] = rho; + obj->rho->data[addr_rank1(obj->nsites, obj->nk, index, n)] = rho; return 0; } @@ -556,7 +306,7 @@ int psi_psi(psi_t * obj, int index, double * psi) { assert(obj); assert(psi); - *psi = obj->psi[addr_rank0(obj->nsites, index)]; + *psi = obj->psi->data[addr_rank0(obj->nsites, index)]; return 0; } @@ -571,7 +321,7 @@ int psi_psi_set(psi_t * obj, int index, double psi) { assert(obj); - obj->psi[addr_rank0(obj->nsites, index)] = psi; + obj->psi->data[addr_rank0(obj->nsites, index)] = psi; return 0; } @@ -592,21 +342,6 @@ int psi_unit_charge(psi_t * obj, double * eunit) { return 0; } -/***************************************************************************** - * - * psi_unit_charge_set - * - *****************************************************************************/ - -int psi_unit_charge_set(psi_t * obj, double eunit) { - - assert(obj); - - obj->e = eunit; - - return 0; -} - /***************************************************************************** * * psi_beta @@ -623,21 +358,6 @@ int psi_beta(psi_t * obj, double * beta) { return 0; } -/***************************************************************************** - * - * psi_beta_set - * - *****************************************************************************/ - -int psi_beta_set(psi_t * obj, double beta) { - - assert(obj); - - obj->beta = beta; - - return 0; -} - /***************************************************************************** * * psi_epsilon @@ -654,21 +374,6 @@ int psi_epsilon(psi_t * obj, double * epsilon) { return 0; } -/***************************************************************************** - * - * psi_epsilon_set - * - *****************************************************************************/ - -int psi_epsilon_set(psi_t * obj, double epsilon) { - - assert(obj); - - obj->epsilon = epsilon; - - return 0; -} - /***************************************************************************** * * psi_epsilon2 @@ -685,22 +390,6 @@ int psi_epsilon2(psi_t * obj, double * epsilon2) { return 0; } - -/***************************************************************************** - * - * psi_epsilon2_set - * - *****************************************************************************/ - -int psi_epsilon2_set(psi_t * obj, double epsilon2) { - - assert(obj); - - obj->epsilon2 = epsilon2; - - return 0; -} - /***************************************************************************** * * psi_ionic_strength @@ -712,110 +401,18 @@ int psi_epsilon2_set(psi_t * obj, double epsilon2) { int psi_ionic_strength(psi_t * psi, int index, double * sion) { - int n; assert(psi); assert(sion); *sion = 0.0; - for (n = 0; n < psi->nk; n++) { + for (int n = 0; n < psi->nk; n++) { *sion += 0.5*psi->valency[n]*psi->valency[n] - *psi->rho[addr_rank1(psi->nsites, psi->nk, index, n)]; + *psi->rho->data[addr_rank1(psi->nsites, psi->nk, index, n)]; } return 0; } -/***************************************************************************** - * - * psi_bjerrum_length - * - * Is equal to e^2 / 4 pi epsilon k_B T - * - *****************************************************************************/ - -int psi_bjerrum_length(psi_t * obj, double * lb) { - - PI_DOUBLE(pi); - - assert(obj); - assert(lb); - - *lb = obj->e*obj->e*obj->beta / (4.0*pi*obj->epsilon); - - return 0; -} - - -/***************************************************************************** - * - * psi_bjerrum_length2 - * - * Is equal to e^2 / 4 pi epsilon2 k_B T if we have - * a dielectric contrast between the electrolytes. - * - *****************************************************************************/ - -int psi_bjerrum_length2(psi_t * obj, double * lb) { - - PI_DOUBLE(pi); - - assert(obj); - assert(lb); - - *lb = obj->e*obj->e*obj->beta / (4.0*pi*obj->epsilon2); - - return 0; -} - -/***************************************************************************** - * - * psi_debye_length - * - * Returns the Debye length for a simple, symmetric electrolyte. - * An ionic strength is required as input (see above); this - * accounts for the factor of 8 in the denominator. - * - *****************************************************************************/ - -int psi_debye_length(psi_t * obj, double rho_b, double * ld) { - - double lb; - PI_DOUBLE(pi); - - assert(obj); - assert(ld); - - psi_bjerrum_length(obj, &lb); - *ld = 1.0 / sqrt(8.0*pi*lb*rho_b); - - return 0; -} - -/***************************************************************************** - * - * psi_debye_length2 - * - * Returns the Debye length for the second phase if we - * have a dielectric contrast between the electrolytes. - * An ionic strength is required as input (see above); this - * accounts for the factor of 8 in the denominator. - * - *****************************************************************************/ - -int psi_debye_length2(psi_t * obj, double rho_b, double * ld) { - - double lb; - PI_DOUBLE(pi); - - assert(obj); - assert(ld); - - psi_bjerrum_length2(obj, &lb); - *ld = 1.0 / sqrt(8.0*pi*lb*rho_b); - - return 0; -} - /***************************************************************************** * * psi_surface_potential @@ -882,52 +479,6 @@ int psi_abstol(psi_t * obj, double * abstol) { return 0; } -/***************************************************************************** - * - * psi_reltol_set - * - *****************************************************************************/ - -int psi_reltol_set(psi_t * obj, double reltol) { - - assert(obj); - - obj->reltol = reltol; - - return 0; -} - -/***************************************************************************** - * - * psi_abstol_set - * - *****************************************************************************/ - -int psi_abstol_set(psi_t * obj, double abstol) { - - assert(obj); - - obj->abstol = abstol; - - return 0; -} - -/***************************************************************************** - * - * psi_multisteps_set - * - *****************************************************************************/ - -int psi_multisteps_set(psi_t * obj, int multisteps) { - - assert(obj); - assert(multisteps); - - obj->multisteps = multisteps; - - return 0; -} - /***************************************************************************** * * psi_multisteps @@ -959,22 +510,6 @@ int psi_multistep_timestep(psi_t * obj, double * dt) { return 0; } -/***************************************************************************** - * - * psi_maxits_set - * - *****************************************************************************/ - -int psi_maxits_set(psi_t * obj, int maxits) { - - assert(obj); - assert(maxits); - - obj->maxits = maxits; - - return 0; -} - /***************************************************************************** * * psi_maxits @@ -1023,35 +558,6 @@ int psi_diffacc(psi_t * obj, double * diffacc) { return 0; } -/***************************************************************************** - * - * psi_skipsteps_set - * - *****************************************************************************/ - -int psi_skipsteps_set(psi_t * obj, double skipsteps) { - - assert(obj); - assert(skipsteps>=1); - - obj->skipsteps = skipsteps; - - return 0; -} - -/***************************************************************************** - * - * psi_skipsteps - * - *****************************************************************************/ - -int psi_skipsteps(psi_t * obj) { - - assert(obj); - - return obj->skipsteps; -} - /***************************************************************************** * * psi_zero_mean @@ -1139,6 +645,8 @@ int psi_halo_psijump(psi_t * psi) { double eps; double beta; + double * psidata = psi->psi->data; + assert(psi); cs_nhalo(psi->cs, &nhalo); @@ -1162,13 +670,13 @@ int psi_halo_psijump(psi_t * psi) { if (periodic[X]) { /* Add external potential */ - psi->psi[addr_rank0(psi->nsites, index)] += psi->e0[X]*ntotal[X]; + psidata[addr_rank0(psi->nsites, index)] += psi->e0[X]*ntotal[X]; } else{ /* Borrow fluid site ic = 1 */ index1 = cs_index(psi->cs, 1, jc, kc); - psi->psi[addr_rank0(psi->nsites, index)] = - psi->psi[addr_rank0(psi->nsites, index1)]; + psidata[addr_rank0(psi->nsites, index)] = + psidata[addr_rank0(psi->nsites, index1)]; } } } @@ -1186,13 +694,13 @@ int psi_halo_psijump(psi_t * psi) { if (periodic[X]) { /* Subtract external potential */ - psi->psi[addr_rank0(psi->nsites, index)] -= psi->e0[X]*ntotal[X]; + psidata[addr_rank0(psi->nsites, index)] -= psi->e0[X]*ntotal[X]; } else { /* Borrow fluid site at end ... */ index1 = cs_index(psi->cs, nlocal[X], jc, kc); - psi->psi[addr_rank0(psi->nsites, index)] = - psi->psi[addr_rank0(psi->nsites, index1)]; + psidata[addr_rank0(psi->nsites, index)] = + psidata[addr_rank0(psi->nsites, index1)]; } } } @@ -1209,13 +717,13 @@ int psi_halo_psijump(psi_t * psi) { if (periodic[Y]) { /* Add external potential */ - psi->psi[addr_rank0(psi->nsites, index)] += psi->e0[Y]*ntotal[Y]; + psidata[addr_rank0(psi->nsites, index)] += psi->e0[Y]*ntotal[Y]; } else { /* Not periodic ... just borrow from fluid site jc = 1 */ index1 = cs_index(psi->cs, ic, 1, kc); - psi->psi[addr_rank0(psi->nsites, index)] = - psi->psi[addr_rank0(psi->nsites, index1)]; + psidata[addr_rank0(psi->nsites, index)] = + psidata[addr_rank0(psi->nsites, index1)]; } } } @@ -1233,13 +741,13 @@ int psi_halo_psijump(psi_t * psi) { if (periodic[Y]) { /* Subtract external potential */ - psi->psi[addr_rank0(psi->nsites, index)] -= psi->e0[Y]*ntotal[Y]; + psidata[addr_rank0(psi->nsites, index)] -= psi->e0[Y]*ntotal[Y]; } else { /* Borrow fluid site at end */ index1 = cs_index(psi->cs, ic, nlocal[Y], kc); - psi->psi[addr_rank0(psi->nsites, index)] = - psi->psi[addr_rank0(psi->nsites, index1)]; + psidata[addr_rank0(psi->nsites, index)] = + psidata[addr_rank0(psi->nsites, index1)]; } } } @@ -1257,13 +765,13 @@ int psi_halo_psijump(psi_t * psi) { if (periodic[Z]) { /* Add external potential */ - psi->psi[addr_rank0(psi->nsites, index)] += psi->e0[Z]*ntotal[Z]; + psidata[addr_rank0(psi->nsites, index)] += psi->e0[Z]*ntotal[Z]; } else { /* Borrow fluid site kc = 1 */ index1 = cs_index(psi->cs, ic, jc, 1); - psi->psi[addr_rank0(psi->nsites, index)] = - psi->psi[addr_rank0(psi->nsites, index1)]; + psidata[addr_rank0(psi->nsites, index)] = + psidata[addr_rank0(psi->nsites, index1)]; } } } @@ -1281,13 +789,13 @@ int psi_halo_psijump(psi_t * psi) { if (periodic[Z]) { /* Subtract external potential */ - psi->psi[addr_rank0(psi->nsites, index)] -= psi->e0[Z]*ntotal[Z]; + psidata[addr_rank0(psi->nsites, index)] -= psi->e0[Z]*ntotal[Z]; } else { /* Borrow fluid site at end ... */ index1 = cs_index(psi->cs, ic, jc, nlocal[Z]); - psi->psi[addr_rank0(psi->nsites, index)] = - psi->psi[addr_rank0(psi->nsites, index1)]; + psidata[addr_rank0(psi->nsites, index)] = + psidata[addr_rank0(psi->nsites, index1)]; } } } diff --git a/src/psi.h b/src/psi.h index 55ffe13b..ebaf6d82 100644 --- a/src/psi.h +++ b/src/psi.h @@ -5,7 +5,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2012-2022 The University of Edinburgh + * (c) 2012-2023 The University of Edinburgh * * Kevin Stratford (kevin@epcc.ed.ac.uk) * @@ -15,42 +15,70 @@ #define LUDWIG_PSI_H #include "pe.h" -#include "coords.h" +#include "field.h" #include "io_harness.h" #include "map.h" +#include "psi_options.h" -/* PSI_NKMAX is here to allow us to declare arrays to hold - * per-species quantities. It avoids allocation of short arrays, - * which is slightly tedious, particularly on the device. */ +typedef struct psi_s psi_t; + +/* + * We store here the unit charge, the electric permittivity, and the + * temperature, all in lattice units. This allows us to work out the + * Bjerrum length, + * l_B = e^2 / (4 \pi epsilon_0 epsilon_r kT) + * which is the scale at which the interaction energy between two + * unit charges is equal to kT. The aim is that the Bjerrum length + * should be < 1 (e.g. 0.7) in lattice units. + * + * For water at room temperature, the Bjerrum length is about + * 7 Angstrom. + * + */ -#define PSI_NKMAX 4 +struct psi_s { + pe_t * pe; /* Parallel environment */ + cs_t * cs; /* Coordinate system */ -/* Force computation method */ + int nk; /* Number of species */ + int nsites; /* Number sites storage */ -enum psi_force_method {PSI_FORCE_NONE = 0, - PSI_FORCE_DIVERGENCE, - PSI_FORCE_GRADMU, - PSI_FORCE_NTYPES + field_t * psi; /* Electric potential */ + field_t * rho; /* Charge densities */ + + double * diffusivity; /* Diffusivity for each species */ + int * valency; /* Valency for each species */ + double e; /* unit charge */ + double epsilon; /* first and reference permittivity */ + double epsilon2; /* second permittivity */ + double beta; /* Boltzmann factor (1 / k_B T) */ + double reltol; /* Relative tolerance for Poisson solver */ + double abstol; /* Absolute tolerance for Poisson solver */ + int method; /* Force computation method */ + int maxits; /* Maximum number of iterations */ + int multisteps; /* Number of substeps in charge dynamics */ + int nfreq_io; /* Field output */ + int nfreq; /* Residual statisics output */ + double diffacc; /* Number of substeps in charge dynamics */ + double e0[3]; /* External electric field */ }; -typedef struct psi_s psi_t; /* f_vare_t describes the signature of the function expected * to return the permittivity as a function of position index. */ typedef int (* f_vare_t)(void * fe, int index, double * epsilon); -int psi_create(pe_t * pe, cs_t * cs, int nk, psi_t ** pobj); -void psi_free(psi_t * obj); -int psi_init_io_info(psi_t * obj, int grid[3], int form_in, int form_out); -int psi_io_info(psi_t * obj, io_info_t ** info); +int psi_create(pe_t * pe, cs_t * cs, const psi_options_t * opts, psi_t ** p); +int psi_free(psi_t ** psi); + +int psi_initialise(pe_t * pe, cs_t * cs, const psi_options_t * opts, + psi_t * psi); +int psi_finalise(psi_t * psi); int psi_nk(psi_t * obj, int * nk); int psi_valency(psi_t * obj, int n, int * iv); -int psi_valency_set(psi_t * obj, int n, int iv); int psi_diffusivity(psi_t * obj, int n, double * diff); -int psi_diffusivity_set(psi_t * obj, int n, double diff); -int psi_e0_set(psi_t * psi, const double e0[3]); int psi_halo_psi(psi_t * obj); int psi_halo_psijump(psi_t * obj); int psi_halo_rho(psi_t * obj); @@ -61,36 +89,21 @@ int psi_psi(psi_t * obj, int index, double * psi); int psi_psi_set(psi_t * obj, int index, double psi); int psi_rho_elec(psi_t * obj, int index, double * rho_elec); int psi_unit_charge(psi_t * obj, double * eunit); -int psi_unit_charge_set(psi_t * obj, double eunit); int psi_beta(psi_t * obj, double * beta); -int psi_beta_set(psi_t * obj, double beta); int psi_epsilon(psi_t * obj, double * epsilon); -int psi_epsilon_set(psi_t * obj, double epsilon); int psi_epsilon2(psi_t * obj, double * epsilon2); -int psi_epsilon2_set(psi_t * obj, double epsilon2); int psi_ionic_strength(psi_t * psi, int index, double * sion); -int psi_bjerrum_length(psi_t * obj, double * lb); -int psi_bjerrum_length2(psi_t * obj, double * lb); -int psi_debye_length(psi_t * obj, double rho_b, double * ld); -int psi_debye_length2(psi_t * obj, double rho_b, double * ld); int psi_surface_potential(psi_t * obj, double sigma, double rho_b, double * sp); int psi_reltol(psi_t * obj, double * reltol); int psi_abstol(psi_t * obj, double * abstol); int psi_maxits(psi_t * obj, int * maxits); -int psi_reltol_set(psi_t * obj, double reltol); -int psi_abstol_set(psi_t * obj, double abstol); -int psi_maxits_set(psi_t * obj, int maxits); int psi_nfreq_set(psi_t * psi, int nfreq); int psi_output_step(psi_t * psi, int its); int psi_multisteps(psi_t * obj, int * multisteps); -int psi_multisteps_set(psi_t * obj, int multisteps); int psi_multistep_timestep(psi_t * obj, double * dt); int psi_diffacc(psi_t * obj, double * diffacc); -int psi_diffacc_set(psi_t * obj, double diffacc); -int psi_skipsteps(psi_t * obj); -int psi_skipsteps_set(psi_t * obj, double skipsteps); int psi_zero_mean(psi_t * obj); int psi_force_method(psi_t * obj, int * flag); int psi_force_method_set(psi_t * obj, int flag); diff --git a/src/psi_colloid.c b/src/psi_colloid.c index 9e06d607..15d428c4 100644 --- a/src/psi_colloid.c +++ b/src/psi_colloid.c @@ -8,7 +8,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2013-2020 The University of Edinburgh + * (c) 2013-2023 The University of Edinburgh * * Contributing authors: * Oliver Henrich (ohenrich@epcc.ed.ac.uk) @@ -21,7 +21,6 @@ #include #include "pe.h" -#include "psi_s.h" #include "util.h" #include "coords.h" #include "psi_colloid.h" diff --git a/src/psi_force.c b/src/psi_force.c index 3cd536d1..65e3083c 100644 --- a/src/psi_force.c +++ b/src/psi_force.c @@ -7,7 +7,7 @@ * Edinburgh Soft Matter and Statisitical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2013-2016 The University of Edinburgh + * (c) 2013-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -22,7 +22,6 @@ #include "pe.h" #include "coords.h" #include "physics.h" -#include "psi_s.h" #include "fe_electro.h" #include "fe_electro_symmetric.h" #include "psi_force.h" diff --git a/src/psi_gradients.c b/src/psi_gradients.c index 5d925e0f..07d5ca16 100644 --- a/src/psi_gradients.c +++ b/src/psi_gradients.c @@ -9,13 +9,13 @@ * Edinburgh Parallel Computing Centre * * Oliver Henrich (ohenrich@epcc.ed.ac.uk) - * (c) 2014-2018 The University of Edinburgh + * + * (c) 2014-2023 The University of Edinburgh * ****************************************************************************/ #include #include "coords.h" -#include "psi_s.h" #include "psi_gradients.h" #include "fe_electro_symmetric.h" @@ -142,9 +142,12 @@ int psi_electric_field(psi_t * psi, int index, double e[3]) { cs_nsites(psi->cs, &nsites); cs_strides(psi->cs, &xs, &ys, &zs); - e[X] = -0.5*(psi->psi[addr_rank0(nsites, index + xs)] - psi->psi[addr_rank0(nsites, index - xs)]); - e[Y] = -0.5*(psi->psi[addr_rank0(nsites, index + ys)] - psi->psi[addr_rank0(nsites, index - ys)]); - e[Z] = -0.5*(psi->psi[addr_rank0(nsites, index + zs)] - psi->psi[addr_rank0(nsites, index - zs)]); + e[X] = -0.5*(psi->psi->data[addr_rank0(nsites, index + xs)] + - psi->psi->data[addr_rank0(nsites, index - xs)]); + e[Y] = -0.5*(psi->psi->data[addr_rank0(nsites, index + ys)] + - psi->psi->data[addr_rank0(nsites, index - ys)]); + e[Z] = -0.5*(psi->psi->data[addr_rank0(nsites, index + zs)] + - psi->psi->data[addr_rank0(nsites, index - zs)]); return 0; } @@ -183,7 +186,7 @@ int psi_electric_field_d3qx(psi_t * psi, int index, double e[3]) { coords_nbr[Z] = coords[Z] + psi_gr_cv[p][Z]; index_nbr = cs_index(psi->cs, coords_nbr[X], coords_nbr[Y], coords_nbr[Z]); - aux = psi_gr_wv[p]* psi_gr_rcs2 * psi->psi[addr_rank0(nsites, index_nbr)]; + aux = psi_gr_wv[p]* psi_gr_rcs2 * psi->psi->data[addr_rank0(nsites, index_nbr)]; e[X] -= aux * psi_gr_cv[p][X]; e[Y] -= aux * psi_gr_cv[p][Y]; @@ -224,6 +227,8 @@ int psi_grad_rho(psi_t * psi, map_t * map, int index, int n, double grad_rho[3] int index0, index1; int status0, status1; + double * rhodata = psi->rho->data; + assert(psi); assert(n < psi->nk); assert(grad_rho); @@ -243,18 +248,18 @@ int psi_grad_rho(psi_t * psi, map_t * map, int index, int n, double grad_rho[3] map_status(map, index1, &status1); if (status0 == MAP_FLUID && status1 == MAP_FLUID){ - grad_rho[X] = 0.5*(psi->rho[addr_rank1(nsites, psi->nk, (index + xs), n)] - - psi->rho[addr_rank1(nsites, psi->nk, (index - xs), n)]); + grad_rho[X] = 0.5*(rhodata[addr_rank1(nsites, psi->nk, (index + xs), n)] + - rhodata[addr_rank1(nsites, psi->nk, (index - xs), n)]); } if (status0 != MAP_FLUID && status1 == MAP_FLUID){ - grad_rho[X] = - 1.5*psi->rho[addr_rank1(nsites, psi->nk, index, n)] - + 2.0*psi->rho[addr_rank1(nsites, psi->nk, (index + xs), n)] - - 0.5*psi->rho[addr_rank1(nsites, psi->nk, (index + 2*xs), n)]; + grad_rho[X] = - 1.5*rhodata[addr_rank1(nsites, psi->nk, index, n)] + + 2.0*rhodata[addr_rank1(nsites, psi->nk, (index + xs), n)] + - 0.5*rhodata[addr_rank1(nsites, psi->nk, (index + 2*xs), n)]; } if (status0 == MAP_FLUID && status1 != MAP_FLUID){ - grad_rho[X] = + 1.5*psi->rho[addr_rank1(nsites, psi->nk, index, n)] - - 2.0*psi->rho[addr_rank1(nsites, psi->nk, (index - xs), n)] - + 0.5*psi->rho[addr_rank1(nsites, psi->nk, (index - 2*xs), n)]; + grad_rho[X] = + 1.5*rhodata[addr_rank1(nsites, psi->nk, index, n)] + - 2.0*rhodata[addr_rank1(nsites, psi->nk, (index - xs), n)] + + 0.5*rhodata[addr_rank1(nsites, psi->nk, (index - 2*xs), n)]; } /* y-direction */ @@ -265,19 +270,19 @@ int psi_grad_rho(psi_t * psi, map_t * map, int index, int n, double grad_rho[3] if (status0 == MAP_FLUID && status1 == MAP_FLUID) { grad_rho[Y] - = 0.5*(+ psi->rho[addr_rank1(nsites, psi->nk, (index+ys), n)] - - psi->rho[addr_rank1(nsites, psi->nk, (index-ys), n)]); + = 0.5*(+ rhodata[addr_rank1(nsites, psi->nk, (index+ys), n)] + - rhodata[addr_rank1(nsites, psi->nk, (index-ys), n)]); } if (status0 != MAP_FLUID && status1 == MAP_FLUID) { grad_rho[Y] - = - 1.5*psi->rho[addr_rank1(nsites, psi->nk, index, n)] - + 2.0*psi->rho[addr_rank1(nsites, psi->nk, (index + ys), n)] - - 0.5*psi->rho[addr_rank1(nsites, psi->nk, (index + 2*ys), n)]; + = - 1.5*rhodata[addr_rank1(nsites, psi->nk, index, n)] + + 2.0*rhodata[addr_rank1(nsites, psi->nk, (index + ys), n)] + - 0.5*rhodata[addr_rank1(nsites, psi->nk, (index + 2*ys), n)]; } if (status0 == MAP_FLUID && status1 != MAP_FLUID) { - grad_rho[Y] = + 1.5*psi->rho[addr_rank1(nsites, psi->nk, index, n)] - - 2.0*psi->rho[addr_rank1(nsites, psi->nk, (index - ys), n)] - + 0.5*psi->rho[addr_rank1(nsites, psi->nk, (index - 2*ys), n)]; + grad_rho[Y] = + 1.5*rhodata[addr_rank1(nsites, psi->nk, index, n)] + - 2.0*rhodata[addr_rank1(nsites, psi->nk, (index - ys), n)] + + 0.5*rhodata[addr_rank1(nsites, psi->nk, (index - 2*ys), n)]; } /* z-direction */ @@ -287,18 +292,18 @@ int psi_grad_rho(psi_t * psi, map_t * map, int index, int n, double grad_rho[3] map_status(map, index1, &status1); if (status0 == MAP_FLUID && status1 == MAP_FLUID) { - grad_rho[Z] = 0.5*(psi->rho[addr_rank1(nsites, psi->nk, (index+zs), n)] - - psi->rho[addr_rank1(nsites, psi->nk, (index-zs), n)]); + grad_rho[Z] = 0.5*(rhodata[addr_rank1(nsites, psi->nk, (index+zs), n)] - + rhodata[addr_rank1(nsites, psi->nk, (index-zs), n)]); } if (status0 != MAP_FLUID && status1 == MAP_FLUID) { - grad_rho[Z] = - 1.5*psi->rho[addr_rank1(nsites, psi->nk, index, n)] - + 2.0*psi->rho[addr_rank1(nsites, psi->nk, (index + zs), n)] - - 0.5*psi->rho[addr_rank1(nsites, psi->nk, (index + 2*zs), n)]; + grad_rho[Z] = - 1.5*rhodata[addr_rank1(nsites, psi->nk, index, n)] + + 2.0*rhodata[addr_rank1(nsites, psi->nk, (index + zs), n)] + - 0.5*rhodata[addr_rank1(nsites, psi->nk, (index + 2*zs), n)]; } if (status0 == MAP_FLUID && status1 != MAP_FLUID) { - grad_rho[Z] = + 1.5*psi->rho[addr_rank1(nsites, psi->nk, index, n)] - - 2.0*psi->rho[addr_rank1(nsites, psi->nk, (index - zs), n)] - + 0.5*psi->rho[addr_rank1(nsites, psi->nk, (index - 2*zs), n)]; + grad_rho[Z] = + 1.5*rhodata[addr_rank1(nsites, psi->nk, index, n)] + - 2.0*rhodata[addr_rank1(nsites, psi->nk, (index - zs), n)] + + 0.5*rhodata[addr_rank1(nsites, psi->nk, (index - 2*zs), n)]; } return 0; @@ -338,6 +343,8 @@ int psi_grad_rho_d3qx(psi_t * psi, map_t * map, int index, int n, double grad_r int status, status1, status2; double aux; + double * rho = psi->rho->data; + assert(psi); assert(n < psi->nk); assert(grad_rho); @@ -362,7 +369,7 @@ int psi_grad_rho_d3qx(psi_t * psi, map_t * map, int index, int n, double grad_r if(status == MAP_FLUID && status1 == MAP_FLUID) { - aux = psi_gr_wv[p]* psi_gr_rcs2 * psi->rho[psi->nk*index1 + n]; + aux = psi_gr_wv[p]* psi_gr_rcs2 * rho[psi->nk*index1 + n]; grad_rho[X] += aux * psi_gr_cv[p][X]; grad_rho[Y] += aux * psi_gr_cv[p][Y]; @@ -389,29 +396,29 @@ int psi_grad_rho_d3qx(psi_t * psi, map_t * map, int index, int n, double grad_r /* Subtract the above 'fluid' half of the incomplete two-point formula. */ /* Note: subtracting means adding here because of inverse lattice vectors. */ - aux = psi_gr_wv[p]* psi_gr_rcs2 * psi->rho[addr_rank1(nsites, psi->nk, index1, n)]; + aux = psi_gr_wv[p]*psi_gr_rcs2*rho[addr_rank1(nsites,psi->nk,index1,n)]; grad_rho[X] += aux * psi_gr_cv[p][X]; grad_rho[Y] += aux * psi_gr_cv[p][Y]; grad_rho[Z] += aux * psi_gr_cv[p][Z]; /* Use one-sided derivative instead */ - grad_rho[X] += psi_gr_wv[p] * psi_gr_rcs2 * - (3.0*psi->rho[addr_rank1(nsites, psi->nk, index, n)] - - 4.0*psi->rho[addr_rank1(nsites, psi->nk, index1, n)] - + 1.0*psi->rho[addr_rank1(nsites, psi->nk, index2, n)]) + grad_rho[X] += psi_gr_wv[p] * psi_gr_rcs2 * + (3.0*rho[addr_rank1(nsites, psi->nk, index, n)] + - 4.0*rho[addr_rank1(nsites, psi->nk, index1, n)] + + 1.0*rho[addr_rank1(nsites, psi->nk, index2, n)]) * psi_gr_rnorm[p]* psi_gr_cv[p][X]; - grad_rho[Y] += psi_gr_wv[p] * psi_gr_rcs2 * - (3.0*psi->rho[addr_rank1(nsites, psi->nk, index, n)] - - 4.0*psi->rho[addr_rank1(nsites, psi->nk, index1, n)] - + 1.0*psi->rho[addr_rank1(nsites, psi->nk, index2, n)]) + grad_rho[Y] += psi_gr_wv[p] * psi_gr_rcs2 * + (3.0*rho[addr_rank1(nsites, psi->nk, index, n)] + - 4.0*rho[addr_rank1(nsites, psi->nk, index1, n)] + + 1.0*rho[addr_rank1(nsites, psi->nk, index2, n)]) * psi_gr_rnorm[p] * psi_gr_cv[p][Y]; - grad_rho[Z] += psi_gr_wv[p] * psi_gr_rcs2 * - (3.0*psi->rho[addr_rank1(nsites, psi->nk, index, n)] - - 4.0*psi->rho[addr_rank1(nsites, psi->nk, index1, n)] - + 1.0*psi->rho[addr_rank1(nsites, psi->nk, index2, n)]) + grad_rho[Z] += psi_gr_wv[p] * psi_gr_rcs2 * + (3.0*rho[addr_rank1(nsites, psi->nk, index, n)] + - 4.0*rho[addr_rank1(nsites, psi->nk, index1, n)] + + 1.0*rho[addr_rank1(nsites, psi->nk, index2, n)]) * psi_gr_rnorm[p] * psi_gr_cv[p][Z]; } } diff --git a/src/psi_init.c b/src/psi_init.c index 1589a1ff..6c4ccf80 100644 --- a/src/psi_init.c +++ b/src/psi_init.c @@ -4,12 +4,10 @@ * * Various initial states for electrokinetics. * - * $Id$ - * * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2012-16 The University of Edinburgh + * (c) 2012-2023 The University of Edinburgh * * Contributing authors: * Oliver Henrich (ohenrich@epcc.ed.ac.uk) @@ -23,7 +21,6 @@ #include "pe.h" #include "coords.h" #include "psi_init.h" -#include "psi_s.h" /***************************************************************************** * diff --git a/src/psi_options.c b/src/psi_options.c new file mode 100644 index 00000000..0478a785 --- /dev/null +++ b/src/psi_options.c @@ -0,0 +1,175 @@ +/***************************************************************************** + * + * psi_options.c + * + * Including default options for the Electrokinetic sector. + * And some derived quantities only dependent on the options. + * + * 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 +#include + +#include "psi_options.h" +#include "util.h" + +/***************************************************************************** + * + * psi_options_default + * + * The nhalo is that required for the potential and the charge density + * fields. + * + *****************************************************************************/ + +psi_options_t psi_options_default(int nhalo) { + + int nk = 2; + + psi_options_t opts = {.nk = nk, + .e = 1.0, + .beta = 1.0, + .epsilon1 = 10000.0, + .epsilon2 = 10000.0, + .e0 = {0.0, 0.0, 0.0}, + .diffusivity = {0.01, 0.01, 0.01, 0.01}, + .valency = {+1, -1, +1, -1}, + .psolver = PSI_POISSON_SOLVER_SOR, + .maxits = 10000, + .nfreq = INT_MAX, + .reltol = FLT_EPSILON, + .abstol = 0.01*FLT_EPSILON, + .nsolver = -1, + .nsmallstep = 1, + .diffacc = 0.0, + .method = PSI_FORCE_NONE, + .psi = {0}, + .rho = {0}}; + + opts.psi = field_options_ndata_nhalo(1, nhalo); + opts.rho = field_options_ndata_nhalo(nk, nhalo); + + return opts; +} + + +/***************************************************************************** + * + * psi_bjerrum_length1 + * + * Is equal to e^2 / 4 pi epsilon1 k_B T + * + * Sensible parameters give return value 0. + * + *****************************************************************************/ + +int psi_bjerrum_length1(const psi_options_t * opts, double * lbjerrum) { + + int ifail = 0; + PI_DOUBLE(pi); + + assert(opts); + assert(lbjerrum); + + if (opts->beta <= 0.0) ifail = -1; + if (opts->epsilon1 <= 0.0) ifail = -1; + + { + double e = opts->e; + double kt = 1.0/opts->beta; + double epsilon1 = opts->epsilon1; + + *lbjerrum = e*e / (4.0*pi*epsilon1*kt); + } + + return ifail; +} + +/***************************************************************************** + * + * psi_bjerrum_length2 + * + * Is equal to e^2 / 4 pi epsilon2 k_B T if we have + * a dielectric contrast between the electrolytes. + * + *****************************************************************************/ + +int psi_bjerrum_length2(const psi_options_t * opts, double * lbjerrum) { + + int ifail = 0; + PI_DOUBLE(pi); + + assert(opts); + assert(lbjerrum); + + if (opts->beta <= 0.0) ifail = -1; + if (opts->epsilon2 <= 0) ifail = -1; + + { + double e = opts->e; + double kt = 1.0/opts->beta; + double epsilon2 = opts->epsilon2; + + *lbjerrum = e*e / (4.0*pi*epsilon2*kt); + } + + return ifail; +} + +/***************************************************************************** + * + * psi_debye_length1 + * + * Returns the Debye length for a simple, symmetric electrolyte. + * An ionic strength is required as input (see above); this + * accounts for the factor of 8 in the denominator. + * + *****************************************************************************/ + +int psi_debye_length1(const psi_options_t * opts, double rho_b, double * ld) { + + double lb; + PI_DOUBLE(pi); + + assert(opts); + assert(ld); + + psi_bjerrum_length1(opts, &lb); + *ld = 1.0 / sqrt(8.0*pi*lb*rho_b); + + return 0; +} + +/***************************************************************************** + * + * psi_debye_length2 + * + * Returns the Debye length for the second phase if we + * have a dielectric contrast between the electrolytes. + * An ionic strength is required as input (see above); this + * accounts for the factor of 8 in the denominator. + * + *****************************************************************************/ + +int psi_debye_length2(const psi_options_t * opts, double rho_b, double * ld) { + + double lb; + PI_DOUBLE(pi); + + assert(opts); + assert(ld); + + psi_bjerrum_length2(opts, &lb); + *ld = 1.0 / sqrt(8.0*pi*lb*rho_b); + + return 0; +} diff --git a/src/psi_options.h b/src/psi_options.h new file mode 100644 index 00000000..10f900c1 --- /dev/null +++ b/src/psi_options.h @@ -0,0 +1,83 @@ +/***************************************************************************** + * + * psi_options.h + * + * Run time options for electrokinetic sector. + * + * 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) + * + *****************************************************************************/ + +#ifndef LUDWIG_PSI_OPTIONS_H +#define LUDWIG_PSI_OPTIONS_H + +#include "field_options.h" + +#define PSI_NKMAX 4 + +/* Poisson solver method */ +typedef enum psi_poisson_solver_enum_s { + PSI_POISSON_SOLVER_INVALID = 0, + PSI_POISSON_SOLVER_SOR = 1, + PSI_POISSON_SOLVER_PETSC = 2 +} psi_poisson_solver_enum_t; + +/* Force computation method */ + +typedef enum psi_force_method_enum_s { + PSI_FORCE_NONE = 0, + PSI_FORCE_DIVERGENCE, + PSI_FORCE_GRADMU, + PSI_FORCE_NTYPES +} psi_force_method_enum_t; + +/* There may be a case for splitting this into different concerns; + * however, it's all here ... */ + +typedef struct psi_options_s psi_options_t; + +struct psi_options_s { + + int nk; /* Number of charged species */ + + /* Physics */ + double e; /* Unit charge */ + double beta; /* Boltzmann factor (1 / k_B T) */ + double epsilon1; /* First permittivity */ + double epsilon2; /* Second permittivity (if required) */ + double e0[3]; /* External electric field */ + double diffusivity[PSI_NKMAX]; /* Per species diffusivity */ + int valency[PSI_NKMAX]; /* Per species charge valency */ + + /* Poisson solver (iterative) */ + int psolver; /* Poisson solver id */ + int maxits; /* Maximum iterations in solver */ + int nfreq; /* Frequency of information (iterations) */ + double reltol; /* Relative tolerance */ + double abstol; /* Absolute tolerance */ + + /* Time stepping for Nernst Planck */ + int nsolver; /* Nernst Planck method */ + int nsmallstep; /* No. small timesteps in time splitting */ + double diffacc; /* FIXME: what exactly is this? */ + + /* Other */ + int method; /* Force computation method */ + field_options_t psi; /* Field options for potential (i/o etc) */ + field_options_t rho; /* field options for charges (i/o etc) */ +}; + +psi_options_t psi_options_default(int nhalo); + + +int psi_bjerrum_length1(const psi_options_t * opts, double * lb); +int psi_bjerrum_length2(const psi_options_t * opts, double * lb); +int psi_debye_length1(const psi_options_t * obj, double rho_b, double * ld); +int psi_debye_length2(const psi_options_t * obj, double rho_b, double * ld); + +#endif diff --git a/src/psi_rt.c b/src/psi_rt.c index f7faf4ce..26f89634 100644 --- a/src/psi_rt.c +++ b/src/psi_rt.c @@ -10,7 +10,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2012-2022 The University of Edinburgh + * (c) 2012-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -27,151 +27,9 @@ #include "psi_rt.h" #include "psi_init.h" #include "io_harness.h" +#include "io_info_args_rt.h" #include "util_bits.h" -/***************************************************************************** - * - * psi_rt_param_init - * - *****************************************************************************/ - -int psi_rt_init_param(pe_t * pe, rt_t * rt, psi_t * obj) { - - int n; - int nk; - int nfreq; - - int valency[2] = {+1, -1}; /* Valencies (should be +/-!)*/ - double diffusivity[2] = {0.01, 0.01}; - - double eunit = 1.0; /* Unit charge */ - double temperature, beta; /* Temperature (set by fluctuations) */ - double epsilon = 0.0; /* Reference permittivity */ - double lbjerrum; /* Bjerrum length; derived, not input */ - double tolerance; /* Numerical tolerance for SOR and Krylov subspace solver */ - int niteration; /* Max. number of iterations */ - - int io_grid[3] = {1,1,1}; - int io_format_in = IO_FORMAT_DEFAULT; - int io_format_out = IO_FORMAT_DEFAULT; - char value[BUFSIZ] = "BINARY"; - - int multisteps; /* Number of substeps in NPE */ - int skipsteps; /* Poisson equation solved every skipstep timesteps */ - double diffacc; /* Relative accuracy of diffusion in NPE */ - - assert(pe); - assert(rt); - - psi_nk(obj, &nk); - assert(nk == 2); /* nk must be two for the time being */ - - rt_int_parameter(rt, "electrokinetics_z0", valency); - rt_int_parameter(rt, "electrokinetics_z1", valency + 1); - rt_double_parameter(rt, "electrokinetics_d0", diffusivity); - rt_double_parameter(rt, "electrokinetics_d1", diffusivity + 1); - - for (n = 0; n < nk; n++) { - psi_valency_set(obj, n, valency[n]); - psi_diffusivity_set(obj, n, diffusivity[n]); - } - - rt_double_parameter(rt, "electrokinetics_eunit", &eunit); - rt_double_parameter(rt, "electrokinetics_epsilon", &epsilon); - - psi_unit_charge_set(obj, eunit); - psi_epsilon_set(obj, epsilon); - psi_epsilon2_set(obj, epsilon); /* Default is no dielectric contrast */ - - n = rt_double_parameter(rt, "temperature", &temperature); - - if (n == 0 || temperature <= 0.0) { - pe_fatal(pe, "Please set a temperature to use electrokinetics\n"); - } - - beta = 1.0/temperature; - - psi_beta_set(obj, beta); - psi_bjerrum_length(obj, &lbjerrum); - - pe_info(pe, "Electrokinetic species: %2d\n", nk); - pe_info(pe, "Boltzmann factor: %14.7e (T = %14.7e)\n", beta, temperature); - pe_info(pe, "Unit charge: %14.7e\n", eunit); - pe_info(pe, "Permittivity: %14.7e\n", epsilon); - pe_info(pe, "Bjerrum length: %14.7e\n", lbjerrum); - - for (n = 0; n < nk; n++) { - pe_info(pe, "Valency species %d: %2d\n", n, valency[n]); - pe_info(pe, "Diffusivity species %d: %14.7e\n", n, diffusivity[n]); - } - - /* Multisteps and diffusive accuracy in NPE */ - - n = rt_int_parameter(rt, "electrokinetics_multisteps", &multisteps); - if (n == 1) psi_multisteps_set(obj, multisteps); - n = rt_int_parameter(rt, "electrokinetics_skipsteps", &skipsteps); - if (n == 1) psi_skipsteps_set(obj, skipsteps); - n = rt_double_parameter(rt, "electrokinetics_diffacc", &diffacc); - if (n == 1) psi_diffacc_set(obj, diffacc); - - psi_multisteps(obj, &multisteps); - pe_info(pe, "Number of multisteps: %d\n", multisteps); - pe_info(pe, "Number of skipsteps: %d\n", psi_skipsteps(obj)); - psi_diffacc(obj, &diffacc); - pe_info(pe, "Diffusive accuracy in NPE: %14.7e\n", diffacc); - - /* Tolerances and Iterations */ - - n = rt_double_parameter(rt, "electrokinetics_rel_tol", &tolerance); - if (n == 1) psi_reltol_set(obj, tolerance); - n = rt_double_parameter(rt, "electrokinetics_abs_tol", &tolerance); - if (n == 1) psi_abstol_set(obj, tolerance); - n = rt_int_parameter(rt, "electrokinetics_maxits", &niteration); - if (n == 1) psi_maxits_set(obj, niteration); - - psi_reltol(obj, &tolerance); - pe_info(pe, "Relative tolerance: %20.7e\n", tolerance); - psi_abstol(obj, &tolerance); - pe_info(pe, "Absolute tolerance: %20.7e\n", tolerance); - psi_maxits(obj, &niteration); - pe_info(pe, "Max. no. of iterations: %16d\n", niteration); - - /* External electric field */ - - { - double e0[3] = {0}; - rt_double_parameter_vector(rt, "electric_e0", e0); - psi_e0_set(obj, e0); - } - - /* Output */ - - n = 0; - n += rt_int_parameter(rt, "freq_statistics", &nfreq); - n += rt_int_parameter(rt, "freq_psi_resid", &nfreq); - if (n > 0) psi_nfreq_set(obj, nfreq);; - - - /* I/O */ - - rt_int_parameter_vector(rt, "default_io_grid", io_grid); - rt_string_parameter(rt, "psi_format", value, BUFSIZ); - - if (strcmp(value, "ASCII") == 0) { - io_format_in = IO_FORMAT_ASCII; - io_format_out = IO_FORMAT_ASCII; - } - - pe_info(pe, "I/O decomposition: %d %d %d\n", - io_grid[0], io_grid[1], - io_grid[2]); - pe_info(pe, "I/O format: %s\n", value); - - psi_init_io_info(obj, io_grid, io_format_in, io_format_out); - - return 0; -} - /***************************************************************************** * * psi_rt_init_rho @@ -197,11 +55,12 @@ int psi_rt_init_rho(pe_t * pe, rt_t * rt, psi_t * obj, map_t * map) { double ld2; /* Second Debye length for dielectric contrast */ double eps1, eps2; /* Dielectric permittivities */ - io_info_t * iohandler; - assert(pe); assert(rt); + psi_options_t opts = {.e = obj->e, .beta = obj->beta, + .epsilon1 = obj->epsilon, .epsilon2 = obj->epsilon2}; + /* Initial charge densities */ pe_info(pe, "\n"); @@ -216,7 +75,7 @@ int psi_rt_init_rho(pe_t * pe, rt_t * rt, psi_t * obj, map_t * map) { n = rt_double_parameter(rt, "electrokinetics_init_rho_el", &rho_el); if (n == 0) pe_fatal(pe, "... please set electrokinetics_init_rho_el\n"); pe_info(pe, "Initial condition rho_el: %14.7e\n", rho_el); - psi_debye_length(obj, rho_el, &ld); + psi_debye_length1(&opts, rho_el, &ld); pe_info(pe, "Debye length: %14.7e\n", ld); n = rt_double_parameter(rt, "electrokinetics_init_sigma", &sigma); @@ -232,7 +91,7 @@ int psi_rt_init_rho(pe_t * pe, rt_t * rt, psi_t * obj, map_t * map) { n = rt_double_parameter(rt, "electrokinetics_init_rho_el", &rho_el); if (n == 0) pe_fatal(pe, "... please set electrokinetics_init_rho_el\n"); pe_info(pe, "Initial condition rho_el: %14.7e\n", rho_el); - psi_debye_length(obj, rho_el, &ld); + psi_debye_length1(&opts, rho_el, &ld); pe_info(pe, "Debye length: %14.7e\n", ld); n = rt_double_parameter(rt, "electrokinetics_init_delta_el", &delta_el); @@ -248,7 +107,7 @@ int psi_rt_init_rho(pe_t * pe, rt_t * rt, psi_t * obj, map_t * map) { n = rt_double_parameter(rt, "electrokinetics_init_rho_el", &rho_el); if (n == 0) pe_fatal(pe, "... please set electrokinetics_init_rho_el\n"); pe_info(pe, "Initial condition rho_el: %14.7e\n", rho_el); - psi_debye_length(obj, rho_el, &ld); + psi_debye_length1(&opts, rho_el, &ld); pe_info(pe, "Debye length: %14.7e\n", ld); /* Call permittivities and check for dielectric contrast */ @@ -258,7 +117,7 @@ int psi_rt_init_rho(pe_t * pe, rt_t * rt, psi_t * obj, map_t * map) { /* Unless really the same number ... */ if (0 == util_double_same(eps1, eps2)) { - psi_debye_length2(obj, rho_el, &ld2); + psi_debye_length2(&opts, rho_el, &ld2); pe_info(pe, "Second Debye length: %14.7e\n", ld2); } @@ -266,12 +125,11 @@ int psi_rt_init_rho(pe_t * pe, rt_t * rt, psi_t * obj, map_t * map) { } if (strcmp(value, "from_file") == 0) { - - sprintf(filestub, "%s", "psi-00000000"); - pe_info(pe, "Initialisation requested from file %s.001-001\n", filestub); - psi_io_info(obj, &iohandler); - io_read_data(iohandler, filestub, obj); - + io_event_t event1 = {0}; + io_event_t event2 = {0}; + pe_info(pe, "Initialisation requested from file(s)\n"); + field_io_read(obj->psi, 0, &event1); + field_io_read(obj->rho, 0, &event2); } if (strcmp(value, "point_charges") == 0) { @@ -281,7 +139,7 @@ int psi_rt_init_rho(pe_t * pe, rt_t * rt, psi_t * obj, map_t * map) { n = rt_double_parameter(rt, "electrokinetics_init_rho_el", &rho_el); if (n == 0) pe_fatal(pe, "... please set electrokinetics_init_rho_el\n"); pe_info(pe, "Initial condition rho_el: %14.7e\n", rho_el); - psi_debye_length(obj, rho_el, &ld); + psi_debye_length1(&opts, rho_el, &ld); pe_info(pe, "Debye length: %14.7e\n", ld); /* Call permittivities and check for dielectric contrast */ @@ -290,7 +148,7 @@ int psi_rt_init_rho(pe_t * pe, rt_t * rt, psi_t * obj, map_t * map) { /* Unless really the same number... */ if (0 == util_double_same(eps1, eps2)) { - psi_debye_length2(obj, rho_el, &ld2); + psi_debye_length1(&opts, rho_el, &ld2); pe_info(pe, "Second Debye length: %14.7e\n", ld2); } /* Set background charge densities */ @@ -305,3 +163,125 @@ int psi_rt_init_rho(pe_t * pe, rt_t * rt, psi_t * obj, map_t * map) { return 0; } + +/***************************************************************************** + * + * psi_options_rt + * + *****************************************************************************/ + +int psi_options_rt(pe_t * pe, cs_t * cs, rt_t * rt, psi_options_t * popts) { + + psi_options_t opts = psi_options_default(cs->param->nhalo); + + assert(pe); + assert(cs); + assert(rt); + + /* Physics */ + /* The Boltzmann factor comes from the temperature */ + /* Can use "epsilon" or specific keys "epsilon1" and "epsilon2" */ + + rt_double_parameter(rt, "electrokinetics_eunit", &opts.e); + + { + double t = -1.0; + rt_double_parameter(rt, "temperature", &t); + if (t <= 0.0) pe_fatal(pe, "Please use a +ve temperature for electro\n"); + opts.beta = 1.0/t; + } + + rt_double_parameter(rt, "electrokinetics_epsilon", &opts.epsilon1); + rt_double_parameter(rt, "electrokinetics_epsilon", &opts.epsilon2); + rt_double_parameter(rt, "electrokinetics_epsilon1", &opts.epsilon1); + rt_double_parameter(rt, "electrokinetics_epsilon2", &opts.epsilon2); + + rt_double_parameter_vector(rt, "electric_e0", opts.e0); + + rt_double_parameter(rt, "electrokinetics_d0", &opts.diffusivity[0]); + rt_double_parameter(rt, "electrokinetics_d1", &opts.diffusivity[1]); + + rt_int_parameter(rt, "electrokinetics_z0", &opts.valency[0]); + rt_int_parameter(rt, "electrokinetics_z1", &opts.valency[1]); + + /* Poisson solver */ + /* There are two possible sources of nfreq */ + + rt_int_parameter(rt, "electrokinetics_maxits", &opts.maxits); + rt_int_parameter(rt, "freq_statistics", &opts.nfreq); + rt_int_parameter(rt, "freq_psi_resid", &opts.nfreq); + + rt_double_parameter(rt, "electrokinetics_rel_tol", &opts.reltol); + rt_double_parameter(rt, "electrokinetics_abs_tol", &opts.abstol); + + /* NPE time splitting and criteria */ + + rt_int_parameter(rt, "electrokinetics_multisteps", &opts.nsmallstep); + rt_double_parameter(rt, "electrokinetics_diffacc", &opts.diffacc); + + /* Field quantites */ + /* At the moment there are two fields (potential and charge densities) + * but only one input key "psi" involved */ + + { + opts.psi = field_options_ndata_nhalo(1, cs->param->nhalo); + opts.rho = field_options_ndata_nhalo(opts.nk, cs->param->nhalo); + + io_info_args_rt(rt, RT_FATAL, "psi", IO_INFO_READ_WRITE, &opts.psi.iodata); + io_info_args_rt(rt, RT_FATAL, "psi", IO_INFO_READ_WRITE, &opts.rho.iodata); + + if (opts.psi.iodata.input.mode != IO_MODE_MPIIO) { + pe_fatal(pe, "Electrokinetics i/o must use psi_io_mode mpiio\n"); + } + } + + *popts = opts; + + return 0; +} + +/***************************************************************************** + * + * psi_info + * + * Could be moved elsewhere. + * + *****************************************************************************/ + +int psi_info(pe_t * pe, const psi_t * psi) { + + double lbjerrum = 0.0; + + assert(pe); + assert(psi); + + /* Refactoring has casued a slight awkwardnesss here... */ + { + psi_options_t opts = {.e = psi->e, .beta = psi->beta, + .epsilon1 = psi->epsilon}; + psi_bjerrum_length1(&opts, &lbjerrum); + } + + /* Information */ + + pe_info(pe, "Electrokinetic species: %2d\n", psi->nk); + pe_info(pe, "Boltzmann factor: %14.7e (T = %14.7e)\n", + psi->beta, 1.0/psi->beta); + pe_info(pe, "Unit charge: %14.7e\n", psi->e); + pe_info(pe, "Permittivity: %14.7e\n", psi->epsilon); + pe_info(pe, "Bjerrum length: %14.7e\n", lbjerrum); + + for (int n = 0; n < psi->nk; n++) { + pe_info(pe, "Valency species %d: %2d\n", n, psi->valency[n]); + pe_info(pe, "Diffusivity species %d: %14.7e\n", n, psi->diffusivity[n]); + } + + pe_info(pe, "Relative tolerance: %20.7e\n", psi->reltol); + pe_info(pe, "Absolute tolerance: %20.7e\n", psi->abstol); + pe_info(pe, "Max. no. of iterations: %16d\n", psi->maxits); + + pe_info(pe, "Number of multisteps: %d\n", psi->multisteps); + pe_info(pe, "Diffusive accuracy in NPE: %14.7e\n", psi->diffacc); + + return 0; +} diff --git a/src/psi_rt.h b/src/psi_rt.h index 2fbfc55c..46f6e0ef 100644 --- a/src/psi_rt.h +++ b/src/psi_rt.h @@ -2,25 +2,26 @@ * * psi_rt.h * - * $Id$ - * * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * + * (c) 2012-2023 The University of Edinburgh + * * Kevin Stratford (kevin@epcc.ed.ac.uk) - * (c) 2012-2016 The University of Edinburgh * *****************************************************************************/ -#ifndef PSI_RT_H -#define PSI_RT_H +#ifndef LUDWIG_PSI_RT_H +#define LUDWIG_PSI_RT_H #include "pe.h" #include "runtime.h" #include "psi.h" #include "map.h" -int psi_rt_init_param(pe_t * pe, rt_t * rt, psi_t * psi); int psi_rt_init_rho(pe_t * pe, rt_t * rt, psi_t * psi, map_t * map); +int psi_options_rt(pe_t * pe, cs_t * cs, rt_t * rt, psi_options_t * opts); +int psi_info(pe_t * pe, const psi_t * psi); + #endif diff --git a/src/psi_s.h b/src/psi_s.h deleted file mode 100644 index e073e9f1..00000000 --- a/src/psi_s.h +++ /dev/null @@ -1,68 +0,0 @@ -/***************************************************************************** - * - * psi_s.h - * - * Edinburgh Soft Matter and Statistical Physics Group and - * Edinburgh Parallel Computing Centre - * - * (c) 2012-2016 The University of Edinburgh - * - * Contributinf authors: - * Kevin Stratford (kevin@epcc.ed.ac.uk) - * - *****************************************************************************/ - -#ifndef LUDWIG_PSI_S_H -#define LUDWIG_PSI_S_H - -#include -#include "io_harness.h" -#include "psi.h" -#include "memory.h" - -/* - * We store here the unit charge, the electric permittivity, and the - * temperature, all in lattice units. This allows us to work out the - * Bjerrum length, - * l_B = e^2 / (4 \pi epsilon_0 epsilon_r kT) - * which is the scale at which the interaction energy between two - * unit charges is equal to kT. The aim is that the Bjerrum length - * should be < 1 (e.g. 0.7) in lattice units. - * - * For water at room temperature, the Bjerrum length is about - * 7 Angstrom. - * - */ - -struct psi_s { - pe_t * pe; /* Parallel environment */ - cs_t * cs; /* Coordinate system */ - - int nk; /* Number of species */ - int nsites; /* Number sites storage */ - double * psi; /* Electric potential */ - double * rho; /* Charge densities */ - double * diffusivity; /* Diffusivity for each species */ - int * valency; /* Valency for each species */ - double e; /* unit charge */ - double epsilon; /* first and reference permittivity */ - double epsilon2; /* second permittivity */ - double beta; /* Boltzmann factor (1 / k_B T) */ - double reltol; /* Relative tolerance for Poisson solver */ - double abstol; /* Absolute tolerance for Poisson solver */ - int method; /* Force computation method */ - int maxits; /* Maximum number of iterations */ - int multisteps; /* Number of substeps in charge dynamics */ - int skipsteps; /* Poisson equation solved every skipsteps timesteps */ - int nfreq_io; /* Field output */ - int nfreq; /* Residual statisics output */ - double diffacc; /* Number of substeps in charge dynamics */ - double e0[3]; /* External electric field */ - MPI_Datatype psihalo[3]; /* psi field halo */ - MPI_Datatype rhohalo[3]; /* charge densities halo */ - io_info_t * info; /* I/O informtation */ -}; - -int psi_halo(int nf, double * f, MPI_Datatype halo[3]); - -#endif diff --git a/src/psi_sor.c b/src/psi_sor.c index 7ffb4539..db27a590 100644 --- a/src/psi_sor.c +++ b/src/psi_sor.c @@ -16,7 +16,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2013-2022 The University of Edinburgh + * (c) 2013-2023 The University of Edinburgh * * Contributing Authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -31,7 +31,6 @@ #include "pe.h" #include "coords.h" -#include "psi_s.h" #include "psi_sor.h" #include "util.h" @@ -118,6 +117,8 @@ int psi_sor_poisson(psi_t * obj, int its) { MPI_Comm comm; /* Cartesian communicator */ + double * __restrict__ psidata = obj->psi->data; + cs_ltot(obj->cs, ltot); cs_nhalo(obj->cs, &nhalo); cs_nsites(obj->cs, &nsites); @@ -159,13 +160,13 @@ int psi_sor_poisson(psi_t * obj, int its) { /* 6-point stencil of Laplacian */ dpsi - = obj->psi[addr_rank0(nsites, index + xs)] - + obj->psi[addr_rank0(nsites, index - xs)] - + obj->psi[addr_rank0(nsites, index + ys)] - + obj->psi[addr_rank0(nsites, index - ys)] - + obj->psi[addr_rank0(nsites, index + zs)] - + obj->psi[addr_rank0(nsites, index - zs)] - - 6.0*obj->psi[addr_rank0(nsites, index)]; + = psidata[addr_rank0(nsites, index + xs)] + + psidata[addr_rank0(nsites, index - xs)] + + psidata[addr_rank0(nsites, index + ys)] + + psidata[addr_rank0(nsites, index - ys)] + + psidata[addr_rank0(nsites, index + zs)] + + psidata[addr_rank0(nsites, index - zs)] + - 6.0*psidata[addr_rank0(nsites, index)]; /* Non-dimensional potential in Poisson eqn requires e/kT */ rnorm_local[0] += fabs(epsilon*dpsi + eunit*beta*rho_elec); @@ -197,17 +198,17 @@ int psi_sor_poisson(psi_t * obj, int its) { /* 6-point stencil of Laplacian */ dpsi - = obj->psi[addr_rank0(nsites, index + xs)] - + obj->psi[addr_rank0(nsites, index - xs)] - + obj->psi[addr_rank0(nsites, index + ys)] - + obj->psi[addr_rank0(nsites, index - ys)] - + obj->psi[addr_rank0(nsites, index + zs)] - + obj->psi[addr_rank0(nsites, index - zs)] - - 6.0*obj->psi[addr_rank0(nsites, index)]; + = psidata[addr_rank0(nsites, index + xs)] + + psidata[addr_rank0(nsites, index - xs)] + + psidata[addr_rank0(nsites, index + ys)] + + psidata[addr_rank0(nsites, index - ys)] + + psidata[addr_rank0(nsites, index + zs)] + + psidata[addr_rank0(nsites, index - zs)] + - 6.0*psidata[addr_rank0(nsites, index)]; /* Non-dimensional potential in Poisson eqn requires e/kT */ residual = epsilon*dpsi + eunit*beta*rho_elec; - obj->psi[addr_rank0(nsites, index)] + psidata[addr_rank0(nsites, index)] -= omega*residual / (-6.0*epsilon); rnorm_local[1] += fabs(residual); } @@ -261,7 +262,8 @@ int psi_sor_poisson(psi_t * obj, int its) { if (n == niteration-1) { pe_info(obj->pe, "\n"); pe_info(obj->pe, "SOR solver exceeded %d iterations\n", n+1); - pe_info(obj->pe, "SOR residual %le (initial) %le (final)\n\n", rnorm[0], rnorm[1]); + pe_info(obj->pe, "SOR residual %le (initial) %le (final)\n\n", + rnorm[0], rnorm[1]); } } @@ -313,6 +315,9 @@ int psi_sor_vare_poisson(psi_t * obj, fe_es_t * fe, f_vare_t fepsilon, int its) MPI_Comm comm; /* Cartesian communicator */ + double * __restrict__ psidata = obj->psi->data; + + assert(obj); assert(fepsilon); @@ -354,39 +359,39 @@ int psi_sor_vare_poisson(psi_t * obj, fe_es_t * fe, f_vare_t fepsilon, int its) /* Laplacian part of operator */ - depsi += eps0*(-6.0*obj->psi[addr_rank0(nsites, index)] - + obj->psi[addr_rank0(nsites, index + xs)] - + obj->psi[addr_rank0(nsites, index - xs)] - + obj->psi[addr_rank0(nsites, index + ys)] - + obj->psi[addr_rank0(nsites, index - ys)] - + obj->psi[addr_rank0(nsites, index + zs)] - + obj->psi[addr_rank0(nsites, index - zs)]); + depsi += eps0*(-6.0*psidata[addr_rank0(nsites, index)] + + psidata[addr_rank0(nsites, index + xs)] + + psidata[addr_rank0(nsites, index - xs)] + + psidata[addr_rank0(nsites, index + ys)] + + psidata[addr_rank0(nsites, index - ys)] + + psidata[addr_rank0(nsites, index + zs)] + + psidata[addr_rank0(nsites, index - zs)]); /* Additional terms in generalised Poisson equation */ fepsilon(fe, index + xs, &eps1); - depsi += 0.25*eps1*(obj->psi[addr_rank0(nsites, index + xs)] - - obj->psi[addr_rank0(nsites, index - xs)]); + depsi += 0.25*eps1*(psidata[addr_rank0(nsites, index + xs)] + - psidata[addr_rank0(nsites, index - xs)]); fepsilon(fe, index - xs, &eps1); - depsi -= 0.25*eps1*(obj->psi[addr_rank0(nsites, index + xs)] - - obj->psi[addr_rank0(nsites, index - xs)]); + depsi -= 0.25*eps1*(psidata[addr_rank0(nsites, index + xs)] + - psidata[addr_rank0(nsites, index - xs)]); fepsilon(fe, index + ys, &eps1); - depsi += 0.25*eps1*(obj->psi[addr_rank0(nsites, index + ys)] - - obj->psi[addr_rank0(nsites, index - ys)]); + depsi += 0.25*eps1*(psidata[addr_rank0(nsites, index + ys)] + - psidata[addr_rank0(nsites, index - ys)]); fepsilon(fe, index - ys, &eps1); - depsi -= 0.25*eps1*(obj->psi[addr_rank0(nsites, index + ys)] - - obj->psi[addr_rank0(nsites, index - ys)]); + depsi -= 0.25*eps1*(psidata[addr_rank0(nsites, index + ys)] + - psidata[addr_rank0(nsites, index - ys)]); fepsilon(fe, index + zs, &eps1); - depsi += 0.25*eps1*(obj->psi[addr_rank0(nsites, index + zs)] - - obj->psi[addr_rank0(nsites, index - zs)]); + depsi += 0.25*eps1*(psidata[addr_rank0(nsites, index + zs)] + - psidata[addr_rank0(nsites, index - zs)]); fepsilon(fe, index - zs, &eps1); - depsi -= 0.25*eps1*(obj->psi[addr_rank0(nsites, index + zs)] - - obj->psi[addr_rank0(nsites, index - zs)]); + depsi -= 0.25*eps1*(psidata[addr_rank0(nsites, index + zs)] + - psidata[addr_rank0(nsites, index - zs)]); /* Non-dimensional potential in Poisson eqn requires e/kT */ rnorm_local[0] += fabs(depsi + eunit*beta*rho_elec); @@ -420,43 +425,43 @@ int psi_sor_vare_poisson(psi_t * obj, fe_es_t * fe, f_vare_t fepsilon, int its) /* Laplacian part of operator */ - depsi += eps0*(-6.0*obj->psi[addr_rank0(nsites, index)] - + obj->psi[addr_rank0(nsites, index + xs)] - + obj->psi[addr_rank0(nsites, index - xs)] - + obj->psi[addr_rank0(nsites, index + ys)] - + obj->psi[addr_rank0(nsites, index - ys)] - + obj->psi[addr_rank0(nsites, index + zs)] - + obj->psi[addr_rank0(nsites, index - zs)]); + depsi += eps0*(-6.0*psidata[addr_rank0(nsites, index)] + + psidata[addr_rank0(nsites, index + xs)] + + psidata[addr_rank0(nsites, index - xs)] + + psidata[addr_rank0(nsites, index + ys)] + + psidata[addr_rank0(nsites, index - ys)] + + psidata[addr_rank0(nsites, index + zs)] + + psidata[addr_rank0(nsites, index - zs)]); /* Additional terms in generalised Poisson equation */ fepsilon(fe, index + xs, &eps1); - depsi += 0.25*eps1*(obj->psi[addr_rank0(nsites, index + xs)] - - obj->psi[addr_rank0(nsites, index - xs)]); + depsi += 0.25*eps1*(psidata[addr_rank0(nsites, index + xs)] + - psidata[addr_rank0(nsites, index - xs)]); fepsilon(fe, index - xs, &eps1); - depsi -= 0.25*eps1*(obj->psi[addr_rank0(nsites, index + xs)] - - obj->psi[addr_rank0(nsites, index - xs)]); + depsi -= 0.25*eps1*(psidata[addr_rank0(nsites, index + xs)] + - psidata[addr_rank0(nsites, index - xs)]); fepsilon(fe, index + ys, &eps1); - depsi += 0.25*eps1*(obj->psi[addr_rank0(nsites, index + ys)] - - obj->psi[addr_rank0(nsites, index - ys)]); + depsi += 0.25*eps1*(psidata[addr_rank0(nsites, index + ys)] + - psidata[addr_rank0(nsites, index - ys)]); fepsilon(fe, index - ys, &eps1); - depsi -= 0.25*eps1*(obj->psi[addr_rank0(nsites, index + ys)] - - obj->psi[addr_rank0(nsites, index - ys)]); + depsi -= 0.25*eps1*(psidata[addr_rank0(nsites, index + ys)] + - psidata[addr_rank0(nsites, index - ys)]); fepsilon(fe, index + zs, &eps1); - depsi += 0.25*eps1*(obj->psi[addr_rank0(nsites, index + zs)] - - obj->psi[addr_rank0(nsites, index - zs)]); + depsi += 0.25*eps1*(psidata[addr_rank0(nsites, index + zs)] + - psidata[addr_rank0(nsites, index - zs)]); fepsilon(fe, index - zs, &eps1); - depsi -= 0.25*eps1*(obj->psi[addr_rank0(nsites, index + zs)] - - obj->psi[addr_rank0(nsites, index - zs)]); + depsi -= 0.25*eps1*(psidata[addr_rank0(nsites, index + zs)] + - psidata[addr_rank0(nsites, index - zs)]); /* Non-dimensional potential in Poisson eqn requires e/kT */ residual = depsi + eunit*beta*rho_elec; - obj->psi[addr_rank0(nsites,index)] -= omega*residual / (-6.0*eps0); + psidata[addr_rank0(nsites,index)] -= omega*residual / (-6.0*eps0); rnorm_local[1] += fabs(residual); } } diff --git a/src/psi_stats.c b/src/psi_stats.c index f992ec7a..8c38185d 100644 --- a/src/psi_stats.c +++ b/src/psi_stats.c @@ -7,7 +7,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2012-2017 The University of Edinburgh + * (c) 2012-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -21,7 +21,6 @@ #include "pe.h" #include "coords.h" #include "util.h" -#include "psi_s.h" #include "psi_stats.h" /***************************************************************************** diff --git a/tests/regression/d3q19-elec/map-elec-gc2.001-001 b/tests/regression/d3q19-elec/map-elec-gc2.001-001 deleted file mode 100644 index 01b0aa2b0ec349d7fc7c33c9aaa484a4d3e5a788..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1024 acmZQ%Ln5Do%r2+w0VG2t0xi%zoC@kv!Bc<9?Q%moao*{gl4m zwQKb@llskl^Vz=Z?=`;r=Q4jD)6kp)b@@0Sz7O|<`%!<;A8Yj-Yd+uuKHvjB-~&G3 z13t(E_=u1Ah>!S)kNAj>QU~TIe8fk5#7BI@M|{Lbe3S|B5g+jpAMp_%@ev=T4$P1E zh>!S)kNAj>_=u1AC==i#KH?)j;v+udBR)zUm>=;GAMp_%@ev>K5g+kUCcsC0#7BI@ zM|{Lbe3UvcKjI@k;v+udBR=9IKH{THfRFfykNAj>_=u1AD0N_d#7BI@M|{Lbe8fk5 z#7CI`AMp_%@ev>K5g+kU>cISnkNAj>_=u1A=pP@Q{j@ay{mwqCo_Sn%_seGozKCOT diff --git a/tests/regression/d3q19-elec/serial-elec-do1.inp b/tests/regression/d3q19-elec/serial-elec-do1.inp index 263911c3..0a483331 100644 --- a/tests/regression/d3q19-elec/serial-elec-do1.inp +++ b/tests/regression/d3q19-elec/serial-elec-do1.inp @@ -111,39 +111,12 @@ freq_measure 100000 freq_config 100000 config_at_end no +psi_io_mode mpiio +psi_io_report no + ############################################################################### # -# 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] -# 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 # ############################################################################### @@ -152,8 +125,9 @@ electrokinetics_z1 -1 electrokinetics_d0 0.1 electrokinetics_d1 0.1 electrokinetics_eunit 1.0 -electrokinetics_epsilon 300.0 -electrokinetics_init uniform +electrokinetics_epsilon1 300.0 +electrokinetics_epsilon2 300.0 +electrokinetics_init uniform electrokinetics_init_rho_el 0.00047 electrokinetics_rel_tol 1e-06 @@ -163,7 +137,6 @@ electrokinetics_diffacc 0.5 electrokinetics_multisteps 1 -electrosymmetric_epsilon2 300.0 electrosymmetric_delta_mu0 +4.0 electrosymmetric_delta_mu1 -4.0 diff --git a/tests/regression/d3q19-elec/serial-elec-do1.log b/tests/regression/d3q19-elec/serial-elec-do1.log index 560e1e24..e73f49a8 100644 --- a/tests/regression/d3q19-elec/serial-elec-do1.log +++ b/tests/regression/d3q19-elec/serial-elec-do1.log @@ -46,14 +46,11 @@ Valency species 0: 1 Diffusivity species 0: 1.0000000e-01 Valency species 1: -1 Diffusivity species 1: 1.0000000e-01 -Number of multisteps: 1 -Number of skipsteps: 1 -Diffusive accuracy in NPE: 5.0000000e-01 Relative tolerance: 1.0000000e-06 Absolute tolerance: 1.0000000e-07 Max. no. of iterations: 10000 -I/O decomposition: 1 1 1 -I/O format: BINARY +Number of multisteps: 1 +Diffusive accuracy in NPE: 5.0000000e-01 Coupling part ------------- diff --git a/tests/regression/d3q19-elec/serial-elec-do2.inp b/tests/regression/d3q19-elec/serial-elec-do2.inp index 5c24d8af..82353bae 100644 --- a/tests/regression/d3q19-elec/serial-elec-do2.inp +++ b/tests/regression/d3q19-elec/serial-elec-do2.inp @@ -112,39 +112,12 @@ freq_measure 100000 freq_config 100000 config_at_end no +psi_io_mode mpiio +psi_io_report no + ############################################################################### # -# 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] -# 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 # ############################################################################### @@ -153,7 +126,8 @@ electrokinetics_z1 -1 electrokinetics_d0 0.1 electrokinetics_d1 0.1 electrokinetics_eunit 1.0 -electrokinetics_epsilon 300.0 +electrokinetics_epsilon1 300.0 +electrokinetics_epsilon2 300.0 electrokinetics_init uniform electrokinetics_init_rho_el 0.00047 @@ -164,7 +138,6 @@ electrokinetics_diffacc 0.5 electrokinetics_multisteps 1 -electrosymmetric_epsilon2 300.0 electrosymmetric_delta_mu0 +4.0 electrosymmetric_delta_mu1 -4.0 diff --git a/tests/regression/d3q19-elec/serial-elec-do2.log b/tests/regression/d3q19-elec/serial-elec-do2.log index abc9b44c..32d38620 100644 --- a/tests/regression/d3q19-elec/serial-elec-do2.log +++ b/tests/regression/d3q19-elec/serial-elec-do2.log @@ -46,14 +46,12 @@ Valency species 0: 1 Diffusivity species 0: 1.0000000e-01 Valency species 1: -1 Diffusivity species 1: 1.0000000e-01 -Number of multisteps: 1 -Number of skipsteps: 1 -Diffusive accuracy in NPE: 5.0000000e-01 Relative tolerance: 1.0000000e-06 Absolute tolerance: 1.0000000e-07 Max. no. of iterations: 10000 -I/O decomposition: 1 1 1 -I/O format: BINARY +Number of multisteps: 1 +Diffusive accuracy in NPE: 5.0000000e-01 + Coupling part ------------- diff --git a/tests/regression/d3q19-elec/serial-elec-do3.inp b/tests/regression/d3q19-elec/serial-elec-do3.inp index 00e1a55d..1cda9f13 100644 --- a/tests/regression/d3q19-elec/serial-elec-do3.inp +++ b/tests/regression/d3q19-elec/serial-elec-do3.inp @@ -1,6 +1,6 @@ ############################################################################## # -# Donnan potential with dielectric contrast cf do1. +# Donnan potential with solvation contrast cf do1. # ############################################################################## @@ -98,9 +98,12 @@ freq_measure 100000 freq_config 100000 config_at_end no +psi_io_mode mpiio +psi_io_report no + ############################################################################### # -# Electrokinetics ALWAYS 2 SPECIES FOR NOW +# Electrokinetics # ############################################################################### @@ -109,7 +112,8 @@ electrokinetics_z1 -1 electrokinetics_d0 0.1 electrokinetics_d1 0.1 electrokinetics_eunit 1.0 -electrokinetics_epsilon 300.0 +electrokinetics_epsilon1 300.0 +electrokinetics_epsilon2 300.0 electrokinetics_init uniform electrokinetics_init_rho_el 0.00047 @@ -120,7 +124,6 @@ electrokinetics_diffacc 0.5 electrokinetics_multisteps 1 -electrosymmetric_epsilon2 300.0 electrosymmetric_delta_mu0 +5.0 electrosymmetric_delta_mu1 -3.0 diff --git a/tests/regression/d3q19-elec/serial-elec-do3.log b/tests/regression/d3q19-elec/serial-elec-do3.log index 2afd21f7..843a4c8c 100644 --- a/tests/regression/d3q19-elec/serial-elec-do3.log +++ b/tests/regression/d3q19-elec/serial-elec-do3.log @@ -46,14 +46,11 @@ Valency species 0: 1 Diffusivity species 0: 1.0000000e-01 Valency species 1: -1 Diffusivity species 1: 1.0000000e-01 -Number of multisteps: 1 -Number of skipsteps: 1 -Diffusive accuracy in NPE: 5.0000000e-01 Relative tolerance: 1.0000000e-06 Absolute tolerance: 1.0000000e-07 Max. no. of iterations: 10000 -I/O decomposition: 1 1 1 -I/O format: BINARY +Number of multisteps: 1 +Diffusive accuracy in NPE: 5.0000000e-01 Coupling part ------------- diff --git a/tests/regression/d3q19-elec/serial-elec-dr1.inp b/tests/regression/d3q19-elec/serial-elec-dr1.inp index e9475a3f..a59545c9 100644 --- a/tests/regression/d3q19-elec/serial-elec-dr1.inp +++ b/tests/regression/d3q19-elec/serial-elec-dr1.inp @@ -114,39 +114,12 @@ freq_measure 10000000 freq_config 10000000 config_at_end no +psi_io_mode mpiio +psi_io_report no + ############################################################################### # -# 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] -# 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 # ############################################################################### @@ -155,7 +128,8 @@ electrokinetics_z1 -1 electrokinetics_d0 0.01 electrokinetics_d1 0.01 electrokinetics_eunit 0.5 -electrokinetics_epsilon 270.0 +electrokinetics_epsilon1 270.0 +electrokinetics_epsilon2 30.0 electrokinetics_init uniform electrokinetics_init_rho_el 0.00 @@ -166,7 +140,6 @@ electrokinetics_diffacc 0.5 electrokinetics_multisteps 1 -electrosymmetric_epsilon2 30.0 electrosymmetric_delta_mu0 0.0 electrosymmetric_delta_mu1 0.0 diff --git a/tests/regression/d3q19-elec/serial-elec-dr1.log b/tests/regression/d3q19-elec/serial-elec-dr1.log index 37bf1188..67f9c76f 100644 --- a/tests/regression/d3q19-elec/serial-elec-dr1.log +++ b/tests/regression/d3q19-elec/serial-elec-dr1.log @@ -46,14 +46,11 @@ Valency species 0: 1 Diffusivity species 0: 1.0000000e-02 Valency species 1: -1 Diffusivity species 1: 1.0000000e-02 -Number of multisteps: 1 -Number of skipsteps: 1 -Diffusive accuracy in NPE: 5.0000000e-01 Relative tolerance: 1.0000000e-06 Absolute tolerance: 1.0000000e-07 Max. no. of iterations: 2000 -I/O decomposition: 1 1 1 -I/O format: BINARY +Number of multisteps: 1 +Diffusive accuracy in NPE: 5.0000000e-01 Coupling part ------------- diff --git a/tests/regression/d3q19-elec/serial-elec-dr2.inp b/tests/regression/d3q19-elec/serial-elec-dr2.inp index a42050de..918a772e 100644 --- a/tests/regression/d3q19-elec/serial-elec-dr2.inp +++ b/tests/regression/d3q19-elec/serial-elec-dr2.inp @@ -115,39 +115,12 @@ freq_measure 10000000 freq_config 10000000 config_at_end no +psi_io_mode mpiio +psi_io_report no + ############################################################################### # -# 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] -# 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 # ############################################################################### @@ -156,7 +129,8 @@ electrokinetics_z1 -1 electrokinetics_d0 0.01 electrokinetics_d1 0.01 electrokinetics_eunit 0.5 -electrokinetics_epsilon 270.0 +electrokinetics_epsilon1 270.0 +electrokinetics_epsilon2 30.0 electrokinetics_init uniform electrokinetics_init_rho_el 0.00 @@ -167,7 +141,6 @@ electrokinetics_diffacc 0.5 electrokinetics_multisteps 1 -electrosymmetric_epsilon2 30.0 electrosymmetric_delta_mu0 0.0 electrosymmetric_delta_mu1 0.0 diff --git a/tests/regression/d3q19-elec/serial-elec-dr2.log b/tests/regression/d3q19-elec/serial-elec-dr2.log index ca382985..7c0bf7e5 100644 --- a/tests/regression/d3q19-elec/serial-elec-dr2.log +++ b/tests/regression/d3q19-elec/serial-elec-dr2.log @@ -46,14 +46,11 @@ Valency species 0: 1 Diffusivity species 0: 1.0000000e-02 Valency species 1: -1 Diffusivity species 1: 1.0000000e-02 -Number of multisteps: 1 -Number of skipsteps: 1 -Diffusive accuracy in NPE: 5.0000000e-01 Relative tolerance: 1.0000000e-06 Absolute tolerance: 1.0000000e-07 Max. no. of iterations: 2000 -I/O decomposition: 1 1 1 -I/O format: BINARY +Number of multisteps: 1 +Diffusive accuracy in NPE: 5.0000000e-01 Coupling part ------------- diff --git a/tests/regression/d3q19-elec/serial-elec-eo1.inp b/tests/regression/d3q19-elec/serial-elec-eo1.inp index 3221cb63..cff5ff19 100644 --- a/tests/regression/d3q19-elec/serial-elec-eo1.inp +++ b/tests/regression/d3q19-elec/serial-elec-eo1.inp @@ -87,14 +87,8 @@ freq_shear_measurement 1000000 freq_shear_output 1000000 config_at_end no -default_io_grid 1_1_1 - -distribution_io_grid 1_1_1 - -phi_io_grid 1_1_1 -phi_format ASCII -psi_format ASCII -vel_format ASCII +psi_io_mode mpiio +psi_io_report no stats_vel_print_vol_flux yes @@ -108,18 +102,7 @@ colloid_io_freq 1000 ############################################################################### # -# 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 -# -# Also important -# -# temperature sets Boltzmann factor beta +# Electrokinetics # ############################################################################### diff --git a/tests/regression/d3q19-elec/serial-elec-eo1.log b/tests/regression/d3q19-elec/serial-elec-eo1.log index f6d8e141..47f0b7fd 100644 --- a/tests/regression/d3q19-elec/serial-elec-eo1.log +++ b/tests/regression/d3q19-elec/serial-elec-eo1.log @@ -30,14 +30,11 @@ Valency species 0: 1 Diffusivity species 0: 1.0000000e-02 Valency species 1: -1 Diffusivity species 1: 1.0000000e-02 -Number of multisteps: 1 -Number of skipsteps: 1 -Diffusive accuracy in NPE: 0.0000000e+00 Relative tolerance: 1.1920929e-07 Absolute tolerance: 1.1920929e-09 Max. no. of iterations: 10000 -I/O decomposition: 1 1 1 -I/O format: ASCII +Number of multisteps: 1 +Diffusive accuracy in NPE: 0.0000000e+00 Force calculation: phi_gradmu_correction System properties diff --git a/tests/regression/d3q19-elec/serial-elec-eo2.inp b/tests/regression/d3q19-elec/serial-elec-eo2.inp index f1a8c541..e4e8b5b3 100644 --- a/tests/regression/d3q19-elec/serial-elec-eo2.inp +++ b/tests/regression/d3q19-elec/serial-elec-eo2.inp @@ -80,9 +80,8 @@ freq_statistics 100 freq_measure 10000000 config_at_end no -default_io_grid 1_1_1 - -distribution_io_grid 1_1_1 +psi_io_mode mpiio +psi_io_report no stats_vel_print_vol_flux yes diff --git a/tests/regression/d3q19-elec/serial-elec-eo2.log b/tests/regression/d3q19-elec/serial-elec-eo2.log index e6856dcb..3770f0d1 100644 --- a/tests/regression/d3q19-elec/serial-elec-eo2.log +++ b/tests/regression/d3q19-elec/serial-elec-eo2.log @@ -30,14 +30,11 @@ Valency species 0: 1 Diffusivity species 0: 1.0000000e-02 Valency species 1: -1 Diffusivity species 1: 1.0000000e-02 -Number of multisteps: 1 -Number of skipsteps: 1 -Diffusive accuracy in NPE: 5.0000000e-01 Relative tolerance: 1.0000000e-07 Absolute tolerance: 1.0000000e-09 Max. no. of iterations: 2000 -I/O decomposition: 1 1 1 -I/O format: BINARY +Number of multisteps: 1 +Diffusive accuracy in NPE: 5.0000000e-01 Force calculation: phi_gradmu_correction System properties diff --git a/tests/regression/d3q19-elec/serial-elec-ep1.inp b/tests/regression/d3q19-elec/serial-elec-ep1.inp index bea80056..29e45c37 100644 --- a/tests/regression/d3q19-elec/serial-elec-ep1.inp +++ b/tests/regression/d3q19-elec/serial-elec-ep1.inp @@ -111,6 +111,9 @@ freq_statistics 100 freq_measure 10000000 config_at_end no +psi_io_mode mpiio +psi_io_report no + ############################################################################## # # colloid i/o @@ -124,37 +127,7 @@ colloid_io_format_output ASCII ############################################################################### # -# 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] -# 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 # ############################################################################### @@ -173,7 +146,6 @@ electrokinetics_maxits 5000 electrokinetics_diffacc 0.5 electrokinetics_multisteps 1 -electrokinetics_skipsteps 1 ############################################################################### # diff --git a/tests/regression/d3q19-elec/serial-elec-ep1.log b/tests/regression/d3q19-elec/serial-elec-ep1.log index fb814029..10fabd17 100644 --- a/tests/regression/d3q19-elec/serial-elec-ep1.log +++ b/tests/regression/d3q19-elec/serial-elec-ep1.log @@ -30,14 +30,11 @@ Valency species 0: 1 Diffusivity species 0: 1.0000000e-02 Valency species 1: -1 Diffusivity species 1: 1.0000000e-02 -Number of multisteps: 1 -Number of skipsteps: 1 -Diffusive accuracy in NPE: 5.0000000e-01 Relative tolerance: 1.0000000e-07 Absolute tolerance: 1.0000000e-15 Max. no. of iterations: 5000 -I/O decomposition: 1 1 1 -I/O format: BINARY +Number of multisteps: 1 +Diffusive accuracy in NPE: 5.0000000e-01 Force calculation: stress_divergence System properties diff --git a/tests/regression/d3q19-elec/serial-elec-ep2.inp b/tests/regression/d3q19-elec/serial-elec-ep2.inp index bfae7fdf..e0d219ca 100644 --- a/tests/regression/d3q19-elec/serial-elec-ep2.inp +++ b/tests/regression/d3q19-elec/serial-elec-ep2.inp @@ -113,6 +113,9 @@ freq_statistics 100 freq_measure 10000000 config_at_end no +psi_io_mode mpiio +psi_io_report no + ############################################################################## # # colloid i/o @@ -126,37 +129,7 @@ colloid_io_format_output ASCII ############################################################################### # -# 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] -# 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 # ############################################################################### @@ -175,7 +148,6 @@ electrokinetics_maxits 5000 electrokinetics_diffacc 0.5 electrokinetics_multisteps 1 -electrokinetics_skipsteps 1 ############################################################################### # diff --git a/tests/regression/d3q19-elec/serial-elec-ep2.log b/tests/regression/d3q19-elec/serial-elec-ep2.log index 46578c4b..cf66c5c2 100644 --- a/tests/regression/d3q19-elec/serial-elec-ep2.log +++ b/tests/regression/d3q19-elec/serial-elec-ep2.log @@ -30,14 +30,11 @@ Valency species 0: 1 Diffusivity species 0: 1.0000000e-02 Valency species 1: -1 Diffusivity species 1: 1.0000000e-02 -Number of multisteps: 1 -Number of skipsteps: 1 -Diffusive accuracy in NPE: 5.0000000e-01 Relative tolerance: 1.0000000e-07 Absolute tolerance: 1.0000000e-15 Max. no. of iterations: 5000 -I/O decomposition: 1 1 1 -I/O format: BINARY +Number of multisteps: 1 +Diffusive accuracy in NPE: 5.0000000e-01 Force calculation: phi_gradmu_correction System properties diff --git a/tests/regression/d3q19-elec/serial-elec-gc1.inp b/tests/regression/d3q19-elec/serial-elec-gc1.inp index 7cdc3781..e51fa836 100644 --- a/tests/regression/d3q19-elec/serial-elec-gc1.inp +++ b/tests/regression/d3q19-elec/serial-elec-gc1.inp @@ -15,7 +15,7 @@ ############################################################################### N_start 0 -N_cycles 1000 +N_cycles 100 ############################################################################## # @@ -75,7 +75,7 @@ boundary_walls 0_0_0 # ############################################################################### -freq_statistics 1000 +freq_statistics 100 freq_psi_resid 10000 config_at_end no @@ -85,18 +85,7 @@ stats_vel_print_vol_flux yes ############################################################################### # -# 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 -# -# Also important -# -# temperature sets Boltzmann factor beta +# Electrokinetics # ############################################################################### @@ -110,6 +99,9 @@ electrokinetics_init gouy_chapman electrokinetics_init_rho_el 0.001 electrokinetics_init_sigma 0.03125 +psi_io_mode mpiio +psi_io_report no + ############################################################################### # # Miscellaneous diff --git a/tests/regression/d3q19-elec/serial-elec-gc1.log b/tests/regression/d3q19-elec/serial-elec-gc1.log index 9e7d3c8a..998d49cb 100644 --- a/tests/regression/d3q19-elec/serial-elec-gc1.log +++ b/tests/regression/d3q19-elec/serial-elec-gc1.log @@ -30,14 +30,11 @@ Valency species 0: 1 Diffusivity species 0: 1.0000000e-02 Valency species 1: -1 Diffusivity species 1: 1.0000000e-02 -Number of multisteps: 1 -Number of skipsteps: 1 -Diffusive accuracy in NPE: 0.0000000e+00 Relative tolerance: 1.1920929e-07 Absolute tolerance: 1.1920929e-09 Max. no. of iterations: 10000 -I/O decomposition: 1 1 1 -I/O format: BINARY +Number of multisteps: 1 +Diffusive accuracy in NPE: 0.0000000e+00 Force calculation: phi_gradmu_correction System properties @@ -63,6 +60,7 @@ I/O grid: 1 1 1 Lattice Boltzmann collision --------------------------- +Relaxation time scheme: M10 Hydrodynamic modes: on Ghost modes: on Isothermal fluctuations: off @@ -111,14 +109,14 @@ Starting time step loop. 1 multisteps Scalars - total mean variance min max -[rho] 992.00 1.00000000000 3.8857806e-15 0.99999987748 1.00000032642 -[psi] -6.3948846e-14 -1.2074611e+00 2.4452182e+00 -[rho] 1.9920000e+00 4.5245968e-04 3.1250000e-02 -[rho] 1.9920000e+00 0.0000000e+00 3.8175240e-03 -[elc] -7.7715612e-16 -3.3650643e-03 3.1250000e-02 +[rho] 992.00 1.00000000000 1.5210055e-14 0.99999998294 1.00000011145 +[psi] 1.1990409e-14 -1.5210888e+00 2.9128003e+00 +[rho] 1.9920000e+00 8.2093661e-04 3.1250000e-02 +[rho] 1.9920000e+00 0.0000000e+00 2.4098347e-03 +[elc] -1.4988011e-14 -1.5888981e-03 3.1250000e-02 Free energy density - timestep total fluid -[fed] 1000 -2.4980173368e-02 -2.2516704343e-02 +[fed] 100 -2.4613216884e-02 -2.2373587017e-02 Momentum - x y z [total ] 5.9174887e-14 -6.6613381e-16 0.0000000e+00 @@ -126,11 +124,11 @@ Momentum - x y z [walls ] 4.1333603e-13 0.0000000e+00 0.0000000e+00 Velocity - x y z -[minimum ] -3.4523433e-08 -6.9388947e-18 0.0000000e+00 -[maximum ] 3.4523432e-08 6.9388945e-18 1.1754944e-38 -[vol flux] -3.5393913e-13 -8.3266727e-16 0.0000000e+00 +[minimum ] -4.7355583e-08 -6.9388940e-18 0.0000000e+00 +[maximum ] 4.7355582e-08 6.9388940e-18 1.1754944e-38 +[vol flux] -1.4543921e-13 -2.2204458e-16 0.0000000e+00 -Completed cycle 1000 +Completed cycle 100 Timer resolution: 1e-06 second diff --git a/tests/regression/d3q19-elec/serial-elec-gc2.inp b/tests/regression/d3q19-elec/serial-elec-gc2.inp deleted file mode 100644 index 3b86e941..00000000 --- a/tests/regression/d3q19-elec/serial-elec-gc2.inp +++ /dev/null @@ -1,120 +0,0 @@ -############################################################################## -# -# Gouy-Chapman electrokinetics -# -# As serial-elec-gc1.inp, but initial conditions taken from file: -# psi-00000000.001-001 -# map-elec-gc2.001-001 -# -# Also fewer time steps. -# -############################################################################## - -############################################################################## -# -# Run duration -# -############################################################################### - -N_start 0 -N_cycles 100 - -############################################################################## -# -# System and MPI -# -############################################################################## - -size 64_4_4 -grid 1_1_2 -periodicity 1_1_1 -lb_halo_scheme lb_halo_openmp_reduced - -############################################################################## -# -# Fluid parameters -# -############################################################################## - -viscosity 0.1 -viscosity_bulk 0.1 - -isothermal_fluctuations off -temperature 3.33333333333333333e-5 - - -############################################################################## -# -# Free energy parameters -# -############################################################################### - -free_energy fe_electro -fe_force_method phi_gradmu_correction - -fd_advection_scheme_order 3 - -############################################################################### -# -# Colloid parameters -# -############################################################################### - -colloid_init none - -############################################################################### -# -# Walls / boundaries -# -############################################################################### - -porous_media_file yes -porous_media_ndata 0 -porous_media_format binary - -############################################################################### -# -# Output frequency and type -# -############################################################################### - -freq_statistics 100 -freq_psi_resid 10000 -config_at_end no - -colloid_io_freq 1000 - -stats_vel_print_vol_flux yes - -############################################################################### -# -# 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 -# -# Also important -# -# temperature sets Boltzmann factor beta -# -############################################################################### - -electrokinetics_z0 +1 -electrokinetics_z1 -1 -electrokinetics_d0 0.01 -electrokinetics_d1 0.01 -electrokinetics_eunit 1.0 -electrokinetics_epsilon 3.3e3 -electrokinetics_init from_file - -############################################################################### -# -# Miscellaneous -# -############################################################################### - -random_seed 8361235 diff --git a/tests/regression/d3q19-elec/serial-elec-gc2.log b/tests/regression/d3q19-elec/serial-elec-gc2.log deleted file mode 100644 index 021ff92f..00000000 --- a/tests/regression/d3q19-elec/serial-elec-gc2.log +++ /dev/null @@ -1,167 +0,0 @@ -Welcome to Ludwig v0.15.0 (Serial version running on 1 process) -Start time: Wed Jan 12 12:46:04 2022 - -Compiler: - name: Gnu 11.2.0 - version-string: 11.2.0 - -Note assertions via standard C assert() are on. - -Target thread model: OpenMP. -OpenMP threads: 1; maximum number of threads: 8. - -Read 30 user parameters from serial-elec-gc2.inp - -System details --------------- -System size: 64 4 4 -Decomposition: 1 1 1 -Local domain: 64 4 4 -Periodic: 1 1 1 -Halo nhalo: 1 -Reorder: true -Initialised: 1 - -Free energy details -------------------- - -Electrokinetics (single fluid) selected - -Parameters: -Electrokinetic species: 2 -Boltzmann factor: 3.0000000e+04 (T = 3.3333333e-05) -Unit charge: 1.0000000e+00 -Permittivity: 3.3000000e+03 -Bjerrum length: 7.2343156e-01 -Valency species 0: 1 -Diffusivity species 0: 1.0000000e-02 -Valency species 1: -1 -Diffusivity species 1: 1.0000000e-02 -Number of multisteps: 1 -Number of skipsteps: 1 -Diffusive accuracy in NPE: 0.0000000e+00 -Relative tolerance: 1.1920929e-07 -Absolute tolerance: 1.1920929e-09 -Max. no. of iterations: 10000 -I/O decomposition: 1 1 1 -I/O format: BINARY -Force calculation: phi_gradmu_correction - -System properties ----------------- -Mean fluid density: 1.00000e+00 -Shear viscosity 1.00000e-01 -Bulk viscosity 1.00000e-01 -Temperature 3.33333e-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: lb_halo_openmp_reduced (host) -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 - -Porous media ------------- -Porous media file stub: capillary -Porous media file data items: 0 -Porous media format (serial): binary -Porous media io grid: 1 1 1 -[User ] Random number seed: 8361235 - -Hydrodynamics -------------- -Hydrodynamics: on - -Advection scheme order: 3 - -Initial charge densities ------------------------- -Initialisation requested from file psi-00000000.001-001 - -Porous Media ------------- -Wall boundary links allocated: 160 -Memory (total, bytes): 2560 - -Arranging initial charge neutrality. - -Initial conditions. - -Scalars - total mean variance min max -[rho] 992.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[psi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[rho] 1.9920000e+00 1.0000000e-03 3.1250000e-02 -[rho] 1.9920000e+00 0.0000000e+00 2.0080645e-03 -[elc] -2.3203661e-14 -1.0080645e-03 3.1250000e-02 - -Free energy density - timestep total fluid -[fed] 0 -2.6048547231e-02 -2.2387073046e-02 - -Momentum - x y z -[total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[walls ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 - -Starting time step loop. -1 multisteps - -Scalars - total mean variance min max -[rho] 992.00 1.00000000000 1.5210055e-14 0.99999998294 1.00000011145 -[psi] 1.1990409e-14 -1.5210888e+00 2.9128003e+00 -[rho] 1.9920000e+00 8.2093661e-04 3.1250000e-02 -[rho] 1.9920000e+00 0.0000000e+00 2.4098347e-03 -[elc] -1.4988011e-14 -1.5888981e-03 3.1250000e-02 - -Free energy density - timestep total fluid -[fed] 100 -2.4613216884e-02 -2.2373587017e-02 - -Momentum - x y z -[total ] -6.6613381e-16 3.1225023e-17 0.0000000e+00 -[fluid ] -1.4854784e-13 3.1225023e-17 0.0000000e+00 -[walls ] 1.4788171e-13 0.0000000e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -4.7355583e-08 -6.9388940e-18 0.0000000e+00 -[maximum ] 4.7355582e-08 6.9388940e-18 1.1754944e-38 -[vol flux] -1.4543921e-13 -2.2204458e-16 0.0000000e+00 - -Completed cycle 100 - -Timer resolution: 1e-06 second - -Timer statistics - Section: tmin tmax total - Total: 2.751 2.751 2.751 2.750747 (1 call) - Time step loop: 0.027 0.029 2.746 0.027462 (100 calls) - Propagation: 0.001 0.001 0.061 0.000605 (100 calls) - Propagtn (krnl) : 0.001 0.001 0.060 0.000604 (100 calls) - Collision: 0.001 0.001 0.120 0.001200 (100 calls) - Collision (krnl) : 0.001 0.001 0.120 0.001198 (100 calls) - Lattice halos: 0.000 0.001 0.066 0.000165 (400 calls) - phi gradients: 0.000 0.000 0.000 0.000000 (100 calls) - BBL: 0.000 0.000 0.002 0.000024 (100 calls) - Force calculation: 0.000 0.000 0.013 0.000067 (200 calls) - phi update: 0.000 0.000 0.000 0.000000 (100 calls) - Poisson equation: 0.023 0.025 2.338 0.023377 (100 calls) - Nernst Planck: 0.001 0.002 0.137 0.001366 (100 calls) - Free1: 0.000 0.001 0.001 0.000009 (100 calls) -End time: Wed Jan 12 12:46:06 2022 -Ludwig finished normally. diff --git a/tests/regression/d3q19-elec/serial-elec-rr1.inp b/tests/regression/d3q19-elec/serial-elec-rr1.inp index 91b55d2d..46c53fb4 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr1.inp +++ b/tests/regression/d3q19-elec/serial-elec-rr1.inp @@ -64,9 +64,12 @@ config_at_end no default_io_grid 1_1_1 colloid_io_freq 1000 +psi_io_mode mpiio +psi_io_report no + ############################################################################### # -# Electrokinetics ALWAYS 2 SPECIES FOR NOW +# Electrokinetics # ############################################################################### diff --git a/tests/regression/d3q19-elec/serial-elec-rr1.log b/tests/regression/d3q19-elec/serial-elec-rr1.log index 2feb3bb9..35b77ae2 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr1.log +++ b/tests/regression/d3q19-elec/serial-elec-rr1.log @@ -30,14 +30,11 @@ Valency species 0: 1 Diffusivity species 0: 1.0000000e-02 Valency species 1: -1 Diffusivity species 1: 1.0000000e-02 -Number of multisteps: 1 -Number of skipsteps: 1 -Diffusive accuracy in NPE: 0.0000000e+00 Relative tolerance: 1.1920929e-07 Absolute tolerance: 1.1920929e-09 Max. no. of iterations: 10000 -I/O decomposition: 1 1 1 -I/O format: BINARY +Number of multisteps: 1 +Diffusive accuracy in NPE: 0.0000000e+00 Force calculation: phi_gradmu_correction System properties diff --git a/tests/regression/d3q19-elec/serial-elec-rr2.inp b/tests/regression/d3q19-elec/serial-elec-rr2.inp index 8b2a7d6d..9a93ec2c 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr2.inp +++ b/tests/regression/d3q19-elec/serial-elec-rr2.inp @@ -64,9 +64,12 @@ config_at_end no default_io_grid 1_1_1 colloid_io_freq 1000 +psi_io_mode mpiio +psi_io_report no + ############################################################################### # -# Electrokinetics ALWAYS 2 SPECIES FOR NOW +# Electrokinetics # ############################################################################### diff --git a/tests/regression/d3q19-elec/serial-elec-rr2.log b/tests/regression/d3q19-elec/serial-elec-rr2.log index 700dedf1..eaf1f68b 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr2.log +++ b/tests/regression/d3q19-elec/serial-elec-rr2.log @@ -30,14 +30,11 @@ Valency species 0: 1 Diffusivity species 0: 1.0000000e-02 Valency species 1: -1 Diffusivity species 1: 1.0000000e-02 -Number of multisteps: 1 -Number of skipsteps: 1 -Diffusive accuracy in NPE: 0.0000000e+00 Relative tolerance: 1.1920929e-07 Absolute tolerance: 1.1920929e-09 Max. no. of iterations: 10000 -I/O decomposition: 1 1 1 -I/O format: BINARY +Number of multisteps: 1 +Diffusive accuracy in NPE: 0.0000000e+00 Force calculation: phi_gradmu_correction System properties diff --git a/tests/regression/d3q19-elec/serial-elec-rr3.inp b/tests/regression/d3q19-elec/serial-elec-rr3.inp index 0afd0c03..1babe87b 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr3.inp +++ b/tests/regression/d3q19-elec/serial-elec-rr3.inp @@ -62,9 +62,12 @@ config_at_end no default_io_grid 1_1_1 colloid_io_freq 1000 +psi_io_mode mpiio +psi_io_report no + ############################################################################### # -# Electrokinetics ALWAYS 2 SPECIES FOR NOW +# Electrokinetics # ############################################################################### diff --git a/tests/regression/d3q19-elec/serial-elec-rr3.log b/tests/regression/d3q19-elec/serial-elec-rr3.log index 97307eb7..7371036c 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr3.log +++ b/tests/regression/d3q19-elec/serial-elec-rr3.log @@ -30,14 +30,11 @@ Valency species 0: 1 Diffusivity species 0: 1.0000000e-02 Valency species 1: -1 Diffusivity species 1: 1.0000000e-02 -Number of multisteps: 1 -Number of skipsteps: 1 -Diffusive accuracy in NPE: 0.0000000e+00 Relative tolerance: 1.1920929e-07 Absolute tolerance: 1.1920929e-09 Max. no. of iterations: 10000 -I/O decomposition: 1 1 1 -I/O format: BINARY +Number of multisteps: 1 +Diffusive accuracy in NPE: 0.0000000e+00 Force calculation: phi_gradmu_correction System properties diff --git a/tests/regression/d3q19-elec/serial-elec-rr4.inp b/tests/regression/d3q19-elec/serial-elec-rr4.inp index 94776dfb..cd3917f5 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr4.inp +++ b/tests/regression/d3q19-elec/serial-elec-rr4.inp @@ -63,9 +63,12 @@ config_at_end no default_io_grid 1_1_1 colloid_io_freq 1000 +psi_io_mode mpiio +psi_io_report no + ############################################################################### # -# Electrokinetics ALWAYS 2 SPECIES FOR NOW +# Electrokinetics # ############################################################################### diff --git a/tests/regression/d3q19-elec/serial-elec-rr4.log b/tests/regression/d3q19-elec/serial-elec-rr4.log index 27707130..dc69fd73 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr4.log +++ b/tests/regression/d3q19-elec/serial-elec-rr4.log @@ -30,14 +30,11 @@ Valency species 0: 1 Diffusivity species 0: 1.0000000e-02 Valency species 1: -1 Diffusivity species 1: 1.0000000e-02 -Number of multisteps: 1 -Number of skipsteps: 1 -Diffusive accuracy in NPE: 0.0000000e+00 Relative tolerance: 1.1920929e-07 Absolute tolerance: 1.1920929e-09 Max. no. of iterations: 10000 -I/O decomposition: 1 1 1 -I/O format: BINARY +Number of multisteps: 1 +Diffusive accuracy in NPE: 0.0000000e+00 Force calculation: phi_gradmu_correction System properties diff --git a/tests/regression/d3q19-elec/serial-rest-ec1.inp b/tests/regression/d3q19-elec/serial-rest-ec1.inp index 8658d52e..952f83cd 100644 --- a/tests/regression/d3q19-elec/serial-rest-ec1.inp +++ b/tests/regression/d3q19-elec/serial-rest-ec1.inp @@ -76,6 +76,11 @@ electrokinetics_epsilon 3.3e3 electrokinetics_init uniform electrokinetics_init_rho_el 0.001 +# Now mandatory to use mpiio for electrokinetics + +psi_io_mode mpiio +psi_io_report no + ############################################################################### # # Miscellaneous diff --git a/tests/regression/d3q19-elec/serial-rest-ec1.log b/tests/regression/d3q19-elec/serial-rest-ec1.log index a533f0c3..679e88e1 100644 --- a/tests/regression/d3q19-elec/serial-rest-ec1.log +++ b/tests/regression/d3q19-elec/serial-rest-ec1.log @@ -30,14 +30,11 @@ Valency species 0: 1 Diffusivity species 0: 1.0000000e-02 Valency species 1: -1 Diffusivity species 1: 1.0000000e-02 -Number of multisteps: 1 -Number of skipsteps: 1 -Diffusive accuracy in NPE: 0.0000000e+00 Relative tolerance: 1.1920929e-07 Absolute tolerance: 1.1920929e-09 Max. no. of iterations: 10000 -I/O decomposition: 1 1 1 -I/O format: BINARY +Number of multisteps: 1 +Diffusive accuracy in NPE: 0.0000000e+00 Force calculation: phi_gradmu_correction System properties diff --git a/tests/regression/d3q19-elec/serial-rest-ec2.inp b/tests/regression/d3q19-elec/serial-rest-ec2.inp index b2949427..4d0c8ecc 100644 --- a/tests/regression/d3q19-elec/serial-rest-ec2.inp +++ b/tests/regression/d3q19-elec/serial-rest-ec2.inp @@ -63,6 +63,9 @@ electrokinetics_epsilon 3.3e3 electrokinetics_init uniform electrokinetics_init_rho_el 0.001 +psi_io_mode mpiio +psi_io_report no + ############################################################################### # # Miscellaneous diff --git a/tests/regression/d3q19-elec/serial-rest-ec2.log b/tests/regression/d3q19-elec/serial-rest-ec2.log index 151dbdcd..a758c117 100644 --- a/tests/regression/d3q19-elec/serial-rest-ec2.log +++ b/tests/regression/d3q19-elec/serial-rest-ec2.log @@ -30,14 +30,11 @@ Valency species 0: 1 Diffusivity species 0: 1.0000000e-02 Valency species 1: -1 Diffusivity species 1: 1.0000000e-02 -Number of multisteps: 1 -Number of skipsteps: 1 -Diffusive accuracy in NPE: 0.0000000e+00 Relative tolerance: 1.1920929e-07 Absolute tolerance: 1.1920929e-09 Max. no. of iterations: 10000 -I/O decomposition: 1 1 1 -I/O format: BINARY +Number of multisteps: 1 +Diffusive accuracy in NPE: 0.0000000e+00 Force calculation: phi_gradmu_correction System properties @@ -109,7 +106,7 @@ Final cell lengths: 2.9090909e+00 2.9090909e+00 2.9090909e+00 Re-starting simulation at step 20 with data read from file Reading distribution files for step 20 Reading rho/vel files for step 20 -electrokinetics files(s) psi-00000020 +Reading electrokinetics files for step 20 Initial conditions. Scalars - total mean variance min max diff --git a/tests/unit/test_fe_electro.c b/tests/unit/test_fe_electro.c index 2158c519..38953286 100644 --- a/tests/unit/test_fe_electro.c +++ b/tests/unit/test_fe_electro.c @@ -7,7 +7,7 @@ * Edinburgh Soft Matter and Statistical Phsyics Group and * Edinburgh Parallel Computing Centre * - * (c) 2014-2017 The University of Edinburgh + * (c) 2014-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -79,11 +79,9 @@ int test_fe_electro_suite(void) { static int do_test1(pe_t * pe, cs_t * cs, physics_t * phys) { - int nk = 2; - double valency[2] = {1, 2}; - double kt = 2.0; - double eunit = 3.0; psi_t * psi = NULL; + double kt = 2.0; + int valency[2] = {1, 2}; int index = 1; double rho0, rho1; /* Test charge densities */ @@ -96,8 +94,18 @@ static int do_test1(pe_t * pe, cs_t * cs, physics_t * phys) { assert(cs); assert(phys); - psi_create(pe, cs, nk, &psi); - psi_unit_charge_set(psi, eunit); + { + int nhalo = 0; + cs_nhalo(cs, &nhalo); + { + psi_options_t opts = psi_options_default(nhalo); + opts.e = 3.0; + opts.beta = 1.0/kt; + opts.valency[0] = valency[0]; + opts.valency[1] = valency[1]; + psi_create(pe, cs, &opts, &psi); + } + } physics_kt_set(phys, kt); @@ -139,13 +147,11 @@ static int do_test1(pe_t * pe, cs_t * cs, physics_t * phys) { fed1 += rho1*0.5*valency[1]*psi0; psi_psi_set(psi, index, psi0); - psi_valency_set(psi, 0, valency[0]); - psi_valency_set(psi, 1, valency[1]); fe_electro_fed(fe, index, &fed); test_assert(fabs(fed - (fed0 + fed1)) < DBL_EPSILON); fe_electro_free(fe); - psi_free(psi); + psi_free(&psi); return 0; } @@ -160,12 +166,10 @@ static int do_test1(pe_t * pe, cs_t * cs, physics_t * phys) { int do_test2(pe_t * pe, cs_t * cs, physics_t * phys) { - int n; - int nk = 3; - double kt = 0.1; - double eunit = 1.0; - double valency[3] = {3, 2, 1}; psi_t * psi = NULL; + double eunit = 1.0; + double kt = 0.1; + int valency[3] = {3, 2, 1}; int index = 1; double rho0; /* Test charge density */ @@ -179,18 +183,30 @@ int do_test2(pe_t * pe, cs_t * cs, physics_t * phys) { assert(cs); assert(phys); - psi_create(pe, cs, nk, &psi); - psi_unit_charge_set(psi, eunit); + { + int nhalo = 0; + cs_nhalo(cs, &nhalo); + { + psi_options_t opts = psi_options_default(nhalo); + opts.nk = 3; + opts.e = eunit; + opts.beta = 1.0/kt; + opts.valency[0] = valency[0]; + opts.valency[1] = valency[1]; + opts.valency[2] = valency[2]; + psi_create(pe, cs, &opts, &psi); + } + } + physics_kt_set(phys, kt); fe_electro_create(pe, psi, &fe); assert(fe); - for (n = 0; n < 3; n++) { + for (int n = 0; n < 3; n++) { rho0 = 1.0 + 1.0*n; psi_rho_set(psi, index, n, rho0); - psi_valency_set(psi, n, valency[n]); - + /* For psi = 0, have mu_a = kT log(rho_a) */ psi0 = 0.0; psi_psi_set(psi, index, psi0); @@ -207,7 +223,7 @@ int do_test2(pe_t * pe, cs_t * cs, physics_t * phys) { } fe_electro_free(fe); - psi_free(psi); + psi_free(&psi); return 0; } @@ -223,7 +239,6 @@ int do_test2(pe_t * pe, cs_t * cs, physics_t * phys) { static int do_test3(pe_t * pe, cs_t * cs, physics_t * phys) { - int nk = 2; int index; int ia, ib; psi_t * psi = NULL; @@ -245,9 +260,19 @@ static int do_test3(pe_t * pe, cs_t * cs, physics_t * phys) { assert(cs); assert(phys); - psi_create(pe, cs, nk, &psi); - psi_epsilon_set(psi, epsilon); - psi_unit_charge_set(psi, eunit); + { + int nhalo = 0; + cs_nhalo(cs, &nhalo); + { + psi_options_t opts = psi_options_default(nhalo); + opts.e = eunit; + opts.beta = 1.0/kt; + opts.epsilon1 = epsilon; + opts.epsilon2 = epsilon; + psi_create(pe, cs, &opts, &psi); + } + } + fe_electro_create(pe, psi, &fe); assert(fe); @@ -298,7 +323,7 @@ static int do_test3(pe_t * pe, cs_t * cs, physics_t * phys) { } fe_electro_free(fe); - psi_free(psi); + psi_free(&psi); return 0; } diff --git a/tests/unit/test_fe_electro_symm.c b/tests/unit/test_fe_electro_symm.c index 6fb70201..f2d28be5 100644 --- a/tests/unit/test_fe_electro_symm.c +++ b/tests/unit/test_fe_electro_symm.c @@ -7,7 +7,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2013-2017 The University of Edinburgh + * (c) 2013-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -63,6 +63,7 @@ int test_fe_electro_symm_suite(void) { static int do_test1(pe_t * pe) { int nk = 2; + int nhalo = 2; int index = 1; double dmu[2] = {1.0, 2.0}; /* Solvation free energy differences */ double dmu_test; @@ -85,11 +86,15 @@ static int do_test1(pe_t * pe) { assert(pe); cs_create(pe, &cs); - cs_nhalo_set(cs, 2); + cs_nhalo_set(cs, nhalo); cs_init(cs); - psi_create(pe, cs, nk, &psi); - assert(psi); + { + psi_options_t opts = psi_options_default(nhalo); + opts.epsilon1 = epsilon1; + opts.epsilon2 = epsilon2; + psi_create(pe, cs, &opts, &psi); + } fe_electro_create(pe, psi, &fe_elec); { @@ -125,8 +130,6 @@ static int do_test1(pe_t * pe) { /* Check epsilon e = ebar [ 1 - gamma phi ] */ - fe_es_epsilon_set(fe, epsilon1, epsilon2); - phi0 = 0.0; field_scalar_set(phi, index, phi0); fe_es_var_epsilon(fe, index, &eps_test); @@ -144,7 +147,7 @@ static int do_test1(pe_t * pe) { fe_es_free(fe); field_grad_free(dphi); field_free(phi); - psi_free(psi); + psi_free(&psi); cs_free(cs); return 0; diff --git a/tests/unit/test_nernst_planck.c b/tests/unit/test_nernst_planck.c index acbf3528..ad790ff1 100644 --- a/tests/unit/test_nernst_planck.c +++ b/tests/unit/test_nernst_planck.c @@ -7,7 +7,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2012-2022 The University of Edinburgh + * (c) 2012-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -26,7 +26,6 @@ #include "control.h" #include "map.h" #include "psi.h" -#include "psi_s.h" #include "psi_sor.h" #include "psi_stats.h" #include "fe_electro.h" @@ -90,8 +89,8 @@ int test_nernst_planck_suite(void) { static int test_nernst_planck_driver(pe_t * pe) { + int nhalo = 1; int ntotal[3] = {64, 4, 4}; /* Quasi-one-dimensional system */ - int nk = 2; /* Number of species */ int nlocal[3]; int noffst[3]; @@ -103,12 +102,6 @@ static int test_nernst_planck_driver(pe_t * pe) { double rho_i; /* Interior charge density */ double rho_b, rho_b_local; /* background ionic strength */ - int valency[2] = {+1, -1}; - double diffusivity[2] = {1.e-2, 1.e-2}; - - double eunit = 1.; /* Unit charge, ... */ - double epsilon = 3.3e3; /* ... epsilon, and ... */ - double beta = 3.0e4; /* ... the Boltzmann factor i.e., t ~ 10^5 */ double rho_el = 1.0e-3; /* charge density */ double ltot[3]; @@ -119,12 +112,17 @@ static int test_nernst_planck_driver(pe_t * pe) { physics_t * phys = NULL; fe_electro_t * fe = NULL; + double epsilon = 3.3e3; /* ... epsilon, and ... */ + double beta = 3.0e4; /* ... the Boltzmann factor i.e., t ~ 10^5 */ + + psi_options_t opts = psi_options_default(nhalo); + assert(pe); physics_create(pe, &phys); cs_create(pe, &cs); - cs_nhalo_set(cs, 1); + cs_nhalo_set(cs, nhalo); cs_ntotal_set(cs, ntotal); { @@ -144,19 +142,12 @@ static int test_nernst_planck_driver(pe_t * pe) { map_create(pe, cs, 0, &map); assert(map); - psi_create(pe, cs, nk, &psi); - assert(psi); - - psi_valency_set(psi, 0, valency[0]); - psi_valency_set(psi, 1, valency[1]); - psi_diffusivity_set(psi, 0, diffusivity[0]); - psi_diffusivity_set(psi, 1, diffusivity[1]); - psi_unit_charge_set(psi, eunit); - psi_epsilon_set(psi, epsilon); - psi_beta_set(psi, beta); + opts.beta = beta; + opts.epsilon1 = epsilon; + opts.epsilon2 = epsilon; + psi_create(pe, cs, &opts, &psi); /* Care. the free energy gets the temperatue from global physics_t. */ - physics_kt_set(phys, 1.0/beta); fe_electro_create(pe, psi, &fe); /* wall charge density */ @@ -253,8 +244,8 @@ static int test_nernst_planck_driver(pe_t * pe) { double ldebye = 0.0; /* Debye length */ double yd = 0.0; /* Dimensionless surface potential */ - psi_bjerrum_length(psi, &lb); - psi_debye_length(psi, rho_b, &ldebye); + psi_bjerrum_length1(&opts, &lb); + psi_debye_length1(&opts, rho_b, &ldebye); psi_surface_potential(psi, rho_w, rho_b, &yd); /* Only the surface potential has really changed compared with the @@ -267,7 +258,7 @@ static int test_nernst_planck_driver(pe_t * pe) { map_free(map); fe_electro_free(fe); - psi_free(psi); + psi_free(&psi); cs_free(cs); physics_free(phys); diff --git a/tests/unit/test_psi.c b/tests/unit/test_psi.c index caaf8694..05f95354 100644 --- a/tests/unit/test_psi.c +++ b/tests/unit/test_psi.c @@ -7,7 +7,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2012-2017 The University of Edinburgh + * (c) 2012-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -18,23 +18,15 @@ #include #include -#include "pe.h" -#include "coords.h" -#include "util.h" #include "psi.h" -#include "psi_s.h" -#include "test_coords_field.h" -#include "tests.h" - -static int testf2(cs_t * cs, int ic, int jc, int kc, int n, void * ref); -static int do_test1(pe_t * pe); -static int do_test2(pe_t * pe); -static int do_test_halo1(pe_t * pe); -static int do_test_halo2(pe_t * pe); -static int do_test_bjerrum(pe_t * pe); -static int do_test_ionic_strength(pe_t * pe); -static int do_test_io1(pe_t * pe); +int test_psi_initialise(pe_t * pe); +int test_psi_create(pe_t * pe); +int test_psi_psi_set(pe_t * pe); +int test_psi_rho_set(pe_t * pe); +int test_psi_halo_psi(pe_t * pe); +int test_psi_halo_rho(pe_t * pe); +int test_psi_ionic_strength(pe_t * pe); /***************************************************************************** * @@ -48,13 +40,13 @@ int test_psi_suite(void) { pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); - do_test1(pe); - do_test2(pe); - do_test_halo1(pe); - do_test_halo2(pe); - do_test_io1(pe); - do_test_bjerrum(pe); - do_test_ionic_strength(pe); + test_psi_initialise(pe); + test_psi_create(pe); + test_psi_psi_set(pe); + test_psi_rho_set(pe); + test_psi_halo_psi(pe); + test_psi_halo_rho(pe); + test_psi_ionic_strength(pe); pe_info(pe, "PASS ./unit/test_psi\n"); pe_free(pe); @@ -64,51 +56,75 @@ int test_psi_suite(void) { /***************************************************************************** * - * do_test1 - * - * Test object creation/deletion, and the various access functions. + * test_psi_initialise * *****************************************************************************/ -static int do_test1(pe_t * pe) { - - int nk; - int iv, n; - int valency[3] = {1, 2, 3}; - double e, diff; - double diffusivity[3] = {1.0, 2.0, 3.0}; - double eunit = -1.0; +int test_psi_initialise(pe_t * pe) { - cs_t * cs; - psi_t * psi; - - assert(pe); + int nhalo = 1; + cs_t * cs = NULL; cs_create(pe, &cs); + cs_nhalo_set(cs, nhalo); cs_init(cs); - nk = 3; - psi_create(pe, cs, nk, &psi); - assert(psi); - psi_nk(psi, &n); - test_assert(n == 3); - - for (n = 0; n < nk; n++) { - psi_valency_set(psi, n, valency[n]); - psi_valency(psi, n, &iv); - test_assert(iv == valency[n]); - psi_diffusivity_set(psi, n, diffusivity[n]); - psi_diffusivity(psi, n, &diff); - test_assert(fabs(diff - diffusivity[n]) < DBL_EPSILON); + { + /* With some non-default values ... */ + int nsites = 0; + psi_options_t opts = psi_options_default(nhalo); + psi_t psi = {0}; + + opts.e = 2.0; + opts.beta = 0.5; + opts.epsilon1 = 0.2; + opts.epsilon2 = 0.3; + opts.e0[X] = 4.0; + opts.e0[Y] = 5.0; + opts.e0[Z] = 6.0; + + psi_initialise(pe, cs, &opts, &psi); + + cs_nsites(cs, &nsites); + + /* Check existing structure */ + assert(psi.pe == pe); + assert(psi.cs == cs); + assert(psi.nk == opts.nk); + assert(psi.nsites == nsites); + assert(psi.psi != NULL); + assert(psi.rho != NULL); + + assert(fabs(psi.diffusivity[0] - opts.diffusivity[0]) < DBL_EPSILON); + assert(fabs(psi.diffusivity[1] - opts.diffusivity[1]) < DBL_EPSILON); + assert(psi.valency[0] == opts.valency[0]); + assert(psi.valency[1] == opts.valency[1]); + + /* Physical quantities */ + assert(fabs(psi.e - opts.e) < DBL_EPSILON); + assert(fabs(psi.epsilon - opts.epsilon1) < DBL_EPSILON); + assert(fabs(psi.epsilon2 - opts.epsilon2) < DBL_EPSILON); + assert(fabs(psi.beta - opts.beta) < DBL_EPSILON); + assert(fabs(psi.e0[X] - opts.e0[X]) < DBL_EPSILON); + assert(fabs(psi.e0[Y] - opts.e0[Y]) < DBL_EPSILON); + assert(fabs(psi.e0[Z] - opts.e0[Z]) < DBL_EPSILON); + + /* Poission */ + assert(psi.nfreq == opts.nfreq); + assert(psi.maxits == opts.maxits); + assert(fabs(psi.reltol - opts.reltol) < DBL_EPSILON); + assert(fabs(psi.abstol - opts.abstol) < DBL_EPSILON); + + /* Nernst Planck */ + assert(psi.multisteps == opts.nsmallstep); + assert(fabs(psi.diffacc - opts.diffacc) < DBL_EPSILON); + + /* Other */ + assert(psi.method == opts.method); + + psi_finalise(&psi); } - psi_unit_charge(psi, &e); - test_assert(fabs(e - 1.0) < DBL_EPSILON); /* Default unit = 1.0 */ - psi_unit_charge_set(psi, eunit); - psi_unit_charge(psi, &e); - test_assert(fabs(eunit - e) < DBL_EPSILON); - - psi_free(psi); cs_free(cs); return 0; @@ -116,62 +132,32 @@ static int do_test1(pe_t * pe) { /***************************************************************************** * - * do_test2 - * - * Check access to the lattice-based quantities. + * test_psi_create * *****************************************************************************/ -static int do_test2(pe_t * pe) { - - int nk = 2; - int iv, n; - int index; - int valency[2] = {1, 2}; - double diff; - double ref, value; - double diffusivity[2] = {1.0, 2.0}; +int test_psi_create(pe_t * pe) { + int nhalo = 2; cs_t * cs = NULL; - psi_t * psi = NULL; - - assert(pe); cs_create(pe, &cs); + cs_nhalo_set(cs, nhalo); cs_init(cs); - psi_create(pe, cs, nk, &psi); - assert(psi); - psi_nk(psi, &n); - test_assert(n == 2); - - for (n = 0; n < nk; n++) { - psi_valency_set(psi, n, valency[n]); - psi_valency(psi, n, &iv); - test_assert(iv == valency[n]); - psi_diffusivity_set(psi, n, diffusivity[n]); - psi_diffusivity(psi, n, &diff); - test_assert(fabs(diff - diffusivity[n]) < DBL_EPSILON); - } + { + psi_options_t opts = psi_options_default(nhalo); + psi_t * psi = NULL; - index = 1; - ref = 1.0; - psi_psi_set(psi, index, ref); - psi_psi(psi, index, &value); - test_assert(fabs(value - ref) < DBL_EPSILON); + psi_create(pe, cs, &opts, &psi); + assert(psi); + assert(psi->psi); + assert(psi->rho); - for (n = 0; n < nk; n++) { - ref = 1.0 + n; - psi_rho_set(psi, index, n, ref); - psi_rho(psi, index, n, &value); - test_assert(fabs(value - ref) < DBL_EPSILON); + psi_free(&psi); + assert(psi == NULL); } - ref = 1.0 + 4.0; - psi_rho_elec(psi, index, &value); - test_assert(fabs(value - ref) < DBL_EPSILON); - - psi_free(psi); cs_free(cs); return 0; @@ -179,43 +165,37 @@ static int do_test2(pe_t * pe) { /***************************************************************************** * - * do_test_halo1 - * - * Take the default system size with nhalo = 2 and nk = 3, and - * check the halo swap. + * test_psi_psi_set * *****************************************************************************/ -static int do_test_halo1(pe_t * pe) { +int test_psi_psi_set(pe_t * pe) { - int nk; int nhalo = 2; - cs_t * cs = NULL; + int ntotal[3] = {64, 4, 4}; + psi_options_t opts = psi_options_default(nhalo); psi_t * psi = NULL; - - assert(pe); + cs_t * cs = NULL; cs_create(pe, &cs); cs_nhalo_set(cs, nhalo); + cs_ntotal_set(cs, ntotal); cs_init(cs); - nk = 3; - psi_create(pe, cs, nk, &psi); - assert(psi); - - test_coords_field_set(cs, 1, psi->psi, MPI_DOUBLE, test_ref_double1); - psi_halo_psi(psi); - test_coords_field_check(cs, nhalo, 1, psi->psi, MPI_DOUBLE, test_ref_double1); - - test_coords_field_set(cs, nk, psi->rho, MPI_DOUBLE, test_ref_double1); - psi_halo_rho(psi); - test_coords_field_check(cs, nhalo, nk, psi->rho, MPI_DOUBLE, test_ref_double1); - - test_coords_field_set(cs, nk, psi->rho, MPI_DOUBLE, testf2); - psi_halo_rho(psi); - test_coords_field_check(cs, nhalo, nk, psi->rho, MPI_DOUBLE, testf2); + psi_create(pe, cs, &opts, &psi); + assert(psi->psi->data); + + { + /* potential */ + int index = 1; + double psi0 = 2.0; + double value = 0.0; + psi_psi_set(psi, index, psi0); + psi_psi(psi, index, &value); + assert(fabs(value - psi0) < DBL_EPSILON); + } - psi_free(psi); + psi_free(&psi); cs_free(cs); return 0; @@ -223,47 +203,37 @@ static int do_test_halo1(pe_t * pe) { /***************************************************************************** * - * do_test_halo2 - * - * Check the halo swap in a one-dimensional decomposition. + * psi_rho_set * *****************************************************************************/ -static int do_test_halo2(pe_t * pe) { +int test_psi_rho_set(pe_t * pe) { - int nk; - int grid[3]; - int nhalo = 3; + int nhalo = 2; + int ntotal[3] = {64, 4, 4}; + psi_options_t opts = psi_options_default(nhalo); + psi_t * psi = NULL; cs_t * cs = NULL; - psi_t * psi; - - assert(pe); - - /* Use a 1-d decomposition, which increases the number of - * MPI tasks in one direction cf. the default. */ - - grid[0] = 1; - grid[1] = pe_mpi_size(pe); - grid[2] = 1; cs_create(pe, &cs); - cs_decomposition_set(cs, grid); cs_nhalo_set(cs, nhalo); + cs_ntotal_set(cs, ntotal); cs_init(cs); - nk = 2; - psi_create(pe, cs, nk, &psi); - assert(psi); - - test_coords_field_set(cs, 1, psi->psi, MPI_DOUBLE, test_ref_double1); - psi_halo_psi(psi); - test_coords_field_check(cs, nhalo, 1, psi->psi, MPI_DOUBLE, test_ref_double1); + psi_create(pe, cs, &opts, &psi); + assert(psi->rho->data); - test_coords_field_set(cs, nk, psi->rho, MPI_DOUBLE, test_ref_double1); - psi_halo_rho(psi); - test_coords_field_check(cs, nhalo, nk, psi->rho, MPI_DOUBLE, test_ref_double1); + /* Charge densities */ + for (int n = 0; n < psi->nk; n++) { + int index = 1 + n; + double rho0 = 6.0 + 1.0*n; + double value = 0.0; + psi_rho_set(psi, index, n, rho0); + psi_rho(psi, index, n, &value); + assert(fabs(value - rho0) < DBL_EPSILON); + } - psi_free(psi); + psi_free(&psi); cs_free(cs); return 0; @@ -271,208 +241,184 @@ static int do_test_halo2(pe_t * pe) { /***************************************************************************** * - * do_test_io1 + * test_psi_halo_psi * - * Note that the io functions must use the psi_ object at the moment. - * Take default (i.e., binary) write, and specify explicitly binary - * read. - * *****************************************************************************/ -static int do_test_io1(pe_t * pe) { +int test_psi_halo_psi(pe_t * pe) { - int nk; - int grid[3] = {1, 1, 1}; - const char * filename = "psi-test-io"; - - cs_t * cs = NULL; + int ifail = 0; + int nhalo = 2; + int ntotal[3] = {16, 16, 16}; + psi_options_t opts = psi_options_default(nhalo); psi_t * psi = NULL; - io_info_t * iohandler = NULL; - MPI_Comm comm; - - assert(pe); + cs_t * cs = NULL; cs_create(pe, &cs); + cs_nhalo_set(cs, nhalo); + cs_ntotal_set(cs, ntotal); cs_init(cs); - cs_cart_comm(cs, &comm); - if (pe_mpi_size(pe) == 8) { - grid[X] = 2; - grid[Y] = 2; - grid[Z] = 2; + psi_create(pe, cs, &opts, &psi); + + /* Provide uniform values ... */ + { + int nlocal[3] = {0}; + double psi0 = 2.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++) { + int index = cs_index(cs, ic, jc, kc); + psi_psi_set(psi, index, psi0); + } + } + } } - nk = 2; - psi_create(pe, cs, nk, &psi); - assert(psi); - psi_init_io_info(psi, grid, IO_FORMAT_DEFAULT, IO_FORMAT_DEFAULT); - - test_coords_field_set(cs, 1, psi->psi, MPI_DOUBLE, test_ref_double1); - test_coords_field_set(cs, nk, psi->rho, MPI_DOUBLE, test_ref_double1); - - psi_io_info(psi, &iohandler); - assert(iohandler); - io_write_data(iohandler, filename, psi); - - psi_free(psi); - MPI_Barrier(comm); - - /* Recreate, and read. This zeros out all the fields, so they - * must be read correctly to pass. */ - - psi_create(pe, cs, nk, &psi); - psi_init_io_info(psi, grid, IO_FORMAT_BINARY, IO_FORMAT_BINARY); - - psi_io_info(psi, &iohandler); - assert(iohandler); - io_read_data(iohandler, filename, psi); - psi_halo_psi(psi); - psi_halo_rho(psi); - /* Zero halo region required */ - test_coords_field_check(cs, 0, 1, psi->psi, MPI_DOUBLE, test_ref_double1); - test_coords_field_check(cs, 0, nk, psi->rho, MPI_DOUBLE, test_ref_double1); - - MPI_Barrier(comm); - io_remove(filename, iohandler); - io_remove_metadata(iohandler, "psi"); + /* Check ... */ + { + int nlocal[3] = {0}; + double psi0 = 2.0; + cs_nlocal(cs, nlocal); + + for (int ic = 1 - nhalo; ic <= nlocal[X] + nhalo; ic++) { + for (int jc = 1 - nhalo; jc <= nlocal[Y] + nhalo; jc++) { + for (int kc = 1 - nhalo; kc <= nlocal[Z] + nhalo; kc++) { + int index = cs_index(cs, ic, jc, kc); + double value = 0.0; + psi_psi(psi, index, &value); + assert(fabs(value - psi0) < DBL_EPSILON); + if (fabs(value - psi0) > DBL_EPSILON) ifail += 1; + } + } + } + } - psi_free(psi); + psi_free(&psi); cs_free(cs); - return 0; + return ifail; } /***************************************************************************** * - * do_test_bjerrum - * - * Test the bjerrum length comes out right. - * l_B = e^2 / 4\pi epsilon KT - * - * We set out unit charge to be 1 in lattice units, and a plausable - * lattice Boltzmann temperature of 10^-05; the units of permeativity - * are still somewhat open to investigation... - * - * At the moment I have the famous 41.4 which is the dielectric - * *isotropy* used for blue phases scaled by an arbitrary 1000. - * - * Also test the Debye length for unit ionic strength while we - * are at it. + * test_psi_halo_rho * *****************************************************************************/ -static int do_test_bjerrum(pe_t * pe) { +int test_psi_halo_rho(pe_t * pe) { + int ifail = 0; + int nhalo = 1; + int ntotal[3] = {16, 16, 16}; + psi_options_t opts = psi_options_default(nhalo); psi_t * psi = NULL; - double eref = 1.0; - double epsilonref = 41.4*1000.0; - double ktref = 0.00001; - double tmp, lbref, ldebyeref; - cs_t * cs = NULL; - PI_DOUBLE(pi_); - - assert(pe); cs_create(pe, &cs); + cs_nhalo_set(cs, nhalo); + cs_ntotal_set(cs, ntotal); cs_init(cs); - psi_create(pe, cs, 2, &psi); - - psi_beta_set(psi, 1.0/ktref); - psi_beta(psi, &tmp); - test_assert(fabs(1.0/ktref - tmp) < DBL_EPSILON); - - psi_epsilon_set(psi, epsilonref); - psi_epsilon(psi, &tmp); - test_assert(fabs(tmp - epsilonref) < DBL_EPSILON); - psi_unit_charge_set(psi, eref); - psi_unit_charge(psi, &tmp); - test_assert(fabs(tmp - eref) < DBL_EPSILON); + psi_create(pe, cs, &opts, &psi); + + /* Provide uniform values per chareged species ... */ + { + 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++) { + int index = cs_index(cs, ic, jc, kc); + for (int n = 0; n < psi->nk; n++) { + double rho0 = 6.0 + 1.0*n; + psi_rho_set(psi, index, n, rho0); + } + } + } + } + } - lbref = eref*eref / (4.0*pi_*epsilonref*ktref); - psi_bjerrum_length(psi, &tmp); - test_assert(fabs(lbref - tmp) < DBL_EPSILON); + psi_halo_rho(psi); - /* For unit ionic strength */ - ldebyeref = 1.0 / sqrt(8.0*pi_*lbref); - psi_debye_length(psi, 1.0, &tmp); - test_assert(fabs(ldebyeref - tmp) < DBL_EPSILON); + /* Check ... */ + { + int nlocal[3] = {0}; + cs_nlocal(cs, nlocal); + + for (int ic = 1 - nhalo; ic <= nlocal[X] + nhalo; ic++) { + for (int jc = 1 - nhalo; jc <= nlocal[Y] + nhalo; jc++) { + for (int kc = 1 - nhalo; kc <= nlocal[Z] + nhalo; kc++) { + int index = cs_index(cs, ic, jc, kc); + for (int n = 0; n < psi->nk; n++) { + double rho0 = 6.0 + 1.0*n; + double value = 0.0; + psi_rho(psi, index, n, &value); + assert(fabs(value - rho0) < DBL_EPSILON); + if (fabs(value - rho0) > DBL_EPSILON) ifail += 1; + } + } + } + } + } - psi_free(psi); + psi_free(&psi); cs_free(cs); - return 0; + return ifail; } /***************************************************************************** * - * do_test_ionic_strength - * - * Test the calculation of the ionic strength. We require just - * valency and charge density for a given number of species. + * test_psi_ionic_strength * *****************************************************************************/ -static int do_test_ionic_strength(pe_t * pe) { - - int n, nk = 2; +int test_psi_ionic_strength(pe_t * pe) { + + int nhalo = 1; + int ntotal[3] = {8, 8, 8}; + psi_options_t opts = psi_options_default(nhalo); psi_t * psi = NULL; + cs_t * cs = NULL; - int index = 1; /* Lattice point will be present */ + /* We require a valency and a charge density ... */ int valency[2] = {+2, -2}; - double rho[2] = {1.0, 2.0}; - double rhoi; - double expect; - cs_t * cs = NULL; + double rho0[2] = {3.0, 5.0}; - assert(pe); + opts.valency[0] = valency[0]; + opts.valency[1] = valency[1]; cs_create(pe, &cs); + cs_nhalo_set(cs, nhalo); + cs_ntotal_set(cs, ntotal); cs_init(cs); - psi_create(pe, cs, nk, &psi); - for (n = 0; n < nk; n++) { - psi_valency_set(psi, n, valency[n]); - psi_rho_set(psi, index, n, rho[n]); + psi_create(pe, cs, &opts, &psi); + + /* Set the charge density, and compute an ionic strength */ + { + int index = 2; + int strength = 0.0; + for (int n = 0; n < psi->nk; n++) { + psi_rho_set(psi, index, n, rho0[n]); + strength += 0.5*valency[n]*valency[n]*rho0[n]; + } + + { + double value = 0.0; + psi_ionic_strength(psi, index, &value); + assert(fabs(value - strength) < DBL_EPSILON); + } } - - expect = 0.5*(pow(valency[0], 2)*rho[0] + pow(valency[1], 2)*rho[1]); - - psi_ionic_strength(psi, index, &rhoi); - test_assert(fabs(expect - rhoi) < DBL_EPSILON); - - psi_free(psi); + + psi_free(&psi); cs_free(cs); return 0; } - -/***************************************************************************** - * - * testf2 - * - * With signature halo_ft from test_coords_field.h - * A 'wall' function perioidic in z-direction. - * - *****************************************************************************/ - -static int testf2(cs_t * cs, int ic, int jc, int kc, int n, void * buf) { - - int ntotal[3]; - double * ref = (double *) buf; - - assert(cs); - assert(ref); - - cs_ntotal(cs, ntotal); - - *ref = -1.0; - - if (kc == 1 || kc == 0) *ref = 1.0; - if (kc == ntotal[Z] || kc == ntotal[Z] + 1) *ref = 1.0; - - return 0; -} diff --git a/tests/unit/test_psi_options.c b/tests/unit/test_psi_options.c new file mode 100644 index 00000000..f9278a99 --- /dev/null +++ b/tests/unit/test_psi_options.c @@ -0,0 +1,218 @@ +/***************************************************************************** + * + * test_psi_options.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 +#include + +#include "pe.h" +#include "psi_options.h" + +int test_psi_options_default(void); +int test_psi_bjerrum_length(void); +int test_psi_debye_length(void); + +/***************************************************************************** + * + * test_psi_options_suite + * + *****************************************************************************/ + +int test_psi_options_suite(void) { + + pe_t * pe = NULL; + + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + /* A change in components requires a test update... */ + + assert(sizeof(psi_options_t) == 384); + assert(PSI_NKMAX >= 2); + + test_psi_options_default(); + test_psi_bjerrum_length(); + test_psi_debye_length(); + + pe_info(pe, "%-9s %s\n", "PASS", __FILE__); + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_psi_options_default + * + *****************************************************************************/ + +int test_psi_options_default(void) { + + int ifail = 0; + + psi_options_t opts = psi_options_default(0); + + assert(opts.nk == 2); + if (ifail != 2) ifail = -1; + + /* Physics */ + /* Check to nk = 2 */ + assert(fabs(opts.e - 1.0) < DBL_EPSILON); + assert(fabs(opts.beta - 1.0) < DBL_EPSILON); + assert(fabs(opts.epsilon1 - 10000.0) < DBL_EPSILON); + assert(fabs(opts.epsilon2 - 10000.0) < DBL_EPSILON); + assert(fabs(opts.e0[0] - 0.0) < DBL_EPSILON); + assert(fabs(opts.e0[1] - 0.0) < DBL_EPSILON); + assert(fabs(opts.e0[2] - 0.0) < DBL_EPSILON); + assert(fabs(opts.diffusivity[0] - 0.01) < DBL_EPSILON); + assert(fabs(opts.diffusivity[1] - 0.01) < DBL_EPSILON); + assert(fabs(opts.valency[0] - 1) < DBL_EPSILON); + assert(fabs(opts.valency[1] - -1) < DBL_EPSILON); + + /* Solver */ + assert(opts.psolver == PSI_POISSON_SOLVER_SOR); + assert(opts.maxits == 10000); + assert(opts.nfreq == INT_MAX); + assert(fabs(opts.reltol - FLT_EPSILON) < DBL_EPSILON); + assert(fabs(opts.abstol - 0.01*FLT_EPSILON) < DBL_EPSILON); + + /* Nernst Planck */ + assert(opts.nsolver == -1); + assert(opts.nsmallstep == 1); + assert(fabs(opts.diffacc - 0.0) < DBL_EPSILON); + + /* Other */ + /* FIXME: this should be replaced */ + if (opts.nsolver != -1) ifail = -1; + + return ifail; +} + +/***************************************************************************** + * + * test_psi_bjerrum_length + * + * Check both Bjerrum lengths. + * + *****************************************************************************/ + +int test_psi_bjerrum_length(void) { + + int ifail = 0; + + { + psi_options_t opts = psi_options_default(0); + double b1 = 0.0; + double b2 = 0.0; + + ifail = psi_bjerrum_length1(&opts, &b1); + assert(ifail == 0); + ifail = psi_bjerrum_length2(&opts, &b2); + assert(ifail == 0); + { + double e = opts.e; + double kt = 1.0/opts.beta; + double epsilon = opts.epsilon1; + double lbjerrum = e*e/(4.0*4.0*atan(1.0)*epsilon*kt); + + ifail = (fabs(b1 - lbjerrum) > DBL_EPSILON); + assert(ifail == 0); + ifail = (fabs(b2 - lbjerrum) > DBL_EPSILON); + assert(ifail == 0); + } + } + + { + double e = 1.0; + double ktref = 0.00001; + double epsilon1 = 41.4*1000.0; + double epsilon2 = 57.1*1000.0; + psi_options_t opts = {.e = e, .beta = 1.0/ktref, .epsilon1 = epsilon1, + .epsilon2 = epsilon2}; + double b1 = 0.0; + double b2 = 0.0; + + ifail = psi_bjerrum_length1(&opts, &b1); + assert(ifail == 0); + assert(fabs(b1 - 0.19221611) < FLT_EPSILON); + + ifail = psi_bjerrum_length2(&opts, &b2); + assert(ifail == 0); + assert(fabs(b2 - 0.13936510) < FLT_EPSILON); + } + + return ifail; +} + +/***************************************************************************** + * + * test_psi_debye_length + * + * Check both Debye lengths. + * + *****************************************************************************/ + +int test_psi_debye_length(void) { + + int ifail = 0; + + { + psi_options_t opts = psi_options_default(0); + double rho_b = 6.0; + double ldebye1 = 0.0; + double ldebye2 = 0.0; + double ldebye0 = 0.0; + + ifail = psi_debye_length1(&opts, rho_b, &ldebye1); + assert(ifail == 0); + ifail = psi_debye_length2(&opts, rho_b, &ldebye2); + assert(ifail == 0); + + { + double lbjerrum = 0.0; + + psi_bjerrum_length1(&opts, &lbjerrum); + ldebye0 = 1.0/sqrt(8.0*4.0*atan(1.0)*lbjerrum*rho_b); + ifail = (fabs(ldebye1 - ldebye0) > DBL_EPSILON); + assert(ifail == 0); + + psi_bjerrum_length2(&opts, &lbjerrum); + ldebye0 = 1.0/sqrt(8.0*4.0*atan(1.0)*lbjerrum*rho_b); + ifail = (fabs(ldebye2 - ldebye0) > DBL_EPSILON); + assert(ifail == 0); + } + } + + { + /* Some numbers from an historical example. */ + double e = 1.0; + double beta = 1.0/0.00033333; + double epsilon1 = 300.0; + double epsilon2 = 400.0; + double rho_el = 0.00047; + double ldebye1 = 0.0; + double ldebye2 = 0.0; + psi_options_t opts = {.e = e, .beta = beta, .epsilon1 = epsilon1, + .epsilon2 = epsilon2}; + + ifail = psi_debye_length1(&opts, rho_el, &ldebye1); + assert(ifail == 0); + ifail = psi_debye_length2(&opts, rho_el, &ldebye2); + assert(ifail == 0); + + assert(fabs(ldebye1 - 1.03141609e+01) < FLT_EPSILON); + assert(fabs(ldebye2 - 1.19097671e+01) < FLT_EPSILON); + } + + return ifail; +} diff --git a/tests/unit/test_psi_sor.c b/tests/unit/test_psi_sor.c index 39bc9a02..44bc6aa4 100644 --- a/tests/unit/test_psi_sor.c +++ b/tests/unit/test_psi_sor.c @@ -7,7 +7,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2012-2022 The University of Edinburgh + * (c) 2012-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -23,7 +23,6 @@ #include "pe.h" #include "coords.h" #include "control.h" -#include "psi_s.h" #include "psi_sor.h" #include "util.h" @@ -90,6 +89,7 @@ int test_psi_sor_poisson(pe_t * pe) { cs_t * cs = NULL; psi_t * psi = NULL; int ntotal[3] = {4, 4, 64}; + int nhalo = 1; assert(pe); @@ -100,19 +100,19 @@ int test_psi_sor_poisson(pe_t * pe) { int dims[3] = {0,0,1}; MPI_Dims_create(pe_mpi_size(pe), ndims, dims); - cs_nhalo_set(cs, 1); + cs_nhalo_set(cs, nhalo); cs_ntotal_set(cs, ntotal); cs_decomposition_set(cs, dims); } cs_init(cs); - psi_create(pe, cs, 2, &psi); - assert(psi); - - psi_valency_set(psi, 0, +1); - psi_valency_set(psi, 1, -1); - psi_beta_set(psi, 1.0); - psi_epsilon_set(psi, REF_PERMEATIVITY); + { + psi_options_t opts = psi_options_default(nhalo); + opts.nk = 2; + opts.beta = 1.0; + opts.epsilon1 = REF_PERMEATIVITY; + psi_create(pe, cs, &opts, &psi); + } test_charge1_set(psi); @@ -125,7 +125,7 @@ int test_psi_sor_poisson(pe_t * pe) { test_charge1_exact(psi, fepsilon_constant); - psi_free(psi); + psi_free(&psi); cs_free(cs); return 0; @@ -148,22 +148,23 @@ int test_psi_sor_vare_poisson(pe_t * pe) { cs_t * cs = NULL; psi_t * psi = NULL; int ntotal[3] = {4, 4, 64}; + int nhalo = 1; assert(pe); cs_create(pe, &cs); - cs_nhalo_set(cs, 1); + cs_nhalo_set(cs, nhalo); cs_ntotal_set(cs, ntotal); cs_init(cs); - psi_create(pe, cs, 2, &psi); - assert(psi); - - psi_valency_set(psi, 0, +1); - psi_valency_set(psi, 1, -1); - psi_beta_set(psi, 1.0); - psi_reltol_set(psi, 0.01*FLT_EPSILON); - psi_epsilon_set(psi, REF_PERMEATIVITY); + { + psi_options_t opts = psi_options_default(nhalo); + opts.nk = 2; + opts.beta = 1.0; + opts.epsilon1 = REF_PERMEATIVITY; + opts.reltol = 0.01*FLT_EPSILON; /* Not the default */ + psi_create(pe, cs, &opts, &psi); + } test_charge1_set(psi); @@ -175,7 +176,7 @@ int test_psi_sor_vare_poisson(pe_t * pe) { test_charge1_exact(psi, fepsilon_constant); - psi_free(psi); + psi_free(&psi); cs_free(cs); return 0; @@ -465,15 +466,3 @@ static int fepsilon_constant(fe_fake_t * fe, int index, double * epsilon) { return 0; } - -/***************************************************************************** - * - * fepsilon_sinz - * - * Permeativity is a function of z only: - * - * e = e0 sin(pi z / Lz) - * - * The - 0.5 is to make it symmetric about the centre line. - * - *****************************************************************************/ diff --git a/tests/unit/tests.c b/tests/unit/tests.c index 5f7befd5..e5674fc8 100644 --- a/tests/unit/tests.c +++ b/tests/unit/tests.c @@ -49,7 +49,7 @@ __host__ int tests_create() { test_pe_suite(); test_coords_suite(); test_cs_limits_suite(); - + test_kernel_suite(); test_gradient_d3q27_suite(); test_angle_cosine_suite(); @@ -112,6 +112,8 @@ __host__ int tests_create() { test_phi_bc_outflow_free_suite(); test_phi_ch_suite(); test_polar_active_suite(); + + test_psi_options_suite(); test_psi_suite(); test_psi_sor_suite(); test_nernst_planck_suite(); diff --git a/tests/unit/tests.h b/tests/unit/tests.h index 6cd5e295..86fa2bf8 100644 --- a/tests/unit/tests.h +++ b/tests/unit/tests.h @@ -96,6 +96,7 @@ int test_phi_bc_inflow_opts_suite(void); int test_phi_bc_inflow_fixed_suite(void); int test_phi_bc_outflow_opts_suite(void); int test_phi_bc_outflow_free_suite(void); +int test_psi_options_suite(void); int test_psi_suite(void); int test_psi_sor_suite(void); int test_random_suite(void); From a496f6c9b19b5addf624af3aa641859ae1f59134 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 2 Mar 2023 15:36:42 +0000 Subject: [PATCH 31/97] Remove repeated test --- tests/regression/d3q19-mpi-extra/Makefile | 22 --- .../d3q19-mpi-extra/parallel-auto-c01.inp | 179 ------------------ .../d3q19-mpi-extra/parallel-auto-c01.log | 134 ------------- .../d3q19-mpi-extra/parallel-auto-c02.inp | 179 ------------------ .../d3q19-mpi-extra/parallel-auto-c02.log | 138 -------------- 5 files changed, 652 deletions(-) delete mode 100644 tests/regression/d3q19-mpi-extra/Makefile delete mode 100644 tests/regression/d3q19-mpi-extra/parallel-auto-c01.inp delete mode 100644 tests/regression/d3q19-mpi-extra/parallel-auto-c01.log delete mode 100644 tests/regression/d3q19-mpi-extra/parallel-auto-c02.inp delete mode 100644 tests/regression/d3q19-mpi-extra/parallel-auto-c02.log diff --git a/tests/regression/d3q19-mpi-extra/Makefile b/tests/regression/d3q19-mpi-extra/Makefile deleted file mode 100644 index 4652d144..00000000 --- a/tests/regression/d3q19-mpi-extra/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -############################################################################### -# -# Makefile -# -# D3Q19 extra 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* diff --git a/tests/regression/d3q19-mpi-extra/parallel-auto-c01.inp b/tests/regression/d3q19-mpi-extra/parallel-auto-c01.inp deleted file mode 100644 index c9870e2d..00000000 --- a/tests/regression/d3q19-mpi-extra/parallel-auto-c01.inp +++ /dev/null @@ -1,179 +0,0 @@ -############################################################################## -# -# Colloid velocity autocorrelation test (no noise). -# -############################################################################## - -############################################################################## -# -# 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 40 - -############################################################################## -# -# 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). -# -# reduced_halo [yes|no] use reduced or full halos. Using reduced halos -# is *only* appropriate for fluid only problems. -# Default is no. -# -############################################################################## - -size 64_64_64 -reduced_halo no - -############################################################################## -# -# Fluid parameters -# -# 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' -# -# ghost_modes [on|off] Default is on. -# force FX_FY_FZ Uniform body force on fluid (default zero) -# -############################################################################## - -free_energy none - -viscosity 0.1 -viscosity_bulk 0.1 - -isothermal_fluctuations off -temperature 0.00002133333 - -############################################################################### -# -# Colloid parameters -# -############################################################################### - -colloid_init input_one - -colloid_one_a0 2.3 -colloid_one_ah 2.3 -colloid_one_r 64.0_64.0_32.0 -colloid_one_v 0.043478261_0.0_0.0 -colloid_one_w 0.0_0.0_0.0 -colloid_one_s 1.0_0.0_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 - -############################################################################### -# -# Periodic conditions / boundaries -# -# boundary_walls_on [yes|no] Use built-in side walls [default no] -# periodicity X_Y_Z Sets periodic boundary conditions in coordinate -# directions [default is 1_1_1]. Best to leave this -# unchanged -# boundary_speed_top For use with built-in walls -# boundary_speed_bottom For use with built-in walls -# -# 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] -# determines type of porous media data to be -# supplied -# -############################################################################### - -boundary_walls_on no -periodicity 1_1_1 -boundary_speed_bottom 0.0 -boundary_speed_top 0.0 - -############################################################################### -# -# 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_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 -# The following for particle data are under review... -# n_io_nodes Number of I/O processors for particles -# output_format [ASCII|BINARY] default output format -# input_format [ASCII|BINARY] default input format -# -# phi_format Override default format for particular quantities -# etc... (both input and output) -# -############################################################################### - -freq_statistics 40 -freq_measure 200000 -freq_config 5000000 -freq_phi 100000 -freq_vel 100000 -freq_shear_measurement 100000 -freq_shear_output 100000 -config_at_end no - -distribution_io_grid 1_1_1 - -phi_format ASCII -vel_format ASCII - -############################################################################## -# -# colloid i/o -# -# colloid_io_freq currently set to freq_measure internally -# colloid_io_grid currently set to 1_1_1 internally -# colloid_io_format_input ASCII ASCII_SERIAL BINARY BINARY_SERIAL -# colloid_io_format_output ASCII BINARY -# -# Note that the output is always parallel. A SERIAL input file must -# be a single serial file. -# -############################################################################## - -colloid_io_freq 1000 -colloids_io_grid 1_1_1 -colloid_io_format_input BINARY -colloid_io_format_output BINARY - -qs_dir_format BINARY - -############################################################################### -# -# Miscellaneous -# -# random_seed +ve integer is the random number generator seed -# -############################################################################### - -random_seed 8361235 diff --git a/tests/regression/d3q19-mpi-extra/parallel-auto-c01.log b/tests/regression/d3q19-mpi-extra/parallel-auto-c01.log deleted file mode 100644 index b1a354e3..00000000 --- a/tests/regression/d3q19-mpi-extra/parallel-auto-c01.log +++ /dev/null @@ -1,134 +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 39 user parameters from pmpi08-auto-c01.inp - -No free energy selected - -System details --------------- -System size: 64 64 64 -Decomposition: 2 2 2 -Local domain: 32 32 32 -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: binary -Output format: binary -Single file read flag: 0 - -Requested one colloid via input: -colloid_one_a0 2.3000000e+00 -colloid_one_ah 2.3000000e+00 -colloid_one_r 6.4000000e+01 6.4000000e+01 3.2000000e+01 -colloid_one_v 4.3478261e-02 0.0000000e+00 0.0000000e+00 -colloid_one_w 0.0000000e+00 0.0000000e+00 0.0000000e+00 -colloid_one_s 1.0000000e+00 0.0000000e+00 0.0000000e+00 - -Initialised 1 colloid - -Colloid cell list information ------------------------------ -Input radius maximum: 2.3000000e+00 -Final cell list: 11 11 11 -Final cell lengths: 2.9090909e+00 2.9090909e+00 2.9090909e+00 - -Initial conditions. - -Scalars - total mean variance min max -[rho] 262087.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] 2.2158700e+00 0.0000000e+00 0.0000000e+00 -[fluid ] 3.6371878e-12 0.0000000e+00 0.0000000e+00 -[colloids] 2.2158700e+00 0.0000000e+00 0.0000000e+00 - -Starting time step loop. - -Particle statistics: - -Colloid velocities - x y z -[minimum ] 2.2940581e-03 5.4917032e-18 -2.4809345e-17 -[maximum ] 2.2940581e-03 5.4917032e-18 -2.4809345e-17 - -Scalars - total mean variance min max -[rho] 262091.00 1.00000000000 6.5539134e-09 0.99967850437 1.00032051415 - -Momentum - x y z -[total ] 2.2158700e+00 -9.5225741e-13 -2.1395060e-14 -[fluid ] 2.1013425e+00 -9.5290442e-13 -2.0705659e-14 -[colloids] 1.1452756e-01 6.4701247e-16 -6.8940056e-16 - -Velocity - x y z -[minimum ] -1.7220078e-04 -4.9018114e-04 -4.9018114e-04 -[maximum ] 2.2654790e-03 4.9018114e-04 4.9018114e-04 - -Completed cycle 40 - -Timer resolution: 1e-06 second - -Timer statistics - Section: tmin tmax total - Total: 3.354 3.354 3.354 3.354108 (1 call) - Time step loop: 0.066 0.101 3.227 0.080675 (40 calls) - Propagation: 0.004 0.027 0.543 0.013564 (40 calls) - Propagtn (krnl) : 0.004 0.027 0.538 0.013446 (40 calls) - Collision: 0.009 0.052 1.410 0.035240 (40 calls) - Collision (krnl) : 0.009 0.052 1.409 0.035227 (40 calls) - Lattice halos: 0.002 0.049 0.653 0.008163 (80 calls) - phi gradients: 0.000 0.000 0.000 0.000000 (40 calls) - Forces: 0.000 0.032 0.013 0.000334 (40 calls) - Rebuild: 0.001 0.041 0.130 0.003239 (40 calls) - BBL: 0.002 0.024 0.206 0.005143 (40 calls) - Particle halos: 0.000 0.019 0.166 0.004158 (40 calls) - Force calculation: 0.000 0.000 0.000 0.000000 (40 calls) - phi update: 0.000 0.000 0.000 0.000000 (40 calls) - Free1: 0.000 0.044 0.039 0.000325 (120 calls) -Ludwig finished normally. diff --git a/tests/regression/d3q19-mpi-extra/parallel-auto-c02.inp b/tests/regression/d3q19-mpi-extra/parallel-auto-c02.inp deleted file mode 100644 index 7f28dc33..00000000 --- a/tests/regression/d3q19-mpi-extra/parallel-auto-c02.inp +++ /dev/null @@ -1,179 +0,0 @@ -############################################################################## -# -# Colloid velocity autocorrelation test (with noise). -# -############################################################################## - -############################################################################## -# -# 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 40 - -############################################################################## -# -# 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). -# -# reduced_halo [yes|no] use reduced or full halos. Using reduced halos -# is *only* appropriate for fluid only problems. -# Default is no. -# -############################################################################## - -size 64_64_64 -reduced_halo no - -############################################################################## -# -# Fluid parameters -# -# 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' -# -# ghost_modes [on|off] Default is on. -# force FX_FY_FZ Uniform body force on fluid (default zero) -# -############################################################################## - -free_energy none - -viscosity 0.1 -viscosity_bulk 0.1 - -isothermal_fluctuations on -temperature 0.00002133333 - -############################################################################### -# -# Colloid parameters -# -############################################################################### - -colloid_init input_one - -colloid_one_a0 2.3 -colloid_one_ah 2.3 -colloid_one_r 64.0_64.0_32.0 -colloid_one_v 0.043478261_0.0_0.0 -colloid_one_w 0.0_0.0_0.0 -colloid_one_s 1.0_0.0_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 - -############################################################################### -# -# Periodic conditions / boundaries -# -# boundary_walls_on [yes|no] Use built-in side walls [default no] -# periodicity X_Y_Z Sets periodic boundary conditions in coordinate -# directions [default is 1_1_1]. Best to leave this -# unchanged -# boundary_speed_top For use with built-in walls -# boundary_speed_bottom For use with built-in walls -# -# 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] -# determines type of porous media data to be -# supplied -# -############################################################################### - -boundary_walls_on no -periodicity 1_1_1 -boundary_speed_bottom 0.0 -boundary_speed_top 0.0 - -############################################################################### -# -# 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_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 -# The following for particle data are under review... -# n_io_nodes Number of I/O processors for particles -# output_format [ASCII|BINARY] default output format -# input_format [ASCII|BINARY] default input format -# -# phi_format Override default format for particular quantities -# etc... (both input and output) -# -############################################################################### - -freq_statistics 40 -freq_measure 200000 -freq_config 5000000 -freq_phi 100000 -freq_vel 100000 -freq_shear_measurement 100000 -freq_shear_output 100000 -config_at_end no - -distribution_io_grid 1_1_1 - -phi_format ASCII -vel_format ASCII - -############################################################################## -# -# colloid i/o -# -# colloid_io_freq currently set to freq_measure internally -# colloid_io_grid currently set to 1_1_1 internally -# colloid_io_format_input ASCII ASCII_SERIAL BINARY BINARY_SERIAL -# colloid_io_format_output ASCII BINARY -# -# Note that the output is always parallel. A SERIAL input file must -# be a single serial file. -# -############################################################################## - -colloid_io_freq 1000 -colloids_io_grid 1_1_1 -colloid_io_format_input BINARY -colloid_io_format_output BINARY - -qs_dir_format BINARY - -############################################################################### -# -# Miscellaneous -# -# random_seed +ve integer is the random number generator seed -# -############################################################################### - -random_seed 8361235 diff --git a/tests/regression/d3q19-mpi-extra/parallel-auto-c02.log b/tests/regression/d3q19-mpi-extra/parallel-auto-c02.log deleted file mode 100644 index b8c656a5..00000000 --- a/tests/regression/d3q19-mpi-extra/parallel-auto-c02.log +++ /dev/null @@ -1,138 +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 39 user parameters from pmpi08-auto-c02.inp - -No free energy selected - -System details --------------- -System size: 64 64 64 -Decomposition: 2 2 2 -Local domain: 32 32 32 -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: on -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: binary -Output format: binary -Single file read flag: 0 - -Requested one colloid via input: -colloid_one_a0 2.3000000e+00 -colloid_one_ah 2.3000000e+00 -colloid_one_r 6.4000000e+01 6.4000000e+01 3.2000000e+01 -colloid_one_v 4.3478261e-02 0.0000000e+00 0.0000000e+00 -colloid_one_w 0.0000000e+00 0.0000000e+00 0.0000000e+00 -colloid_one_s 1.0000000e+00 0.0000000e+00 0.0000000e+00 - -Initialised 1 colloid - -Colloid cell list information ------------------------------ -Input radius maximum: 2.3000000e+00 -Final cell list: 11 11 11 -Final cell lengths: 2.9090909e+00 2.9090909e+00 2.9090909e+00 - -Initial conditions. - -Scalars - total mean variance min max -[rho] 262087.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] 2.2158700e+00 0.0000000e+00 0.0000000e+00 -[fluid ] 3.6371878e-12 0.0000000e+00 0.0000000e+00 -[colloids] 2.2158700e+00 0.0000000e+00 0.0000000e+00 - -Starting time step loop. - -Particle statistics: - -Colloid velocities - x y z -[minimum ] 1.3675763e-03 -5.6482729e-04 5.1186310e-04 -[maximum ] 1.3675763e-03 -5.6482729e-04 5.1186310e-04 - -Scalars - total mean variance min max -[rho] 262091.00 1.00000000000 6.3519338e-05 0.96361208587 1.03533858441 - -Momentum - x y z -[total ] 2.2158700e+00 1.7273726e-14 4.0794625e-14 -[fluid ] 2.1321758e+00 1.3760202e-03 -3.8498072e-03 -[colloids] 8.3694239e-02 -1.3760202e-03 3.8498072e-03 - -Velocity - x y z -[minimum ] -2.2651159e-02 -2.0201971e-02 -2.2199155e-02 -[maximum ] 2.2262575e-02 2.0391258e-02 2.4334011e-02 - -Isothermal fluctuations -[eqipart.] 2.1335610e-05 2.1257847e-05 2.1371772e-05 -[measd/kT] 6.3965230e-05 6.3999990e-05 - -Completed cycle 40 - -Timer resolution: 1e-06 second - -Timer statistics - Section: tmin tmax total - Total: 3.801 3.801 3.801 3.800790 (1 call) - Time step loop: 0.076 0.103 3.667 0.091664 (40 calls) - Propagation: 0.004 0.025 0.612 0.015305 (40 calls) - Propagtn (krnl) : 0.004 0.025 0.612 0.015298 (40 calls) - Collision: 0.012 0.065 2.134 0.053342 (40 calls) - Collision (krnl) : 0.012 0.065 2.133 0.053331 (40 calls) - Lattice halos: 0.002 0.053 0.483 0.006032 (80 calls) - phi gradients: 0.000 0.000 0.000 0.000000 (40 calls) - Forces: 0.000 0.041 0.010 0.000244 (40 calls) - Rebuild: 0.001 0.041 0.077 0.001919 (40 calls) - BBL: 0.002 0.019 0.158 0.003953 (40 calls) - Particle halos: 0.000 0.015 0.122 0.003039 (40 calls) - Force calculation: 0.000 0.000 0.000 0.000000 (40 calls) - phi update: 0.000 0.000 0.000 0.000000 (40 calls) - Free1: 0.000 0.078 0.072 0.000598 (120 calls) -Ludwig finished normally. From 73ae3a062fde940c3c802531819669ba10fdc36b Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 3 Mar 2023 15:48:14 +0000 Subject: [PATCH 32/97] Separate solver options --- src/psi.c | 28 ++---- src/psi.h | 11 +-- src/psi_options.c | 6 +- src/psi_options.h | 16 +--- src/psi_solver_options.c | 183 +++++++++++++++++++++++++++++++++++++++ src/psi_solver_options.h | 56 ++++++++++++ 6 files changed, 254 insertions(+), 46 deletions(-) create mode 100644 src/psi_solver_options.c create mode 100644 src/psi_solver_options.h diff --git a/src/psi.c b/src/psi.c index a5b9d47d..a120153f 100644 --- a/src/psi.c +++ b/src/psi.c @@ -114,11 +114,8 @@ int psi_initialise(pe_t * pe, cs_t * cs, const psi_options_t * opts, psi->valency[n] = opts->valency[n]; } - /* Poisson solver */ - psi->maxits = opts->maxits; - psi->nfreq = opts->nfreq; - psi->reltol = opts->reltol; - psi->abstol = opts->abstol; + /* Solver options */ + psi->solver = opts->solver; /* Nernst-Planck */ psi->multisteps = opts->nsmallstep; @@ -456,7 +453,7 @@ int psi_reltol(psi_t * obj, double * reltol) { assert(obj); assert(reltol); - *reltol = obj->reltol; + *reltol = obj->solver.reltol; return 0; } @@ -474,7 +471,7 @@ int psi_abstol(psi_t * obj, double * abstol) { assert(obj); assert(abstol); - *abstol = obj->abstol; + *abstol = obj->solver.abstol; return 0; } @@ -521,7 +518,7 @@ int psi_maxits(psi_t * obj, int * maxits) { assert(obj); assert(maxits); - *maxits = obj->maxits; + *maxits = obj->solver.maxits; return 0; } @@ -837,21 +834,6 @@ int psi_force_method_set(psi_t * psi, int flag) { return 0; } -/***************************************************************************** - * - * psi_nfreq_set - * - *****************************************************************************/ - -int psi_nfreq_set(psi_t * psi, int nfreq) { - - assert(psi); - - psi->nfreq = nfreq; - - return 0; -} - /***************************************************************************** * * psi_output_step diff --git a/src/psi.h b/src/psi.h index ebaf6d82..397a05f2 100644 --- a/src/psi.h +++ b/src/psi.h @@ -52,15 +52,17 @@ struct psi_s { double epsilon; /* first and reference permittivity */ double epsilon2; /* second permittivity */ double beta; /* Boltzmann factor (1 / k_B T) */ - double reltol; /* Relative tolerance for Poisson solver */ - double abstol; /* Absolute tolerance for Poisson solver */ + int method; /* Force computation method */ - int maxits; /* Maximum number of iterations */ int multisteps; /* Number of substeps in charge dynamics */ int nfreq_io; /* Field output */ - int nfreq; /* Residual statisics output */ + double diffacc; /* Number of substeps in charge dynamics */ double e0[3]; /* External electric field */ + + /* Solver options */ + psi_solver_options_t solver; + }; @@ -98,7 +100,6 @@ int psi_surface_potential(psi_t * obj, double sigma, double rho_b, int psi_reltol(psi_t * obj, double * reltol); int psi_abstol(psi_t * obj, double * abstol); int psi_maxits(psi_t * obj, int * maxits); -int psi_nfreq_set(psi_t * psi, int nfreq); int psi_output_step(psi_t * psi, int its); int psi_multisteps(psi_t * obj, int * multisteps); diff --git a/src/psi_options.c b/src/psi_options.c index 0478a785..225ecc40 100644 --- a/src/psi_options.c +++ b/src/psi_options.c @@ -43,11 +43,7 @@ psi_options_t psi_options_default(int nhalo) { .e0 = {0.0, 0.0, 0.0}, .diffusivity = {0.01, 0.01, 0.01, 0.01}, .valency = {+1, -1, +1, -1}, - .psolver = PSI_POISSON_SOLVER_SOR, - .maxits = 10000, - .nfreq = INT_MAX, - .reltol = FLT_EPSILON, - .abstol = 0.01*FLT_EPSILON, + .solver = psi_solver_options_default(), .nsolver = -1, .nsmallstep = 1, .diffacc = 0.0, diff --git a/src/psi_options.h b/src/psi_options.h index 10f900c1..5f5aa563 100644 --- a/src/psi_options.h +++ b/src/psi_options.h @@ -17,16 +17,10 @@ #define LUDWIG_PSI_OPTIONS_H #include "field_options.h" +#include "psi_solver_options.h" #define PSI_NKMAX 4 -/* Poisson solver method */ -typedef enum psi_poisson_solver_enum_s { - PSI_POISSON_SOLVER_INVALID = 0, - PSI_POISSON_SOLVER_SOR = 1, - PSI_POISSON_SOLVER_PETSC = 2 -} psi_poisson_solver_enum_t; - /* Force computation method */ typedef enum psi_force_method_enum_s { @@ -54,12 +48,8 @@ struct psi_options_s { double diffusivity[PSI_NKMAX]; /* Per species diffusivity */ int valency[PSI_NKMAX]; /* Per species charge valency */ - /* Poisson solver (iterative) */ - int psolver; /* Poisson solver id */ - int maxits; /* Maximum iterations in solver */ - int nfreq; /* Frequency of information (iterations) */ - double reltol; /* Relative tolerance */ - double abstol; /* Absolute tolerance */ + /* Solver options */ + psi_solver_options_t solver; /* Time stepping for Nernst Planck */ int nsolver; /* Nernst Planck method */ diff --git a/src/psi_solver_options.c b/src/psi_solver_options.c new file mode 100644 index 00000000..c3a29403 --- /dev/null +++ b/src/psi_solver_options.c @@ -0,0 +1,183 @@ +/***************************************************************************** + * + * psi_solver_options.c + * + * Container for Poission solver options. + * + * + * 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 + +#include "psi_solver_options.h" +#include "util.h" + +/***************************************************************************** + * + * psi_poisson_solver_to_string + * + *****************************************************************************/ + +const char * psi_poisson_solver_to_string(psi_poisson_solver_enum_t mytype) { + + const char * str = "invalid"; + + switch (mytype) { + case PSI_POISSON_SOLVER_SOR: + str = "sor"; + break; + case PSI_POISSON_SOLVER_PETSC: + str = "petsc"; + break; + case PSI_POISSON_SOLVER_NONE: + str = "none"; + break; + default: + str = "invalid"; + } + + return str; +} + +/***************************************************************************** + * + * psi_poisson_solver_from_string + * + *****************************************************************************/ + +psi_poisson_solver_enum_t psi_poisson_solver_from_string(const char * str) { + + psi_poisson_solver_enum_t mytype = PSI_POISSON_SOLVER_INVALID; + char value[BUFSIZ] = {0}; + + strncpy(value, str, BUFSIZ-1); + util_str_tolower(value, strlen(value)); + + if (strcmp(value, "sor") == 0) mytype = PSI_POISSON_SOLVER_SOR; + if (strcmp(value, "petsc") == 0) mytype = PSI_POISSON_SOLVER_PETSC; + if (strcmp(value, "none") == 0) mytype = PSI_POISSON_SOLVER_NONE; + + return mytype; +} + +/***************************************************************************** + * + * psi_solver_options_default + * + *****************************************************************************/ + +psi_solver_options_t psi_solver_options_default(void) { + + psi_solver_options_t pso = psi_solver_options_type(PSI_POISSON_SOLVER_SOR); + + return pso; +} + +/***************************************************************************** + * + * psi_solver_options_type + * + *****************************************************************************/ + +psi_solver_options_t psi_solver_options_type(psi_poisson_solver_enum_t ptype) { + + psi_solver_options_t pso = { + .psolver = ptype, + .maxits = 10000, + .verbose = 0, + .nfreq = INT_MAX, + .nstencil = 7, + .reltol = 1.0e-08, + .abstol = 1.0e-15, + }; + + return pso; +} + +/***************************************************************************** + * + * psi_solver_options_to_json + * + * Caller to release new json object when finished. + * + *****************************************************************************/ + +int psi_solver_options_to_json(const psi_solver_options_t * pso, + cJSON ** json) { + int ifail = 0; + + if (pso == NULL || json == NULL || *json != NULL) { + ifail = -1; + } + else { + cJSON * obj = cJSON_CreateObject(); + + cJSON_AddStringToObject(obj, "Solver type", + psi_poisson_solver_to_string(pso->psolver)); + cJSON_AddNumberToObject(obj, "Maximum iterations", pso->maxits); + cJSON_AddNumberToObject(obj, "Level of verbosity", pso->verbose); + cJSON_AddNumberToObject(obj, "Frequency of output", pso->nfreq); + cJSON_AddNumberToObject(obj, "Stencil points", pso->nstencil); + + cJSON_AddNumberToObject(obj, "Relative tolerance", pso->reltol); + cJSON_AddNumberToObject(obj, "Absolute tolerance", pso->abstol); + + *json = obj; + } + + return ifail; +} + +/***************************************************************************** + * + * psi_solver_options_from_json + * + *****************************************************************************/ + +int psi_solver_options_from_json(const cJSON * json, + psi_solver_options_t * pso) { + + int ifail = 0; + + if (json == NULL || pso == NULL) { + ifail = -1; + } + else { + cJSON * psolver = cJSON_GetObjectItem(json, "Solver type"); + cJSON * maxits = cJSON_GetObjectItem(json, "Maximum iterations"); + cJSON * verbose = cJSON_GetObjectItem(json, "Level of verbosity"); + cJSON * nfreq = cJSON_GetObjectItem(json, "Frequency of output"); + cJSON * nsten = cJSON_GetObjectItem(json, "Stencil points"); + cJSON * reltol = cJSON_GetObjectItem(json, "Relative tolerance"); + cJSON * abstol = cJSON_GetObjectItem(json, "Absolute tolerance"); + + /* There must be at least a solver type ... */ + + if (psolver == NULL) { + pso->psolver = PSI_POISSON_SOLVER_INVALID; + ifail = -1; + } + else { + char * str = cJSON_GetStringValue(psolver); + pso->psolver = psi_poisson_solver_from_string(str); + + if (maxits) pso->maxits = cJSON_GetNumberValue(maxits); + if (verbose) pso->verbose = cJSON_GetNumberValue(verbose); + if (nfreq) pso->nfreq = cJSON_GetNumberValue(nfreq); + if (nsten) pso->nstencil = cJSON_GetNumberValue(nsten); + if (reltol) pso->reltol = cJSON_GetNumberValue(reltol); + if (abstol) pso->abstol = cJSON_GetNumberValue(abstol); + } + } + + return ifail; +} diff --git a/src/psi_solver_options.h b/src/psi_solver_options.h new file mode 100644 index 00000000..ea68750e --- /dev/null +++ b/src/psi_solver_options.h @@ -0,0 +1,56 @@ +/***************************************************************************** + * + * psi_solver_options.h + * + * Poisson solver options. + * + * 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) + * + *****************************************************************************/ + +#ifndef LUDWIG_PSI_SOLVER_OPTIONS_H +#define LUDWIG_PSI_SOLVER_OPTIONS_H + +#include "util_json.h" + +/* Poisson solver method */ + +typedef enum psi_poisson_solver_enum_s { + PSI_POISSON_SOLVER_INVALID = 0, + PSI_POISSON_SOLVER_SOR = 1, + PSI_POISSON_SOLVER_PETSC = 2, + PSI_POISSON_SOLVER_NONE = 3 +} psi_poisson_solver_enum_t; + +/* This is intended to be general; some components might not be relevant + in all specific cases. */ + +typedef struct psi_solver_options_s psi_solver_options_t; + +struct psi_solver_options_s { + + psi_poisson_solver_enum_t psolver; /* Poisson solver id */ + int maxits; /* Maximum iterations in solver */ + int verbose; /* Level of verbosity */ + int nfreq; /* Frequency of report */ + int nstencil; /* Stencil option */ + + double reltol; /* Relative tolerance */ + double abstol; /* Absolute tolerance */ +}; + +const char * psi_poisson_solver_to_string(psi_poisson_solver_enum_t mytype); +psi_poisson_solver_enum_t psi_poisson_solver_from_string(const char * str); + +psi_solver_options_t psi_solver_options_default(void); +psi_solver_options_t psi_solver_options_type(psi_poisson_solver_enum_t mytype); + +int psi_solver_options_to_json(const psi_solver_options_t * opts, cJSON ** js); +int psi_solver_options_from_json(const cJSON * json, psi_solver_options_t * p); + +#endif From 972540879ecd0f4ffe6c6e75f7b93646db9a328f Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 3 Mar 2023 15:48:57 +0000 Subject: [PATCH 33/97] Add extra Debye length information --- src/psi_rt.c | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/psi_rt.c b/src/psi_rt.c index 26f89634..3bcf977f 100644 --- a/src/psi_rt.c +++ b/src/psi_rt.c @@ -70,19 +70,30 @@ int psi_rt_init_rho(pe_t * pe, rt_t * rt, psi_t * obj, map_t * map) { rt_string_parameter(rt, "electrokinetics_init", value, BUFSIZ); if (strcmp(value, "gouy_chapman") == 0) { + double ld_actual = 0.0; pe_info(pe, "Initial conditions: %s\n", "Gouy Chapman"); n = rt_double_parameter(rt, "electrokinetics_init_rho_el", &rho_el); if (n == 0) pe_fatal(pe, "... please set electrokinetics_init_rho_el\n"); - pe_info(pe, "Initial condition rho_el: %14.7e\n", rho_el); - psi_debye_length1(&opts, rho_el, &ld); - pe_info(pe, "Debye length: %14.7e\n", ld); n = rt_double_parameter(rt, "electrokinetics_init_sigma", &sigma); if (n == 0) pe_fatal(pe, "... please set electrokinetics_init_sigma\n"); - pe_info(pe, "Initial condition sigma: %14.7e\n", sigma); + psi_debye_length1(&opts, rho_el, &ld); psi_init_gouy_chapman(obj, map, rho_el, sigma); + + { + /* We want a real Debye length from the actual charge/countercharge + densities that have been initialisated in the fluid. */ + int index = cs_index(obj->cs, 2, 1, 1); + double rho_actual = 0.0; + psi_ionic_strength(obj, index, &rho_actual); + psi_debye_length1(&opts, rho_actual, &ld_actual); + } + pe_info(pe, "Initial condition rho_el: %14.7e\n", rho_el); + pe_info(pe, "Debye length: %14.7e\n", ld); + pe_info(pe, "Debye length (actual): %14.7e\n", ld_actual); + pe_info(pe, "Initial condition sigma: %14.7e\n", sigma); } if (strcmp(value, "liquid_junction") == 0) { @@ -207,12 +218,12 @@ int psi_options_rt(pe_t * pe, cs_t * cs, rt_t * rt, psi_options_t * popts) { /* Poisson solver */ /* There are two possible sources of nfreq */ - rt_int_parameter(rt, "electrokinetics_maxits", &opts.maxits); - rt_int_parameter(rt, "freq_statistics", &opts.nfreq); - rt_int_parameter(rt, "freq_psi_resid", &opts.nfreq); + rt_int_parameter(rt, "electrokinetics_maxits", &opts.solver.maxits); + rt_int_parameter(rt, "freq_statistics", &opts.solver.nfreq); + rt_int_parameter(rt, "freq_psi_resid", &opts.solver.nfreq); - rt_double_parameter(rt, "electrokinetics_rel_tol", &opts.reltol); - rt_double_parameter(rt, "electrokinetics_abs_tol", &opts.abstol); + rt_double_parameter(rt, "electrokinetics_rel_tol", &opts.solver.reltol); + rt_double_parameter(rt, "electrokinetics_abs_tol", &opts.solver.abstol); /* NPE time splitting and criteria */ @@ -276,9 +287,10 @@ int psi_info(pe_t * pe, const psi_t * psi) { pe_info(pe, "Diffusivity species %d: %14.7e\n", n, psi->diffusivity[n]); } - pe_info(pe, "Relative tolerance: %20.7e\n", psi->reltol); - pe_info(pe, "Absolute tolerance: %20.7e\n", psi->abstol); - pe_info(pe, "Max. no. of iterations: %16d\n", psi->maxits); + /* Add full information ... */ + pe_info(pe, "Relative tolerance: %20.7e\n", psi->solver.reltol); + pe_info(pe, "Absolute tolerance: %20.7e\n", psi->solver.abstol); + pe_info(pe, "Max. no. of iterations: %16d\n", psi->solver.maxits); pe_info(pe, "Number of multisteps: %d\n", psi->multisteps); pe_info(pe, "Diffusive accuracy in NPE: %14.7e\n", psi->diffacc); From e80d1f96d0bb80867b1dac2e3112dd39391107b4 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 3 Mar 2023 15:49:56 +0000 Subject: [PATCH 34/97] Repair for PETSc 3.18.x --- src/psi_petsc.c | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/src/psi_petsc.c b/src/psi_petsc.c index a98b6558..e310b7c1 100644 --- a/src/psi_petsc.c +++ b/src/psi_petsc.c @@ -36,13 +36,8 @@ #include "pe.h" #include "coords.h" #include "control.h" -#include "physics.h" -#include "psi_s.h" #include "psi.h" -#include "psi_sor.h" #include "psi_gradients.h" -#include "map.h" -#include "util.h" #include "psi_petsc.h" #include "petscksp.h" #include "petscdmda.h" @@ -108,20 +103,22 @@ int psi_petsc_init(psi_t * obj, fe_t * fe, f_vare_t fepsilon){ /* Create 3D distributed array */ cs_nhalo(obj->cs, &nhalo); - DMDACreate3d(PETSC_COMM_WORLD, \ - DM_BOUNDARY_PERIODIC, DM_BOUNDARY_PERIODIC, DM_BOUNDARY_PERIODIC, \ - DMDA_STENCIL_BOX, ntotal[X], ntotal[Y], ntotal[Z], \ - mpi_cartsz[X], mpi_cartsz[Y], mpi_cartsz[Z], 1, nhalo, \ + DMDACreate3d(PETSC_COMM_WORLD, + DM_BOUNDARY_PERIODIC, DM_BOUNDARY_PERIODIC, DM_BOUNDARY_PERIODIC, + DMDA_STENCIL_BOX, ntotal[X], ntotal[Y], ntotal[Z], + mpi_cartsz[X], mpi_cartsz[Y], mpi_cartsz[Z], 1, nhalo, NULL, NULL, NULL, &da); + PetscCall(DMSetVecType(da, VECSTANDARD)); + PetscCall(DMSetMatType(da, MATMPIAIJ)); + PetscCall(DMSetUp(da)); + /* Create global vectors on DM */ DMCreateGlobalVector(da,&x); VecDuplicate(x,&b); /* Create matrix on DM pre-allocated according to distributed array structure */ DMCreateMatrix(da,&A); - DMSetMatType(da,MATMPIAIJ); - DMSetMatrixPreallocateOnly(da,PETSC_TRUE); /* Initialise solver context and preconditioner */ @@ -613,7 +610,7 @@ int psi_petsc_copy_psi_to_da(psi_t * obj) { ic = i - noffset[X] + 1; index = cs_index(obj->cs, ic,jc,kc); - psi_3d[k][j][i] = obj->psi[index]; + psi_3d[k][j][i] = obj->psi->data[index]; } } @@ -670,7 +667,7 @@ int psi_petsc_copy_da_to_psi(psi_t * obj) { ic = i - noffset[X] + 1; index = cs_index(obj->cs, ic,jc,kc); - obj->psi[index] = psi_3d[k][j][i]; + obj->psi->data[index] = psi_3d[k][j][i]; } } @@ -706,8 +703,6 @@ int psi_petsc_set_rhs(psi_t * obj) { double eunit, beta; double epsilon, e0[3]; - physics_t * phys = NULL; - assert(obj); cs_cartsz(obj->cs, mpi_cartsz); @@ -741,10 +736,11 @@ int psi_petsc_set_rhs(psi_t * obj) { } } - /* Modify right hand side for external electric field */ - physics_ref(&phys); - physics_e0(phys, e0); + /* Modify right hand side for external electric field */ + e0[X] = obj->e0[X]; + e0[Y] = obj->e0[Y]; + e0[Z] = obj->e0[Z]; if (e0[X] || e0[Y] || e0[Z]) { cs_ntotal(obj->cs, ntotal); @@ -855,8 +851,6 @@ int psi_petsc_set_rhs_vare(psi_t * obj, fe_es_t * fe, f_vare_t fepsilon) { double eunit, beta; double eps, e0[3]; - physics_t * phys = NULL; - assert(obj); assert(fe); assert(fepsilon); @@ -893,8 +887,10 @@ int psi_petsc_set_rhs_vare(psi_t * obj, fe_es_t * fe, f_vare_t fepsilon) { } /* Modify right hand side for external electric field */ - physics_ref(&phys); - physics_e0(phys, e0); + + e0[X] = obj->e0[X]; + e0[Y] = obj->e0[Y]; + e0[Z] = obj->e0[Z]; if (e0[X] || e0[Y] || e0[Z]) { From c05c75bb0fe9abc8dbe6bdd4916fd5ff11b72fbb Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 3 Mar 2023 15:50:19 +0000 Subject: [PATCH 35/97] Change residual computation --- src/psi_sor.c | 114 ++++++++++++++++++-------------------------------- 1 file changed, 41 insertions(+), 73 deletions(-) diff --git a/src/psi_sor.c b/src/psi_sor.c index db27a590..e7308217 100644 --- a/src/psi_sor.c +++ b/src/psi_sor.c @@ -157,23 +157,17 @@ int psi_sor_poisson(psi_t * obj, int its) { psi_rho_elec(obj, index, &rho_elec); - /* 6-point stencil of Laplacian */ - - dpsi - = psidata[addr_rank0(nsites, index + xs)] - + psidata[addr_rank0(nsites, index - xs)] - + psidata[addr_rank0(nsites, index + ys)] - + psidata[addr_rank0(nsites, index - ys)] - + psidata[addr_rank0(nsites, index + zs)] - + psidata[addr_rank0(nsites, index - zs)] - - 6.0*psidata[addr_rank0(nsites, index)]; - /* Non-dimensional potential in Poisson eqn requires e/kT */ - rnorm_local[0] += fabs(epsilon*dpsi + eunit*beta*rho_elec); + /* This is just the L2 norm of the right hand side. */ + + residual = eunit*beta*rho_elec; + rnorm_local[0] += residual*residual; } } } + rnorm_local[0] = sqrt(rnorm_local[0]); + /* Iterate to solution */ omega = 1.0; @@ -207,10 +201,11 @@ int psi_sor_poisson(psi_t * obj, int its) { - 6.0*psidata[addr_rank0(nsites, index)]; /* Non-dimensional potential in Poisson eqn requires e/kT */ + residual = epsilon*dpsi + eunit*beta*rho_elec; psidata[addr_rank0(nsites, index)] -= omega*residual / (-6.0*epsilon); - rnorm_local[1] += fabs(residual); + rnorm_local[1] += residual*residual; } } } @@ -232,28 +227,31 @@ int psi_sor_poisson(psi_t * obj, int its) { } if ((n % ncheck) == 0) { + /* Compare residual and exit if small enough */ + rnorm_local[1] = sqrt(rnorm_local[1]); + MPI_Allreduce(rnorm_local, rnorm, 2, MPI_DOUBLE, MPI_SUM, comm); if (rnorm[1] < tol_abs) { - if (its % obj->nfreq == 0) { + if (its % obj->solver.nfreq == 0) { pe_info(obj->pe, "\n"); pe_info(obj->pe, "SOR solver converged to absolute tolerance\n"); - pe_info(obj->pe, "SOR residual per site %14.7e at %d iterations\n", - rnorm[1]/(ltot[X]*ltot[Y]*ltot[Z]), n); + pe_info(obj->pe, "SOR residual %14.7e at %d iterations\n", rnorm[1], + n); } break; } - if (rnorm[1] < tol_abs || rnorm[1] < tol_rel*rnorm[0]) { + if (rnorm[1] < tol_rel*rnorm[0]) { - if (its % obj->nfreq == 0) { + if (its % obj->solver.nfreq == 0) { pe_info(obj->pe, "\n"); pe_info(obj->pe, "SOR solver converged to relative tolerance\n"); - pe_info(obj->pe, "SOR residual per site %14.7e at %d iterations\n", - rnorm[1]/(ltot[X]*ltot[Y]*ltot[Z]), n); + pe_info(obj->pe, "SOR residual %14.7e at %d iterations\n", rnorm[1], + n); } break; } @@ -344,61 +342,26 @@ int psi_sor_vare_poisson(psi_t * obj, fe_es_t * fe, f_vare_t fepsilon, int its) psi_beta(obj, &beta); psi_unit_charge(obj, &eunit); + + /* Compute the initial norm of the right hand side. */ + rnorm_local[0] = 0.0; for (ic = 1; ic <= nlocal[X]; ic++) { for (jc = 1; jc <= nlocal[Y]; jc++) { for (kc = 1; kc <= nlocal[Z]; kc++) { - depsi = 0.0; - index = cs_index(obj->cs, ic, jc, kc); - psi_rho_elec(obj, index, &rho_elec); - fepsilon(fe, index, &eps0); - /* Laplacian part of operator */ - - depsi += eps0*(-6.0*psidata[addr_rank0(nsites, index)] - + psidata[addr_rank0(nsites, index + xs)] - + psidata[addr_rank0(nsites, index - xs)] - + psidata[addr_rank0(nsites, index + ys)] - + psidata[addr_rank0(nsites, index - ys)] - + psidata[addr_rank0(nsites, index + zs)] - + psidata[addr_rank0(nsites, index - zs)]); - - /* Additional terms in generalised Poisson equation */ - - fepsilon(fe, index + xs, &eps1); - depsi += 0.25*eps1*(psidata[addr_rank0(nsites, index + xs)] - - psidata[addr_rank0(nsites, index - xs)]); - - fepsilon(fe, index - xs, &eps1); - depsi -= 0.25*eps1*(psidata[addr_rank0(nsites, index + xs)] - - psidata[addr_rank0(nsites, index - xs)]); - - fepsilon(fe, index + ys, &eps1); - depsi += 0.25*eps1*(psidata[addr_rank0(nsites, index + ys)] - - psidata[addr_rank0(nsites, index - ys)]); - - fepsilon(fe, index - ys, &eps1); - depsi -= 0.25*eps1*(psidata[addr_rank0(nsites, index + ys)] - - psidata[addr_rank0(nsites, index - ys)]); - - fepsilon(fe, index + zs, &eps1); - depsi += 0.25*eps1*(psidata[addr_rank0(nsites, index + zs)] - - psidata[addr_rank0(nsites, index - zs)]); - - fepsilon(fe, index - zs, &eps1); - depsi -= 0.25*eps1*(psidata[addr_rank0(nsites, index + zs)] - - psidata[addr_rank0(nsites, index - zs)]); - - /* Non-dimensional potential in Poisson eqn requires e/kT */ - rnorm_local[0] += fabs(depsi + eunit*beta*rho_elec); + residual = eunit*beta*rho_elec; + rnorm_local[0] += residual*residual; } } } + rnorm_local[0] = sqrt(rnorm_local[0]); + /* Iterate to solution */ omega = 1.0; @@ -462,7 +425,7 @@ int psi_sor_vare_poisson(psi_t * obj, fe_es_t * fe, f_vare_t fepsilon, int its) /* Non-dimensional potential in Poisson eqn requires e/kT */ residual = depsi + eunit*beta*rho_elec; psidata[addr_rank0(nsites,index)] -= omega*residual / (-6.0*eps0); - rnorm_local[1] += fabs(residual); + rnorm_local[1] += residual*residual; } } } @@ -480,34 +443,39 @@ int psi_sor_vare_poisson(psi_t * obj, fe_es_t * fe, f_vare_t fepsilon, int its) /* Compare residual and exit if small enough */ + rnorm_local[1] = sqrt(rnorm_local[1]); MPI_Allreduce(rnorm_local, rnorm, 2, MPI_DOUBLE, MPI_SUM, comm); if (rnorm[1] < tol_abs) { - if (its % obj->nfreq == 0) { + if (its % obj->solver.nfreq == 0) { pe_info(obj->pe, "\n"); - pe_info(obj->pe, "SOR (heterogeneous) solver converged to absolute tolerance\n"); - pe_info(obj->pe, "SOR residual per site %14.7e at %d iterations\n", - rnorm[1]/(ltot[X]*ltot[Y]*ltot[Z]), n); + pe_info(obj->pe, "SOR (heterogeneous) solver converged to " + "absolute tolerance\n"); + pe_info(obj->pe, "SOR residual %14.7e at %d iterations\n", + rnorm[1], n); } break; } if (rnorm[1] < tol_rel*rnorm[0]) { - if (its % obj->nfreq == 0) { + if (its % obj->solver.nfreq == 0) { pe_info(obj->pe, "\n"); - pe_info(obj->pe, "SOR (heterogeneous) solver converged to relative tolerance\n"); - pe_info(obj->pe, "SOR residual per site %14.7e at %d iterations\n", - rnorm[1]/(ltot[X]*ltot[Y]*ltot[Z]), n); + pe_info(obj->pe, "SOR (heterogeneous) solver converged to " + "relative tolerance\n"); + pe_info(obj->pe, "SOR residual %14.7e at %d iterations\n", + rnorm[1], n); } break; } if (n == niteration-1) { pe_info(obj->pe, "\n"); - pe_info(obj->pe, "SOR solver (heterogeneous) exceeded %d iterations\n", n+1); - pe_info(obj->pe, "SOR residual %le (initial) %le (final)\n\n", rnorm[0], rnorm[1]); + pe_info(obj->pe, "SOR solver (heterogeneous) exceeded %d iterations\n", + n+1); + pe_info(obj->pe, "SOR residual %le (initial) %le (final)\n\n", + rnorm[0], rnorm[1]); } } } From b964c40cd3d8cac9b099abcd5acf6a3e109978ba Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 3 Mar 2023 15:51:54 +0000 Subject: [PATCH 36/97] Use separate test tolerance --- tests/unit/test_psi_sor.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_psi_sor.c b/tests/unit/test_psi_sor.c index 44bc6aa4..5b313fb2 100644 --- a/tests/unit/test_psi_sor.c +++ b/tests/unit/test_psi_sor.c @@ -162,7 +162,7 @@ int test_psi_sor_vare_poisson(pe_t * pe) { opts.nk = 2; opts.beta = 1.0; opts.epsilon1 = REF_PERMEATIVITY; - opts.reltol = 0.01*FLT_EPSILON; /* Not the default */ + opts.solver.reltol = 0.01*FLT_EPSILON; /* Not the default */ psi_create(pe, cs, &opts, &psi); } @@ -402,7 +402,7 @@ static int test_charge1_exact(psi_t * obj, f_vare_t fepsilon) { /* Check the Gauss Jordan answer b[] against the answer from psi_t */ - psi_abstol(obj, &tolerance); + tolerance = FLT_EPSILON; rhotot = 0.0; psi0 = 0.0; From bad4ae72508c86d0a0cd053cc0033edf3938a93a Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 3 Mar 2023 15:52:39 +0000 Subject: [PATCH 37/97] Separate Poisson solver options --- tests/unit/test_psi.c | 8 +- tests/unit/test_psi_options.c | 17 +- tests/unit/test_psi_solver_options.c | 262 +++++++++++++++++++++++++++ tests/unit/tests.c | 1 + tests/unit/tests.h | 1 + 5 files changed, 274 insertions(+), 15 deletions(-) create mode 100644 tests/unit/test_psi_solver_options.c diff --git a/tests/unit/test_psi.c b/tests/unit/test_psi.c index 05f95354..97cca1b2 100644 --- a/tests/unit/test_psi.c +++ b/tests/unit/test_psi.c @@ -109,11 +109,9 @@ int test_psi_initialise(pe_t * pe) { assert(fabs(psi.e0[Y] - opts.e0[Y]) < DBL_EPSILON); assert(fabs(psi.e0[Z] - opts.e0[Z]) < DBL_EPSILON); - /* Poission */ - assert(psi.nfreq == opts.nfreq); - assert(psi.maxits == opts.maxits); - assert(fabs(psi.reltol - opts.reltol) < DBL_EPSILON); - assert(fabs(psi.abstol - opts.abstol) < DBL_EPSILON); + /* Solver options */ + /* Assume correctly covered in solver options tests ... */ + assert(psi.solver.psolver == PSI_POISSON_SOLVER_SOR); /* Nernst Planck */ assert(psi.multisteps == opts.nsmallstep); diff --git a/tests/unit/test_psi_options.c b/tests/unit/test_psi_options.c index f9278a99..54b40002 100644 --- a/tests/unit/test_psi_options.c +++ b/tests/unit/test_psi_options.c @@ -37,7 +37,8 @@ int test_psi_options_suite(void) { /* A change in components requires a test update... */ - assert(sizeof(psi_options_t) == 384); + printf("sizeof(psi_options_t): %ld\n", sizeof(psi_options_t)); + assert(sizeof(psi_options_t) == 392); assert(PSI_NKMAX >= 2); test_psi_options_default(); @@ -63,10 +64,9 @@ int test_psi_options_default(void) { psi_options_t opts = psi_options_default(0); assert(opts.nk == 2); - if (ifail != 2) ifail = -1; + if (opts.nk != 2) ifail = -1; /* Physics */ - /* Check to nk = 2 */ assert(fabs(opts.e - 1.0) < DBL_EPSILON); assert(fabs(opts.beta - 1.0) < DBL_EPSILON); assert(fabs(opts.epsilon1 - 10000.0) < DBL_EPSILON); @@ -76,15 +76,12 @@ int test_psi_options_default(void) { assert(fabs(opts.e0[2] - 0.0) < DBL_EPSILON); assert(fabs(opts.diffusivity[0] - 0.01) < DBL_EPSILON); assert(fabs(opts.diffusivity[1] - 0.01) < DBL_EPSILON); - assert(fabs(opts.valency[0] - 1) < DBL_EPSILON); - assert(fabs(opts.valency[1] - -1) < DBL_EPSILON); + + assert(opts.valency[0] == +1); + assert(opts.valency[1] == -1); /* Solver */ - assert(opts.psolver == PSI_POISSON_SOLVER_SOR); - assert(opts.maxits == 10000); - assert(opts.nfreq == INT_MAX); - assert(fabs(opts.reltol - FLT_EPSILON) < DBL_EPSILON); - assert(fabs(opts.abstol - 0.01*FLT_EPSILON) < DBL_EPSILON); + assert(opts.solver.psolver == PSI_POISSON_SOLVER_SOR); /* Nernst Planck */ assert(opts.nsolver == -1); diff --git a/tests/unit/test_psi_solver_options.c b/tests/unit/test_psi_solver_options.c new file mode 100644 index 00000000..b4c2ebab --- /dev/null +++ b/tests/unit/test_psi_solver_options.c @@ -0,0 +1,262 @@ +/***************************************************************************** + * + * test_psi_solver_options.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 +#include + +#include +#include + +#include "pe.h" +#include "psi_solver_options.h" + +int test_psi_poisson_solver_to_string(void); +int test_psi_poisson_solver_from_string(void); +int test_psi_solver_options_default(void); +int test_psi_solver_options_type(void); +int test_psi_solver_options_to_json(void); +int test_psi_solver_options_from_json(void); + +/***************************************************************************** + * + * test_psi_solver_options_suite + * + *****************************************************************************/ + +int test_psi_solver_options_suite(void) { + + pe_t * pe = NULL; + + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + /* Change in size means change in tests required ... */ + assert(sizeof(psi_solver_options_t) == 40); + + test_psi_poisson_solver_to_string(); + test_psi_poisson_solver_from_string(); + test_psi_solver_options_default(); + test_psi_solver_options_type(); + test_psi_solver_options_to_json(); + test_psi_solver_options_from_json(); + + pe_info(pe, "%-9s %s\n", "PASS", __FILE__); + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_psi_poisson_solver_to_string + * + *****************************************************************************/ + +int test_psi_poisson_solver_to_string(void) { + + int ifail = 0; + + { + const char * str = psi_poisson_solver_to_string(PSI_POISSON_SOLVER_SOR); + ifail += strcmp(str, "sor"); + assert(ifail == 0); + } + + { + const char * str = psi_poisson_solver_to_string(PSI_POISSON_SOLVER_PETSC); + ifail += strcmp(str, "petsc"); + assert(ifail == 0); + } + + { + const char * str = psi_poisson_solver_to_string(PSI_POISSON_SOLVER_NONE); + ifail += strcmp(str, "none"); + assert(ifail == 0); + } + + { + const char * str = psi_poisson_solver_to_string(PSI_POISSON_SOLVER_INVALID); + ifail += strcmp(str, "invalid"); + assert(ifail == 0); + } + + return ifail; +} + +/***************************************************************************** + * + * test_psi_poisson_solver_from_string + * + *****************************************************************************/ + +int test_psi_poisson_solver_from_string(void) { + + int ifail = 0; + + { + psi_poisson_solver_enum_t ps = psi_poisson_solver_from_string("SOR"); + if (ps != PSI_POISSON_SOLVER_SOR) ifail += 1; + assert(ifail == 0); + } + + { + psi_poisson_solver_enum_t ps = psi_poisson_solver_from_string("PETSC"); + if (ps != PSI_POISSON_SOLVER_PETSC) ifail += 1; + assert(ifail == 0); + } + + { + psi_poisson_solver_enum_t ps = psi_poisson_solver_from_string("NONE"); + if (ps != PSI_POISSON_SOLVER_NONE) ifail += 1; + } + + { + /* Sample rubbish */ + psi_poisson_solver_enum_t ps = psi_poisson_solver_from_string("RUBBISH"); + if (ps != PSI_POISSON_SOLVER_INVALID) ifail += 1; + assert(ifail == 0); + } + + return ifail; +} + +/***************************************************************************** + * + * test_psi_solver_options_default + * + *****************************************************************************/ + +int test_psi_solver_options_default(void) { + + int ifail = 0; + psi_solver_options_t pso = psi_solver_options_default(); + + assert(pso.psolver == PSI_POISSON_SOLVER_SOR); + assert(pso.maxits == 10000); + assert(pso.verbose == 0); + assert(pso.nfreq == INT_MAX); + assert(pso.nstencil == 7); + + assert(pso.reltol == 1.0e-08); + assert(pso.abstol == 1.0e-15); + + ifail = pso.verbose; + assert(ifail == 0); + + return ifail; +} + +/***************************************************************************** + * + * test_psi_solver_options_type + * + *****************************************************************************/ + +int test_psi_solver_options_type(void) { + + int ifail = 0; + + /* Assume default has passed, and then only the type is relevant... */ + { + psi_solver_options_t p = psi_solver_options_type(PSI_POISSON_SOLVER_SOR); + if (p.psolver != PSI_POISSON_SOLVER_SOR) ifail += 1; + assert(ifail == 0); + } + + { + psi_solver_options_t p = psi_solver_options_type(PSI_POISSON_SOLVER_PETSC); + if (p.psolver != PSI_POISSON_SOLVER_PETSC) ifail += 1; + assert(ifail == 0); + } + + { + psi_solver_options_t p = psi_solver_options_type(PSI_POISSON_SOLVER_NONE); + if (p.psolver != PSI_POISSON_SOLVER_NONE) ifail += 1; + assert(ifail == 0); + } + + return ifail; +} + + +/***************************************************************************** + * + * test_psi_solver_options_to_json + * + *****************************************************************************/ + +int test_psi_solver_options_to_json(void) { + + int ifail = 0; + psi_solver_options_t pso = psi_solver_options_default(); + cJSON * json = NULL; + + ifail = psi_solver_options_to_json(&pso, &json); + assert(ifail == 0); + + { + /* We assume psi_solver_options_from_json() is independent ... */ + psi_solver_options_t check = {PSI_POISSON_SOLVER_INVALID}; + ifail = psi_solver_options_from_json(json, &check); + assert(ifail == 0); + if (check.psolver != PSI_POISSON_SOLVER_SOR) ifail += 1; + assert(ifail == 0); + } + + cJSON_Delete(json); + + return ifail; +} + +/***************************************************************************** + * + * test_psi_solver_options_from_json + * + *****************************************************************************/ + +int test_psi_solver_options_from_json(void) { + + int ifail = 0; + const char * jstr = "{\"Solver type\": \"petsc\"," + "\"Maximum iterations\": 200," + "\"Level of verbosity\": 2," + "\"Frequency of output\": 20," + "\"Stencil points\": 19," + "\"Relative tolerance\": 0.01," + "\"Absolute tolerance\": 0.02}"; + + cJSON * json = cJSON_Parse(jstr); + assert(json); + + { + /* Check result ... */ + psi_solver_options_t pso = {PSI_POISSON_SOLVER_INVALID}; + ifail = psi_solver_options_from_json(json, &pso); + assert(ifail == 0); + + assert(pso.psolver == PSI_POISSON_SOLVER_PETSC); + assert(pso.maxits == 200); + assert(pso.verbose == 2); + assert(pso.nfreq == 20); + assert(pso.nstencil == 19); + + assert(fabs(pso.reltol - 0.01) < DBL_EPSILON); + assert(fabs(pso.abstol - 0.02) < DBL_EPSILON); + } + + return ifail; +} + + diff --git a/tests/unit/tests.c b/tests/unit/tests.c index e5674fc8..fb925904 100644 --- a/tests/unit/tests.c +++ b/tests/unit/tests.c @@ -113,6 +113,7 @@ __host__ int tests_create() { test_phi_ch_suite(); test_polar_active_suite(); + test_psi_solver_options_suite(); test_psi_options_suite(); test_psi_suite(); test_psi_sor_suite(); diff --git a/tests/unit/tests.h b/tests/unit/tests.h index 86fa2bf8..92bb42b6 100644 --- a/tests/unit/tests.h +++ b/tests/unit/tests.h @@ -96,6 +96,7 @@ int test_phi_bc_inflow_opts_suite(void); int test_phi_bc_inflow_fixed_suite(void); int test_phi_bc_outflow_opts_suite(void); int test_phi_bc_outflow_free_suite(void); +int test_psi_solver_options_suite(void); int test_psi_options_suite(void); int test_psi_suite(void); int test_psi_sor_suite(void); From 20c21db33bc45ad738f7652bfd647676b5ced34a Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 24 Mar 2023 17:02:47 +0000 Subject: [PATCH 38/97] Refactor Poisson solver and stencils --- README.md | 10 +- src/advection.c | 85 -- src/advection.h | 6 +- src/advection_bcs.c | 53 +- src/advection_bcs.h | 4 +- src/fe_electro.c | 4 +- src/fe_electro_symmetric.c | 6 +- src/ludwig.c | 58 +- src/main.c | 14 +- src/nernst_planck.c | 204 +++- src/psi.c | 14 +- src/psi.h | 10 +- src/psi_force.c | 322 +----- src/psi_force.h | 17 +- src/psi_gradients.c | 456 +------- src/psi_gradients.h | 33 +- src/psi_petsc.c | 1520 +++++++++++--------------- src/psi_petsc.h | 41 +- src/psi_rt.c | 65 +- src/psi_solver.c | 104 ++ src/psi_solver.h | 57 + src/psi_solver_options.c | 23 +- src/psi_solver_options.h | 2 + src/psi_sor.c | 295 ++--- src/psi_sor.h | 32 +- src/stencil.h | 45 + src/stencil_d3q19.c | 79 ++ src/stencil_d3q19.h | 24 + src/stencil_d3q27.c | 84 ++ src/stencil_d3q27.h | 25 + src/stencil_d3q7.c | 87 ++ src/stencil_d3q7.h | 34 + src/stencils.c | 85 ++ src/util_petsc.c | 81 ++ src/util_petsc.h | 37 + tests/unit/Makefile | 5 + tests/unit/test_fe_electro.c | 1 + tests/unit/test_nernst_planck.c | 110 +- tests/unit/test_psi.c | 5 + tests/unit/test_psi_solver_options.c | 40 +- tests/unit/test_psi_solver_petsc.c | 67 ++ tests/unit/test_psi_sor.c | 172 +-- tests/unit/test_stencil_d3q19.c | 65 ++ tests/unit/test_stencil_d3q27.c | 73 ++ tests/unit/test_stencil_d3q7.c | 66 ++ tests/unit/test_stencils.c | 130 +++ tests/unit/tests.c | 13 +- tests/unit/tests.h | 7 +- 48 files changed, 2582 insertions(+), 2188 deletions(-) create mode 100644 src/psi_solver.c create mode 100644 src/psi_solver.h create mode 100644 src/stencil.h create mode 100644 src/stencil_d3q19.c create mode 100644 src/stencil_d3q19.h create mode 100644 src/stencil_d3q27.c create mode 100644 src/stencil_d3q27.h create mode 100644 src/stencil_d3q7.c create mode 100644 src/stencil_d3q7.h create mode 100644 src/stencils.c create mode 100644 src/util_petsc.c create mode 100644 src/util_petsc.h create mode 100644 tests/unit/test_psi_solver_petsc.c create mode 100644 tests/unit/test_stencil_d3q19.c create mode 100644 tests/unit/test_stencil_d3q27.c create mode 100644 tests/unit/test_stencil_d3q7.c create mode 100644 tests/unit/test_stencils.c diff --git a/README.md b/README.md index 569fe8cd..a2a4c825 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ $ make test Full details of the build process are available at https://ludwig.epcc.ed.ac.uk/. -#### Background and Tutorial +#### Background Background documentation on the LB model and various free energy choices is available in the `docs` directory. @@ -57,14 +57,6 @@ $ make ``` will produce a pdf version of the LaTeX source. -A short tutorial, which includes some examples in which the -results are visualised, is also provided: -``` -$ cd docs/tutorial -$ make -``` -to produce a pdf of the tutorial instructions. - #### Contributing If you would like to contribute, please consider a pull request. diff --git a/src/advection.c b/src/advection.c index 6b30bf09..05e9397e 100644 --- a/src/advection.c +++ b/src/advection.c @@ -34,7 +34,6 @@ #include #include "advection_s.h" -#include "psi_gradients.h" #include "hydro.h" #include "timer.h" @@ -1462,90 +1461,6 @@ static int advection_le_5th(advflux_t * flux, hydro_t * hydro, int nf, return 0; } -/***************************************************************************** - * - * advective_fluxes_d3qx - * - * General routine for nf fields at starting address f. - * No Lees Edwards boundaries. - * - * The storage of the field(s) for all the related routines is - * assumed to be f[index][nf], where index is the spatial index. - * - *****************************************************************************/ - -int advective_fluxes_d3qx(hydro_t * hydro, int nf, double * f, - double ** flx) { - - assert(hydro); - assert(nf > 0); - assert(f); - assert(flx); - - advective_fluxes_2nd_d3qx(hydro, nf, f, flx); - - return 0; -} - -/***************************************************************************** - * - * advective_fluxes_2nd_d3qx - * - * 'Centred difference' advective fluxes. No LE planes. - * TODO: belongs to Nernst Planck. - * - * Symmetric two-point stencil. - * - *****************************************************************************/ - -int advective_fluxes_2nd_d3qx(hydro_t * hydro, int nf, double * f, - double ** flx) { - - int nlocal[3]; - int ic, jc, kc, c; - int n; - int index0, index1; - int nsites; - double u0[3], u1[3], u; - - assert(hydro); - assert(hydro->cs); - assert(nf > 0); - assert(f); - assert(flx); - - cs_nlocal(hydro->cs, nlocal); - cs_nsites(hydro->cs, &nsites); - - for (ic = 1; ic <= nlocal[X]; ic++) { - for (jc = 1; jc <= nlocal[Y]; jc++) { - for (kc = 1; kc <= nlocal[Z]; kc++) { - - index0 = cs_index(hydro->cs, ic, jc, kc); - hydro_u(hydro, index0, u0); - - for (c = 1; c < PSI_NGRAD; c++) { - - index1 = cs_index(hydro->cs, ic + psi_gr_cv[c][X], jc + psi_gr_cv[c][Y], kc + psi_gr_cv[c][Z]); - hydro_u(hydro, index1, u1); - - u = 0.5*((u0[X] + u1[X])*psi_gr_cv[c][X] + (u0[Y] + u1[Y])*psi_gr_cv[c][Y] + (u0[Z] + u1[Z])*psi_gr_cv[c][Z]); - - for (n = 0; n < nf; n++) { - flx[addr_rank1(nsites, nf, index0, n)][c - 1] - = u*0.5*(f[addr_rank1(nsites, nf, index1, n)] - + f[addr_rank1(nsites, nf, index0, n)]); - } - } - - /* Next site */ - } - } - } - - return 0; -} - /***************************************************************************** * * advflux_cs_compute diff --git a/src/advection.h b/src/advection.h index 9e5dbf77..07cb0714 100644 --- a/src/advection.h +++ b/src/advection.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-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -36,10 +36,6 @@ __host__ int advflux_cs_compute(advflux_t * flux, hydro_t * h, field_t * f); __host__ int advflux_cs_zero(advflux_t * flux); __host__ int advection_x(advflux_t * obj, hydro_t * hydro, field_t * field); -__host__ int advective_fluxes_d3qx(hydro_t * hydro, int nf, double * f, - double ** flx); -__host__ int advective_fluxes_2nd_d3qx(hydro_t * hydro, int nf, double * f, - double ** flx); __host__ int advection_order_set(const int order); __host__ int advection_order(int * order); diff --git a/src/advection_bcs.c b/src/advection_bcs.c index 971b6907..ec9d6ddd 100644 --- a/src/advection_bcs.c +++ b/src/advection_bcs.c @@ -7,7 +7,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2009-2021 The University of Edinburgh + * (c) 2009-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -23,7 +23,6 @@ #include "coords.h" #include "kernel.h" #include "advection_s.h" -#include "psi_gradients.h" #include "timer.h" #include "advection_bcs.h" @@ -246,56 +245,6 @@ __global__ void advflux_cs_no_flux_kernel(kernel_ctxt_t * ktx, return; } -/***************************************************************************** - * - * advective_bcs_no_flux_d3qx - * - * Set normal fluxes at solid fluid interfaces to zero. - * - *****************************************************************************/ - -int advective_bcs_no_flux_d3qx(int nf, double ** flx, map_t * map) { - - int n; - int nsites; - int nlocal[3]; - int ic, jc, kc, index0, index1; - int status; - int c; - double mask[PSI_NGRAD]; - - assert(nf > 0); - assert(flx); - assert(map); - - cs_nsites(map->cs, &nsites); - cs_nlocal(map->cs, nlocal); - - for (ic = 1; ic <= nlocal[X]; ic++) { - for (jc = 1; jc <= nlocal[Y]; jc++) { - for (kc = 1; kc <= nlocal[Z]; kc++) { - - index0 = cs_index(map->cs, ic, jc, kc); - map_status(map, index0, &status); - mask[0] = (status == MAP_FLUID); - - for (c = 1; c < PSI_NGRAD; c++) { - - index1 = cs_index(map->cs, ic + psi_gr_cv[c][X], jc + psi_gr_cv[c][Y], kc + psi_gr_cv[c][Z]); - map_status(map, index1, &status); - mask[c] = (status == MAP_FLUID); - - for (n = 0; n < nf; n++) { - flx[addr_rank1(nsites, nf, index0, n)][c - 1] *= mask[0]*mask[c]; - } - } - } - } - } - - return 0; -} - /***************************************************************************** * * advection_bcs_wall diff --git a/src/advection_bcs.h b/src/advection_bcs.h index a05e1e22..6d1430ae 100644 --- a/src/advection_bcs.h +++ b/src/advection_bcs.h @@ -5,7 +5,7 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2009-2019 The University of Edinburgh + * (c) 2009-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -21,8 +21,6 @@ __host__ int advection_bcs_no_normal_flux(int nf, advflux_t * flux, map_t * map); __host__ int advection_bcs_wall(field_t * phi); - -__host__ int advective_bcs_no_flux_d3qx(int nf, double ** flx, map_t * map); __host__ int advflux_cs_no_normal_flux(advflux_t * flux, map_t * map); #endif diff --git a/src/fe_electro.c b/src/fe_electro.c index 85b0a8b2..b4e475aa 100644 --- a/src/fe_electro.c +++ b/src/fe_electro.c @@ -309,7 +309,7 @@ int fe_electro_stress(fe_electro_t * fe, int index, double s[3][3]) { reunit = 1.0/eunit; psi_epsilon(fe->psi, &epsilon); - psi_electric_field_d3qx(fe->psi, index, e); + psi_electric_field(fe->psi, index, e); e2 = 0.0; @@ -362,7 +362,7 @@ int fe_electro_stress_ex(fe_electro_t * fe, int index, double s[3][3]) { reunit = 1.0/eunit; psi_epsilon(fe->psi, &epsilon); - psi_electric_field_d3qx(fe->psi, index, e); + psi_electric_field(fe->psi, index, e); e2 = 0.0; diff --git a/src/fe_electro_symmetric.c b/src/fe_electro_symmetric.c index 35b6a6e8..9e291743 100644 --- a/src/fe_electro_symmetric.c +++ b/src/fe_electro_symmetric.c @@ -271,7 +271,7 @@ __host__ int fe_es_mu_phi(fe_es_t * fe, int index, double * mu) { e2 = 0.0; - psi_electric_field_d3qx(fe->psi, index, e); + psi_electric_field(fe->psi, index, e); for (ia = 0; ia < 3; ia++) { e[ia] *= kt*reunit; @@ -364,8 +364,6 @@ __host__ int fe_es_deltamu_set(fe_es_t * fe, int nk, double * deltamu) { * If phi = +1 then epsilon(r) = epsilon2, as set in the call to * fe_es_epsilon_set(). * - * The function is of signature f_vare_t (see psi_sor.h). - * *****************************************************************************/ __host__ int fe_es_var_epsilon(fe_es_t * fe, int index, double * epsilon) { @@ -439,7 +437,7 @@ __host__ int fe_es_stress_ex(fe_es_t * fe, int index, double s[3][3]) { requires phi and total electric field */ field_scalar(fe->fe_symm->phi, index, &phi); - psi_electric_field_d3qx(fe->psi, index, e); + psi_electric_field(fe->psi, index, e); e2 = 0.0; diff --git a/src/ludwig.c b/src/ludwig.c index e58e5f36..d11fa876 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -101,12 +101,11 @@ /* Electrokinetics */ #include "psi.h" #include "psi_rt.h" -#include "psi_sor.h" #include "psi_stats.h" #include "psi_force.h" +#include "psi_solver.h" #include "psi_colloid.h" #include "nernst_planck.h" -#include "psi_petsc.h" /* Statistics */ #include "stats_colloid.h" @@ -146,7 +145,8 @@ struct ludwig_s { wall_t * wall; /* Side walls / Porous media */ noise_t * noise_rho; /* Lattice fluctuation generator (rho) */ noise_t * noise_phi; /* Binary fluid noise generation (fluxes) */ - f_vare_t epsilon; /* Variable epsilon function for Poisson solver */ + + psi_solver_t * poisson; /* Poisson solver */ fe_t * fe; /* Free energy "polymorphic" version */ ch_t * ch; /* Cahn Hilliard (surfactants) */ @@ -237,10 +237,6 @@ static int ludwig_rt(ludwig_t * ludwig) { physics_init_rt(rt, ludwig->phys); physics_info(pe, ludwig->phys); -#ifdef PETSC - if (ludwig->psi) psi_petsc_init(ludwig->psi, ludwig->fe, ludwig->epsilon); -#endif - lb_run_time(pe, cs, rt, &ludwig->lb); collision_run_time(pe, rt, ludwig->lb, ludwig->noise_rho); map_init_rt(pe, cs, rt, &ludwig->map); @@ -617,11 +613,9 @@ void ludwig_run(const char * inputfile) { /* Poisson solve */ TIMER_start(TIMER_ELECTRO_POISSON); -#ifdef PETSC - psi_petsc_solve(ludwig->psi, ludwig->fe, ludwig->epsilon); -#else - psi_sor_solve(ludwig->psi, ludwig->fe, ludwig->epsilon, step); -#endif + + ludwig->poisson->impl->solve(ludwig->poisson, step); + TIMER_stop(TIMER_ELECTRO_POISSON); if (ludwig->hydro) { @@ -658,8 +652,8 @@ void ludwig_run(const char * inputfile) { /* Force calculation as divergence of stress tensor */ if (flag == PSI_FORCE_DIVERGENCE) { - psi_force_divstress_d3qx(ludwig->psi, ludwig->fe, ludwig->hydro, - ludwig->map, ludwig->collinfo); + psi_force_divstress(ludwig->psi, ludwig->fe, ludwig->hydro, + ludwig->collinfo); } TIMER_stop(TIMER_FORCE_CALCULATION); @@ -987,9 +981,8 @@ void ludwig_run(const char * inputfile) { MPI_Barrier(comm); /* Shut down cleanly. Give the timer statistics. Finalise PE. */ -#ifdef PETSC - if (ludwig->psi) psi_petsc_finish(); -#endif + + if (ludwig->poisson) ludwig->poisson->impl->free(&ludwig->poisson); if (ludwig->psi) psi_free(&ludwig->psi); if (ludwig->stat_rheo) stats_rheology_free(ludwig->stat_rheo); @@ -1762,6 +1755,7 @@ int free_energy_init_rt(ludwig_t * ludwig) { } else if(strcmp(description, "fe_electro") == 0) { + int ifail = 0; fe_electro_t * fe = NULL; fe_force_method_enum_t method = fe_force_method_default(); int psi_method = PSI_FORCE_NONE; @@ -1807,6 +1801,7 @@ int free_energy_init_rt(ludwig_t * ludwig) { pe_info(pe, "Parameters:\n"); { + /* Options here currently include solver options ... */ psi_options_t opts = psi_options_default(nhalo); psi_options_rt(pe, cs, rt, &opts); psi_create(pe, cs, &opts, &ludwig->psi); @@ -1820,6 +1815,16 @@ int free_energy_init_rt(ludwig_t * ludwig) { /* Create FE objects and set function pointers */ fe_electro_create(pe, ludwig->psi, &fe); ludwig->fe = (fe_t *) fe; + + /* Uniform solver ok */ + + ifail = psi_solver_create(ludwig->psi, &ludwig->poisson); + if (ifail != 0) { + pe_info(pe, "Poisson solver initialisation failed\n"); + pe_info(pe, "This probably means you specified \"petsc\" but it has\n"); + pe_info(pe, "not been compiled. Please specify sor in the input.\n"); + pe_fatal(pe, "Please check and try again\n"); + } } else if(strcmp(description, "fe_electro_symmetric") == 0) { @@ -1925,11 +1930,28 @@ int free_energy_init_rt(ludwig_t * ludwig) { /* If permittivities really not the same number... */ if (util_double_same(e1, e2)) { + int ifail = 0; + ifail = psi_solver_create(ludwig->psi, &ludwig->poisson); + if (ifail != 0) { + pe_info(pe, "Poisson solver initialisation failed\n"); + pe_info(pe, "This may mean you specified \"petsc\" but it has\n"); + pe_info(pe, "not been compiled. Please specify sor in the input.\n"); + pe_fatal(pe, "Please check and try again\n"); + } pe_info(pe, "Poisson solver: %15s\n", "uniform"); } else { + int ifail = 0; + var_epsilon_t user = {.fe = (fe_t *) fes, + .epsilon = (var_epsilon_ft) fe_es_var_epsilon}; + ifail = psi_solver_var_epsilon_create(ludwig->psi, user, &ludwig->poisson); + if (ifail != 0) { + pe_info(pe, "Poisson solver initialisation failed\n"); + pe_info(pe, "This may mean you specified \"petsc\" but it has\n"); + pe_info(pe, "not been compiled. Please specify sor in the input.\n"); + pe_fatal(pe, "Please check and try again\n"); + } pe_info(pe, "Poisson solver: %15s\n", "heterogeneous"); - ludwig->epsilon = (f_vare_t) fe_es_var_epsilon; } /* Force */ diff --git a/src/main.c b/src/main.c index 796fa181..0b0ca610 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-2022 The University of Edinburgh + * (c) 2011-2023 The University of Edinburgh * *****************************************************************************/ @@ -17,14 +17,15 @@ #include "pe.h" #include "ludwig.h" -#ifdef PETSC - #include "petscksp.h" -#endif +#include "util_petsc.h" /***************************************************************************** * * main * + * The Petsc initialisation/finalisation is a facade if there's no + * actual Petsc in the build. + * *****************************************************************************/ int main(int argc, char ** argv) { @@ -33,16 +34,13 @@ int main(int argc, char ** argv) { 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]); ludwig_run(inputfile); -#ifdef PETSC PetscFinalize(); -#endif MPI_Finalize(); return 0; diff --git a/src/nernst_planck.c b/src/nernst_planck.c index 424cbfcf..dbc9e0c2 100644 --- a/src/nernst_planck.c +++ b/src/nernst_planck.c @@ -93,6 +93,9 @@ static int nernst_planck_update_d3qx(psi_t * psi, map_t * map, double ** flx); static double max_acc; +int np_advective_fluxes(psi_t * psi, hydro_t * hydro, double ** flux); +int np_no_flux_boundary(psi_t * psi, map_t * map, double ** flux); + /***************************************************************************** * * nernst_planck_driver @@ -359,19 +362,20 @@ int nernst_planck_driver_d3qx(psi_t * psi, fe_t * fe, hydro_t * hydro, if (flx == NULL) pe_fatal(psi->pe, "calloc(flx) failed\n"); for (ia = 0; ia < psi->nsites*nk; ia++) { - flx[ia] = (double *) calloc(PSI_NGRAD-1, sizeof(double)); + int nflux = psi->stencil->npoints; + flx[ia] = (double *) calloc(nflux-1, sizeof(double)); assert(flx[ia]); if (flx[ia] == NULL) pe_fatal(psi->pe, "calloc(flx[]) failed\n"); } /* Add advective fluxes */ - if (hydro) advective_fluxes_d3qx(hydro, nk, psi->rho->data, flx); + if (hydro) np_advective_fluxes(psi, hydro, flx); /* Add diffusive fluxes */ nernst_planck_fluxes_d3qx(psi, fe, hydro, map, cinfo, flx); /* Apply no-flux BC */ - if (map) advective_bcs_no_flux_d3qx(nk, flx, map); + if (map) np_no_flux_boundary(psi, map, flx); /* Update charges */ nernst_planck_update_d3qx(psi, map, flx); @@ -422,6 +426,8 @@ static int nernst_planck_fluxes_d3qx(psi_t * psi, fe_t * fe, hydro_t * hydro, double * __restrict__ psidata = psi->psi->data; double * __restrict__ rhodata = psi->rho->data; + LB_RCS_TABLE(rcs); + assert(psi); assert(fe); assert(fe->func->mu_solv); @@ -447,10 +453,17 @@ static int nernst_planck_fluxes_d3qx(psi_t * psi, fe_t * fe, hydro_t * hydro, continue; } else { + stencil_t * s = psi->stencil; + assert(s); + + for (c = 1; c < s->npoints; c++) { - for (c = 1; c < PSI_NGRAD; c++) { + int8_t cx = s->cv[c][X]; + int8_t cy = s->cv[c][Y]; + int8_t cz = s->cv[c][Z]; + int8_t pcv = cx*cx + cy*cy + cz*cz; - index1 = cs_index(psi->cs, ic + psi_gr_cv[c][X], jc + psi_gr_cv[c][Y], kc + psi_gr_cv[c][Z]); + index1 = cs_index(psi->cs, ic + cx, jc + cy, kc + cz); map_status(map, index1, &status1); if (status1 == MAP_FLUID) { @@ -470,7 +483,7 @@ static int nernst_planck_fluxes_d3qx(psi_t * psi, fe_t * fe, hydro_t * hydro, rho1 = rhodata[addr_rank1(psi->nsites, nk, index1, n)]*b1; flx[addr_rank1(psi->nsites, nk, index0, n)][c - 1] - -= psi->diffusivity[n]*0.5*(1.0 + b0)*(rho1 - rho0)*psi_gr_rnorm[c]; + -= psi->diffusivity[n]*0.5*(1.0 + b0)*(rho1 - rho0)*rcs[pcv]; } } } @@ -520,7 +533,6 @@ int nernst_planck_fluxes_force_d3qx(psi_t * psi, fe_t * fe, hydro_t * hydro, double flocal[4] = {0.0, 0.0, 0.0, 0.0}, fsum[4], f[3]; double flxtmp[2]; double dt; - double aux; MPI_Comm comm; colloid_t * pc = NULL; @@ -528,6 +540,8 @@ int nernst_planck_fluxes_force_d3qx(psi_t * psi, fe_t * fe, hydro_t * hydro, double * __restrict__ psidata = psi->psi->data; double * __restrict__ rhodata = psi->rho->data; + LB_RCS_TABLE(rcs); + assert(psi); assert(flx); @@ -558,7 +572,7 @@ int nernst_planck_fluxes_force_d3qx(psi_t * psi, fe_t * fe, hydro_t * hydro, /* Total electrostatic force on colloid */ if (pc) { - psi_electric_field_d3qx(psi, index0, e); + psi_electric_field(psi, index0, e); f[X] = rho_elec * e[X] * dt; f[Y] = rho_elec * e[Y] * dt; @@ -571,10 +585,15 @@ int nernst_planck_fluxes_force_d3qx(psi_t * psi, fe_t * fe, hydro_t * hydro, } else { - /* Internal electrostatic force on fluid */ - for (c = 1; c < PSI_NGRAD; c++) { + stencil_t * s = psi->stencil; + /* Internal electrostatic force on fluid */ + for (c = 1; c < s->npoints; c++) { + int8_t cx = s->cv[c][X]; + int8_t cy = s->cv[c][Y]; + int8_t cz = s->cv[c][Z]; + int8_t pcv = cx*cx + cy*cy + cz*cz; - index1 = cs_index(psi->cs, ic + psi_gr_cv[c][X], jc + psi_gr_cv[c][Y], kc + psi_gr_cv[c][Z]); + index1 = cs_index(psi->cs, ic + cx, jc + cy, kc + cz); map_status(map, index1, &status1); if (status1 == MAP_FLUID) { @@ -592,27 +611,17 @@ int nernst_planck_fluxes_force_d3qx(psi_t * psi, fe_t * fe, hydro_t * hydro, b1 = exp(+beta*(mu1 - mu0)); rho1 = rhodata[addr_rank1(nsites, nk, index1, n)]*b1; - /* Auxiliary terms */ - /* Adding flxtmp[1] to flxtmp[0] below subtracts the ideal gas part */ - flxtmp[0] = - 0.5*(1.0 + b0)*(rho1 - rho0) * psi_gr_rnorm[c]; - flxtmp[1] = (rhodata[addr_rank1(nsites, nk, index1, n)] - - rhodata[addr_rank1(nsites, nk, index0, n)]) - * psi_gr_rnorm[c]; + flxtmp[0] = - 0.5*(1.0 + b0)*(rho1 - rho0)*rcs[pcv]; - /* Link flux */ + /* Diffusive flux accumulated */ flx[addr_rank1(nsites, nk, index0, n)][c - 1] += psi->diffusivity[n]*flxtmp[0]; - /* Force on fluid including ideal gas part in chemical potential */ - aux = psi_gr_rcs2 * psi_gr_wv[c] * flxtmp[0] * rbeta; - - f[X] -= aux * psi_gr_cv[c][X]; - f[Y] -= aux * psi_gr_cv[c][Y]; - f[Z] -= aux * psi_gr_cv[c][Z]; - + /* Force, including ideal gas part in chemical potential */ + f[X] -= s->wgradients[c]*cx*flxtmp[0]*rbeta; + f[Y] -= s->wgradients[c]*cy*flxtmp[0]*rbeta; + f[Z] -= s->wgradients[c]*cz*flxtmp[0]*rbeta; } - } - } /* Electrostatic force in external field */ @@ -652,19 +661,13 @@ int nernst_planck_fluxes_force_d3qx(psi_t * psi, fe_t * fe, hydro_t * hydro, index0 = cs_index(psi->cs, ic, jc, kc); colloids_info_map(cinfo, index0, &pc); - if (pc) { - continue; - } - else { - - f[X] = -fsum[X]; - f[Y] = -fsum[Y]; - f[Z] = -fsum[Z]; + if (pc) continue; - if (hydro) hydro_f_local_add(hydro, index0, f); - - } + f[X] = -fsum[X]; + f[Y] = -fsum[Y]; + f[Z] = -fsum[Z]; + if (hydro) hydro_f_local_add(hydro, index0, f); } } } @@ -708,10 +711,11 @@ static int nernst_planck_update_d3qx(psi_t * psi, map_t * map, double ** flx) { map_status(map, index, &status); if (status == MAP_FLUID) { + stencil_t * s = psi->stencil; for (n = 0; n < nk; n++) { acc = 0.0; - for (c = 1; c < PSI_NGRAD; c++) { + for (c = 1; c < s->npoints; c++) { psi->rho->data[addr_rank1(nsites, nk, index, n)] -= flx[addr_rank1(nsites, nk, index, n)][c - 1] * dt; acc += fabs(flx[addr_rank1(nsites, nk, index, n)][c - 1] * dt); @@ -817,3 +821,125 @@ int nernst_planck_adjust_multistep(psi_t * psi) { return 0; } + +/***************************************************************************** + * + * np_advective_fluxes + * + * 'Centred difference' advective fluxes for the char densities rho. + * + * Symmetric two-point stencil. + * + *****************************************************************************/ + +int np_advective_fluxes(psi_t * psi, hydro_t * hydro, double ** flx) { + + int nlocal[3] = {0}; + cs_t * cs = NULL; + stencil_t * s = NULL; + + double * __restrict__ rho = psi->rho->data; + + assert(psi); + assert(hydro); + assert(flx); + + cs = psi->cs; + s = psi->stencil; + assert(cs); + assert(s); + + 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++) { + + int index0 = cs_index(cs, ic, jc, kc); + double u0[3] = {0}; + hydro_u(hydro, index0, u0); + + for (int p = 1; p < s->npoints; p++) { + + int8_t cx = s->cv[p][X]; + int8_t cy = s->cv[p][Y]; + int8_t cz = s->cv[p][Z]; + int index1 = cs_index(cs, ic + cx, jc + cy, kc + cz); + double u1[3] = {0}; + double u = 0.0; + hydro_u(hydro, index1, u1); + + u = 0.5*((u0[X]+u1[X])*cx + (u0[Y]+u1[Y])*cy + (u0[Z]+u1[Z])*cz); + + for (int n = 0; n < psi->nk; n++) { + double rho0 = rho[addr_rank1(psi->nsites, psi->nk, index0, n)]; + double rho1 = rho[addr_rank1(psi->nsites, psi->nk, index1, n)]; + double flux = u*0.5*(rho0 + rho1); + flx[addr_rank1(psi->nsites, psi->nk, index0, n)][p-1] = flux; + } + } + /* Next site */ + } + } + } + + return 0; +} + +/***************************************************************************** + * + * advective_bcs_no_flux_d3qx + * + * Set normal fluxes at solid fluid interfaces to zero. + * + *****************************************************************************/ + +int np_no_flux_boundary(psi_t * psi, map_t * map, double ** flx) { + + int nlocal[3] = {0}; + cs_t * cs = NULL; + stencil_t * s = NULL; + + assert(psi); + assert(map); + assert(flx); + + cs = psi->cs; + s = psi->stencil; + assert(cs); + assert(s); + + 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++) { + + int index0 = cs_index(cs, ic, jc, kc); + int mask0 = 1; + int status = MAP_BOUNDARY; + map_status(map, index0, &status); + mask0 = (status == MAP_FLUID); + + for (int p = 1; p < s->npoints; p++) { + + int8_t cx = s->cv[p][X]; + int8_t cy = s->cv[p][Y]; + int8_t cz = s->cv[p][Z]; + int index1 = cs_index(cs, ic + cx, jc + cy, kc + cz); + int mask = 1; + + map_status(map, index1, &status); + mask = (status == MAP_FLUID); + mask = mask*mask0; + + for (int n = 0; n < psi->nk; n++) { + flx[addr_rank1(psi->nsites, psi->nk, index0, n)][p-1] *= mask; + } + } + } + } + } + + return 0; +} diff --git a/src/psi.c b/src/psi.c index a120153f..f831b00d 100644 --- a/src/psi.c +++ b/src/psi.c @@ -18,14 +18,10 @@ *****************************************************************************/ #include -#include #include +#include -#include "pe.h" -#include "coords.h" -#include "util.h" -#include "map.h" -#include "psi_gradients.h" +#include "psi.h" /***************************************************************************** * @@ -83,6 +79,8 @@ int psi_free(psi_t ** psi) { int psi_initialise(pe_t * pe, cs_t * cs, const psi_options_t * opts, psi_t * psi) { + int ifail = 0; + assert(pe); assert(cs); assert(opts); @@ -116,6 +114,7 @@ int psi_initialise(pe_t * pe, cs_t * cs, const psi_options_t * opts, /* Solver options */ psi->solver = opts->solver; + ifail = stencil_create(opts->solver.nstencil, &psi->stencil); /* Nernst-Planck */ psi->multisteps = opts->nsmallstep; @@ -132,7 +131,7 @@ int psi_initialise(pe_t * pe, cs_t * cs, const psi_options_t * opts, psi->nfreq_io = INT_MAX; - return 0; + return ifail; } /***************************************************************************** @@ -145,6 +144,7 @@ int psi_finalise(psi_t * psi) { assert(psi->psi); + stencil_free(&psi->stencil); field_free(psi->rho); field_free(psi->psi); diff --git a/src/psi.h b/src/psi.h index 397a05f2..aa740994 100644 --- a/src/psi.h +++ b/src/psi.h @@ -16,9 +16,9 @@ #include "pe.h" #include "field.h" -#include "io_harness.h" #include "map.h" #include "psi_options.h" +#include "stencil.h" typedef struct psi_s psi_t; @@ -61,16 +61,12 @@ struct psi_s { double e0[3]; /* External electric field */ /* Solver options */ - psi_solver_options_t solver; + psi_solver_options_t solver; /* User options */ + stencil_t * stencil; /* Finite difference stencil info */ }; -/* f_vare_t describes the signature of the function expected - * to return the permittivity as a function of position index. */ - -typedef int (* f_vare_t)(void * fe, int index, double * epsilon); - int psi_create(pe_t * pe, cs_t * cs, const psi_options_t * opts, psi_t ** p); int psi_free(psi_t ** psi); diff --git a/src/psi_force.c b/src/psi_force.c index 65e3083c..589dc50e 100644 --- a/src/psi_force.c +++ b/src/psi_force.c @@ -120,7 +120,7 @@ int psi_force_gradmu_e(psi_t * psi, fe_t * fe, hydro_t * hydro, are implicitly calculated */ psi_rho_elec(psi, index, &rho_elec); - psi_electric_field_d3qx(psi, index, e); + psi_electric_field(psi, index, e); for (ia = 0; ia < 3; ia++) { e[ia] *= kt*reunit; @@ -295,7 +295,7 @@ int psi_force_gradmu_es(psi_t * psi, fe_t * fe, field_t * phi, hydro_t * hydro, are implicitly calculated */ psi_rho_elec(psi, index, &rho_elec); - psi_electric_field_d3qx(psi, index, e); + psi_electric_field(psi, index, e); for (ia = 0; ia < 3; ia++) { e[ia] *= kt*reunit; @@ -361,324 +361,60 @@ int psi_force_gradmu_es(psi_t * psi, fe_t * fe, field_t * phi, hydro_t * hydro, * * psi_force_divstress * - * A test routine for force via divergence of stress, allowing + * A routine for force via divergence of stress, allowing * the stress to be computed inside the colloids. * * The stress is to include the full electric field. * - * TODO: The assert(0) indicates there is no test for this; - * the _d3qx version is preferred. - * *****************************************************************************/ int psi_force_divstress(psi_t * psi, fe_t * fe, hydro_t * hydro, colloids_info_t * cinfo) { - int ic, jc, kc; - int index, index1; - int ia; - int nlocal[3]; - - double force[3]; - double pth1[3][3]; - - colloid_t * pc = NULL; - - assert(psi); - assert(fe); - assert(cinfo); - - cs_nlocal(psi->cs, nlocal); - - assert(0); /* NO TEST? */ - - for (ic = 1; ic <= nlocal[X]; ic++) { - for (jc = 1; jc <= nlocal[Y]; jc++) { - for (kc = 1; kc <= nlocal[Z]; kc++) { - - index = cs_index(psi->cs, ic, jc, kc); - - /* Calculate divergence based on 6-pt stencil */ - - index1 = cs_index(psi->cs, ic+1, jc, kc); - fe->func->stress(fe, index1, pth1); - - for (ia = 0; ia < 3; ia++) { - force[ia] = -0.5*(pth1[ia][X]); - } - - index1 = cs_index(psi->cs, ic-1, jc, kc); - fe->func->stress(fe, index1, pth1); - for (ia = 0; ia < 3; ia++) { - force[ia] += 0.5*(pth1[ia][X]); - } - - index1 = cs_index(psi->cs, ic, jc+1, kc); - fe->func->stress(fe, index1, pth1); - for (ia = 0; ia < 3; ia++) { - force[ia] -= 0.5*(pth1[ia][Y]); - } - - index1 = cs_index(psi->cs, ic, jc-1, kc); - fe->func->stress(fe, index1, pth1); - for (ia = 0; ia < 3; ia++) { - force[ia] += 0.5*(pth1[ia][Y]); - } - - index1 = cs_index(psi->cs, ic, jc, kc+1); - fe->func->stress(fe, index1, pth1); - for (ia = 0; ia < 3; ia++) { - force[ia] -= 0.5*(pth1[ia][Z]); - } - - index1 = cs_index(psi->cs, ic, jc, kc-1); - fe->func->stress(fe, index1, pth1); - for (ia = 0; ia < 3; ia++) { - force[ia] += 0.5*(pth1[ia][Z]); - } - - /* Store the force on lattice */ - if (pc) { - pc->force[X] += force[X]; - pc->force[Y] += force[Y]; - pc->force[Z] += force[Z]; - } - else { - hydro_f_local_add(hydro, index, force); - } - - - } - } - } - - return 0; -} - - -/***************************************************************************** - * - * psi_force_divstress_d3qx - * - * A routine for force via divergence of stress, allowing - * the stress to be computed inside the colloids. The - * calculation of the divergence is based on the D3QX stencil - * with X=6, 18 or 26. The stress is to include the full - * electric field. - * - *****************************************************************************/ - -int psi_force_divstress_d3qx(psi_t * psi, fe_t * fe, hydro_t * hydro, - map_t * map, colloids_info_t * cinfo) { - - int ic, jc, kc; - int index, index_nb; - int status, status_nb; - int ia, ib; - int nlocal[3]; - - double force[3]; - double pth_nb[3][3]; - - colloid_t * pc = NULL; - - int p; - int coords[3], coords_nb[3]; + int nlocal[3] = {0}; + cs_t * cs = NULL; + stencil_t * s = NULL; assert(psi); assert(cinfo); - cs_nlocal(psi->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(psi->cs, ic, jc, kc); - map_status(map, index, &status); - colloids_info_map(cinfo, index, &pc); - - cs_index_to_ijk(psi->cs, index, coords); - - for (ia = 0; ia < 3; ia++) { - force[ia] = 0.0; - } - - /* Calculate divergence based on D3QX stencil */ - for (p = 1; p < PSI_NGRAD; p++) { - - coords_nb[X] = coords[X] + psi_gr_cv[p][X]; - coords_nb[Y] = coords[Y] + psi_gr_cv[p][Y]; - coords_nb[Z] = coords[Z] + psi_gr_cv[p][Z]; - - index_nb = cs_index(psi->cs, coords_nb[X], coords_nb[Y], coords_nb[Z]); - map_status(map, index_nb, &status_nb); + cs = psi->cs; + s = psi->stencil; + assert(cs); + assert(s); - fe->func->stress(fe, index_nb, pth_nb); + cs_nlocal(cs, nlocal); - for (ia = 0; ia < 3; ia++) { - for (ib = 0; ib < 3; ib++) { - force[ia] -= psi_gr_wv[p] * psi_gr_rcs2 * pth_nb[ia][ib] * psi_gr_cv[p][ib]; - } - } - - } - - /* Store the force on lattice */ - - if (pc) { - pc->force[X] += force[X]; - pc->force[Y] += force[Y]; - pc->force[Z] += force[Z]; - } - else { - hydro_f_local_add(hydro, index, force); - } - - } - } - } - - return 0; -} - -/***************************************************************************** - * - * psi_force_divstress_one_sided_d3qx - * - * A routine for force via divergence of stress, allowing - * the stress to be computed inside the colloids. - * This is the attempt to use one-sided derivatives at the - * surface of the particles. - * The calculation of the divergence is based on the D3QX stencil - * with X=6, 18 or 26. The stress is to include the full - * electric field. - * - * TODO - * This routine has not been refactored as no test exists. The - * refactoring has retained hardwired references to fe_electro - * (only). - * - *****************************************************************************/ - -int psi_force_divstress_one_sided_d3qx(psi_t * psi, hydro_t * hydro, map_t * map, colloids_info_t * cinfo) { - - int ic, jc, kc; - int index, index_nb, index1, index2; - int status, status_nb; - int ia, ib; - int nlocal[3]; - int p; - int coords[3], coords_nb[3], coords1[3], coords2[3]; - - double force[3]; - double pth1[3][3], pth2[3][3]; - double pth[3][3], pth_nb[3][3]; - - fe_electro_t * fe = NULL; /* See note above */ - colloid_t * pc = NULL; + for (int ic = 1; ic <= nlocal[X]; ic++) { + for (int jc = 1; jc <= nlocal[Y]; jc++) { + for (int kc = 1; kc <= nlocal[Z]; kc++) { - assert(psi); - assert(cinfo); - - cs_nlocal(psi->cs, nlocal); - - assert(0); /* NO TEST? */ + int index = cs_index(cs, ic, jc, kc); + double force[3] = {0}; + colloid_t * pc = NULL; - for (ic = 1; ic <= nlocal[X]; ic++) { - for (jc = 1; jc <= nlocal[Y]; jc++) { - for (kc = 1; kc <= nlocal[Z]; kc++) { - - index = cs_index(psi->cs, ic, jc, kc); - map_status(map, index, &status); colloids_info_map(cinfo, index, &pc); - cs_index_to_ijk(psi->cs, index, coords); + /* Calculate divergence based on the stencil */ + for (int p = 1; p < s->npoints; p++) { - for (ia = 0; ia < 3; ia++) { - force[ia] = 0.0; - } - - /* Calculate divergence based on D3QX stencil */ - for (p = 1; p < PSI_NGRAD; p++) { - - coords_nb[X] = coords[X] + psi_gr_cv[p][X]; - coords_nb[Y] = coords[Y] + psi_gr_cv[p][Y]; - coords_nb[Z] = coords[Z] + psi_gr_cv[p][Z]; - - index_nb = cs_index(psi->cs, coords_nb[X], coords_nb[Y], coords_nb[Z]); - map_status(map, index_nb, &status_nb); - - if (status != MAP_FLUID) { + int8_t cx = s->cv[p][X]; + int8_t cy = s->cv[p][Y]; + int8_t cz = s->cv[p][Z]; + int index1 = cs_index(cs, ic + cx, jc + cy, kc + cz); + double pth[3][3] = {0}; - fe_electro_stress_ex(fe, index_nb, pth_nb); + fe->func->stress(fe, index1, pth); - for (ia = 0; ia < 3; ia++) { - for (ib = 0; ib < 3; ib++) { - force[ia] -= psi_gr_wv[p] * psi_gr_rcs2 * pth_nb[ia][ib] * psi_gr_cv[p][ib]; - } + for (int ia = 0; ia < 3; ia++) { + for (int ib = 0; ib < 3; ib++) { + force[ia] -= s->wgradients[p]*pth[ia][ib]*s->cv[p][ib]; } - } - - if (status == MAP_FLUID && status_nb == MAP_FLUID) { - - fe_electro_stress(fe, index_nb, pth_nb); - - for (ia = 0; ia < 3; ia++) { - force[ia] -= psi_gr_wv[p] * psi_gr_rcs2 * pth_nb[ia][X] * psi_gr_cv[p][X]; - force[ia] -= psi_gr_wv[p] * psi_gr_rcs2 * pth_nb[ia][Y] * psi_gr_cv[p][Y]; - force[ia] -= psi_gr_wv[p] * psi_gr_rcs2 * pth_nb[ia][Z] * psi_gr_cv[p][Z]; - } - } - - if (status == MAP_FLUID && status_nb != MAP_FLUID) { - - /* Current site r */ - fe_electro_stress(fe, index, pth); - - /* Site r - cv */ - coords1[X] = coords[X] - psi_gr_cv[p][X]; - coords1[Y] = coords[Y] - psi_gr_cv[p][Y]; - coords1[Z] = coords[Z] - psi_gr_cv[p][Z]; - - index1 = cs_index(psi->cs, coords1[X], coords1[Y], coords1[Z]); - - fe_electro_stress(fe, index1, pth1); - - /* Subtract the above 'fluid' half of the incomplete two-point formula. */ - /* Note: subtracting means adding here because of inverse lattice vectors. */ - for (ia = 0; ia < 3; ia++) { - force[ia] -= psi_gr_wv[p] * psi_gr_rcs2 * pth1[ia][X] * psi_gr_cv[p][X]; - force[ia] -= psi_gr_wv[p] * psi_gr_rcs2 * pth1[ia][Y] * psi_gr_cv[p][Y]; - force[ia] -= psi_gr_wv[p] * psi_gr_rcs2 * pth1[ia][Z] * psi_gr_cv[p][Z]; - } - - /* Site r - 2*cv */ - coords2[X] = coords[X] - 2*psi_gr_cv[p][X]; - coords2[Y] = coords[Y] - 2*psi_gr_cv[p][Y]; - coords2[Z] = coords[Z] - 2*psi_gr_cv[p][Z]; - - index2 = cs_index(psi->cs, coords2[X], coords2[Y], coords2[Z]); - - fe_electro_stress(fe, index2, pth2); - - /* Use one-sided derivative instead */ - for (ia = 0; ia < 3; ia++) { - for (ib = 0; ib < 3; ib++) { - force[ia] -= psi_gr_wv[p] * psi_gr_rcs2 * - (3.0*pth[ia][ib] - 4.0*pth1[ia][ib] + 1.0*pth2[ia][ib]) - * psi_gr_rnorm[p]* psi_gr_cv[p][ib]; - } - } - - } - - } + /* Store the force on the colloid or on the lattice */ - /* Store the force on lattice */ if (pc) { pc->force[X] += force[X]; pc->force[Y] += force[Y]; diff --git a/src/psi_force.h b/src/psi_force.h index d3cbfee0..7ea1c8bc 100644 --- a/src/psi_force.h +++ b/src/psi_force.h @@ -2,20 +2,17 @@ * * psi_force.h * - * $Id$ - * * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * Contributing Authors: - * Kevin Stratford (kevin@epcc.ed.ac.uk) + * (c) 2013-2023 The University of Edinburgh * - * (c) 2013-2016 The University of Edinburgh + * Kevin Stratford (kevin@epcc.ed.ac.uk) * *****************************************************************************/ -#ifndef PSI_FORCE_H -#define PSI_FORCE_H +#ifndef LUDWIG_PSI_FORCE_H +#define LUDWIG_PSI_FORCE_H #include "psi.h" #include "free_energy.h" @@ -24,12 +21,8 @@ #include "map.h" int psi_force_gradmu(psi_t * psi, fe_t * fe, field_t * phi, hydro_t * hydro, - map_t * map, colloids_info_t * cinfo); + map_t * map, colloids_info_t * cinfo); int psi_force_divstress(psi_t * psi, fe_t * fe, hydro_t * hydro, colloids_info_t * cinfo); -int psi_force_divstress_d3qx(psi_t * psi, fe_t * fe, hydro_t * hydro, - map_t * map, colloids_info_t * cinfo); -int psi_force_divstress_one_sided_d3qx(psi_t * psi, hydro_t * hydro, - map_t * map, colloids_info_t * cinfo); #endif diff --git a/src/psi_gradients.c b/src/psi_gradients.c index 07d5ca16..21bec91e 100644 --- a/src/psi_gradients.c +++ b/src/psi_gradients.c @@ -2,8 +2,9 @@ * * psi_gradients.c * - * Finite difference stencils used in the - * electrokinetic routines. + * Currently just routines for the electric field, aka, the gradient + * of the potential. + * * * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre @@ -15,461 +16,52 @@ ****************************************************************************/ #include + #include "coords.h" #include "psi_gradients.h" -#include "fe_electro_symmetric.h" - -#if defined NP_D3Q18 -const int psi_gr_cv[PSI_NGRAD][3] = {{ 0, 0, 0}, - { 1, 1, 0}, { 1, 0, 1}, { 1, 0, 0}, - { 1, 0, -1}, { 1, -1, 0}, { 0, 1, 1}, - { 0, 1, 0}, { 0, 1, -1}, { 0, 0, 1}, - { 0, 0, -1}, { 0, -1, 1}, { 0, -1, 0}, - { 0, -1, -1}, {-1, 1, 0}, {-1, 0, 1}, - {-1, 0, 0}, {-1, 0, -1}, {-1, -1, 0}}; - - -#define w0 (12.0/36.0) -#define w1 (2.0/36.0) -#define w2 (1.0/36.0) - -#define sqrt2 1.4142135623730951 - -const double psi_gr_wv[PSI_NGRAD] = {w0, - w2, w2, w1, w2, w2, w2, - w1, w2, w1, w1, w2, w1, - w2, w2, w2, w1, w2, w2}; - -const double psi_gr_rnorm[PSI_NGRAD] = {0.0, - 1.0/sqrt2, 1.0/sqrt2, 1.0, 1.0/sqrt2, 1.0/sqrt2, 1.0/sqrt2, - 1.0, 1.0/sqrt2, 1.0, 1.0, 1.0/sqrt2, 1.0, - 1.0/sqrt2, 1.0/sqrt2, 1.0/sqrt2, 1.0, 1.0/sqrt2, 1.0/sqrt2}; - -const double psi_gr_rcs2 = 3.0; - -#elif defined NP_D3Q26 - -const int psi_gr_cv[PSI_NGRAD][3] = {{ 0, 0, 0}, - {-1,-1,-1}, {-1,-1, 0}, {-1,-1, 1}, - {-1, 0,-1}, {-1, 0, 0}, {-1, 0, 1}, - {-1, 1,-1}, {-1, 1, 0}, {-1, 1, 1}, - { 0,-1,-1}, { 0,-1, 0}, { 0,-1, 1}, - { 0, 0,-1}, { 0, 0, 1}, - { 0, 1,-1}, { 0, 1, 0}, { 0, 1, 1}, - { 1,-1,-1}, { 1,-1, 0}, { 1,-1, 1}, - { 1, 0,-1}, { 1, 0, 0}, { 1, 0, 1}, - { 1, 1,-1}, { 1, 1, 0}, { 1, 1, 1}}; - - -#define w0 (8.0/27.0) -#define w1 (2.0/27.0) -#define w2 (1.0/54.0) -#define w3 (1.0/216.0) - -#define sqrt2 1.4142135623730951 -#define sqrt3 1.7320508075688772 - -const double psi_gr_wv[PSI_NGRAD] = {w0, - w3, w2, w3, - w2, w1, w2, - w3, w2, w3, - w2, w1, w2, - w1, w1, - w2, w1, w2, - w3, w2, w3, - w2, w1, w2, - w3, w2, w3}; - -const double psi_gr_rnorm[PSI_NGRAD] = {0.0, - 1.0/sqrt3, 1.0/sqrt2, 1.0/sqrt3, - 1.0/sqrt2, 1.0, 1.0/sqrt2, - 1.0/sqrt3, 1.0/sqrt2, 1.0/sqrt3, - 1.0/sqrt2, 1.0, 1.0/sqrt2, - 1.0, 1.0, - 1.0/sqrt2, 1.0, 1.0/sqrt2, - 1.0/sqrt3, 1.0/sqrt2, 1.0/sqrt3, - 1.0/sqrt2, 1.0, 1.0/sqrt2, - 1.0/sqrt3, 1.0/sqrt2, 1.0/sqrt3}; - -const double psi_gr_rcs2 = 3.0; - - -#else - -/* NP_D3Q6 */ - -const int psi_gr_cv[PSI_NGRAD][3] = {{ 0, 0, 0}, - {-1, 0, 0}, { 0, -1, 0}, { 0, 0, -1}, - { 1, 0, 0}, { 0, 1, 0}, { 0, 0, 1}}; - -#define w0 (0.0) -#define w1 (1.0/6.0) - -const double psi_gr_wv[PSI_NGRAD] = {w0, - w1, w1, w1, - w1, w1, w1}; - -const double psi_gr_rnorm[PSI_NGRAD] = {0.0, - 1.0, 1.0, 1.0, - 1.0, 1.0, 1.0}; - -const double psi_gr_rcs2 = 3.0; -#endif - /***************************************************************************** * * psi_electric_field * * Return the electric field associated with the current potential. - * - * The gradient of the potential is differenced as - * E_x = - (1/2) [ psi(i+1,j,k) - psi(i-1,j,k ] - * etc - * - * TODO: The assert(0) indicates that there is no test of this code. + * E_a = - \nabla_a \psi * *****************************************************************************/ int psi_electric_field(psi_t * psi, int index, double e[3]) { - int nsites; - int xs, ys, zs; + int ijk[3] = {0}; + cs_t * cs = NULL; + stencil_t * s = NULL; assert(psi); - assert(0); /* NO TEST? */ - - cs_nsites(psi->cs, &nsites); - cs_strides(psi->cs, &xs, &ys, &zs); - - e[X] = -0.5*(psi->psi->data[addr_rank0(nsites, index + xs)] - - psi->psi->data[addr_rank0(nsites, index - xs)]); - e[Y] = -0.5*(psi->psi->data[addr_rank0(nsites, index + ys)] - - psi->psi->data[addr_rank0(nsites, index - ys)]); - e[Z] = -0.5*(psi->psi->data[addr_rank0(nsites, index + zs)] - - psi->psi->data[addr_rank0(nsites, index - zs)]); - - return 0; -} - -/***************************************************************************** - * - * psi_electric_field_d3qx - * - * Return the electric field associated with the current potential. - * - * The gradient of the potential is differenced with wider stencil - * that includes nearest and next-to-nearest neighbours. - * - *****************************************************************************/ - -int psi_electric_field_d3qx(psi_t * psi, int index, double e[3]) { - int p; - int nsites; - int coords[3], coords_nbr[3], index_nbr; - double aux; - - assert(psi); + cs = psi->cs; + s = psi->stencil; + assert(cs); + assert(s); - cs_nsites(psi->cs, &nsites); - cs_index_to_ijk(psi->cs, index, coords); + cs_index_to_ijk(cs, index, ijk); - e[X] = 0; + e[X] = 0; e[Y] = 0; e[Z] = 0; - for (p = 1; p < PSI_NGRAD; p++) { - - coords_nbr[X] = coords[X] + psi_gr_cv[p][X]; - coords_nbr[Y] = coords[Y] + psi_gr_cv[p][Y]; - coords_nbr[Z] = coords[Z] + psi_gr_cv[p][Z]; - - index_nbr = cs_index(psi->cs, coords_nbr[X], coords_nbr[Y], coords_nbr[Z]); - aux = psi_gr_wv[p]* psi_gr_rcs2 * psi->psi->data[addr_rank0(nsites, index_nbr)]; - - e[X] -= aux * psi_gr_cv[p][X]; - e[Y] -= aux * psi_gr_cv[p][Y]; - e[Z] -= aux * psi_gr_cv[p][Z]; - - } - - return 0; -} - -/***************************************************************************** - * - * psi_grad_rho - * - * Returns the gradient of the charge densities of each species. - * - * The gradient of the potential is differenced as - * grad_rho_x = (1/2) [ psi->rho(i+1,j,k) - psi->rho(i-1,j,k)] - * etc. - * - * If we are next to a boundary site we use one-sided derivatives. - * - * Boundary to the left: - * grad_rho_x = -3/2*psi->rho(i,j,k)+2*psi->rho(i+1,k,j)-1/2*psi->rho(i+2,k,j) - * etc. - * Boundary to the right: - * grad_rho_x = 3/2*psi->rho(i,j,k)-2*psi->rho(i-1,k,j)+1/2*psi->rho(i-2,k,j) - * etc. - * - * TODO: The assert(0) indicates there is no test in place. - * - *****************************************************************************/ - -int psi_grad_rho(psi_t * psi, map_t * map, int index, int n, double grad_rho[3]) { - - int nsites; - int xs, ys, zs; - int index0, index1; - int status0, status1; - - double * rhodata = psi->rho->data; - - assert(psi); - assert(n < psi->nk); - assert(grad_rho); - assert(0); /* NO TEST? */ - - cs_nsites(psi->cs, &nsites); - cs_strides(psi->cs, &xs, &ys, &zs); - - grad_rho[X] = 0.0; - grad_rho[Y] = 0.0; - grad_rho[Z] = 0.0; - - /* x-direction */ - index0 = index - xs; - map_status(map, index0, &status0); - index1 = index + xs; - map_status(map, index1, &status1); + for (int p = 1; p < s->npoints; p++) { - if (status0 == MAP_FLUID && status1 == MAP_FLUID){ - grad_rho[X] = 0.5*(rhodata[addr_rank1(nsites, psi->nk, (index + xs), n)] - - rhodata[addr_rank1(nsites, psi->nk, (index - xs), n)]); - } - if (status0 != MAP_FLUID && status1 == MAP_FLUID){ - grad_rho[X] = - 1.5*rhodata[addr_rank1(nsites, psi->nk, index, n)] - + 2.0*rhodata[addr_rank1(nsites, psi->nk, (index + xs), n)] - - 0.5*rhodata[addr_rank1(nsites, psi->nk, (index + 2*xs), n)]; - } - if (status0 == MAP_FLUID && status1 != MAP_FLUID){ - grad_rho[X] = + 1.5*rhodata[addr_rank1(nsites, psi->nk, index, n)] - - 2.0*rhodata[addr_rank1(nsites, psi->nk, (index - xs), n)] - + 0.5*rhodata[addr_rank1(nsites, psi->nk, (index - 2*xs), n)]; - } - - /* y-direction */ - index0 = index - ys; - map_status(map, index0, &status0); - index1 = index + ys; - map_status(map, index1, &status1); - - if (status0 == MAP_FLUID && status1 == MAP_FLUID) { - grad_rho[Y] - = 0.5*(+ rhodata[addr_rank1(nsites, psi->nk, (index+ys), n)] - - rhodata[addr_rank1(nsites, psi->nk, (index-ys), n)]); - } - if (status0 != MAP_FLUID && status1 == MAP_FLUID) { - grad_rho[Y] - = - 1.5*rhodata[addr_rank1(nsites, psi->nk, index, n)] - + 2.0*rhodata[addr_rank1(nsites, psi->nk, (index + ys), n)] - - 0.5*rhodata[addr_rank1(nsites, psi->nk, (index + 2*ys), n)]; - } - if (status0 == MAP_FLUID && status1 != MAP_FLUID) { - grad_rho[Y] = + 1.5*rhodata[addr_rank1(nsites, psi->nk, index, n)] - - 2.0*rhodata[addr_rank1(nsites, psi->nk, (index - ys), n)] - + 0.5*rhodata[addr_rank1(nsites, psi->nk, (index - 2*ys), n)]; - } - - /* z-direction */ - index0 = index - zs; - map_status(map, index0, &status0); - index1 = index + zs; - map_status(map, index1, &status1); - - if (status0 == MAP_FLUID && status1 == MAP_FLUID) { - grad_rho[Z] = 0.5*(rhodata[addr_rank1(nsites, psi->nk, (index+zs), n)] - - rhodata[addr_rank1(nsites, psi->nk, (index-zs), n)]); - } - if (status0 != MAP_FLUID && status1 == MAP_FLUID) { - grad_rho[Z] = - 1.5*rhodata[addr_rank1(nsites, psi->nk, index, n)] - + 2.0*rhodata[addr_rank1(nsites, psi->nk, (index + zs), n)] - - 0.5*rhodata[addr_rank1(nsites, psi->nk, (index + 2*zs), n)]; - } - if (status0 == MAP_FLUID && status1 != MAP_FLUID) { - grad_rho[Z] = + 1.5*rhodata[addr_rank1(nsites, psi->nk, index, n)] - - 2.0*rhodata[addr_rank1(nsites, psi->nk, (index - zs), n)] - + 0.5*rhodata[addr_rank1(nsites, psi->nk, (index - 2*zs), n)]; - } - - return 0; -} - - -/***************************************************************************** - * - * psi_grad_rho_d3qx - * - * Returns the gradient of the charge densities of each species. - * - * The gradient of the potential is differenced as - * grad_rho_x = (1/2) [ psi->rho(i+1,j,k) - psi->rho(i-1,j,k)] - * etc. - * - * If we are next to a boundary site we use one-sided derivatives based on - * Lagrange interpolating polynomials. - * - * Boundary to the left: - * grad_rho_x = -3/2*psi->rho(i,j,k)+2*psi->rho(i+1,k,j)-1/2*psi->rho(i+2,k,j) - * etc. - * Boundary to the right: - * grad_rho_x = 3/2*psi->rho(i,j,k)-2*psi->rho(i-1,k,j)+1/2*psi->rho(i-2,k,j) - * etc. - * - * TODO: The assert(0) indicates there is no test for this code. - * - *****************************************************************************/ - -int psi_grad_rho_d3qx(psi_t * psi, map_t * map, int index, int n, double grad_rho[3]) { - - int p; - int nsites; - int coords[3], coords1[3], coords2[3]; - int index1, index2; - int status, status1, status2; - double aux; - - double * rho = psi->rho->data; - - assert(psi); - assert(n < psi->nk); - assert(grad_rho); - assert(0); /* NO TEST? */ - - cs_nsites(psi->cs, &nsites); - cs_index_to_ijk(psi->cs, index, coords); - map_status(map, index, &status); - - grad_rho[X] = 0; - grad_rho[Y] = 0; - grad_rho[Z] = 0; - - for (p = 1; p < PSI_NGRAD; p++) { - - coords1[X] = coords[X] + psi_gr_cv[p][X]; - coords1[Y] = coords[Y] + psi_gr_cv[p][Y]; - coords1[Z] = coords[Z] + psi_gr_cv[p][Z]; + int8_t cx = s->cv[p][X]; + int8_t cy = s->cv[p][Y]; + int8_t cz = s->cv[p][Z]; - index1 = cs_index(psi->cs, coords1[X], coords1[Y], coords1[Z]); - map_status(map, index1, &status1); + int index1 = cs_index(cs, ijk[X] + cx, ijk[Y] + cy, ijk[Z] + cz); + double psi0 = psi->psi->data[addr_rank0(psi->nsites, index1)]; - if(status == MAP_FLUID && status1 == MAP_FLUID) { - - aux = psi_gr_wv[p]* psi_gr_rcs2 * rho[psi->nk*index1 + n]; - - grad_rho[X] += aux * psi_gr_cv[p][X]; - grad_rho[Y] += aux * psi_gr_cv[p][Y]; - grad_rho[Z] += aux * psi_gr_cv[p][Z]; - - } - - else { - - coords1[X] = coords[X] - psi_gr_cv[p][X]; - coords1[Y] = coords[Y] - psi_gr_cv[p][Y]; - coords1[Z] = coords[Z] - psi_gr_cv[p][Z]; - index1 = cs_index(psi->cs, coords1[X], coords1[Y], coords1[Z]); - map_status(map, index1, &status1); - - coords2[X] = coords[X] - 2*psi_gr_cv[p][X]; - coords2[Y] = coords[Y] - 2*psi_gr_cv[p][Y]; - coords2[Z] = coords[Z] - 2*psi_gr_cv[p][Z]; - index2 = cs_index(psi->cs, coords2[X], coords2[Y], coords2[Z]); - map_status(map, index2, &status2); - - if(status == MAP_FLUID && status1 == MAP_FLUID && status2 == MAP_FLUID) { - - /* Subtract the above 'fluid' half of the incomplete two-point formula. */ - /* Note: subtracting means adding here because of inverse lattice vectors. */ - - aux = psi_gr_wv[p]*psi_gr_rcs2*rho[addr_rank1(nsites,psi->nk,index1,n)]; - - grad_rho[X] += aux * psi_gr_cv[p][X]; - grad_rho[Y] += aux * psi_gr_cv[p][Y]; - grad_rho[Z] += aux * psi_gr_cv[p][Z]; - - /* Use one-sided derivative instead */ - grad_rho[X] += psi_gr_wv[p] * psi_gr_rcs2 * - (3.0*rho[addr_rank1(nsites, psi->nk, index, n)] - - 4.0*rho[addr_rank1(nsites, psi->nk, index1, n)] - + 1.0*rho[addr_rank1(nsites, psi->nk, index2, n)]) - * psi_gr_rnorm[p]* psi_gr_cv[p][X]; - - grad_rho[Y] += psi_gr_wv[p] * psi_gr_rcs2 * - (3.0*rho[addr_rank1(nsites, psi->nk, index, n)] - - 4.0*rho[addr_rank1(nsites, psi->nk, index1, n)] - + 1.0*rho[addr_rank1(nsites, psi->nk, index2, n)]) - * psi_gr_rnorm[p] * psi_gr_cv[p][Y]; - - grad_rho[Z] += psi_gr_wv[p] * psi_gr_rcs2 * - (3.0*rho[addr_rank1(nsites, psi->nk, index, n)] - - 4.0*rho[addr_rank1(nsites, psi->nk, index1, n)] - + 1.0*rho[addr_rank1(nsites, psi->nk, index2, n)]) - * psi_gr_rnorm[p] * psi_gr_cv[p][Z]; - } - } + /* E_a = -\nabla_a psi */ + e[X] -= s->wgradients[p]*cx*psi0; + e[Y] -= s->wgradients[p]*cy*psi0; + e[Z] -= s->wgradients[p]*cz*psi0; } return 0; } - -/***************************************************************************** - * - * psi_grad_eps_d3qx - * - * TODO: This could be tested in psi_sor_vare_poisson() (see psi_sor.h) - * - *****************************************************************************/ - -int psi_grad_eps_d3qx(psi_t * psi, fe_t * fe, f_vare_t fepsilon, int index, - double grad_eps[3]) { - - int p; - int coords[3], coords1[3]; - int index1; - double aux, eps1; - - assert(psi); - assert(fepsilon); - assert(grad_eps); - - cs_index_to_ijk(psi->cs, index, coords); - - grad_eps[X] = 0; - grad_eps[Y] = 0; - grad_eps[Z] = 0; - - for (p = 1; p < PSI_NGRAD; p++) { - - coords1[X] = coords[X] + psi_gr_cv[p][X]; - coords1[Y] = coords[Y] + psi_gr_cv[p][Y]; - coords1[Z] = coords[Z] + psi_gr_cv[p][Z]; - - index1 = cs_index(psi->cs, coords1[X], coords1[Y], coords1[Z]); - fepsilon(fe, index1, &eps1); - - aux = psi_gr_wv[p]* psi_gr_rcs2 * eps1; - - grad_eps[X] += aux * psi_gr_cv[p][X]; - grad_eps[Y] += aux * psi_gr_cv[p][Y]; - grad_eps[Z] += aux * psi_gr_cv[p][Z]; - - } - - return 0; -} - diff --git a/src/psi_gradients.h b/src/psi_gradients.h index 297da553..88ec6198 100644 --- a/src/psi_gradients.h +++ b/src/psi_gradients.h @@ -5,46 +5,19 @@ * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2014-2018 The University of Edinburgh + * (c) 2014-2023 The University of Edinburgh * * Contributing authors: - * Oliver Henrich (ohenrich@epcc.ed.ac.uk) + * Oliver Henrich (ohenrich@epcc.ed.ac.uk) (now U. Strathclyde) + * Kevin Stratford (kevin@epcc.ed.ac.uk) * ****************************************************************************/ #ifndef LUDWIG_PSI_GRADIENTS_H #define LUDWIG_PSI_GRADIENTS_H -#include "map.h" #include "psi.h" -#include "fe_electro_symmetric.h" - -#if defined NP_D3Q18 - -#define PSI_NGRAD 19 - -#elif defined NP_D3Q26 - -#define PSI_NGRAD 27 - -#else - -/* Default to 7-point stencil */ - -#define PSI_NGRAD 7 - -#endif - -extern const int psi_gr_cv[PSI_NGRAD][3]; -extern const double psi_gr_wv[PSI_NGRAD]; -extern const double psi_gr_rnorm[PSI_NGRAD]; -extern const double psi_gr_rcs2; int psi_electric_field(psi_t * psi, int index, double e[3]); -int psi_electric_field_d3qx(psi_t * psi, int index, double e[3]); -int psi_grad_rho_d3qx(psi_t * obj, map_t * map, int index, int n, - double grad_rho[3]); -int psi_grad_eps_d3qx(psi_t * psi, fe_t * fe, f_vare_t fepsilon, int index, - double grad_eps[3]); #endif diff --git a/src/psi_petsc.c b/src/psi_petsc.c index e310b7c1..885950b8 100644 --- a/src/psi_petsc.c +++ b/src/psi_petsc.c @@ -7,572 +7,337 @@ * * This uses the PETSc library. * - * The Poisson equation homogeneous permittivity looks like + * The Poisson equation with homogeneous permittivity looks like * * nabla^2 \psi = - rho_elec / epsilon * * where psi is the potential, rho_elec is the free charge density, and - * epsilon is a permeability. + * epsilon is a permittivity. + * + * There is also a version for non-uniform dielectric. + * * * * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2013-2017 The University of Edinburgh + * (c) 2013-2023 The University of Edinburgh * * Contributing Authors: - * Oliver Henrich (ohenrich@epcc.ed.ac.uk) - * Kevin Stratford (kevin@epcc.ed.ac.uk) + * Oliver Henrich (now U. Strathclyde) + * Kevin Stratford (kevin@epcc.ed.ac.uk) * *****************************************************************************/ #include #include #include -#include - -#ifdef PETSC #include "pe.h" #include "coords.h" -#include "control.h" -#include "psi.h" -#include "psi_gradients.h" #include "psi_petsc.h" -#include "petscksp.h" -#include "petscdmda.h" -DM da; /* distributed array */ -Vec x,b; /* approx solution, RHS */ -Mat A; /* linear system matrix */ -MatNullSpace nullsp; /* null space of matrix */ -KSP ksp; /* linear solver context */ -PC pc; /* preconditioner context */ -PetscReal norm; /* norm of solution error */ -int i,j,its; +static psi_solver_vt_t vt_ = { + (psi_solver_free_ft) psi_solver_petsc_free, + (psi_solver_solve_ft) psi_solver_petsc_solve +}; + +static psi_solver_vt_t vart_ = { + (psi_solver_free_ft) psi_solver_petsc_free, + (psi_solver_solve_ft) psi_solver_petsc_var_epsilon_solve +}; + +int psi_solver_petsc_initialise(psi_t * psi, psi_solver_petsc_t * solver); +int psi_solver_petsc_matrix_set(psi_solver_petsc_t * solver); +int psi_solver_petsc_rhs_set(psi_solver_petsc_t * solver); +int psi_solver_petsc_psi_to_da(psi_solver_petsc_t * solver); +int psi_solver_petsc_da_to_psi(psi_solver_petsc_t * solver); + +int psi_solver_petsc_var_epsilon_initialise(psi_t * psi, var_epsilon_t epsilon, + psi_solver_petsc_t * solver); +int psi_solver_petsc_var_epsilon_matrix_set(psi_solver_petsc_t * solver); +int psi_solver_petsc_var_epsilon_rhs_set(psi_solver_petsc_t * solver); -int view_matrix = 0; -int view_vector = 0; /***************************************************************************** * - * psi_petsc_init - * - * Initialises PETSc vectors, matrices and KSP solver context + * psi_solver_petsc_create * *****************************************************************************/ -int psi_petsc_init(psi_t * obj, fe_t * fe, f_vare_t fepsilon){ - - MPI_Comm new_comm; - int new_rank, nhalo; - int mpi_cartsz[3], mpi_coords[3]; - int ntotal[3]; - char version[256]; - double tol_rel; /* Relative tolerance */ - double tol_abs; /* Absolute tolerance */ - int niteration = 10000; /* Number of iterations */ - KSPType solver_type; - PCType pc_type; - PetscReal rtol, abstol, dtol; - int maxits; - - assert(obj); - assert(fe); - - /* In order for the DMDA and the Cartesian MPI communicator - to share the same part of the domain decomposition it is - necessary to renumber the process ranks of the default - PETSc communicator. Default PETSc is column major decomposition. - */ - - cs_cartsz(obj->cs, mpi_cartsz); - cs_cart_coords(obj->cs, mpi_coords); - cs_ntotal(obj->cs, ntotal); - - /* Set new rank according to PETSc ordering */ - new_rank = mpi_coords[Z]*mpi_cartsz[Y]*mpi_cartsz[X] \ - + mpi_coords[Y]*mpi_cartsz[X] + mpi_coords[X]; - - /* Create communicator with new ranks according to PETSc ordering */ - MPI_Comm_split(PETSC_COMM_WORLD, 1, new_rank, &new_comm); - - /* Override default PETSc communicator */ - PETSC_COMM_WORLD = new_comm; - - /* Create 3D distributed array */ - cs_nhalo(obj->cs, &nhalo); - - DMDACreate3d(PETSC_COMM_WORLD, - DM_BOUNDARY_PERIODIC, DM_BOUNDARY_PERIODIC, DM_BOUNDARY_PERIODIC, - DMDA_STENCIL_BOX, ntotal[X], ntotal[Y], ntotal[Z], - mpi_cartsz[X], mpi_cartsz[Y], mpi_cartsz[Z], 1, nhalo, - NULL, NULL, NULL, &da); - - PetscCall(DMSetVecType(da, VECSTANDARD)); - PetscCall(DMSetMatType(da, MATMPIAIJ)); - PetscCall(DMSetUp(da)); - - /* Create global vectors on DM */ - DMCreateGlobalVector(da,&x); - VecDuplicate(x,&b); - - /* Create matrix on DM pre-allocated according to distributed array structure */ - DMCreateMatrix(da,&A); - - /* Initialise solver context and preconditioner */ - - psi_reltol(obj, &tol_rel); - psi_abstol(obj, &tol_abs); - psi_maxits(obj, &niteration); - - KSPCreate(PETSC_COMM_WORLD,&ksp); - KSPSetOperators(ksp,A,A); - KSPSetTolerances(ksp,tol_rel,tol_abs,PETSC_DEFAULT,niteration); - KSPSetFromOptions(ksp); - KSPSetUp(ksp); - - PetscGetVersion(version, 256); - pe_info(obj->pe, "\n%s\n", version); - - KSPGetType(ksp, &solver_type); - KSPGetTolerances(ksp, &rtol, &abstol, &dtol, &maxits); - KSPGetPC(ksp, &pc); - PCGetType(pc, &pc_type); - - pe_info(obj->pe, "\nUsing Krylov subspace solver\n"); - pe_info(obj->pe, "----------------------------\n"); - pe_info(obj->pe, "Solver type %s\n", solver_type); - pe_info(obj->pe, "Tolerances rtol %g abstol %g maxits %d\n", rtol, abstol, maxits); - pe_info(obj->pe, "Preconditioner type %s\n", pc_type); - - if (fepsilon == NULL) psi_petsc_compute_laplacian(obj); - if (fepsilon != NULL) psi_petsc_compute_matrix(obj, (fe_es_t *) fe, fepsilon); +int psi_solver_petsc_create(psi_t * psi, psi_solver_petsc_t ** solver) { - return 0; + int ifail = -1; /* Check PETSC is available */ + int isInitialised = 0; + + PetscInitialised(&isInitialised); + + if (isInitialised) { + psi_solver_petsc_t * petsc = NULL; + + petsc = (psi_solver_petsc_t *) calloc(1, sizeof(psi_solver_petsc_t)); + assert(petsc); + + if (petsc != NULL) { + /* initialise ... */ + petsc->super.impl = &vt_; + petsc->psi = psi; + ifail = psi_solver_petsc_initialise(psi, petsc); + if (ifail != 0) free(petsc); + if (ifail == 0) *solver = petsc; + } + } + + return ifail; } /***************************************************************************** * - * psi_petsc_compute_laplacian - * - * Computes the Laplacian for KSP solver. - * Note that this routine uses the PETSc stencil structure, which permits - * local assembly of the matrix. + * psi_solver_petsc_var_epsilon_create * *****************************************************************************/ -int psi_petsc_compute_laplacian(psi_t * obj) { +int psi_solver_petsc_var_epsilon_create(psi_t * psi, var_epsilon_t user, + psi_solver_petsc_t ** solver) { - int i, j, k; - int xs, ys, zs, xw, yw, zw, xe, ye, ze; - double epsilon; + int ifail = -1; /* Check PETSC is available */ + int isInitialised = 0; + PetscInitialised(&isInitialised); -#if defined NP_D3Q26 - double v[27]; - MatStencil row, col[27]; - const double r10 = 0.1; - const double r30 = (1.0/30.0); - const double r15_7 = (7.0/15.0); - const double r15_64 = (64.0/15.0); + if (isInitialised) { + psi_solver_petsc_t * petsc = NULL; -#elif defined NP_D3Q18 - double v[19]; - MatStencil row, col[19]; - const double r3 = (1.0/3.0); - const double r6 = (1.0/6.0); + petsc = (psi_solver_petsc_t *) calloc(1, sizeof(psi_solver_petsc_t)); + assert(petsc); -#else - /* NP_D3Q6 is the default */ - double v[7]; - MatStencil row, col[7]; -#endif + if (petsc != NULL) { + /* initialise ... */ + petsc->super.impl = &vart_; + petsc->psi = psi; + petsc->fe = user.fe; + petsc->epsilon = user.epsilon; + ifail = psi_solver_petsc_initialise(psi, petsc); + if (ifail != 0) free(petsc); + if (ifail == 0) *solver = petsc; + } + } - assert(obj); + return ifail; +} - /* Get details of the distributed array data structure. - The PETSc directives return global indices, but - every process works only on its local copy. */ +/***************************************************************************** + * + * psi_solver_petsc_free + * + *****************************************************************************/ - DMDAGetCorners(da,&xs,&ys,&zs,&xw,&yw,&zw); +int psi_solver_petsc_free(psi_solver_petsc_t ** solver) { - xe = xs + xw; - ye = ys + yw; - ze = zs + zw; + assert(solver && *solver); - psi_epsilon(obj, &epsilon); + free(*solver); + *solver = NULL; - /* 3D-Laplacian with periodic BCs */ - for(k=zs; kpe, "\nPETSc output matrix\n"); - PetscViewer viewer; - PetscViewerASCIIOpen(PETSC_COMM_WORLD, "matrix.log", &viewer); -#if PETSC_VERSION_GE(3, 7, 0) - PetscViewerPushFormat(viewer, PETSC_VIEWER_ASCII_INDEX); - PetscViewerPopFormat(viewer); #else - PetscViewerSetFormat(viewer, PETSC_VIEWER_ASCII_INDEX); -#endif - MatView(A,viewer);; - PetscViewerDestroy(&viewer); - } - return 0; -} +#include "petscdmda.h" +#include "petscksp.h" + +/* Here's the internal state. */ + +struct psi_solver_petsc_block_s { + DM da; /* Domain management */ + Mat a; /* System matrix */ + Vec x; /* Unknown (potential) */ + Vec b; /* Right-hand side */ + KSP ksp; /* Krylov solver context */ +}; /***************************************************************************** * - * psi_petsc_compute_matrix - * - * Computes the matrix for KSP solver for a system with dielectric contrast. - * Note that this routine uses the PETSc stencil structure, which permits - * local assembly of the matrix. + * psi_solver_petsc_initialise * *****************************************************************************/ -int psi_petsc_compute_matrix(psi_t * obj, fe_es_t * fe, f_vare_t fepsilon) { - - int ic, jc, kc, p; - int index; - int noffset[3]; - int i, j, k, ia; - int xs, ys, zs, xw, yw, zw, xe, ye, ze; - double eps, grad_eps[3]; - -#if defined NP_D3Q26 - double v[27]; - MatStencil row, col[27]; - - const double r10 = 0.1; - const double r30 = (1.0/30.0); - const double r15_7 = (7.0/15.0); - const double r15_64 = (64.0/15.0); - - const double matval[27] = {r15_64, - -r30, -r10 , -r30, - -r10, -r15_7, -r10, - -r30, -r10 , -r30, - -r10, -r15_7, -r10, - -r15_7, -r15_7, - -r10, -r15_7, -r10, - -r30, -r10 , -r30, - -r10, -r15_7, -r10, - -r30, -r10 , -r30}; - -#elif defined NP_D3Q18 - double v[19]; - MatStencil row, col[19]; - - const double r3 = (1.0/3.0); - const double r6 = (1.0/6.0); - const double matval[19] = {4.0, - -r6, -r6, -r3, - -r6, -r6, -r6, - -r3, -r6, -r3, - -r3, -r6, -r3, - -r6, -r6, -r6, - -r3, -r6, -r6}; +int psi_solver_petsc_initialise(psi_t * psi, psi_solver_petsc_t * solver) { -#else - /* NP_D3Q6 is the default */ - double v[7]; - MatStencil row, col[7]; - const double matval[7] = {6.0, - -1.0, -1.0, -1.0, - -1.0, -1.0, -1.0}; -#endif + assert(psi); + assert(solver); - assert(obj); - assert(fe); - assert(fepsilon); + { + size_t sz = sizeof(psi_solver_petsc_block_t); + solver->block = (psi_solver_petsc_block_t *) calloc(1, sz); + assert(solver->block); + if (solver->block == NULL) return -1; + } - /* Get details of the distributed array data structure. - The PETSc directives return global indices, but - every process works only on its local copy. */ + /* In order for the DMDA and the Cartesian MPI communicator + * to share the same part of the domain decomposition it is + * necessary to renumber the process ranks of the default + * PETSc communicator. Default PETSc is column major decomposition. */ + + { + cs_t * cs = psi->cs; + int coords[3] = {0}; + int cartsz[3] = {0}; + int ntotal[3] = {0}; + int nhalo = -1; + int rank = -1; + + MPI_Comm comm = MPI_COMM_NULL; + DMBoundaryType periodic = DM_BOUNDARY_PERIODIC; + + cs_cartsz(cs, cartsz); + cs_cart_coords(cs, coords); + cs_nhalo(cs, &nhalo); + cs_ntotal(cs, ntotal); + + /* Set new rank according to PETSc ordering */ + /* Create communicator with new ranks according to PETSc ordering */ + /* Override default PETSc communicator */ + + rank = coords[Z]*cartsz[Y]*cartsz[X] + coords[Y]*cartsz[X] + coords[X]; + MPI_Comm_split(PETSC_COMM_WORLD, 1, rank, &comm); + PETSC_COMM_WORLD = comm; + + /* Create 3D distributed array (always periodic) */ + + DMDACreate3d(PETSC_COMM_WORLD, periodic, periodic, periodic, + DMDA_STENCIL_BOX, ntotal[X], ntotal[Y], ntotal[Z], + cartsz[X], cartsz[Y], cartsz[Z], 1, nhalo, + NULL, NULL, NULL, &solver->block->da); + + PetscCall(DMSetVecType(solver->block->da, VECSTANDARD)); + PetscCall(DMSetMatType(solver->block->da, MATMPIAIJ)); + PetscCall(DMSetUp(solver->block->da)); + } - DMDAGetCorners(da,&xs,&ys,&zs,&xw,&yw,&zw); + /* Create global vectors and matrix */ - xe = xs + xw; - ye = ys + yw; - ze = zs + zw; + DMCreateMatrix(solver->block->da, &solver->block->a); + DMCreateGlobalVector(solver->block->da, &solver->block->x); + VecDuplicate(solver->block->x, &solver->block->b); - cs_nlocal_offset(obj->cs, noffset); + /* Initialise solver context */ - /* 3D-operator with periodic BCs */ - for(k=zs; kcs, ic, jc, kc); - - fepsilon(fe, index, &eps); - psi_grad_eps_d3qx(obj, (fe_t *) fe, fepsilon, index, grad_eps); - - for (p = 0; p < PSI_NGRAD; p++){ + { + PetscReal abstol = psi->solver.abstol; + PetscReal rtol = psi->solver.reltol; + PetscInt maxits = psi->solver.maxits; - /* Laplacian part of operator */ - v[p] = matval[p] * eps; + KSPCreate(PETSC_COMM_WORLD, &solver->block->ksp); + KSPSetOperators(solver->block->ksp, solver->block->a, solver->block->a); + KSPSetTolerances(solver->block->ksp, rtol, abstol, PETSC_DEFAULT, maxits); + } - /* Addtional terms in generalised Poisson equation */ - for(ia = 0; ia < 3; ia++){ - v[p] -= grad_eps[ia] * psi_gr_wv[p] * psi_gr_rcs2 * psi_gr_cv[p][ia]; - } + /* Not required in var-epsilon case, but no harm. */ + psi_solver_petsc_matrix_set(solver); - } - - MatSetValuesStencil(A,1,&row,27,col,v,INSERT_VALUES); - -#elif defined NP_D3Q18 - /* 19-point stencil */ - col[0].i = row.i; col[0].j = row.j; col[0].k = row.k; - col[1].i = i+1; col[1].j = j+1; col[1].k = k; - col[2].i = i+1; col[2].j = j; col[2].k = k+1; - col[3].i = i+1; col[3].j = j; col[3].k = k; - col[4].i = i+1; col[4].j = j; col[4].k = k-1; - col[5].i = i+1; col[5].j = j-1; col[5].k = k; - col[6].i = i; col[6].j = j+1; col[6].k = k+1; - col[7].i = i; col[7].j = j+1; col[7].k = k; - col[8].i = i; col[8].j = j+1; col[8].k = k-1; - col[9].i = i; col[9].j = j; col[9].k = k+1; - col[10].i = i; col[10].j = j; col[10].k = k-1; - col[11].i = i; col[11].j = j-1; col[11].k = k+1; - col[12].i = i; col[12].j = j-1; col[12].k = k; - col[13].i = i; col[13].j = j-1; col[13].k = k-1; - col[14].i = i-1; col[14].j = j+1; col[14].k = k; - col[15].i = i-1; col[15].j = j; col[15].k = k+1; - col[16].i = i-1; col[16].j = j; col[16].k = k; - col[17].i = i-1; col[17].j = j; col[17].k = k-1; - col[18].i = i-1; col[18].j = j-1; col[18].k = k; - - /* Laplacian part of operator */ - ic = (col[0].i + 1) - noffset[X]; - jc = (col[0].j + 1) - noffset[Y]; - kc = (col[0].k + 1) - noffset[Z]; - index = cs_index(obj->cs, ic, jc, kc); - - fepsilon(fe, index, &eps); - psi_grad_eps_d3qx(obj, (fe_t *) fe, fepsilon, index, grad_eps); - - for (p = 0; p < PSI_NGRAD; p++){ + KSPSetUp(solver->block->ksp); - /* Laplacian part of operator */ - v[p] = matval[p] * eps; + return 0; +} - /* Addtional terms in generalised Poisson equation */ - for(ia = 0; ia < 3; ia++){ - v[p] -= grad_eps[ia] * psi_gr_wv[p] * psi_gr_rcs2 * psi_gr_cv[p][ia]; - } +/***************************************************************************** + * + * psi_solver_petsc_matrix_set + * + *****************************************************************************/ - } +int psi_solver_petsc_matrix_set(psi_solver_petsc_t * solver) { - MatSetValuesStencil(A,1,&row,19,col,v,INSERT_VALUES); + int xs, ys, zs; + int xw, yw, zw; + int xe, ye, ze; + double epsilon; -#else - /* 7-point stencil */ - col[0].i = row.i; col[0].j = row.j; col[0].k = row.k; - col[1].i = i-1; col[1].j = j; col[1].k = k; - col[2].i = i; col[2].j = j-1; col[2].k = k; - col[3].i = i; col[3].j = j; col[3].k = k-1; - col[4].i = i+1; col[4].j = j; col[4].k = k; - col[5].i = i; col[5].j = j+1; col[5].k = k; - col[6].i = i; col[6].j = j; col[6].k = k+1; + double v[27] = {0}; /* Accomodate largest current stencil */ + MatStencil col[27] = {0}; /* Ditto */ - ic = (col[0].i + 1) - noffset[X]; - jc = (col[0].j + 1) - noffset[Y]; - kc = (col[0].k + 1) - noffset[Z]; - index = cs_index(obj->cs, ic, jc, kc); + stencil_t * s = solver->psi->stencil; - fepsilon(fe, index, &eps); - psi_grad_eps_d3qx(obj, (fe_t *) fe, fepsilon, index, grad_eps); + assert(solver); + assert(solver->psi->solver.nstencil <= 27); - for (p = 0; p < PSI_NGRAD; p++){ + /* Obtain start and width ... */ + DMDAGetCorners(solver->block->da, &xs, &ys, &zs, &xw, &yw, &zw); - /* Laplacian part of operator */ - v[p] = matval[p] * eps; + xe = xs + xw; + ye = ys + yw; + ze = zs + zw; - /* Addtional terms in generalised Poisson equation */ - for(ia = 0; ia < 3; ia++){ - v[p] -= grad_eps[ia] * psi_gr_wv[p] * psi_gr_rcs2 * psi_gr_cv[p][ia]; - } + /* 3D-Laplacian with periodic BCs */ + /* Uniform dielectric constant */ - } + psi_epsilon(solver->psi, &epsilon); - MatSetValuesStencil(A,1,&row,7,col,v,INSERT_VALUES); -#endif + for (int k = zs; k < ze; k++) { + for (int j = ys; j < ye; j++) { + for (int i = xs; i < xe; i++) { + + MatStencil row = {.i = i, .j = j, .k = k}; + for (int p = 0; p < s->npoints; p++) { + col[p].i = i + s->cv[p][X]; + col[p].j = j + s->cv[p][Y]; + col[p].k = k + s->cv[p][Z]; + v[p] = s->wlaplacian[p]*epsilon; + } + MatSetValuesStencil(solver->block->a, 1, &row, s->npoints, col, v, + INSERT_VALUES); } } } - /* Matrix assembly & halo swap */ - MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY); - MatAssemblyEnd(A,MAT_FINAL_ASSEMBLY); + /* Matrix assembly & halo swap */ /* Retain the non-zero structure of the matrix */ - MatSetOption(A,MAT_NEW_NONZERO_LOCATIONS,PETSC_FALSE); - - /* Set the matrix, preconditioner and nullspace */ - KSPSetOperators(ksp,A,A); - MatNullSpaceCreate(PETSC_COMM_WORLD, PETSC_TRUE, 0, NULL, &nullsp); -#if PETSC_VERSION_GE(3, 6, 0) - MatSetNullSpace(A, nullsp); -#else - KSPSetNullSpace(ksp, nullsp); -#endif + MatAssemblyBegin(solver->block->a, MAT_FINAL_ASSEMBLY); + MatAssemblyEnd(solver->block->a, MAT_FINAL_ASSEMBLY); + MatSetOption(solver->block->a, MAT_NEW_NONZERO_LOCATIONS, PETSC_FALSE); - MatNullSpaceDestroy(&nullsp); - KSPSetFromOptions(ksp); + /* Set the matrix, and the nullspace */ + KSPSetOperators(solver->block->ksp, solver->block->a, solver->block->a); - if (view_matrix) { - pe_info(obj->pe, "\nPETSc output matrix\n"); - PetscViewer viewer; - PetscViewerASCIIOpen(PETSC_COMM_WORLD, "matrix.log", &viewer); -#if PETSC_VERSION_GE(3, 7, 0) - PetscViewerPushFormat(viewer, PETSC_VIEWER_ASCII_INDEX); - PetscViewerPopFormat(viewer); -#else - PetscViewerSetFormat(viewer, PETSC_VIEWER_ASCII_INDEX); -#endif - MatView(A,viewer);; - PetscViewerDestroy(&viewer); + { + MatNullSpace nullsp; + MatNullSpaceCreate(PETSC_COMM_WORLD, PETSC_TRUE, 0, NULL, &nullsp); + MatSetNullSpace(solver->block->a, nullsp); + MatNullSpaceDestroy(&nullsp); } return 0; @@ -580,523 +345,564 @@ int psi_petsc_compute_matrix(psi_t * obj, fe_es_t * fe, f_vare_t fepsilon) { /***************************************************************************** * - * psi_petsc_copy_psi_to_da + * psi_solver_petsc_solve * *****************************************************************************/ -int psi_petsc_copy_psi_to_da(psi_t * obj) { +int psi_solver_petsc_solve(psi_solver_petsc_t * solver, int ntimestep) { - int ic,jc,kc,index; - int noffset[3]; - int i,j,k; - int xs,ys,zs,xw,yw,zw,xe,ye,ze; - double *** psi_3d; + assert(solver); - assert(obj); - cs_nlocal_offset(obj->cs, noffset); + psi_solver_petsc_rhs_set(solver); + psi_solver_petsc_psi_to_da(solver); - DMDAGetCorners(da,&xs,&ys,&zs,&xw,&yw,&zw); - DMDAVecGetArray(da, x, &psi_3d); + KSPSetInitialGuessNonzero(solver->block->ksp, PETSC_TRUE); + KSPSolve(solver->block->ksp, solver->block->b, solver->block->x); - xe = xs + xw; - ye = ys + yw; - ze = zs + zw; - - for (k=zs; kcs, ic,jc,kc); - psi_3d[k][j][i] = obj->psi->data[index]; - - } - } + if (ntimestep % solver->psi->solver.nfreq == 0) { + /* Report on progress of the solver. + * Note the default Petsc residual is the preconditioned L2 norm. */ + pe_t * pe = solver->psi->pe; + PetscInt its = 0; + PetscReal norm = 0.0; + PetscCall(KSPGetIterationNumber(solver->block->ksp, &its)); + PetscCall(KSPGetResidualNorm(solver->block->ksp, &norm)); + pe_info(pe, "\n"); + pe_info(pe, "Krylov solver\n"); + pe_info(pe, "Norm of residual %g at %d iterations\n", norm, its); } - DMDAVecRestoreArray(da, x, &psi_3d); - - if (view_vector) { - pe_info(obj->pe, "\nPETSc output DA vector\n"); - PetscViewer viewer; - PetscViewerASCIIOpen(PETSC_COMM_WORLD, "da.log", &viewer); -#if PETSC_VERSION_GE(3, 7, 0) - PetscViewerPushFormat(viewer, PETSC_VIEWER_ASCII_INDEX); - PetscViewerPopFormat(viewer); -#else - PetscViewerSetFormat(viewer, PETSC_VIEWER_ASCII_INDEX); -#endif - VecView(x,viewer); - PetscViewerDestroy(&viewer); - } + psi_solver_petsc_da_to_psi(solver); return 0; } /***************************************************************************** * - * psi_petsc_copy_da_to_psi + * psi_solver_petsc_rhs_set * *****************************************************************************/ -int psi_petsc_copy_da_to_psi(psi_t * obj) { +int psi_solver_petsc_rhs_set(psi_solver_petsc_t * solver) { + + cs_t * cs = NULL; + int xs, ys, zs; + int xw, yw, zw; + int xe, ye, ze; + int offset[3] = {0}; + double e0[3] = {0}; + double *** rho_3d = {0}; - int ic,jc,kc,index; - int noffset[3]; - int i,j,k; - int xs,ys,zs,xw,yw,zw,xe,ye,ze; - double *** psi_3d; + assert(solver); - assert(obj); - cs_nlocal_offset(obj->cs, noffset); + cs = solver->psi->cs; + cs_nlocal_offset(cs, offset); - DMDAGetCorners(da,&xs,&ys,&zs,&xw,&yw,&zw); - DMDAVecGetArray(da, x, &psi_3d); + DMDAGetCorners(solver->block->da, &xs, &ys, &zs, &xw, &yw, &zw); + DMDAVecGetArray(solver->block->da, solver->block->b, &rho_3d); xe = xs + xw; ye = ys + yw; ze = zs + zw; - for (k=zs; kcs, ic,jc,kc); - obj->psi->data[index] = psi_3d[k][j][i]; - + for (int k = zs; k < ze; k++) { + int kc = k - offset[Z] + 1; + for (int j = ys; j < ye; j++) { + int jc = j - offset[Y] + 1; + for (int i = xs; i < xe; i++) { + + int ic = i - offset[X] + 1; + int index = cs_index(cs, ic, jc, kc); + double rho_elec = 0.0; + /* Non-dimensional potential in Poisson eqn requires e/kT */ + double eunit = solver->psi->e; + double beta = solver->psi->beta; + + psi_rho_elec(solver->psi, index, &rho_elec); + rho_3d[k][j][i] = rho_elec*eunit*beta; } } } - DMDAVecRestoreArray(da, x, &psi_3d); - - psi_halo_psi(obj); - - return 0; -} + /* Modify right hand side for external electric field */ + /* The system must be periodic, so no need to check. */ -/***************************************************************************** - * - * psi_petsc_set_rhs - * - * Sets the right hand side of the Poisson equation and - * modifies the boundary sites if an external electric - * field is present. - * - *****************************************************************************/ + e0[X] = solver->psi->e0[X]; + e0[Y] = solver->psi->e0[Y]; + e0[Z] = solver->psi->e0[Z]; -int psi_petsc_set_rhs(psi_t * obj) { - - int ic,jc,kc,index; - int mpi_cartsz[3], mpi_coords[3]; - int ntotal[3], noffset[3]; - int periodic[3]; - int i,j,k; - int xs,ys,zs,xw,yw,zw,xe,ye,ze; - double *** rho_3d; - double rho_elec; - double eunit, beta; - double epsilon, e0[3]; - - assert(obj); - - cs_cartsz(obj->cs, mpi_cartsz); - cs_cart_coords(obj->cs, mpi_coords); - cs_nlocal_offset(obj->cs, noffset); - cs_periodic(obj->cs, periodic); - - DMDAGetCorners(da,&xs,&ys,&zs,&xw,&yw,&zw); - DMDAVecGetArray(da, b, &rho_3d); - - psi_unit_charge(obj, &eunit); - psi_beta(obj, &beta); - - xe = xs + xw; - ye = ys + yw; - ze = zs + zw; - - for (k=zs; kcs, ic,jc,kc); - psi_rho_elec(obj, index, &rho_elec); - /* Non-dimensional potential in Poisson eqn requires e/kT */ - rho_3d[k][j][i] = rho_elec * eunit * beta; - - } - } - } - - /* Modify right hand side for external electric field */ - - e0[X] = obj->e0[X]; - e0[Y] = obj->e0[Y]; - e0[Z] = obj->e0[Z]; if (e0[X] || e0[Y] || e0[Z]) { - cs_ntotal(obj->cs, ntotal); + int ntotal[3] = {0}; + int mpi_coords[3] = {0}; + int mpi_cartsz[3] = {0}; + double epsilon = 0.0; - psi_epsilon(obj, &epsilon); + cs_ntotal(cs, ntotal); + cs_cart_coords(cs, mpi_coords); + cs_cartsz(cs, mpi_cartsz); - if (periodic[X]) { + psi_epsilon(solver->psi, &epsilon); - if (mpi_coords[X] == 0) { - for (k=zs; kblock->da, solver->block->b, &rho_3d); - } - - DMDAVecRestoreArray(da, b, &rho_3d); - - if (view_vector) { - pe_info(obj->pe, "\nPETSc output RHS\n"); - PetscViewer viewer; - PetscViewerASCIIOpen(PETSC_COMM_WORLD, "rhs.log", &viewer); -#if PETSC_VERSION_GE(3, 7, 0) - PetscViewerPushFormat(viewer, PETSC_VIEWER_ASCII_INDEX); - PetscViewerPopFormat(viewer); -#else - PetscViewerSetFormat(viewer, PETSC_VIEWER_ASCII_INDEX); -#endif - VecView(b,viewer);; - PetscViewerDestroy(&viewer); - } - - return 0; + return 0; } /***************************************************************************** * - * psi_petsc_set_rhs_vare + * psi_solver_petsc_psi_to_da * - * Sets the right hand side of the Poisson equation and modifies - * the boundary sites if an external electric field is present, - * acounting for dielectric contrast. + * Copy the potential from the psi_t represetation to the solution + * vector as an initial guess. * *****************************************************************************/ -int psi_petsc_set_rhs_vare(psi_t * obj, fe_es_t * fe, f_vare_t fepsilon) { - - int ic,jc,kc,index; - int mpi_cartsz[3], mpi_coords[3]; - int ntotal[3], noffset[3]; - int periodic[3]; - int i,j,k; - int xs,ys,zs,xw,yw,zw,xe,ye,ze; - double *** rho_3d; - double rho_elec; - double eunit, beta; - double eps, e0[3]; - - assert(obj); - assert(fe); - assert(fepsilon); - - cs_cartsz(obj->cs, mpi_cartsz); - cs_cart_coords(obj->cs, mpi_coords); - cs_nlocal_offset(obj->cs, noffset); - cs_periodic(obj->cs, periodic); - - DMDAGetCorners(da,&xs,&ys,&zs,&xw,&yw,&zw); - DMDAVecGetArray(da, b, &rho_3d); - - psi_unit_charge(obj, &eunit); - psi_beta(obj, &beta); - - xe = xs + xw; - ye = ys + yw; - ze = zs + zw; - - for (k=zs; kcs, ic,jc,kc); - psi_rho_elec(obj, index, &rho_elec); - /* Non-dimensional potential in Poisson eqn requires e/kT */ - rho_3d[k][j][i] = rho_elec * eunit * beta; - - } - } - } - - /* Modify right hand side for external electric field */ - - e0[X] = obj->e0[X]; - e0[Y] = obj->e0[Y]; - e0[Z] = obj->e0[Z]; +int psi_solver_petsc_psi_to_da(psi_solver_petsc_t * solver) { - if (e0[X] || e0[Y] || e0[Z]) { + cs_t * cs = NULL; + int xs, ys, zs; + int xw, yw, zw; + int xe, ye, ze; + int offset[3] = {0}; + double *** psi_3d = NULL; - cs_ntotal(obj->cs, ntotal); + assert(solver); - if (periodic[X]) { + cs = solver->psi->cs; + cs_nlocal_offset(cs, offset); - if (mpi_coords[X] == 0) { - i = 0; - ic = i - noffset[X] + 1; - for (k=zs; kblock->da, &xs, &ys, &zs, &xw, &yw, &zw); + DMDAVecGetArray(solver->block->da, solver->block->x, &psi_3d); - index = cs_index(obj->cs, ic,jc,kc); - fepsilon(fe, index, &eps); - rho_3d[k][j][i] += eps * e0[X] * ntotal[X]; + xe = xs + xw; + ye = ys + yw; + ze = zs + zw; - } - } + for (int k = zs; k < ze; k++) { + int kc = k - offset[Z] + 1; + for (int j = ys; j < ye; j++) { + int jc = j - offset[Y] + 1; + for (int i = xs; i < xe; i++) { + int ic = i - offset[X] + 1; + int index = cs_index(cs, ic, jc, kc); + psi_3d[k][j][i] = solver->psi->psi->data[index]; } + } + } - if (mpi_coords[X] == mpi_cartsz[X]-1) { - i = xe - 1; - ic = i - noffset[X] + 1; - for (k=zs; kcs, ic,jc,kc); - fepsilon(fe, index, &eps); - rho_3d[k][j][i] -= eps * e0[X] * ntotal[X]; + DMDAVecRestoreArray(solver->block->da, solver->block->x, &psi_3d); - } - } - } + return 0; +} - } +/***************************************************************************** + * + * psi_solver_petsc_da_to_psi + * + * Copy te Petsc solution back to the psi_t represetation. + * + *****************************************************************************/ - if (periodic[Y]) { +int psi_solver_petsc_da_to_psi(psi_solver_petsc_t * solver) { - if (mpi_coords[Y] == 0) { - j = 0; - jc = j - noffset[Y] + 1; - for (k=zs; kcs, ic,jc,kc); - fepsilon(fe, index, &eps); - rho_3d[k][j][i] += eps * e0[Y] * ntotal[Y]; + assert(solver); - } - } - } + cs = solver->psi->cs; + cs_nlocal_offset(cs, offset); - if (mpi_coords[Y] == mpi_cartsz[Y]-1) { - j = ye - 1; - jc = j - noffset[Y] + 1; - for (k=zs; kblock->da, &xs, &ys, &zs, &xw, &yw, &zw); + DMDAVecGetArray(solver->block->da, solver->block->x, &psi_3d); - index = cs_index(obj->cs, ic,jc,kc); - fepsilon(fe, index, &eps); - rho_3d[k][j][i] -= eps * e0[Y] * ntotal[Y]; + xe = xs + xw; + ye = ys + yw; + ze = zs + zw; - } - } + for (int k = zs; k < ze; k++) { + int kc = k - offset[Z] + 1; + for (int j = ys; j < ye; j++) { + int jc = j - offset[Y] + 1; + for (int i = xs; i < xe; i++) { + int ic = i - offset[X] + 1; + int index = cs_index(cs, ic, jc, kc); + solver->psi->psi->data[index] = psi_3d[k][j][i]; } - } + } - if (periodic[Z]) { - - if (mpi_coords[Z] == 0) { - k = 0; - kc = k - noffset[Z] + 1; - for (j=ys; jblock->da, solver->block->x, &psi_3d); - index = cs_index(obj->cs, ic,jc,kc); - fepsilon(fe, index, &eps); - rho_3d[k][j][i] += eps * e0[Z] * ntotal[Z]; + return 0; +} - } - } - } +/***************************************************************************** + * + * psi_solver_petsc_var_epsilon_solve + * + *****************************************************************************/ - if (mpi_coords[Z] == mpi_cartsz[Z]-1) { - k = ze - 1; - kc = k - noffset[Z] + 1; - for (j=ys; jcs, ic,jc,kc); - fepsilon(fe, index, &eps); - rho_3d[k][j][i] -= eps * e0[Z] * ntotal[Z]; + assert(solver); - } - } - } + psi_solver_petsc_var_epsilon_matrix_set(solver); + psi_solver_petsc_var_epsilon_rhs_set(solver); - } + psi_solver_petsc_psi_to_da(solver); + KSPSetInitialGuessNonzero(solver->block->ksp, PETSC_TRUE); + KSPSolve(solver->block->ksp, solver->block->b, solver->block->x); + if (nt % solver->psi->solver.nfreq == 0) { + /* Report on progress of the solver. + * Note the default Petsc residual is the preconditioned L2 norm. */ + pe_t * pe = solver->psi->pe; + PetscInt its = 0; + PetscReal norm = 0.0; + PetscCall(KSPGetIterationNumber(solver->block->ksp, &its)); + PetscCall(KSPGetResidualNorm(solver->block->ksp, &norm)); + pe_info(pe, "\n"); + pe_info(pe, "Krylov solver (with dielectric contrast)\n"); + pe_info(pe, "Norm of residual %g at %d iterations\n", norm, its); } - - DMDAVecRestoreArray(da, b, &rho_3d); - - if (view_vector) { - pe_info(obj->pe, "\nPETSc output RHS\n"); - PetscViewer viewer; - PetscViewerASCIIOpen(PETSC_COMM_WORLD, "rhs.log", &viewer); -#if PETSC_VERSION_GE(3, 7, 0) - PetscViewerPushFormat(viewer, PETSC_VIEWER_ASCII_INDEX); - PetscViewerPopFormat(viewer); -#else - PetscViewerSetFormat(viewer, PETSC_VIEWER_ASCII_INDEX); -#endif - VecView(b,viewer);; - PetscViewerDestroy(&viewer); - } - - return 0; + psi_solver_petsc_da_to_psi(solver); + + return 0; } /***************************************************************************** * - * psi_petsc_solve - * - * If the f_vare_t argument is NULL, the uniform epsilon solver is used. - * If the argument is present, the non-uniform solver is used. + * psi_solver_petsc_var_epsilon_matrix_set * *****************************************************************************/ -int psi_petsc_solve(psi_t * obj, fe_t * fe, f_vare_t fepsilon) { +int psi_solver_petsc_var_epsilon_matrix_set(psi_solver_petsc_t * solver) { + + cs_t * cs = NULL; + int xs, ys, zs; + int xw, yw, zw; + int xe, ye, ze; + int offset[3] = {0}; + + double v[27] = {0}; + MatStencil col[27] = {0}; + stencil_t * s = solver->psi->stencil; + + assert(solver); + + cs = solver->psi->cs; + cs_nlocal_offset(cs, offset); - assert(obj); - assert(fe); + /* Get details of the distributed array data structure. + The PETSc directives return global indices, but + every process works only on its local copy. */ + + DMDAGetCorners(solver->block->da, &xs, &ys, &zs, &xw, &yw, &zw); + + xe = xs + xw; + ye = ys + yw; + ze = zs + zw; + + /* 3D-operator with periodic BCs */ + + for (int k = zs; k < ze; k++) { + int kc = 1 + k - offset[Z]; + for (int j = ys; j < ye; j++) { + int jc = 1 + j - offset[Y]; + for (int i = xs; i < xe; i++) { + + int ic = 1 + i - offset[X]; + int index = cs_index(cs, ic, jc, kc); + double epsilon0 = 0.0; + double gradeps[3] = {0}; + + MatStencil row = {.i = i, .j = j, .k = k}; + + solver->epsilon(solver->fe, index, &epsilon0); + + /* Local approx. to grad epsilon ... */ + for (int p = 1; p < s->npoints; p++) { + int ic1 = ic + s->cv[p][X]; + int jc1 = jc + s->cv[p][Y]; + int kc1 = kc + s->cv[p][Z]; + int index1 = cs_index(cs, ic1, jc1, kc1); + double epsilon1 = 0.0; + solver->epsilon(solver->fe, index1, &epsilon1); + gradeps[X] += s->wgradients[p]*s->cv[p][X]*epsilon1; + gradeps[Y] += s->wgradients[p]*s->cv[p][Y]*epsilon1; + gradeps[Z] += s->wgradients[p]*s->cv[p][Z]*epsilon1; + } + + for (int p = 0; p < s->npoints; p++) { + col[p].i = i + s->cv[p][X]; + col[p].j = j + s->cv[p][Y]; + col[p].k = k + s->cv[p][Z]; + + /* Laplacian part of operator */ + v[p] = s->wlaplacian[p]*epsilon0; + + /* Addtional terms in generalised Poisson equation */ + v[p] += s->wgradients[p]*s->cv[p][X]*gradeps[X]; + v[p] += s->wgradients[p]*s->cv[p][Y]*gradeps[Y]; + v[p] += s->wgradients[p]*s->cv[p][Z]*gradeps[Z]; + } - if(fepsilon == NULL) { - psi_petsc_set_rhs(obj); + MatSetValuesStencil(solver->block->a, 1, &row, s->npoints, col, v, + INSERT_VALUES); + } + } } - if(fepsilon != NULL) { - psi_petsc_compute_matrix(obj, (fe_es_t *) fe, fepsilon); - psi_petsc_set_rhs_vare(obj, (fe_es_t *) fe, fepsilon); + /* Matrix assembly & halo swap */ + /* Retain the non-zero structure of the matrix */ + + MatAssemblyBegin(solver->block->a, MAT_FINAL_ASSEMBLY); + MatAssemblyEnd(solver->block->a, MAT_FINAL_ASSEMBLY); + MatSetOption(solver->block->a, MAT_NEW_NONZERO_LOCATIONS, PETSC_FALSE); + + /* Set the matrix, preconditioner and nullspace */ + KSPSetOperators(solver->block->ksp, solver->block->a, solver->block->a); + + { + MatNullSpace nullsp; + MatNullSpaceCreate(PETSC_COMM_WORLD, PETSC_TRUE, 0, NULL, &nullsp); + MatSetNullSpace(solver->block->a, nullsp); + MatNullSpaceDestroy(&nullsp); } - psi_petsc_copy_psi_to_da(obj); - psi_petsc_poisson(obj); - psi_petsc_copy_da_to_psi(obj); + KSPSetFromOptions(solver->block->ksp); return 0; } /***************************************************************************** * - * psi_petsc_poisson + * psi_solver_petsc_var_epsilon_rhs_set * - * Solves the Poisson equation for constant permittivity. - * The vectors b, x are distributed arrays (DA). + * The only difference here (cf. uniform epsilon) is in the external + * field terms. * *****************************************************************************/ -int psi_petsc_poisson(psi_t * obj) { +int psi_solver_petsc_var_epsilon_rhs_set(psi_solver_petsc_t * solver) { + + cs_t * cs = NULL; + int xs, ys, zs; + int xw, yw, zw; + int xe, ye, ze; + int offset[3] = {0}; + double e0[3] = {0}; + double *** rho_3d = {0}; - assert(obj); + assert(solver); - KSPSetInitialGuessNonzero(ksp,PETSC_TRUE); - KSPSolve(ksp,b,x); + cs = solver->psi->cs; + cs_nlocal_offset(cs, offset); - if (is_statistics_step()) { - KSPGetResidualNorm(ksp,&norm); - KSPGetIterationNumber(ksp,&its); - pe_info(obj->pe, "\nKrylov solver\nNorm of residual %g at %d iterations\n",norm,its); + DMDAGetCorners(solver->block->da, &xs, &ys, &zs, &xw, &yw, &zw); + DMDAVecGetArray(solver->block->da, solver->block->b, &rho_3d); + + xe = xs + xw; + ye = ys + yw; + ze = zs + zw; + + for (int k = zs; k < ze; k++) { + int kc = k - offset[Z] + 1; + for (int j = ys; j < ye; j++) { + int jc = j - offset[Y] + 1; + for (int i = xs; i < xe; i++) { + + int ic = i - offset[X] + 1; + int index = cs_index(cs, ic, jc, kc); + double rho_elec = 0.0; + /* Non-dimensional potential in Poisson eqn requires e/kT */ + double eunit = solver->psi->e; + double beta = solver->psi->beta; + + psi_rho_elec(solver->psi, index, &rho_elec); + rho_3d[k][j][i] = rho_elec*eunit*beta; + } + } } + /* Modify right hand side for external electric field */ + /* The system must be periodic, so no need to check. */ + + e0[X] = solver->psi->e0[X]; + e0[Y] = solver->psi->e0[Y]; + e0[Z] = solver->psi->e0[Z]; + + if (e0[X] || e0[Y] || e0[Z]) { + + int ntotal[3] = {0}; + int mpi_coords[3] = {0}; + int mpi_cartsz[3] = {0}; + + cs_ntotal(cs, ntotal); + cs_cart_coords(cs, mpi_coords); + cs_cartsz(cs, mpi_cartsz); + + if (e0[X] && mpi_coords[X] == 0) { + for (int k = zs; k < ze; k++) { + int kc = 1 + k - offset[Z]; + for (int j = ys; j < ye; j++) { + int jc = 1 + j - offset[Y]; + int index = cs_index(cs, 1, jc, kc); + double epsilon = 0.0; + solver->epsilon(solver->fe, index, &epsilon); + rho_3d[k][j][0] += epsilon*e0[X]*ntotal[X]; + } + } + } + + if (e0[X] && mpi_coords[X] == mpi_cartsz[X] - 1) { + for (int k = zs; k < ze; k++) { + int kc = 1 + k - offset[Z]; + for (int j = ys; j < ye; j++) { + int jc = 1 + j - offset[Y]; + int ic = xe - offset[X]; + int index = cs_index(cs, ic, jc, kc); + double epsilon = 0.0; + solver->epsilon(solver->fe, index, &epsilon); + rho_3d[k][j][xe-1] -= epsilon*e0[X]*ntotal[X]; + } + } + } + + if (e0[Y] && mpi_coords[Y] == 0) { + for (int k = zs; k < ze; k++) { + int kc = 1 + k - offset[Z]; + for (int i = xs; i < xe; i++) { + int ic = 1 + i - offset[X]; + int index = cs_index(cs, ic, 1, kc); + double epsilon = 0.0; + solver->epsilon(solver->fe, index, &epsilon); + rho_3d[k][0][i] += epsilon*e0[Y]*ntotal[Y]; + } + } + } + + if (e0[Y] && mpi_coords[Y] == mpi_cartsz[Y] - 1) { + for (int k = zs; k < ze; k++) { + int kc = 1 + k - offset[Z]; + for (int i = xs; i < xe; i++) { + int jc = ye - offset[Y]; + int ic = 1 + i - offset[X]; + int index = cs_index(cs, ic, jc, kc); + double epsilon = 0.0; + solver->epsilon(solver->fe, index, &epsilon); + rho_3d[k][ye-1][i] -= epsilon*e0[Y]*ntotal[Y]; + } + } + } + + if (e0[Z] && mpi_coords[Z] == 0) { + for (int j = ys; j < ye; j++) { + int jc = 1 + j - offset[Y]; + for (int i = xs; i < xe; i++) { + int ic = 1 + i - offset[X]; + int index = cs_index(cs, ic, jc, 1); + double epsilon = 0.0; + solver->epsilon(solver->fe, index, &epsilon); + rho_3d[0][j][i] += epsilon*e0[Z]*ntotal[Z]; + } + } + } + + if (e0[Z] && mpi_coords[Z] == mpi_cartsz[Z] - 1) { + int kc = ze - offset[Z]; + for (int j = ys; j < ye; j++) { + int jc = 1 + j - offset[Y]; + for (int i = xs; i < xe; i++) { + int ic = 1 + i - offset[X]; + int index = cs_index(cs, ic, jc, kc); + double epsilon = 0.0; + solver->epsilon(solver->fe, index, &epsilon); + rho_3d[ze-1][j][i] -= epsilon*e0[Z]*ntotal[Z]; + } + } + } + } + + DMDAVecRestoreArray(solver->block->da, solver->block->b, &rho_3d); + return 0; } /***************************************************************************** * - * psi_petsc_finish - * - * Destroys the solver context, distributed array, matrix and vectors. + * psi_solver_petsc_finalise * *****************************************************************************/ -int psi_petsc_finish() { +int psi_solver_petsc_finalise(psi_solver_petsc_t * solver) { + + assert(solver); + + PetscCall(KSPDestroy(&solver->block->ksp)); + PetscCall(VecDestroy(&solver->block->x)); + PetscCall(VecDestroy(&solver->block->b)); + PetscCall(MatDestroy(&solver->block->a)); + PetscCall(DMDestroy(&solver->block->da)); - KSPDestroy(&ksp); - VecDestroy(&x); - VecDestroy(&b); - MatDestroy(&A); - DMDestroy(&da); + free(solver->block); return 0; } diff --git a/src/psi_petsc.h b/src/psi_petsc.h index 8cffc313..419a6131 100644 --- a/src/psi_petsc.h +++ b/src/psi_petsc.h @@ -1,14 +1,11 @@ -#ifdef PETSC /***************************************************************************** * * psi_petsc.h * - * $Id$ - * * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre * - * (c) 2012-2013 The University of Edinburgh + * (c) 2012-2023 The University of Edinburgh * * Contributing Authors: * Oliver Henrich (ohenrich@epcc.ed.ac.uk) @@ -16,23 +13,29 @@ * *****************************************************************************/ -#ifndef PSI_PETSC_H -#define PSI_PETSC_H +#ifndef LUDWIG_PSI_SOLVER_PETSC_H +#define LUDWIG_PSI_SOLVER_PETSC_H -#include "psi.h" -#include "fe_electro_symmetric.h" +#include "psi_solver.h" -int psi_petsc_init(psi_t * obj, fe_t * fe, f_vare_t fepsilon); -int psi_petsc_copy_psi_to_da(psi_t * obj); -int psi_petsc_copy_da_to_psi(psi_t * obj); -int psi_petsc_compute_laplacian(psi_t * obj); -int psi_petsc_set_rhs(psi_t * obj); -int psi_petsc_compute_matrix(psi_t * obj, fe_es_t * fe, f_vare_t fepsilon); -int psi_petsc_set_rhs_vare(psi_t * obj, fe_es_t * fe, f_vare_t epsilon); -int psi_petsc_solve(psi_t * obj, fe_t * fe, f_vare_t fepsilon); -int psi_petsc_poisson(psi_t * obj); -int psi_petsc_finish(); +typedef struct psi_solver_petsc_s psi_solver_petsc_t; +typedef struct psi_solver_petsc_block_s psi_solver_petsc_block_t; + +struct psi_solver_petsc_s { + psi_solver_t super; /* Superclass block */ + psi_t * psi; /* Retain a reference to psi_t */ + fe_t * fe; /* Free energy */ + var_epsilon_ft epsilon; /* Variable dielectric model */ + psi_solver_petsc_block_t * block; /* Opaque internal information. */ +}; + +int psi_solver_petsc_create(psi_t * psi, psi_solver_petsc_t ** solver); +int psi_solver_petsc_free(psi_solver_petsc_t ** solver); +int psi_solver_petsc_solve(psi_solver_petsc_t * solver, int ntimestep); + +int psi_solver_petsc_var_epsilon_create(psi_t * psi, var_epsilon_t epsilon, + psi_solver_petsc_t ** solver); +int psi_solver_petsc_var_epsilon_solve(psi_solver_petsc_t * solver, int nt); -#endif #endif diff --git a/src/psi_rt.c b/src/psi_rt.c index 3bcf977f..418e8f7c 100644 --- a/src/psi_rt.c +++ b/src/psi_rt.c @@ -24,10 +24,9 @@ #include "pe.h" #include "runtime.h" +#include "io_info_args_rt.h" #include "psi_rt.h" #include "psi_init.h" -#include "io_harness.h" -#include "io_info_args_rt.h" #include "util_bits.h" /***************************************************************************** @@ -109,6 +108,18 @@ int psi_rt_init_rho(pe_t * pe, rt_t * rt, psi_t * obj, map_t * map) { if (n == 0) pe_fatal(pe, "... please set electrokinetics_init_delta_el\n"); pe_info(pe, "Initial condition delta_el: %14.7e\n", delta_el); + { + /* psi_p is the saturation potential */ + double beta = obj->beta; + double e = obj->e; + double dplus = obj->diffusivity[0]; + double dminus = obj->diffusivity[1]; + double psi_p = dplus*dminus*delta_el/(beta*e*(dplus + dminus)*rho_el); + double tau_e = obj->epsilon/(beta*e*e*(dplus + dminus)*rho_el); + pe_info(pe, "Saturation potential: %14.7e\n", psi_p); + pe_info(pe, "Saturation timescale: %14.7e\n", tau_e); + } + psi_init_liquid_junction(obj, rho_el, delta_el); } @@ -218,6 +229,49 @@ int psi_options_rt(pe_t * pe, cs_t * cs, rt_t * rt, psi_options_t * popts) { /* Poisson solver */ /* There are two possible sources of nfreq */ + { + /* Solver type */ + char str[BUFSIZ] = {0}; + strcpy(str, psi_poisson_solver_to_string(psi_poisson_solver_default())); + rt_string_parameter(rt, "electrokinetics_solver_type", str, BUFSIZ); + opts.solver.psolver = psi_poisson_solver_from_string(str); + switch (opts.solver.psolver) { + case (PSI_POISSON_SOLVER_INVALID): + /* Not recognised */ + pe_info(pe, "electrokinetics_solver_type: %s\n", str); + pe_info(pe, "is not recongnised\n"); + pe_fatal(pe, "Please check and try again!\n"); + break; + case (PSI_POISSON_SOLVER_PETSC): + /* Check Petsc is actually available */ + { + int isAvailable = 0; + PetscInitialised(&isAvailable); + if (isAvailable == 0) { + pe_info(pe, "electrokinetics_solver_type: petsc\n"); + pe_info(pe, "Petsc has not been compiled in this build\n"); + pe_info(pe, "Please use the `sor` solver.\n"); + pe_fatal(pe, "Please check the input and try again!\n"); + } + } + break; + default: + ; /* ok */ + } + } + + /* Stencil must be available. */ + if (rt_int_parameter(rt, "electrokinetics_solver_stencil", + &opts.solver.nstencil)) { + stencil_t * s = NULL; + int ifail = stencil_create(opts.solver.nstencil, &s); + if (ifail != 0) { + pe_info(pe, "electroknietics_solver_stencil: %d\n", opts.solver.nstencil); + pe_fatal(pe, "Not supported. Please check and try again!\n"); + } + if (s) stencil_free(&s); + } + rt_int_parameter(rt, "electrokinetics_maxits", &opts.solver.maxits); rt_int_parameter(rt, "freq_statistics", &opts.solver.nfreq); rt_int_parameter(rt, "freq_psi_resid", &opts.solver.nfreq); @@ -288,8 +342,11 @@ int psi_info(pe_t * pe, const psi_t * psi) { } /* Add full information ... */ - pe_info(pe, "Relative tolerance: %20.7e\n", psi->solver.reltol); - pe_info(pe, "Absolute tolerance: %20.7e\n", psi->solver.abstol); + pe_info(pe, "Solver type: %20s\n", + psi_poisson_solver_to_string(psi->solver.psolver)); + pe_info(pe, "Solver stencil points: %16d\n", psi->solver.nstencil); + pe_info(pe, "Relative tolerance: %20.7e\n", psi->solver.reltol); + pe_info(pe, "Absolute tolerance: %20.7e\n", psi->solver.abstol); pe_info(pe, "Max. no. of iterations: %16d\n", psi->solver.maxits); pe_info(pe, "Number of multisteps: %d\n", psi->multisteps); diff --git a/src/psi_solver.c b/src/psi_solver.c new file mode 100644 index 00000000..7eb40b01 --- /dev/null +++ b/src/psi_solver.c @@ -0,0 +1,104 @@ +/***************************************************************************** + * + * psi_solver.c + * + * Factory function for the solver object. + * + * + * 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 + +/* Available implementations ... */ +#include "psi_petsc.h" +#include "psi_sor.h" + +/***************************************************************************** + * + * psi_solver_create + * + * Returns pointer to the abstract solver type. + * + * We must allow that PETSc is not available, so the return value + * must be checked by the caller. + * + *****************************************************************************/ + +int psi_solver_create(psi_t * psi, psi_solver_t ** solver) { + + int ifail = 0; + + assert(solver && *solver == NULL); + + switch (psi->solver.psolver) { + + case (PSI_POISSON_SOLVER_PETSC): + { + psi_solver_petsc_t * petsc = NULL; + ifail = psi_solver_petsc_create(psi, &petsc); + if (ifail == 0) *solver = (psi_solver_t *) petsc; + } + break; + + case (PSI_POISSON_SOLVER_SOR): + { + psi_solver_sor_t * sor = NULL; + ifail = psi_solver_sor_create(psi, &sor); + if (ifail == 0) *solver = (psi_solver_t *) sor; + } + break; + + default: + ifail = -1; + } + + return ifail; +} + +/***************************************************************************** + * + * psi_solver_var_epsilon_create + * + * The equivalent for the version allowing electric contrast. + * The same comments apply. + * + *****************************************************************************/ + +int psi_solver_var_epsilon_create(psi_t * psi, var_epsilon_t user, + psi_solver_t ** solver) { + + int ifail = 0; + + assert(solver && *solver == NULL); + + switch (psi->solver.psolver) { + + case (PSI_POISSON_SOLVER_PETSC): + { + psi_solver_petsc_t * petsc = NULL; + ifail = psi_solver_petsc_var_epsilon_create(psi, user, &petsc); + if (ifail == 0) *solver = (psi_solver_t *) petsc; + } + break; + + case (PSI_POISSON_SOLVER_SOR): + { + psi_solver_sor_t * sor = NULL; + ifail = psi_solver_sor_var_epsilon_create(psi, user, &sor); + *solver = (psi_solver_t *) sor; + } + break; + + default: + ifail = -1; + } + + return ifail; +} diff --git a/src/psi_solver.h b/src/psi_solver.h new file mode 100644 index 00000000..0099dd3e --- /dev/null +++ b/src/psi_solver.h @@ -0,0 +1,57 @@ +/***************************************************************************** + * + * psi_solver.h + * + * Abstract Poisson solver interface. + * + * 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) + * + *****************************************************************************/ + +#ifndef LUDWIG_PSI_SOLVER_H +#define LUDWIG_PSI_SOLVER_H + +#include "free_energy.h" +#include "psi.h" + +typedef struct psi_solver_vt_s psi_solver_vt_t; +typedef struct psi_solver_s psi_solver_t; + +typedef int (* psi_solver_free_ft) (psi_solver_t ** psolver); +typedef int (* psi_solver_solve_ft) (psi_solver_t * solver, int ntimestep); + +struct psi_solver_vt_s { + psi_solver_free_ft free; /* Destructor */ + psi_solver_solve_ft solve; /* Driver of solve */ +}; + +struct psi_solver_s { + const psi_solver_vt_t * impl; /* Implementation vtable */ +}; + +/* Factory methods */ + +int psi_solver_create(psi_t * psi, psi_solver_t ** solver); + +/* For dielectric contrast, we need some abstract component which + * involves the free energy. However, as only electrosymmetric is + * relevant at the moment, I haven't added a further method to the + * abstract free energy interface. So we have a slightly add hoc + * addition here... */ + +typedef struct var_epsilon_s var_epsilon_t; +typedef int (* var_epsilon_ft)(void * fe, int index, double * epsilon); + +struct var_epsilon_s { + fe_t * fe; + var_epsilon_ft epsilon; +}; + +int psi_solver_var_epsilon_create(psi_t * psi, var_epsilon_t epsilon, + psi_solver_t ** solver); +#endif diff --git a/src/psi_solver_options.c b/src/psi_solver_options.c index c3a29403..669a01d6 100644 --- a/src/psi_solver_options.c +++ b/src/psi_solver_options.c @@ -4,6 +4,9 @@ * * Container for Poission solver options. * + * nb. The default solver type is Petsc if Petsc is available, + * otherwise fall back to the internal sor implementation. + * * * Edinburgh Soft Matter and Statistical Physics Group and * Edinburgh Parallel Computing Centre @@ -21,6 +24,23 @@ #include "psi_solver_options.h" #include "util.h" +/***************************************************************************** + * + * psi_poisson_solver_default + * + *****************************************************************************/ + +psi_poisson_solver_enum_t psi_poisson_solver_default(void) { + + int havePetsc = 0; + psi_poisson_solver_enum_t psolver = PSI_POISSON_SOLVER_SOR; + + PetscInitialised(&havePetsc); + if (havePetsc) psolver = PSI_POISSON_SOLVER_PETSC; + + return psolver; +} + /***************************************************************************** * * psi_poisson_solver_to_string @@ -77,7 +97,8 @@ psi_poisson_solver_enum_t psi_poisson_solver_from_string(const char * str) { psi_solver_options_t psi_solver_options_default(void) { - psi_solver_options_t pso = psi_solver_options_type(PSI_POISSON_SOLVER_SOR); + psi_poisson_solver_enum_t psolver = psi_poisson_solver_default(); + psi_solver_options_t pso = psi_solver_options_type(psolver); return pso; } diff --git a/src/psi_solver_options.h b/src/psi_solver_options.h index ea68750e..4096d6ac 100644 --- a/src/psi_solver_options.h +++ b/src/psi_solver_options.h @@ -17,6 +17,7 @@ #define LUDWIG_PSI_SOLVER_OPTIONS_H #include "util_json.h" +#include "util_petsc.h" /* Poisson solver method */ @@ -46,6 +47,7 @@ struct psi_solver_options_s { const char * psi_poisson_solver_to_string(psi_poisson_solver_enum_t mytype); psi_poisson_solver_enum_t psi_poisson_solver_from_string(const char * str); +psi_poisson_solver_enum_t psi_poisson_solver_default(void); psi_solver_options_t psi_solver_options_default(void); psi_solver_options_t psi_solver_options_type(psi_poisson_solver_enum_t mytype); diff --git a/src/psi_sor.c b/src/psi_sor.c index e7308217..74aa2f7c 100644 --- a/src/psi_sor.c +++ b/src/psi_sor.c @@ -34,31 +34,71 @@ #include "psi_sor.h" #include "util.h" +/* Function table */ + +static psi_solver_vt_t vt_ = { + (psi_solver_free_ft) psi_solver_sor_free, + (psi_solver_solve_ft) psi_solver_sor_solve +}; + +static psi_solver_vt_t vart_ = { + (psi_solver_free_ft) psi_solver_sor_free, + (psi_solver_solve_ft) psi_solver_sor_var_epsilon_solve +}; + /***************************************************************************** * - * psi_sor_solve + * psi_solver_sor_create + * + *****************************************************************************/ + +int psi_solver_sor_create(psi_t * psi, psi_solver_sor_t ** sor) { + + int ifail = 0; + psi_solver_sor_t * solver = NULL; + + assert(psi); + assert(sor); + + solver = (psi_solver_sor_t *) calloc(1, sizeof(psi_solver_sor_t)); + if (solver == NULL) { + ifail = -1; + } + else { + /* Set the function table ... */ + solver->super.impl = &vt_; + solver->psi = psi; + } + + *sor = solver; + + return ifail; +} + +/***************************************************************************** * - * If the f_vare_t argument is NULL, the uniform epsilon solver is used. - * If the argument is present, the non-uniform solver is used. + * psi_solver_sor_free * *****************************************************************************/ -int psi_sor_solve(psi_t * obj, fe_t * fe, f_vare_t fepsilon, int its) { +int psi_solver_sor_free(psi_solver_sor_t ** sor) { - assert(obj); + assert(sor); + assert(*sor); - if (fepsilon == NULL) psi_sor_poisson(obj, its); - if (fepsilon != NULL) psi_sor_vare_poisson(obj, (fe_es_t *) fe, fepsilon, its); + free(*sor); + *sor = NULL; return 0; } /***************************************************************************** * - * psi_sor_poisson + * psi_solver_sor_solve * - * Uniform permittivity. The differencing is a seven - * point stencil for \nabla^2 \psi. So + * Solve Poisson equation with uniform permittivity. + * + * The differencing is a seven point stencil for \nabla^2 \psi. So * * epsilon [ psi(i+1,j,k) - 2 psi(i,j,k) + psi(i-1,j,k) * + psi(i,j+1,k) - 2 psi(i,j,k) + psi(i,j-1,k) @@ -89,16 +129,12 @@ int psi_sor_solve(psi_t * obj, fe_t * fe, f_vare_t fepsilon, int its) { * *****************************************************************************/ -int psi_sor_poisson(psi_t * obj, int its) { +int psi_solver_sor_solve(psi_solver_sor_t * sor, int its) { int niteration = 1000; /* Maximum number of iterations */ const int ncheck = 5; /* Check global residual every n iterations */ - int ic, jc, kc, index; int nhalo; - int n; /* Relaxation iterations */ - int pass; /* Red/black iteration */ - int kst; /* Start kc index for red/black iteration */ int nlocal[3]; int nsites; int xs, ys, zs; /* Memory strides */ @@ -110,21 +146,20 @@ int psi_sor_poisson(psi_t * obj, int its) { double dpsi; double omega; /* Over-relaxation parameter 1 < omega < 2 */ double radius; /* Spectral radius of Jacobi iteration */ - double tol_rel; /* Relative tolerance */ - double tol_abs; /* Absolute tolerance */ double eunit, beta; double ltot[3]; MPI_Comm comm; /* Cartesian communicator */ - double * __restrict__ psidata = obj->psi->data; + psi_t * psi = sor->psi; + double * __restrict__ psidata = psi->psi->data; - cs_ltot(obj->cs, ltot); - cs_nhalo(obj->cs, &nhalo); - cs_nsites(obj->cs, &nsites); - cs_nlocal(obj->cs, nlocal); - cs_cart_comm(obj->cs, &comm); - cs_strides(obj->cs, &xs, &ys, &zs); + cs_ltot(psi->cs, ltot); + cs_nhalo(psi->cs, &nhalo); + cs_nsites(psi->cs, &nsites); + cs_nlocal(psi->cs, nlocal); + cs_cart_comm(psi->cs, &comm); + cs_strides(psi->cs, &xs, &ys, &zs); assert(nhalo >= 1); @@ -139,23 +174,21 @@ int psi_sor_poisson(psi_t * obj, int its) { radius = 1.0 - 0.5*pow(4.0*atan(1.0)/dmax(ltot[X],ltot[Z]), 2); - psi_epsilon(obj, &epsilon); - psi_reltol(obj, &tol_rel); - psi_abstol(obj, &tol_abs); - psi_maxits(obj, &niteration); + psi_epsilon(psi, &epsilon); + psi_maxits(psi, &niteration); - psi_beta(obj, &beta); - psi_unit_charge(obj, &eunit); + psi_beta(psi, &beta); + psi_unit_charge(psi, &eunit); rnorm_local[0] = 0.0; - for (ic = 1; ic <= nlocal[X]; ic++) { - for (jc = 1; jc <= nlocal[Y]; jc++) { - for (kc = 1; kc <= nlocal[Z]; kc++) { + for (int ic = 1; ic <= nlocal[X]; ic++) { + for (int jc = 1; jc <= nlocal[Y]; jc++) { + for (int kc = 1; kc <= nlocal[Z]; kc++) { - index = cs_index(obj->cs, ic, jc, kc); + int index = cs_index(psi->cs, ic, jc, kc); - psi_rho_elec(obj, index, &rho_elec); + psi_rho_elec(psi, index, &rho_elec); /* Non-dimensional potential in Poisson eqn requires e/kT */ /* This is just the L2 norm of the right hand side. */ @@ -172,22 +205,22 @@ int psi_sor_poisson(psi_t * obj, int its) { omega = 1.0; - for (n = 0; n < niteration; n++) { + for (int n = 0; n < niteration; n++) { /* Compute current normal of the residual */ rnorm_local[1] = 0.0; - for (pass = 0; pass < 2; pass++) { + for (int pass = 0; pass < 2; pass++) { - for (ic = 1; ic <= nlocal[X]; ic++) { - for (jc = 1; jc <= nlocal[Y]; jc++) { - kst = 1 + (ic + jc + pass) % 2; - for (kc = kst; kc <= nlocal[Z]; kc += 2) { + for (int ic = 1; ic <= nlocal[X]; ic++) { + for (int jc = 1; jc <= nlocal[Y]; jc++) { + int kst = 1 + (ic + jc + pass) % 2; + for (int kc = kst; kc <= nlocal[Z]; kc += 2) { - index = cs_index(obj->cs, ic, jc, kc); + int index = cs_index(psi->cs, ic, jc, kc); - psi_rho_elec(obj, index, &rho_elec); + psi_rho_elec(psi, index, &rho_elec); /* 6-point stencil of Laplacian */ @@ -218,49 +251,47 @@ int psi_sor_poisson(psi_t * obj, int its) { else { omega = 1.0 / (1.0 - 0.25*radius*radius*omega); } - assert(1.0 < omega); - assert(omega < 2.0); + assert(1.0 < omega && omega < 2.0); - psi_halo_psi(obj); - psi_halo_psijump(obj); + psi_halo_psi(psi); + psi_halo_psijump(psi); } if ((n % ncheck) == 0) { /* Compare residual and exit if small enough */ + pe_t * pe = psi->pe; rnorm_local[1] = sqrt(rnorm_local[1]); MPI_Allreduce(rnorm_local, rnorm, 2, MPI_DOUBLE, MPI_SUM, comm); - if (rnorm[1] < tol_abs) { + if (rnorm[1] < psi->solver.abstol) { - if (its % obj->solver.nfreq == 0) { - pe_info(obj->pe, "\n"); - pe_info(obj->pe, "SOR solver converged to absolute tolerance\n"); - pe_info(obj->pe, "SOR residual %14.7e at %d iterations\n", rnorm[1], - n); + if (its % psi->solver.nfreq == 0) { + pe_info(pe, "\n"); + pe_info(pe, "SOR solver converged to absolute tolerance\n"); + pe_info(pe, "SOR residual %14.7e at %d iterations\n", rnorm[1], n); } break; } - if (rnorm[1] < tol_rel*rnorm[0]) { + if (rnorm[1] < psi->solver.reltol*rnorm[0]) { - if (its % obj->solver.nfreq == 0) { - pe_info(obj->pe, "\n"); - pe_info(obj->pe, "SOR solver converged to relative tolerance\n"); - pe_info(obj->pe, "SOR residual %14.7e at %d iterations\n", rnorm[1], - n); + if (its % psi->solver.nfreq == 0) { + pe_info(pe, "\n"); + pe_info(pe, "SOR solver converged to relative tolerance\n"); + pe_info(pe, "SOR residual %14.7e at %d iterations\n", rnorm[1], n); } break; } } if (n == niteration-1) { - pe_info(obj->pe, "\n"); - pe_info(obj->pe, "SOR solver exceeded %d iterations\n", n+1); - pe_info(obj->pe, "SOR residual %le (initial) %le (final)\n\n", + pe_info(psi->pe, "\n"); + pe_info(psi->pe, "SOR solver exceeded %d iterations\n", n+1); + pe_info(psi->pe, "SOR residual %le (initial) %le (final)\n\n", rnorm[0], rnorm[1]); } } @@ -270,7 +301,38 @@ int psi_sor_poisson(psi_t * obj, int its) { /***************************************************************************** * - * psi_sor_vare_poisson + * psi_solver_sor_var_epsilon_create + * + *****************************************************************************/ + +int psi_solver_sor_var_epsilon_create(psi_t * psi, var_epsilon_t user, + psi_solver_sor_t ** sor) { + int ifail = 0; + psi_solver_sor_t * solver = NULL; + + assert(psi); + assert(sor); + + solver = (psi_solver_sor_t *) calloc(1, sizeof(psi_solver_sor_t)); + if (solver == NULL) { + ifail = -1; + } + else { + /* Set the function table etc... */ + solver->super.impl = &vart_; + solver->psi = psi; + solver->fe = user.fe; + solver->epsilon = user.epsilon; + } + + *sor = solver; + + return ifail; +} + +/***************************************************************************** + * + * psi_solver_sor_var_epsilon_solve * * This is essentially a copy of the above, but it allows a spatially * varying permittivity epsilon: @@ -281,15 +343,11 @@ int psi_sor_poisson(psi_t * obj, int its) { * ****************************************************************************/ -int psi_sor_vare_poisson(psi_t * obj, fe_es_t * fe, f_vare_t fepsilon, int its) { +int psi_solver_sor_var_epsilon_solve(psi_solver_sor_t * sor, int its) { int niteration = 2000; /* Maximum number of iterations */ const int ncheck = 1; /* Check global residual every n iterations */ - - int ic, jc, kc, index; - int n; /* Relaxation iterations */ - int pass; /* Red/black iteration */ - int kst; /* Start kc index for red/black iteration */ + int nlocal[3]; int nsites; int xs, ys, zs; /* Memory strides */ @@ -305,25 +363,19 @@ int psi_sor_vare_poisson(psi_t * obj, fe_es_t * fe, f_vare_t fepsilon, int its) double omega; /* Over-relaxation parameter 1 < omega < 2 */ double radius; /* Spectral radius of Jacobi iteration */ - double tol_rel; /* Relative tolerance */ - double tol_abs; /* Absolute tolerance */ - double ltot[3]; double eunit, beta; MPI_Comm comm; /* Cartesian communicator */ - double * __restrict__ psidata = obj->psi->data; - + psi_t * psi = sor->psi; + double * __restrict__ psidata = psi->psi->data; - assert(obj); - assert(fepsilon); - - cs_ltot(obj->cs, ltot); - cs_nlocal(obj->cs, nlocal); - cs_nsites(obj->cs, &nsites); - cs_cart_comm(obj->cs, &comm); - cs_strides(obj->cs, &xs, &ys, &zs); + cs_ltot(psi->cs, ltot); + cs_nlocal(psi->cs, nlocal); + cs_nsites(psi->cs, &nsites); + cs_cart_comm(psi->cs, &comm); + cs_strides(psi->cs, &xs, &ys, &zs); /* The red/black operation needs to be tested for odd numbers * of points in parallel. */ @@ -336,23 +388,21 @@ int psi_sor_vare_poisson(psi_t * obj, fe_es_t * fe, f_vare_t fepsilon, int its) radius = 1.0 - 0.5*pow(4.0*atan(1.0)/dmax(ltot[X],ltot[Z]), 2); - psi_reltol(obj, &tol_rel); - psi_abstol(obj, &tol_abs); - psi_maxits(obj, &niteration); - psi_beta(obj, &beta); - psi_unit_charge(obj, &eunit); + psi_maxits(psi, &niteration); + psi_beta(psi, &beta); + psi_unit_charge(psi, &eunit); /* Compute the initial norm of the right hand side. */ rnorm_local[0] = 0.0; - for (ic = 1; ic <= nlocal[X]; ic++) { - for (jc = 1; jc <= nlocal[Y]; jc++) { - for (kc = 1; kc <= nlocal[Z]; kc++) { + for (int ic = 1; ic <= nlocal[X]; ic++) { + for (int jc = 1; jc <= nlocal[Y]; jc++) { + for (int kc = 1; kc <= nlocal[Z]; kc++) { - index = cs_index(obj->cs, ic, jc, kc); - psi_rho_elec(obj, index, &rho_elec); + int index = cs_index(psi->cs, ic, jc, kc); + psi_rho_elec(psi, index, &rho_elec); residual = eunit*beta*rho_elec; rnorm_local[0] += residual*residual; @@ -366,25 +416,24 @@ int psi_sor_vare_poisson(psi_t * obj, fe_es_t * fe, f_vare_t fepsilon, int its) omega = 1.0; - for (n = 0; n < niteration; n++) { + for (int n = 0; n < niteration; n++) { /* Compute current normal of the residual */ rnorm_local[1] = 0.0; - for (pass = 0; pass < 2; pass++) { + for (int pass = 0; pass < 2; pass++) { - for (ic = 1; ic <= nlocal[X]; ic++) { - for (jc = 1; jc <= nlocal[Y]; jc++) { - kst = 1 + (ic + jc + pass) % 2; - for (kc = kst; kc <= nlocal[Z]; kc += 2) { + for (int ic = 1; ic <= nlocal[X]; ic++) { + for (int jc = 1; jc <= nlocal[Y]; jc++) { + int kst = 1 + (ic + jc + pass) % 2; + for (int kc = kst; kc <= nlocal[Z]; kc += 2) { + int index = cs_index(psi->cs, ic, jc, kc); depsi = 0.0; - index = cs_index(obj->cs, ic, jc, kc); - - psi_rho_elec(obj, index, &rho_elec); - fepsilon(fe, index, &eps0); + psi_rho_elec(psi, index, &rho_elec); + sor->epsilon(sor->fe, index, &eps0); /* Laplacian part of operator */ @@ -398,27 +447,27 @@ int psi_sor_vare_poisson(psi_t * obj, fe_es_t * fe, f_vare_t fepsilon, int its) /* Additional terms in generalised Poisson equation */ - fepsilon(fe, index + xs, &eps1); + sor->epsilon(sor->fe, index + xs, &eps1); depsi += 0.25*eps1*(psidata[addr_rank0(nsites, index + xs)] - psidata[addr_rank0(nsites, index - xs)]); - fepsilon(fe, index - xs, &eps1); + sor->epsilon(sor->fe, index - xs, &eps1); depsi -= 0.25*eps1*(psidata[addr_rank0(nsites, index + xs)] - psidata[addr_rank0(nsites, index - xs)]); - fepsilon(fe, index + ys, &eps1); + sor->epsilon(sor->fe, index + ys, &eps1); depsi += 0.25*eps1*(psidata[addr_rank0(nsites, index + ys)] - psidata[addr_rank0(nsites, index - ys)]); - fepsilon(fe, index - ys, &eps1); + sor->epsilon(sor->fe, index - ys, &eps1); depsi -= 0.25*eps1*(psidata[addr_rank0(nsites, index + ys)] - psidata[addr_rank0(nsites, index - ys)]); - fepsilon(fe, index + zs, &eps1); + sor->epsilon(sor->fe, index + zs, &eps1); depsi += 0.25*eps1*(psidata[addr_rank0(nsites, index + zs)] - psidata[addr_rank0(nsites, index - zs)]); - fepsilon(fe, index - zs, &eps1); + sor->epsilon(sor->fe, index - zs, &eps1); depsi -= 0.25*eps1*(psidata[addr_rank0(nsites, index + zs)] - psidata[addr_rank0(nsites, index - zs)]); @@ -430,8 +479,8 @@ int psi_sor_vare_poisson(psi_t * obj, fe_es_t * fe, f_vare_t fepsilon, int its) } } - psi_halo_psi(obj); - psi_halo_psijump(obj); + psi_halo_psi(psi); + psi_halo_psijump(psi); } @@ -446,35 +495,35 @@ int psi_sor_vare_poisson(psi_t * obj, fe_es_t * fe, f_vare_t fepsilon, int its) rnorm_local[1] = sqrt(rnorm_local[1]); MPI_Allreduce(rnorm_local, rnorm, 2, MPI_DOUBLE, MPI_SUM, comm); - if (rnorm[1] < tol_abs) { + if (rnorm[1] < psi->solver.abstol) { - if (its % obj->solver.nfreq == 0) { - pe_info(obj->pe, "\n"); - pe_info(obj->pe, "SOR (heterogeneous) solver converged to " + if (its % psi->solver.nfreq == 0) { + pe_info(psi->pe, "\n"); + pe_info(psi->pe, "SOR (heterogeneous) solver converged to " "absolute tolerance\n"); - pe_info(obj->pe, "SOR residual %14.7e at %d iterations\n", + pe_info(psi->pe, "SOR residual %14.7e at %d iterations\n", rnorm[1], n); } break; } - if (rnorm[1] < tol_rel*rnorm[0]) { + if (rnorm[1] < psi->solver.reltol*rnorm[0]) { - if (its % obj->solver.nfreq == 0) { - pe_info(obj->pe, "\n"); - pe_info(obj->pe, "SOR (heterogeneous) solver converged to " + if (its % psi->solver.nfreq == 0) { + pe_info(psi->pe, "\n"); + pe_info(psi->pe, "SOR (heterogeneous) solver converged to " "relative tolerance\n"); - pe_info(obj->pe, "SOR residual %14.7e at %d iterations\n", + pe_info(psi->pe, "SOR residual %14.7e at %d iterations\n", rnorm[1], n); } break; } if (n == niteration-1) { - pe_info(obj->pe, "\n"); - pe_info(obj->pe, "SOR solver (heterogeneous) exceeded %d iterations\n", + pe_info(psi->pe, "\n"); + pe_info(psi->pe, "SOR solver (heterogeneous) exceeded %d iterations\n", n+1); - pe_info(obj->pe, "SOR residual %le (initial) %le (final)\n\n", + pe_info(psi->pe, "SOR residual %le (initial) %le (final)\n\n", rnorm[0], rnorm[1]); } } diff --git a/src/psi_sor.h b/src/psi_sor.h index d8025b08..a8f13c62 100644 --- a/src/psi_sor.h +++ b/src/psi_sor.h @@ -9,18 +9,34 @@ * Kevin Stratford (kevin@epcc.ed.ac.uk) * Ignacio Pagonabarraga (ipagonabarraga@ub.edu) * - * (c) 2012-2022 The University of Edinburgh + * (c) 2012-2023 The University of Edinburgh * *****************************************************************************/ -#ifndef LUDWIG_PSI_SOR_H -#define LUDWIG_PSI_SOR_H +#ifndef LUDWIG_PSI_SOLVER_SOR_H +#define LUDWIG_PSI_SOLVER_SOR_H -#include "psi.h" -#include "fe_electro_symmetric.h" +#include "psi_solver.h" + +typedef struct psi_solver_sor_s psi_solver_sor_t; + +struct psi_solver_sor_s { + psi_solver_t super; /* superclass block */ + psi_t * psi; /* Reference to psi structure */ + fe_t * fe; /* abstract free energy */ + var_epsilon_ft epsilon; /* provides local epsilon */ +}; + +int psi_solver_sor_create(psi_t * psi, psi_solver_sor_t ** sor); +int psi_solver_sor_free(psi_solver_sor_t ** sor); +int psi_solver_sor_solve(psi_solver_sor_t * sor, int ntimestep); + +/* This might actually be a separate solver type. */ + +int psi_solver_sor_var_epsilon_create(psi_t * psi, var_epsilon_t epsilon, + psi_solver_sor_t ** sor); + +int psi_solver_sor_var_epsilon_solve(psi_solver_sor_t * sor, int ntimestep); -int psi_sor_solve(psi_t * obj, fe_t * fe, f_vare_t fepsilon, int its); -int psi_sor_poisson(psi_t * obj, int its); -int psi_sor_vare_poisson(psi_t * obj, fe_es_t * fe, f_vare_t fepsilon, int its); #endif diff --git a/src/stencil.h b/src/stencil.h new file mode 100644 index 00000000..cfbc7f2a --- /dev/null +++ b/src/stencil.h @@ -0,0 +1,45 @@ +/***************************************************************************** + * + * stencil.h + * + * Lattice Boltzmann velocities/weights re-interpreted as a + * finite-difference stencil. + * + * + * 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) + * + *****************************************************************************/ + +#ifndef LUDWIG_STENCIL_H +#define LUDWIG_STENCIL_H + +#include +#include "cartesian.h" /* not used explicitly here, but required ... */ + +typedef struct stencil_s stencil_t; + +struct stencil_s { + int8_t ndim; /* 2 or 3 */ + int8_t npoints; /* or velocities */ + int8_t ** cv; /* cv[npoints][ndim] */ + double * wlaplacian; /* weights for Laplacian */ + double * wgradients; /* weights for Gradient */ +}; + +int stencil_create(int npoints, stencil_t ** s); +int stencil_free(stencil_t ** s); +int stencil_finalise(stencil_t * s); + +/* Could be part of the lattice Boltzmann definition, but used with stencils */ +/* Table for 1/sqrt(cx^2 + cy^2 + cz^2) indexed by c^2 */ +/* We set rcs[0] = 0, rcs[1] = 1, etc... */ + +#define LB_RCS_TABLE(rcs) \ + const double rcs[4] = {0.0, 1.0, 1.0/sqrt(2.0), 1.0/sqrt(3.0)}; + +#endif diff --git a/src/stencil_d3q19.c b/src/stencil_d3q19.c new file mode 100644 index 00000000..49bcb38d --- /dev/null +++ b/src/stencil_d3q19.c @@ -0,0 +1,79 @@ +/***************************************************************************** + * + * stencil_d3q19.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 "stencil_d3q19.h" + +/***************************************************************************** + * + * stencil_d3q19_create + * + *****************************************************************************/ + +int stencil_d3q19_create(stencil_t ** stencil) { + + stencil_t * s = NULL; + + s = (stencil_t *) calloc(1, sizeof(stencil_t)); + assert(s); + + s->ndim = 3; + s->npoints = NVEL_D3Q19; + s->cv = (int8_t **) calloc(NVEL_D3Q19, sizeof(int8_t *)); + s->wlaplacian = (double *) calloc(NVEL_D3Q19, sizeof(double)); + s->wgradients = (double *) calloc(NVEL_D3Q19, sizeof(double)); + + if (s->cv == NULL) goto err; + if (s->wlaplacian == NULL) goto err; + if (s->wgradients == NULL) goto err; + + s->cv[0] = (int8_t *) calloc(s->ndim*s->npoints, sizeof(int8_t)); + if (s->cv[0] == NULL) goto err; + + for (int p = 1; p < s->npoints; p++) { + s->cv[p] = s->cv[p-1] + s->ndim; + } + + /* Set velocities/weights/stencil */ + { + double wlap0 = 0.0; + LB_CV_D3Q19(cv); + LB_WEIGHTS_D3Q19(wv); + for (int p = 0; p < s->npoints; p++) { + for (int ia = 0; ia < s->ndim; ia++) { + s->cv[p][ia] = cv[p][ia]; + } + s->wlaplacian[p] = -36.0*wv[p]; + s->wgradients[p] = +3.0*wv[p]; + if (p > 0) wlap0 += s->wlaplacian[p]; + } + s->wlaplacian[0] = -wlap0; + s->wgradients[0] = 0.0; + } + + *stencil = s; + return 0; + + err: + + if (s->wgradients) free(s->wgradients); + if (s->wlaplacian) free(s->wlaplacian); + if (s->cv) free(s->cv); + + *stencil = NULL; + + return -1; +} diff --git a/src/stencil_d3q19.h b/src/stencil_d3q19.h new file mode 100644 index 00000000..ce0b0086 --- /dev/null +++ b/src/stencil_d3q19.h @@ -0,0 +1,24 @@ +/***************************************************************************** + * + * stencil_d3q19.h + * + * A 19 point finite deifferrence stencil following lb_d3q19.h. + * + * 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) + * + *****************************************************************************/ + +#ifndef LUDWIG_STENCIL_D3Q19_H +#define LUDWIG_STENCIL_D3Q19_H + +#include "lb_d3q19.h" +#include "stencil.h" + +int stencil_d3q19_create(stencil_t ** s); + +#endif diff --git a/src/stencil_d3q27.c b/src/stencil_d3q27.c new file mode 100644 index 00000000..6a0eb755 --- /dev/null +++ b/src/stencil_d3q27.c @@ -0,0 +1,84 @@ +/***************************************************************************** + * + * stencil_d3q27.c + * + * Finite difference stencils based on lb_d3q27.h weights. + * + * + * 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 "stencil_d3q27.h" + +/***************************************************************************** + * + * stencil_d3q27_create + * + *****************************************************************************/ + +int stencil_d3q27_create(stencil_t ** stencil) { + + stencil_t * s = NULL; + + assert(stencil); + + s = (stencil_t *) calloc(1, sizeof(stencil_t)); + assert(s); + + s->ndim = 3; + s->npoints = NVEL_D3Q27; + s->cv = (int8_t **) calloc(NVEL_D3Q27, sizeof(int8_t *)); + s->wlaplacian = (double *) calloc(NVEL_D3Q27, sizeof(double)); + s->wgradients = (double *) calloc(NVEL_D3Q27, sizeof(double)); + + if (s->cv == NULL) goto err; + if (s->wlaplacian == NULL) goto err; + if (s->wgradients == NULL) goto err; + + s->cv[0] = (int8_t *) calloc(s->ndim*s->npoints, sizeof(int8_t)); + if (s->cv[0] == NULL) goto err; + + for (int p = 1; p < s->npoints; p++) { + s->cv[p] = s->cv[p-1] + s->ndim; + } + + /* Set velocities/weights/stencil */ + { + double wlap0 = 0.0; + LB_CV_D3Q27(cv); + LB_WEIGHTS_D3Q27(wv); + for (int p = 0; p < s->npoints; p++) { + for (int ia = 0; ia < s->ndim; ia++) { + s->cv[p][ia] = cv[p][ia]; + } + s->wlaplacian[p] = -216.0*wv[p]; + s->wgradients[p] = 3.0*wv[p]; + if (p > 0) wlap0 += s->wlaplacian[p]; + } + /* Central point */ + s->wlaplacian[0] = -wlap0; + s->wgradients[0] = 0.0; + } + + *stencil = s; + return 0; + + err: + + if (s->wgradients) free(s->wgradients); + if (s->wlaplacian) free(s->wlaplacian); + if (s->cv) free(s->cv); + + *stencil = NULL; + + return -1; +} diff --git a/src/stencil_d3q27.h b/src/stencil_d3q27.h new file mode 100644 index 00000000..ca550d44 --- /dev/null +++ b/src/stencil_d3q27.h @@ -0,0 +1,25 @@ +/***************************************************************************** + * + * stencil_d3q27.h + * + * A finite difference stencil following lb_d3q27.h. + * + * + * 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) + * + *****************************************************************************/ + +#ifndef LUDWIG_STENCIL_D3Q27_H +#define LUDWIG_STENCIL_D3Q27_H + +#include "lb_d3q27.h" +#include "stencil.h" + +int stencil_d3q27_create(stencil_t ** s); + +#endif diff --git a/src/stencil_d3q7.c b/src/stencil_d3q7.c new file mode 100644 index 00000000..9497fa1f --- /dev/null +++ b/src/stencil_d3q7.c @@ -0,0 +1,87 @@ +/***************************************************************************** + * + * stencil_d3q7.c + * + * A 7-point stencil in three dimensions. + * + * + * 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 "stencil_d3q7.h" + +/***************************************************************************** + * + * stencil_d3q7_create + * + * The LB weights are {2.0/8.0, 1.0/8.0, ...} + * Laplacian. The weights should be {-6.0, +1.0, ...} so a factor of 8. + * Gradient. The weights should be { 0.0, +0.5, ...} so a factor of 4. + * + *****************************************************************************/ + +int stencil_d3q7_create(stencil_t ** stencil) { + + stencil_t * s = NULL; + + s = (stencil_t *) calloc(1, sizeof(stencil_t)); + assert(s); + + s->ndim = 3; + s->npoints = NVEL_D3Q7; + s->cv = (int8_t **) calloc(NVEL_D3Q7, sizeof(int8_t *)); + s->wlaplacian = (double *) calloc(NVEL_D3Q7, sizeof(double)); + s->wgradients = (double *) calloc(NVEL_D3Q7, sizeof(double)); + + if (s->cv == NULL) goto err; + if (s->wlaplacian == NULL) goto err; + if (s->wgradients == NULL) goto err; + + s->cv[0] = (int8_t *) calloc(s->ndim*NVEL_D3Q7, sizeof(int8_t)); + if (s->cv[0] == NULL) goto err; + + for (int p = 1; p < s->npoints; p++) { + s->cv[p] = s->cv[p-1] + s->ndim; + } + + /* Set velocities/weights/stencil */ + { + double wlap0 = 0.0; + LB_CV_D3Q7(cv); + LB_WEIGHTS_D3Q7(wv); + for (int p = 0; p < s->npoints; p++) { + for (int ia = 0; ia < s->ndim; ia++) { + s->cv[p][ia] = cv[p][ia]; + } + s->wlaplacian[p] = -8.0*wv[p]; /* 2/8, 1/8 -> 2, 1 */ + s->wgradients[p] = +4.0*wv[p]; /* 1/8 -> 1/2 */ + if (p > 0) wlap0 += s->wlaplacian[p]; + } + /* We must have wlaplacian[0] = -\sum_{p=1} wlaplacian[p]. */ + /* No contribution to the gradient from the central point. */ + s->wlaplacian[0] = -wlap0; + s->wgradients[0] = 0.0; + } + + *stencil = s; + return 0; + + err: + + if (s->wgradients) free(s->wgradients); + if (s->wlaplacian) free(s->wlaplacian); + if (s->cv) free(s->cv); + + *stencil = NULL; + + return -1; +} diff --git a/src/stencil_d3q7.h b/src/stencil_d3q7.h new file mode 100644 index 00000000..9eeefeb7 --- /dev/null +++ b/src/stencil_d3q7.h @@ -0,0 +1,34 @@ +/***************************************************************************** + * + * stencil_d3q7.h + * + * A 7-point stencil inn three dimensions. + * + * + * 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) + * + *****************************************************************************/ + +#ifndef LUDWIG_STENCIL_D3Q7_H +#define LUDWIG_STENCIL_D3Q7_H + +#include +#include "stencil.h" + +enum {NVEL_D3Q7 = 7}; + +#define LB_CV_D3Q7(cv) const int8_t cv[NVEL_D3Q7][3] = { \ +{0,0,0}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, {0, 0, -1}, {0, -1, 0}, {-1, 0, 0}}; + +/* The weights are |0| = 2 and |1| = 1 (all over 8). */ +#define LB_WEIGHTS_D3Q7(wv) const double wv[NVEL_D3Q7] = { \ +2.0/8.0, 1.0/8.0, 1.0/8.0, 1.0/8.0, 1.0/8.0, 1.0/8.0, 1.0/8.0 }; + +int stencil_d3q7_create(stencil_t ** s); + +#endif diff --git a/src/stencils.c b/src/stencils.c new file mode 100644 index 00000000..598762bc --- /dev/null +++ b/src/stencils.c @@ -0,0 +1,85 @@ +/***************************************************************************** + * + * stencils.c + * + * Factory method for stencils. + * + * + * 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 "stencil_d3q7.h" +#include "stencil_d3q19.h" +#include "stencil_d3q27.h" + +/***************************************************************************** + * + * stencil_create + * + *****************************************************************************/ + +int stencil_create(int npoints, stencil_t ** s) { + + int ifail = 0; + + switch (npoints) { + case NVEL_D3Q7: + ifail = stencil_d3q7_create(s); + break; + case NVEL_D3Q19: + ifail = stencil_d3q19_create(s); + break; + case NVEL_D3Q27: + ifail = stencil_d3q27_create(s); + break; + default: + ifail = -1; + } + + return ifail; +} + +/***************************************************************************** + * + * stencil_free + * + *****************************************************************************/ + +int stencil_free(stencil_t ** s) { + + assert(s); + assert(*s); + + stencil_finalise(*s); + free(*s); + *s = NULL; + + return 0; +} + +/***************************************************************************** + * + * stencil_finalise + * + *****************************************************************************/ + +int stencil_finalise(stencil_t * s) { + + assert(s); + + free(s->wgradients); + free(s->wlaplacian); + free(s->cv[0]); + free(s->cv); + + return 0; +} diff --git a/src/util_petsc.c b/src/util_petsc.c new file mode 100644 index 00000000..b3504396 --- /dev/null +++ b/src/util_petsc.c @@ -0,0 +1,81 @@ +/***************************************************************************** + * + * util_petsc.c + * + * A facade. + * + * 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 "util_petsc.h" + +/***************************************************************************** + * + * PetscInitialised + * + * A slightly adapted version. + * + *****************************************************************************/ + +int PetscInitialised(int * isInitialised) { + + PetscBool havePetsc = PETSC_FALSE; + PetscInitialized(&havePetsc); + + *isInitialised = 0; + if (havePetsc == PETSC_TRUE) *isInitialised = 1; + + return 0; +} + +#ifdef PETSC +/* Nothing more required. */ +#else + +/***************************************************************************** + * + * PetscInitialize + * + *****************************************************************************/ + +PetscErrorCode PetscInitialize(int * argc, char *** argv, const char * file, + const char * help) { + + return 0; +} + +/***************************************************************************** + * + * PetscFinalize + * + *****************************************************************************/ + +PetscErrorCode PetscFinalize(void) { + + return 0; +} + +/***************************************************************************** + * + * PetscInitialized + * + *****************************************************************************/ + +PetscErrorCode PetscInitialized(PetscBool * isInitialised) { + + assert(isInitialised); + + *isInitialised = (PetscBool) 0; /* Always zero */ + + return 0; +} + +#endif diff --git a/src/util_petsc.h b/src/util_petsc.h new file mode 100644 index 00000000..37049484 --- /dev/null +++ b/src/util_petsc.h @@ -0,0 +1,37 @@ +/***************************************************************************** + * + * util_petsc.h + * + * A stub interface to deal with Petsc availability. + * The idea is to use PetscInitialised() [sic]. + * + * 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) + * + *****************************************************************************/ + +#ifndef LUDWIG_UTIL_PETSC_H +#define LUDWIG_UTIL_PETSC_H + +#ifdef PETSC +#include "petscsys.h" +#else + +/* Stub replacements for ... */ + +typedef enum {PETSC_FALSE, PETSC_TRUE} PetscBool; +typedef int PetscErrorCode; + +PetscErrorCode PetscInitialize(int * argc, char *** argv, const char * file, + const char * help); +PetscErrorCode PetscFinalize(void); +PetscErrorCode PetscInitialized(PetscBool * isInitialised); +#endif + +int PetscInitialised(int * isInitialised); + +#endif diff --git a/tests/unit/Makefile b/tests/unit/Makefile index 35a2e14c..06dd2179 100644 --- a/tests/unit/Makefile +++ b/tests/unit/Makefile @@ -45,6 +45,11 @@ LIBS = $(BLIBS) $(SRC)/libludwig.a -lm MPI_RUN = $(LAUNCH_MPIRUN_CMD) $(MPIRUN_NTASK_FLAG) $(MPIRUN_NTASKS) +ifdef HAVE_PETSC +INCL += $(PETSC_INCL) +LIBS += $(PETSC_LIB) +endif + #------------------------------------------------------------------------------ # Rules #------------------------------------------------------------------------------ diff --git a/tests/unit/test_fe_electro.c b/tests/unit/test_fe_electro.c index 38953286..0c3094c6 100644 --- a/tests/unit/test_fe_electro.c +++ b/tests/unit/test_fe_electro.c @@ -293,6 +293,7 @@ static int do_test3(pe_t * pe, cs_t * cs, physics_t * phys) { /* With a potential (only): explicitly set the relevant terms * (we need to know the differencing scheme in fe_electro.c). */ + /* Note these values don't test the sign of the field terms. */ psi0 = 1.0; psi1 = 2.0; diff --git a/tests/unit/test_nernst_planck.c b/tests/unit/test_nernst_planck.c index ad790ff1..7579f6bd 100644 --- a/tests/unit/test_nernst_planck.c +++ b/tests/unit/test_nernst_planck.c @@ -23,18 +23,12 @@ #include "pe.h" #include "coords.h" #include "physics.h" -#include "control.h" #include "map.h" -#include "psi.h" #include "psi_sor.h" -#include "psi_stats.h" #include "fe_electro.h" #include "nernst_planck.h" -#include "util_fopen.h" -#include "tests.h" static int test_nernst_planck_driver(pe_t * pe); -static int test_io(cs_t * cs, psi_t * psi, int tstep); /***************************************************************************** * @@ -94,7 +88,6 @@ static int test_nernst_planck_driver(pe_t * pe) { int nlocal[3]; int noffst[3]; - int test_output_required = 0; int mpi_cartsz[3]; int mpi_cartcoords[3]; @@ -147,7 +140,7 @@ static int test_nernst_planck_driver(pe_t * pe) { opts.epsilon2 = epsilon; psi_create(pe, cs, &opts, &psi); - /* Care. the free energy gets the temperatue from global physics_t. */ + /* Care. the free energy gets the temperature from global physics_t. */ fe_electro_create(pe, psi, &fe); /* wall charge density */ @@ -208,13 +201,18 @@ static int test_nernst_planck_driver(pe_t * pe) { map_halo(map); psi_halo_psi(psi); - psi_sor_poisson(psi, -1); + + { + psi_solver_sor_t * sor = NULL; + psi_solver_sor_create(psi, &sor); + psi_solver_sor_solve(sor, -1); + psi_solver_sor_free(&sor); + } + psi_halo_rho(psi); nernst_planck_driver(psi, (fe_t *) fe, map); - if (test_output_required) test_io(cs, psi, 0); - /* We adopt a rather simple way to extract the answer from the * MPI task holding the centre of the system. The charge * density must be > 0 to compute the debye length and the @@ -264,93 +262,3 @@ static int test_nernst_planck_driver(pe_t * pe) { return 0; } - -/***************************************************************************** - * - * test_io - * - *****************************************************************************/ - -static int test_io(cs_t * cs, psi_t * psi, int tstep) { - - int ntotal[3]; - int nlocal[3]; - int ic, jc, kc, index; - - double * field; /* 1-d field (local) */ - double * psifield; /* 1-d psi field for output */ - double * rho0field; /* 1-d rho0 field for output */ - double * rho1field; /* 1-d rho0 field for output */ - - char filename[BUFSIZ]; - FILE * out; - MPI_Comm comm; - - cs_nlocal(cs, nlocal); - cs_ntotal(cs, ntotal); - cs_cart_comm(cs, &comm); - - jc = 2; - kc = 2; - - /* 1D output. calloc() is used to zero the arays, then - * MPI_Gather to get complete picture. */ - - field = (double *) calloc(nlocal[X], sizeof(double)); - psifield = (double *) calloc(ntotal[X], sizeof(double)); - rho0field = (double *) calloc(ntotal[X], sizeof(double)); - rho1field = (double *) calloc(ntotal[X], sizeof(double)); - assert(field); - assert(psifield); - assert(rho0field); - assert(rho1field); - if (field == NULL) pe_fatal(psi->pe, "calloc(field) failed\n"); - if (psifield == NULL) pe_fatal(psi->pe, "calloc(psifield) failed\n"); - if (rho0field == NULL) pe_fatal(psi->pe, "calloc(rho0field) failed\n"); - if (rho1field == NULL) pe_fatal(psi->pe, "calloc(rho1field) failed\n"); - - for (ic = 1; ic <= nlocal[X]; ic++) { - - index = cs_index(cs, ic, jc, kc); - psi_psi(psi, index, field + ic - 1); - } - - MPI_Gather(field, nlocal[X], MPI_DOUBLE, - psifield, nlocal[X], MPI_DOUBLE, 0, comm); - - for (ic = 1; ic <= nlocal[X]; ic++) { - index = cs_index(cs, ic, jc, kc); - psi_rho(psi, index, 0, field + ic - 1); - } - - MPI_Gather(field, nlocal[X], MPI_DOUBLE, - rho0field, nlocal[X], MPI_DOUBLE, 0, comm); - - for (ic = 1; ic <= nlocal[X]; ic++) { - index = cs_index(cs, ic, jc, kc); - psi_rho(psi, index, 1, field + ic - 1); - } - - MPI_Gather(field, nlocal[X], MPI_DOUBLE, - rho1field, nlocal[X], MPI_DOUBLE, 0, comm); - - if (cs_cart_rank(cs) == 0) { - - sprintf(filename, "np_test-%d.dat", tstep); - out = util_fopen(filename, "w"); - if (out == NULL) pe_fatal(psi->pe, "Could not open %s\n", filename); - - for (ic = 1; ic <= ntotal[X]; ic++) { - fprintf(out, "%d %14.7e %14.7e %14.7e\n", ic, psifield[ic-1], - rho0field[ic-1], rho1field[ic-1]); - } - fclose(out); - } - - free(rho1field); - free(rho0field); - free(psifield); - free(field); - - return 0; -} diff --git a/tests/unit/test_psi.c b/tests/unit/test_psi.c index 97cca1b2..ec0d2781 100644 --- a/tests/unit/test_psi.c +++ b/tests/unit/test_psi.c @@ -40,6 +40,9 @@ int test_psi_suite(void) { pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + /* Changes in psi_t should be accompanied by changes in tests... */ + assert(sizeof(psi_t) == 184); + test_psi_initialise(pe); test_psi_create(pe); test_psi_psi_set(pe); @@ -112,6 +115,8 @@ int test_psi_initialise(pe_t * pe) { /* Solver options */ /* Assume correctly covered in solver options tests ... */ assert(psi.solver.psolver == PSI_POISSON_SOLVER_SOR); + assert(psi.stencil); + assert(psi.stencil->npoints == psi.solver.nstencil); /* Nernst Planck */ assert(psi.multisteps == opts.nsmallstep); diff --git a/tests/unit/test_psi_solver_options.c b/tests/unit/test_psi_solver_options.c index b4c2ebab..54b36e01 100644 --- a/tests/unit/test_psi_solver_options.c +++ b/tests/unit/test_psi_solver_options.c @@ -25,6 +25,7 @@ int test_psi_poisson_solver_to_string(void); int test_psi_poisson_solver_from_string(void); +int test_psi_poisson_solver_default(int argc, char ** argv); int test_psi_solver_options_default(void); int test_psi_solver_options_type(void); int test_psi_solver_options_to_json(void); @@ -36,7 +37,7 @@ int test_psi_solver_options_from_json(void); * *****************************************************************************/ -int test_psi_solver_options_suite(void) { +int test_psi_solver_options_suite(int argc, char ** argv) { pe_t * pe = NULL; @@ -47,6 +48,7 @@ int test_psi_solver_options_suite(void) { test_psi_poisson_solver_to_string(); test_psi_poisson_solver_from_string(); + test_psi_poisson_solver_default(argc, argv); test_psi_solver_options_default(); test_psi_solver_options_type(); test_psi_solver_options_to_json(); @@ -132,6 +134,40 @@ int test_psi_poisson_solver_from_string(void) { return ifail; } +/***************************************************************************** + * + * test_psi_poisson_solver_default + * + *****************************************************************************/ + +int test_psi_poisson_solver_default(int argc, char ** argv) { + + int ifail = 0; + + { + /* No Petsc */ + psi_poisson_solver_enum_t psolver = psi_poisson_solver_default(); + if (psolver != PSI_POISSON_SOLVER_SOR) ifail = -1; + assert(ifail == 0); + } + + { + /* Petsc. This requires check with PetscInitialised(). */ + int havePetsc = 0; + + PetscInitialize(&argc, &argv, (char *) 0, NULL); + PetscInitialised(&havePetsc); + if (havePetsc) { + psi_poisson_solver_enum_t psolver = psi_poisson_solver_default(); + if (psolver != PSI_POISSON_SOLVER_PETSC) ifail = -1; + assert(ifail == 0); + } + PetscFinalize(); + } + + return ifail; +} + /***************************************************************************** * * test_psi_solver_options_default @@ -143,7 +179,7 @@ int test_psi_solver_options_default(void) { int ifail = 0; psi_solver_options_t pso = psi_solver_options_default(); - assert(pso.psolver == PSI_POISSON_SOLVER_SOR); + assert(pso.psolver == psi_poisson_solver_default()); assert(pso.maxits == 10000); assert(pso.verbose == 0); assert(pso.nfreq == INT_MAX); diff --git a/tests/unit/test_psi_solver_petsc.c b/tests/unit/test_psi_solver_petsc.c new file mode 100644 index 00000000..6d87e320 --- /dev/null +++ b/tests/unit/test_psi_solver_petsc.c @@ -0,0 +1,67 @@ +/***************************************************************************** + * + * test_psi_solver_petsc.c + * + * Some ducking and diving is required depending on whether Petsc + * is available. + * + * + * 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 "psi_petsc.h" + +int test_psi_solver_petsc_create(pe_t * pe); + +/***************************************************************************** + * + * test_psi_solver_petsc_suite + * + *****************************************************************************/ + +int test_psi_solver_petsc_suite(void) { + + pe_t * pe = NULL; + + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + test_psi_solver_petsc_create(pe); + + pe_info(pe, "%-9s %s\n", "PASS", __FILE__); + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_psi_solver_petsc_create + * + *****************************************************************************/ + +int test_psi_solver_petsc_create(pe_t * pe) { + + int ifail = 0; + int isInitialised = 0; + + PetscInitialised(&isInitialised); + + if (isInitialised == 0) { + psi_t * psi = NULL; + psi_solver_petsc_t * petsc = NULL; + + ifail = psi_solver_petsc_create(psi, &petsc); + assert(ifail != 0); + if (ifail != 0) ifail = 0; + } + + return 0; +} diff --git a/tests/unit/test_psi_sor.c b/tests/unit/test_psi_sor.c index 5b313fb2..464dae6d 100644 --- a/tests/unit/test_psi_sor.c +++ b/tests/unit/test_psi_sor.c @@ -22,20 +22,19 @@ #include "pe.h" #include "coords.h" -#include "control.h" #include "psi_sor.h" - #include "util.h" -#include "psi_stats.h" -#include "tests.h" #define fe_fake_t void -int test_psi_sor_poisson(pe_t * pe); -int test_psi_sor_vare_poisson(pe_t * pe); +int test_psi_solver_sor_create(pe_t * pe); +int test_psi_solver_sor_solve(pe_t * pe); + +int test_psi_solver_sor_var_epsilon_create(pe_t * pe); +int test_psi_solver_sor_var_epsilon_solve(pe_t * pe); static int test_charge1_set(psi_t * psi); -static int test_charge1_exact(psi_t * obj, f_vare_t fepsilon); +static int test_charge1_exact(psi_t * obj, var_epsilon_ft fepsilon); #define REF_PERMEATIVITY 1.0 static int fepsilon_constant(fe_fake_t * fe, int index, double * epsilon); @@ -52,20 +51,13 @@ int test_psi_sor_suite(void) { pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); - { - int mpisz = pe_mpi_size(pe); + test_psi_solver_sor_create(pe); + test_psi_solver_sor_solve(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); + test_psi_solver_sor_var_epsilon_create(pe); + test_psi_solver_sor_var_epsilon_solve(pe); - pe_info(pe, "PASS ./unit/test_psi_sor\n"); - } - } + pe_info(pe, "%-9s %s\n", "PASS", __FILE__); pe_free(pe); @@ -74,22 +66,53 @@ int test_psi_sor_suite(void) { /***************************************************************************** * - * test_psi_sor_poisson + * test_psi_solver_sor_create * - * Set rho(z = 1) = + (1/2NxNy) - * rho(z = Lz) = + (1/2NxNy) - * rho = - 1/(NxNy*(Nz-2)) everywhere else. + *****************************************************************************/ + +int test_psi_solver_sor_create(pe_t * pe) { + + int ifail = 0; + int nhalo = 2; + + cs_t * cs = NULL; + psi_t * psi = NULL; + psi_options_t opts = psi_options_default(nhalo); + + cs_create(pe, &cs); + cs_nhalo_set(cs, nhalo); + cs_init(cs); + psi_create(pe, cs, &opts, &psi); + + { + psi_solver_sor_t * sor = NULL; + + ifail = psi_solver_sor_create(psi, &sor); + assert(ifail == 0); + assert(sor->psi == psi); + assert(sor->super.impl->solve); + + psi_solver_sor_free(&sor); + assert(sor == NULL); + } + + return ifail; +} + +/***************************************************************************** * - * This is a fully periodic system with zero total charge. + * test_psi_solver_sor_solve * *****************************************************************************/ -int test_psi_sor_poisson(pe_t * pe) { +int test_psi_solver_sor_solve(pe_t * pe) { + + int nhalo = 1; + int ntotal[3] = {2, 2, 64}; /* Always quasi-1d system in z */ cs_t * cs = NULL; psi_t * psi = NULL; - int ntotal[3] = {4, 4, 64}; - int nhalo = 1; + psi_solver_sor_t * sor = NULL; assert(pe); @@ -119,12 +142,16 @@ int test_psi_sor_poisson(pe_t * pe) { psi_halo_psi(psi); psi_halo_rho(psi); + psi_solver_sor_create(psi, &sor); + /* Time step is -1 for no output. */ - psi_sor_poisson(psi, -1); + psi_solver_sor_solve(sor, -1); test_charge1_exact(psi, fepsilon_constant); + /* Clear up */ + psi_solver_sor_free(&sor); psi_free(&psi); cs_free(cs); @@ -133,7 +160,44 @@ int test_psi_sor_poisson(pe_t * pe) { /***************************************************************************** * - * test_psi_sor_vare_poisson + * test_psi_solver_sor_var_epsilon_create + * + *****************************************************************************/ + +int test_psi_solver_sor_var_epsilon_create(pe_t * pe) { + + int ifail = 0; + int nhalo = 2; + + cs_t * cs = NULL; + psi_t * psi = NULL; + psi_options_t opts = psi_options_default(nhalo); + + cs_create(pe, &cs); + cs_nhalo_set(cs, nhalo); + cs_init(cs); + psi_create(pe, cs, &opts, &psi); + + { + psi_solver_sor_t * sor = NULL; + var_epsilon_t user = {.fe = NULL, .epsilon = fepsilon_constant}; + + ifail = psi_solver_sor_var_epsilon_create(psi, user, &sor); + assert(ifail == 0); + assert(sor->psi == psi); + assert(sor->epsilon); + assert(sor->super.impl->solve); + + psi_solver_sor_free(&sor); + assert(sor == NULL); + } + + return ifail; +} + +/***************************************************************************** + * + * test_psi_solver_sor_var_epsilon_solve * * Same problem as above, but use variable epsilon solver (albeit with * fixed epsilon here). @@ -143,12 +207,14 @@ int test_psi_sor_poisson(pe_t * pe) { * *****************************************************************************/ -int test_psi_sor_vare_poisson(pe_t * pe) { +int test_psi_solver_sor_var_epsilon_solve(pe_t * pe) { + + int nhalo = 1; + int ntotal[3] = {2, 2, 64}; /* Always quasi-1d system in z */ cs_t * cs = NULL; psi_t * psi = NULL; - int ntotal[3] = {4, 4, 64}; - int nhalo = 1; + psi_solver_sor_t * sor = NULL; assert(pe); @@ -172,7 +238,14 @@ int test_psi_sor_vare_poisson(pe_t * pe) { psi_halo_rho(psi); /* Time step is -1 to avoid output */ - psi_sor_vare_poisson(psi, NULL, fepsilon_constant, -1); + /* FIXME: want some abstraction in args ... */ + { + var_epsilon_t user = {.fe = NULL, .epsilon = fepsilon_constant}; + + psi_solver_sor_var_epsilon_create(psi, user, &sor); + psi_solver_sor_var_epsilon_solve(sor, -1); + psi_solver_sor_free(&sor); + } test_charge1_exact(psi, fepsilon_constant); @@ -205,10 +278,6 @@ static int test_charge1_set(psi_t * psi) { double ltot[3]; double rho0, rho1; - - double rho_min[4]; /* For psi_stats */ - double rho_max[4]; /* For psi_stats */ - double rho_tot[4]; /* For psi_stats */ MPI_Comm comm; cs_ltot(psi->cs, ltot); @@ -221,7 +290,7 @@ static int test_charge1_set(psi_t * psi) { rho1 = 1.0 / (ltot[X]*ltot[Y]*(ltot[Z] - 2.0)); /* Interior values */ psi_nk(psi, &nk); - test_assert(nk == 2); + assert(nk == 2); /* Throughout set to rho1 */ @@ -266,27 +335,6 @@ static int test_charge1_set(psi_t * psi) { } } - psi_stats_reduce(psi, rho_min, rho_max, rho_tot, 0, comm); - - if (pe_mpi_rank(psi->pe) == 0) { - /* psi all zero */ - test_assert(fabs(rho_min[0] - 0.0) < DBL_EPSILON); - test_assert(fabs(rho_max[0] - 0.0) < DBL_EPSILON); - test_assert(fabs(rho_tot[0] - 0.0) < DBL_EPSILON); - /* First rho0 interior */ - test_assert(fabs(rho_min[1] - 0.0) < DBL_EPSILON); - test_assert(fabs(rho_max[1] - rho0) < DBL_EPSILON); - test_assert(fabs(rho_tot[1] - 1.0) < DBL_EPSILON); - /* Next rho1 edge */ - test_assert(fabs(rho_min[2] - 0.0) < DBL_EPSILON); - test_assert(fabs(rho_max[2] - rho1) < DBL_EPSILON); - test_assert(fabs(rho_tot[2] - 1.0) < FLT_EPSILON); - /* Total rho_elec */ - test_assert(fabs(rho_min[3] + rho1) < DBL_EPSILON); /* + because valency is - */ - test_assert(fabs(rho_max[3] - rho0) < DBL_EPSILON); - test_assert(fabs(rho_tot[3] - 0.0) < FLT_EPSILON); - } - return 0; } @@ -314,7 +362,7 @@ static int test_charge1_set(psi_t * psi) { * We also recompute the RHS by differencing the SOR solution with * a three point stencil in one dimension to provide a final check. * - * For variable epsilon, described by the f_vare_t fepsilon, + * For variable epsilon, described by the var_epsilon_ft fepsilon, * we set up a difference scheme using a three-point stencil: * * e(i+1/2) psi(i+1) - [ e(i+1/2) + e(i-1/2) ] psi(i) + e(i-1/2) psi(i-1) @@ -324,7 +372,7 @@ static int test_charge1_set(psi_t * psi) { * *****************************************************************************/ -static int test_charge1_exact(psi_t * obj, f_vare_t fepsilon) { +static int test_charge1_exact(psi_t * obj, var_epsilon_ft fepsilon) { int k, kp1, km1, index; int nlocal[3]; @@ -441,7 +489,7 @@ static int test_charge1_exact(psi_t * obj, f_vare_t fepsilon) { } /* Total rho should be unchanged at zero. */ - test_assert(fabs(rhotot) < tolerance); + assert(fabs(rhotot) < tolerance); free(b); free(a); diff --git a/tests/unit/test_stencil_d3q19.c b/tests/unit/test_stencil_d3q19.c new file mode 100644 index 00000000..711478c8 --- /dev/null +++ b/tests/unit/test_stencil_d3q19.c @@ -0,0 +1,65 @@ +/***************************************************************************** + * + * test_stencil_d3q19.c + * + *****************************************************************************/ + +#include +#include +#include + +#include "pe.h" +#include "stencil_d3q19.h" + +int test_stencil_d3q19_create(void); + +/***************************************************************************** + * + * test_stencil_d3q19_suite + * + *****************************************************************************/ + +int test_stencil_d3q19_suite(void) { + + pe_t * pe = NULL; + + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + test_stencil_d3q19_create(); + + pe_info(pe, "%-9s %s\n", "PASS", __FILE__); + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_stencil_d3q19_create + * + *****************************************************************************/ + +int test_stencil_d3q19_create(void) { + + int ifail = 0; + stencil_t * s = NULL; + + ifail = stencil_d3q19_create(&s); + assert(ifail == 0); + assert(s); + assert(s->ndim == 3); + assert(s->npoints == 19); + assert(s->cv); + assert(s->wlaplacian); + assert(s->wgradients); + + if (s->wlaplacian[0] != 24.0) ifail = -1; + if (s->wgradients[0] != 0.0) ifail = -1; + assert(ifail == 0); + + ifail = stencil_free(&s); + assert(ifail == 0); + assert(s == NULL); + + return ifail; +} diff --git a/tests/unit/test_stencil_d3q27.c b/tests/unit/test_stencil_d3q27.c new file mode 100644 index 00000000..98910269 --- /dev/null +++ b/tests/unit/test_stencil_d3q27.c @@ -0,0 +1,73 @@ +/***************************************************************************** + * + * test_stencil_d3q27.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 + +#include "pe.h" +#include "stencil_d3q27.h" + +int test_stencil_d3q27_create(void); + +/***************************************************************************** + * + * test_stencil_d3q27_suite + * + *****************************************************************************/ + +int test_stencil_d3q27_suite(void) { + + pe_t * pe = NULL; + + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + test_stencil_d3q27_create(); + + pe_info(pe, "%-9s %s\n", "PASS", __FILE__); + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_stencil_d3q27_create + * + *****************************************************************************/ + +int test_stencil_d3q27_create(void) { + + int ifail = 0; + stencil_t * s = NULL; + + ifail = stencil_d3q27_create(&s); + assert(ifail == 0); + assert(s); + assert(s->ndim == 3); + assert(s->npoints == 27); + assert(s->cv); + assert(s->wlaplacian); + assert(s->wgradients); + + if (s->wlaplacian[0] != 152.0) ifail = -1; + if (s->wgradients[0] != 0.0) ifail = -1; + assert(ifail == 0); + + ifail = stencil_free(&s); + assert(ifail == 0); + assert(s == NULL); + + return ifail; +} diff --git a/tests/unit/test_stencil_d3q7.c b/tests/unit/test_stencil_d3q7.c new file mode 100644 index 00000000..aa8a6bc5 --- /dev/null +++ b/tests/unit/test_stencil_d3q7.c @@ -0,0 +1,66 @@ +/***************************************************************************** + * + * test_stencil_d3q7.c + * + *****************************************************************************/ + +#include +#include +#include + +#include "pe.h" +#include "stencil_d3q7.h" + +int test_stencil_d3q7_create(void); + +/***************************************************************************** + * + * test_stencil_d3q7_suite + * + *****************************************************************************/ + +int test_stencil_d3q7_suite(void) { + + pe_t * pe = NULL; + + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + test_stencil_d3q7_create(); + + pe_info(pe, "%-9s %s\n", "PASS", __FILE__); + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_stencil_d3q7_create + * + *****************************************************************************/ + +int test_stencil_d3q7_create(void) { + + int ifail = 0; + stencil_t * s = NULL; + + ifail = stencil_d3q7_create(&s); + + assert(ifail == 0); + assert(s); + assert(s->ndim == 3); + assert(s->npoints == NVEL_D3Q7); + assert(s->cv); + assert(s->wlaplacian); + assert(s->wgradients); + + if (s->wlaplacian[0] != 6.0) ifail = -1; + if (s->wgradients[0] != 0.0) ifail = -1; + assert(ifail == 0); + + ifail = stencil_free(&s); + assert(ifail == 0); + assert(s == NULL); + + return ifail; +} diff --git a/tests/unit/test_stencils.c b/tests/unit/test_stencils.c new file mode 100644 index 00000000..a06afd91 --- /dev/null +++ b/tests/unit/test_stencils.c @@ -0,0 +1,130 @@ +/***************************************************************************** + * + * test_stencils.c + * + * Generic tests for the various finite difference stencils. + * + * + * 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 + +#include "pe.h" +#include "stencil.h" + +int test_stencil_create(int npoints); + +/***************************************************************************** + * + * test_stencils_suite + * + *****************************************************************************/ + +int test_stencils_suite(void) { + + pe_t * pe = NULL; + + pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); + + test_stencil_create(7); + test_stencil_create(19); + test_stencil_create(27); + + pe_info(pe, "%-9s %s\n", "PASS", __FILE__); + pe_free(pe); + + return 0; +} + +/***************************************************************************** + * + * test_stencil_create + * + * These are generic tests related to the Laplacian and Gradient + * computations. Specific errors should be captured by the + * relevant specific test. + * + *****************************************************************************/ + +int test_stencil_create(int npoints) { + + int ifail = 0; + stencil_t * s = NULL; + + ifail = stencil_create(npoints, &s); + assert(ifail == 0); + assert(s); + assert(s->npoints == npoints); + + /* Laplacian central weight */ + + { + double sum = 0.0; + for (int p = 1; p < s->npoints; p++) { + sum += s->wlaplacian[p]; + } + if (fabs(s->wlaplacian[0] + sum) > DBL_EPSILON) ifail = -1; + assert(ifail == 0); + } + + /* Gradient */ + + /* d_x */ + { + double fgrad[3] = {0}; + for (int p = 0; p < s->npoints; p++) { + double f = 1.0*s->cv[p][X]; + fgrad[X] += s->wgradients[p]*s->cv[p][X]*f; + fgrad[Y] += s->wgradients[p]*s->cv[p][Y]*f; + fgrad[Z] += s->wgradients[p]*s->cv[p][Z]*f; + } + if (fabs(fgrad[X] - 1.0) > DBL_EPSILON) ifail = -1; + if (fabs(fgrad[Y] - 0.0) > DBL_EPSILON) ifail = -2; + if (fabs(fgrad[Z] - 0.0) > DBL_EPSILON) ifail = -4; + assert(ifail == 0); + } + + /* d_y */ + { + double fgrad[3] = {0}; + for (int p = 0; p < s->npoints; p++) { + double f = 1.0*s->cv[p][Y]; + fgrad[X] += s->wgradients[p]*s->cv[p][X]*f; + fgrad[Y] += s->wgradients[p]*s->cv[p][Y]*f; + fgrad[Z] += s->wgradients[p]*s->cv[p][Z]*f; + } + if (fabs(fgrad[X] - 0.0) > DBL_EPSILON) ifail = -1; + if (fabs(fgrad[Y] - 1.0) > DBL_EPSILON) ifail = -2; + if (fabs(fgrad[Z] - 0.0) > DBL_EPSILON) ifail = -4; + assert(ifail == 0); + } + + /* d_z */ + { + double fgrad[3] = {0}; + for (int p = 0; p < s->npoints; p++) { + double f = 1.0*s->cv[p][Z]; + fgrad[X] += s->wgradients[p]*s->cv[p][X]*f; + fgrad[Y] += s->wgradients[p]*s->cv[p][Y]*f; + fgrad[Z] += s->wgradients[p]*s->cv[p][Z]*f; + } + if (fabs(fgrad[X] - 0.0) > DBL_EPSILON) ifail = -1; + if (fabs(fgrad[Y] - 0.0) > DBL_EPSILON) ifail = -2; + if (fabs(fgrad[Z] - 1.0) > DBL_EPSILON) ifail = -4; + assert(ifail == 0); + } + + stencil_free(&s); + + return ifail; +} + diff --git a/tests/unit/tests.c b/tests/unit/tests.c index fb925904..6610ab31 100644 --- a/tests/unit/tests.c +++ b/tests/unit/tests.c @@ -19,7 +19,7 @@ #include "tests.h" -__host__ int tests_create(void); +__host__ int tests_create(int argc, char ** argv); /***************************************************************************** * @@ -31,7 +31,7 @@ __host__ int main(int argc, char ** argv) { MPI_Init(&argc, &argv); - tests_create(); + tests_create(argc, argv); MPI_Finalize(); @@ -44,7 +44,7 @@ __host__ int main(int argc, char ** argv) { * *****************************************************************************/ -__host__ int tests_create() { +__host__ int tests_create(int argc, char ** argv) { test_pe_suite(); test_coords_suite(); @@ -113,14 +113,19 @@ __host__ int tests_create() { test_phi_ch_suite(); test_polar_active_suite(); - test_psi_solver_options_suite(); + test_psi_solver_options_suite(argc, argv); test_psi_options_suite(); test_psi_suite(); + test_psi_solver_petsc_suite(); test_psi_sor_suite(); test_nernst_planck_suite(); test_lb_prop_suite(); test_random_suite(); test_rt_suite(); + test_stencil_d3q7_suite(); + test_stencil_d3q19_suite(); + test_stencil_d3q27_suite(); + test_stencils_suite(); test_timer_suite(); test_util_suite(); test_util_bits_suite(); diff --git a/tests/unit/tests.h b/tests/unit/tests.h index 92bb42b6..47ebfa7d 100644 --- a/tests/unit/tests.h +++ b/tests/unit/tests.h @@ -96,12 +96,17 @@ int test_phi_bc_inflow_opts_suite(void); int test_phi_bc_inflow_fixed_suite(void); int test_phi_bc_outflow_opts_suite(void); int test_phi_bc_outflow_free_suite(void); -int test_psi_solver_options_suite(void); +int test_psi_solver_options_suite(int argc, char ** argv); int test_psi_options_suite(void); int test_psi_suite(void); +int test_psi_solver_petsc_suite(void); int test_psi_sor_suite(void); int test_random_suite(void); int test_rt_suite(void); +int test_stencil_d3q7_suite(void); +int test_stencil_d3q19_suite(void); +int test_stencil_d3q27_suite(void); +int test_stencils_suite(void); int test_timer_suite(void); int test_util_suite(void); int test_util_bits_suite(void); From 53b0356c0947088d24e570a5c1db55c2991fe949 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 24 Mar 2023 17:26:39 +0000 Subject: [PATCH 39/97] stdint.h missing --- src/stencil.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stencil.h b/src/stencil.h index cfbc7f2a..8b72ab7c 100644 --- a/src/stencil.h +++ b/src/stencil.h @@ -18,6 +18,7 @@ #ifndef LUDWIG_STENCIL_H #define LUDWIG_STENCIL_H +#include #include #include "cartesian.h" /* not used explicitly here, but required ... */ From 2c01963bb1bcf3c2c215822fb46c6c012f58b466 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 24 Mar 2023 17:27:31 +0000 Subject: [PATCH 40/97] Avoid potential buffer overflow --- util/extract.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/util/extract.c b/util/extract.c index a5a51d13..f1577a05 100644 --- a/util/extract.c +++ b/util/extract.c @@ -238,7 +238,8 @@ int main(int argc, char ** argv) { 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); + snprintf(filename, FILENAME_MAX-1, "%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); @@ -267,11 +268,11 @@ int main(int argc, char ** argv) { exit(-1); } else { - char buf[FILENAME_MAX] = {0}; + char buf[BUFSIZ] = {0}; 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, nfile, ifile); + snprintf(filename, BUFSIZ-1, "%s.%3.3d-%3.3d.meta", stub, nfile, ifile); read_meta_data_file(filename, &metadata); } } From dd68e283aa868e9c1d631431d91582441a412818 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 24 Mar 2023 17:28:36 +0000 Subject: [PATCH 41/97] Fix comments --- tests/unit/test_psi_options.c | 2 -- tests/unit/test_psi_sor.c | 1 - 2 files changed, 3 deletions(-) diff --git a/tests/unit/test_psi_options.c b/tests/unit/test_psi_options.c index 54b40002..69dd59c7 100644 --- a/tests/unit/test_psi_options.c +++ b/tests/unit/test_psi_options.c @@ -89,8 +89,6 @@ int test_psi_options_default(void) { assert(fabs(opts.diffacc - 0.0) < DBL_EPSILON); /* Other */ - /* FIXME: this should be replaced */ - if (opts.nsolver != -1) ifail = -1; return ifail; } diff --git a/tests/unit/test_psi_sor.c b/tests/unit/test_psi_sor.c index 464dae6d..d995f8c0 100644 --- a/tests/unit/test_psi_sor.c +++ b/tests/unit/test_psi_sor.c @@ -238,7 +238,6 @@ int test_psi_solver_sor_var_epsilon_solve(pe_t * pe) { psi_halo_rho(psi); /* Time step is -1 to avoid output */ - /* FIXME: want some abstraction in args ... */ { var_epsilon_t user = {.fe = NULL, .epsilon = fepsilon_constant}; From 38dc7bc0c99a6e2ec4284a184cca3c516aaf83f7 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 25 Mar 2023 11:47:06 +0000 Subject: [PATCH 42/97] Remove printf() --- tests/unit/test_psi_options.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/unit/test_psi_options.c b/tests/unit/test_psi_options.c index 69dd59c7..0185dfaa 100644 --- a/tests/unit/test_psi_options.c +++ b/tests/unit/test_psi_options.c @@ -36,8 +36,6 @@ int test_psi_options_suite(void) { pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); /* A change in components requires a test update... */ - - printf("sizeof(psi_options_t): %ld\n", sizeof(psi_options_t)); assert(sizeof(psi_options_t) == 392); assert(PSI_NKMAX >= 2); From 3f790b4079ea6b876d2a843c7667453c82094850 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sat, 25 Mar 2023 11:47:39 +0000 Subject: [PATCH 43/97] Restore system size to allow parallel test to pass --- tests/unit/test_psi_sor.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_psi_sor.c b/tests/unit/test_psi_sor.c index d995f8c0..a97f874d 100644 --- a/tests/unit/test_psi_sor.c +++ b/tests/unit/test_psi_sor.c @@ -108,7 +108,7 @@ int test_psi_solver_sor_create(pe_t * pe) { int test_psi_solver_sor_solve(pe_t * pe) { int nhalo = 1; - int ntotal[3] = {2, 2, 64}; /* Always quasi-1d system in z */ + int ntotal[3] = {4, 4, 64}; /* Always quasi-1d system in z */ cs_t * cs = NULL; psi_t * psi = NULL; @@ -210,7 +210,7 @@ int test_psi_solver_sor_var_epsilon_create(pe_t * pe) { int test_psi_solver_sor_var_epsilon_solve(pe_t * pe) { int nhalo = 1; - int ntotal[3] = {2, 2, 64}; /* Always quasi-1d system in z */ + int ntotal[3] = {4, 4, 64}; /* Always quasi-1d system in z */ cs_t * cs = NULL; psi_t * psi = NULL; From 14a32caab1bb2a8b7242192f08d5389ac236dc41 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 4 Apr 2023 08:59:07 +0100 Subject: [PATCH 44/97] Repair test on GPU --- tests/unit/test_field.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/unit/test_field.c b/tests/unit/test_field.c index 3c45d9f7..e9688d2a 100644 --- a/tests/unit/test_field.c +++ b/tests/unit/test_field.c @@ -719,10 +719,13 @@ int test_field_io_write(pe_t * pe, cs_t * cs, const field_options_t * opts) { assert(opts); /* Establish data and test values. */ + /* Because field_io_write() has a memcpyDeviceToHost, we need to make + * sure the test data is on the device before the write */ field_create(pe, cs, NULL, "test-field-io", opts, &field); util_field_data_check_set(field); + field_memcpy(field, tdpMemcpyHostToDevice); /* Write */ From 331d61ee6d748d413754100002ab0a8b036c5a96 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 8 May 2023 19:07:55 +0100 Subject: [PATCH 45/97] Fix bad LE initialisation possibility --- src/ludwig.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ludwig.c b/src/ludwig.c index e156ee64..a1e0fa22 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -321,8 +321,15 @@ static int ludwig_rt(ludwig_t * ludwig) { physics_rho0(ludwig->phys, &rho0); if (ludwig->hydro) hydro_rho0(ludwig->hydro, rho0); + /* This should be relocated with LE plane input */ rt_int_parameter(rt, "LE_init_profile", &n); - if (n != 0) lb_le_init_shear_profile(ludwig->lb, ludwig->le); + if (n != 0) { + if (lees_edw_nplane_total(ludwig->le) == 0) { + pe_info(ludwig->pe, "Cannot use LE_init_profile with no planes\n"); + pe_fatal(ludwig->pe, "Please check the input and try again\n"); + } + lb_le_init_shear_profile(ludwig->lb, ludwig->le); + } } else { /* Distributions */ From 6b6d531c1751e1c5dc6a066cc4bc805d79c78809 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 8 May 2023 19:30:08 +0100 Subject: [PATCH 46/97] Simply code --- src/ludwig.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ludwig.c b/src/ludwig.c index a1e0fa22..67829687 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -316,14 +316,12 @@ static int ludwig_rt(ludwig_t * ludwig) { if (ntstep == 0) { double rho0 = 1.0; - n = 0; lb_rt_initial_conditions(pe, rt, ludwig->lb, ludwig->phys); physics_rho0(ludwig->phys, &rho0); if (ludwig->hydro) hydro_rho0(ludwig->hydro, rho0); /* This should be relocated with LE plane input */ - rt_int_parameter(rt, "LE_init_profile", &n); - if (n != 0) { + if (rt_switch(ludwig->rt, "LE_init_profile")) { if (lees_edw_nplane_total(ludwig->le) == 0) { pe_info(ludwig->pe, "Cannot use LE_init_profile with no planes\n"); pe_fatal(ludwig->pe, "Please check the input and try again\n"); From 0ca9956c969669fcc9ee3eeb579d9d2a980742bd Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Mon, 8 May 2023 19:54:13 +0100 Subject: [PATCH 47/97] Remove unused code --- src/ludwig.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/ludwig.c b/src/ludwig.c index 67829687..c4640075 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -206,7 +206,6 @@ static int ludwig_rt(ludwig_t * ludwig) { int ntstep; int n, nstat; char filename[FILENAME_MAX]; - char subdirectory[FILENAME_MAX/2]; pe_t * pe = NULL; cs_t * cs = NULL; @@ -311,7 +310,6 @@ static int ludwig_rt(ludwig_t * ludwig) { /* NOW INITIAL CONDITIONS */ - pe_subdirectory(pe, subdirectory); ntstep = physics_control_timestep(ludwig->phys); if (ntstep == 0) { @@ -368,7 +366,7 @@ static int ludwig_rt(ludwig_t * ludwig) { if (ludwig->psi) { psi_io_info(ludwig->psi, &iohandler); - sprintf(filename,"%spsi-%8.8d", subdirectory, ntstep); + sprintf(filename,"psi-%8.8d", ntstep); pe_info(pe, "electrokinetics files(s) %s\n", filename); io_read_data(iohandler, filename, ludwig->psi); } @@ -449,7 +447,6 @@ static int ludwig_rt(ludwig_t * ludwig) { void ludwig_run(const char * inputfile) { char filename[FILENAME_MAX]; - char subdirectory[FILENAME_MAX/2]; int is_porous_media = 0; int step = 0; int is_pm = 0; @@ -509,8 +506,6 @@ void ludwig_run(const char * inputfile) { /* Report initial statistics */ - pe_subdirectory(ludwig->pe, subdirectory); - /* Move initilaised data to target for initial conditions/time stepping */ map_memcpy(ludwig->map, tdpMemcpyHostToDevice); @@ -896,7 +891,7 @@ void ludwig_run(const char * inputfile) { if (is_config_step() || is_measurement_step() || is_colloid_io_step()) { if (ncolloid > 0) { pe_info(ludwig->pe, "Writing colloid output at step %d!\n", step); - sprintf(filename, "%s%s%8.8d", subdirectory, "config.cds", step); + sprintf(filename, "%s%8.8d", "config.cds", step); colloid_io_write(ludwig->cio, filename); } } @@ -930,7 +925,7 @@ void ludwig_run(const char * inputfile) { 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); + sprintf(filename,"psi-%8.8d", step); io_write_data(iohandler, filename, ludwig->psi); } } @@ -949,7 +944,7 @@ void ludwig_run(const char * inputfile) { } if (is_shear_output_step()) { - sprintf(filename, "%sstr-%8.8d.dat", subdirectory, step); + sprintf(filename, "str-%8.8d.dat", step); stats_rheology_stress_section(ludwig->stat_rheo, filename); stats_rheology_stress_profile_zero(ludwig->stat_rheo); } From 5748ffb3baebc1d242d970b1ba93aaa199f03a74 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 20 Jun 2023 15:09:27 +0100 Subject: [PATCH 48/97] Add options in json --- src/psi_options.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++ src/psi_options.h | 4 ++- 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/src/psi_options.c b/src/psi_options.c index 225ecc40..71f86f7a 100644 --- a/src/psi_options.c +++ b/src/psi_options.c @@ -57,6 +57,89 @@ psi_options_t psi_options_default(int nhalo) { return opts; } +/***************************************************************************** + * + * psi_options_to_json + * + * Return a new cJSON object for the options provided. + * + *****************************************************************************/ + +int psi_options_to_json(const psi_options_t * opts, cJSON ** json) { + + int ifail = 0; + + assert(opts); + + if (json == NULL || *json != NULL) { + ifail = -1; + } + else { + int nk = opts->nk; + cJSON * myjson = cJSON_CreateObject(); + cJSON * valencies = cJSON_CreateIntArray(opts->valency, nk); + cJSON * diffusivities = cJSON_CreateDoubleArray(opts->diffusivity, nk); + cJSON * electric_field = cJSON_CreateDoubleArray(opts->e0, 3); + + cJSON * solver_options = NULL; + ifail = psi_solver_options_to_json(&opts->solver, &solver_options); + + cJSON_AddNumberToObject(myjson, "Number of species", nk); + cJSON_AddNumberToObject(myjson, "Unit charge", opts->e); + cJSON_AddNumberToObject(myjson, "Boltzmann factor", opts->beta); + cJSON_AddNumberToObject(myjson, "First permittivity", opts->epsilon1); + cJSON_AddNumberToObject(myjson, "Second permittivity", opts->epsilon2); + + cJSON_AddItemToObject(myjson, "Valencies", valencies); + cJSON_AddItemToObject(myjson, "Diffusivities", diffusivities); + cJSON_AddItemToObject(myjson, "External field", electric_field); + cJSON_AddItemToObject(myjson, "Solver options", solver_options); + + *json = myjson; + } + + return ifail; +} + +/***************************************************************************** + * + * psi_options_from_json + * + *****************************************************************************/ + +int psi_options_from_json(const cJSON * json, psi_options_t * opts) { + + int ifail = 0; + + if (json == NULL || opts == NULL) { + ifail = -1; + } + else { + cJSON * nk = cJSON_GetObjectItem(json, "Number of species"); + cJSON * e = cJSON_GetObjectItem(json, "Unit charge"); + cJSON * beta = cJSON_GetObjectItem(json, "Boltzmann factor"); + cJSON * epsilon1 = cJSON_GetObjectItem(json, "First permittivity"); + cJSON * epsilon2 = cJSON_GetObjectItem(json, "Second permittivity"); + cJSON * valencies = cJSON_GetObjectItem(json, "Valencies"); + cJSON * diffs = cJSON_GetObjectItem(json, "Diffusivities"); + cJSON * electric = cJSON_GetObjectItem(json, "External field"); + cJSON * solver = cJSON_GetObjectItem(json, "Solver options"); + + if (nk) opts->nk = cJSON_GetNumberValue(nk); + if (e) opts->e = cJSON_GetNumberValue(e); + if (beta) opts->beta = cJSON_GetNumberValue(beta); + if (epsilon1) opts->epsilon1 = cJSON_GetNumberValue(epsilon1); + if (epsilon2) opts->epsilon2 = cJSON_GetNumberValue(epsilon2); + + if (valencies) util_json_to_int_array(valencies, opts->valency, opts->nk); + if (diffs) util_json_to_double_array(diffs, opts->diffusivity, opts->nk); + if (electric) util_json_to_double_array(electric, opts->e0, 3); + + if (solver) ifail = psi_solver_options_from_json(solver, &opts->solver); + } + + return ifail; +} /***************************************************************************** * diff --git a/src/psi_options.h b/src/psi_options.h index 5f5aa563..4ad09776 100644 --- a/src/psi_options.h +++ b/src/psi_options.h @@ -54,7 +54,7 @@ struct psi_options_s { /* Time stepping for Nernst Planck */ int nsolver; /* Nernst Planck method */ int nsmallstep; /* No. small timesteps in time splitting */ - double diffacc; /* FIXME: what exactly is this? */ + double diffacc; /* Criterion for time splitting adjustment */ /* Other */ int method; /* Force computation method */ @@ -64,6 +64,8 @@ struct psi_options_s { psi_options_t psi_options_default(int nhalo); +int psi_options_to_json(const psi_options_t * opts, cJSON ** json); +int psi_options_from_json(const cJSON * json, psi_options_t * opts); int psi_bjerrum_length1(const psi_options_t * opts, double * lb); int psi_bjerrum_length2(const psi_options_t * opts, double * lb); From 29fc5e9b478bed55a31e239aedace4f2c992c3a1 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 20 Jun 2023 15:12:02 +0100 Subject: [PATCH 49/97] Add json --- tests/unit/test_psi_options.c | 85 +++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/tests/unit/test_psi_options.c b/tests/unit/test_psi_options.c index 0185dfaa..7ffd18aa 100644 --- a/tests/unit/test_psi_options.c +++ b/tests/unit/test_psi_options.c @@ -20,6 +20,8 @@ #include "psi_options.h" int test_psi_options_default(void); +int test_psi_options_to_json(void); +int test_psi_options_from_json(void); int test_psi_bjerrum_length(void); int test_psi_debye_length(void); @@ -40,6 +42,8 @@ int test_psi_options_suite(void) { assert(PSI_NKMAX >= 2); test_psi_options_default(); + test_psi_options_to_json(); + test_psi_options_from_json(); test_psi_bjerrum_length(); test_psi_debye_length(); @@ -91,6 +95,87 @@ int test_psi_options_default(void) { return ifail; } +/***************************************************************************** + * + * test_psi_options_to_json + * + *****************************************************************************/ + +int test_psi_options_to_json(void) { + + int ifail = 0; + psi_options_t opts = psi_options_default(0); + cJSON * json = NULL; + + ifail = psi_options_to_json(&opts, &json); + assert(ifail == 0); + + { + psi_options_t check = {0}; + ifail = psi_options_from_json(json, &check); + assert(ifail == 0); + if (check.nk != 2) ifail = -1; + assert(check.nk == 2); + } + + cJSON_Delete(json); + + return ifail; +} + +/***************************************************************************** + * + * test_psi_options_from_json + * + * Using the minimum requirement for solver options. + * + *****************************************************************************/ + +int test_psi_options_from_json(void) { + + int ifail = 0; + const char * jstr = "{ \"Number of species\": 2," + " \"Unit charge\": 2.5," + " \"Boltzmann factor\": 3.0," + " \"First permittivity\": 2000.0," + " \"Second permittivity\": 3000.0," + " \"Valencies\": [1, -1]," + " \"Diffusivities\": [0.01, 0.02]," + " \"External field\": [1.0, 2.0, 3.0]," + " \"Solver options\": {" + " \"Solver type\": \"sor\" " + "}}"; + + cJSON * json = cJSON_Parse(jstr); + assert(json); + + { + /* Check result */ + psi_options_t opts = {0}; + ifail = psi_options_from_json(json, &opts); + assert(ifail == 0); + + assert(opts.nk == 2); + assert(fabs(opts.e - 2.5) < DBL_EPSILON); + assert(fabs(opts.beta - 3.0) < DBL_EPSILON); + assert(fabs(opts.epsilon1 - 2000.0) < DBL_EPSILON); + assert(fabs(opts.epsilon2 - 3000.0) < DBL_EPSILON); + assert(opts.valency[0] == +1); + assert(opts.valency[1] == -1); + assert(fabs(opts.diffusivity[0] - 0.01) < DBL_EPSILON); + assert(fabs(opts.diffusivity[1] - 0.02) < DBL_EPSILON); + assert(fabs(opts.e0[0] - 1.0) < DBL_EPSILON); + assert(fabs(opts.e0[1] - 2.0) < DBL_EPSILON); + assert(fabs(opts.e0[2] - 3.0) < DBL_EPSILON); + + assert(opts.solver.psolver == PSI_POISSON_SOLVER_SOR); + } + + cJSON_Delete(json); + + return ifail; +} + /***************************************************************************** * * test_psi_bjerrum_length From ef30aa256a6d2f3664ebde0fca72a23c9a89ffb2 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 20 Jun 2023 15:13:05 +0100 Subject: [PATCH 50/97] Missing return value added --- tests/unit/test_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_util.c b/tests/unit/test_util.c index 18699da4..8b7ced2e 100644 --- a/tests/unit/test_util.c +++ b/tests/unit/test_util.c @@ -137,7 +137,7 @@ int util_jacobi_check(void) { assert(ifail == 0); } - return 0; + return ifail; } /***************************************************************************** From 95cd5ec68f4fbe7fbd6e81c2637a9d4352150af0 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 20 Jun 2023 15:22:10 +0100 Subject: [PATCH 51/97] Adjust/correct liquid junction initialisation --- src/psi_init.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/psi_init.c b/src/psi_init.c index 6c4ccf80..e6f2f2bc 100644 --- a/src/psi_init.c +++ b/src/psi_init.c @@ -157,13 +157,15 @@ int psi_init_gouy_chapman(psi_t * obj, map_t * map, double rho_el, * * psi_init_liquid_junction * - * Set rho(1 <= x <= Lx/2) = 1.01 * electrolyte - * rho(Lx/2+1 <= x <= Lx) = 0.99 * electrolyte + * This follows Mafe et al., so we set for two species: * - * This sets up the system for liquid junction potential. + * rho_left = rho_el + delta_el / 2 + * rho_right = rho_el - delta_el / 2 * - * rho_el is the average electrolyte concentration. - * delta_el is the relative difference of the concentrations. + * where left and right are separated by the half way point in the + * x-direction. + * + * We should have delta_el << rho_el. * *****************************************************************************/ @@ -189,13 +191,13 @@ int psi_init_liquid_junction(psi_t * obj, double rho_el, double delta_el) { psi_psi_set(obj, index, 0.0); - if ((1 <= noff[0] + ic) && (noff[0] + ic < ntotal[X]/2)) { - psi_rho_set(obj, index, 0, rho_el * (1.0 + delta_el)); - psi_rho_set(obj, index, 1, rho_el * (1.0 + delta_el)); + if ((1 <= noff[X] + ic) && (noff[X] + ic <= ntotal[X]/2)) { + psi_rho_set(obj, index, 0, rho_el + 0.5*delta_el); + psi_rho_set(obj, index, 1, rho_el + 0.5*delta_el); } - else{ - psi_rho_set(obj, index, 0, rho_el * (1.0 - delta_el)); - psi_rho_set(obj, index, 1, rho_el * (1.0 - delta_el)); + else { + psi_rho_set(obj, index, 0, rho_el - 0.5*delta_el); + psi_rho_set(obj, index, 1, rho_el - 0.5*delta_el); } } } From a9374879766a2fdde58817ee25830c97e9d16d8d Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 20 Jun 2023 15:24:34 +0100 Subject: [PATCH 52/97] Add input option for solver tolerances --- src/psi_rt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/psi_rt.c b/src/psi_rt.c index 418e8f7c..99638cb5 100644 --- a/src/psi_rt.c +++ b/src/psi_rt.c @@ -276,8 +276,11 @@ int psi_options_rt(pe_t * pe, cs_t * cs, rt_t * rt, psi_options_t * popts) { rt_int_parameter(rt, "freq_statistics", &opts.solver.nfreq); rt_int_parameter(rt, "freq_psi_resid", &opts.solver.nfreq); + /* Accept either form (older "rel_tol" "abs_tol" to be removed) */ rt_double_parameter(rt, "electrokinetics_rel_tol", &opts.solver.reltol); rt_double_parameter(rt, "electrokinetics_abs_tol", &opts.solver.abstol); + rt_double_parameter(rt, "electrokinetics_solver_reltol", &opts.solver.reltol); + rt_double_parameter(rt, "electrokinetics_solver_abstol", &opts.solver.abstol); /* NPE time splitting and criteria */ From 8a400a208fb7bd0afbaf7c57026d40b5a6cd4b34 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 20 Jun 2023 15:26:40 +0100 Subject: [PATCH 53/97] Adjust extra metadata --- src/field.c | 5 ++--- src/io_event.h | 2 ++ src/io_metadata.c | 11 ++++++----- src/io_metadata.h | 3 ++- src/ludwig.c | 5 +---- src/model.c | 2 +- src/psi.c | 34 ++++++++++++++++++++++++++++++++++ src/psi.h | 5 +++++ 8 files changed, 53 insertions(+), 14 deletions(-) diff --git a/src/field.c b/src/field.c index 0fbfdc25..c17aab2d 100644 --- a/src/field.c +++ b/src/field.c @@ -1666,9 +1666,8 @@ int field_io_write(field_t * field, int timestep, io_event_t * event) { /* Metadata */ if (meta->iswriten == 0) { - /* No extra comments at the moment */ - cJSON * comments = NULL; - int ifail = io_metadata_write(meta, field->name, comments); + int ifail = io_metadata_write(meta, field->name, event->extra_name, + event->extra_json); if (ifail == 0) field->iometadata_out.iswriten = 1; } diff --git a/src/io_event.h b/src/io_event.h index afe05988..b4abe949 100644 --- a/src/io_event.h +++ b/src/io_event.h @@ -31,6 +31,8 @@ typedef enum io_event_record_enum io_event_record_t; typedef struct io_event_s io_event_t; struct io_event_s { + const char * extra_name; /* Extra metadata name */ + cJSON * extra_json; /* Extra JSON section */ double time[IO_EVENT_MAX]; /* MPI_Wtime()s */ }; diff --git a/src/io_metadata.c b/src/io_metadata.c index 8c1ee916..d171b864 100644 --- a/src/io_metadata.c +++ b/src/io_metadata.c @@ -237,13 +237,14 @@ int io_metadata_from_json(cs_t * cs, const cJSON * json, io_metadata_t * m) { * * io_metadata_write * - * Driver to write to file with an optional extra comment block. + * Driver to write to file with an optional extra json block. * *****************************************************************************/ int io_metadata_write(const io_metadata_t * metadata, const char * stub, - const cJSON * comments) { + const char * extra_name, + const cJSON * extra_json) { int ifail = 0; cJSON * json = NULL; @@ -257,9 +258,9 @@ int io_metadata_write(const io_metadata_t * metadata, /* 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); + if (extra_name && extra_json) { + cJSON * jtmp = cJSON_Duplicate(extra_json, 1); + cJSON_AddItemToObject(json, extra_name, jtmp); } /* The extension uses indices in natural numbers 001-002 etc. */ diff --git a/src/io_metadata.h b/src/io_metadata.h index 1023a776..06709bef 100644 --- a/src/io_metadata.h +++ b/src/io_metadata.h @@ -55,7 +55,8 @@ 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); + const char * extra, + const cJSON * json); int io_metadata_from_file(pe_t * pe, const char * filename, io_metadata_t ** metadata); diff --git a/src/ludwig.c b/src/ludwig.c index d11fa876..eb916a21 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -914,11 +914,8 @@ void ludwig_run(const char * inputfile) { if (ludwig->psi) { if (is_psi_output_step() || is_config_step()) { - io_event_t event1 = {0}; - io_event_t event2 = {0}; pe_info(ludwig->pe, "Writing psi file at step %d!\n", step); - field_io_write(ludwig->psi->psi, step, &event1); - field_io_write(ludwig->psi->rho, step, &event2); + psi_io_write(ludwig->psi, step); } } diff --git a/src/model.c b/src/model.c index f75e1ba4..475b2132 100644 --- a/src/model.c +++ b/src/model.c @@ -1575,7 +1575,7 @@ int lb_io_write(lb_t * lb, int timestep, io_event_t * event) { if (meta->iswriten == 0) { /* No comments at the moment */ cJSON * comments = NULL; - int ifail = io_metadata_write(meta, "dist", comments); + int ifail = io_metadata_write(meta, "dist", NULL, comments); if (ifail == 0) lb->output.iswriten = 1; } diff --git a/src/psi.c b/src/psi.c index f831b00d..6fcff1fd 100644 --- a/src/psi.c +++ b/src/psi.c @@ -131,6 +131,9 @@ int psi_initialise(pe_t * pe, cs_t * cs, const psi_options_t * opts, psi->nfreq_io = INT_MAX; + /* Copy of the options structure */ + psi->options = *opts; + return ifail; } @@ -957,3 +960,34 @@ int psi_electroneutral(psi_t * psi, map_t * map) { return 0; } +/***************************************************************************** + * + * psi_io_write + * + * Convenience to write both psi, rho with extra information. + * + *****************************************************************************/ + +int psi_io_write(psi_t * psi, int nstep) { + + int ifail = 0; + io_event_t io1 = {0}; + io_event_t io2 = {0}; + const char * extra = "electrokinetics"; + cJSON * json = NULL; + + ifail = psi_options_to_json(&psi->options, &json); + if (ifail == 0) { + io1.extra_name = extra; + io2.extra_name = extra; + io1.extra_json = json; + io2.extra_json = json; + } + + ifail += field_io_write(psi->psi, nstep, &io1); + ifail += field_io_write(psi->rho, nstep, &io2); + + cJSON_Delete(json); + + return ifail; +} diff --git a/src/psi.h b/src/psi.h index aa740994..85f50228 100644 --- a/src/psi.h +++ b/src/psi.h @@ -64,6 +64,9 @@ struct psi_s { psi_solver_options_t solver; /* User options */ stencil_t * stencil; /* Finite difference stencil info */ + /* Options */ + psi_options_t options; /* Overall options (currently a copy) */ + }; @@ -81,6 +84,8 @@ int psi_halo_psi(psi_t * obj); int psi_halo_psijump(psi_t * obj); int psi_halo_rho(psi_t * obj); +int psi_io_write(psi_t * psi, int nstep); + int psi_rho(psi_t * obj, int index, int n, double * rho); int psi_rho_set(psi_t * obj, int index, int n, double rho); int psi_psi(psi_t * obj, int index, double * psi); From df1aad6d9e782240b243f9e25b1e7b6da25911c1 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 20 Jun 2023 15:28:01 +0100 Subject: [PATCH 54/97] Structure update --- tests/unit/test_psi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit/test_psi.c b/tests/unit/test_psi.c index ec0d2781..ed7e6697 100644 --- a/tests/unit/test_psi.c +++ b/tests/unit/test_psi.c @@ -41,7 +41,7 @@ int test_psi_suite(void) { pe_create(MPI_COMM_WORLD, PE_QUIET, &pe); /* Changes in psi_t should be accompanied by changes in tests... */ - assert(sizeof(psi_t) == 184); + assert(sizeof(psi_t) == 576); test_psi_initialise(pe); test_psi_create(pe); @@ -124,6 +124,7 @@ int test_psi_initialise(pe_t * pe) { /* Other */ assert(psi.method == opts.method); + assert(psi.options.nk == opts.nk); psi_finalise(&psi); } From 076aff16b3867f83f34830300b465f47354ab3c7 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 20 Jun 2023 15:28:33 +0100 Subject: [PATCH 55/97] Extra metadata write --- tests/unit/test_io_metadata.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_io_metadata.c b/tests/unit/test_io_metadata.c index 4d189b06..7e6208c4 100644 --- a/tests/unit/test_io_metadata.c +++ b/tests/unit/test_io_metadata.c @@ -214,7 +214,7 @@ int test_io_metadata_write(cs_t * cs, int keep) { assert(cs); io_metadata_create(cs, &options, &element, &meta); - ifail = io_metadata_write(meta, "test-io", header); + ifail = io_metadata_write(meta, "test-io", "Extra", header); assert(ifail == 0); /* Remove at rank 0 (a test that the file exists with the correct name) */ From 0552745563099758c116ac6742dd788ed39f13e3 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Tue, 20 Jun 2023 15:38:18 +0100 Subject: [PATCH 56/97] Resolve residual conflict --- src/ludwig.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/ludwig.c b/src/ludwig.c index 86670ab2..184dbe9b 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -915,12 +915,7 @@ void ludwig_run(const char * inputfile) { if (ludwig->psi) { if (is_psi_output_step() || is_config_step()) { pe_info(ludwig->pe, "Writing psi file at step %d!\n", step); -<<<<<<< HEAD psi_io_write(ludwig->psi, step); -======= - sprintf(filename,"psi-%8.8d", step); - io_write_data(iohandler, filename, ludwig->psi); ->>>>>>> develop } } From 57bcd5c2e656f215a2665442b1b76df49d66b993 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 21 Jun 2023 11:46:05 +0100 Subject: [PATCH 57/97] Add explicit tolerances --- tests/regression/d3q19-elec/serial-elec-gc1.inp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/regression/d3q19-elec/serial-elec-gc1.inp b/tests/regression/d3q19-elec/serial-elec-gc1.inp index e51fa836..23b5fb62 100644 --- a/tests/regression/d3q19-elec/serial-elec-gc1.inp +++ b/tests/regression/d3q19-elec/serial-elec-gc1.inp @@ -95,6 +95,11 @@ electrokinetics_d0 0.01 electrokinetics_d1 0.01 electrokinetics_eunit 1.0 electrokinetics_epsilon 3.3e3 + +electrokinetics_rel_tol 1e-08 +electrokinetics_abs_tol 1e-15 +electrokinetics_maxits 2000 + electrokinetics_init gouy_chapman electrokinetics_init_rho_el 0.001 electrokinetics_init_sigma 0.03125 From 0d3b4a7798cde021e6f8c4b1a3e0715996f80047 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 21 Jun 2023 12:31:12 +0100 Subject: [PATCH 58/97] Remove reporting of time spitting --- src/ludwig.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/ludwig.c b/src/ludwig.c index 184dbe9b..0387282c 100644 --- a/src/ludwig.c +++ b/src/ludwig.c @@ -673,11 +673,7 @@ void ludwig_run(const char * inputfile) { TIMER_stop(TIMER_HALO_LATTICE); nernst_planck_adjust_multistep(ludwig->psi); - - if (is_statistics_step()) pe_info(ludwig->pe, "%d multisteps\n",im); - psi_zero_mean(ludwig->psi); - } /* order parameter dynamics (not if symmetric_lb) */ From 614ef9db78a57a4f1b99ece64cdd33770f868472 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 21 Jun 2023 12:32:14 +0100 Subject: [PATCH 59/97] New counter charge only GC test replaces original --- .../regression/d3q19-elec/serial-elec-gc1.log | 151 ---------------- .../serial-elec-gc1.inp | 80 +++++---- .../d3q19-short/serial-elec-gc1.log | 169 ++++++++++++++++++ 3 files changed, 217 insertions(+), 183 deletions(-) delete mode 100644 tests/regression/d3q19-elec/serial-elec-gc1.log rename tests/regression/{d3q19-elec => d3q19-short}/serial-elec-gc1.inp (53%) create mode 100644 tests/regression/d3q19-short/serial-elec-gc1.log diff --git a/tests/regression/d3q19-elec/serial-elec-gc1.log b/tests/regression/d3q19-elec/serial-elec-gc1.log deleted file mode 100644 index 998d49cb..00000000 --- a/tests/regression/d3q19-elec/serial-elec-gc1.log +++ /dev/null @@ -1,151 +0,0 @@ -Welcome to Ludwig v0.5.48 (Serial version running on 1 process) - -The SVN revision details are: 2982 -Note assertions via standard C assert() are on. - -Read 38 user parameters from serial-elec-gc1.inp - -System details --------------- -System size: 64 4 4 -Decomposition: 1 1 1 -Local domain: 64 4 4 -Periodic: 1 1 1 -Halo nhalo: 1 -Reorder: true -Initialised: 1 - -Free energy details -------------------- - -Electrokinetics (single fluid) selected - -Parameters: -Electrokinetic species: 2 -Boltzmann factor: 3.0000000e+04 (T = 3.3333333e-05) -Unit charge: 1.0000000e+00 -Permittivity: 3.3000000e+03 -Bjerrum length: 7.2343156e-01 -Valency species 0: 1 -Diffusivity species 0: 1.0000000e-02 -Valency species 1: -1 -Diffusivity species 1: 1.0000000e-02 -Relative tolerance: 1.1920929e-07 -Absolute tolerance: 1.1920929e-09 -Max. no. of iterations: 10000 -Number of multisteps: 1 -Diffusive accuracy in NPE: 0.0000000e+00 -Force calculation: phi_gradmu_correction - -System properties ----------------- -Mean fluid density: 1.00000e+00 -Shear viscosity 1.00000e-01 -Bulk viscosity 1.00000e-01 -Temperature 3.33333e-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: lb_halo_openmp_reduced (host) -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 - -Advection scheme order: 3 - -Initial charge densities ------------------------- -Initial conditions: Gouy Chapman -Initial condition rho_el: 1.0000000e-03 -Debye length: 7.4161985e+00 -Initial condition sigma: 3.1250000e-02 - -Porous Media ------------- -Wall boundary links allocated: 160 -Memory (total, bytes): 2560 -Arranging initial charge neutrality. - -Initial conditions. - -Scalars - total mean variance min max -[rho] 992.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[psi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[rho] 1.9920000e+00 1.0000000e-03 3.1250000e-02 -[rho] 1.9920000e+00 0.0000000e+00 2.0080645e-03 -[elc] -1.2878587e-14 -1.0080645e-03 3.1250000e-02 - -Free energy density - timestep total fluid -[fed] 0 -2.6048547231e-02 -2.2387073046e-02 - -Momentum - x y z -[total ] 1.3766766e-14 0.0000000e+00 0.0000000e+00 -[fluid ] 1.3766766e-14 0.0000000e+00 0.0000000e+00 -[walls ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 - -Starting time step loop. -1 multisteps - -Scalars - total mean variance min max -[rho] 992.00 1.00000000000 1.5210055e-14 0.99999998294 1.00000011145 -[psi] 1.1990409e-14 -1.5210888e+00 2.9128003e+00 -[rho] 1.9920000e+00 8.2093661e-04 3.1250000e-02 -[rho] 1.9920000e+00 0.0000000e+00 2.4098347e-03 -[elc] -1.4988011e-14 -1.5888981e-03 3.1250000e-02 - -Free energy density - timestep total fluid -[fed] 100 -2.4613216884e-02 -2.2373587017e-02 - -Momentum - x y z -[total ] 5.9174887e-14 -6.6613381e-16 0.0000000e+00 -[fluid ] -3.5416114e-13 -6.6613381e-16 0.0000000e+00 -[walls ] 4.1333603e-13 0.0000000e+00 0.0000000e+00 - -Velocity - x y z -[minimum ] -4.7355583e-08 -6.9388940e-18 0.0000000e+00 -[maximum ] 4.7355582e-08 6.9388940e-18 1.1754944e-38 -[vol flux] -1.4543921e-13 -2.2204458e-16 0.0000000e+00 - -Completed cycle 100 - -Timer resolution: 1e-06 second - -Timer statistics - Section: tmin tmax total - Total: 34.975 34.975 34.975 34.974571 (1 call) - Time step loop: 0.032 0.047 34.970 0.034970 (1000 calls) - Propagation: 0.001 0.002 0.702 0.000702 (1000 calls) - Propagtn (krnl) : 0.001 0.002 0.700 0.000700 (1000 calls) - Collision: 0.001 0.002 1.261 0.001261 (1000 calls) - Collision (krnl) : 0.001 0.002 1.258 0.001258 (1000 calls) - Lattice halos: 0.000 0.001 0.761 0.000190 (4000 calls) - phi gradients: 0.000 0.000 0.001 0.000001 (1000 calls) - BBL: 0.000 0.000 0.023 0.000023 (1000 calls) - Force calculation: 0.000 0.000 0.151 0.000075 (2000 calls) - phi update: 0.000 0.000 0.000 0.000000 (1000 calls) - Poisson equation: 0.028 0.041 30.327 0.030327 (1000 calls) - Nernst Planck: 0.001 0.003 1.647 0.001647 (1000 calls) - Free1: 0.000 0.001 0.002 0.000002 (1000 calls) -Ludwig finished normally. diff --git a/tests/regression/d3q19-elec/serial-elec-gc1.inp b/tests/regression/d3q19-short/serial-elec-gc1.inp similarity index 53% rename from tests/regression/d3q19-elec/serial-elec-gc1.inp rename to tests/regression/d3q19-short/serial-elec-gc1.inp index 23b5fb62..9fc01d95 100644 --- a/tests/regression/d3q19-elec/serial-elec-gc1.inp +++ b/tests/regression/d3q19-short/serial-elec-gc1.inp @@ -2,9 +2,31 @@ # # Guoy-Chapman electrokinetics # -# Note residual reporting is switched off to allow this test to -# pass at 1000 steps. (Exact residual varies above tolerance on -# different platforms.) +# This is a sample input file for the problem described at +# https://ludwig.epcc.ed.ac.uk/tutorials/electrokinetics/electrokinetics.html +# +# We have a quasi-one dimensional system in the x-direction and we +# use the special initialisation +# +# electrokinetics_init gouy_chapman +# +# to provide walls at x = 1 and x = Lx. We arrange for the system to +# have a surface charge (0.3125, 0.03125, or 0.003125) and zero +# co-ion concentration (counter-charge only). The whole system is +# electroneutral. E.g., +# +# electrokinetics_init_rho_el 0.0 +# electrokinetics_init_sigma 0.003125 +# +# For the purposes of this test, only 100 time steps are used, +# and the SOR residual reporting is switch off to allow the test +# to pass (the residual can be sensitive to the platform/compiler +# at the level of the test tolerance). +# +# For the results presented, 100,000 steps were used, and the +# final configuration file is used to provide the charge density. +# Statistics and residual reporting can be used at 1000 steps +# interval. # ############################################################################## @@ -14,7 +36,6 @@ # ############################################################################### -N_start 0 N_cycles 100 ############################################################################## @@ -23,19 +44,19 @@ N_cycles 100 # ############################################################################## -size 64_4_4 -grid 1_1_2 -periodicity 1_1_1 +size 64_4_4 +periodicity 1_1_1 lb_halo_scheme lb_halo_openmp_reduced ############################################################################## # # Fluid parameters # +# The temperature is relevant for k_B T in the electrokinetics context. +# ############################################################################## -viscosity 0.1 -viscosity_bulk 0.1 +viscosity 0.1 isothermal_fluctuations off temperature 3.33333333333333333e-5 @@ -47,7 +68,7 @@ temperature 3.33333333333333333e-5 # ############################################################################### -free_energy fe_electro +free_energy fe_electro fe_force_method phi_gradmu_correction fd_advection_scheme_order 3 @@ -59,7 +80,6 @@ fd_advection_scheme_order 3 ############################################################################### colloid_init none -magnetic_b0 0.0_0.0_0.0 ############################################################################### # @@ -75,13 +95,13 @@ boundary_walls 0_0_0 # ############################################################################### -freq_statistics 100 -freq_psi_resid 10000 -config_at_end no +freq_statistics 100 +freq_psi_resid 1000 +config_at_end no -colloid_io_freq 1000 - -stats_vel_print_vol_flux yes +psi_io_mode mpiio +psi_io_format ascii +psi_io_report no ############################################################################### # @@ -89,23 +109,19 @@ stats_vel_print_vol_flux yes # ############################################################################### -electrokinetics_z0 +1 -electrokinetics_z1 -1 -electrokinetics_d0 0.01 -electrokinetics_d1 0.01 -electrokinetics_eunit 1.0 -electrokinetics_epsilon 3.3e3 - -electrokinetics_rel_tol 1e-08 -electrokinetics_abs_tol 1e-15 -electrokinetics_maxits 2000 +electrokinetics_z0 +1 +electrokinetics_z1 -1 +electrokinetics_d0 0.01 +electrokinetics_d1 0.01 +electrokinetics_eunit 1.0 +electrokinetics_epsilon 3.3e3 -electrokinetics_init gouy_chapman -electrokinetics_init_rho_el 0.001 -electrokinetics_init_sigma 0.03125 +electrokinetics_init gouy_chapman +electrokinetics_init_rho_el 0.0 +electrokinetics_init_sigma 0.003125 -psi_io_mode mpiio -psi_io_report no +electrokinetics_solver_type sor +electrokinetics_solver_stencil 7 ############################################################################### # diff --git a/tests/regression/d3q19-short/serial-elec-gc1.log b/tests/regression/d3q19-short/serial-elec-gc1.log new file mode 100644 index 00000000..44f61776 --- /dev/null +++ b/tests/regression/d3q19-short/serial-elec-gc1.log @@ -0,0 +1,169 @@ +Welcome to: Ludwig v0.19.1 (Serial version running on 1 process) +Git commit: 0552745563099758c116ac6742dd788ed39f13e3 + +Start time: Wed Jun 21 12:27:56 2023 + +Compiler: + name: Gnu 11.3.0 + version-string: 11.3.0 + options: -O2 -g -fopenmp -Wall -Werror + +Note assertions via standard C assert() are on. + +Target thread model: OpenMP. +OpenMP threads: 1; maximum number of threads: 8. + +Read 30 user parameters from input + +System details +-------------- +System size: 64 4 4 +Decomposition: 1 1 1 +Local domain: 64 4 4 +Periodic: 1 1 1 +Halo nhalo: 1 +Reorder: true +Initialised: 1 + +Free energy details +------------------- + +Electrokinetics (single fluid) selected + +Parameters: +Electrokinetic species: 2 +Boltzmann factor: 3.0000000e+04 (T = 3.3333333e-05) +Unit charge: 1.0000000e+00 +Permittivity: 3.3000000e+03 +Bjerrum length: 7.2343156e-01 +Valency species 0: 1 +Diffusivity species 0: 1.0000000e-02 +Valency species 1: -1 +Diffusivity species 1: 1.0000000e-02 +Solver type: sor +Solver stencil points: 7 +Relative tolerance: 1.0000000e-08 +Absolute tolerance: 1.0000000e-15 +Max. no. of iterations: 10000 +Number of multisteps: 1 +Diffusive accuracy in NPE: 0.0000000e+00 +Force calculation: phi_gradmu_correction + +System properties +---------------- +Mean fluid density: 1.00000e+00 +Shear viscosity 1.00000e-01 +Bulk viscosity 1.00000e-01 +Temperature 3.33333e-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: lb_halo_openmp_reduced (host) +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 + +Advection scheme order: 3 + +Initial charge densities +------------------------ +Initial conditions: Gouy Chapman +Initial condition rho_el: 0.0000000e+00 +Debye length: inf +Debye length (actual): 3.3033317e+01 +Initial condition sigma: 3.1250000e-03 + +Porous Media +------------ +Wall boundary links allocated: 160 +Memory (total, bytes): 2560 + +Arranging initial charge neutrality. + +Initial conditions. + +Scalars - total mean variance min max +[rho] 992.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 +[psi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[rho] 1.0000000e-01 0.0000000e+00 3.1250000e-03 +[rho] 1.0000000e-01 0.0000000e+00 1.0080645e-04 +[elc] -2.5413699e-16 -1.0080645e-04 3.1250000e-03 + +Free energy density - timestep total fluid +[fed] 0 -1.6572880074e-03 -1.0284584879e-03 + +Momentum - x y z +[total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[walls ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 + +Starting time step loop. + +Scalars - total mean variance min max +[rho] 992.00 1.00000000000 1.0214052e-14 0.99999999983 1.00000000113 +[psi] 1.3017365e-13 -1.5610986e-01 2.9805899e-01 +[rho] 1.0000000e-01 0.0000000e+00 3.1250000e-03 +[rho] 1.0000000e-01 0.0000000e+00 1.0273708e-04 +[elc] -8.3006518e-16 -1.0273708e-04 3.1250000e-03 + +Free energy density - timestep total fluid +[fed] 100 -1.6422762401e-03 -1.0279856047e-03 + +Momentum - x y z +[total ] 3.6637360e-14 1.3877788e-17 0.0000000e+00 +[fluid ] 1.1457502e-13 1.3877788e-17 0.0000000e+00 +[walls ] -7.7937656e-14 0.0000000e+00 0.0000000e+00 + +Velocity - x y z +[minimum ] -4.7309645e-10 -6.9388939e-18 0.0000000e+00 +[maximum ] 4.7309630e-10 6.9388939e-18 1.1754944e-38 + +Completed cycle 100 + +Timer resolution: 1e-06 second + +Timer statistics + Section: tmin tmax total + Total: 1.863 1.863 1.863 1.862596 (1 call) + Time step loop: 0.018 0.037 1.860 0.018596 (100 calls) + Propagation: 0.001 0.001 0.061 0.000608 (100 calls) + Propagtn (krnl) : 0.001 0.001 0.060 0.000603 (100 calls) + Collision: 0.001 0.002 0.130 0.001299 (100 calls) + Collision (krnl) : 0.001 0.002 0.130 0.001295 (100 calls) + Lattice halos: 0.000 0.000 0.056 0.000141 (400 calls) + -> irecv: 0.000 0.000 0.000 0.000000 (100 calls) + -> pack: 0.000 0.000 0.012 0.000124 (100 calls) + -> isend: 0.000 0.000 0.000 0.000000 (100 calls) + -> waitall: 0.000 0.000 0.000 0.000000 (100 calls) + -> unpack: 0.000 0.000 0.012 0.000121 (100 calls) + phi gradients: 0.000 0.000 0.000 0.000000 (100 calls) + BBL: 0.000 0.000 0.003 0.000026 (100 calls) + Force calculation: 0.000 0.000 0.015 0.000073 (200 calls) + phi update: 0.000 0.000 0.000 0.000000 (100 calls) + Poisson equation: 0.014 0.032 1.439 0.014394 (100 calls) + Nernst Planck: 0.001 0.002 0.145 0.001452 (100 calls) +Diagnostics / output: 0.000 0.001 0.001 0.000009 (100 calls) +End time: Wed Jun 21 12:27:58 2023 +Ludwig finished normally. From f86d93fcbf4fb5f769631c4db75db4110b56c050 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 21 Jun 2023 14:12:56 +0100 Subject: [PATCH 60/97] Add liquid junction test --- .../regression/d3q19-elec/serial-elec-lj1.inp | 125 +++++++++++++++ .../regression/d3q19-elec/serial-elec-lj1.log | 149 ++++++++++++++++++ 2 files changed, 274 insertions(+) create mode 100644 tests/regression/d3q19-elec/serial-elec-lj1.inp create mode 100644 tests/regression/d3q19-elec/serial-elec-lj1.log diff --git a/tests/regression/d3q19-elec/serial-elec-lj1.inp b/tests/regression/d3q19-elec/serial-elec-lj1.inp new file mode 100644 index 00000000..fedba0e0 --- /dev/null +++ b/tests/regression/d3q19-elec/serial-elec-lj1.inp @@ -0,0 +1,125 @@ +############################################################################## +# +# Liquid junction problem +# +# This is the liquid junction time dependence as discussed at +# https://ludwig.epcc.ed.ac.uk/tutorials/electrokinectics/electrokinetics.html +# +# For a quasi-one-dimensional system we use the special intialisation +# +# electrokinetics_init liquid_junction +# +# to provide two charged species with an initial step at the centre +# of the system in the long direction (the x-direction). +# +# The hydrodynamics is switched off for this problem. It is purely +# diffusive. +# +# For the short time scale, the system is run for 5000 time steps +# with statistics at 50 time steps, while for the long time regime +# the run is 100,000 time steps with statistics at 1000 steps. +# +# For system size 128_4_4, the parameters are the same; for the +# system size 256_4_4, the short time regime is the same, but +# the long time regime is run for 200,000 steps. +# +# Note that large differences in the diffusivities (e.g., as much +# as 0.0125 and 0.075) will lead to noticable deviations from the +# theory of Mafe et al., which assumes they are "close". +# +# +# For the purposes of this test, the number of steps is reduced to +# 100, with statistics at 100 steps. +# +############################################################################## + +############################################################################## +# +# Run duration +# +############################################################################### + +N_cycles 100 + +############################################################################## +# +# System and MPI +# +############################################################################## + +size 64_4_4 +periodicity 1_1_1 +lb_halo_scheme lb_halo_openmp_reduced + +############################################################################## +# +# Fluid parameters +# +############################################################################## + +hydrodynamics off +temperature 3.33333333333333333e-5 + +############################################################################## +# +# Free energy parameters +# +############################################################################### + +free_energy fe_electro +fe_force_method phi_gradmu_correction + +############################################################################### +# +# Colloid parameters +# +############################################################################### + +colloid_init none + +############################################################################### +# +# Walls / boundaries +# +############################################################################### + +boundary_walls 0_0_0 + +############################################################################### +# +# Output frequency and type +# +############################################################################### + +freq_statistics 100 +freq_psi_resid 1000 +config_at_end no + +############################################################################### +# +# Electrokinetics +# +############################################################################### + +electrokinetics_z0 +1 +electrokinetics_z1 -1 +electrokinetics_d0 0.0105 +electrokinetics_d1 0.0095 +electrokinetics_eunit 1.0 +electrokinetics_epsilon 3.3e3 + +electrokinetics_init liquid_junction +electrokinetics_init_rho_el 0.01 +electrokinetics_init_delta_el 0.0002 + +psi_io_mode mpiio +psi_io_format ascii +psi_io_report no + +############################################################################### +# +# Miscellaneous +# +############################################################################### + +random_seed 8361235 diff --git a/tests/regression/d3q19-elec/serial-elec-lj1.log b/tests/regression/d3q19-elec/serial-elec-lj1.log new file mode 100644 index 00000000..c1b4de07 --- /dev/null +++ b/tests/regression/d3q19-elec/serial-elec-lj1.log @@ -0,0 +1,149 @@ +Welcome to: Ludwig v0.19.1 (Serial version running on 1 process) +Git commit: 0552745563099758c116ac6742dd788ed39f13e3 + +Start time: Wed Jun 21 14:08:46 2023 + +Compiler: + name: Gnu 11.3.0 + version-string: 11.3.0 + options: -O2 -g -fopenmp -Wall -Werror + +Note assertions via standard C assert() are on. + +Target thread model: OpenMP. +OpenMP threads: 1; maximum number of threads: 8. + +Read 26 user parameters from input + +System details +-------------- +System size: 64 4 4 +Decomposition: 1 1 1 +Local domain: 64 4 4 +Periodic: 1 1 1 +Halo nhalo: 1 +Reorder: true +Initialised: 1 + +Free energy details +------------------- + +Electrokinetics (single fluid) selected + +Parameters: +Electrokinetic species: 2 +Boltzmann factor: 3.0000000e+04 (T = 3.3333333e-05) +Unit charge: 1.0000000e+00 +Permittivity: 3.3000000e+03 +Bjerrum length: 7.2343156e-01 +Valency species 0: 1 +Diffusivity species 0: 1.0500000e-02 +Valency species 1: -1 +Diffusivity species 1: 9.5000000e-03 +Solver type: sor +Solver stencil points: 7 +Relative tolerance: 1.0000000e-08 +Absolute tolerance: 1.0000000e-15 +Max. no. of iterations: 10000 +Number of multisteps: 1 +Diffusive accuracy in NPE: 0.0000000e+00 +Force calculation: phi_gradmu_correction + +System properties +---------------- +Mean fluid density: 1.00000e+00 +Shear viscosity 1.66667e-01 +Bulk viscosity 1.66667e-01 +Temperature 3.33333e-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: lb_halo_openmp_reduced (host) +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: 1.00000e+00 +Bulk relaxation time: 1.00000e+00 +Ghost relaxation time: 1.00000e+00 +[User ] Random number seed: 8361235 + +Hydrodynamics +------------- +Hydrodynamics: off + +Advection scheme order: 1 (default) + +Initial charge densities +------------------------ +Initial conditions: Liquid junction +Initial condition rho_el: 1.0000000e-02 +Debye length: 2.3452079e+00 +Initial condition delta_el: 2.0000000e-04 +Saturation potential: 3.3250000e-09 +Saturation timescale: 5.5000000e+02 + +Arranging initial charge neutrality. + +Initial conditions. + +Scalars - total mean variance min max +[rho] 1024.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 +[psi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[rho] 1.0240000e+01 9.9000000e-03 1.0100000e-02 +[rho] 1.0240000e+01 9.9000000e-03 1.0100000e-02 +[elc] 0.0000000e+00 0.0000000e+00 0.0000000e+00 + +Free energy density - timestep total fluid +[fed] 0 -1.1210240370e-01 -1.1210240370e-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] 1024.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 +[psi] -9.5816367e-18 -8.2430681e-05 8.2435962e-05 +[rho] 1.0240000e+01 9.9000000e-03 1.0100000e-02 +[rho] 1.0240000e+01 9.9000000e-03 1.0100000e-02 +[elc] -1.1102230e-16 -2.2451392e-06 2.2458928e-06 + +Free energy density - timestep total fluid +[fed] 100 -1.1210250016e-01 -1.1210250016e-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 + +Completed cycle 100 + +Timer resolution: 1e-06 second + +Timer statistics + Section: tmin tmax total + Total: 2.667 2.667 2.667 2.667317 (1 call) + Time step loop: 0.002 0.034 2.665 0.026649 (100 calls) + Lattice halos: 0.000 0.000 0.023 0.000114 (200 calls) + phi gradients: 0.000 0.000 0.000 0.000000 (100 calls) + Force calculation: 0.000 0.000 0.011 0.000057 (200 calls) + phi update: 0.000 0.000 0.000 0.000000 (100 calls) + Poisson equation: 0.000 0.032 2.516 0.025155 (100 calls) + Nernst Planck: 0.001 0.001 0.110 0.001100 (100 calls) +Diagnostics / output: 0.000 0.001 0.001 0.000008 (100 calls) +End time: Wed Jun 21 14:08:48 2023 +Ludwig finished normally. From 92cdcc6391fbeb4daaf6ae62807ddc3e35e561f2 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 21 Jun 2023 14:45:32 +0100 Subject: [PATCH 61/97] Update remove/replace charge tests --- .../serial-elec-rr1.inp | 8 +++++++- .../serial-elec-rr1.log | 17 ++++++---------- .../serial-elec-rr2.inp | 8 +++++++- .../serial-elec-rr2.log | 15 +++++--------- .../serial-elec-rr3.inp | 6 ++++++ .../serial-elec-rr3.log | 20 ++++++------------- .../serial-elec-rr4.inp | 6 ++++++ .../serial-elec-rr4.log | 18 +++++------------ 8 files changed, 48 insertions(+), 50 deletions(-) rename tests/regression/{d3q19-elec => d3q19-short}/serial-elec-rr1.inp (94%) rename tests/regression/{d3q19-elec => d3q19-short}/serial-elec-rr1.log (94%) rename tests/regression/{d3q19-elec => d3q19-short}/serial-elec-rr2.inp (94%) rename tests/regression/{d3q19-elec => d3q19-short}/serial-elec-rr2.log (95%) rename tests/regression/{d3q19-elec => d3q19-short}/serial-elec-rr3.inp (94%) rename tests/regression/{d3q19-elec => d3q19-short}/serial-elec-rr3.log (93%) rename tests/regression/{d3q19-elec => d3q19-short}/serial-elec-rr4.inp (95%) rename tests/regression/{d3q19-elec => d3q19-short}/serial-elec-rr4.log (94%) diff --git a/tests/regression/d3q19-elec/serial-elec-rr1.inp b/tests/regression/d3q19-short/serial-elec-rr1.inp similarity index 94% rename from tests/regression/d3q19-elec/serial-elec-rr1.inp rename to tests/regression/d3q19-short/serial-elec-rr1.inp index 46c53fb4..11783808 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr1.inp +++ b/tests/regression/d3q19-short/serial-elec-rr1.inp @@ -59,7 +59,8 @@ colloid_one_q1 0.0000 ############################################################################### freq_statistics 1 -config_at_end no +freq_psi_resid 10 +config_at_end no default_io_grid 1_1_1 colloid_io_freq 1000 @@ -79,6 +80,11 @@ electrokinetics_d0 0.01 electrokinetics_d1 0.01 electrokinetics_eunit 1.0 electrokinetics_epsilon 3.3e3 + +electrokinetics_rel_tol 1e-08 +electrokinetics_abs_tol 1e-15 +electrokinetics_maxits 2000 + electrokinetics_init uniform electrokinetics_init_rho_el 0.001 diff --git a/tests/regression/d3q19-elec/serial-elec-rr1.log b/tests/regression/d3q19-short/serial-elec-rr1.log similarity index 94% rename from tests/regression/d3q19-elec/serial-elec-rr1.log rename to tests/regression/d3q19-short/serial-elec-rr1.log index 35b77ae2..b509382e 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr1.log +++ b/tests/regression/d3q19-short/serial-elec-rr1.log @@ -1,6 +1,5 @@ Welcome to Ludwig v0.7.32 (Serial version running on 1 process) -The SVN revision details are: 3209M Note assertions via standard C assert() are on. Read 36 user parameters from serial-elec-rr1.inp @@ -30,9 +29,11 @@ Valency species 0: 1 Diffusivity species 0: 1.0000000e-02 Valency species 1: -1 Diffusivity species 1: 1.0000000e-02 -Relative tolerance: 1.1920929e-07 -Absolute tolerance: 1.1920929e-09 -Max. no. of iterations: 10000 +Solver type: sor +Solver stencil points: 7 +Relative tolerance: 1.0000000e-08 +Absolute tolerance: 1.0000000e-15 +Max. no. of iterations: 2000 Number of multisteps: 1 Diffusive accuracy in NPE: 0.0000000e+00 Force calculation: phi_gradmu_correction @@ -140,9 +141,6 @@ Colloid velocities - x y z [minimum ] 0.0000000e+00 0.0000000e+00 1.0000000e-02 [maximum ] 0.0000000e+00 0.0000000e+00 1.0000000e-02 -SOR solver converged to relative tolerance -SOR residual per site 4.6031502e-09 at 105 iterations -1 multisteps Scalars - total mean variance min max [rho] 32716.00 1.00000000000 2.0078220e-08 0.99474397928 1.00525602072 @@ -172,13 +170,10 @@ Colloid velocities - x y z [minimum ] 1.2272861e-17 -4.1743567e-18 5.2560207e-03 [maximum ] 1.2272861e-17 -4.1743567e-18 5.2560207e-03 -SOR solver converged to relative tolerance -SOR residual per site 7.8603898e-11 at 110 iterations -1 multisteps Scalars - total mean variance min max [rho] 32715.00 1.00000000000 3.1667262e-08 0.99486242850 1.00470540593 -[psi] -2.4471310e-14 -6.0146459e-04 1.3552052e-02 +[psi] -2.4471310e-14 -6.0146458e-04 1.3552052e-02 [rho] 3.2726000e+01 1.8867925e-04 1.0264264e-03 [rho] 3.2726000e+01 4.3470370e-20 1.0269783e-03 [elc] 2.4730652e-15 -5.5183543e-07 1.8867925e-04 diff --git a/tests/regression/d3q19-elec/serial-elec-rr2.inp b/tests/regression/d3q19-short/serial-elec-rr2.inp similarity index 94% rename from tests/regression/d3q19-elec/serial-elec-rr2.inp rename to tests/regression/d3q19-short/serial-elec-rr2.inp index 9a93ec2c..6b1668b5 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr2.inp +++ b/tests/regression/d3q19-short/serial-elec-rr2.inp @@ -59,7 +59,8 @@ colloid_one_q1 0.0000 ############################################################################### freq_statistics 1 -config_at_end no +freq_psi_resid 10 +config_at_end no default_io_grid 1_1_1 colloid_io_freq 1000 @@ -79,6 +80,11 @@ electrokinetics_d0 0.01 electrokinetics_d1 0.01 electrokinetics_eunit 1.0 electrokinetics_epsilon 3.3e3 + +electrokinetics_rel_tol 1e-08 +electrokinetics_abs_tol 1e-15 +electrokinetics_maxits 2000 + electrokinetics_init uniform electrokinetics_init_rho_el 0.001 diff --git a/tests/regression/d3q19-elec/serial-elec-rr2.log b/tests/regression/d3q19-short/serial-elec-rr2.log similarity index 95% rename from tests/regression/d3q19-elec/serial-elec-rr2.log rename to tests/regression/d3q19-short/serial-elec-rr2.log index eaf1f68b..2ebc1b91 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr2.log +++ b/tests/regression/d3q19-short/serial-elec-rr2.log @@ -1,6 +1,5 @@ Welcome to Ludwig v0.7.32 (Serial version running on 1 process) -The SVN revision details are: 3209M Note assertions via standard C assert() are on. Read 38 user parameters from serial-elec-rr2.inp @@ -30,9 +29,11 @@ Valency species 0: 1 Diffusivity species 0: 1.0000000e-02 Valency species 1: -1 Diffusivity species 1: 1.0000000e-02 -Relative tolerance: 1.1920929e-07 -Absolute tolerance: 1.1920929e-09 -Max. no. of iterations: 10000 +Solver type: sor +Solver stencil points: 7 +Relative tolerance: 1.0000000e-08 +Absolute tolerance: 1.0000000e-15 +Max. no. of iterations: 2000 Number of multisteps: 1 Diffusive accuracy in NPE: 0.0000000e+00 Force calculation: phi_gradmu_correction @@ -142,9 +143,6 @@ Colloid velocities - x y z [minimum ] 0.0000000e+00 0.0000000e+00 1.0000000e-02 [maximum ] 0.0000000e+00 0.0000000e+00 1.0000000e-02 -SOR solver converged to relative tolerance -SOR residual per site 4.5872319e-09 at 105 iterations -1 multisteps Scalars - total mean variance min max [rho] 32715.00 1.00000000000 1.9422049e-08 0.99561998273 1.00525602072 @@ -174,9 +172,6 @@ Colloid velocities - x y z [minimum ] 1.3724696e-17 -1.7125470e-20 5.2560207e-03 [maximum ] 1.3724696e-17 -1.7125470e-20 5.2560207e-03 -SOR solver converged to relative tolerance -SOR residual per site 7.8574724e-11 at 110 iterations -1 multisteps Scalars - total mean variance min max [rho] 32716.00 1.00000000000 3.1603349e-08 0.99563728266 1.00516207987 diff --git a/tests/regression/d3q19-elec/serial-elec-rr3.inp b/tests/regression/d3q19-short/serial-elec-rr3.inp similarity index 94% rename from tests/regression/d3q19-elec/serial-elec-rr3.inp rename to tests/regression/d3q19-short/serial-elec-rr3.inp index 1babe87b..90b2dcf6 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr3.inp +++ b/tests/regression/d3q19-short/serial-elec-rr3.inp @@ -57,6 +57,7 @@ colloid_one_q1 0.0000 ############################################################################### freq_statistics 1 +freq_psi_resid 10 config_at_end no default_io_grid 1_1_1 @@ -77,6 +78,11 @@ electrokinetics_d0 0.01 electrokinetics_d1 0.01 electrokinetics_eunit 1.0 electrokinetics_epsilon 3.3e3 + +electrokinetics_rel_tol 1e-08 +electrokinetics_abs_tol 1e-15 +electrokinetics_maxits 2000 + electrokinetics_init uniform electrokinetics_init_rho_el 0.001 diff --git a/tests/regression/d3q19-elec/serial-elec-rr3.log b/tests/regression/d3q19-short/serial-elec-rr3.log similarity index 93% rename from tests/regression/d3q19-elec/serial-elec-rr3.log rename to tests/regression/d3q19-short/serial-elec-rr3.log index 7371036c..d6c49f82 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr3.log +++ b/tests/regression/d3q19-short/serial-elec-rr3.log @@ -1,6 +1,5 @@ Welcome to Ludwig v0.7.32 (Serial version running on 1 process) -The SVN revision details are: 3209M Note assertions via standard C assert() are on. Read 36 user parameters from serial-elec-rr3.inp @@ -30,9 +29,11 @@ Valency species 0: 1 Diffusivity species 0: 1.0000000e-02 Valency species 1: -1 Diffusivity species 1: 1.0000000e-02 -Relative tolerance: 1.1920929e-07 -Absolute tolerance: 1.1920929e-09 -Max. no. of iterations: 10000 +Solver type: sor +Solver stencil points: 7 +Relative tolerance: 1.0000000e-08 +Absolute tolerance: 1.0000000e-15 +Max. no. of iterations: 2000 Number of multisteps: 1 Diffusive accuracy in NPE: 0.0000000e+00 Force calculation: phi_gradmu_correction @@ -140,9 +141,6 @@ Colloid velocities - x y z [minimum ] 0.0000000e+00 0.0000000e+00 1.0000000e-02 [maximum ] 0.0000000e+00 0.0000000e+00 1.0000000e-02 -SOR solver converged to relative tolerance -SOR residual per site 4.6031502e-09 at 105 iterations -1 multisteps Scalars - total mean variance min max [rho] 32716.00 1.00000000000 2.0078220e-08 0.99474397928 1.00525602072 @@ -172,13 +170,10 @@ Colloid velocities - x y z [minimum ] 1.0872407e-17 -2.4106414e-18 5.2560207e-03 [maximum ] 1.0872407e-17 -2.4106414e-18 5.2560207e-03 -SOR solver converged to relative tolerance -SOR residual per site 7.8603969e-11 at 110 iterations -1 multisteps Scalars - total mean variance min max [rho] 32715.00 1.00000000000 3.1667262e-08 0.99486242850 1.00470540593 -[psi] -1.4641066e-14 -6.0146459e-04 1.3552052e-02 +[psi] -1.4641066e-14 -6.0146458e-04 1.3552052e-02 [rho] 3.2726000e+01 1.8867925e-04 1.0264264e-03 [rho] 3.2726000e+01 1.2785403e-22 1.0269783e-03 [elc] 2.4223787e-15 -5.5183543e-07 1.8867925e-04 @@ -204,9 +199,6 @@ Colloid velocities - x y z [minimum ] 4.6899957e-18 5.7442582e-18 4.4861247e-03 [maximum ] 4.6899957e-18 5.7442582e-18 4.4861247e-03 -SOR solver converged to relative tolerance -SOR residual per site 4.3258364e-12 at 100 iterations -1 multisteps Scalars - total mean variance min max [rho] 32715.00 1.00000000000 3.1199459e-08 0.99620284837 1.00361727101 diff --git a/tests/regression/d3q19-elec/serial-elec-rr4.inp b/tests/regression/d3q19-short/serial-elec-rr4.inp similarity index 95% rename from tests/regression/d3q19-elec/serial-elec-rr4.inp rename to tests/regression/d3q19-short/serial-elec-rr4.inp index cd3917f5..95886237 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr4.inp +++ b/tests/regression/d3q19-short/serial-elec-rr4.inp @@ -58,6 +58,7 @@ colloid_one_q1 0.0000 ############################################################################### freq_statistics 1 +freq_psi_resid 10 config_at_end no default_io_grid 1_1_1 @@ -78,6 +79,11 @@ electrokinetics_d0 0.01 electrokinetics_d1 0.01 electrokinetics_eunit 1.0 electrokinetics_epsilon 3.3e3 + +electrokinetics_rel_tol 1e-08 +electrokinetics_abs_tol 1e-15 +electrokinetics_maxits 2000 + electrokinetics_init uniform electrokinetics_init_rho_el 0.001 diff --git a/tests/regression/d3q19-elec/serial-elec-rr4.log b/tests/regression/d3q19-short/serial-elec-rr4.log similarity index 94% rename from tests/regression/d3q19-elec/serial-elec-rr4.log rename to tests/regression/d3q19-short/serial-elec-rr4.log index dc69fd73..8b896829 100644 --- a/tests/regression/d3q19-elec/serial-elec-rr4.log +++ b/tests/regression/d3q19-short/serial-elec-rr4.log @@ -1,6 +1,5 @@ Welcome to Ludwig v0.7.32 (Serial version running on 1 process) -The SVN revision details are: 3209M Note assertions via standard C assert() are on. Read 38 user parameters from serial-elec-rr4.inp @@ -30,9 +29,11 @@ Valency species 0: 1 Diffusivity species 0: 1.0000000e-02 Valency species 1: -1 Diffusivity species 1: 1.0000000e-02 -Relative tolerance: 1.1920929e-07 -Absolute tolerance: 1.1920929e-09 -Max. no. of iterations: 10000 +Solver type: sor +Solver stencil points: 7 +Relative tolerance: 1.0000000e-08 +Absolute tolerance: 1.0000000e-15 +Max. no. of iterations: 2000 Number of multisteps: 1 Diffusive accuracy in NPE: 0.0000000e+00 Force calculation: phi_gradmu_correction @@ -142,9 +143,6 @@ Colloid velocities - x y z [minimum ] 0.0000000e+00 0.0000000e+00 1.0000000e-02 [maximum ] 0.0000000e+00 0.0000000e+00 1.0000000e-02 -SOR solver converged to relative tolerance -SOR residual per site 4.5872319e-09 at 105 iterations -1 multisteps Scalars - total mean variance min max [rho] 32715.00 1.00000000000 1.9422049e-08 0.99561998273 1.00525602072 @@ -174,9 +172,6 @@ Colloid velocities - x y z [minimum ] 4.7845262e-18 4.6982895e-18 5.2560207e-03 [maximum ] 4.7845262e-18 4.6982895e-18 5.2560207e-03 -SOR solver converged to relative tolerance -SOR residual per site 7.8574612e-11 at 110 iterations -1 multisteps Scalars - total mean variance min max [rho] 32716.00 1.00000000000 3.1603349e-08 0.99563728266 1.00516207987 @@ -206,9 +201,6 @@ Colloid velocities - x y z [minimum ] 1.2400469e-17 1.2170529e-17 4.4267044e-03 [maximum ] 1.2400469e-17 1.2170529e-17 4.4267044e-03 -SOR solver converged to relative tolerance -SOR residual per site 4.3359265e-12 at 100 iterations -1 multisteps Scalars - total mean variance min max [rho] 32716.00 1.00000000000 3.1084085e-08 0.99649224677 1.00374026557 From 6cc1071fbcdf5429715e3a84575c6677409326e8 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Wed, 21 Jun 2023 15:16:45 +0100 Subject: [PATCH 62/97] Add in correct location --- tests/regression/{d3q19-elec => d3q19-short}/serial-elec-lj1.inp | 0 tests/regression/{d3q19-elec => d3q19-short}/serial-elec-lj1.log | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/regression/{d3q19-elec => d3q19-short}/serial-elec-lj1.inp (100%) rename tests/regression/{d3q19-elec => d3q19-short}/serial-elec-lj1.log (100%) diff --git a/tests/regression/d3q19-elec/serial-elec-lj1.inp b/tests/regression/d3q19-short/serial-elec-lj1.inp similarity index 100% rename from tests/regression/d3q19-elec/serial-elec-lj1.inp rename to tests/regression/d3q19-short/serial-elec-lj1.inp diff --git a/tests/regression/d3q19-elec/serial-elec-lj1.log b/tests/regression/d3q19-short/serial-elec-lj1.log similarity index 100% rename from tests/regression/d3q19-elec/serial-elec-lj1.log rename to tests/regression/d3q19-short/serial-elec-lj1.log From eff26ded1a92b5f6ac32a72e14585d69f18b1c32 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 22 Jun 2023 17:47:21 +0100 Subject: [PATCH 63/97] Missing return value --- tests/unit/test_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_util.c b/tests/unit/test_util.c index 18699da4..8b7ced2e 100644 --- a/tests/unit/test_util.c +++ b/tests/unit/test_util.c @@ -137,7 +137,7 @@ int util_jacobi_check(void) { assert(ifail == 0); } - return 0; + return ifail; } /***************************************************************************** From 6dd79d6ffa5e531e791bfc15e10b9ce04697174f Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 22 Jun 2023 17:47:41 +0100 Subject: [PATCH 64/97] Trap command line arguments with message --- src/main.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 11a35d4e..72770bc8 100644 --- a/src/main.c +++ b/src/main.c @@ -36,7 +36,19 @@ int main(int argc, char ** argv) { PetscInitialize(&argc, &argv, (char*) 0, NULL); #endif - ludwig_run(inputfile); + if (argc == 1) { + ludwig_run(inputfile); + } + else { + /* No command line arguments please */ + int rank = -1; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank == 0) { + printf("Command line arguments are now disabled.\n"); + printf("In particular, the input file must be called \"input\"\n"); + printf("and be in the current working directory.\n"); + } + } #ifdef PETSC PetscFinalize(); From 89340117f77cae99a488dbb1d22a7311d8b32b85 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 22 Jun 2023 17:48:03 +0100 Subject: [PATCH 65/97] Disallow moving wall if wall is X --- src/wall_rt.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/wall_rt.c b/src/wall_rt.c index 22d44a1a..6aaf1676 100644 --- a/src/wall_rt.c +++ b/src/wall_rt.c @@ -25,8 +25,6 @@ int wall_rt_init(pe_t * pe, cs_t * cs, rt_t * rt, lb_t * lb, map_t * map, wall_t ** wall) { - double ux_bot = 0.0; - double ux_top = 0.0; wall_slip_t ws = {0}; wall_param_t p = {0}; @@ -47,6 +45,8 @@ int wall_rt_init(pe_t * pe, cs_t * cs, rt_t * rt, lb_t * lb, map_t * map, /* Run through input parameters */ if (p.iswall) { + double ux_bot = 0.0; + double ux_top = 0.0; rt_double_parameter(rt, "boundary_speed_bottom", &ux_bot); rt_double_parameter(rt, "boundary_speed_top", &ux_top); @@ -55,6 +55,12 @@ int wall_rt_init(pe_t * pe, cs_t * cs, rt_t * rt, lb_t * lb, map_t * map, p.ubot[X] = ux_bot; p.ubot[Y] = 0.0; p.ubot[Z] = 0.0; p.utop[X] = ux_top; p.utop[Y] = 0.0; p.utop[Z] = 0.0; + /* If there is a boundary ux, the wall cannot be in X */ + if ((ux_bot != 0.0 || ux_top != 0.0) && p.isboundary[X]) { + pe_info(pe, "Cannot have non-zero u_x wall velocity if wall is X\n"); + pe_fatal(pe, "Please check and try again\n"); + } + rt_double_parameter(rt, "boundary_lubrication_rcnormal", &p.lubr_rc[X]); p.lubr_rc[Y] = p.lubr_rc[X]; p.lubr_rc[Z] = p.lubr_rc[X]; From 1b8e52bd7cf41806f7badd655666c457fcce2a69 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Jun 2023 07:51:59 +0100 Subject: [PATCH 66/97] Move serial tests to github --- .github/workflows/regression.yml | 20 ++++++++++++++++++++ .travis.yml | 5 ----- 2 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/regression.yml diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml new file mode 100644 index 00000000..de70edf9 --- /dev/null +++ b/.github/workflows/regression.yml @@ -0,0 +1,20 @@ +# Regression tests (serial) + +name: "Regression (serial)" + +on: + pull_request: + branches: + - master + - develop + +jobs: + run-serial-tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Configure + run: gcc --version diff --git a/.travis.yml b/.travis.yml index 6958a826..0c1fdeaf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,12 +5,7 @@ cache: before_install: - bash ./config/build-mpi.sh script: - - cp config/travis-gcc.mk ./config.mk - - make serial - - make - export OMP_NUM_THREADS=1 - - make test - - make clean - export PATH=$(pwd)/mpi/bin:${PATH} - cp config/travis-mpicc.mk ./config.mk - make From 0cdb0a8459f0b4de95c717e099db7a0df12214f2 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Jun 2023 08:09:12 +0100 Subject: [PATCH 67/97] Add configuration --- .github/workflows/regression.yml | 7 +++++-- config/github-gcc.mk | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 config/github-gcc.mk diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index de70edf9..6aa7b554 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -1,6 +1,6 @@ # Regression tests (serial) -name: "Regression (serial)" +name: "Regression test" on: pull_request: @@ -9,7 +9,7 @@ on: - develop jobs: - run-serial-tests: + make-d3q19-short: runs-on: ubuntu-latest steps: @@ -18,3 +18,6 @@ jobs: - name: Configure run: gcc --version + run: cp config/github-gcc.mk config.mk + run: make serial + run: make diff --git a/config/github-gcc.mk b/config/github-gcc.mk new file mode 100644 index 00000000..1e5f9d44 --- /dev/null +++ b/config/github-gcc.mk @@ -0,0 +1,22 @@ +############################################################################## +# +# github-gcc.mk +# +############################################################################## + +BUILD = serial +MODEL = -D_D3Q19_ + +CC = gcc -fopenmp +CFLAGS = -g -Wall -O2 + +AR = ar +ARFLAGS = -cru +LDFLAGS = + +MPI_INC_PATH = ./mpi_s +MPI_LIB_PATH = ./mpi_s +MPI_LIB = -lmpi + +LAUNCH_SERIAL_CMD = +LAUNCH_MPIRUN_CMD = From 7202d1dee76cd6e6cd926133f3e75a9d675c8155 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Jun 2023 08:20:50 +0100 Subject: [PATCH 68/97] Correction --- .github/workflows/regression.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index 6aa7b554..3381dc10 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -17,7 +17,7 @@ jobs: uses: actions/checkout@v3 - name: Configure - run: gcc --version - run: cp config/github-gcc.mk config.mk - run: make serial - run: make + - run: gcc --version + - run: cp config/github-gcc.mk config.mk + - run: make serial + - run: make From 24f9f1d33dcab53e556dbcb11eedc72ccee13cfd Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Jun 2023 08:25:22 +0100 Subject: [PATCH 69/97] Every step must have a uses or a run --- .github/workflows/regression.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index 3381dc10..6d8ab167 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -16,8 +16,9 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - - name: Configure - - run: gcc --version - - run: cp config/github-gcc.mk config.mk - - run: make serial - - run: make + - name: Build + run: | + gcc --version + cp config/github-gcc.mk config.mk + make serial + make -j 2 From 9e9f36d9bfaf25ac5be1d4b50dc4c25f44855d49 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Jun 2023 08:29:01 +0100 Subject: [PATCH 70/97] Indentation is significant --- .github/workflows/regression.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index 6d8ab167..34e96aa5 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -18,7 +18,7 @@ jobs: - name: Build run: | - gcc --version - cp config/github-gcc.mk config.mk - make serial - make -j 2 + gcc --version + cp config/github-gcc.mk config.mk + make serial + make -j 2 From 53305301d8740f8971861ae9201bc99857b4b9ed Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Jun 2023 08:39:29 +0100 Subject: [PATCH 71/97] Add check --- .github/workflows/regression.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index 34e96aa5..ec169efc 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -22,3 +22,6 @@ jobs: cp config/github-gcc.mk config.mk make serial make -j 2 + + - name: Check + run: make test From b082132197f23b1052fd0378f38cbe9b73c003ae Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Jun 2023 08:46:25 +0100 Subject: [PATCH 72/97] Control threads --- .github/workflows/regression.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index ec169efc..5a10ac8e 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -23,5 +23,8 @@ jobs: make serial make -j 2 + # Some care may be required with threads - name: Check - run: make test + run: | + export OMP_NUM_THREADS=1 + make test From 0e15dd54636d63c98350abbda3d60e9ae9d487ff Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Jun 2023 09:03:06 +0100 Subject: [PATCH 73/97] Old test failing and removed as irrelevant --- tests/regression/d2q9/serial-surf-t02.inp | 107 ------------------ tests/regression/d2q9/serial-surf-t02.log | 132 ---------------------- 2 files changed, 239 deletions(-) delete mode 100644 tests/regression/d2q9/serial-surf-t02.inp delete mode 100644 tests/regression/d2q9/serial-surf-t02.log diff --git a/tests/regression/d2q9/serial-surf-t02.inp b/tests/regression/d2q9/serial-surf-t02.inp deleted file mode 100644 index fecc1efb..00000000 --- a/tests/regression/d2q9/serial-surf-t02.inp +++ /dev/null @@ -1,107 +0,0 @@ -############################################################################## -# -# Surfactant model test (with hydrodynamics) -# -# See also serial-surf-t01.inp -# -# In two dimensions, intialise a drop (ie, a circle) -# in the composition with uniform surfactant. -# -# Hydrodynamics is on, and the force on the fluid from -# the thermodynamics is via phi grad mu (the only way -# available at the time of writing; there's no option). -# -# Some 100,000 time steps are required to reach equilibrium -# in this case (no change at precision of standard output). -# -############################################################################## - -############################################################################## -# -# Run duration -# -############################################################################### - -N_cycles 20 - -############################################################################## -# -# System -# -############################################################################## - -size 32_32_1 -grid 1_1_1 - -############################################################################## -# -# Fluid parameters -# -############################################################################## - -viscosity 0.16666666666667 - -############################################################################## -# -# Free energy parameters -# -############################################################################### - -free_energy surfactant - -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 0.01 - -phi_initialisation drop -phi_init_drop_radius 8.0 -phi_init_drop_amplitude 1.0 - -psi_initialisation uniform -psi_initialisation_psi0 0.0001 - -hydrodynamics yes -fd_gradient_calculation 2d_tomita_fluid - -############################################################################### -# -# Colloid parameters -# -############################################################################### - -colloid_init no_colloids - -############################################################################### -# -# Periodic conditions / boundaries -# -############################################################################### - -periodicity 1_1_1 - -############################################################################### -# -# Output frequency and type -# -############################################################################### - -freq_statistics 20 -config_at_end no - -############################################################################### -# -# Miscellaneous -# -# random_seed +ve integer is the random number generator seed -# -############################################################################### - -random_seed 8361235 diff --git a/tests/regression/d2q9/serial-surf-t02.log b/tests/regression/d2q9/serial-surf-t02.log deleted file mode 100644 index 662ab528..00000000 --- a/tests/regression/d2q9/serial-surf-t02.log +++ /dev/null @@ -1,132 +0,0 @@ -Welcome to Ludwig v0.9.2 (Serial version running on 1 process) - -Note assertions via standard C assert() are on. - -Read 27 user parameters from serial-surf-t02.inp - -System details --------------- -System size: 32 32 1 -Decomposition: 1 1 1 -Local domain: 32 32 1 -Periodic: 1 1 1 -Halo nhalo: 2 -Reorder: true -Initialised: 1 - -Surfactant free energy ----------------------- -Surfactant free energy parameters: -Bulk parameter A = -2.08333e-02 -Bulk parameter B = 2.08333e-02 -Surface penalty kappa = 1.20000e-01 -Scale energy kT = 5.65870e-04 -Surface adsorption e = 3.00000e-02 -Surface psi^2 beta = 0.00000e+00 -Enthalpic term W = 0.00000e+00 - -Derived quantities -Interfacial tension = 4.71404e-02 -Interfacial width = 3.39412e+00 -Langmuir isotherm = 9.98442e+00 - -Using Cahn-Hilliard solver: -Number of fields = 2 -Mobility (phi) = 1.50000e-01 -Mobility (psi) = 1.00000e-02 - -System properties ----------------- -Mean fluid density: 1.00000e+00 -Shear viscosity 1.66667e-01 -Bulk viscosity 1.66667e-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: 1.00000e+00 -Bulk relaxation time: 1.00000e+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) -Initialising droplet radius: 8.0000000e+00 -Initialising droplet amplitude: 1.0000000e+00 -Initialising psi to a uniform value psi0 -Initial value psi0: 1.0000000e-04 -Gradient calculation: 2d_tomita_fluid -Initial conditions. - -Scalars - total mean variance min max -[rho] 1024.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 -[phi] 5.6597516e+02 5.5271012e-01 3.6411123e-01 -9.7620396e-01 9.9964006e-01 -[phi] 1.0240000e-01 1.0000000e-04-4.5825806e-22 1.0000000e-04 1.0000000e-04 - -Momentum - x y z -[total ] 1.4210855e-14 0.0000000e+00 0.0000000e+00 -[fluid ] 1.4210855e-14 0.0000000e+00 0.0000000e+00 - -Starting time step loop. - -Scalars - total mean variance min max -[rho] 1024.00 1.00000000000 3.3785405e-05 0.99310447271 1.01432330422 -[phi] 5.6597516e+02 5.5271012e-01 3.6204978e-01 -9.8526474e-01 9.9464016e-01 -[phi] 1.0240000e-01 1.0000000e-04 3.2331504e-10 7.4125189e-05 1.5362939e-04 - -Free energy density - timestep total fluid -[fed] 20 -2.9403928223e-03 -2.9403928223e-03 - -Momentum - x y z -[total ] 1.8093166e-14 6.4531713e-16 0.0000000e+00 -[fluid ] 1.8093166e-14 6.4531713e-16 0.0000000e+00 - -Velocity - x y z -[minimum ] -1.5015248e-03 -1.5015248e-03 0.0000000e+00 -[maximum ] 1.5015248e-03 1.5015248e-03 1.1754944e-38 - -Completed cycle 20 - -Timer resolution: 1e-06 second - -Timer statistics - Section: tmin tmax total - Total: 0.106 0.106 0.106 0.105538 (1 call) - Time step loop: 0.005 0.006 0.103 0.005159 (20 calls) - Propagation: 0.001 0.001 0.014 0.000701 (20 calls) - Propagtn (krnl) : 0.001 0.001 0.014 0.000697 (20 calls) - Collision: 0.001 0.002 0.028 0.001417 (20 calls) - Collision (krnl) : 0.001 0.002 0.028 0.001413 (20 calls) - Lattice halos: 0.000 0.001 0.008 0.000424 (20 calls) - phi gradients: 0.000 0.001 0.010 0.000482 (20 calls) - phi halos: 0.000 0.000 0.004 0.000219 (20 calls) - BBL: 0.000 0.000 0.000 0.000001 (20 calls) - Force calculation: 0.001 0.001 0.012 0.000621 (20 calls) - phi update: 0.001 0.001 0.027 0.001369 (20 calls) - Free1: 0.000 0.000 0.000 0.000020 (20 calls) From d4af781c77f5896c3df4ac1597285c110c28ec28 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Jun 2023 09:14:03 +0100 Subject: [PATCH 74/97] Reinstate d2q9 tests --- .github/workflows/regression.yml | 21 +++++++++++++++++++++ tests/Makefile | 6 +++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index 5a10ac8e..bf060cde 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -9,6 +9,27 @@ on: - develop jobs: + + make-d2q9: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Build + run: | + gcc --version + cp config/github-gcc.mk config.mk + sed -i "s/D3Q19/D2Q9/" config.mk + make serial + make -j 2 + + - name: Check + run: | + make unit + make -C tests d2q9 + make-d3q19-short: runs-on: ubuntu-latest diff --git a/tests/Makefile b/tests/Makefile index 15b5d052..0bbda9e6 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -6,6 +6,7 @@ # # default: builds unit tests # test: runs unit tests and d3q19-short (default test) +# d2q9 D2Q9 tests # d3q19-short a batch of shorter tests # d3q19-io a batch of tests with file I/O # d3q19-elec a batch of tests for electrokinetics @@ -15,7 +16,7 @@ # Edinburgh Soft Matter and Statistical Physics Group and # Edinburgh Parallel Computing Centre # -# (c) 2015-2022 The University of Edinburgh +# (c) 2015-2023 The University of Edinburgh # Contributing authors: # Kevin Stratford (kevinAepcc.ed.ac.uk) # @@ -40,6 +41,9 @@ verbose: # Specific tests +d2q9: + $(MAKE) -C regression/d2q9 + d3q19-short: $(MAKE) -C regression/d3q19-short From 129cfa435f07d09aaa34b1238ed00abe6960e740 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Jun 2023 09:15:04 +0100 Subject: [PATCH 75/97] Updates for additions of free energy t = 0 --- tests/regression/d2q9/Makefile | 2 +- tests/regression/d2q9/serial-open-phi.log | 5 ++++- tests/regression/d2q9/serial-surf-t01.log | 7 +++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/regression/d2q9/Makefile b/tests/regression/d2q9/Makefile index 43df1dfa..34d0ca27 100644 --- a/tests/regression/d2q9/Makefile +++ b/tests/regression/d2q9/Makefile @@ -28,4 +28,4 @@ mpix64: @echo "TEST --> regression test mpi (64 mpi tasks)" clean: - rm -f *new test-diff* + rm -f *new test-diff* input diff --git a/tests/regression/d2q9/serial-open-phi.log b/tests/regression/d2q9/serial-open-phi.log index c272651a..888cec57 100644 --- a/tests/regression/d2q9/serial-open-phi.log +++ b/tests/regression/d2q9/serial-open-phi.log @@ -36,7 +36,7 @@ Interfacial width = 1.13137e+00 Using Cahn-Hilliard finite difference solver. Mobility M = 1.00000e-01 Order parameter noise = off -Force calculation: divergence method +Force calculation: stress_divergence System properties ---------------- @@ -126,6 +126,9 @@ Scalars - total mean variance min max [rho] 3200.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 [phi] 2.7928338e+03 8.7276057e-01 2.0274235e-01 -9.9999651e-01 1.0000000e+00 +Free energies - timestep f v f/v f_s1 fs_s2 +[fe] 0 -4.7825362015e+01 3.2000000000e+03 -1.4945425630e-02 0.0000000000e+00 0.0000000000e+00 + Momentum - x y z [total ] 3.2000000e+01 0.0000000e+00 0.0000000e+00 [fluid ] 3.2000000e+01 0.0000000e+00 0.0000000e+00 diff --git a/tests/regression/d2q9/serial-surf-t01.log b/tests/regression/d2q9/serial-surf-t01.log index bb2c93d5..9fdac62e 100644 --- a/tests/regression/d2q9/serial-surf-t01.log +++ b/tests/regression/d2q9/serial-surf-t01.log @@ -85,8 +85,11 @@ Initial conditions. Scalars - total mean variance min max [rho] 512.00 1.00000000000 1.1102230e-16 1.00000000000 1.00000000000 -[phi] -2.1427304e-14 -4.1850204e-17 9.4696695e-01 -1.0000000e+00 1.0000000e+00 -[phi] 5.1200000e-02 1.0000000e-04-2.4815418e-22 1.0000000e-04 1.0000000e-04 +[phi] -2.1427304e-14 -4.1850204e-17 9.4696695e-01 -1.0000000e+00 1.0000000e+00 +[phi] 5.1200000e-02 1.0000000e-04 -2.4815418e-22 1.0000000e-04 1.0000000e-04 + +Free energy density - timestep total fluid +[fed] 0 -4.8447490995e-03 -4.8447490995e-03 Momentum - x y z [total ] 7.1054274e-15 0.0000000e+00 0.0000000e+00 From c7010a522375b7e2326664e19a43931a279291f6 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Jun 2023 09:16:52 +0100 Subject: [PATCH 76/97] Do not use tabs --- .github/workflows/regression.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index bf060cde..9da196e4 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -21,14 +21,14 @@ jobs: run: | gcc --version cp config/github-gcc.mk config.mk - sed -i "s/D3Q19/D2Q9/" config.mk + sed -i "s/D3Q19/D2Q9/" config.mk make serial make -j 2 - name: Check run: | make unit - make -C tests d2q9 + make -C tests d2q9 make-d3q19-short: runs-on: ubuntu-latest From 76ef57c0ba8c5596748a56a140298245f4be8812 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Jun 2023 09:27:53 +0100 Subject: [PATCH 77/97] Remove duplicate --- tests/regression/d3q15/pmpi08-auto-c01.inp | 74 ------------ tests/regression/d3q15/pmpi08-auto-c01.log | 130 --------------------- 2 files changed, 204 deletions(-) delete mode 100644 tests/regression/d3q15/pmpi08-auto-c01.inp delete mode 100644 tests/regression/d3q15/pmpi08-auto-c01.log diff --git a/tests/regression/d3q15/pmpi08-auto-c01.inp b/tests/regression/d3q15/pmpi08-auto-c01.inp deleted file mode 100644 index 50898837..00000000 --- a/tests/regression/d3q15/pmpi08-auto-c01.inp +++ /dev/null @@ -1,74 +0,0 @@ -############################################################################## -# -# Colloid velocity autocorrelation test (no noise). -# -############################################################################## - -N_cycles 40 - -############################################################################## -# -# System and MPI -# -############################################################################## - -size 64_64_64 -grid 2_2_2 - - -############################################################################## -# -# Fluid parameters -# -############################################################################## - -free_energy none - -viscosity 0.1 -viscosity_bulk 0.1 - -isothermal_fluctuations off -temperature 0.00002133333 - -############################################################################### -# -# Colloid parameters -# -############################################################################### - -colloid_init input_one - -colloid_one_a0 2.3 -colloid_one_ah 2.3 -colloid_one_r 32.0_32.0_32.0 -colloid_one_v 0.043478261_0.0_0.0 - -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 40 -config_at_end no - -colloid_io_freq 1000 - -############################################################################### -# -# Miscellaneous -# -############################################################################### - -random_seed 8361235 diff --git a/tests/regression/d3q15/pmpi08-auto-c01.log b/tests/regression/d3q15/pmpi08-auto-c01.log deleted file mode 100644 index 708efcb0..00000000 --- a/tests/regression/d3q15/pmpi08-auto-c01.log +++ /dev/null @@ -1,130 +0,0 @@ -Welcome to Ludwig v0.4.6 (MPI version running on 8 processes) - -The SVN revision details are: -Note assertions via standard C assert() are on. - -Read 21 user parameters from pmpi08-auto-c01.inp - -No free energy selected - -System details --------------- -System size: 64 64 64 -Decomposition: 2 2 2 -Local domain: 32 32 32 -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: d3q15 R -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 ---------------------------- -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_a0 2.3000000e+00 -colloid_one_ah 2.3000000e+00 -colloid_one_r 3.2000000e+01 3.2000000e+01 3.2000000e+01 -colloid_one_v 4.3478261e-02 0.0000000e+00 0.0000000e+00 - -Initialised 1 colloid - -Colloid cell list information ------------------------------ -Input radius maximum: 2.3000000e+00 -Final cell list: 11 11 11 -Final cell lengths: 2.9090909e+00 2.9090909e+00 2.9090909e+00 - -Initial conditions. - -Scalars - total mean variance min max -[rho] 262087.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] 2.2158700e+00 0.0000000e+00 0.0000000e+00 -[fluid ] 3.6371878e-12 0.0000000e+00 0.0000000e+00 -[colloids] 2.2158700e+00 0.0000000e+00 0.0000000e+00 - -Starting time step loop. - -Particle statistics: - -Colloid velocities - x y z -[minimum ] 2.3053953e-03 -1.5394796e-17 -1.4571112e-17 -[maximum ] 2.3053953e-03 -1.5394796e-17 -1.4571112e-17 - -Scalars - total mean variance min max -[rho] 262091.00 1.00000000000 6.5697665e-09 0.99967393682 1.00032331163 - -Momentum - x y z -[total ] 2.2158700e+00 1.0323777e-14 -2.2451801e-14 -[fluid ] 2.0999793e+00 1.0991208e-14 -2.2037927e-14 -[colloids] 1.1589069e-01 -6.6743101e-16 -4.1387362e-16 - -Velocity - x y z -[minimum ] -1.8677558e-04 -5.3624238e-04 -5.3624238e-04 -[maximum ] 2.4256769e-03 5.3624238e-04 5.3624238e-04 - -Completed cycle 40 - -Timer resolution: 0.001 second - -Timer statistics - Section: tmin tmax total - Total: 2.544 2.544 2.544 2.543866 (1 call) - Time step loop: 0.062 0.066 2.492 0.062306 (40 calls) - Propagation: 0.001 0.002 0.050 0.001261 (40 calls) - Collision: 0.049 0.053 1.981 0.049522 (40 calls) - Collision (krnl) : 0.049 0.053 1.963 0.049066 (40 calls) - Lattice halos: 0.003 0.007 0.284 0.003555 (80 calls) - phi gradients: 0.000 0.000 0.000 0.000000 (40 calls) - Forces: 0.000 0.000 0.001 0.000021 (40 calls) - Rebuild: 0.001 0.001 0.046 0.001142 (40 calls) - BBL: 0.001 0.001 0.029 0.000732 (40 calls) - Particle halos: 0.000 0.001 0.007 0.000171 (40 calls) - Force calculation: 0.000 0.000 0.000 0.000002 (40 calls) - phi update: 0.000 0.000 0.000 0.000000 (40 calls) - Free1: 0.000 0.000 0.000 0.000000 (80 calls) -Ludwig finished normally. From 1b5eea36145113ebf7ad2e1773bb0096ac02cc58 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Jun 2023 09:33:54 +0100 Subject: [PATCH 78/97] Reinstate d3q15 tests --- .github/workflows/regression.yml | 24 ++++++++++++++++++++++-- tests/Makefile | 4 ++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index 9da196e4..a815b0e0 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -1,6 +1,6 @@ # Regression tests (serial) -name: "Regression test" +name: "Regression" on: pull_request: @@ -10,7 +10,7 @@ on: jobs: - make-d2q9: + d2q9: runs-on: ubuntu-latest steps: @@ -30,6 +30,26 @@ jobs: make unit make -C tests d2q9 + d3q15: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Build + run: | + gcc --version + cp config/github-gcc.mk config.mk + sed -i "s/D3Q19/D3Q15/" config.mk + make serial + make -j 2 + + - name: Check + run: | + make unit + make -C tests d3q15 + make-d3q19-short: runs-on: ubuntu-latest diff --git a/tests/Makefile b/tests/Makefile index 0bbda9e6..5ca482f6 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -7,6 +7,7 @@ # default: builds unit tests # test: runs unit tests and d3q19-short (default test) # d2q9 D2Q9 tests +# d3q15 D3Q15 tests # d3q19-short a batch of shorter tests # d3q19-io a batch of tests with file I/O # d3q19-elec a batch of tests for electrokinetics @@ -44,6 +45,9 @@ verbose: d2q9: $(MAKE) -C regression/d2q9 +d3q15: + $(MAKE) -C regression/d3q15 + d3q19-short: $(MAKE) -C regression/d3q19-short From a28935cbbb6f74efe61e82757649631c66e87330 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Jun 2023 09:35:04 +0100 Subject: [PATCH 79/97] Remove separate parallel rules --- tests/regression/d3q15/Makefile | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/tests/regression/d3q15/Makefile b/tests/regression/d3q15/Makefile index 3d3e09c5..3ac3de4d 100644 --- a/tests/regression/d3q15/Makefile +++ b/tests/regression/d3q15/Makefile @@ -15,19 +15,5 @@ serial: @echo "TEST --> regression tests serial" inputs='serial*inp'; \ for file in $$inputs; do ../../test.sh $$file "" ""; done - -mpix01: - @echo "TEST --> regression tests mpi (1 mpi task)" - inputs='serial*inp'; \ - for file in $$inputs; do ../../test.sh $$file "$(SER)" "$(PAR) 1"; done - -mpix08: - @echo "TEST --> regression tests mpi (8 mpi tasks)" - inputs='pmpi08*inp'; \ - for file in $$inputs; do ../../test.sh $$file "$(SER)" "$(PAR) 8"; done - -mpix64: - @echo "TEST --> regression test mpi (64 mpi tasks)" - clean: - rm -f *new test-diff* + rm -f *new test-diff* input From e8d64d971fb2ac5604d935619724268f598ff82b Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Jun 2023 09:35:24 +0100 Subject: [PATCH 80/97] Add free energy at time zero --- tests/regression/d3q15/serial-le2d-lb1.log | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/regression/d3q15/serial-le2d-lb1.log b/tests/regression/d3q15/serial-le2d-lb1.log index e1eecc06..f1363144 100644 --- a/tests/regression/d3q15/serial-le2d-lb1.log +++ b/tests/regression/d3q15/serial-le2d-lb1.log @@ -1,6 +1,5 @@ Welcome to Ludwig v0.7.33 (Serial version running on 1 process) -The SVN revision details are: 3210M Note assertions via standard C assert() are on. Read 25 user parameters from serial-le2d-lb1.inp @@ -86,6 +85,9 @@ Scalars - total mean variance min max [rho] 4096.00 1.00000000000 0.0000000e+00 1.00000000000 1.00000000000 [phi] -1.1802440e-02 -2.8814551e-06 8.2680383e-04 -4.9977721e-02 4.9988335e-02 +Free energy density - timestep total fluid +[fed] 0 -2.0247427840e-05 -2.0247427840e-05 + Momentum - x y z [total ] 1.7763568e-15 1.3322676e-15 0.0000000e+00 [fluid ] 1.7763568e-15 1.3322676e-15 0.0000000e+00 From 4b7de0f01426f3ce657d3d3175f1d9964ff22012 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Jun 2023 09:57:04 +0100 Subject: [PATCH 81/97] Removed as duplicates --- tests/regression/d3q19-mpi-extra/Makefile | 22 --- .../d3q19-mpi-extra/parallel-auto-c01.inp | 179 ------------------ .../d3q19-mpi-extra/parallel-auto-c01.log | 134 ------------- .../d3q19-mpi-extra/parallel-auto-c02.inp | 179 ------------------ .../d3q19-mpi-extra/parallel-auto-c02.log | 138 -------------- 5 files changed, 652 deletions(-) delete mode 100644 tests/regression/d3q19-mpi-extra/Makefile delete mode 100644 tests/regression/d3q19-mpi-extra/parallel-auto-c01.inp delete mode 100644 tests/regression/d3q19-mpi-extra/parallel-auto-c01.log delete mode 100644 tests/regression/d3q19-mpi-extra/parallel-auto-c02.inp delete mode 100644 tests/regression/d3q19-mpi-extra/parallel-auto-c02.log diff --git a/tests/regression/d3q19-mpi-extra/Makefile b/tests/regression/d3q19-mpi-extra/Makefile deleted file mode 100644 index 4652d144..00000000 --- a/tests/regression/d3q19-mpi-extra/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -############################################################################### -# -# Makefile -# -# D3Q19 extra 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* diff --git a/tests/regression/d3q19-mpi-extra/parallel-auto-c01.inp b/tests/regression/d3q19-mpi-extra/parallel-auto-c01.inp deleted file mode 100644 index c9870e2d..00000000 --- a/tests/regression/d3q19-mpi-extra/parallel-auto-c01.inp +++ /dev/null @@ -1,179 +0,0 @@ -############################################################################## -# -# Colloid velocity autocorrelation test (no noise). -# -############################################################################## - -############################################################################## -# -# 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 40 - -############################################################################## -# -# 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). -# -# reduced_halo [yes|no] use reduced or full halos. Using reduced halos -# is *only* appropriate for fluid only problems. -# Default is no. -# -############################################################################## - -size 64_64_64 -reduced_halo no - -############################################################################## -# -# Fluid parameters -# -# 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' -# -# ghost_modes [on|off] Default is on. -# force FX_FY_FZ Uniform body force on fluid (default zero) -# -############################################################################## - -free_energy none - -viscosity 0.1 -viscosity_bulk 0.1 - -isothermal_fluctuations off -temperature 0.00002133333 - -############################################################################### -# -# Colloid parameters -# -############################################################################### - -colloid_init input_one - -colloid_one_a0 2.3 -colloid_one_ah 2.3 -colloid_one_r 64.0_64.0_32.0 -colloid_one_v 0.043478261_0.0_0.0 -colloid_one_w 0.0_0.0_0.0 -colloid_one_s 1.0_0.0_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 - -############################################################################### -# -# Periodic conditions / boundaries -# -# boundary_walls_on [yes|no] Use built-in side walls [default no] -# periodicity X_Y_Z Sets periodic boundary conditions in coordinate -# directions [default is 1_1_1]. Best to leave this -# unchanged -# boundary_speed_top For use with built-in walls -# boundary_speed_bottom For use with built-in walls -# -# 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] -# determines type of porous media data to be -# supplied -# -############################################################################### - -boundary_walls_on no -periodicity 1_1_1 -boundary_speed_bottom 0.0 -boundary_speed_top 0.0 - -############################################################################### -# -# 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_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 -# The following for particle data are under review... -# n_io_nodes Number of I/O processors for particles -# output_format [ASCII|BINARY] default output format -# input_format [ASCII|BINARY] default input format -# -# phi_format Override default format for particular quantities -# etc... (both input and output) -# -############################################################################### - -freq_statistics 40 -freq_measure 200000 -freq_config 5000000 -freq_phi 100000 -freq_vel 100000 -freq_shear_measurement 100000 -freq_shear_output 100000 -config_at_end no - -distribution_io_grid 1_1_1 - -phi_format ASCII -vel_format ASCII - -############################################################################## -# -# colloid i/o -# -# colloid_io_freq currently set to freq_measure internally -# colloid_io_grid currently set to 1_1_1 internally -# colloid_io_format_input ASCII ASCII_SERIAL BINARY BINARY_SERIAL -# colloid_io_format_output ASCII BINARY -# -# Note that the output is always parallel. A SERIAL input file must -# be a single serial file. -# -############################################################################## - -colloid_io_freq 1000 -colloids_io_grid 1_1_1 -colloid_io_format_input BINARY -colloid_io_format_output BINARY - -qs_dir_format BINARY - -############################################################################### -# -# Miscellaneous -# -# random_seed +ve integer is the random number generator seed -# -############################################################################### - -random_seed 8361235 diff --git a/tests/regression/d3q19-mpi-extra/parallel-auto-c01.log b/tests/regression/d3q19-mpi-extra/parallel-auto-c01.log deleted file mode 100644 index b1a354e3..00000000 --- a/tests/regression/d3q19-mpi-extra/parallel-auto-c01.log +++ /dev/null @@ -1,134 +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 39 user parameters from pmpi08-auto-c01.inp - -No free energy selected - -System details --------------- -System size: 64 64 64 -Decomposition: 2 2 2 -Local domain: 32 32 32 -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: binary -Output format: binary -Single file read flag: 0 - -Requested one colloid via input: -colloid_one_a0 2.3000000e+00 -colloid_one_ah 2.3000000e+00 -colloid_one_r 6.4000000e+01 6.4000000e+01 3.2000000e+01 -colloid_one_v 4.3478261e-02 0.0000000e+00 0.0000000e+00 -colloid_one_w 0.0000000e+00 0.0000000e+00 0.0000000e+00 -colloid_one_s 1.0000000e+00 0.0000000e+00 0.0000000e+00 - -Initialised 1 colloid - -Colloid cell list information ------------------------------ -Input radius maximum: 2.3000000e+00 -Final cell list: 11 11 11 -Final cell lengths: 2.9090909e+00 2.9090909e+00 2.9090909e+00 - -Initial conditions. - -Scalars - total mean variance min max -[rho] 262087.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] 2.2158700e+00 0.0000000e+00 0.0000000e+00 -[fluid ] 3.6371878e-12 0.0000000e+00 0.0000000e+00 -[colloids] 2.2158700e+00 0.0000000e+00 0.0000000e+00 - -Starting time step loop. - -Particle statistics: - -Colloid velocities - x y z -[minimum ] 2.2940581e-03 5.4917032e-18 -2.4809345e-17 -[maximum ] 2.2940581e-03 5.4917032e-18 -2.4809345e-17 - -Scalars - total mean variance min max -[rho] 262091.00 1.00000000000 6.5539134e-09 0.99967850437 1.00032051415 - -Momentum - x y z -[total ] 2.2158700e+00 -9.5225741e-13 -2.1395060e-14 -[fluid ] 2.1013425e+00 -9.5290442e-13 -2.0705659e-14 -[colloids] 1.1452756e-01 6.4701247e-16 -6.8940056e-16 - -Velocity - x y z -[minimum ] -1.7220078e-04 -4.9018114e-04 -4.9018114e-04 -[maximum ] 2.2654790e-03 4.9018114e-04 4.9018114e-04 - -Completed cycle 40 - -Timer resolution: 1e-06 second - -Timer statistics - Section: tmin tmax total - Total: 3.354 3.354 3.354 3.354108 (1 call) - Time step loop: 0.066 0.101 3.227 0.080675 (40 calls) - Propagation: 0.004 0.027 0.543 0.013564 (40 calls) - Propagtn (krnl) : 0.004 0.027 0.538 0.013446 (40 calls) - Collision: 0.009 0.052 1.410 0.035240 (40 calls) - Collision (krnl) : 0.009 0.052 1.409 0.035227 (40 calls) - Lattice halos: 0.002 0.049 0.653 0.008163 (80 calls) - phi gradients: 0.000 0.000 0.000 0.000000 (40 calls) - Forces: 0.000 0.032 0.013 0.000334 (40 calls) - Rebuild: 0.001 0.041 0.130 0.003239 (40 calls) - BBL: 0.002 0.024 0.206 0.005143 (40 calls) - Particle halos: 0.000 0.019 0.166 0.004158 (40 calls) - Force calculation: 0.000 0.000 0.000 0.000000 (40 calls) - phi update: 0.000 0.000 0.000 0.000000 (40 calls) - Free1: 0.000 0.044 0.039 0.000325 (120 calls) -Ludwig finished normally. diff --git a/tests/regression/d3q19-mpi-extra/parallel-auto-c02.inp b/tests/regression/d3q19-mpi-extra/parallel-auto-c02.inp deleted file mode 100644 index 7f28dc33..00000000 --- a/tests/regression/d3q19-mpi-extra/parallel-auto-c02.inp +++ /dev/null @@ -1,179 +0,0 @@ -############################################################################## -# -# Colloid velocity autocorrelation test (with noise). -# -############################################################################## - -############################################################################## -# -# 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 40 - -############################################################################## -# -# 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). -# -# reduced_halo [yes|no] use reduced or full halos. Using reduced halos -# is *only* appropriate for fluid only problems. -# Default is no. -# -############################################################################## - -size 64_64_64 -reduced_halo no - -############################################################################## -# -# Fluid parameters -# -# 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' -# -# ghost_modes [on|off] Default is on. -# force FX_FY_FZ Uniform body force on fluid (default zero) -# -############################################################################## - -free_energy none - -viscosity 0.1 -viscosity_bulk 0.1 - -isothermal_fluctuations on -temperature 0.00002133333 - -############################################################################### -# -# Colloid parameters -# -############################################################################### - -colloid_init input_one - -colloid_one_a0 2.3 -colloid_one_ah 2.3 -colloid_one_r 64.0_64.0_32.0 -colloid_one_v 0.043478261_0.0_0.0 -colloid_one_w 0.0_0.0_0.0 -colloid_one_s 1.0_0.0_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 - -############################################################################### -# -# Periodic conditions / boundaries -# -# boundary_walls_on [yes|no] Use built-in side walls [default no] -# periodicity X_Y_Z Sets periodic boundary conditions in coordinate -# directions [default is 1_1_1]. Best to leave this -# unchanged -# boundary_speed_top For use with built-in walls -# boundary_speed_bottom For use with built-in walls -# -# 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] -# determines type of porous media data to be -# supplied -# -############################################################################### - -boundary_walls_on no -periodicity 1_1_1 -boundary_speed_bottom 0.0 -boundary_speed_top 0.0 - -############################################################################### -# -# 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_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 -# The following for particle data are under review... -# n_io_nodes Number of I/O processors for particles -# output_format [ASCII|BINARY] default output format -# input_format [ASCII|BINARY] default input format -# -# phi_format Override default format for particular quantities -# etc... (both input and output) -# -############################################################################### - -freq_statistics 40 -freq_measure 200000 -freq_config 5000000 -freq_phi 100000 -freq_vel 100000 -freq_shear_measurement 100000 -freq_shear_output 100000 -config_at_end no - -distribution_io_grid 1_1_1 - -phi_format ASCII -vel_format ASCII - -############################################################################## -# -# colloid i/o -# -# colloid_io_freq currently set to freq_measure internally -# colloid_io_grid currently set to 1_1_1 internally -# colloid_io_format_input ASCII ASCII_SERIAL BINARY BINARY_SERIAL -# colloid_io_format_output ASCII BINARY -# -# Note that the output is always parallel. A SERIAL input file must -# be a single serial file. -# -############################################################################## - -colloid_io_freq 1000 -colloids_io_grid 1_1_1 -colloid_io_format_input BINARY -colloid_io_format_output BINARY - -qs_dir_format BINARY - -############################################################################### -# -# Miscellaneous -# -# random_seed +ve integer is the random number generator seed -# -############################################################################### - -random_seed 8361235 diff --git a/tests/regression/d3q19-mpi-extra/parallel-auto-c02.log b/tests/regression/d3q19-mpi-extra/parallel-auto-c02.log deleted file mode 100644 index b8c656a5..00000000 --- a/tests/regression/d3q19-mpi-extra/parallel-auto-c02.log +++ /dev/null @@ -1,138 +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 39 user parameters from pmpi08-auto-c02.inp - -No free energy selected - -System details --------------- -System size: 64 64 64 -Decomposition: 2 2 2 -Local domain: 32 32 32 -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: on -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: binary -Output format: binary -Single file read flag: 0 - -Requested one colloid via input: -colloid_one_a0 2.3000000e+00 -colloid_one_ah 2.3000000e+00 -colloid_one_r 6.4000000e+01 6.4000000e+01 3.2000000e+01 -colloid_one_v 4.3478261e-02 0.0000000e+00 0.0000000e+00 -colloid_one_w 0.0000000e+00 0.0000000e+00 0.0000000e+00 -colloid_one_s 1.0000000e+00 0.0000000e+00 0.0000000e+00 - -Initialised 1 colloid - -Colloid cell list information ------------------------------ -Input radius maximum: 2.3000000e+00 -Final cell list: 11 11 11 -Final cell lengths: 2.9090909e+00 2.9090909e+00 2.9090909e+00 - -Initial conditions. - -Scalars - total mean variance min max -[rho] 262087.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 - -Momentum - x y z -[total ] 2.2158700e+00 0.0000000e+00 0.0000000e+00 -[fluid ] 3.6371878e-12 0.0000000e+00 0.0000000e+00 -[colloids] 2.2158700e+00 0.0000000e+00 0.0000000e+00 - -Starting time step loop. - -Particle statistics: - -Colloid velocities - x y z -[minimum ] 1.3675763e-03 -5.6482729e-04 5.1186310e-04 -[maximum ] 1.3675763e-03 -5.6482729e-04 5.1186310e-04 - -Scalars - total mean variance min max -[rho] 262091.00 1.00000000000 6.3519338e-05 0.96361208587 1.03533858441 - -Momentum - x y z -[total ] 2.2158700e+00 1.7273726e-14 4.0794625e-14 -[fluid ] 2.1321758e+00 1.3760202e-03 -3.8498072e-03 -[colloids] 8.3694239e-02 -1.3760202e-03 3.8498072e-03 - -Velocity - x y z -[minimum ] -2.2651159e-02 -2.0201971e-02 -2.2199155e-02 -[maximum ] 2.2262575e-02 2.0391258e-02 2.4334011e-02 - -Isothermal fluctuations -[eqipart.] 2.1335610e-05 2.1257847e-05 2.1371772e-05 -[measd/kT] 6.3965230e-05 6.3999990e-05 - -Completed cycle 40 - -Timer resolution: 1e-06 second - -Timer statistics - Section: tmin tmax total - Total: 3.801 3.801 3.801 3.800790 (1 call) - Time step loop: 0.076 0.103 3.667 0.091664 (40 calls) - Propagation: 0.004 0.025 0.612 0.015305 (40 calls) - Propagtn (krnl) : 0.004 0.025 0.612 0.015298 (40 calls) - Collision: 0.012 0.065 2.134 0.053342 (40 calls) - Collision (krnl) : 0.012 0.065 2.133 0.053331 (40 calls) - Lattice halos: 0.002 0.053 0.483 0.006032 (80 calls) - phi gradients: 0.000 0.000 0.000 0.000000 (40 calls) - Forces: 0.000 0.041 0.010 0.000244 (40 calls) - Rebuild: 0.001 0.041 0.077 0.001919 (40 calls) - BBL: 0.002 0.019 0.158 0.003953 (40 calls) - Particle halos: 0.000 0.015 0.122 0.003039 (40 calls) - Force calculation: 0.000 0.000 0.000 0.000000 (40 calls) - phi update: 0.000 0.000 0.000 0.000000 (40 calls) - Free1: 0.000 0.078 0.072 0.000598 (120 calls) -Ludwig finished normally. From 48d97d83deb4534f97876160819587505a20ee17 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Jun 2023 10:00:34 +0100 Subject: [PATCH 82/97] Add d3q27 --- .github/workflows/regression.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index a815b0e0..598e9b9e 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -69,3 +69,23 @@ jobs: run: | export OMP_NUM_THREADS=1 make test + + d3q27: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Build + run: | + gcc --version + cp config/github-gcc.mk config.mk + sed -i "s/D3Q19/D3Q27/" config.mk + make serial + make -j 2 + + # No specific tests yet + - name: Check + run: | + make unit From 7dd77fd47280a2dae0af2f6c8f8c929a829f4552 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 23 Jun 2023 10:58:07 +0100 Subject: [PATCH 83/97] Use snprintf --- util/extract.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/util/extract.c b/util/extract.c index a5a51d13..1c92b045 100644 --- a/util/extract.c +++ b/util/extract.c @@ -238,7 +238,8 @@ int main(int argc, char ** argv) { 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); + snprintf(filename, FILENAME_MAX, "%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); @@ -271,7 +272,8 @@ 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, nfile, ifile); + snprintf(filename, FILENAME_MAX, "%s.%3.3d-%3.3d.meta", + stub, nfile, ifile); read_meta_data_file(filename, &metadata); } } From 51d936e282f56e47e120f9377b0b9d0b56e2b79f Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 29 Jun 2023 14:51:55 +0100 Subject: [PATCH 84/97] Remove unnecessary look-up tables --- src/leesedwards.c | 200 +++++----------------------------------------- 1 file changed, 20 insertions(+), 180 deletions(-) diff --git a/src/leesedwards.c b/src/leesedwards.c index d0a98f5a..e4afa362 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-2022 The University of Edinburgh + * (c) 2010-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -34,10 +34,6 @@ struct lees_edw_s { lees_edw_param_t * param; /* Parameters */ int nref; /* Reference count */ - int * icbuff_to_real; /* look up table */ - int * icreal_to_buff; /* look up table */ - int * buffer_duy; /* look up table +/- uy as function of ib */ - MPI_Comm le_comm; /* 1-d communicator */ MPI_Comm le_plane_comm; /* 2-d communicator */ @@ -74,10 +70,6 @@ static __constant__ lees_edw_param_t static_param; __host__ __device__ int lees_edw_buffer_duy(lees_edw_t * le, int ib); -/* Scheduled for removal */ -__host__ __device__ int lees_edw_index_real_to_buffer(lees_edw_t * le, int ic, int idisplace); -__host__ __device__ int lees_edw_index_buffer_to_real(lees_edw_t * le, int ibuf); - /***************************************************************************** * * lees_edw_create @@ -175,9 +167,6 @@ __host__ int lees_edw_free(lees_edw_t * le) { pe_free(le->pe); cs_free(le->cs); - free(le->icbuff_to_real); - free(le->icreal_to_buff); - free(le->buffer_duy); free(le->param); free(le); } @@ -437,7 +426,7 @@ __host__ int lees_edw_info(lees_edw_t * le) { static int lees_edw_init_tables(lees_edw_t * le) { - int ib, ic, ip, n, nb, nh, np; + /* int ib, ic, ip, n, nb, nh, np;*/ int nhalo; int rdims[3]; int cartsz[3]; @@ -464,117 +453,6 @@ static int lees_edw_init_tables(lees_edw_t * le) { le->param->nxbuffer = 2*nhalo*le->param->nplanelocal; - if (le->param->nxbuffer > 0) { - le->icbuff_to_real = (int *) calloc(le->param->nxbuffer, sizeof(int)); - if (le->icbuff_to_real == NULL) pe_fatal(le->pe, "calloc(le) failed\n"); - } - - ib = 0; - for (n = 0; n < le->param->nplanelocal; n++) { - ic = lees_edw_plane_location(le, n) - (nhalo - 1); - for (nh = 0; nh < 2*nhalo; nh++) { - assert(ib < 2*nhalo*le->param->nplanelocal); - le->icbuff_to_real[ib] = ic + nh; - ib++; - } - } - - /* Look up table for real -> buffer index */ - - /* For each x location in the real system, work out the index of - * the appropriate x-location in the buffer region. This is more - * complex, as it depends on whether you are looking across an - * LE boundary, and if so, in which direction. - * ie., we need a look up table = function(x, +/- dx). - * Note that this table exists when no planes are present, ie., - * there is no transformation, ie., f(x, dx) = x + dx for all dx. - */ - - n = (le->param->nlocal[X] + 2*nhalo)*(2*nhalo + 1); - le->param->index_real_nbuffer = n; - - le->icreal_to_buff = (int *) calloc(n, sizeof(int)); - assert(le->icreal_to_buff); - if (le->icreal_to_buff == NULL) pe_fatal(le->pe, "calloc(le) failed\n"); - - /* Set table in abscence of planes. */ - /* Note the elements of the table at the extreme edges of the local - * system point outside the system. Accesses must take care. */ - - for (ic = 1 - nhalo; ic <= le->param->nlocal[X] + nhalo; ic++) { - for (nh = -nhalo; nh <= nhalo; nh++) { - n = (ic + nhalo - 1)*(2*nhalo+1) + (nh + nhalo); - assert(n >= 0 && n < (le->param->nlocal[X] + 2*nhalo)*(2*nhalo + 1)); - le->icreal_to_buff[n] = ic + nh; - } - } - - /* For each position in the buffer, add appropriate - * corrections in the table. */ - - nb = le->param->nlocal[X] + nhalo + 1; - - for (ib = 0; ib < le->param->nxbuffer; ib++) { - np = ib / (2*nhalo); - ip = lees_edw_plane_location(le, np); - - /* This bit of logic chooses the first nhalo points of the - * buffer region for each plane as the 'downward' looking part */ - - if ((ib - np*2*nhalo) < nhalo) { - - /* Looking across the plane in the -ve x-direction */ - - for (ic = ip + 1; ic <= ip + nhalo; ic++) { - for (nh = -nhalo; nh <= -1; nh++) { - if (ic + nh == le->icbuff_to_real[ib]) { - n = (ic + nhalo - 1)*(2*nhalo+1) + (nh + nhalo); - assert(n >= 0 && n < (le->param->nlocal[X] + 2*nhalo)*(2*nhalo + 1)); - le->icreal_to_buff[n] = nb+ib; - } - } - } - } - else { - /* looking across the plane in the +ve x-direction */ - - for (ic = ip - (nhalo - 1); ic <= ip; ic++) { - for (nh = 1; nh <= nhalo; nh++) { - if (ic + nh == le->icbuff_to_real[ib]) { - n = (ic + nhalo - 1)*(2*nhalo+1) + (nh + nhalo); - assert(n >= 0 && n < (le->param->nlocal[X] + 2*nhalo)*(2*nhalo + 1)); - le->icreal_to_buff[n] = nb+ib; - } - } - } - } - /* Next buffer point */ - } - - /* Buffer velocity jumps. When looking from the real system across - * a boundary into a given buffer, what is the associated velocity - * jump? This is +1 for 'looking up' and -1 for 'looking down'.*/ - - if (le->param->nxbuffer > 0) { - le->buffer_duy = (int *) calloc(le->param->nxbuffer, sizeof(int)); - assert(le->buffer_duy); - if (le->buffer_duy == NULL) pe_fatal(le->pe,"calloc(buffer_duy) failed\n"); - } - - ib = 0; - for (n = 0; n < le->param->nplanelocal; n++) { - for (nh = 0; nh < nhalo; nh++) { - assert(ib < le->param->nxbuffer); - le->buffer_duy[ib] = -1; - ib++; - } - for (nh = 0; nh < nhalo; nh++) { - assert(ib < le->param->nxbuffer); - le->buffer_duy[ib] = +1; - ib++; - } - } - /* Set up a 1-dimensional communicator for transfer of data * along the y-direction. */ @@ -804,53 +682,6 @@ int lees_edw_plane_location(lees_edw_t * le, int np) { return ix; } -/***************************************************************************** - * - * lees_edw_index_real_to_buffer - * - * For x index 'ic' and step size 'idisplace', return the x index of the - * translated buffer. - * - *****************************************************************************/ - -__host__ __device__ -int lees_edw_index_real_to_buffer(lees_edw_t * le, int ic, int idisplace) { - - int ib; - - assert(le); - assert(le->icreal_to_buff); - - assert(idisplace >= -le->param->nhalo && idisplace <= +le->param->nhalo); - - ib = (ic + le->param->nhalo - 1)*(2*le->param->nhalo + 1) + idisplace + le->param->nhalo; - - assert(ib >= 0 && ib < le->param->index_real_nbuffer); - - assert(le->icreal_to_buff[ib] == lees_edw_ic_to_buff(le, ic, idisplace)); - return le->icreal_to_buff[ib]; -} - -/***************************************************************************** - * - * lees_edw_index_buffer_to_real - * - * For x index in the buffer region, return the corresponding - * x index in the real system. - * - *****************************************************************************/ - -__host__ __device__ -int lees_edw_index_buffer_to_real(lees_edw_t * le, int ib) { - - assert(le); - assert(le->icbuff_to_real); - assert(ib >=0 && ib < le->param->nxbuffer); - - assert(le->icbuff_to_real[ib] == lees_edw_ibuff_to_real(le, ib)); - return le->icbuff_to_real[ib]; -} - /***************************************************************************** * * lees_edw_buffer_displacement @@ -879,8 +710,7 @@ int lees_edw_buffer_displacement(lees_edw_t * le, int ib, double t, double * dy) *dy = 0.0; 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)); + *dy = tle*le->param->uy*lees_edw_buffer_duy(le, ib); } if (le->param->type == LE_SHEAR_TYPE_OSCILLATORY) { @@ -1207,8 +1037,7 @@ __host__ int lees_edw_buffer_du(lees_edw_t * le, int ib, double ule[3]) { 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)); + ule[Y] = le->param->uy*lees_edw_buffer_duy(le, ib); ule[Z] = 0.0; } else { @@ -1283,21 +1112,32 @@ int lees_edw_ic_to_buff(lees_edw_t * le, int ic, int di) { return ib; } -/****************************************************************************** +/***************************************************************************** * * lees_edw_buffer_duy * - ******************************************************************************/ + * This is the sign (-1 or +1) of the velocity change associated + * with buffer location ib. + * + * The picture is: + * + * nplane = 0 nplane = 1 ... x, ib -> + * nhalo nhalo nhalo nhalo + * -1 +1 -1 +1 + * + *****************************************************************************/ __host__ __device__ int lees_edw_buffer_duy(lees_edw_t * le, int ib) { - int pm1; + int pm1 = +1; assert(le); - assert(ib >= 0 && ib < le->param->nxbuffer); + assert(0 <= ib && ib < le->param->nxbuffer); - pm1 = +1; + /* The first nhalo values for each plane are -1 ... */ if (ib % (2*le->param->nhalo) < le->param->nhalo) pm1 = -1; + assert(pm1 == -1 || pm1 == +1); + return pm1; } From ea937d4c271173201342c78227b22bd6b4305044 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 30 Jun 2023 13:33:18 +0100 Subject: [PATCH 85/97] Remove unused code --- src/leesedwards.c | 43 +++---------------------------------------- 1 file changed, 3 insertions(+), 40 deletions(-) diff --git a/src/leesedwards.c b/src/leesedwards.c index e4afa362..c13d40e4 100644 --- a/src/leesedwards.c +++ b/src/leesedwards.c @@ -206,36 +206,6 @@ __host__ int lees_edw_target(lees_edw_t * le, lees_edw_t ** target) { return 0; } -/***************************************************************************** - * - * lees_edw_nplane_set - * - *****************************************************************************/ - -int lees_edw_nplane_set(lees_edw_t * le, int nplanetotal) { - - assert(le); - - le->param->nplanetotal = nplanetotal; - - return 0; -} - -/***************************************************************************** - * - * lees_edw_plane_uy_set - * - *****************************************************************************/ - -int lees_edw_plane_uy_set(lees_edw_t * le, double uy) { - - assert(le); - - le->param->uy = uy; - - return 0; -} - /***************************************************************************** * * lees_edw_oscillatory_set @@ -426,7 +396,6 @@ __host__ int lees_edw_info(lees_edw_t * le) { static int lees_edw_init_tables(lees_edw_t * le) { - /* int ib, ic, ip, n, nb, nh, np;*/ int nhalo; int rdims[3]; int cartsz[3]; @@ -443,14 +412,7 @@ static int lees_edw_init_tables(lees_edw_t * le) { le->param->nhalo = nhalo; le->param->nplanelocal = le->param->nplanetotal / cartsz[X]; - /* Look up table for buffer -> real index */ - - /* For each 'x' location in the buffer region, work out the corresponding - * x index in the real system: - * - for each boundary there are 2*nhalo buffer planes - * - the locations extend nhalo points either side of the boundary. - */ - + /* Number of buffer planes */ le->param->nxbuffer = 2*nhalo*le->param->nplanelocal; /* Set up a 1-dimensional communicator for transfer of data @@ -585,7 +547,7 @@ int lees_edw_steady_uy(lees_edw_t * le, int ic, double * uy) { /***************************************************************************** * - * lees_edw_get_block_uy + * lees_edw_block_uy * * Return the velocity of the LE 'block' at ic relative to the * centre of the system. @@ -811,6 +773,7 @@ int lees_edw_shear_rate(lees_edw_t * le, double * gammadot) { double ltot[3]; assert(le); + cs_ltot(le->cs, ltot); *gammadot = le->param->uy*le->param->nplanetotal/ltot[X]; From 4e28662235f579823b1c199dbd01e0fc0c982f58 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 30 Jun 2023 17:56:00 +0100 Subject: [PATCH 86/97] Replace electro osmotic flow tests --- .../regression/d3q19-elec/serial-elec-eo1.inp | 127 -------------- .../regression/d3q19-elec/serial-elec-eo1.log | 163 ----------------- .../regression/d3q19-elec/serial-elec-eo2.inp | 131 -------------- .../regression/d3q19-elec/serial-elec-eo2.log | 165 ------------------ 4 files changed, 586 deletions(-) delete mode 100644 tests/regression/d3q19-elec/serial-elec-eo1.inp delete mode 100644 tests/regression/d3q19-elec/serial-elec-eo1.log delete mode 100644 tests/regression/d3q19-elec/serial-elec-eo2.inp delete mode 100644 tests/regression/d3q19-elec/serial-elec-eo2.log diff --git a/tests/regression/d3q19-elec/serial-elec-eo1.inp b/tests/regression/d3q19-elec/serial-elec-eo1.inp deleted file mode 100644 index cff5ff19..00000000 --- a/tests/regression/d3q19-elec/serial-elec-eo1.inp +++ /dev/null @@ -1,127 +0,0 @@ -############################################################################## -# -# Electro-osmotic flow -# -############################################################################## - -N_start 0 -N_cycles 1000 - -############################################################################## -# -# System and MPI -# -############################################################################## - -size 64_2_2 -grid 8_1_1 -periodicity 0_1_1 - -############################################################################## -# -# Fluid parameters -# -############################################################################## - -viscosity 0.1 -viscosity_bulk 0.1 - -isothermal_fluctuations off -temperature 3.3333e-5 - -############################################################################## -# -# Uniform external fields -# -# magnetic_b0 Bx_By_Bz e.g., for dipoles (default zero) -# electric_e0 Ex_Ey_Ez e.g., for electrokinetics (default zero) -# -############################################################################## - -magnetic_b0 0.0_0.0_0.0 -electric_e0 0.0_1.0e-6_0.0 - -############################################################################## -# -# Free energy parameters -# -############################################################################### - -free_energy fe_electro -fe_force_method phi_gradmu_correction - -fd_advection_scheme_order 3 - -############################################################################### -# -# Colloid parameters -# -############################################################################### - -colloid_init none - -############################################################################### -# -# Walls / boundaries -# -############################################################################### - -boundary_walls 1_0_0 -boundary_speed_bottom 0.0 -boundary_speed_top 0.0 -boundary_lubrication_rcnormal 0.0 - -############################################################################### -# -# Output frequency and type -# -############################################################################### - -freq_statistics 1000 -freq_measure 10000000 -freq_config 10000000 -freq_phi 10000000 -freq_psi 50000 -freq_vel 50000 -freq_shear_measurement 1000000 -freq_shear_output 1000000 -config_at_end no - -psi_io_mode mpiio -psi_io_report no - -stats_vel_print_vol_flux yes - -############################################################################## -# -# colloid i/o -# -############################################################################## - -colloid_io_freq 1000 - -############################################################################### -# -# Electrokinetics -# -############################################################################### - -electrokinetics_z0 +1 -electrokinetics_z1 -1 -electrokinetics_d0 0.01 -electrokinetics_d1 0.01 -electrokinetics_eunit 1.0 -electrokinetics_epsilon 3300.0 -electrokinetics_init gouy_chapman -electrokinetics_init_rho_el 0.00 -electrokinetics_init_sigma 0.0125 - -############################################################################### -# -# Miscellaneous -# -# random_seed +ve integer is the random number generator seed -# -############################################################################### - -random_seed 8361235 diff --git a/tests/regression/d3q19-elec/serial-elec-eo1.log b/tests/regression/d3q19-elec/serial-elec-eo1.log deleted file mode 100644 index 47f0b7fd..00000000 --- a/tests/regression/d3q19-elec/serial-elec-eo1.log +++ /dev/null @@ -1,163 +0,0 @@ -Welcome to Ludwig v0.5.48 (Serial version running on 1 process) - -The SVN revision details are: 2982 -Note assertions via standard C assert() are on. - -Read 53 user parameters from serial-elec-eo1.inp - -System details --------------- -System size: 64 2 2 -Decomposition: 1 1 1 -Local domain: 64 2 2 -Periodic: 0 1 1 -Halo nhalo: 1 -Reorder: true -Initialised: 1 - -Free energy details -------------------- - -Electrokinetics (single fluid) selected - -Parameters: -Electrokinetic species: 2 -Boltzmann factor: 3.0000300e+04 (T = 3.3333000e-05) -Unit charge: 1.0000000e+00 -Permittivity: 3.3000000e+03 -Bjerrum length: 7.2343879e-01 -Valency species 0: 1 -Diffusivity species 0: 1.0000000e-02 -Valency species 1: -1 -Diffusivity species 1: 1.0000000e-02 -Relative tolerance: 1.1920929e-07 -Absolute tolerance: 1.1920929e-09 -Max. no. of iterations: 10000 -Number of multisteps: 1 -Diffusive accuracy in NPE: 0.0000000e+00 -Force calculation: phi_gradmu_correction - -System properties ----------------- -Mean fluid density: 1.00000e+00 -Shear viscosity 1.00000e-01 -Bulk viscosity 1.00000e-01 -Temperature 3.33330e-05 -External body force density 0.00000e+00 0.00000e+00 0.00000e+00 -External E-field amplitude 0.00000e+00 1.00000e-06 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 ---------------------------- -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 - -Advection scheme order: 3 - -Initial charge densities ------------------------- -Initial conditions: Gouy Chapman -Initial condition rho_el: 0.0000000e+00 -Debye length: inf -Initial condition sigma: 1.2500000e-02 - -Boundary walls --------------- -Boundary walls: X - - -Boundary speed u_x (bottom): 0.0000000e+00 -Boundary speed u_x (top): 0.0000000e+00 -Boundary normal lubrication rc: 0.0000000e+00 -Wall boundary links allocated: 40 -Memory (total, bytes): 640 -Boundary shear initialise: 0 - -Porous Media ------------- -Wall boundary links allocated: 40 -Memory (total, bytes): 640 -Arranging initial charge neutrality. - -Initial conditions. - -Scalars - total mean variance min max -[rho] 248.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[psi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[rho] 1.0000000e-01 0.0000000e+00 1.2500000e-02 -[rho] 1.0000000e-01 0.0000000e+00 4.0322581e-04 -[elc] -1.4224733e-16 -4.0322581e-04 1.2500000e-02 - -Free energies - timestep f v f/v f_s1 fs_s2 -[fe] 0 -8.8160138392e-01 2.4800000000e+02 -3.5548442900e-03 0.0000000000e+00 0.0000000000e+00 - -Momentum - x y z -[total ] 3.4416914e-15 0.0000000e+00 0.0000000e+00 -[fluid ] 3.4416914e-15 0.0000000e+00 0.0000000e+00 -[walls ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 - -Starting time step loop. - -SOR solver converged to relative tolerance -SOR residual per site 4.5083930e-11 at 235 iterations -1 multisteps - -Scalars - total mean variance min max -[rho] 248.00 1.00000000000 2.2204460e-16 0.99999997379 1.00000004969 -[psi] 1.9317881e-14 -6.0376531e-01 1.1609255e+00 -[rho] 1.0000000e-01 0.0000000e+00 1.2500000e-02 -[rho] 1.0000000e-01 0.0000000e+00 5.4240246e-04 -[elc] -1.7347235e-17 -5.4240246e-04 1.2500000e-02 - -Free energies - timestep f v f/v f_s1 fs_s2 -[fe] 1000 -8.8100642125e-01 2.4800000000e+02 -3.5524452470e-03 0.0000000000e+00 0.0000000000e+00 - -Momentum - x y z -[total ] 3.0239700e-14 -3.3332436e-09 0.0000000e+00 -[fluid ] 5.5885852e-14 -2.4895684e-09 0.0000000e+00 -[walls ] -2.5646152e-14 -8.4367519e-10 0.0000000e+00 - -Velocity - x y z -[minimum ] -5.5809081e-09 -1.3023345e-11 0.0000000e+00 -[maximum ] 5.5809084e-09 1.1754944e-38 1.1754944e-38 -[vol flux] 5.5150329e-14 -2.4891785e-09 0.0000000e+00 - -Completed cycle 1000 - -Timer resolution: 1e-06 second - -Timer statistics - Section: tmin tmax total - Total: 17.781 17.781 17.781 17.781467 (1 call) - Time step loop: 0.017 0.026 17.778 0.017778 (1000 calls) - Propagation: 0.000 0.001 0.303 0.000303 (1000 calls) - Propagtn (krnl) : 0.000 0.001 0.301 0.000301 (1000 calls) - Collision: 0.001 0.001 0.544 0.000544 (1000 calls) - Collision (krnl) : 0.001 0.001 0.543 0.000543 (1000 calls) - Lattice halos: 0.000 0.001 0.445 0.000111 (4000 calls) - phi gradients: 0.000 0.000 0.000 0.000000 (1000 calls) - BBL: 0.000 0.000 0.006 0.000006 (1000 calls) - Force calculation: 0.000 0.000 0.036 0.000018 (2000 calls) - phi update: 0.000 0.000 0.000 0.000000 (1000 calls) - Poisson equation: 0.015 0.023 15.906 0.015906 (1000 calls) - Nernst Planck: 0.000 0.001 0.498 0.000498 (1000 calls) - Free1: 0.000 0.001 0.001 0.000001 (1000 calls) -Ludwig finished normally. diff --git a/tests/regression/d3q19-elec/serial-elec-eo2.inp b/tests/regression/d3q19-elec/serial-elec-eo2.inp deleted file mode 100644 index e4e8b5b3..00000000 --- a/tests/regression/d3q19-elec/serial-elec-eo2.inp +++ /dev/null @@ -1,131 +0,0 @@ -############################################################################## -# -# Electro-osmotic flow -# -############################################################################## - -N_start 0 -N_cycles 100 - -############################################################################## -# -# System and MPI -# -############################################################################## - -size 64_4_4 -grid 1_1_1 -periodicity 0_1_1 - -############################################################################## -# -# Fluid parameters -# -############################################################################## - -viscosity 0.1 -viscosity_bulk 0.1 - -isothermal_fluctuations off -temperature 3.3333e-5 - -############################################################################## -# -# External electric field -# -# electric_e0 Ex_Ey_Ez e.g., for electrokinetics (default zero) -# -############################################################################## - -electric_e0 0.0_3.0e-2_0.0 - -############################################################################## -# -# Free energy parameters -# -############################################################################### - -free_energy fe_electro -fe_force_method phi_gradmu_correction - -fd_advection_scheme_order 3 - - -############################################################################### -# -# Colloid parameters -# -############################################################################### - -colloid_init none - -############################################################################### -# -# Walls / boundaries -# -############################################################################### - -boundary_walls 1_0_0 -boundary_speed_bottom 0.0 -boundary_speed_top 0.0 -boundary_lubrication_rcnormal 0.0 - -############################################################################### -# -# Output frequency and type -# -############################################################################### - -freq_statistics 100 -freq_measure 10000000 -config_at_end no - -psi_io_mode mpiio -psi_io_report no - -stats_vel_print_vol_flux yes - -############################################################################### -# -# 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 -# -# Also important -# -# temperature sets Boltzmann factor beta -# -############################################################################### - -electrokinetics_z0 +1 -electrokinetics_z1 -1 -electrokinetics_d0 0.01 -electrokinetics_d1 0.01 -electrokinetics_eunit 1.0 -electrokinetics_epsilon 3300.0 -electrokinetics_init gouy_chapman -electrokinetics_init_rho_el 0.00 -electrokinetics_init_sigma 0.0125 - -electrokinetics_rel_tol 1e-07 -electrokinetics_abs_tol 1e-09 -electrokinetics_maxits 2000 -electrokinetics_diffacc 0.5 - -electrokinetics_multisteps 1 - -############################################################################### -# -# Miscellaneous -# -# random_seed +ve integer is the random number generator seed -# -############################################################################### - -#random_seed 7361237 -random_seed 8361235 diff --git a/tests/regression/d3q19-elec/serial-elec-eo2.log b/tests/regression/d3q19-elec/serial-elec-eo2.log deleted file mode 100644 index 3770f0d1..00000000 --- a/tests/regression/d3q19-elec/serial-elec-eo2.log +++ /dev/null @@ -1,165 +0,0 @@ -Welcome to Ludwig v0.7.32 (Serial version running on 1 process) - -The SVN revision details are: 3209M -Note assertions via standard C assert() are on. - -Read 46 user parameters from serial-elec-eo2.inp - -System details --------------- -System size: 64 4 4 -Decomposition: 1 1 1 -Local domain: 64 4 4 -Periodic: 0 1 1 -Halo nhalo: 1 -Reorder: true -Initialised: 1 - -Free energy details -------------------- - -Electrokinetics (single fluid) selected - -Parameters: -Electrokinetic species: 2 -Boltzmann factor: 3.0000300e+04 (T = 3.3333000e-05) -Unit charge: 1.0000000e+00 -Permittivity: 3.3000000e+03 -Bjerrum length: 7.2343879e-01 -Valency species 0: 1 -Diffusivity species 0: 1.0000000e-02 -Valency species 1: -1 -Diffusivity species 1: 1.0000000e-02 -Relative tolerance: 1.0000000e-07 -Absolute tolerance: 1.0000000e-09 -Max. no. of iterations: 2000 -Number of multisteps: 1 -Diffusive accuracy in NPE: 5.0000000e-01 -Force calculation: phi_gradmu_correction - -System properties ----------------- -Mean fluid density: 1.00000e+00 -Shear viscosity 1.00000e-01 -Bulk viscosity 1.00000e-01 -Temperature 3.33330e-05 -External body force density 0.00000e+00 0.00000e+00 0.00000e+00 -External E-field amplitude 0.00000e+00 3.00000e-02 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: 8.00000e-01 -Bulk relaxation time: 8.00000e-01 -Ghost relaxation time: 1.00000e+00 -[User ] Random number seed: 8361235 - -Hydrodynamics -------------- -Hydrodynamics: on - -Advection scheme order: 3 - -Initial charge densities ------------------------- -Initial conditions: Gouy Chapman -Initial condition rho_el: 0.0000000e+00 -Debye length: inf -Initial condition sigma: 1.2500000e-02 - -Boundary walls --------------- -Boundary walls: X - - -Boundary speed u_x (bottom): 0.0000000e+00 -Boundary speed u_x (top): 0.0000000e+00 -Boundary normal lubrication rc: 0.0000000e+00 -Wall boundary links allocated: 160 -Memory (total, bytes): 2560 -Boundary shear initialise: 0 - -Porous Media ------------- -Wall boundary links allocated: 160 -Memory (total, bytes): 2560 - -Arranging initial charge neutrality. - -Initial conditions. - -Scalars - total mean variance min max -[rho] 992.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 -[psi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 -[rho] 4.0000000e-01 0.0000000e+00 1.2500000e-02 -[rho] 4.0000000e-01 0.0000000e+00 4.0322581e-04 -[elc] -1.0165480e-15 -4.0322581e-04 1.2500000e-02 - -Free energies - timestep f v f/v f_s1 fs_s2 -[fe] 0 -3.5264055357e+00 9.9200000000e+02 -3.5548442900e-03 0.0000000000e+00 0.0000000000e+00 - -Momentum - x y z -[total ] 1.3766766e-14 0.0000000e+00 0.0000000e+00 -[fluid ] 1.3766766e-14 0.0000000e+00 0.0000000e+00 -[walls ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 - -Starting time step loop. - -SOR solver converged to relative tolerance -SOR residual per site 5.9828809e-11 at 235 iterations -1 multisteps - -Scalars - total mean variance min max -[rho] 992.00 1.00000000000 6.3282712e-15 0.99999999731 1.00000001802 -[psi] 2.7511327e-13 -6.6776790e-01 1.2344090e+00 -[rho] 4.0000000e-01 0.0000000e+00 1.2500000e-02 -[rho] 4.0000000e-01 0.0000000e+00 4.3462600e-04 -[elc] -3.5145498e-15 -4.3462600e-04 1.2500000e-02 - -Free energies - timestep f v f/v f_s1 fs_s2 -[fe] 100 -3.5194748004e+00 9.9200000000e+02 -3.5478576617e-03 0.0000000000e+00 0.0000000000e+00 - -Momentum - x y z -[total ] 2.0927704e-14 -3.9999600e-05 0.0000000e+00 -[fluid ] 3.0253577e-14 -3.6870184e-05 0.0000000e+00 -[walls ] -9.3258734e-15 -3.1294162e-06 0.0000000e+00 - -Velocity - x y z -[minimum ] -7.5708912e-09 -4.0048301e-08 0.0000000e+00 -[maximum ] 7.5708914e-09 1.1754944e-38 1.1754944e-38 -[vol flux] 2.9309890e-14 -3.6717137e-05 0.0000000e+00 - -Completed cycle 100 - -Timer resolution: 0.01 second - -Timer statistics - Section: tmin tmax total - Total: 3.038 3.038 3.038 3.038123 (1 call) - Time step loop: 0.030 0.033 3.034 0.030344 (100 calls) - Propagation: 0.001 0.001 0.057 0.000565 (100 calls) - Propagtn (krnl) : 0.001 0.001 0.056 0.000564 (100 calls) - Collision: 0.001 0.001 0.124 0.001240 (100 calls) - Collision (krnl) : 0.001 0.001 0.124 0.001239 (100 calls) - Lattice halos: 0.000 0.001 0.062 0.000156 (400 calls) - phi gradients: 0.000 0.000 0.000 0.000000 (100 calls) - BBL: 0.000 0.000 0.002 0.000023 (100 calls) - Force calculation: 0.000 0.000 0.012 0.000062 (200 calls) - phi update: 0.000 0.000 0.000 0.000000 (100 calls) - Poisson equation: 0.026 0.029 2.615 0.026147 (100 calls) - Nernst Planck: 0.002 0.002 0.153 0.001526 (100 calls) - Free1: 0.000 0.001 0.001 0.000007 (100 calls) -Ludwig finished normally. From 5afb3327351cfe3bea5ebfd02c8cff7744186cda Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 30 Jun 2023 17:57:12 +0100 Subject: [PATCH 87/97] add explicit tolerances --- tests/regression/d3q19-elec/serial-elec-do1.inp | 6 ++++-- tests/regression/d3q19-elec/serial-elec-do2.inp | 4 ++-- tests/regression/d3q19-elec/serial-elec-do3.inp | 4 ++-- tests/regression/d3q19-elec/serial-elec-ep1.inp | 2 +- tests/regression/d3q19-elec/serial-elec-ep2.inp | 2 +- tests/regression/d3q19-elec/serial-rest-ec1.inp | 6 ++++++ tests/regression/d3q19-elec/serial-rest-ec2.inp | 5 +++++ 7 files changed, 21 insertions(+), 8 deletions(-) diff --git a/tests/regression/d3q19-elec/serial-elec-do1.inp b/tests/regression/d3q19-elec/serial-elec-do1.inp index 0a483331..5e15c852 100644 --- a/tests/regression/d3q19-elec/serial-elec-do1.inp +++ b/tests/regression/d3q19-elec/serial-elec-do1.inp @@ -130,8 +130,10 @@ electrokinetics_epsilon2 300.0 electrokinetics_init uniform electrokinetics_init_rho_el 0.00047 -electrokinetics_rel_tol 1e-06 -electrokinetics_abs_tol 1e-07 +electrokinetics_solver_type sor +electrokinetics_solver_stencil 7 +electrokinetics_rel_tol 1e-10 +electrokinetics_abs_tol 1e-15 electrokinetics_maxits 10000 electrokinetics_diffacc 0.5 diff --git a/tests/regression/d3q19-elec/serial-elec-do2.inp b/tests/regression/d3q19-elec/serial-elec-do2.inp index 82353bae..592fcfee 100644 --- a/tests/regression/d3q19-elec/serial-elec-do2.inp +++ b/tests/regression/d3q19-elec/serial-elec-do2.inp @@ -131,8 +131,8 @@ electrokinetics_epsilon2 300.0 electrokinetics_init uniform electrokinetics_init_rho_el 0.00047 -electrokinetics_rel_tol 1e-06 -electrokinetics_abs_tol 1e-07 +electrokinetics_rel_tol 1e-10 +electrokinetics_abs_tol 1e-15 electrokinetics_maxits 10000 electrokinetics_diffacc 0.5 diff --git a/tests/regression/d3q19-elec/serial-elec-do3.inp b/tests/regression/d3q19-elec/serial-elec-do3.inp index 1cda9f13..759a2d03 100644 --- a/tests/regression/d3q19-elec/serial-elec-do3.inp +++ b/tests/regression/d3q19-elec/serial-elec-do3.inp @@ -117,8 +117,8 @@ electrokinetics_epsilon2 300.0 electrokinetics_init uniform electrokinetics_init_rho_el 0.00047 -electrokinetics_rel_tol 1e-06 -electrokinetics_abs_tol 1e-07 +electrokinetics_rel_tol 1e-09 +electrokinetics_abs_tol 1e-15 electrokinetics_maxits 10000 electrokinetics_diffacc 0.5 diff --git a/tests/regression/d3q19-elec/serial-elec-ep1.inp b/tests/regression/d3q19-elec/serial-elec-ep1.inp index 29e45c37..de7a9bd1 100644 --- a/tests/regression/d3q19-elec/serial-elec-ep1.inp +++ b/tests/regression/d3q19-elec/serial-elec-ep1.inp @@ -140,7 +140,7 @@ electrokinetics_epsilon 100.0 electrokinetics_init uniform electrokinetics_init_rho_el 0.00104 -electrokinetics_rel_tol 1e-07 +electrokinetics_rel_tol 1e-08 electrokinetics_abs_tol 1e-15 electrokinetics_maxits 5000 electrokinetics_diffacc 0.5 diff --git a/tests/regression/d3q19-elec/serial-elec-ep2.inp b/tests/regression/d3q19-elec/serial-elec-ep2.inp index e0d219ca..0a3b392f 100644 --- a/tests/regression/d3q19-elec/serial-elec-ep2.inp +++ b/tests/regression/d3q19-elec/serial-elec-ep2.inp @@ -142,7 +142,7 @@ electrokinetics_epsilon 100.0 electrokinetics_init uniform electrokinetics_init_rho_el 0.00104 -electrokinetics_rel_tol 1e-07 +electrokinetics_rel_tol 1e-08 electrokinetics_abs_tol 1e-15 electrokinetics_maxits 5000 electrokinetics_diffacc 0.5 diff --git a/tests/regression/d3q19-elec/serial-rest-ec1.inp b/tests/regression/d3q19-elec/serial-rest-ec1.inp index 952f83cd..83908003 100644 --- a/tests/regression/d3q19-elec/serial-rest-ec1.inp +++ b/tests/regression/d3q19-elec/serial-rest-ec1.inp @@ -73,6 +73,12 @@ electrokinetics_d0 0.01 electrokinetics_d1 0.01 electrokinetics_eunit 1.0 electrokinetics_epsilon 3.3e3 + +electrokinetics_rel_tol 1e-09 +electrokinetics_abs_tol 1e-15 +electrokinetics_maxits 2000 + + electrokinetics_init uniform electrokinetics_init_rho_el 0.001 diff --git a/tests/regression/d3q19-elec/serial-rest-ec2.inp b/tests/regression/d3q19-elec/serial-rest-ec2.inp index 4d0c8ecc..79f11272 100644 --- a/tests/regression/d3q19-elec/serial-rest-ec2.inp +++ b/tests/regression/d3q19-elec/serial-rest-ec2.inp @@ -60,6 +60,11 @@ electrokinetics_d0 0.01 electrokinetics_d1 0.01 electrokinetics_eunit 1.0 electrokinetics_epsilon 3.3e3 + +electrokinetics_rel_tol 1e-09 +electrokinetics_abs_tol 1e-15 +electrokinetics_maxits 2000 + electrokinetics_init uniform electrokinetics_init_rho_el 0.001 From 88779aeed3de706c43f71eba89186f8ce7004b4d Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 30 Jun 2023 17:58:03 +0100 Subject: [PATCH 88/97] Replace electro-osmotic flow tests as documented --- .../d3q19-short/serial-elec-eo1.inp | 145 +++++++++++++++ .../d3q19-short/serial-elec-eo1.log | 169 ++++++++++++++++++ 2 files changed, 314 insertions(+) create mode 100644 tests/regression/d3q19-short/serial-elec-eo1.inp create mode 100644 tests/regression/d3q19-short/serial-elec-eo1.log diff --git a/tests/regression/d3q19-short/serial-elec-eo1.inp b/tests/regression/d3q19-short/serial-elec-eo1.inp new file mode 100644 index 00000000..1c4c9efa --- /dev/null +++ b/tests/regression/d3q19-short/serial-elec-eo1.inp @@ -0,0 +1,145 @@ +############################################################################## +# +# Electro-osmotic flow +# +# This is a sample input file for the problem described at +# https://ludwig.epcc.ed.ac.uk/tutorials/electrokinetics/electrokinetics.html +# +# We have a quasi-one dimensional system in the x-direction and we +# use the special initialisation +# +# electrokinetics_init gouy_chapman +# +# to provide walls at x = 1 and x = Lx. We arrange for the system to +# have a surface charge 0.03125 and counter-charge only). The whole +# system is electroneutral. E.g., +# +# electrokinetics_init_rho_el 0.0 +# electrokinetics_init_sigma 0.003125 +# +# To drive the flow, we have an external electric field +# +# electric_e0 0.0_0.001_0.0 +# +# For viscoity 0.1 the simulation can be run for 100,000 steps to +# approach a steady state; if the viscosity is 0.01, about 300,000 +# steps will be required. +# +# The fluid velocity can be obtained by switching +# +# config_at_end yes +# +# For the purposes of this test, we only run 10 time steps (and +# the output is switched off). +# +############################################################################## + +############################################################################## +# +# Run duration +# +############################################################################### + +N_cycles 10 + +############################################################################## +# +# System and MPI +# +############################################################################## + +size 64_4_4 +periodicity 1_1_1 +lb_halo_scheme lb_halo_openmp_reduced + +############################################################################## +# +# Fluid parameters +# +# The temperature is relevant for k_B T in the electrokinetics context. +# +############################################################################## + +viscosity 0.1 + +isothermal_fluctuations off +temperature 3.33333333333333333e-5 + + +############################################################################## +# +# Free energy parameters +# +############################################################################### + +free_energy fe_electro +fe_force_method phi_gradmu_correction + +fd_advection_scheme_order 3 + +############################################################################### +# +# Colloid parameters +# +############################################################################### + +colloid_init none + +############################################################################### +# +# Walls / boundaries +# +############################################################################### + +boundary_walls 0_0_0 + +############################################################################### +# +# Output frequency and type +# +############################################################################### + +freq_statistics 10 +freq_psi_resid 1000 +config_at_end no + +default_io_mode mpiio +default_io_format ascii + +psi_io_mode mpiio +psi_io_format ascii +psi_io_report no + + +############################################################################### +# +# Electrokinetics +# +############################################################################### + +electrokinetics_z0 +1 +electrokinetics_z1 -1 +electrokinetics_d0 0.01 +electrokinetics_d1 0.01 +electrokinetics_eunit 1.0 +electrokinetics_epsilon 3.3e3 + +electrokinetics_init gouy_chapman +electrokinetics_init_rho_el 0.0 +electrokinetics_init_sigma 0.03125 + +electrokinetics_solver_type sor +electrokinetics_solver_stencil 7 + +# External electric field in y-direction + +electric_e0 0.0_0.001_0.0 + + +############################################################################### +# +# Miscellaneous +# +############################################################################### + +random_seed 8361235 diff --git a/tests/regression/d3q19-short/serial-elec-eo1.log b/tests/regression/d3q19-short/serial-elec-eo1.log new file mode 100644 index 00000000..a79875df --- /dev/null +++ b/tests/regression/d3q19-short/serial-elec-eo1.log @@ -0,0 +1,169 @@ +Welcome to: Ludwig v0.19.1 (Serial version running on 1 process) +Git commit: 6cc1071fbcdf5429715e3a84575c6677409326e8 + +Start time: Fri Jun 30 17:48:44 2023 + +Compiler: + name: Gnu 11.3.0 + version-string: 11.3.0 + options: -O2 -g -fopenmp -Wall -Werror + +Note assertions via standard C assert() are on. + +Target thread model: OpenMP. +OpenMP threads: 1; maximum number of threads: 8. + +Read 33 user parameters from input + +System details +-------------- +System size: 64 4 4 +Decomposition: 1 1 1 +Local domain: 64 4 4 +Periodic: 1 1 1 +Halo nhalo: 1 +Reorder: true +Initialised: 1 + +Free energy details +------------------- + +Electrokinetics (single fluid) selected + +Parameters: +Electrokinetic species: 2 +Boltzmann factor: 3.0000000e+04 (T = 3.3333333e-05) +Unit charge: 1.0000000e+00 +Permittivity: 3.3000000e+03 +Bjerrum length: 7.2343156e-01 +Valency species 0: 1 +Diffusivity species 0: 1.0000000e-02 +Valency species 1: -1 +Diffusivity species 1: 1.0000000e-02 +Solver type: sor +Solver stencil points: 7 +Relative tolerance: 1.0000000e-08 +Absolute tolerance: 1.0000000e-15 +Max. no. of iterations: 10000 +Number of multisteps: 1 +Diffusive accuracy in NPE: 0.0000000e+00 +Force calculation: phi_gradmu_correction + +System properties +---------------- +Mean fluid density: 1.00000e+00 +Shear viscosity 1.00000e-01 +Bulk viscosity 1.00000e-01 +Temperature 3.33333e-05 +External body force density 0.00000e+00 0.00000e+00 0.00000e+00 +External E-field amplitude 0.00000e+00 1.00000e-03 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_openmp_reduced (host) +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 + +Advection scheme order: 3 + +Initial charge densities +------------------------ +Initial conditions: Gouy Chapman +Initial condition rho_el: 0.0000000e+00 +Debye length: inf +Debye length (actual): 1.0446052e+01 +Initial condition sigma: 3.1250000e-02 + +Porous Media +------------ +Wall boundary links allocated: 160 +Memory (total, bytes): 2560 + +Arranging initial charge neutrality. + +Initial conditions. + +Scalars - total mean variance min max +[rho] 992.00 1.00000000000 2.2204460e-16 1.00000000000 1.00000000000 +[psi] 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[rho] 1.0000000e+00 1.2982447e-17 3.1250000e-02 +[rho] 1.0000000e+00 0.0000000e+00 1.0080645e-03 +[elc] 1.8041124e-14 -1.0080645e-03 3.1250000e-02 + +Free energy density - timestep total fluid +[fed] 0 -1.2075643565e-02 -7.9634305517e-03 + +Momentum - x y z +[total ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[fluid ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 +[walls ] 0.0000000e+00 0.0000000e+00 0.0000000e+00 + +Starting time step loop. + +Scalars - total mean variance min max +[rho] 992.00 1.00000000000 2.2204460e-16 0.99999998307 1.00000013759 +[psi] -4.4408921e-16 -1.5627146e+00 2.9822130e+00 +[rho] 1.0000000e+00 1.2639475e-17 3.1250000e-02 +[rho] 1.0000000e+00 0.0000000e+00 1.0350169e-03 +[elc] 1.4821477e-14 -1.0350169e-03 3.1250000e-02 + +Free energy density - timestep total fluid +[fed] 10 -1.0574510134e-02 -7.9162489905e-03 + +Momentum - x y z +[total ] 1.7558871e-14 -3.3333335e-07 0.0000000e+00 +[fluid ] 8.8991314e-15 -3.2485317e-07 0.0000000e+00 +[walls ] 8.6597396e-15 -8.4801826e-09 0.0000000e+00 + +Velocity - x y z +[minimum ] -6.8238846e-08 -3.1909581e-10 0.0000000e+00 +[maximum ] 6.8238846e-08 1.1754944e-38 1.1754944e-38 + +Completed cycle 10 + +Timer resolution: 1e-06 second + +Timer statistics + Section: tmin tmax total + Total: 0.249 0.249 0.249 0.248758 (1 call) + Time step loop: 0.023 0.038 0.246 0.024586 (10 calls) + Propagation: 0.001 0.001 0.006 0.000608 (10 calls) + Propagtn (krnl) : 0.001 0.001 0.006 0.000605 (10 calls) + Collision: 0.001 0.001 0.013 0.001261 (10 calls) + Collision (krnl) : 0.001 0.001 0.013 0.001255 (10 calls) + Lattice halos: 0.000 0.000 0.005 0.000135 (40 calls) + -> irecv: 0.000 0.000 0.000 0.000001 (10 calls) + -> pack: 0.000 0.000 0.001 0.000118 (10 calls) + -> isend: 0.000 0.000 0.000 0.000000 (10 calls) + -> waitall: 0.000 0.000 0.000 0.000000 (10 calls) + -> unpack: 0.000 0.000 0.001 0.000110 (10 calls) + phi gradients: 0.000 0.000 0.000 0.000000 (10 calls) + BBL: 0.000 0.000 0.000 0.000027 (10 calls) + Force calculation: 0.000 0.000 0.001 0.000072 (20 calls) + phi update: 0.000 0.000 0.000 0.000000 (10 calls) + Poisson equation: 0.019 0.033 0.204 0.020357 (10 calls) + Nernst Planck: 0.001 0.002 0.015 0.001471 (10 calls) +Diagnostics / output: 0.000 0.001 0.001 0.000081 (10 calls) +End time: Fri Jun 30 17:48:44 2023 +Ludwig finished normally. From 4d38a2def9721227cc4ce3599647cb5443d64ee5 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 30 Jun 2023 20:39:06 +0100 Subject: [PATCH 89/97] Biff! typo --- util/extract.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/extract.c b/util/extract.c index ac2fece1..2a73c8ee 100644 --- a/util/extract.c +++ b/util/extract.c @@ -272,7 +272,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]); - snprintf(filename, BIFSIZ, "%s.%3.3d-%3.3d.meta", stub, nfile, ifile); + snprintf(filename, BUFSIZ, "%s.%3.3d-%3.3d.meta", stub, nfile, ifile); read_meta_data_file(filename, &metadata); } } From 8d82ec356b22c7aa7adea0a5ff809a2ea5fb02ac Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sun, 2 Jul 2023 16:01:27 +0100 Subject: [PATCH 90/97] Remove unused code --- src/leesedwards.c | 35 +++++++++++------------------------ src/leesedwards.h | 5 ++++- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/src/leesedwards.c b/src/leesedwards.c index c13d40e4..0d402fdf 100644 --- a/src/leesedwards.c +++ b/src/leesedwards.c @@ -68,8 +68,6 @@ static int lees_edw_init_tables(lees_edw_t * le); static __constant__ lees_edw_param_t static_param; -__host__ __device__ int lees_edw_buffer_duy(lees_edw_t * le, int ib); - /***************************************************************************** * * lees_edw_create @@ -206,23 +204,6 @@ __host__ int lees_edw_target(lees_edw_t * le, lees_edw_t ** target) { return 0; } -/***************************************************************************** - * - * lees_edw_oscillatory_set - * - *****************************************************************************/ - -int lees_edw_oscillatory_set(lees_edw_t * le, int period) { - - assert(le); - - le->param->type = LE_SHEAR_TYPE_OSCILLATORY; - le->param->period = period; - le->param->omega = 2.0*4.0*atan(1.0)/le->param->period; - - return 0; -} - /***************************************************************************** * * lees_edw_toffset_set @@ -245,10 +226,6 @@ int lees_edw_toffset_set(lees_edw_t * le, int nt0) { * We assume there are a given number of equally-spaced planes * all with the same speed. * - * Pending (KS): - * - dx should be integers, i.e, ntotal[X] % ntotal must be zero - * - look at displacement issue - * *****************************************************************************/ static int lees_edw_init(lees_edw_t * le, const lees_edw_options_t * info) { @@ -278,6 +255,14 @@ static int lees_edw_init(lees_edw_t * le, const lees_edw_options_t * info) { le->param->dx_sep = 1.0*ntotal[X] / le->param->nplanetotal; le->param->dx_min = 0.5*le->param->dx_sep; le->param->time0 = 1.0*le->param->nt0; + + if (le->param->type == LE_SHEAR_TYPE_OSCILLATORY) { + if (info->period <= 0) { + pe_info(le->pe, "Oscillatory shear must have period > 0\n"); + pe_fatal(le->pe, "Please check the input and try again."); + } + le->param->omega = 2.0*4.0*atan(1.0)/le->param->period; + } } lees_edw_checks(le); @@ -609,7 +594,9 @@ 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 == LE_SHEAR_TYPE_OSCILLATORY) *uy *= cos(le->param->omega*tle); + if (le->param->type == LE_SHEAR_TYPE_OSCILLATORY) { + *uy *= cos(le->param->omega*tle); + } return 0; } diff --git a/src/leesedwards.h b/src/leesedwards.h index 03812738..04853eba 100644 --- a/src/leesedwards.h +++ b/src/leesedwards.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 * *****************************************************************************/ @@ -70,4 +71,6 @@ __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]); + +__host__ __device__ int lees_edw_buffer_duy(lees_edw_t * le, int ib); #endif From e6f2edacfb3b46aba0753c4113a76b38974ca0c2 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sun, 2 Jul 2023 16:01:49 +0100 Subject: [PATCH 91/97] Add tests --- tests/unit/test_le.c | 190 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 189 insertions(+), 1 deletion(-) diff --git a/tests/unit/test_le.c b/tests/unit/test_le.c index 2d10fa7e..54ed8a6f 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-2022 The University of Edinburgh + * (c) 2010-2023 The University of Edinburgh * * Contributing authors: * Kevin Stratford (kevin@epcc.ed.ac.uk) @@ -30,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_create(pe_t * pe, cs_t * cs); +int test_lees_edw_buffer_displacement(pe_t * pe, cs_t * cs); +int test_lees_edw_buffer_du(pe_t * pe, cs_t * cs); +int test_lees_edw_buffer_duy(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); @@ -54,6 +59,11 @@ int test_le_suite(void) { physics_create(pe, &phys); + test_lees_edw_create(pe, cs); + test_lees_edw_buffer_displacement(pe, cs); + test_lees_edw_buffer_du(pe, cs); + test_lees_edw_buffer_duy(pe, cs); + test_parallel1(pe, cs); test_le_parallel2(pe, cs); @@ -71,6 +81,184 @@ int test_le_suite(void) { return 0; } +/***************************************************************************** + * + * test_lees_edw_create + * + *****************************************************************************/ + +int test_lees_edw_create(pe_t * pe, cs_t * cs) { + + int ifail = 0; + + /* No planes */ + { + lees_edw_options_t opts = {.nplanes = 0}; + lees_edw_t * le = NULL; + + ifail = lees_edw_create(pe, cs, &opts, &le); + assert(le); + assert(lees_edw_nplane_total(le) == 0); + assert(lees_edw_nplane_local(le) == 0); + lees_edw_free(le); + } + + /* Two planes */ + { + lees_edw_options_t opts = {.nplanes = 2, .type = LE_SHEAR_TYPE_STEADY, + .nt0 = 0, .uy = 0.01}; + lees_edw_t * le = NULL; + ifail = lees_edw_create(pe, cs, &opts, &le); + assert(ifail == 0); + assert(lees_edw_nplane_total(le) == 2); + lees_edw_free(le); + } + + return ifail; +} + +/***************************************************************************** + * + * test_lees_edw_buffer_displacement + * + *****************************************************************************/ + +int test_lees_edw_buffer_displacement(pe_t * pe, cs_t * cs) { + + int ifail = 0; + + /* Steady displacement is uy.t, with a sign dependent on buffer ib */ + { + int ib = 0; + double t = 2.0; + double dy = 0.0; + + lees_edw_options_t opts = {.nplanes = 1, .type = LE_SHEAR_TYPE_STEADY, + .nt0 = 0, .uy = 0.01}; + lees_edw_t * le = NULL; + + ifail = lees_edw_create(pe, cs, &opts, &le); + assert(ifail == 0); + lees_edw_buffer_displacement(le, ib, t, &dy); + if (fabs(dy + t*opts.uy) > DBL_EPSILON) ifail = -1; /* ib = 0 is -ve */ + assert(ifail == 0); + lees_edw_free(le); + } + + /* Oscillatory displacement */ + /* The uy is just set to fix up the displacement at time t = 0.5; + * it's not realistic */ + { + lees_edw_options_t opts = {.nplanes = 1, .type = LE_SHEAR_TYPE_OSCILLATORY, + .nt0 = 0, .period = 2, .uy = 4.0*atan(1.0)}; + lees_edw_t * le = NULL; + int ib = 0; + double t = 0.5; + double dy = 0.0; + + ifail = lees_edw_create(pe, cs, &opts, &le); + assert(ifail == 0); + lees_edw_buffer_displacement(le, ib, t, &dy); + if (fabs(dy - 1.0) > FLT_EPSILON) ifail = -1; + assert(ifail == 0); + lees_edw_free(le); + } + + return ifail; +} +/***************************************************************************** + * + * test_lees_edw_buffer_du + * + *****************************************************************************/ + +int test_lees_edw_buffer_du(pe_t * pe, cs_t * cs) { + + int ifail = 0; + + lees_edw_options_t opts = {.nplanes = 1, .type = LE_SHEAR_TYPE_STEADY, + .nt0 = 0, .uy = 0.01}; + lees_edw_t * le = NULL; + + ifail = lees_edw_create(pe, cs, &opts, &le); + assert(ifail == 0); + + /* This follows duy */ + { + int ib = 0; + int nhalo = -1; + + lees_edw_nhalo(le, &nhalo); + + for (int p = 0; p < lees_edw_nplane_local(le); p++) { + for (int nh = 0; nh < nhalo; nh++) { + int isgn = -1; + double u[3] = {-1.0, -1.0, -1.0}; + lees_edw_buffer_du(le, ib, u); + assert(u[X] == 0.0 && u[Z] == 0.0); + if (fabs(u[Y] - opts.uy*isgn) > DBL_EPSILON) ifail = -1; + assert(ifail == 0); + ib++; + } + for (int nh = 0; nh < nhalo; nh++) { + int isgn = +1; + double u[3] = {-1.0, -1.0, -1.0}; + lees_edw_buffer_du(le, ib, u); + assert(u[X] == 0.0 && u[Z] == 0.0); + if (fabs(u[Y] - opts.uy*isgn) > DBL_EPSILON) ifail = -1; + assert(ifail == 0); + ib++; + } + } + } + + lees_edw_free(le); + + return ifail; +} + +/***************************************************************************** + * + * test_lees_edw_buffer_duy + * + *****************************************************************************/ + +int test_lees_edw_buffer_duy(pe_t * pe, cs_t * cs) { + + int ifail = 0; + + lees_edw_options_t opts = {.nplanes = 2, .type = LE_SHEAR_TYPE_STEADY, + .nt0 = 0, .uy = 0.01}; + lees_edw_t * le = NULL; + + ifail = lees_edw_create(pe, cs, &opts, &le); + assert(ifail == 0); + + /* Check pattern using alternative initialisation code */ + + { + int nhalo = -1; + int ib = 0; /* Increments by +1 moving along the buffer */ + + lees_edw_nhalo(le, &nhalo); + + for (int p = 0; p < lees_edw_nplane_local(le); p++) { + for (int nh = 0; nh < nhalo; nh++) { + assert(lees_edw_buffer_duy(le, ib) == -1); + ib++; + } + for (int nh = 0; nh < nhalo; nh++) { + assert(lees_edw_buffer_duy(le, ib) == +1); + ib++; + } + } + } + + lees_edw_free(le); + + return ifail; +} + /***************************************************************************** * * test_parallel1 From 4b07f180fbfa8a80cb78f17e10a3b85b626d30be Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sun, 2 Jul 2023 16:12:13 +0100 Subject: [PATCH 92/97] Initialiser list out of order --- tests/unit/test_le.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_le.c b/tests/unit/test_le.c index 54ed8a6f..2d35455b 100644 --- a/tests/unit/test_le.c +++ b/tests/unit/test_le.c @@ -150,7 +150,7 @@ int test_lees_edw_buffer_displacement(pe_t * pe, cs_t * cs) { * it's not realistic */ { lees_edw_options_t opts = {.nplanes = 1, .type = LE_SHEAR_TYPE_OSCILLATORY, - .nt0 = 0, .period = 2, .uy = 4.0*atan(1.0)}; + .period = 2, .nt0 = 0, .uy = 4.0*atan(1.0)}; lees_edw_t * le = NULL; int ib = 0; double t = 0.5; From f90dc3eef138f681683747308455a55b49f424d0 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sun, 2 Jul 2023 17:11:46 +0100 Subject: [PATCH 93/97] Remove unused code --- src/leesedwards.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/leesedwards.c b/src/leesedwards.c index 0d402fdf..f7b7d1a2 100644 --- a/src/leesedwards.c +++ b/src/leesedwards.c @@ -204,21 +204,6 @@ __host__ int lees_edw_target(lees_edw_t * le, lees_edw_t ** target) { return 0; } -/***************************************************************************** - * - * lees_edw_toffset_set - * - *****************************************************************************/ - -int lees_edw_toffset_set(lees_edw_t * le, int nt0) { - - assert(le); - - le->param->nt0 = nt0; - - return 0; -} - /***************************************************************************** * * lees_edw_init From dafb380a05f0da56cddfe7774e38859b84c1972a Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Sun, 2 Jul 2023 17:12:35 +0100 Subject: [PATCH 94/97] Add plane uy now test --- tests/unit/test_le.c | 46 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/unit/test_le.c b/tests/unit/test_le.c index 2d35455b..0ecd3694 100644 --- a/tests/unit/test_le.c +++ b/tests/unit/test_le.c @@ -34,6 +34,7 @@ int test_lees_edw_create(pe_t * pe, cs_t * cs); int test_lees_edw_buffer_displacement(pe_t * pe, cs_t * cs); int test_lees_edw_buffer_du(pe_t * pe, cs_t * cs); int test_lees_edw_buffer_duy(pe_t * pe, cs_t * cs); +int test_lees_edw_plane_uy_now(pe_t * pe, cs_t * cs); int test_lees_edw_type_to_string(void); int test_lees_edw_type_from_string(void); @@ -63,6 +64,7 @@ int test_le_suite(void) { test_lees_edw_buffer_displacement(pe, cs); test_lees_edw_buffer_du(pe, cs); test_lees_edw_buffer_duy(pe, cs); + test_lees_edw_plane_uy_now(pe, cs); test_parallel1(pe, cs); test_le_parallel2(pe, cs); @@ -259,6 +261,50 @@ int test_lees_edw_buffer_duy(pe_t * pe, cs_t * cs) { return ifail; } +/***************************************************************************** + * + * test_lees_edw_plane_uy_now + * + *****************************************************************************/ + +int test_lees_edw_plane_uy_now(pe_t * pe, cs_t * cs) { + + int ifail = 0; + + /* Steady shear */ + { + lees_edw_options_t opts = {.nplanes = 1, .type = LE_SHEAR_TYPE_STEADY, + .nt0 = 0, .uy = 0.01}; + lees_edw_t * le = NULL; + double t = 0.5; /* Steady result independent of t */ + double uy = 0.0; + + ifail = lees_edw_create(pe, cs, &opts, &le); + lees_edw_plane_uy_now(le, t, &uy); + if (fabs(uy - opts.uy) > DBL_EPSILON) ifail = -1; + assert(ifail == 0); + lees_edw_free(le); + } + + /* Oscillatory shear */ + /* Parameters are to get uy cos(omega.t) = 1 ... */ + { + lees_edw_options_t opts = {.nplanes = 1, LE_SHEAR_TYPE_OSCILLATORY, + .period = 3, .nt0 = 100, .uy = -2.0}; + lees_edw_t * le = NULL; + double t = 1.0 + 1.0*opts.nt0; + double uy = 0.0; + + ifail = lees_edw_create(pe, cs, &opts, &le); + lees_edw_plane_uy_now(le, t, &uy); + if (fabs(uy - 1.0) > FLT_EPSILON) ifail = -1; + assert(ifail == 0); + lees_edw_free(le); + } + + return ifail; +} + /***************************************************************************** * * test_parallel1 From bc7e31e3bc2afb731e22cc060478b9f775cea52b Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 6 Jul 2023 17:42:44 +0100 Subject: [PATCH 95/97] Move updated electrokinetic material to html --- docs/electrokinetic.tex | 174 ----------------------------------- docs/pics/test_eo.pdf | Bin 14003 -> 0 bytes docs/pics/test_lj_decay1.pdf | Bin 42005 -> 0 bytes docs/pics/test_lj_zoom1.pdf | Bin 14351 -> 0 bytes docs/pics/test_lj_zoom3.pdf | Bin 140645 -> 0 bytes 5 files changed, 174 deletions(-) delete mode 100644 docs/pics/test_eo.pdf delete mode 100644 docs/pics/test_lj_decay1.pdf delete mode 100644 docs/pics/test_lj_zoom1.pdf delete mode 100644 docs/pics/test_lj_zoom3.pdf diff --git a/docs/electrokinetic.tex b/docs/electrokinetic.tex index bf5b41e9..42bb0f1d 100644 --- a/docs/electrokinetic.tex +++ b/docs/electrokinetic.tex @@ -273,180 +273,6 @@ \subsubsection{Gouy-Chapman} Debye length $l_D=3.514$, the surface potential $\Psi_D=2.267\e{-4}$ and the centre potential $\Psi_c=-3.395\e{-5}$. -\subsubsection{Liquid-junction potential} - -The liquid junction potential -is a charge separation process that -occurs when electrolytes with slightly different concentrations -whose species have different diffusivities are brought into contact. -Charges from the regions of higher concentration diffuse -into the parts with lower concentration. Due to the difference -in diffusivity they migrate at different speeds, leaving parts of -the system charged. This leads to a build-up of a potential -which balances the diffusive flux. - -After the initial build-up phase the potential decreases slowly -again until the charge concentration has become homogeneous throughout -the system. Both timescales of emergence and decay of the potential -can be separated by chosing a sufficiently large system size. - -This problem allowed us to verify the correct temporal -behaviour of the Nernst-Planck equation solver by resolving the transient -dynamics without having to account for advective terms. - -\begin{figure}[h!t] -\includegraphics[width=0.495\textwidth]{./pics/test_lj_zoom1.pdf} -\includegraphics[width=0.495\textwidth]{./pics/test_lj_zoom3.pdf} -\caption{Time evolution of the liquid junction potential for $L_x=128$ (blue), $L_x=256$ (green) and $L_x=512$ (blue). The dashed black curves represent the approximate solution in the limit $l_D/L_x<<1$. Deviations can be seen which are presumably due -to the approximate nature of the analytical solution and the fact that we -have a different asymptotic flux close to the boundary - the theoretical -analysis assumes no flux at a boundary which is infinitely far away from -the diffusive region.} -\label{fig3} -\end{figure} - -For simplicity we considered systems of size -$L_x\times L_y\times Lz=128\times4\times4$ and -$L_x\times L_y\times Lz=256\times4\times4$ with -periodic boundary conditions at either end. -The two halfs were electroneutral and had ionic concentrations -$\rho_{L,\pm}=\rho_{0,\pm} + \delta\rho$ and -$\rho_{R,\pm}=\rho_{0,\pm} - \delta\rho$ -with $\rho_{0,\pm}=1\e{-2}$ and $\delta\rho = 0.01$. - -The potential difference between both sides during the build-up -obeys approximately - -\begin{equation} -\Delta\Psi(t)\simeq\Delta\Psi_P \left\{1-\exp\left(-\frac{t}{\tau_e}\right)\right\}\\ -\end{equation} - -with - -\begin{equation} -\Delta\Psi_P=\frac{(D_+ D_-)}{\beta e (D_+ + D_-)} \frac{\delta\rho}{\rho_0}\\ -\end{equation} - -as saturation value of the potential difference. -The saturation time scale is given by - -\begin{equation} -\tau_e=\frac{\varepsilon}{\beta \, e^2 (D_+ + D_-) \rho_0}. -\end{equation} - -\begin{figure}[htpb] -\includegraphics[width=0.9\textwidth]{./pics/test_lj_decay1.pdf} -\caption{Rescaled plot of the decay: Times have been Time evolution of the liquid junction potential for $L_x=128$ (blue), $L_x=256$ (green) and $L_x=512$ (blue). The dashed black curves represent the approximate solution in the limit $l_D/L_x<<1$. Deviations can be seen which are due -to the approximate nature of the analytical solution and the diffusive fluxes close to the boundary.} -\label{fig4} -\end{figure} - -A more exact solution can be derived in the limit $l_D/L_x<<1, N\to\infty$: - -\begin{eqnarray} -\Delta\Psi(t)&=&\Delta\Psi_P \left\{1-\exp\left(-\frac{t}{\tau_e}\right)\right\}\frac{4}{\pi}\left\{\sum_{m=1}^N \frac{\sin^3(m\pi/2)}{m} \exp\left(-\frac{ m^2\, t}{\tau_d}\right)\right\}\\ -\tau_d&=&\frac{L^2}{2\pi^2 (D_+ + D_-)}.\label{taud} -\end{eqnarray} - -It contains as well the dependence on the decay timescale $\tau_d$. -Only odd indices $m$ contribute to the sum: - -\begin{eqnarray} -\sum_{m=1}^N \frac{\sin^3(m\pi/2)}{m} \exp\left(-\frac{m^2\, t}{\tau_d}\right)&=&\nonumber\\ -&&\hspace*{-4cm} \exp\left(-\frac{t}{\tau_d}\right)-\frac{1}{3} \exp\left(-\frac{9 t}{\tau_d}\right)+\frac{1}{5}\exp\left(-\frac{25t}{\tau_d}\right)-\frac{1}{9}\exp\left(-\frac{81 t}{\tau_d}\right)+\ldots -\end{eqnarray} - -A complete discussion of the solution can be found in \cite{Mafe}. -There, the upper limit of significant modes has been also estimated as $N_{max} = L/\pi l_D$. -Note the factor 2 difference between Eq. \ref{taud} and the corresponding expression in \cite{Mafe}. - -The following parameters were used: -dielectric permittivity $\epsilon=3.3\e{3}$, temperature $\beta^{-1}=3.333\e{-5}$, unit charge $e=1$, valency $z_\pm=\pm1$, diffusivities $D_+=0.0125$ and $D_-=0.0075$. -We obtained -$\Delta\Psi_P=1.6667\e{-7}$, $\tau_e=550$, $\tau_d=41501.2\, (L_x=128)$, $\tau_d=166004.6\, (L_x=256)$ and $\tau_d=664018.5 (L_x=512)$. -The results for the potential difference over time are shown in Fig. \ref{fig3}. - -Fig. \ref{fig4} shows results with times rescaled to the decay -time scale $\tau_d$ (cf. Eq. \ref{taud}). Obviously the -deviations we observe are not due to the limited system size -and have a more systematic origin. - -The curves coincide if -the theoretic limit for $\tau_d$ is rescaled by a factor $1.067$, -suggesting the effective system length for this sort of setup is -actually about 3\% larger than the numerical value. - -A reason for this might be the approximate nature of the analytical solution -and the fact that it was gained -for an infinitely large system with constant charge concentrations, -vanishing currents at both ends and finite diffusive zone of size $L_x$. -In our situation the entire system is within the diffusive zone. -This may lead to smaller effective diffusivities or larger effective -system sizes. -Interestingly, all runs with solid walls at both ends resulted in -oscillatory behaviour and an effective system size of $2L_x$. - - -\subsubsection{Electroosmotic Flow} - -To test the implementation with all couplings to external and -internal forces we consider a forced charged fluid in a slit -of size $L_z$. An electrostatic field $E_{||}$ is allied -parallel to the walls. The entire system is electroneutral with -each wall having the surface charge density $\sigma$ -and compensationg counterions with total charge $2 \sigma A_{wall}$ -in the fluid. - -In equilibrium the charge density at a distance $x$ from the wall obeys - -\begin{equation} -\rho(x)=\frac{\rho_0}{\cos^2(K\,x)} -\end{equation} - -with - -\begin{equation} -\rho_0=K^2/2\pi l_B -\end{equation} - -and - -\begin{eqnarray} -K \,L_x \tan\left(\frac{K\, L_x}{2}\right)&=&\pi\, l_B\, L_x\, 2\sigma\label{kex} \\ -K \,L_x&\simeq&\sqrt{4\pi \,l_B\,L_x\,2\sigma}\label{klin}. -\end{eqnarray} - - -The liniarised version Eq. \ref{klin} has only a limited range of applicabilty. -We solved Eq. \ref{kex} numerically and found solutions -$K=0.01959\; (\sigma=0.003125)$ and $K=0.03311\; (\sigma=0.00125)$, -which is reasonably far away from the -theoretical limit $K_{max}=\pi/L_x$ set by the tangent. - -Note the factor 2 difference on the lhs of Eq. \ref{kex} with respect -to \cite{Capuani, Rotenberg}. There is also a factor $L_x$ missing on -the rhs of Eq. \ref{klin}. - -The steady state velocity of the fluid can be derived from the -force balance of the gradient of the stresses and the electrostatic -forces: - -\begin{eqnarray} -v_y(x)&=&\hat{v} \ln\left(\frac{\cos(K\,x)}{\cos(K\,L_x/2)}\right)\label{vy}\\ -\hat{v}&=&\frac{e \,E_{||}\rho_o}{\eta\, K^2}=\frac{e \,E_{||}}{2\pi\eta l_B}\label{vhat} -\end{eqnarray} - -The result for two different charge densities is shown in Fig. \ref{fig6}. -The accuracy is acceptable with deviations for high surface -charged potentially being caused by the chosen discretisation or -by the numerical solution of Eq. \ref{kex} approaching the limit -of $\pi/L_z\simeq0.049$. - -\begin{figure}[htpb] -\includegraphics[width=0.9\textwidth]{./pics/test_eo.pdf} -\caption{Steady state flow profile across a slit of width $L_x=63$ for an applied field of magnitude $e \beta E L_x=1.89$ for two different charge densities $\sigma=0.003125$ (red) and $\sigma=0.0125$ (blue), Bjerrum length $l_B=0.7234$, viscosity $\eta=0.1$ and unit charge $e=1$. The dashed black lines correspond to theoretical prediction according to Eqs. \ref{vy} and \ref{vhat}.} -\label{fig6} -\end{figure} \subsubsection{Debye-H\"uckel Theory} diff --git a/docs/pics/test_eo.pdf b/docs/pics/test_eo.pdf deleted file mode 100644 index e12d0f0af91a9b82cb2639efe87c54009a6eb9f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14003 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 BE71S| diff --git a/docs/pics/test_lj_decay1.pdf b/docs/pics/test_lj_decay1.pdf deleted file mode 100644 index cc2cdd8597ed66b71c36b4d81c8a3b2285e6f144..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42005 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 K*Jah@K>r6#zHTu9 diff --git a/docs/pics/test_lj_zoom3.pdf b/docs/pics/test_lj_zoom3.pdf deleted file mode 100644 index 21f90a49a2dec4dcdae8c9c041ce7f6511c6bc14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 140645 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%0Aa%qyGH12E0g)!CbD%}Z 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é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 z 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

QAROc{T?LJV$&=Vw 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 From 7620be0529a452ec78b362c82dd998da79767056 Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Thu, 6 Jul 2023 17:52:45 +0100 Subject: [PATCH 96/97] Remove material superceded by html --- docs/input.tex | 781 +------------------------------------------------ docs/intro.tex | 194 +----------- 2 files changed, 5 insertions(+), 970 deletions(-) diff --git a/docs/input.tex b/docs/input.tex index 49a91313..17d193d2 100644 --- a/docs/input.tex +++ b/docs/input.tex @@ -13,785 +13,8 @@ \section{The Input File} -\subsection{General} - -By default, the run time expects to find user input in a file -\texttt{input} in the current working directory. If a different -file name is required, its name -should be provided as the sole command line argument, e.g., -\begin{lstlisting} -./Ludwig.exe input_file_name -\end{lstlisting} -If the input file is not located in the current working directory -the code will terminate immediately with an error message. - -When an input file is located, its content is read by a single MPI -task, and its contents then broadcast to all MPI relevant tasks. -The format of the file is plain ASCII text, and its contents are -parsed on a line by line basis. Lines may contain the following: -\begin{itemize} -\item comments introduced by \texttt{\#}. -\item key value pairs separated by white space. -\end{itemize} -Blank lines are treated as comments. The behaviour of the code is -determined by a set of key value pairs. Any given key may appear -only once in the input file; unused key value pairs are not reported. -If the key value pairs are not correctly formed, the code will terminate -with an error message and indicate the offending input line. - -Key value pairs may be present in the input file, but have no effect for -any given run: they are merely ignored. Relevant control parameters for -given input are reported in the standard output. - -\subsubsection{Key value pairs} - -Key value pairs are made up of a key --- an alphanumeric string with no -white space --- and corresponding value following white space. Values -may take on the follow forms: -\begin{lstlisting} -key_string value_string - -key_integer_scalar 1 -key_integer_vector 1_2_3 - -key_double_scalar 1.0 -key_double_vector 1.0_2.0_3.0 -\end{lstlisting} -Values which are strings should contain no white space. Scalar parameters -may be integer values, or floating point values with a decimal point -(scientific notation is also allowed). Vector parameters are introduced -by a set of three values (to be interpreted as $x,y,z$ components of the -vector in Cartesian coordinates) separated by an underscore. The identity -of the key will specify what type of value is expected. Keys and (string) -values are case sensitive. - - -Most keys have an associated default value which will be used if -the key is not present. Some keys must be specified: an error will -occur if they are missing. The remainder of this part -of the guide details the various choices for key value pairs, -along with any default values, and any relevant constraints. - -\subsection{The Free Energy} -\label{input-free-energy} - -The choice of free energy is determined as follows: -\begin{lstlisting} -free_energy none -\end{lstlisting} -The default value is \texttt{none}, i.e., a simple Newtonian fluid is used. -Possible values of the \texttt{free\_energy} key are: -\begin{lstlisting} -# none Newtonian fluid [DEFAULT] -# symmetric Symmetric binary fluid (finite difference) -# symmetric_lb Symmetric binary fluid (two distributions) -# brazovskii Brazovskii smectics -# surfactant Surfactants -# polar_active Polar active gels -# lc_blue_phase Liquid crystal (nematics, cholesterics, BPs) -# lc_droplet Liquid crystal emulsions -# fe_electro Single fluid electrokinetics -# fe_electro_symmetric Binary fluid electrokinetics -\end{lstlisting} - -The choice of free energy will control automatically a number of factors -related to choice of order parameter, the degree of parallel communication -required, and so on. Each free energy has a number of associated parameters -discussed in the following sections. - -Details of general (Newtonian) fluid parameters, such as viscosity, -are discussed in Section~\ref{input-fluid-parameters}. - -\subsubsection{Symmetric Binary Fluids} - -We recall that the free energy density is, as a function of compositional -order $\phi$: -\[ -{\textstyle \frac{1}{2}} A\phi^2 + -{\textstyle \frac{1}{4}} B\phi^4 + -{\textstyle \frac{1}{2}} \kappa (\mathbf{\nabla}\phi)^2. -\] - -Parameters are introduced by (with default values): -\begin{lstlisting} -free_energy symmetric -A -0.0625 # Default: -0.003125 -B +0.0625 # Default: +0.003125 -K +0.04 # Default: +0.002 -\end{lstlisting} -Common usage has $A < 0$ and $B = -A$ so that $\phi^\star = \pm 1$. -The parameter $\kappa$ -(key \texttt{K}) controls -the interfacial energy penalty and is usually positive. - -\subsubsection{Brazovskii smectics} -\label{input-brazovskki-smectics} -The free energy density is: -\[ -{\textstyle \frac{1}{2}} A\phi^2 + -{\textstyle \frac{1}{4}} B\phi^4 + -{\textstyle \frac{1}{2}} \kappa (\mathbf{\nabla}\phi)^2 + -{\textstyle \frac{1}{2}} C (\nabla^2 \phi)^2 -\] - -Parameters are introduced via the keys: -\begin{lstlisting} -free_energy brazovskii -A -0.0005 # Default: 0.0 -B +0.0005 # Default: 0.0 -K -0.0006 # Default: 0.0 -C +0.00076 # Default: 0.0 -\end{lstlisting} -For $A<0$, phase separation occurs with a result depending on $\kappa$: -one gets two symmetric phases for $\kappa >0$ (cf.\ the symmetric case) -or a lamellar phase for $\kappa < 0$. Typically, $B = -A$ and the -parameter in the highest derivative $C > 0$. - -\subsubsection{Surfactants} -\label{input-surfactants} - -The surfactant free energy should not be used at the present time. - -\subsubsection{Polar active gels} -\label{input-polar-active-gels} - -The free energy density is a function of vector order parameter $P_\alpha$: -\[ -{\textstyle \frac{1}{2}} A P_\alpha P_\alpha + -{\textstyle \frac{1}{4}} B (P_\alpha P_\alpha)^2 + -{\textstyle \frac{1}{2}} \kappa (\partial_\alpha P_\beta)^2 -\] - -There are no default parameters: -\begin{lstlisting} -free_energy polar_active -polar_active_a -0.1 # Default: 0.0 -polar_active_b +0.1 # Default: 0.0 -polar_active_k 0.01 # Default: 0.0 -\end{lstlisting} -It is usual to choose $B > 0$, in which case $A > 0$ gives -an isotropic phase, whereas $A < 0$ gives a polar nematic phase. -The elastic constant $\kappa$ (key \texttt{polar\_active\_k}) -is positive. - -\subsubsection{Liquid crystal} -\label{input-liquid-crystal} -The free energy density is a function of tensor order parameter -$Q_{\alpha\beta}$: -\begin{eqnarray} -{\textstyle\frac{1}{2}} A_0 (1 - \gamma/3)Q^2_{\alpha\beta} - -{\textstyle\frac{1}{3}} A_0 \gamma - Q_{\alpha\beta}Q_{\beta\delta}Q_{\delta\alpha} + -{\textstyle\frac{1}{4}} A_0 \gamma (Q^2_{\alpha\beta})^2 -\nonumber \\ -+ {\textstyle\frac{1}{2}} \Big( -\kappa_0 (\epsilon_{\alpha\delta\sigma} \partial_\delta Q_{\sigma\beta} + -2q_0 Q_{\alpha\beta})^2 + \kappa_1(\partial_\alpha Q_{\alpha\beta})^2 \Big) -\nonumber -\end{eqnarray} - -The corresponding \texttt{free\_energy} value, despite its name, is -suitable for nematics and cholesterics, and not just blue phases: -\begin{lstlisting} -free_energy lc_blue_phase -lc_a0 0.01 # Deafult: 0.0 -lc_gamma 3.0 # Default: 0.0 -lc_q0 0.19635 # Default: 0.0 -lc_kappa0 0.00648456 # Default: 0.0 -lc_kappa1 0.00648456 # Default: 0.0 -\end{lstlisting} -The bulk free energy parameter $A_0$ is positive and controls the -energy scale (key \texttt{lc\_a0}); $\gamma$ is positive and -influences the position in the phase diagram relative to the -isotropic/nematic transition (key \texttt{lc\_gamma}). -The two elastic constants must be equal, i.e., we enforce the -single elastic constant approximation (both keys \texttt{lc\_kappa0} and -\texttt{lc\_kappa1} must be specified). - -Other important parameters in the liquid crystal picture are: -\begin{lstlisting} -lc_xi 0.7 # Default: 0.0 -lc_Gamma 0.5 # Default: 0.0 -lc_active_zeta 0.0 # Default: 0.0 -\end{lstlisting} -The first is $\xi$ (key \texttt{lc\_xi}) is the effective molecular -aspect ratio and should be in the range $0< \xi< 1$. The rotational -diffusion constant is $\Gamma$ (key \texttt{lc\_Gamma}; not to be -confused with \texttt{lc\_gamma}). The (optional) apolar activity -parameter is $\zeta$ (key \texttt{lc\_active\_zeta}). - -\subsubsection{Liquid crystal anchoring} - -Different types of anchoring are available at solid surfaces, with -one or two related free energy parameters depending on the type. -The type of anchoring may be set independently for stationary -boundaries (walls) and colloids. -\begin{lstlisting} -lc_anchoring_strength 0.01 # free energy parameter w1 -lc_anchoring_strength_2 0.0 # free energy parameter w2 -lc_wall_anchoring normal # ``normal'' or ``planar'' -lc_coll_anchoring normal # ``normal'' or ``planar'' -\end{lstlisting} -See section \ref{section-lc-anchoring} for details of surface anchoring. - -\subsubsection{Liquid crystal emulsion} -\label{input-liquid-crystal-emulsion} - -This an interaction free energy which combines the symmetric and liquid -crystal free energies. The liquid crystal free energy constant $\gamma$ -becomes a -function of composition via $\gamma(\phi) = \gamma_0 + \delta(1 + \phi)$, -and a coupling term is added to the free energy density: -\[ -WQ_{\alpha\beta} \partial_\alpha \phi \partial_\beta \phi. -\] -Typically, we might choose $\gamma_0$ and $\delta$ so that -$\gamma(-\phi^\star) < 2.7$ and the $-\phi^\star$ phase is isotropic, -while $\gamma(+\phi^\star) > 2.7$ and the -$+\phi^\star$ phase is ordered (nematic, cholesteric, or blue phase). -Experience suggests that a suitable choice is $\gamma_0 = 2.5$ and -$\delta = 0.25$. - -For anchoring constant $W > 0$, the liquid crystal anchoring at the -interface is planar, while for $W < 0$ the anchoring is normal. This -is set via key \texttt{lc\_droplet\_W}. - -Relevant keys (with default values) are: -\begin{lstlisting} -free_energy lc_droplet - -A -0.0625 -B +0.0625 -K +0.053 - -lc_a0 0.1 -lc_q0 0.19635 -lc_kappa0 0.007 -lc_kappa1 0.007 - -lc_droplet_gamma 2.586 # Default: 0.0 -lc_droplet_delta 0.25 # Default: 0.0 -lc_droplet_W -0.05 # Default: 0.0 -\end{lstlisting} -Note that key \texttt{lc\_gamma} is not set in this case. - -\subsection{System Parameters} -\label{input-system-parameters} - -Basic parameters controlling the number of time steps -and the system size are: -\begin{lstlisting} -N_start 0 # Default: 0 -N_cycles 100 # Default: 0 -size 128_128_1 # Default: 64_64_64 -\end{lstlisting} -A typical simulation will start from time zero (key \texttt{N\_start}) -and run for a certain number of time steps (key \texttt{N\_cycles}). -The system size (key \texttt{size}) specifies the total number of -lattice sites in each dimension. If a two-dimensional system is -required, the extent in the $z$-direction must be set to unity, as -in the above example. - -If a restart from a previous run is required, the choice of parameters -may be as follows: -\begin{lstlisting} -N_start 100 -N_cycles 400 -\end{lstlisting} -This will restart from data previously saved at time step 100, and -run a further 400 cycles, i.e., to time step 500. -% TODO Cross-reference to I/O. - -\subsubsection{Parallel decomposition} - -In parallel, the domain decomposition is closely related to the -system size, and is specified as follows: -\begin{lstlisting} -size 64_64_64 -grid 4_2_1 -\end{lstlisting} -The \texttt{grid} key specifies the number of MPI tasks required in -each coordinate direction. In the above example, the decomposition -is into 4 in the $x$-direction, into 2 in the $y$-direction, while -the $z$-direction is not decomposed. In this example, the local domain -size per MPI -task would then be $16\times32\times64$. The total number of MPI tasks -available must match the total implied by \texttt{grid} (8 in the -example). - -The \texttt{grid} specifications must exactly divide the system size; -if no decomposition is possible, the code will terminate with an error -message. -If the requested decomposition is not valid, or \texttt{grid} is -omitted, the code will try to supply a decomposition based on -the number of MPI tasks available and \texttt{MPI\_Dims\_create()}; -this may be implementation dependent. - - -\subsection{Fluid Parameters} -\label{input-fluid-parameters} - -Control parameters for a Newtonian fluid include: -\begin{lstlisting} -fluid_rho0 1.0 -viscosity 0.166666666666666 -viscosity_bulk 0.166666666666666 -isothermal_fluctuations off -temperature 0.0 -\end{lstlisting} -The mean fluid density is $\rho_0$ (key \texttt{fluid\_rho0}) which -defaults to unity in lattice units; it is not usually necessary to -change this. The shear viscosity is -\texttt{viscosity} and as default value 1/6 to correspond to -unit relaxation time in the lattice Boltzmann picture. Reasonable -values of the shear viscosity are $0.2 > \eta > 0.0001$ in lattice -units. Higher values move further into the over-relaxation region, and can -result in poor behaviour. Lower -values increase the Reynolds number and tend to cause -problems with stability. The bulk -viscosity has a default value which is equal to whatever shear -viscosity has been selected. Higher values of the bulk viscosity -may be set independently and can help to suppress large deviations -from incompressibility and maintain numerical stability -in certain situations. - -If fluctuating hydrodynamics is wanted, set the value of - \texttt{isothermal\_fluctuations} to \texttt{on}. The associated -temperature is in lattice units: reasonable values (at $\rho_0 = 1$) -are $0 < kT < 0.0001$. If the temperature is too high, local -velocities will rapidly exceed the Mach number constraint and -the simulation will be unstable. - -\subsection{Lees Edwards Planes} - -Constant uniform shear may be introduced via a number of Lees Edwards -planes with given speed. This is appropriate for fluid-only -systems with periodic boundaries. -\begin{lstlisting} -N_LE_plane 2 # Number of planes (default: 0) -LE_plane_vel 0.05 # Constant plane speed -\end{lstlisting} -The placing of the planes in the system is as follows. -The number of planes $N$ must -divide evenly the lattice size in the $x$-direction to give an integer -$\delta x$. Planes are then placed at $\delta x / 2, 3\delta x/2, \ldots$. -All planes have the same, constant, velocity jump associated with them: -this is positive in the positive $x$-direction. (This jump is usually -referred to as the plane speed.) The uniform shear rate -will be $\dot{\gamma} = N U_{LE} / L_x$ where $U_{LE}$ is the plane -speed (which is always in the $y$-direction). - -The velocity gradient or shear direction is $x$, the flow -direction is $y$ and the vorticity direction is $z$. - -The spacing between planes must not be less than twice the halo size -in lattice units; 8--16 lattice units may be the practical limit in -many cases. In additional, the speed of the planes must not cause a -violation of the -Mach number constraint in the fluid velocity on the lattice, which -will match the plane speed in magnitude directly either side of the -planes. A value of around 0.05 should be regarded as a maximum safe -limit for practical purposes. - -Additional keys associated with the Lees Edwards planes are: -\begin{lstlisting} -LE_init_profile 1 # Initialise u(x) (off/on) -LE_time_offset 10000 # Offset time (default 0) -\end{lstlisting} -If \texttt{LE\_init\_profile} is set, the fluid velocity is initialised -to reflect a steady state shear flow appropriate for the number of -planes at the given speed (ie., shear rate). If set to zero (the default), -the fluid is initialised with zero velocity. - -The code works out the current displacement of the planes by computing -$U_{LE} t$, where $t$ is the current time step. A shear run should then -start from $t = 0$, i.e. zero plane displacement. -It is often convenient to run an equilibration with no shear, and -then to start an experiment after some number of steps. This -key allows you to offset the start of the Lees-Edwards motion. -It should then take the value of the start time (in time steps) -corresponding to the restart at the end of the equilibration -period. - -There are a couple of additional constraints to use the Lees-Edwards -planes in parallel. In particular, the planes cannot fall at a -processor boundary in the $x$-direction. This means you should -arrange an integer number of planes per process in the $x$-direction. -(For example, use one plane per process; this will also ensure the number -of planes -still evenly divides the total system size.) -This will interleave the planes with the processor decomposition. -The $y$-direction and $z$-direction may be decomposed without -further constraint. - -Note that this means a simulation with one plane will only work -if there is one process in the $x$ decomposition. - - - -\subsection{Colloids} -If no relevant key words are present, no colloids will be -expected at run time. The simulation will progress in the -usual fashion with fluid only. - -If colloids are required, the \texttt{colloid\_init} -key word must be present to allow the code to determine where -colloid information will come from. The options for the -\texttt{colloid\_init} key word are summarised below: -\begin{lstlisting} -colloid_init none - -# none no colloids [DEFAULT] -# input_one one colloid from input file -# input_two two colloids from input file -# input_three three colloids from input file -# input_random Small number at random -# from_file Read a separate file (including all restarts) -\end{lstlisting} - -For idealised simulations which require 1, 2, or 3 colloids, relevant -initial state information -for each particle can be included in the input file. In principle, most -of the colloid state as defined in the colloid -state structure in \texttt{colloid.h} may be specified. (Some state is -reserved for internal use only and cannot be set from the input file.) -Furthermore, not all the state is relevant in all simulations --- -quantities such as charge and wetting parameters may not be required, -in which case they a simply ignored. - -A minimal initialisation is shown in the following example: -\begin{lstlisting} -colloid_init input_one - -colloid_one_a0 1.25 -colloid_one_ah 1.25 -colloid_one_r 12.0_12.0_12.0 -\end{lstlisting} -This initialises a single colloid with an input radius $a_0=1.25$, -and a hydrodynamic radius $a_h=1.25$; in general both are required, -but they do not have to be equal. -A valid position is required within the extent of the system -$0.5 < x,y,z < L + 0.5$ as specified by the \texttt{size} key word. -State which is not explicitly defined is initialised to zero. - -\subsubsection{General Initialisation} - -A full list of colloid state-related key words is as follows. All -the quantities are floating point numbers unless explicitly stated -to be otherwise: -\begin{lstlisting} -# colloid_one_nbonds (integer) number of bonds -# colloid_one_bond1 (integer) index of bond partner 1 -# colloid_one_bond2 (integer) index of bond partner 2 -# colloid_one_isfixedr colloid has fixed position (integer 0 or 1) -# colloid_one_isfixedv colloid has fixed velocity (integer 0 or 1) -# colloid_one_isfixedw colloid has fixed angular velocity (0 or 1) -# colloid_one_isfixeds colloid has fixed spin (0 or 1) -# colloid_one_type ``default'' COLLOID_TYPE_DEFAULT -# ``active'' COLLOID_TYPE_ACTIVE -# ``subgrid'' COLLOID_TYPE_SUBGRID -# colloid_one_a0 input radius -# colloid_one_ah hydrodynamic radius -# colloid_one_r position vector -# colloid_one_v velocity (vector) -# colloid_one_w angular velocity (vector) -# colloid_one_s spin (unit vector) -# colloid_one_m direction of motion (unit) vector for swimmers -\end{lstlisting} - -Note that for magnetic particles, the appropriate initialisation involves -the spin key word \texttt{colloid\_one\_s} which relates to the dipole -moment $\mu \mathbf{s}_i$, while \texttt{colloid\_one\_m} relates to the -direction of motion vector. Do not confuse the two. -It is possible in principle to have magnetic active particles, -in which case the dipole direction or spin ($\mathbf{s}_i$) and the -direction of swimming motion $\mathbf{m}_i$ are allowed to be distinct. - -\begin{lstlisting} -# colloid_one_b1 Squirmer parameter B_1 -# colloid_one_b2 Squirmer parameter B_2 -# colloid_one_rng (integer) random number generator state -# colloid_one_q0 charge (charge species 0) -# colloid_one_q1 charge (charge species 1) -# colloid_one_epsilon Permeativity -# colloid_one_c Wetting parameter C -# colloid_one_h Wetting parameter H -\end{lstlisting} - -{\bf Example}: Single active (squirmer) particle - -The following example shows a single active particle with initial -swimming direction along the $x$-axis. -\begin{lstlisting} -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 -\end{lstlisting} - - - -\subsubsection{Random initialisation} - -For suspensions with more than few colloids, but still at -relatively low volume fraction (10--20\% by volume), it is -possible to request initialisation at random positions. - -The additional key word value pair \texttt{colloid\_random\_no} -determines the total number of particles to be placed in -the system. To prevent particles being initialised very -close together, which can cause problems in the first few -time steps if strong potential interactions are present, -a ``grace'' distance or minimum surface-surface separation -may also be specified (\texttt{colloid\_random\_dh}). - -The following example asks for 100 colloids to be initialised -at random positions, with a minimum separation of 0.5 lattice -spacing. - -\begin{lstlisting} -colloid_init input_random - -colloid_random_no 100 # Total number of colloids -colloid_random_dh 0.5 # ``Grace'' distance - -colloid_random_a0 2.30 -colloid_random_ah 2.40 -\end{lstlisting} - -An input radius and hydrodynamic radius must be provided: these -are the same for all colloids. -If specific initialisations of the colloid state (excepting the -position) other than the radii are wanted, values should be provided -as for the single particle case in the preceding section, but using -\texttt{colloid\_random\_a0} in place of -\texttt{colloid\_one\_a0} and so on. - -The code will try to initialise the requested number in the current -system size, but only makes a finite number of attempts to place -particles at random with no overlaps. (The initialisation will also -take into account the presence of any solid walls, using the same -grace distance.) If the the number of particles is too large, the -code will halt with a message to that effect. - -In general, colloid information for a arbitrary configuration with many -particles should be read from a pre-prepared file. See the section on -File I/O for further information on reading files. - - -\subsubsection{Interactions} - -Note that two-body pair-potential interactions are defined uniformly for -all colloids in a simulation. The same is true for lubrication corrections. -There are a number of constraints related to the computation of -interactions discussed below. - -{\bf Boundary-colloid lubrication correction}. -Lubrication corrections (here the normal force) between a flat wall -(see section XREF) are -required to prevent overlap between colloid and the wall. -The cutoff distance is set via the key word value pair -\begin{lstlisting} -boundary_lubrication_rcnormal 0.5 -\end{lstlisting} -It is recommended that this is used in all cases involving walls. -A reasonable value is in the range $0.1 < r_c < 0.5$ in lattice -units, and should be calibrated for particle hydrodynamic radius -and fluid viscosity if exact results are important. - -{\bf Colloid-colloid lubrication corrections}. -The key words to activate the calculation of lubrication corrections -are: -\begin{lstlisting} -lubrication_on 1 -lubrication_normal_cutoff 0.5 -lubrication_tangential_cutoff 0.05 -\end{lstlisting} - - - -{\bf Soft sphere potential}. -A cut-and-shifted soft-sphere potential of the form -$v \sim \epsilon (\sigma/r)^\nu$ is -available. Some trial-and-error with the parameters may be required in -any given situation to ensure simulation stability in the long run. The -following gives an example of the relevant input key words: -\begin{lstlisting} -soft_sphere_on 1 # integer 0/1 for off/on -soft_sphere_epsilon 0.0004 # energy units -soft_sphere_sigma 1.0 # a length -soft_sphere_nu 1.0 # exponent is positive -soft_sphere_cutoff 2.25 # a surface-surface separation -\end{lstlisting} -See Section~\ref{section-colloids-soft-sphere} for a detailed description. - -{\bf Lennard-Jones potential}. -The Lennard-Jones potential (Section~\ref{section-colloids-lennard-jones}) -is controlled by the following key words: -\begin{lstlisting} -lennard_jones_on 1 # integer 0/1 off/on -lj_epsilon 0.1 # energy units -lj_sigma 2.6 # potential length scale -lj_cutoff 8.0 # a centre-centre separation -\end{lstlisting} - - -{\bf Yukawa potential}. -A cut-and-shifted Yukawa potential of the form -$v \sim \epsilon \exp(-\kappa r)/r$ is -available using the following key word value pairs: - -\begin{lstlisting} -yukawa_on 1 # integer 0/1 off/on -yukawa_epsilon 1.330 # energy units -yukawa_kappa 0.725 # an inverse length -yukawa_cutoff 16.0 # a centre-centre cutoff -\end{lstlisting} - -{\bf Dipole-dipole interactions (Ewald sum)}. -The Ewald sum is completely specified in the input file -by the uniform dipole strength $\mu$ and the real-space cut off $r_c$. -\begin{lstlisting} -ewald_sum 1 # integer 0/1 off/on -ewald_mu 0.285 # dipole strength mu -ewald_rc 16.0 # real space cut off -\end{lstlisting} - - -If short range interactions are required, particle information is stored -in a cell list, which allows efficient computation of the potentially -$N^2$ interactions present. This gives rise to a constraint that the -width of the cells must be large enough that all relevant interactions -are included. This generally means that the cells must be at least -$2a_h + h_c$ where $h_c$ is the largest relevant cut off distance. - -The requirement for at least two cells per local domain in parallel -means that there is a associated minimum local domain size. This is -computed at run time on the basis of the input. If the local domain -is too small, the code will terminate with an error message. The -local domain size should be increased. - - -\subsubsection{External forces} - -{\bf External body force (gravity)}. -The following example requests a uniform body force in the negative -$z$-direction on all particles. -\begin{lstlisting} -colloid_gravity 0.0_0.0_-0.001 # vector -\end{lstlisting} -The counterbalancing body force on the fluid which enforces the -constraint of momentum conservation for the system as a whole is -computed automatically by the code at each time step. - - -\subsection{Order parameter initialisations} - -Free energy choices requiring one or more fluid order parameters can make -use of the following initial coniditions. - -\subsubsection{Composition $\phi$} - -The following initialisations are available. -\begin{lstlisting} -phi_initialisation spinodal # spinodal -phi0 0.0 # mean -noise 0.05 # noise amplitude -random_seed 102839 # +ve integer -\end{lstlisting} -Suitable for initialising isothermal spinodal decomposition, the order -parameter may be set at random at each position via -$\phi = \phi_0 + A(\hat{\phi} - 1/2)$ with the random variate -$\hat\phi$ selected uniformly on the interval $[0,1)$. For symmetric -quenches (mean order parameter $\phi_0 = 0$ and $\phi^\star = \pm 1$), -a value of $A$ in the range 0.05-0.1 is usually appropriate. - -For off-symmetric quenches, larger patches of fluid may be required to -initiate decomposition: -\begin{lstlisting} -phi_initialisation patches # patches of phi = +/- 1 -phi_init_patch_size 2 # patch size -phi_init_patch_vol 0.1 # volume fraction phi = -1 phase -random_seed 13 # +ve integer -\end{lstlisting} -The initialises cubics patches of fluid of given size with $\phi= \pm 1$ -at random. The requested overall volume fractions may be met approximately. - -A spherical drop can be initialised at the centre of the system. -\begin{lstlisting} -phi_initialisation drop # spherical droplet -phi_init_drop_radius 16.0 # radius -phi_init_drop_amplitude -1.0 # phi value inside -\end{lstlisting} -The drop is initialised with a $\tanh(r/\xi)$ profile where the -interfacial width $\xi$ is computed via the current free energy -parameters. - -% block -% bath - -For restarted simulations, the default position is to read order -parameter information from file -\begin{lstlisting} -phi_initialisation from_file -\end{lstlisting} -in which case a file or files for the appropriate time step should -be present in the working directory. - - -\subsubsection{Tensor order $Q_{\alpha\beta}$} - -A number of different initialisations are available for the liquid -crystal order parameter $Q_{\alpha\beta}$. Some care may be required -to ensure consistency between the choice and the free energy -parameters, the system size, and so on (particularly for the blue phases). - -A summmary of choices is: -\begin{lstlisting} -lc_q_initialisation nematic # uniform nematic... -lc_init_nematic 1.0_0.0_0.0 # ...with given director - -lc_q_initialisation cholesteric_x # cholesteric with helical axis x -lc_q_initialisation cholesteric_y # cholesteric with helical axis y -lc_q_initialisation cholesteric_z # cholesteric with helical axis z - -lc_q_initialisation o8m # BPI high chirality limit -lc_q_initialisation o2 # BPII high chirality limit -lc_q_initialisation o5 -lc_q_initialisation h2d # 2d hexagonal -lc_q_initialisation h3da # 3d hexagonal BP A -lc_q_initialisation h3db # 3d hexagonal BP B -lc_q_initialisation dtc # double twist cylinders - -lc_q_initialisation bp3 - -lc_q_initialisation cf1_x # cholesteric ``finger'' axis x -lc_q_initialisation cf1_y # cholesteric ``finger'' axis y -lc_q_initialisation cf1_z # cholesteric ``finger'' axis z - -lc_q_initialisation cf1_fluc_x # as cf1_x with random perterbations -lc_q_initialisation cf1_fluc_y # as cf1_y with random perturbations -lc_q_initialisation cf1_flux_z # as cf1_z with random perturbations - -lc_q_initialistion random # with randomly chosen unit director -\end{lstlisting} -Note many of the initialiations require an initial amplitude of order, -which should be set via -\begin{lstlisting} -lc_q_init_amplitude 0.01 # initial amplitude of order A -\end{lstlisting} -For example, if an initial uniform nematic is requested with -unit director $n_\alpha$, the corresponding initial tensor will be -$$ -Q_{\alpha\beta} = -{\textstyle \frac{1}{2}} A (3 n_\alpha n_\beta - \delta_{\alpha\beta}). -$$ +The up-to-date version of this material now appears at +\texttt{https://ludwig.epcc.ed.ac.uk/inputs/index.html} \vfill \pagebreak diff --git a/docs/intro.tex b/docs/intro.tex index 17ca71fc..9847d358 100755 --- a/docs/intro.tex +++ b/docs/intro.tex @@ -7,7 +7,9 @@ % Edinburgh Soft Matter and Statistical Physics Group and % Edinburgh Parallel Computing Centre % -% (c) 2016-2019 The University of Edinburgh +% (c) 2016-2023 The University of Edinburgh +% +% This material is being migrated to https://ludwig.epcc.ed.ac.uk/index.html % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -96,196 +98,6 @@ \subsection{Obtaining the code} The current stable release is available as the master branch. - -\vfill -\pagebreak - -\section{Quick Start for Users} - - - -\subsection{Running an Example} - -\subsubsection{Input file} - -The behaviour of \textit{Ludwig} at run time is controlled by -a plain text input file which consists of a series for key value -pairs. Each key value pair controls a parameter or parameters -within the code, for example the system size and number of time steps, -the fluid properties density and viscosity, the free energy type -and associated parameters (if required), frequency and type of -output, parallel decomposition, and so on. - -For most keys, the associated property has some default value which -will be used by the code if the relevant key is not present (or -commented out) in the input file. While such default values are -chosen to be at least sensible, users need to be aware that all -necessary keys need to be considered for a given problem. However, -many keys are irrelevant for any given problem. - -A significant number of example input files are available as -part of the test suite, and these can form a useful starting -point for a given type of problem. We will consider one which -uses some of the common keys. - -\subsubsection{Example input} - -We will consider a simple example which computes the motion of -a single spherical colloidal particle in an initially stationary -fluid of given viscosity. The velocity may be measured as a -function of time. Assume we have an executable in the \texttt{src} -directory. - -Make a copy of the input file -\begin{lstlisting} -$ cp ../tests/regression/d3q19-extra/serial-auto-c01.inp . -\end{lstlisting} - -Inspection of this file will reveal the following: blank lines -and comments --- lines beginning with \texttt{\#} --- may be included, -but are ignored at run time; other lines are parsed as key value -pairs, each pair on a separate line, and with key and value separated -by one or more spaces. Values may be characters or strings, a -scalar integer or 3-vector of integers, or a scalar or 3-vector -of floating point numbers. Values which are 3-vectors are delineated -by an underscore character (not a space), e.g., \texttt{1\_2\_3}, -and always correspond to a Cartesian triple $(x,y,z)$. -A given value is parsed appropriately in the context of the associated key. - -So, the first few lines of the above file are: -\lstinputlisting[firstline=1, lastline= 16] -{../tests/regression/d3q19-extra/serial-auto-c01.inp} -Here, the first key value pair \texttt{N\_cycles 40} sets the number -of time steps to 40, while the second, \texttt{size 64\_64\_64}, sets -the system size to be 64 points in each of the three coordinate -directions. The \texttt{grid} key relates to the parallel -domain decomposition as is discussed in the following -section. - -\subsubsection{Run time} - -The executable takes a single argument on the command line which -is the name of the input file, which should be in the same -directory as the executable. If no input file name is supplied, -the executable will look for a default \texttt{input}. If no -input file is located at all, an error to that effect will be -reported. - -\textbf{Serial: } -If a serial executable is run in the normal way with the copy of the -input file as the argument the code should take around 10--20 seconds -to execute 40 steps. The fist few lines of output should look like: - -\begin{lstlisting}[belowskip=0pt] -$ ./Ludwig.exe ./serial-auto-c01.inp -\end{lstlisting} -\lstinputlisting[aboveskip=0pt, firstline=1, lastline= 16] -{../tests/regression/d3q19-extra/serial-auto-c01.log} -This output shows that the appropriate input file has been read, and -the system size set correspondingly with a number of default settings -including periodic boundary conditions. ``No free energy'' tells us we -are using a single Newtonian fluid. - -Normal termination of execution is accompanied by a report -of the time take by various parts of the code, and finally by -\begin{lstlisting} -... -Ludwig finished normally. -\end{lstlisting} - -\textbf{Parallel: } -The executable compiled and linked against the MPI library can -be run with the MPI launcher for the local system, often -\texttt{mpirun}. For example, a run on 8 MPI tasks produces a -similar output to that seen in the serial case, but reports -details of the local domain decomposition: -\begin{lstlisting} -$ mpirun -np 8 ./Ludwig.exe ./serial-auto-c01.inp -Welcome to Ludwig v0.1.26 (MPI version running on 8 processes) -... -System details --------------- -System size: 64 64 64 -Decomposition: 2 2 2 -Local domain: 32 32 32 -... -\end{lstlisting} -The decomposition is controlled by the \texttt{grid} key in -the input. Here, \texttt{grid 2\_2\_2} is consistent with the -8 MPI tasks specified on the command line, and the resulting -local decomposition is 32 lattice points in each coordinate direction. -If no grid is specified in the input, or a request is -make for a grid which cannot be met (e.g., the product -of the grid dimensions does not agree with the total number -of MPI tasks available) -the code will try to determine its own decomposition as best -it can. If no valid parallel decomposition is available at all, -the code will exit with a message to that effect. - -Further details of various input key value pairs are given in -relevant sections of the documentation. - -\subsubsection{Output} - -The standard output of the running code produces a number of -aggregate quantities which allow a broad overview of the progress -of the computation to be seen by the user. These include, where -relevant, global statistics related to fluid density and momentum, -the integrated free energy, particle-related quantities, and so on. -The frequency of this information can be adjusted from the input -file (see, e.g., \texttt{freq\_statistics}). - -Output for lattice-based quantities (for example, the velocity field -$u(\mathbf{r}; t)$) is via external file. This output is in serial -or in parallel according to how the model is run, and may be in -either ASCII or raw binary format as required. Output files are produced -with time step encoded in the file, and an extension which describes the -parallel output decomposition. Further, each quantity output in this way -is accompanied by a metadata description with \texttt{meta} extension. -For example, the two output files -\begin{lstlisting} -bash-3.2$ ls dist* -dist-00000020.001-001 dist.001-001.meta -\end{lstlisting} -contain the LB distributions for time step 20 (the file is 1 of a total -number of 1 in parallel), and the plain text metadata description, -respectively. - -To ensure output for based-based quantities is in the correct order for -analysis, post-processing -may be required (always if the code is run in parallel). This uses a utility -provided for the purpose which required both the data and the metadata -description to recombine the parallel output. This utility is described -ELSEWHERE. - -Output for colloidal particle data is again to file with a name encoding -the time step and parallel decomposition of the output. For example, -\begin{lstlisting} -bash-3.2$ ls config* -config.cds00000020.001-001 -\end{lstlisting} -contains particle data for time step 20 in a format described in -Section~\ref{section-examples}. -These data may be requested in ASCII or raw binary format. - -\subsubsection{Errors and run time failures} - -The code attempts to provide meaningful diagnostic error messages -for common problems. Such errors include missing or incorrectly -formatted input files, inconsistent input values, and unacceptable -feature combinations. The code should also detect other run time -problems such as insufficient memory and errors writing output -files. These errors will result in termination. - -Instability in the computation will often be manifested by numerically -absurd values in the statistical output (ultimately \texttt{NaN} in -many cases). Instability may or may not result in termination, depending -on the problem. Such instability is very often related to poor parameter -choices, of which there can be many combinations. Check the -relevant section of the documentation for advice on reasonable starting -parameters for different problems. - - \subsection{Note on Units} All computation in \textit{Ludwig} is undertaken in ``lattice From 23271a96d9fd3decb0f40053f7836d13b33006cc Mon Sep 17 00:00:00 2001 From: Kevin Stratford Date: Fri, 7 Jul 2023 17:16:25 +0100 Subject: [PATCH 97/97] Version 0.20.0 --- CHANGES.md | 8 +++++++- README.md | 3 ++- version.h | 4 ++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 213574e5..c4db7f54 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,11 +3,17 @@ version 0.20.0 +- IMPORTANT: The input file can no longer be specified as a command + line argument. It must be called "input" in the current directory. +- The electrokinetics sector has been updated and information is + available at + https://ludwig.epcc.ed.ac.uk/tutorials/electrokinetics/electrokinetics.html +- A D3Q27 model is available - Added coverage via https://about.codecov.io/ - The free energy is now reported at t = 0 for the initial state. - An extra "first touch" option has been added. For details, see https://ludwig.epcc.ed.ac.uk/inputs/parallel.html - +- Various minor changes and code quality improvements. version 0.19.1 - Fix bug in io_subfile to prevent failure at iogrid > 1. diff --git a/README.md b/README.md index a2a4c825..1d1eea40 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,8 @@ $ make test ``` -Full details of the build process are available at +Full details of the build process, and tutorials on how to +use the code are available at https://ludwig.epcc.ed.ac.uk/. #### Background diff --git a/version.h b/version.h index 3f2d0d7c..a78d17de 100644 --- a/version.h +++ b/version.h @@ -13,7 +13,7 @@ #define LUDWIG_VERSION_H #define LUDWIG_MAJOR_VERSION 0 -#define LUDWIG_MINOR_VERSION 19 -#define LUDWIG_PATCH_VERSION 1 +#define LUDWIG_MINOR_VERSION 20 +#define LUDWIG_PATCH_VERSION 0 #endif