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

fluid.c (13556B)


      1 #include <stdlib.h>
      2 #include <math.h>
      3 #include <err.h>
      4 #include "simulation.h"
      5 #include "arrays.h"
      6 
      7 void
      8 hydrostatic_fluid_pressure_distribution(struct simulation *sim)
      9 {
     10 	int i;
     11 
     12 	for (i = 0; i < sim->nz; ++i)
     13 		sim->p_f_ghost[i + 1] = sim->p_f_top
     14 		                        + sim->phi[i] * sim->rho_f * sim->G
     15 		                        * (sim->L_z - sim->z[i]);
     16 }
     17 
     18 static double
     19 diffusivity(struct simulation *sim, int i) {
     20 	if (sim->D > 0.0)
     21 		return sim->D;
     22 	else
     23 		return sim->k[i]
     24 		       / ((sim->alpha + sim->phi[i] * sim->beta_f) * sim->mu_f);
     25 }
     26 
     27 /* Determines the largest time step for the current simulation state. Note
     28  * that the time step should be recalculated if cell sizes or
     29  * diffusivities (i.e., permeabilities, porosities, viscosities, or
     30  * compressibilities) change. The safety factor should be in ]0;1] */
     31 int
     32 set_largest_fluid_timestep(struct simulation *sim, const double safety)
     33 {
     34 	int i;
     35 	double dx_min, diff, diff_max, *dx;
     36 
     37 	dx = spacing(sim->z, sim->nz);
     38 	dx_min = INFINITY;
     39 	for (i = 0; i < sim->nz - 1; ++i) {
     40 		if (dx[i] < 0.0) {
     41 			fprintf(stderr, "error: cell spacing negative (%g) in cell %d\n",
     42 			        dx[i], i);
     43 			free(dx);
     44 			return 1;
     45 		}
     46 		if (dx[i] < dx_min)
     47 			dx_min = dx[i];
     48 	}
     49 	free(dx);
     50 
     51 	diff_max = -INFINITY;
     52 	for (i = 0; i < sim->nz; ++i) {
     53 		diff = diffusivity(sim, i);
     54 		if (diff > diff_max)
     55 			diff_max = diff;
     56 	}
     57 
     58 	sim->dt = safety * 0.5 * dx_min * dx_min / diff_max;
     59 	if (sim->file_dt * 0.5 < sim->dt)
     60 	    sim->dt = sim->file_dt;
     61 
     62 	return 0;
     63 }
     64 
     65 static double
     66 sine_wave(const double time,
     67           const double ampl,
     68           const double freq,
     69           const double phase)
     70 {
     71 	return ampl * sin(2.0 * PI * freq * time + phase);
     72 }
     73 
     74 static double
     75 triangular_pulse(const double time,
     76                  const double peak_ampl,
     77                  const double freq,
     78                  const double peak_time)
     79 {
     80 	if (peak_time - 1.0 / (2.0 * freq) < time && time <= peak_time)
     81 		return peak_ampl * 2.0 * freq * (time - peak_time) + peak_ampl;
     82 	else if (peak_time < time && time < peak_time + 1.0 / (2.0 * freq))
     83 		return peak_ampl * 2.0 * freq * (peak_time - time) + peak_ampl;
     84 	else
     85 		return 0.0;
     86 }
     87 
     88 static double
     89 square_pulse(const double time,
     90              const double peak_ampl,
     91              const double freq,
     92              const double peak_time)
     93 {
     94 	if (peak_time - 1.0 / (2.0 * freq) < time &&
     95 	    time < peak_time + 1.0 / (2.0 * freq))
     96 		return peak_ampl;
     97 	else
     98 		return 0.0;
     99 }
    100 
    101 static void
    102 set_fluid_bcs(double *p_f_ghost, struct simulation *sim, const double p_f_top)
    103 {
    104 	/* correct ghost node at top BC for hydrostatic pressure gradient */
    105 	set_bc_dirichlet(p_f_ghost, sim->nz, +1,
    106 	                 p_f_top - sim->phi[0] * sim->rho_f * sim->G * sim->dz);
    107 	p_f_ghost[sim->nz] = p_f_top;	/* include top node in BC */
    108 	set_bc_neumann(p_f_ghost,
    109 	               sim->nz,
    110 	               -1,
    111 	               sim->phi[0] * sim->rho_f * sim->G,
    112 	               sim->dz);
    113 }
    114 
    115 static double
    116 darcy_pressure_change_1d(const int i,
    117                          const int nz,
    118                          const double *p_f_ghost_in,
    119                          const double *phi,
    120                          const double *phi_dot,
    121                          const double *k,
    122                          const double dz,
    123                          const double beta_f,
    124                          const double alpha,
    125                          const double mu_f,
    126                          const double D)
    127 {
    128 	double k_, div_k_grad_p, k_zn, k_zp;
    129 
    130 	if (D > 0.0)
    131 		return D * (p_f_ghost_in[i + 2]
    132 		            - 2.0 * p_f_ghost_in[i + 1]
    133 		            + p_f_ghost_in[i]) / (dz * dz);
    134 	else {
    135 		k_ = k[i];
    136 		if (i == 0)
    137 			k_zn = k_;
    138 		else
    139 			k_zn = k[i - 1];
    140 		if (i == nz - 1)
    141 			k_zp = k_;
    142 		else
    143 			k_zp = k[i + 1];
    144 
    145 		div_k_grad_p = (2.0 * k_zp * k_ / (k_zp + k_)
    146 		                * (p_f_ghost_in[i + 2] - p_f_ghost_in[i + 1]) / dz
    147 		                - 2.0 * k_zn * k_ / (k_zn + k_)
    148 		                * (p_f_ghost_in[i + 1] - p_f_ghost_in[i]) / dz
    149 ) / dz;
    150 
    151 #ifdef DEBUG
    152 		printf("%s [%d]: phi=%g\tdiv_k_grad_p=%g\tphi_dot=%g\n",
    153 		        __func__, i, phi[i], div_k_grad_p, phi_dot[i]);
    154 
    155 		printf("  p[%d, %d, %d] = [%g, %g, %g]\tk=[%g, %g, %g]\n",
    156 		       i, i + 1, i + 2,
    157 		       p_f_ghost_in[i], p_f_ghost_in[i + 1], p_f_ghost_in[i + 2],
    158 		       k_zn, k_, k_zp);
    159 #endif
    160 
    161 		return 1.0 / ((alpha + beta_f * phi[i]) * mu_f) * div_k_grad_p
    162 		       - 1.0 / ((alpha + beta_f * phi[i]) * (1.0 - phi[i])) * phi_dot[i];
    163 	}
    164 }
    165 
    166 static double
    167 darcy_pressure_change_1d_impl(const int i,
    168                               const int nz,
    169                               const double dt,
    170                               const double *p_f_old_val,
    171                               const double *p_f_ghost_in,
    172                               double *p_f_ghost_out,
    173                               const double *phi,
    174                               const double *phi_dot,
    175                               const double *k,
    176                               const double dz,
    177                               const double beta_f,
    178                               const double alpha,
    179                               const double mu_f,
    180                               const double D)
    181 {
    182 	double k_, div_k_grad_p, k_zn, k_zp, rhs_term, omega = 1.0;
    183 
    184 	if (D > 0.0)
    185 		return D * (p_f_ghost_in[i + 2]
    186 		            - 2.0 * p_f_ghost_in[i + 1]
    187 		            + p_f_ghost_in[i]) / (dz * dz);
    188 	else {
    189 		k_ = k[i];
    190 		if (i == 0)
    191 			k_zn = k_;
    192 		else
    193 			k_zn = k[i - 1];
    194 		if (i == nz - 1)
    195 			k_zp = k_;
    196 		else
    197 			k_zp = k[i + 1];
    198 
    199 		rhs_term = dt / ((alpha + beta_f * phi[i]) * mu_f)
    200 		           * ((2.0 * k_zp * k_ / (k_zp + k_) / (dz * dz))
    201 		              + (2.0 * k_zn * k_ / (k_zn + k_) / (dz * dz)));
    202 
    203 		p_f_ghost_out[i + 1] = 1.0 / (1.0 + rhs_term)
    204 		                       * (p_f_old_val[i + 1] + dt
    205 		                       * (1.0 / ((alpha + beta_f * phi[i]) * mu_f)
    206 		                       * (2.0 * k_zp * k_ / (k_zp + k_)
    207 		                       * (p_f_ghost_in[i + 2]) / dz
    208 		                       - 2.0 * k_zn * k_ / (k_zn + k_)
    209 		                       * -p_f_ghost_in[i] / dz) / dz
    210 		                       - 1.0 / ((alpha + beta_f * phi[i])
    211 		                       * (1.0 - phi[i])) * phi_dot[i]));
    212 		p_f_ghost_out[i + 1] = omega * p_f_ghost_out[i + 1]
    213 		                       + (1.0 - omega) * p_f_ghost_in[i + 1];
    214 
    215 		div_k_grad_p = (2.0 * k_zp * k_ / (k_zp + k_)
    216 		                 * (p_f_ghost_out[i + 2] - p_f_ghost_out[i + 1]) / dz
    217 		                 - 2.0 * k_zn * k_ / (k_zn + k_)
    218 		                 * (p_f_ghost_out[i + 1] - p_f_ghost_out[i]) / dz
    219 		                ) / dz;
    220 #ifdef DEBUG
    221 		printf("%s [%d]: phi=%g\tdiv_k_grad_p=%g\tphi_dot=%g\n",
    222 		        __func__, i, phi[i], div_k_grad_p, phi_dot[i]);
    223 
    224 		printf("  p[%d, %d, %d] = [%g, %g, %g]\tk=[%g, %g, %g]\n",
    225 		       i, i + 1, i + 2,
    226 		       p_f_ghost_in[i], p_f_ghost_in[i + 1], p_f_ghost_in[i + 2],
    227 		       k_zn, k_, k_zp);
    228 #endif
    229 		/* use the values from the next time step as the time derivative for this iteration */
    230 		return 1.0 / ((alpha + beta_f * phi[i]) * mu_f) * div_k_grad_p
    231 		       - 1.0 / ((alpha + beta_f * phi[i]) * (1.0 - phi[i])) * phi_dot[i];
    232 	}
    233 }
    234 
    235 int
    236 darcy_solver_1d(struct simulation *sim,
    237                 const int max_iter,
    238                 const double rel_tol)
    239 {
    240 	int i, iter, solved = 0;
    241 	double epsilon, p_f_top, omega, r_norm_max = NAN, *k_n, *phi_n;
    242 
    243 	copy_values(sim->p_f_dot, sim->p_f_dot_old, sim->nz);
    244 
    245 	/* choose integration method, parameter in [0.0; 1.0]
    246 	 * epsilon = 0.0: explicit
    247 	 * epsilon = 0.5: Crank-Nicolson
    248 	 * epsilon = 1.0: implicit */
    249 	epsilon = 0.5;
    250 
    251 	/* underrelaxation parameter in ]0.0; 1.0],
    252 	 * where omega = 1.0: no underrelaxation */
    253 	omega = 1.0;
    254 
    255 	for (i = 0; i < sim->nz; ++i)
    256 		sim->p_f_dot_impl[i] = sim->p_f_dot_expl[i] = 0.0;
    257 
    258 	if (isnan(sim->p_f_mod_pulse_time))
    259 		p_f_top = sim->p_f_top
    260 		          + sine_wave(sim->t,
    261 		                      sim->p_f_mod_ampl,
    262 		                      sim->p_f_mod_freq,
    263 		                      sim->p_f_mod_phase);
    264 	else if (sim->p_f_mod_pulse_shape == 1)
    265 		p_f_top = sim->p_f_top
    266 		          + square_pulse(sim->t,
    267 		                         sim->p_f_mod_ampl,
    268 		                         sim->p_f_mod_freq,
    269 		                         sim->p_f_mod_pulse_time);
    270 	else
    271 		p_f_top = sim->p_f_top
    272 		          + triangular_pulse(sim->t,
    273 		                             sim->p_f_mod_ampl,
    274 		                             sim->p_f_mod_freq,
    275 		                             sim->p_f_mod_pulse_time);
    276 
    277 	/* set fluid BCs (1 of 2) */
    278 	set_fluid_bcs(sim->p_f_ghost, sim, p_f_top);
    279 	set_fluid_bcs(sim->p_f_next_ghost, sim, p_f_top);
    280 
    281 	/* explicit solution to pressure change */
    282 	if (epsilon < 1.0) {
    283 #ifdef DEBUG
    284 		printf("\nEXPLICIT SOLVER IN %s\n", __func__);
    285 #endif
    286 		for (i = 0; i < sim->nz - 1; ++i)
    287 			sim->p_f_dot_expl[i] = darcy_pressure_change_1d(i,
    288 			                                                sim->nz,
    289 			                                                sim->p_f_ghost,
    290 			                                                sim->phi,
    291 			                                                sim->phi_dot,
    292 			                                                sim->k,
    293 			                                                sim->dz,
    294 			                                                sim->beta_f,
    295 			                                                sim->alpha,
    296 			                                                sim->mu_f,
    297 			                                                sim->D);
    298 	}
    299 
    300 	/* implicit solution with Jacobian iterations */
    301 	if (epsilon > 0.0) {
    302 
    303 #ifdef DEBUG
    304 		printf("\nIMPLICIT SOLVER IN %s\n", __func__);
    305 #endif
    306 
    307 		k_n = zeros(sim->nz);
    308 		phi_n = zeros(sim->nz);
    309 		if (sim->transient)
    310 			for (i = 0; i < sim->nz; ++i) {
    311 				/* grabbing the n + 1 iteration values for k and phi */
    312 				phi_n[i] = sim->phi[i] + sim->dt * sim->phi_dot[i];
    313 				k_n[i] = kozeny_carman(sim->d, phi_n[i]);
    314 			}
    315 		else
    316 			for (i = 0; i < sim->nz; ++i) {
    317 				phi_n[i] = sim->phi[i];
    318 				k_n[i] = sim->k[i];
    319 			}
    320 		copy_values(sim->p_f_next_ghost, sim->tmp_ghost, sim->nz + 2);
    321 
    322 		for (iter = 0; iter < max_iter; ++iter) {
    323 			copy_values(sim->p_f_dot_impl, sim->fluid_old_val, sim->nz);
    324 
    325 #ifdef DEBUG
    326 			puts(".. p_f_ghost bfore BC:");
    327 			print_array(sim->tmp_ghost, sim->nz + 2);
    328 #endif
    329 
    330 			/* set fluid BCs (2 of 2) */
    331 			set_fluid_bcs(sim->tmp_ghost, sim, p_f_top);
    332 
    333 #ifdef DEBUG
    334 			puts(".. p_f_ghost after BC:");
    335 			print_array(sim->tmp_ghost, sim->nz + 2);
    336 #endif
    337 
    338 			for (i = 0; i < sim->nz - 1; ++i)
    339 				sim->p_f_dot_impl[i] = darcy_pressure_change_1d_impl(i,
    340 				                                                     sim->nz,
    341 				                                                     sim->dt,
    342 				                                                     sim->p_f_ghost,
    343 				                                                     sim->tmp_ghost,
    344 				                                                     sim->p_f_next_ghost,
    345 				                                                     phi_n,
    346 				                                                     sim->phi_dot,
    347 				                                                     k_n,
    348 				                                                     sim->dz,
    349 				                                                     sim->beta_f,
    350 				                                                     sim->alpha,
    351 				                                                     sim->mu_f,
    352 				                                                     sim->D);
    353 
    354 			for (i = 0; i < sim->nz; ++i)
    355 				if (isnan(sim->p_f_dot_impl[i]))
    356 					errx(1, "NaN at sim->p_f_dot_impl[%d] (t = %g s, iter = %d)",
    357 						 i, sim->t, iter);
    358 
    359 			set_fluid_bcs(sim->p_f_next_ghost, sim, p_f_top);
    360 			for (i = 0; i < sim->nz-1; ++i)
    361 				sim->p_f_dot_impl_r_norm[i] = fabs(residual(sim->p_f_next_ghost[i],
    362 				                                            sim->tmp_ghost[i]));
    363 			r_norm_max = max(sim->p_f_dot_impl_r_norm, sim->nz - 1);
    364 			copy_values(sim->p_f_next_ghost, sim->tmp_ghost, sim->nz + 2);
    365 
    366 #ifdef DEBUG
    367 			puts(".. p_f_ghost_new:");
    368 			print_array(sim->tmp_ghost, sim->nz + 2);
    369 #endif
    370 
    371 			if (r_norm_max <= rel_tol) {
    372 #ifdef DEBUG
    373 				printf(".. Iterative solution converged after %d iterations\n", iter);
    374 #endif
    375 				solved = 1;
    376 				break;
    377 			}
    378 		}
    379 		free(k_n);
    380 		free(phi_n);
    381 		if (!solved) {
    382 			fprintf(stderr, "darcy_solver_1d: ");
    383 			fprintf(stderr, "Solution did not converge after %d iterations\n",
    384 			        iter);
    385 			fprintf(stderr, ".. Residual normalized error: %f\n", r_norm_max);
    386 		}
    387 	} else
    388 		solved = 1;
    389 
    390 	for (i = 0; i < sim->nz; ++i)
    391 		sim->p_f_dot[i] = epsilon * sim->p_f_dot_impl[i]
    392 		                  + (1.0 - epsilon) * sim->p_f_dot_expl[i];
    393 
    394 	for (i = 0; i < sim->nz; ++i)
    395 		    sim->p_f_dot[i] = omega * sim->p_f_dot[i]
    396 		                      + (1.0 - omega) * sim->p_f_dot_old[i];
    397 
    398 	for (i = 0; i < sim->nz-1; ++i)
    399 		sim->p_f_next_ghost[i + 1] = sim->p_f_dot[i] * sim->dt + sim->p_f_ghost[i + 1];
    400 
    401 	set_fluid_bcs(sim->p_f_ghost, sim, p_f_top);
    402 	set_fluid_bcs(sim->p_f_next_ghost, sim, p_f_top);
    403 #ifdef DEBUG
    404 	printf(".. epsilon = %g\n", epsilon);
    405 	puts(".. p_f_dot_expl:");
    406 	print_array(sim->p_f_dot_expl, sim->nz);
    407 	puts(".. p_f_dot_impl:");
    408 	print_array(sim->p_f_dot_impl, sim->nz);
    409 #endif
    410 
    411 	for (i = 0; i < sim->nz; ++i)
    412 		if (isnan(sim->p_f_dot_expl[i]) || isinf(sim->p_f_dot_expl[i]))
    413 			errx(1, "invalid: sim->p_f_dot_expl[%d] = %g (t = %g s)",
    414 				 i, sim->p_f_dot_expl[i], sim->t);
    415 
    416 	for (i = 0; i < sim->nz; ++i)
    417 		if (isnan(sim->p_f_dot_impl[i]) || isinf(sim->p_f_dot_impl[i]))
    418 			errx(1, "invalid: sim->p_f_dot_impl[%d] = %g (t = %g s)",
    419 				 i, sim->p_f_dot_impl[i], sim->t);
    420 
    421 	return solved - 1;
    422 }