cngf-pf

continuum model for granular flows with pore-pressure dynamics (renamed from 1d_fd_simple_shear)
git clone git://src.adamsgaard.dk/cngf-pf # fast
git clone https://src.adamsgaard.dk/cngf-pf.git # slow
Log | Files | Refs | README | LICENSE Back to index

simulation.c (27249B)


      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <math.h>
      4 #include <err.h>
      5 #include "arrays.h"
      6 #include "simulation.h"
      7 #include "fluid.h"
      8 
      9 
     10 /* iteration limits for solvers */
     11 #define MAX_ITER_GRANULAR 100000
     12 #define MAX_ITER_DARCY 1000000
     13 
     14 /* tolerance criteria when in velocity driven or velocity limited mode */
     15 #define RTOL_VELOCITY 1e-3
     16 
     17 /* lower limit for effective normal stress sigma_n_eff for granular solver */
     18 #define SIGMA_N_EFF_MIN 1e-1
     19 
     20 
     21 /* Simulation settings */
     22 void
     23 init_sim(struct simulation *sim)
     24 {
     25 	int ret;
     26 
     27 	ret = snprintf(sim->name, sizeof(sim->name), DEFAULT_SIMULATION_NAME);
     28 	if (ret < 0 || (size_t)ret == sizeof(sim->name))
     29 		err(1, "%s: could not write simulation name", __func__);
     30 
     31 	sim->G = 9.81;
     32 	sim->P_wall = 120e3;
     33 	sim->mu_wall = 0.45;
     34 	sim->v_x_bot = 0.0;
     35 	sim->v_x_fix = NAN;
     36 	sim->v_x_limit = NAN;
     37 	sim->nz = -1;                   /* cell size equals grain size if negative */
     38 
     39 	sim->A = 0.40;		            /* Loose fit to Damsgaard et al 2013 */
     40 	sim->b = 0.9377;	            /* Henann and Kamrin 2016 */
     41 	/* sim->mu_s = 0.3819; */            /* Henann and Kamrin 2016 */
     42 	/* sim->C = 0.0;       */            /* Henann and Kamrin 2016 */
     43 	sim->mu_s = tan(DEG2RAD(22.0)); /* Damsgaard et al 2013 */
     44 	sim->C = 0.0;                   /* Damsgaard et al 2013 */
     45 	sim->phi = initval(0.25, 1);
     46 	sim->d = 0.04;		            /* Damsgaard et al 2013 */
     47 	sim->transient = 0;
     48 	sim->phi_min = 0.20;
     49 	sim->phi_max = 0.55;
     50 	sim->dilatancy_constant = 4.09;	/* Pailha & Pouliquen 2009 */
     51 
     52 	/* Iverson et al 1997, 1998: Storglaciaren till */
     53 	/* sim->mu_s = tan(DEG2RAD(26.3)); */
     54 	/* sim->C = 5.0e3; */
     55 	/* sim->phi = initval(0.22, 1); */
     56 	/* sim->d = ??; */
     57 
     58 	/* Iverson et al 1997, 1998: Two Rivers till */
     59 	/* sim->mu_s = tan(DEG2RAD(17.8)); */
     60 	/* sim->C = 14.0e3; */
     61 	/* sim->phi = initval(0.37, 1); */
     62 	/* sim->d = ??; */
     63 
     64 	/* Tulaczyk et al 2000a: Upstream B till */
     65 	/* sim->mu_s = tan(DEG2RAD(23.9)); */
     66 	/* sim->C = 3.0e3; */
     67 	/* sim->phi = initval(0.35, 1); */
     68 	/* sim->d = ??; */
     69 
     70 	sim->rho_s = 2.6e3;	            /* Damsgaard et al 2013 */
     71 	sim->origo_z = 0.0;
     72 	sim->L_z = 1.0;
     73 	sim->t = 0.0;
     74 	sim->dt = 1.0;
     75 	sim->t_end = 1.0;
     76 	sim->file_dt = 1.0;
     77 	sim->n_file = 0;
     78 	sim->fluid = 0;
     79 	sim->rho_f = 1e3;
     80 
     81 	/* Water at 20 deg C */
     82 	/* sim->beta_f = 4.5e-10; */    /* Goren et al 2011 */
     83 	/* sim->mu_f = 1.0-3; */        /* Goren et al 2011 */
     84 
     85 	/* Water at 0 deg C */
     86 	sim->beta_f = 3.9e-10;	/* doi:10.1063/1.1679903 */
     87 	sim->mu_f = 1.787e-3;	/* Cuffey and Paterson 2010 */
     88 
     89 	sim->alpha = 1e-8;
     90 	sim->D = -1.0;	/* disabled when negative */
     91 
     92 	sim->k = initval(1.9e-15, 1);   /* Damsgaard et al 2015 */
     93 
     94 	/* Iverson et al 1994: Storglaciaren */
     95 	/* sim->k = initval(1.3e-14, 1); */
     96 
     97 	/* Engelhardt et al 1990: Upstream B */
     98 	/* sim->k = initval(2.0e-16, 1); */
     99 
    100 	/* Leeman et al 2016: Upstream B till */
    101 	/* sim->k = initval(4.9e-17, 1); */
    102 
    103 	/* no fluid-pressure variations */
    104 	sim->p_f_top = 0.0;
    105 	sim->p_f_mod_ampl = 0.0;
    106 	sim->p_f_mod_freq = 1.0;
    107 	sim->p_f_mod_phase = 0.0;
    108 	sim->p_f_mod_pulse_time = NAN;
    109 	sim->p_f_mod_pulse_shape = 0;
    110 }
    111 
    112 void
    113 prepare_arrays(struct simulation *sim)
    114 {
    115 	if (sim->nz < 2) {
    116 		fprintf(stderr, "error: grid size (nz) must be at least 2 but is %d\n",
    117 		        sim->nz);
    118 		exit(1);
    119 	}
    120 	free(sim->phi);
    121 	free(sim->k);
    122 
    123 	sim->z = linspace(sim->origo_z,
    124 	                  sim->origo_z + sim->L_z,
    125 	                  sim->nz);
    126 	sim->dz = sim->z[1] - sim->z[0];
    127 	sim->mu = zeros(sim->nz);
    128 	sim->mu_c = zeros(sim->nz);
    129 	sim->sigma_n_eff = zeros(sim->nz);
    130 	sim->sigma_n = zeros(sim->nz);
    131 	sim->p_f_ghost = zeros(sim->nz + 2);
    132 	sim->p_f_next_ghost = zeros(sim->nz + 2);
    133 	sim->p_f_dot = zeros(sim->nz);
    134 	sim->p_f_dot_expl = zeros(sim->nz);
    135 	sim->p_f_dot_impl = zeros(sim->nz);
    136 	sim->p_f_dot_impl_r_norm = zeros(sim->nz);
    137 	sim->phi = zeros(sim->nz);
    138 	sim->phi_c = zeros(sim->nz);
    139 	sim->phi_dot = zeros(sim->nz);
    140 	sim->k = zeros(sim->nz);
    141 	sim->xi = zeros(sim->nz);
    142 	sim->gamma_dot_p = zeros(sim->nz);
    143 	sim->v_x = zeros(sim->nz);
    144 	sim->d_x = zeros(sim->nz);
    145 	sim->g_local = zeros(sim->nz);
    146 	sim->g_ghost = zeros(sim->nz + 2);
    147 	sim->g_r_norm = zeros(sim->nz);
    148 	sim->I = zeros(sim->nz);
    149 	sim->tan_psi = zeros(sim->nz);
    150 	sim->old_val = empty(sim->nz);
    151 	sim->fluid_old_val = empty(sim->nz);
    152 	sim->tmp_ghost = empty(sim->nz + 2);
    153 	sim->p_f_dot_old = zeros(sim->nz);
    154 }
    155 
    156 void
    157 free_arrays(struct simulation *sim)
    158 {
    159 	free(sim->z);
    160 	free(sim->mu);
    161 	free(sim->mu_c);
    162 	free(sim->sigma_n_eff);
    163 	free(sim->sigma_n);
    164 	free(sim->p_f_ghost);
    165 	free(sim->p_f_next_ghost);
    166 	free(sim->p_f_dot);
    167 	free(sim->p_f_dot_expl);
    168 	free(sim->p_f_dot_impl);
    169 	free(sim->p_f_dot_impl_r_norm);
    170 	free(sim->k);
    171 	free(sim->phi);
    172 	free(sim->phi_c);
    173 	free(sim->phi_dot);
    174 	free(sim->xi);
    175 	free(sim->gamma_dot_p);
    176 	free(sim->v_x);
    177 	free(sim->d_x);
    178 	free(sim->g_local);
    179 	free(sim->g_ghost);
    180 	free(sim->g_r_norm);
    181 	free(sim->I);
    182 	free(sim->tan_psi);
    183 	free(sim->old_val);
    184 	free(sim->fluid_old_val);
    185 	free(sim->tmp_ghost);
    186 	free(sim->p_f_dot_old);
    187 }
    188 
    189 static void
    190 warn_parameter_value(const char message[],
    191                      const double value,
    192                      int *return_status)
    193 {
    194 	fprintf(stderr,
    195 	        "check_simulation_parameters: %s (%.17g)\n",
    196 	        message, value);
    197 	*return_status = 1;
    198 }
    199 
    200 static void
    201 check_float(const char name[], const double value, int *return_status)
    202 {
    203 	int ret;
    204 	char message[100];
    205 
    206 #ifdef SHOW_PARAMETERS
    207 	printf("%30s: %.17g\n", name, value);
    208 #endif
    209 	if (isnan(value)) {
    210 		ret = snprintf(message, sizeof(message), "%s is NaN", name);
    211 		if (ret < 0 || (size_t)ret >= sizeof(message))
    212 			err(1, "%s: message parsing", __func__);
    213 		warn_parameter_value(message, value, return_status);
    214 	} else if (isinf(value)) {
    215 		ret = snprintf(message, sizeof(message), "%s is infinite", name);
    216 		if (ret < 0 || (size_t)ret >= sizeof(message))
    217 			err(1, "%s: message parsing", __func__);
    218 		warn_parameter_value(message, value, return_status);
    219 	}
    220 }
    221 
    222 void
    223 check_simulation_parameters(struct simulation *sim)
    224 {
    225 	int return_status = 0;
    226 
    227 	check_float("sim->G", sim->G, &return_status);
    228 	if (sim->G < 0.0)
    229 		warn_parameter_value("sim->G is negative", sim->G, &return_status);
    230 
    231 	check_float("sim->P_wall", sim->P_wall, &return_status);
    232 	if (sim->P_wall < 0.0)
    233 		warn_parameter_value("sim->P_wall is negative", sim->P_wall,
    234 		                     &return_status);
    235 
    236 	check_float("sim->v_x_bot", sim->v_x_bot, &return_status);
    237 
    238 	check_float("sim->mu_wall", sim->mu_wall, &return_status);
    239 	if (sim->mu_wall < 0.0)
    240 		warn_parameter_value("sim->mu_wall is negative", sim->mu_wall,
    241 		                     &return_status);
    242 
    243 	check_float("sim->A", sim->A, &return_status);
    244 	if (sim->A < 0.0)
    245 		warn_parameter_value("sim->A is negative", sim->A, &return_status);
    246 
    247 	check_float("sim->b", sim->b, &return_status);
    248 	if (sim->b < 0.0)
    249 		warn_parameter_value("sim->b is negative", sim->b, &return_status);
    250 
    251 	check_float("sim->mu_s", sim->mu_s, &return_status);
    252 	if (sim->mu_s < 0.0)
    253 		warn_parameter_value("sim->mu_s is negative", sim->mu_s,
    254 		                     &return_status);
    255 
    256 	check_float("sim->C", sim->C, &return_status);
    257 
    258 	check_float("sim->d", sim->d, &return_status);
    259 	if (sim->d <= 0.0)
    260 		warn_parameter_value("sim->d is not a positive number", sim->d,
    261 		                     &return_status);
    262 
    263 	check_float("sim->rho_s", sim->rho_s, &return_status);
    264 	if (sim->rho_s <= 0.0)
    265 		warn_parameter_value("sim->rho_s is not a positive number", sim->rho_s,
    266 		                     &return_status);
    267 
    268 	if (sim->nz <= 0)
    269 		warn_parameter_value("sim->nz is not a positive number", sim->nz,
    270 		                     &return_status);
    271 
    272 	check_float("sim->origo_z", sim->origo_z, &return_status);
    273 	check_float("sim->L_z", sim->L_z, &return_status);
    274 	if (sim->L_z <= sim->origo_z)
    275 		warn_parameter_value("sim->L_z is smaller or equal to sim->origo_z",
    276 		                     sim->L_z, &return_status);
    277 
    278 	if (sim->nz <= 0)
    279 		warn_parameter_value("sim->nz is not a positive number", sim->nz,
    280 		                     &return_status);
    281 
    282 	check_float("sim->dz", sim->dz, &return_status);
    283 	if (sim->dz <= 0.0)
    284 		warn_parameter_value("sim->dz is not a positive number", sim->dz,
    285 		                     &return_status);
    286 
    287 	check_float("sim->t", sim->t, &return_status);
    288 	if (sim->t < 0.0)
    289 		warn_parameter_value("sim->t is a negative number",
    290 		                     sim->t, &return_status);
    291 
    292 	check_float("sim->t_end", sim->t_end, &return_status);
    293 	if (sim->t > sim->t_end)
    294 		warn_parameter_value("sim->t_end is smaller than sim->t",
    295 		                     sim->t, &return_status);
    296 
    297 	check_float("sim->dt", sim->dt, &return_status);
    298 	if (sim->dt < 0.0)
    299 		warn_parameter_value("sim->dt is less than zero",
    300 		                     sim->dt, &return_status);
    301 
    302 	check_float("sim->file_dt", sim->file_dt, &return_status);
    303 	if (sim->file_dt < 0.0)
    304 		warn_parameter_value("sim->file_dt is a negative number",
    305 		                     sim->file_dt, &return_status);
    306 
    307 	check_float("sim->phi[0]", sim->phi[0], &return_status);
    308 	if (sim->phi[0] < 0.0 || sim->phi[0] > 1.0)
    309 		warn_parameter_value("sim->phi[0] is not within [0;1]",
    310 		                     sim->phi[0], &return_status);
    311 
    312 	check_float("sim->phi_min", sim->phi_min, &return_status);
    313 	if (sim->phi_min < 0.0 || sim->phi_min > 1.0)
    314 		warn_parameter_value("sim->phi_min is not within [0;1]",
    315 		                     sim->phi_min, &return_status);
    316 
    317 	check_float("sim->phi_max", sim->phi_max, &return_status);
    318 	if (sim->phi_max < 0.0 || sim->phi_max > 1.0)
    319 		warn_parameter_value("sim->phi_max is not within [0;1]",
    320 		                     sim->phi_max, &return_status);
    321 
    322 	check_float("sim->dilatancy_constant", sim->dilatancy_constant, &return_status);
    323 	if (sim->dilatancy_constant < 0.0 || sim->dilatancy_constant > 100.0)
    324 		warn_parameter_value("sim->dilatancy_constant is not within [0;100]",
    325 		                     sim->dilatancy_constant, &return_status);
    326 
    327 	if (sim->fluid != 0 && sim->fluid != 1)
    328 		warn_parameter_value("sim->fluid has an invalid value",
    329 		                     (double) sim->fluid, &return_status);
    330 
    331 	if (sim->transient != 0 && sim->transient != 1)
    332 		warn_parameter_value("sim->transient has an invalid value",
    333 		                     (double) sim->transient, &return_status);
    334 
    335 	if (sim->fluid) {
    336 		check_float("sim->p_f_mod_ampl", sim->p_f_mod_ampl, &return_status);
    337 		if (sim->p_f_mod_ampl < 0.0)
    338 			warn_parameter_value("sim->p_f_mod_ampl is not a zero or positive",
    339 			                     sim->p_f_mod_ampl, &return_status);
    340 
    341 		check_float("sim->p_f_mod_freq", sim->p_f_mod_freq, &return_status);
    342 		if (sim->p_f_mod_freq < 0.0)
    343 			warn_parameter_value("sim->p_f_mod_freq is not a zero or positive",
    344 			                     sim->p_f_mod_freq, &return_status);
    345 
    346 		check_float("sim->beta_f", sim->beta_f, &return_status);
    347 		if (sim->beta_f <= 0.0)
    348 			warn_parameter_value("sim->beta_f is not positive",
    349 			                     sim->beta_f, &return_status);
    350 
    351 		check_float("sim->alpha", sim->alpha, &return_status);
    352 		if (sim->alpha <= 0.0)
    353 			warn_parameter_value("sim->alpha is not positive",
    354 			                     sim->alpha, &return_status);
    355 
    356 		check_float("sim->mu_f", sim->mu_f, &return_status);
    357 		if (sim->mu_f <= 0.0)
    358 			warn_parameter_value("sim->mu_f is not positive",
    359 			                     sim->mu_f, &return_status);
    360 
    361 		check_float("sim->rho_f", sim->rho_f, &return_status);
    362 		if (sim->rho_f <= 0.0)
    363 			warn_parameter_value("sim->rho_f is not positive",
    364 			                     sim->rho_f, &return_status);
    365 
    366 		check_float("sim->k[0]", sim->k[0], &return_status);
    367 		if (sim->k[0] <= 0.0)
    368 			warn_parameter_value("sim->k[0] is not positive",
    369 			                      sim->k[0], &return_status);
    370 
    371 		check_float("sim->D", sim->D, &return_status);
    372 		if (sim->transient && sim->D > 0.0)
    373 			warn_parameter_value("constant diffusivity does not work in "
    374 			                     "transient simulations",
    375 			                     sim->D, &return_status);
    376 	}
    377 
    378 	if (return_status != 0) {
    379 		fprintf(stderr, "error: aborting due to invalid parameter choices\n");
    380 		exit(return_status);
    381 	}
    382 }
    383 
    384 void
    385 lithostatic_pressure_distribution(struct simulation *sim)
    386 {
    387 	int i;
    388 
    389 	for (i = 0; i < sim->nz; ++i)
    390 		sim->sigma_n[i] = sim->P_wall
    391 		                  + (1.0 - sim->phi[i]) * sim->rho_s * sim->G
    392 		                  * (sim->L_z - sim->z[i]);
    393 }
    394 
    395 inline static double
    396 inertia_number(double gamma_dot_p, double d, double sigma_n_eff, double rho_s)
    397 {
    398 	return fabs(gamma_dot_p) * d / sqrt(sigma_n_eff / rho_s);
    399 }
    400 
    401 void
    402 compute_inertia_number(struct simulation *sim)
    403 {
    404 	int i;
    405 
    406 	for (i = 0; i < sim->nz; ++i)
    407 		sim->I[i] = inertia_number(sim->gamma_dot_p[i],
    408 		                           sim->d,
    409 		                           fmax(sim->sigma_n_eff[i], SIGMA_N_EFF_MIN),
    410 		                           sim->rho_s);
    411 }
    412 
    413 void
    414 compute_critical_state_porosity(struct simulation *sim)
    415 {
    416 	int i;
    417 
    418 	for (i = 0; i < sim->nz; ++i)
    419 		sim->phi_c[i] = sim->phi_min
    420 		                + (sim->phi_max - sim->phi_min) * sim->I[i];
    421 }
    422 
    423 void
    424 compute_critical_state_friction(struct simulation *sim)
    425 {
    426 	int i;
    427 
    428 	if (sim->fluid)
    429 		for (i = 0; i < sim->nz; ++i)
    430 			sim->mu_c[i] = sim->mu_wall
    431 			               / (fmax(sim->sigma_n_eff[i], SIGMA_N_EFF_MIN)
    432 			                  / (sim->P_wall - sim->p_f_top));
    433 	else
    434 		for (i = 0; i < sim->nz; ++i)
    435 			sim->mu_c[i] = sim->mu_wall
    436 			               / (1.0 + (1.0 - sim->phi[i]) * sim->rho_s * sim->G *
    437 			                  (sim->L_z - sim->z[i]) / sim->P_wall);
    438 }
    439 
    440 static void
    441 compute_friction(struct simulation *sim)
    442 {
    443 	int i;
    444 
    445 	if (sim->transient)
    446 		for (i = 0; i < sim->nz; ++i)
    447 			sim->mu[i] = sim->mu_c[i] + sim->tan_psi[i];
    448 	else
    449 		for (i = 0; i < sim->nz; ++i)
    450 			sim->mu[i] = sim->mu_c[i];
    451 }
    452 
    453 static void
    454 compute_tan_dilatancy_angle(struct simulation *sim)
    455 {
    456 	int i;
    457 
    458 	for (i = 0; i < sim->nz; ++i)
    459 		sim->tan_psi[i] = sim->dilatancy_constant * (sim->phi_c[i] - sim->phi[i]);
    460 }
    461 
    462 static void
    463 compute_porosity_change(struct simulation *sim)
    464 {
    465 	int i;
    466 
    467 	for (i = 0; i < sim->nz; ++i)
    468 		sim->phi_dot[i] = sim->tan_psi[i] * sim->gamma_dot_p[i] * sim->phi[i];
    469 }
    470 
    471 double
    472 kozeny_carman(const double diameter, const double porosity)
    473 {
    474 	return pow(diameter, 2.0) / 180.0
    475 	       * pow(porosity, 3.0)
    476 	       / pow(1.0 - porosity, 2.0);
    477 }
    478 
    479 static void
    480 compute_permeability(struct simulation *sim)
    481 {
    482 	int i;
    483 
    484 	for (i = 0; i < sim->nz; ++i)
    485 		sim->k[i] = kozeny_carman(sim->d, sim->phi[i]);
    486 }
    487 
    488 static double
    489 shear_strain_rate_plastic(const double fluidity, const double friction)
    490 {
    491 	return fluidity * friction;
    492 }
    493 
    494 static void
    495 compute_shear_strain_rate_plastic(struct simulation *sim)
    496 {
    497 	int i;
    498 
    499 	for (i = 0; i < sim->nz; ++i)
    500 		sim->gamma_dot_p[i] = shear_strain_rate_plastic(sim->g_ghost[i + 1],
    501 		                                                sim->mu[i]);
    502 }
    503 
    504 static void
    505 compute_shear_velocity(struct simulation *sim)
    506 {
    507 	int i;
    508 
    509 	/* TODO: implement iterative solver for v_x from gamma_dot_p field */
    510 	/* Dirichlet BC at bottom */
    511 	sim->v_x[0] = sim->v_x_bot;
    512 
    513 	for (i = 1; i < sim->nz; ++i)
    514 		sim->v_x[i] = sim->v_x[i - 1] + sim->gamma_dot_p[i] * sim->dz;
    515 }
    516 
    517 void
    518 compute_effective_stress(struct simulation *sim)
    519 {
    520 	int i;
    521 
    522 	if (sim->fluid)
    523 		for (i = 0; i < sim->nz; ++i) {
    524 			/* average of current and next step pressure values for effective stress - may not be optimal */
    525 			sim->sigma_n_eff[i] = sim->sigma_n[i]
    526 			                      - ((sim->p_f_ghost[i + 1]
    527 			                          + sim->p_f_next_ghost[i + 1])
    528 			                         / 2.0);
    529 			/* sim->sigma_n_eff[i] = sim->sigma_n[i] - sim->p_f_ghost[i + 1]; */
    530 			if (sim->sigma_n_eff[i] < 0)
    531 				warnx("%s: sigma_n_eff[%d] is negative with value %g",
    532 				     __func__, i, sim->sigma_n_eff[i]);
    533 		}
    534 	else
    535 		for (i = 0; i < sim->nz; ++i)
    536 			sim->sigma_n_eff[i] = sim->sigma_n[i];
    537 }
    538 
    539 static double
    540 cooperativity_length(const double A,
    541                      const double d,
    542                      const double mu,
    543                      const double p,
    544                      const double mu_s,
    545                      const double C)
    546 {
    547 	return A * d / sqrt(fabs((mu - C / p) - mu_s));
    548 }
    549 
    550 static void
    551 compute_cooperativity_length(struct simulation *sim)
    552 {
    553 	int i;
    554 
    555 	for (i = 0; i < sim->nz; ++i)
    556 		sim->xi[i] = cooperativity_length(sim->A,
    557 		                                  sim->d,
    558 		                                  sim->mu[i],
    559 		                                  fmax(sim->sigma_n_eff[i], SIGMA_N_EFF_MIN),
    560 		                                  sim->mu_s,
    561 		                                  sim->C);
    562 }
    563 
    564 static double
    565 local_fluidity(const double p,
    566                const double mu,
    567                const double mu_s,
    568                const double C,
    569                const double b,
    570                const double rho_s,
    571                const double d)
    572 {
    573 	if (mu - C / p <= mu_s)
    574 		return 0.0;
    575 	else
    576 		return sqrt(p / (rho_s * d * d)) * ((mu - C / p) - mu_s) / (b * mu);
    577 }
    578 
    579 static void
    580 compute_local_fluidity(struct simulation *sim)
    581 {
    582 	int i;
    583 
    584 	for (i = 0; i < sim->nz; ++i)
    585 		sim->g_local[i] = local_fluidity(fmax(sim->sigma_n_eff[i],
    586 		                                      SIGMA_N_EFF_MIN),
    587 		                                 sim->mu[i],
    588 		                                 sim->mu_s,
    589 		                                 sim->C,
    590 		                                 sim->b,
    591 		                                 sim->rho_s,
    592 		                                 sim->d);
    593 }
    594 
    595 void
    596 set_bc_neumann(double *a,
    597                const int nz,
    598                const int boundary,
    599                const double df,
    600                const double dx)
    601 {
    602 	if (boundary == -1)
    603 		a[0] = a[1] + df * dx;
    604 	else if (boundary == +1)
    605 		a[nz + 1] = a[nz] - df * dx;
    606 	else
    607 		errx(1, "%s: Unknown boundary %d\n", __func__, boundary);
    608 }
    609 
    610 void
    611 set_bc_dirichlet(double *a,
    612                  const int nz,
    613                  const int boundary,
    614                  const double value)
    615 {
    616 	if (boundary == -1)
    617 		a[0] = value;
    618 	else if (boundary == +1)
    619 		a[nz + 1] = value;
    620 	else
    621 		errx(1, "%s: Unknown boundary %d\n", __func__, boundary);
    622 }
    623 
    624 double
    625 residual(double new_val, double old_val)
    626 {
    627 	return (new_val - old_val) / (old_val + 1e-16);
    628 }
    629 
    630 static void
    631 poisson_solver_1d_cell_update(int i,
    632                               const double *g_in,
    633                               const double *g_local,
    634                               double *g_out,
    635                               double *r_norm,
    636                               const double dz,
    637                               const double *xi)
    638 {
    639 	double coorp_term = dz * dz / (2.0 * pow(xi[i], 2.0));
    640 
    641 	g_out[i + 1] = 1.0 / (1.0 + coorp_term)
    642 	               * (coorp_term * g_local[i] + g_in[i + 2] / 2.0
    643 	                  + g_in[i] / 2.0);
    644 	r_norm[i] = fabs(residual(g_out[i + 1], g_in[i + 1]));
    645 
    646 #ifdef DEBUG
    647 	printf("-- %d --------------\n", i);
    648 	printf("coorp_term: %g\n", coorp_term);
    649 	printf("   g_local: %g\n", g_local[i]);
    650 	printf("      g_in: %g\n", g_in[i + 1]);
    651 	printf("     g_out: %g\n", g_out[i + 1]);
    652 	printf("    r_norm: %g\n", r_norm[i]);
    653 #endif
    654 }
    655 
    656 static int
    657 implicit_1d_jacobian_poisson_solver(struct simulation *sim,
    658                                     const int max_iter,
    659                                     const double rel_tol)
    660 {
    661 	int iter, i;
    662 	double r_norm_max = NAN, *tmp;
    663 
    664 	for (iter = 0; iter < max_iter; ++iter) {
    665 #ifdef DEBUG
    666 		printf("\n@@@ %s: ITERATION %d @@@\n", __func__, iter);
    667 #endif
    668 		/* Dirichlet BCs resemble fixed particle velocities */
    669 		set_bc_dirichlet(sim->g_ghost, sim->nz, -1, 0.0);
    670 		set_bc_dirichlet(sim->g_ghost, sim->nz, +1, 0.0);
    671 
    672 		/* Neumann BCs resemble free surfaces */
    673 		/* set_bc_neumann(sim->g_ghost, sim->nz, +1, 0.0, sim->dz); */
    674 
    675 		for (i = 0; i < sim->nz; ++i)
    676 			poisson_solver_1d_cell_update(i,
    677 			                              sim->g_ghost,
    678 			                              sim->g_local,
    679 			                              sim->tmp_ghost,
    680 			                              sim->g_r_norm,
    681 			                              sim->dz,
    682 			                              sim->xi);
    683 		r_norm_max = max(sim->g_r_norm, sim->nz);
    684 
    685 		tmp = sim->tmp_ghost;
    686 		sim->tmp_ghost = sim->g_ghost;
    687 		sim->g_ghost = tmp;
    688 
    689 		if (r_norm_max <= rel_tol) {
    690 			set_bc_dirichlet(sim->g_ghost, sim->nz, -1, 0.0);
    691 			set_bc_dirichlet(sim->g_ghost, sim->nz, +1, 0.0);
    692 #ifdef DEBUG
    693 			printf(".. Solution converged after %d iterations\n", iter + 1);
    694 #endif
    695 			return 0;
    696 		}
    697 	}
    698 
    699 	fprintf(stderr, "implicit_1d_jacobian_poisson_solver: ");
    700 	fprintf(stderr, "Solution did not converge after %d iterations\n", iter + 1);
    701 	fprintf(stderr, ".. Residual normalized error: %f\n", r_norm_max);
    702 	return 1;
    703 }
    704 
    705 void
    706 write_output_file(struct simulation *sim, const int normalize)
    707 {
    708 	int ret;
    709 	char outfile[200];
    710 	FILE *fp;
    711 
    712 	ret = snprintf(outfile, sizeof(outfile), "%s.output%05d.txt",
    713 	               sim->name, sim->n_file++);
    714 	if (ret < 0 || (size_t)ret >= sizeof(outfile))
    715 		err(1, "%s: outfile snprintf", __func__);
    716 
    717 	if ((fp = fopen(outfile, "w")) != NULL) {
    718 		print_output(sim, fp, normalize);
    719 		fclose(fp);
    720 	} else {
    721 		fprintf(stderr, "could not open output file: %s", outfile);
    722 		exit(1);
    723 	}
    724 }
    725 
    726 void
    727 print_output(struct simulation *sim, FILE *fp, const int norm)
    728 {
    729 	int i;
    730 	double *v_x_out;
    731 
    732 	if (norm)
    733 		v_x_out = normalize(sim->v_x, sim->nz);
    734 	else
    735 		v_x_out = copy(sim->v_x, sim->nz);
    736 
    737 	for (i = 0; i < sim->nz; ++i)
    738 		fprintf(fp,
    739 		        "%.17g\t%.17g\t%.17g\t"
    740 		        "%.17g\t%.17g\t%.17g\t"
    741 		        "%.17g\t%.17g\t%.17g\t%.17g"
    742 		        "\n",
    743 		        sim->z[i],
    744 		        v_x_out[i],
    745 		        sim->sigma_n_eff[i],
    746 		        sim->p_f_ghost[i + 1],
    747 		        sim->mu[i],
    748 		        sim->gamma_dot_p[i],
    749 		        sim->phi[i],
    750 		        sim->I[i],
    751 		        sim->mu[i] * fmax(sim->sigma_n_eff[i], SIGMA_N_EFF_MIN),
    752 		        sim->d_x[i]);
    753 
    754 	free(v_x_out);
    755 }
    756 
    757 static void
    758 temporal_increment(struct simulation *sim)
    759 {
    760 	int i;
    761 
    762 	if (sim->transient)
    763 		for (i = 0; i < sim->nz; ++i)
    764 			sim->phi[i] += sim->phi_dot[i] * sim->dt;
    765 
    766 	if (sim->fluid)
    767 		for (i = 0; i < sim->nz; ++i) {
    768 			if (isnan(sim->p_f_dot[i])) {
    769 				errx(1, "encountered NaN at sim->p_f_dot[%d] (t = %g s)",
    770 				     i, sim->t);
    771 			} else {
    772 				sim->p_f_ghost[i + 1] += sim->p_f_dot[i] * sim->dt;
    773 			}
    774 		}
    775 
    776 	for (i = 0; i < sim->nz; ++i)
    777 		sim->d_x[i] += sim->v_x[i] * sim->dt;
    778 	sim->t += sim->dt;
    779 }
    780 
    781 int
    782 coupled_shear_solver(struct simulation *sim,
    783                      const int max_iter,
    784                      const double rel_tol)
    785 {
    786 	int i, coupled_iter, stress_iter = 0;
    787 	double r_norm_max, vel_res_norm = NAN, mu_wall_orig = sim->mu_wall;
    788 
    789 	copy_values(sim->p_f_ghost, sim->p_f_next_ghost, sim->nz + 2);
    790 	compute_effective_stress(sim);	/* Eq. 9 */
    791 
    792 	do {			/* stress iterations */
    793 		coupled_iter = 0;
    794 		do {		/* coupled iterations */
    795 
    796 			if (sim->transient) {
    797 				copy_values(sim->phi_dot, sim->old_val, sim->nz);
    798 
    799 				/* step 1 */
    800 				compute_inertia_number(sim);	/* Eq. 1 */
    801 				/* step 2 */
    802 				compute_critical_state_porosity(sim);	/* Eq. 2 */
    803 				/* step 3 */
    804 				compute_tan_dilatancy_angle(sim);	/* Eq. 5 */
    805 			}
    806 			compute_critical_state_friction(sim);	/* Eq. 7 */
    807 
    808 			/* step 4 */
    809 			if (sim->transient) {
    810 				compute_porosity_change(sim);	/* Eq. 3 */
    811 				compute_permeability(sim);	/* Eq. 6 */
    812 			}
    813 			compute_friction(sim);	/* Eq. 4 */
    814 
    815 			/* step 5, Eq. 13 */
    816 			if (sim->fluid && (sim->t > 0) )
    817 				if (darcy_solver_1d(sim, MAX_ITER_DARCY, 1e-3 * rel_tol))
    818 					exit(11);
    819 
    820 			/* step 6 */
    821 			compute_effective_stress(sim);	/* Eq. 9 */
    822 
    823 			/* step 7 */
    824 			compute_local_fluidity(sim);	/* Eq. 10 */
    825 			compute_cooperativity_length(sim);	/* Eq. 12 */
    826 
    827 			/* step 8, Eq. 11 */
    828 			if (implicit_1d_jacobian_poisson_solver(sim,
    829 			                                        MAX_ITER_GRANULAR,
    830 			                                        rel_tol))
    831 				exit(12);
    832 
    833 			/* step 9 */
    834 			compute_shear_strain_rate_plastic(sim);	/* Eq. 8 */
    835 			compute_inertia_number(sim);	/* Eq. 1 */
    836 			compute_shear_velocity(sim);
    837 
    838 #ifdef DEBUG
    839 			/* for (i = 0; i < sim->nz; ++i) { */
    840 			for (i = sim->nz-1; i < sim->nz; ++i) {
    841 				printf("\nsim->t = %g s\n", sim->t);
    842 				printf("sim->I[%d] = %g\n", i, sim->I[i]);
    843 				printf("sim->phi_c[%d] = %g\n", i, sim->phi_c[i]);
    844 				printf("sim->tan_psi[%d] = %g\n", i, sim->tan_psi[i]);
    845 				printf("sim->mu_c[%d] = %g\n", i, sim->mu_c[i]);
    846 				printf("sim->phi[%d] = %g\n", i, sim->phi[i]);
    847 				printf("sim->phi_dot[%d] = %g\n", i, sim->phi_dot[i]);
    848 				printf("sim->k[%d] = %g\n", i, sim->k[i]);
    849 				printf("sim->mu[%d] = %g\n", i, sim->mu[i]);
    850 			}
    851 #endif
    852 
    853 			/* stable porosity change field == coupled solution converged */
    854 			if (sim->transient) {
    855 				for (i = 0; i < sim->nz; ++i)
    856 					sim->g_r_norm[i] = fabs(residual(sim->phi_dot[i], sim->old_val[i]));
    857 				r_norm_max = max(sim->g_r_norm, sim->nz);
    858 				if (r_norm_max <= rel_tol && coupled_iter > 0)
    859 					break;
    860 				if (coupled_iter++ >= max_iter) {
    861 					fprintf(stderr, "coupled_shear_solver: ");
    862 					fprintf(stderr, "Transient solution did not converge "
    863 					        "after %d iterations\n", coupled_iter);
    864 					fprintf(stderr, ".. Residual normalized error: %g\n",
    865 					        r_norm_max);
    866 					return 1;
    867 				}
    868 			}
    869 
    870 		} while (sim->transient);
    871 		if (!isnan(sim->v_x_limit) || !isnan(sim->v_x_fix)) {
    872 			if (!isnan(sim->v_x_limit)) {
    873 				vel_res_norm = (sim->v_x_limit - sim->v_x[sim->nz - 1])
    874 				               / (sim->v_x[sim->nz - 1] + 1e-12);
    875 				if (vel_res_norm > 0.0)
    876 					vel_res_norm = 0.0;
    877 			} else {
    878 				vel_res_norm = (sim->v_x_fix - sim->v_x[sim->nz - 1])
    879 				               / (sim->v_x[sim->nz - 1] + 1e-12);
    880 			}
    881 			sim->mu_wall *= 1.0 + (vel_res_norm * 1e-3);
    882 		}
    883 		if (++stress_iter > max_iter) {
    884 			fprintf(stderr, "error: stress solution did not converge:\n");
    885 			fprintf(stderr, "v_x=%g, v_x_fix=%g, v_x_limit=%g, "
    886 			        "vel_res_norm=%g, mu_wall=%g\n",
    887 			        sim->v_x[sim->nz - 1], sim->v_x_fix, sim->v_x_limit,
    888 			        vel_res_norm, sim->mu_wall);
    889 			return 10;
    890 		}
    891 	} while ((!isnan(sim->v_x_fix) || !isnan(sim->v_x_limit))
    892 	        && fabs(vel_res_norm) > RTOL_VELOCITY);
    893 
    894 	if (!isnan(sim->v_x_limit))
    895 		sim->mu_wall = mu_wall_orig;
    896 
    897 	temporal_increment(sim);
    898 
    899 	return 0;
    900 }
    901 
    902 double
    903 find_flux(const struct simulation *sim)
    904 {
    905 	int i;
    906 	double flux = 0.0;
    907 
    908 	for (i = 1; i < sim->nz; ++i)
    909 		flux += (sim->v_x[i] + sim->v_x[i - 1]) / 2.0 * sim->dz;
    910 
    911 	return flux;
    912 }
    913 
    914 void
    915 set_coupled_fluid_transient_timestep(struct simulation *sim, const double safety)
    916 {
    917 	double max_gamma_dot, mu, xi, max_I, dt;
    918 
    919 	/* max expected strain rate */
    920 	max_gamma_dot = 1.0/sim->d;
    921 	if (!isnan(sim->v_x_fix))
    922 		max_gamma_dot = sim->v_x_fix / sim->dz;
    923 	if (!isnan(sim->v_x_limit))
    924 		max_gamma_dot = sim->v_x_limit / sim->dz;
    925 
    926 	/* estimate for shear friction */
    927 	mu = (sim->mu_wall / ((sim->sigma_n[sim->nz-1] - sim->p_f_mod_ampl)
    928 	     / (sim->P_wall - sim->p_f_top)))
    929 	     + sim->dilatancy_constant * sim->phi[sim->nz-1];
    930 
    931 	/* estimate for cooperativity length */
    932 	xi = cooperativity_length(sim->A,
    933 	                          sim->d,
    934 	                          mu,
    935 	                          (sim->sigma_n[sim->nz - 1] - sim->p_f_mod_ampl),
    936 	                          sim->mu_s,
    937 	                          sim->C);
    938 
    939 	/* max expected inertia number */
    940 	max_I = inertia_number(max_gamma_dot,
    941 						   sim->d,
    942 						   sim->sigma_n[sim->nz - 1] - sim->p_f_mod_ampl,
    943 						   sim->rho_s);
    944 
    945 	dt = xi * (sim->alpha + sim->phi[sim->nz - 1] * sim->beta_f) * sim->mu_f
    946 	     / (sim->phi[sim->nz - 1] * sim->phi[sim->nz - 1]
    947 	        * sim->phi[sim->nz - 1] * sim->L_z * max_I);
    948 
    949 	if (sim->dt > safety * dt)
    950 		sim->dt = safety * dt;
    951 }