1d_fd_simple_shear

Continuum model for granular flows with pore-pressure dynamics
git clone git://src.adamsgaard.dk/1d_fd_simple_shear
Log | Files | Refs | README | LICENSE

commit 76f099629e26cfd4d8338b5862856ebc974d4e74
parent 9e079cfadd9105a19d6d651546fd09fe069612e3
Author: Anders Damsgaard <anders@adamsgaard.dk>
Date:   Mon, 29 Apr 2019 15:02:51 +0000

Merge branch 'darcy' into 'master'

Darcian diffusion of porewater

See merge request admesg/1d_fd_simple_shear!1
Diffstat:
M.gitlab-ci.yml | 2+-
M1d_fd_simple_shear.png | 0
D1d_fd_simple_shear_damsgaard2013.h | 66------------------------------------------------------------------
A1d_fd_simple_shear_fluid.gp | 35+++++++++++++++++++++++++++++++++++
M1d_fd_simple_shear_henann_kamrin2016.h | 2+-
M1d_fd_simple_shear_rheology.png | 0
M1d_fd_simple_shear_rheology_iverson.png | 0
M1d_fd_simple_shear_rheology_kamb.png | 0
M1d_fd_simple_shear_rheology_tulaczyk.png | 0
MMakefile | 19++++++++++++++++++-
MREADME.md | 46+++++++++++++++++++++++++++++++++++++++++++++-
Marrays.c | 46+++++++++++++++++++++++++++++++++++++++++++---
Marrays.h | 23++++++++++++++++++++---
Adiurnal.gif | 0
Afluid.c | 185+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Afluid.h | 13+++++++++++++
Djulia/1d_fd_simple_shear.jl | 277-------------------------------------------------------------------------------
Djulia/1d_fd_simple_shear.png | 0
Djulia/Makefile | 7-------
Mmain.c | 203++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Aparameter_defaults.h | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msimulation.c | 244++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msimulation.h | 51+++++++++++++++++++++++++++++++++++++++++++++------
23 files changed, 878 insertions(+), 412 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml @@ -4,7 +4,7 @@ build-alpine: before_script: - apk --no-cache add build-base gnuplot bash script: - - make + - make plots artifacts: paths: - 1d_fd_simple_shear.png diff --git a/1d_fd_simple_shear.png b/1d_fd_simple_shear.png Binary files differ. diff --git a/1d_fd_simple_shear_damsgaard2013.h b/1d_fd_simple_shear_damsgaard2013.h @@ -1,66 +0,0 @@ -#ifndef ONED_FD_SIMPLE_SHEAR_ -#define ONED_FD_SIMPLE_SHEAR_ - -#include <math.h> -#include <stdio.h> -#include "arrays.h" -#include "simulation.h" - -#define PI 3.14159265358979323846 -#define DEG2RAD(x) (x*PI/180.0) - -/* Simulation settings */ -struct simulation init_sim(void) -{ - struct simulation sim; - - sim.G = 9.81; - - sim.P_wall = 120e3; /* larger normal stress deepens the shear depth */ - sim.mu_wall = 0.40; - sim.v_x_bot = 0.0; - - sim.nz = 100; - - /* lower values of A mean that the velocity curve can have sharper curves, - * e.g. at the transition from μ ≈ μ_s */ - sim.A = 0.40; /* Loose fit to Damsgaard et al 2013 */ - - /* lower values of b mean larger shear velocity for a given stress ratio - * above yield */ - sim.b = 0.9377; /* Henann and Kamrin 2016 */ - - sim.mu_s = atan(DEG2RAD(22.0)); /* Damsgaard et al 2013 */ - - sim.phi = 0.25; /* Damsgaard et al 2013 */ - - /* lower values of d mean that the shear velocity curve can have sharper - * curves, e.g. at the transition from μ ≈ μ_s */ - sim.d = 0.04; /* Damsgaard et al 2013 */ - - /* grain material density [kg/m^3] */ - sim.rho_s = 2.6e3; /* Damsgaard et al 2013 */ - - /* Spatial settings */ - sim.origo_z = 0.0; - sim.L_z = 0.7; /* Damsgaard et al 2013 */ - - return sim; -} - -void init_pressure(struct simulation* sim) -{ - for (int i=0; i<sim->nz; ++i) - sim->p[i] = sim->P_wall + - (1.0 - sim->phi)*sim->rho_s*sim->G*(sim->L_z - sim->z[i]); -} - -void init_friction(struct simulation* sim) -{ - for (int i=0; i<sim->nz; ++i) - sim->mu[i] = sim->mu_wall / - (1.0 + (1.0 - sim->phi)*sim->rho_s*sim->G*(sim->L_z - sim->z[i])/ - sim->P_wall); -} - -#endif diff --git a/1d_fd_simple_shear_fluid.gp b/1d_fd_simple_shear_fluid.gp @@ -0,0 +1,35 @@ +#!/usr/bin/env gnuplot + +# specify input file with: +# gnuplot -e "filename='file.txt'" 1d_fd_simple_shear_fluid.gp + +set terminal pngcairo color size 60 cm, 17.6 cm +set multiplot layout 1,3 +# set multiplot layout 1,4 + + +set yrange [0.0:2.0] + +set key bottom right #samplen 0.9 + +set xlabel "Water pressure, p_f [Pa]" +set ylabel "Vertical position, z [m]" offset 2 +set xrange [p_min:p_max] +plot filename u 4:1 w l lw 2 lc "blue" t "" + +set xlabel "Effective normal stress [Pa]" +set ylabel "" +set xrange [0:200e3] +plot filename u 3:1 w l lw 2 lc "black" t "" + +# set xlabel "Friction, mu [-]" +# set xrange [-0.05:1.5] +# plot filename u 5:1 w l lw 2 lc "gray" t "" + +set xlabel "Horizontal velocity, v_x [m/s]" +set ylabel "" +set xrange [0.0:0.025] +plot filename u 2:1 w l lw 2 lc "red" t "" + + +unset multiplot diff --git a/1d_fd_simple_shear_henann_kamrin2016.h b/1d_fd_simple_shear_henann_kamrin2016.h @@ -23,7 +23,7 @@ struct simulation init_sim(void) sim.A = 0.48; sim.b = 0.9377; sim.mu_s = 0.3819; - sim.phi = 0.38; + sim.phi = initval(0.38, sim.nz); sim.d = 1e-3; sim.rho_s = 2.485e3; diff --git a/1d_fd_simple_shear_rheology.png b/1d_fd_simple_shear_rheology.png Binary files differ. diff --git a/1d_fd_simple_shear_rheology_iverson.png b/1d_fd_simple_shear_rheology_iverson.png Binary files differ. diff --git a/1d_fd_simple_shear_rheology_kamb.png b/1d_fd_simple_shear_rheology_kamb.png Binary files differ. diff --git a/1d_fd_simple_shear_rheology_tulaczyk.png b/1d_fd_simple_shear_rheology_tulaczyk.png Binary files differ. diff --git a/Makefile b/Makefile @@ -4,12 +4,29 @@ SRC=$(wildcard *.c) OBJ=$(patsubst %.c,%.o,$(SRC)) HDR=$(wildcard *.h) -default: 1d_fd_simple_shear.png \ +.PHONY: default +default: 1d_fd_simple_shear + +.PHONY: plots +plots: 1d_fd_simple_shear.png \ 1d_fd_simple_shear_rheology.png \ 1d_fd_simple_shear_rheology_kamb.png \ 1d_fd_simple_shear_rheology_iverson.png \ 1d_fd_simple_shear_rheology_tulaczyk.png +diurnal.mp4: 1d_fd_simple_shear 1d_fd_simple_shear_fluid.gp + /usr/bin/env zsh -c '\ + ./$< --resolution 50 --length 2.0 --normal-stress 150e3 --fluid --fluid-permeability 1e-17 --fluid-pressure-top 50e3 --fluid-pressure-ampl 50e3 --fluid-pressure-freq $$(( 1.0/(3600*24) )) --time-step 1e-1 --file-interval $$(( 60*10 )) --time-end $$(( 3600*24*4 )) diurnal' + /bin/bash -c '\ + for f in diurnal.output*.txt; do \ + gnuplot -e "filename=\"$$f\"; p_min=\"0\"; p_max=\"100e3\"" $<_fluid.gp > $$f.png; \ + done' + ffmpeg -i diurnal.output%05d.txt.png -y diurnal.mp4 + +diurnal.gif: diurnal.mp4 + convert diurnal.output*.txt.png \ + -delay 1 -loop 0 -fuzz 10% -layers Optimize $@ + 1d_fd_simple_shear: $(OBJ) $(HDR) $(CC) $(LDFLAGS) $(OBJ) -o $@ diff --git a/README.md b/README.md @@ -12,6 +12,44 @@ directory and an output PNG figure is generated. Alternatively, an implementation in [Julia](https://julialang.org) resides in **julia/**. +### Advanced usage +The majority of simulation parameters can be adjusted from the command line: + +``` +usage: 1d_fd_simple_shear [OPTIONS] [NAME] +runs a simulation and outputs state in files prefixed with NAME. +optional arguments: + -N, --normalize normalize output velocity + -G, --gravity VAL gravity magnitude [m/s^2] + -P, --normal-stress VAL normal stress on top [Pa] + -m, --stress-ratio VAL applied stress ratio [-] + -V, --velocity-bottom VAL base velocity at bottom [m/s] + -A, --nonlocal-amplitude VAL amplitude of nonlocality [-] + -b, --rate-dependence VAL rate dependence beyond yield [-] + -f, --friction-coefficient VAL grain friction coefficient [-] + -p, --porosity VAL porosity fraction [-] + -d, --grain-size VAL representative grain size [m] + -r, --density VAL grain material density [kg/m^3] + -n, --resolution VAL number of cells in domain [-] + -o, --origo VAL coordinate system origo [m] + -L, --length VAL domain length [m] + -F, --fluid enable pore fluid computations + -c, --fluid-compressibility VAL fluid compressibility [Pa^-1] + -i, --fluid-viscosity VAL fluid viscosity [Pa*s] + -R, --fluid-density VAL fluid density [kg/m^3] + -k, --fluid-permeability VAL fluid intrinsic permeability [m^2] + -O, --fluid-pressure-top VAL fluid pressure at +z edge [Pa] + -a, --fluid-pressure-ampl VAL amplitude of pressure variations [Pa] + -q, --fluid-pressure-freq VAL frequency of pressure variations [s^-1] + -H, --fluid-pressure-phase VAL fluid pressure at +z edge [Pa] + -t, --time VAL simulation start time [s] + -T, --time-end VAL simulation end time [s] + -D, --time-step VAL computational time step length [s] + -I, --file-interval VAL interval between output files [s] + -v, --version show version information + -h, --help show this message +``` + ## Results ### Strain distribution @@ -26,7 +64,6 @@ probably be improved further. | ![damsgaard2013-fig8.png](https://gitlab.com/admesg/1d_fd_simple_shear/raw/master/damsgaard2013-fig8.png) | ![1d_fd_simple_shear.png](https://gitlab.com/admesg/1d_fd_simple_shear/raw/master/1d_fd_simple_shear.png) | ### Stress and strain rate - The rheology is of Bingham type, where no deformation occurs beneath the Mohr-Coulomb yield limit. Above it, deformation is highly non-linearly viscous. The model has a parameter *b* for rate dependence beyond yield. Glass beads @@ -41,3 +78,10 @@ have *b* = 0.94 ([Forterre and Pouliquen | ![iverson2010-fig2a.png](https://gitlab.com/admesg/1d_fd_simple_shear/raw/master/iverson2010-fig2a.png) | ![1d_fd_simple_shear_rheology_iverson.png](https://gitlab.com/admesg/1d_fd_simple_shear/raw/master/1d_fd_simple_shear_rheology_iverson.png) | | Whillans Ice Plain ([Tulaczyk 2006](https://doi.org/10.3189/172756506781828601)): | | | ![tulaczyk2006-fig1.png](https://gitlab.com/admesg/1d_fd_simple_shear/raw/master/tulaczyk2006-fig1.png) | ![1d_fd_simple_shear_rheology_tulaczyk.png](https://gitlab.com/admesg/1d_fd_simple_shear/raw/master/1d_fd_simple_shear_rheology_tulaczyk.png) | + +### Variable water pressure +The model is expanded from the Henann and Kamrin 2013 model by including a +diffusive porewater pressure parameterization. Below is an example of diurnal +water-pressure variations that gradually propagate into the bed. + +![diurnal.gif](https://gitlab.com/admesg/1d_fd_simple_shear/raw/master/diurnal.gif) diff --git a/arrays.c b/arrays.c @@ -54,7 +54,7 @@ double* linspace(const double lower, const double upper, const int n) } /* Return an array of `n` values with the value 0.0 */ -double* zeros(const double n) +double* zeros(const int n) { double *x = malloc(n*sizeof(double)); for (int i=0; i<n; ++i) @@ -63,7 +63,7 @@ double* zeros(const double n) } /* Return an array of `n` values with the value 1.0 */ -double* ones(const double n) +double* ones(const int n) { double *x = malloc(n*sizeof(double)); for (int i=0; i<n; ++i) @@ -71,8 +71,17 @@ double* ones(const double n) return x; } +/* Return an array of `n` values with a specified value */ +double* initval(const double value, const int n) +{ + double *x = malloc(n*sizeof(double)); + for (int i=0; i<n; ++i) + x[i] = value; + return x; +} + /* Return an array of `n` uninitialized values */ -double* empty(const double n) +double* empty(const int n) { return malloc(n*sizeof(double)); } @@ -116,6 +125,37 @@ void print_arrays_2nd_normalized(const double* a, const double* b, const int n) printf("%.17g\t%.17g\n", a[i], b[i]/max_b); } +void print_three_arrays( + const double* a, + const double* b, + const double* c, + const int n) +{ + for (int i=0; i<n; ++i) + printf("%.17g\t%.17g\t%.17g\n", a[i], b[i], c[i]); +} + +void fprint_arrays( + FILE* fp, + const double* a, + const double* b, + const int n) +{ + for (int i=0; i<n; ++i) + fprintf(fp, "%.17g\t%.17g\n", a[i], b[i]); +} + +void fprint_three_arrays( + FILE* fp, + const double* a, + const double* b, + const double* c, + const int n) +{ + for (int i=0; i<n; ++i) + fprintf(fp, "%.17g\t%.17g\t%.17g\n", a[i], b[i], c[i]); +} + void copy_values(const double* in, double* out, const int n) { for (int i=0; i<n; ++i) diff --git a/arrays.h b/arrays.h @@ -1,3 +1,5 @@ +#include <stdio.h> + #ifndef ARRAYS_ #define ARRAYS_ @@ -18,9 +20,10 @@ unsigned int idx2g( unsigned int idx1g(const unsigned int i); double* linspace(const double lower, const double upper, const int n); -double* zeros(const double n); -double* ones(const double n); -double* empty(const double n); +double* zeros(const int n); +double* ones(const int n); +double* initval(const double value, const int n); +double* empty(const int n); double max(const double* a, const int n); double min(const double* a, const int n); @@ -28,6 +31,20 @@ double min(const double* a, const int n); void print_array(const double* a, const int n); void print_arrays(const double* a, const double* b, const int n); void print_arrays_2nd_normalized(const double* a, const double* b, const int n); +void print_three_arrays( + const double* a, + const double* b, + const double* c, + const int n); + +void fprint_arrays(FILE* fp, const double* a, const double* b, const int n); + +void fprint_three_arrays( + FILE* fp, + const double* a, + const double* b, + const double* c, + const int n); void copy_values(const double* in, double* out, const int n); diff --git a/diurnal.gif b/diurnal.gif Binary files differ. diff --git a/fluid.c b/fluid.c @@ -0,0 +1,185 @@ +#include <stdlib.h> +#include <math.h> +#include "simulation.h" +#include "arrays.h" + +void hydrostatic_fluid_pressure_distribution(struct simulation* sim) +{ + for (int i=0; i<sim->nz; ++i) + sim->p_f_ghost[idx1g(i)] = sim->p_f_top + + sim->phi[i]*sim->rho_f*sim->G*(sim->L_z - sim->z[i]); +} + +static double sine_wave( + const double time, + const double amplitude, + const double frequency, + const double phase, + const double base_value) +{ + return amplitude*sin(2.0*PI*frequency*time + phase) + base_value; +} + +static double darcy_pressure_change_1d( + const int i, + const int nz, + const double* p_f_ghost_in, + const double* phi, + const double* k, + const double dz, + const double dt, + const double beta_f, + const double mu_f) +{ + const double p = p_f_ghost_in[idx1g(i)]; + const double p_zn = p_f_ghost_in[idx1g(i-1)]; + const double p_zp = p_f_ghost_in[idx1g(i+1)]; + + const double k_ = k[i]; + double k_zn, k_zp; + if (i==0) k_zn = k_; else k_zn = k[i-1]; + if (i==nz-1) k_zp = k_; else k_zp = k[i+1]; +#ifdef DEBUG + printf("%d->%d: p=[%g, %g, %g]\tk=[%g, %g, %g]\n", + i, idx1g(i), + p_zn, p, p_zp, + k_zn, k_, k_zp); +#endif + + const double div_k_grad_p = + (2.0*k_zp*k_/(k_zp + k_) * (p_zp - p)/dz - + 2.0*k_zn*k_/(k_zn + k_) * (p - p_zn)/dz + )/dz; +#ifdef DEBUG + printf("phi[%d]=%g\tdiv_k_grad_p[%d]=%g\n", + i, phi[i], i, div_k_grad_p); +#endif + + /* return delta p */ + return dt/(beta_f*phi[i]*mu_f)*div_k_grad_p; +} + +int darcy_solver_1d( + struct simulation* sim, + const int max_iter, + const double rel_tol) +{ + + /* compute explicit solution to pressure change */ + double* dp_f_expl = zeros(sim->nz); + for (int i=0; i<sim->nz; ++i) + dp_f_expl[i] = darcy_pressure_change_1d( + i, + sim->nz, + sim->p_f_ghost, + sim->phi, + sim->k, + sim->dz, + sim->dt, + sim->beta_f, + sim->mu_f); + + /* choose integration method, parameter in [0.0; 1.0] + * epsilon = 0.0: explicit + * epsilon = 0.5: Crank-Nicolson + * epsilon = 1.0: implicit */ + const double epsilon = 0.5; + + /* choose relaxation factor, parameter in ]0.0; 1.0] + * theta in ]0.0; 1.0]: underrelaxation + * theta = 1.0: Gauss-Seidel + * theta > 1.0: overrrelaxation */ + const double theta = 0.05; + /* const double theta = 1.7; */ + + double p_f; + + /* compute implicit solution to pressure change */ + int iter; + double* dp_f_impl = zeros(sim->nz); + double* p_f_ghost_out = zeros(sim->nz+2); + double* r_norm = zeros(sim->nz); + double r_norm_max = NAN; + double p_f_top = sine_wave( + sim->t, + sim->p_f_mod_ampl, + sim->p_f_mod_freq, + sim->p_f_mod_phase, + sim->p_f_top); + + + for (iter=0; iter<max_iter; ++iter) { + + set_bc_dirichlet(sim->p_f_ghost, sim->nz, +1, p_f_top); + sim->p_f_ghost[idx1g(sim->nz-1)] = p_f_top; /* Include top node in BC */ + set_bc_neumann(sim->p_f_ghost, sim->nz, -1); +#ifdef DEBUG + puts(".. p_f_ghost after BC:"); print_array(sim->p_f_ghost, sim->nz+2); +#endif + + /* for (int i=0; i<sim->nz; ++i) */ + for (int i=0; i<sim->nz-1; ++i) + dp_f_impl[i] = darcy_pressure_change_1d( + i, + sim->nz, + sim->p_f_ghost, + sim->phi, + sim->k, + sim->dz, + sim->dt, + sim->beta_f, + sim->mu_f); + /* for (int i=0; i<sim->nz; ++i) { */ + for (int i=0; i<sim->nz-1; ++i) { +#ifdef DEBUG + printf("dp_f_expl[%d] = %g\ndp_f_impl[%d] = %g\n", + i, dp_f_expl[i], i, dp_f_impl[i]); +#endif + + p_f = sim->p_f_ghost[idx1g(i)]; + + p_f_ghost_out[idx1g(i)] = p_f + + (1.0 - epsilon)*dp_f_expl[i] + epsilon*dp_f_impl[i]; + + /* apply relaxation */ + p_f_ghost_out[idx1g(i)] = p_f*(1.0 - theta) + + p_f_ghost_out[idx1g(i)]*theta; + + r_norm[i] = (p_f_ghost_out[idx1g(i)] - p_f)/(p_f + 1e-16); + } + + r_norm_max = max(r_norm, sim->nz); +#ifdef DEBUG + puts(".. p_f_ghost_out:"); print_array(p_f_ghost_out, sim->nz+2); +#endif + + copy_values(p_f_ghost_out, sim->p_f_ghost, sim->nz+2); +#ifdef DEBUG + puts(".. p_f_ghost after update:"); + print_array(sim->p_f_ghost, sim->nz+2); +#endif + + if (r_norm_max <= rel_tol) { + set_bc_dirichlet(sim->p_f_ghost, sim->nz, +1, p_f_top); + sim->p_f_ghost[idx1g(sim->nz-1)] = p_f_top; /* top node in BC */ + set_bc_neumann(sim->p_f_ghost, sim->nz, -1); + free(dp_f_expl); + free(dp_f_impl); + free(p_f_ghost_out); + free(r_norm); +#ifdef DEBUG + printf(".. Solution converged after %d iterations\n", iter); +#endif + return 0; + } + } + + free(dp_f_expl); + free(dp_f_impl); + free(p_f_ghost_out); + free(r_norm); + fprintf(stderr, "darcy_solver_1d: "); + fprintf(stderr, "Solution did not converge after %d iterations\n", iter); + fprintf(stderr, ".. Residual normalized error: %f\n", r_norm_max); + return 1; +} diff --git a/fluid.h b/fluid.h @@ -0,0 +1,13 @@ +#ifndef FLUID_H_ +#define FLUID_H_ + +#include "simulation.h" + +void hydrostatic_fluid_pressure_distribution(struct simulation* sim); + +int darcy_solver_1d( + struct simulation* sim, + const int max_iter, + const double rel_tol); + +#endif diff --git a/julia/1d_fd_simple_shear.jl b/julia/1d_fd_simple_shear.jl @@ -1,277 +0,0 @@ -#!/usr/bin/env julia - -ENV["MPLBACKEND"] = "Agg" -import PyPlot - -let - -# Simulation settings - -## Gravitational acceleration magnitude -G = 9.81 - -## Wall parameters - -### Effective normal stress exerted by top wall -# A larger normal stress deepens the deformational depth -P_wall_ = [10, 20, 40, 60, 80, 120] .* 1e3 # normal stress [Pa] - -### bottom velocity along x [m/s] -v_x_bot = 0.0 - -# stress ratio at top wall -μ_wall = 0.40 - -### Nodes along z -nz = 100 - -## Material properties - -### nonlocal amplitude [-] -# lower values of A mean that the velocity curve can have sharper curves, e.g. -# at the transition from μ ≈ μ_s -#A = 0.48 # Henann and Kamrin 2016 -A = 0.40 # Loose fit to Damsgaard et al 2013 - -### rate dependence beyond yield [-] -# lower values of b mean larger shear velocity for a given stress ratio above -# yield -b = 0.9377 # Henann and Kamrin 2016 - -### bulk and critical state static yield friction coefficient [-] -#μ_s = 0.3819 # Henann and Kamrin 2016 -μ_s = atan(deg2rad(22.0)) # Damsgaard et al 2013 - -### porosity [-] -#ϕ = 0.38 # Henann and Kamrin 2016 -ϕ = 0.25 # Damsgaard et al 2013 - -# representative grain size [m] -# lower values of d mean that the shear velocity curve can have sharper curves, -# e.g. at the transition from μ ≈ μ_s -#d = 1e-3 # Henann and Kamrin 2016 -d = 0.04 # Damsgaard et al 2013 - -### grain material density [kg/m^3] -#ρ_s = 2.485e3 # Henann and Kamrin 2016 -ρ_s = 2.6e3 # Damsgaard et al 2013 - -## Spatial settings -origo_z = 0.0 -#L_z = 20.0*d # Henann and Kamrin 2016 -L_z = 0.7 # Damsgaard et al 2013 -z = collect(range(origo_z, L_z, length=nz)) -Δz = z[2] - z[1] - -## Allocate other arrays -μ = zero(z) # local stress ratio -p = zero(z) # local pressure -v_x = zero(z) # local shear velocity -g_ghost = zeros(size(z)[1]+2) # local fluidity with ghost nodes - - -# Function definitions - -## Shear plastic strain rate (eq 2) -γ_dot_p(g, μ) = g.*μ - -## Normal stress -p_lithostatic(P_wall, z) = P_wall .+ (1 - ϕ).*ρ_s.*G.*(L_z .- z) - -## local cooperativity length -ξ(μ) = A*d./sqrt.(abs.(μ .- μ_s)) - -## Local fluidity -function g_local(p, μ) - if μ <= μ_s - return 0.0 - else - return sqrt(p./ρ_s.*d.^2.0) .* (μ .- μ_s)./(b.*μ) - end -end - -## Update ghost nodes for g from current values -## BC: Neumann (dg/dx = 0) -function set_bc_neumann(g_ghost, boundary) - if boundary == "-z" - g_ghost[1] = g_ghost[2] - elseif boundary == "+z" - g_ghost[end] = g_ghost[end-1] - else - @error "boundary '$boundary' not understood" - end -end - -## Update ghost nodes for g from current values -## BC: Dirichlet (g = 0) -function set_bc_dirichlet(g_ghost, boundary; value=0.0, idx_offset=0) - if boundary == "-z" - g_ghost[1+idx_offset] = value - elseif boundary == "+z" - g_ghost[end-idx_offset] = value - else - @error "boundary '$boundary' not understood" - end -end - -## Compute shear stress from velocity gradient using finite differences -# function shear_stress(v, Δz) -# τ = zero(v) -# -# # compute inner values with central differences -# for i=2:length(v)-1 -# τ[i] = (v[i+1] - v[i-1])/(2.0*Δz) -# end -# -# # use forward/backward finite differences at edges -# τ[1] = (v[2] - v[1])/Δz -# τ[end] = (v[end] - v[end-1])/Δz -# -# return τ -# end - -#friction(τ, p) = τ./(p .+ 1e-16) - -## A single iteration for solving a Poisson equation Laplace(phi) = f on a -## Cartesian grid. The function returns the normalized residual value -function poisson_solver_1d_iteration(g_in, g_out, r_norm, - μ, p, i, Δz, - verbose=false) - - coorp_term = Δz^2.0/(2.0*ξ(μ[i])^2.0) - g_out[i+1] = 1.0/(1.0 + coorp_term) * (coorp_term*g_local(p[i], μ[i]) - + g_in[i+1+1]/2.0 + g_in[i-1+1]/2.0) - r_norm[i] = (g_out[i+1] - g_in[i+1])^2.0 / (g_out[i+1]^2.0 + 1e-16) - - if verbose - println("-- $i -----------------") - println("coorp_term: $coorp_term") - println(" g_local: $(g_local(p[i], μ[i]))") - println(" g_in: $(g_in[i+1])") - println(" g_out: $(g_out[i+1])") - println(" r_norm: $(r_norm[i])") - end -end - -## Iteratively solve the system laplace(phi) = f -function implicit_1d_jacobian_poisson_solver(g, p, μ, Δz, - rel_tol=1e-5, - max_iter=10_000, - verbose=false) - - # allocate second array of g for Jacobian solution procedure - g_out = zero(g) - - if verbose - println("g: ") - println(g) - println() - println("g_out: ") - println(g_out) - end - - # array of normalized residuals - r_norm = zero(p) - r_norm_max = 0.0 - - for iter=1:max_iter - #println("\n@@@ ITERATION $iter @@@") - - set_bc_dirichlet(g, "-z") - set_bc_dirichlet(g, "+z") - #set_bc_neumann(g, "+z") - - if verbose - println("g after BC: ") - println(g) - end - - # perform a single jacobi iteration in each cell - for iz=1:length(p) - poisson_solver_1d_iteration(g, g_out, r_norm, - μ, p, iz, Δz) - end - r_norm_max = maximum(r_norm) - - # Flip-flop arrays - tmp = g - g = g_out - g_out = tmp - - if verbose - @info ".. Relative normalized error: $r_norm_max" - end - - # stop iterating if the relative tolerance is satisfied - if r_norm_max <= rel_tol - @info ".. Solution converged after $iter iterations" - return - end - end - @error "Solution didn't converge after $max_iter iterations ($r_norm_max)" -end - -function shear_velocity(γ_dot, Δz, v_x_bot) - - v_x = zero(γ_dot) - - # BC - v_x[1] = v_x_bot - - for i=2:length(v_x) - v_x[i] = v_x[i-1] + γ_dot[i]*Δz - end - - return v_x -end - -function plot_profile(z, v, label, filename) - PyPlot.figure(figsize=[4,4]) - PyPlot.plot(v, z, "+k") - PyPlot.xlabel(label) - PyPlot.ylabel("\$z\$ [m]") - PyPlot.tight_layout() - PyPlot.savefig(filename) - PyPlot.close() -end - -init_μ(μ_wall, ϕ, ρ_s, G, z, P_wall) = - μ_wall./(1.0 .+ (1.0-ϕ)*ρ_s*G.*(L_z .- z)./P_wall) - - -# Main - -for P_wall in P_wall_ - - ## calculate stresses - p = p_lithostatic(P_wall, z) - μ = init_μ(μ_wall, ϕ, ρ_s, G, z, P_wall) - - ## solve for fluidity - implicit_1d_jacobian_poisson_solver(g_ghost, p, μ, Δz) - - ## calculate shear velocitiesj - γ_dot = γ_dot_p(g_ghost[2:end-1], μ) - v_x = shear_velocity(γ_dot, Δz, v_x_bot) - - ## plot results - P = Int(round(P_wall/1e3)) - PyPlot.plot(v_x/maximum(v_x), z, "+-", label="\$P_{wall}\$ = $P kPa") - # plot_profile(z, v_x, "Shear velocity, \$v_x\$ [m/s]", - # "1d_fd_simple_shear_v_x_P$(P)kPa.png") - # plot_profile(z, μ, "Stress ratio, μ [-]", - # "1d_fd_simple_shear_mu_P$(P)kPa.png") - # plot_profile(z, p, "Normal stress, \$p\$ [Pa]", - # "1d_fd_simple_shear_p_P$(P)kPa.png") - # plot_profile(z, g_ghost[2:end-1], "Fluidity, \$g\$", - # "1d_fd_simple_shear_g_P$(P)kPa.png") -end - -PyPlot.xlabel("Normalized shear displacement, [m]") -PyPlot.ylabel("Vertical position, \$z\$ [m]") -PyPlot.legend() -PyPlot.tight_layout() -PyPlot.savefig("1d_fd_simple_shear.png") -PyPlot.close() - -end # end let diff --git a/julia/1d_fd_simple_shear.png b/julia/1d_fd_simple_shear.png Binary files differ. diff --git a/julia/Makefile b/julia/Makefile @@ -1,7 +0,0 @@ -JULIA=julia --banner=no --color=yes -.PHONY: run-julia -run-julia: 1d_fd_simple_shear.jl - echo "$<" | entr -s '$(JULIA) "$<"' - -1d_fd_simple_shear.png: 1d_fd_simple_shear.jl - $(JULIA) $< diff --git a/main.c b/main.c @@ -4,33 +4,47 @@ #include <getopt.h> #include "simulation.h" +#include "fluid.h" -#define VERSION "0.1" +#define VERSION "0.2" #define PROGNAME "1d_fd_simple_shear" -/* set default simulation parameter values */ -#include "1d_fd_simple_shear_damsgaard2013.h" +#include "parameter_defaults.h" static void usage(void) { - printf("%s: %s [OPTIONS]\n" + printf("%s: %s [OPTIONS] [NAME]\n" + "runs a simulation and outputs state in files prefixed with NAME.\n" "optional arguments:\n" - " -N, --normalize normalize output velocity\n" - " -G, --gravity VAL gravity magnitude [m/s^2]\n" - " -P, --pressure VAL normal stress [Pa]\n" - " -m, --stress-ratio VAL applied stress ratio [-]\n" - " -V, --velocity-bottom VAL base velocity at bottom [m/s]\n" - " -A, --nonlocal-amplitude VAL amplitude of nonlocality [-]\n" - " -b, --rate-dependence VAL rate dependence beyond yield [-]\n" - " -f, --friction-coefficient VAL grain friction coefficient [-]\n" - " -p, --porosity VAL porosity fraction [-]\n" - " -d, --grain-size VAL representative grain size [m]\n" - " -r, --density VAL grain material density [kg/m^3]\n" - " -n, --resolution VAL number of cells in domain [-]\n" - " -o, --origo VAL coordinate system origo [m]\n" - " -L, --length VAL domain length [m]\n" - " -v, --version show version information\n" - " -h, --help show this message\n" + " -N, --normalize normalize output velocity\n" + " -G, --gravity VAL gravity magnitude [m/s^2]\n" + " -P, --normal-stress VAL normal stress on top [Pa]\n" + " -m, --stress-ratio VAL applied stress ratio [-]\n" + " -V, --velocity-bottom VAL base velocity at bottom [m/s]\n" + " -A, --nonlocal-amplitude VAL amplitude of nonlocality [-]\n" + " -b, --rate-dependence VAL rate dependence beyond yield [-]\n" + " -f, --friction-coefficient VAL grain friction coefficient [-]\n" + " -p, --porosity VAL porosity fraction [-]\n" + " -d, --grain-size VAL representative grain size [m]\n" + " -r, --density VAL grain material density [kg/m^3]\n" + " -n, --resolution VAL number of cells in domain [-]\n" + " -o, --origo VAL coordinate system origo [m]\n" + " -L, --length VAL domain length [m]\n" + " -F, --fluid enable pore fluid computations\n" + " -c, --fluid-compressibility VAL fluid compressibility [Pa^-1]\n" + " -i, --fluid-viscosity VAL fluid viscosity [Pa*s]\n" + " -R, --fluid-density VAL fluid density [kg/m^3]\n" + " -k, --fluid-permeability VAL fluid intrinsic permeability [m^2]\n" + " -O, --fluid-pressure-top VAL fluid pressure at +z edge [Pa]\n" + " -a, --fluid-pressure-ampl VAL amplitude of pressure variations [Pa]\n" + " -q, --fluid-pressure-freq VAL frequency of pressure variations [s^-1]\n" + " -H, --fluid-pressure-phase VAL fluid pressure at +z edge [Pa]\n" + " -t, --time VAL simulation start time [s]\n" + " -T, --time-end VAL simulation end time [s]\n" + " -D, --time-step VAL computational time step length [s]\n" + " -I, --file-interval VAL interval between output files [s]\n" + " -v, --version show version information\n" + " -h, --help show this message\n" , __func__, PROGNAME); } @@ -52,12 +66,12 @@ int main(int argc, char* argv[]) int normalize = 0; int opt; - const char* optstring = "hvNG:P:m:V:A:b:f:p:d:r:n:o:L:"; + const char* optstring = "hvNn:G:P:m:V:A:b:f:Fp:d:r:o:L:c:i:R:k:O:a:q:H:t:T:D:I:"; const struct option longopts[] = { {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, {"gravity", required_argument, NULL, 'G'}, - {"pressure", required_argument, NULL, 'P'}, + {"normal-stress", required_argument, NULL, 'P'}, {"stress-ratio", required_argument, NULL, 'm'}, {"velocity-bottom", required_argument, NULL, 'V'}, {"nonlocal-amplitude", required_argument, NULL, 'A'}, @@ -69,9 +83,24 @@ int main(int argc, char* argv[]) {"resolution", required_argument, NULL, 'n'}, {"origo", required_argument, NULL, 'o'}, {"length", required_argument, NULL, 'L'}, + {"fluid", no_argument, NULL, 'F'}, + {"fluid-compressiblity", required_argument, NULL, 'c'}, + {"fluid-viscosity", required_argument, NULL, 'i'}, + {"fluid-density", required_argument, NULL, 'R'}, + {"fluid-permeability", required_argument, NULL, 'k'}, + {"fluid-pressure-top", required_argument, NULL, 'O'}, + {"fluid-pressure-ampl", required_argument, NULL, 'a'}, + {"fluid-pressure-freq", required_argument, NULL, 'q'}, + {"fluid-pressure-phase", required_argument, NULL, 'H'}, + {"time", required_argument, NULL, 't'}, + {"time-end", required_argument, NULL, 'T'}, + {"time-step", required_argument, NULL, 'D'}, + {"file-interval", required_argument, NULL, 'I'}, {NULL, 0, NULL, 0} }; + double new_phi = sim.phi[0]; + double new_k = sim.k[0]; while ((opt = getopt_long(argc, argv, optstring, longopts, NULL)) != -1) { switch (opt) { case -1: /* no more arguments */ @@ -84,6 +113,9 @@ int main(int argc, char* argv[]) case 'v': version(); return 0; + case 'n': + sim.nz = atoi(optarg); + break; case 'N': normalize = 1; break; @@ -109,7 +141,7 @@ int main(int argc, char* argv[]) sim.mu_s = atof(optarg); break; case 'p': - sim.phi = atof(optarg); + new_phi = atof(optarg); break; case 'd': sim.d = atof(optarg); @@ -117,15 +149,51 @@ int main(int argc, char* argv[]) case 'r': sim.rho_s = atof(optarg); break; - case 'n': - sim.nz = atoi(optarg); - break; case 'o': sim.origo_z = atof(optarg); break; case 'L': sim.L_z = atof(optarg); break; + case 'F': + sim.fluid = 1; + break; + case 'c': + sim.beta_f = atof(optarg); + break; + case 'i': + sim.mu_f = atof(optarg); + break; + case 'R': + sim.rho_f = atof(optarg); + break; + case 'k': + new_k = atof(optarg); + break; + case 'O': + sim.p_f_top = atof(optarg); + break; + case 'a': + sim.p_f_mod_ampl = atof(optarg); + break; + case 'q': + sim.p_f_mod_freq = atof(optarg); + break; + case 'H': + sim.p_f_mod_phase = atof(optarg); + break; + case 't': + sim.t = atof(optarg); + break; + case 'T': + sim.t_end = atof(optarg); + break; + case 'D': + sim.dt = atof(optarg); + break; + case 'I': + sim.file_dt = atof(optarg); + break; default: fprintf(stderr, "%s: invalid option -- %c\n", argv[0], opt); @@ -134,29 +202,90 @@ int main(int argc, char* argv[]) return -2; } } + for (int i=optind; i<argc; ++i) { + if (i>optind) { + fprintf(stderr, "error: more than one simulation name specified\n"); + return 1; + } + sprintf(sim.name, "%s", argv[i]); + } prepare_arrays(&sim); - init_pressure(&sim); - init_friction(&sim); - compute_cooperativity_length(&sim); + if (!isnan(new_phi)) + for (int i=0; i<sim.nz; ++i) + sim.phi[i] = new_phi; + if (!isnan(new_k)) + for (int i=0; i<sim.nz; ++i) + sim.k[i] = new_k; + + lithostatic_pressure_distribution(&sim); + + if (sim.fluid) + hydrostatic_fluid_pressure_distribution(&sim); + +#ifdef DEBUG + puts(".. p_f_ghost before iterations:"); print_array(sim.p_f_ghost, sim.nz+2); + puts(""); + puts(".. normal stress before iterations:"); print_array(sim.sigma_n, sim.nz); + puts(""); +#endif + + double filetimeclock = 0.0; + unsigned long iter = 0; + while (sim.t <= sim.t_end) { + + if (sim.fluid) { + if (darcy_solver_1d(&sim, 10000, 1e-3)) + exit(1); +#ifdef DEBUG + puts(".. p_f_ghost:"); print_array(sim.p_f_ghost, sim.nz+2); + puts(""); +#endif + } + + compute_effective_stress(&sim); + compute_friction(&sim); + compute_cooperativity_length(&sim); + + if (iter == 0) + check_simulation_parameters(&sim); #ifdef DEBUG - puts("\n## Before solver"); - puts(".. p:"); print_array(sim.p, sim.nz); - puts(".. mu:"); print_array(sim.mu, sim.nz); + puts("\n## Before solver"); + puts(".. sigma_n_eff:"); print_array(sim.sigma_n_eff, sim.nz); + puts(".. mu:"); print_array(sim.mu, sim.nz); #endif - if (implicit_1d_jacobian_poisson_solver(&sim, 10000, 1e-5)) - exit(1); + if (implicit_1d_jacobian_poisson_solver(&sim, 10000, 1e-3)) + exit(1); - compute_shear_strain_rate_plastic(&sim); - compute_shear_velocity(&sim); + compute_shear_strain_rate_plastic(&sim); + compute_shear_velocity(&sim); + + sim.t += sim.dt; + filetimeclock += sim.dt; + iter++; + + if (filetimeclock >= sim.file_dt) { + write_output_file(&sim); + filetimeclock = 0.0; + } + } if (normalize) print_arrays_2nd_normalized(sim.z, sim.v_x, sim.nz); else - print_arrays(sim.z, sim.v_x, sim.nz); + if (sim.fluid) + for (int i=0; i<sim.nz; ++i) + printf("%.17g\t%.17g\t%.17g\t%.17g\n", + sim.z[i], + sim.v_x[i], + sim.sigma_n_eff[i], + sim.mu[i]); + /* sim.p_f_ghost[idx1g(i)]); */ + else + print_three_arrays(sim.z, sim.v_x, sim.sigma_n_eff, sim.nz); free_arrays(&sim); return 0; diff --git a/parameter_defaults.h b/parameter_defaults.h @@ -0,0 +1,71 @@ +#ifndef ONED_FD_SIMPLE_SHEAR_ +#define ONED_FD_SIMPLE_SHEAR_ + +#include <math.h> +#include <stdio.h> +#include "arrays.h" +#include "simulation.h" + +/* Simulation settings */ +struct simulation init_sim(void) +{ + struct simulation sim; + + sprintf(sim.name, "unnamed"); + + sim.G = 9.81; + + sim.P_wall = 120e3; /* larger normal stress deepens the shear depth */ + sim.mu_wall = 0.40; + sim.v_x_bot = 0.0; + + sim.nz = 100; + + /* lower values of A mean that the velocity curve can have sharper curves, + * e.g. at the transition from μ ≈ μ_s */ + sim.A = 0.40; /* Loose fit to Damsgaard et al 2013 */ + + /* lower values of b mean larger shear velocity for a given stress ratio + * above yield */ + sim.b = 0.9377; /* Henann and Kamrin 2016 */ + + sim.mu_s = atan(DEG2RAD(22.0)); /* Damsgaard et al 2013 */ + + sim.phi = initval(0.25, 1); /* Damsgaard et al 2013 */ + + /* lower values of d mean that the shear velocity curve can have sharper + * curves, e.g. at the transition from μ ≈ μ_s */ + sim.d = 0.04; /* Damsgaard et al 2013 */ + + /* grain material density [kg/m^3] */ + sim.rho_s = 2.6e3; /* Damsgaard et al 2013 */ + + /* spatial settings */ + sim.origo_z = 0.0; + sim.L_z = 0.7; /* Damsgaard et al 2013 */ + + /* temporal settings */ + sim.t = 0.0; + sim.dt = 2.0; + sim.t_end = 1.0; + sim.file_dt = 0.1; + sim.n_file = 0; + + /* fluid settings */ + sim.fluid = 0; + + sim.beta_f = 4.5e-10; /* Water, Goren et al 2011 */ + sim.mu_f = 1e-3; /* Water, Goren et al 2011 */ + sim.rho_f = 1e3; /* Water */ + sim.k = initval(1.9e-15, 1); /* Damsgaard et al 2015 */ + + /* no fluid-pressure variations */ + sim.p_f_top = 0.0; + sim.p_f_mod_ampl = 0.0; + sim.p_f_mod_freq = 1.0; + sim.p_f_mod_phase = 0.0; + + return sim; +} + +#endif diff --git a/simulation.c b/simulation.c @@ -1,5 +1,6 @@ #include <stdio.h> #include <stdlib.h> +#include <math.h> #include "arrays.h" #include "simulation.h" @@ -9,25 +10,213 @@ void prepare_arrays(struct simulation* sim) sim->origo_z + sim->L_z, sim->nz); sim->dz = sim->z[1] - sim->z[0]; /* cell spacing */ - sim->mu = zeros(sim->nz); /* local stress ratio */ - sim->p = zeros(sim->nz); /* local pressure */ + sim->mu = zeros(sim->nz); /* stress ratio */ + sim->sigma_n_eff = zeros(sim->nz); /* effective normal stress */ + sim->sigma_n = zeros(sim->nz); /* normal stess */ + sim->p_f_ghost = zeros(sim->nz+2); /* fluid pressure with ghost nodes */ + free(sim->phi); + sim->phi = zeros(sim->nz); /* porosity */ + free(sim->k); + sim->k = zeros(sim->nz); /* permeability */ sim->xi = zeros(sim->nz); /* cooperativity length */ - sim->gamma_dot_p = zeros(sim->nz); /* local shear velocity */ - sim->v_x = zeros(sim->nz); /* local shear velocity */ - sim->g_ghost = zeros(sim->nz+2); /* local fluidity with ghost nodes */ + sim->gamma_dot_p = zeros(sim->nz); /* shear velocity */ + sim->v_x = zeros(sim->nz); /* shear velocity */ + sim->g_ghost = zeros(sim->nz+2); /* fluidity with ghost nodes */ } void free_arrays(struct simulation* sim) { free(sim->z); free(sim->mu); - free(sim->p); + free(sim->sigma_n_eff); + free(sim->sigma_n); + free(sim->p_f_ghost); + free(sim->k); + free(sim->phi); free(sim->xi); free(sim->gamma_dot_p); free(sim->v_x); free(sim->g_ghost); } +static void warn_parameter_value( + const char message[], + const double value, + int* return_status) +{ + fprintf(stderr, "check_simulation_parameters: %s (%.17g)\n", + message, value); + *return_status = 1; +} + +static void check_float( + const char name[], + const double value, + int* return_status) +{ + if (isnan(value)) { + warn_parameter_value("%s is NaN", value, return_status); + *return_status = 1; + } else if (isinf(value)) { + warn_parameter_value("%s is infinite", value, return_status); + *return_status = 1; + } +} + +void check_simulation_parameters(const struct simulation* sim) +{ + int return_status = 0; + + check_float("sim.G", sim->G, &return_status); + if (sim->G < 0.0) + warn_parameter_value("sim.G is negative", sim->G, &return_status); + + check_float("sim.P_wall", sim->P_wall, &return_status); + if (sim->P_wall < 0.0) + warn_parameter_value("sim.P_wall is negative", sim->P_wall, + &return_status); + + check_float("sim.v_x_bot", sim->v_x_bot, &return_status); + + check_float("sim.mu_wall", sim->mu_wall, &return_status); + if (sim->mu_wall < 0.0) + warn_parameter_value("sim.mu_wall is negative", sim->mu_wall, + &return_status); + + check_float("sim.A", sim->A, &return_status); + if (sim->A < 0.0) + warn_parameter_value("sim.A is negative", sim->A, &return_status); + + check_float("sim.b", sim->b, &return_status); + if (sim->b < 0.0) + warn_parameter_value("sim.b is negative", sim->b, &return_status); + + check_float("sim.mu_s", sim->mu_s, &return_status); + if (sim->mu_s < 0.0) + warn_parameter_value("sim.mu_s is negative", sim->mu_s, + &return_status); + + check_float("sim.d", sim->d, &return_status); + if (sim->d <= 0.0) + warn_parameter_value("sim.d is not a positive number", sim->d, + &return_status); + + check_float("sim.rho_s", sim->rho_s, &return_status); + if (sim->rho_s <= 0.0) + warn_parameter_value("sim.rho_s is not a positive number", sim->rho_s, + &return_status); + + if (sim->nz <= 0) + warn_parameter_value("sim.nz is not a positive number", sim->nz, + &return_status); + + check_float("sim.origo_z", sim->origo_z, &return_status); + check_float("sim.L_z", sim->L_z, &return_status); + if (sim->L_z <= sim->origo_z) + warn_parameter_value("sim.L_z is smaller or equal to sim.origo_z", + sim->L_z, &return_status); + + if (sim->nz <= 0) + warn_parameter_value("sim.nz is not a positive number", sim->nz, + &return_status); + + check_float("sim.dz", sim->dz, &return_status); + if (sim->dz <= 0.0) + warn_parameter_value("sim.dz is not a positive number", sim->dz, + &return_status); + + check_float("sim.t", sim->t, &return_status); + if (sim->t < 0.0) + warn_parameter_value("sim.t is a negative number", + sim->t, &return_status); + + check_float("sim.t_end", sim->t_end, &return_status); + if (sim->t > sim->t_end) + warn_parameter_value("sim.t_end is smaller than sim.t", + sim->t, &return_status); + + check_float("sim.dt", sim->t_end, &return_status); + if (sim->dt <= 0.0) + warn_parameter_value("sim.dt is not a positive number", + sim->dt, &return_status); + + check_float("sim.file_dt", sim->file_dt, &return_status); + if (sim->file_dt < 0.0) + warn_parameter_value("sim.file_dt is a negative number", + sim->file_dt, &return_status); + + check_float("sim.phi[0]", sim->phi[0], &return_status); + if (sim->phi[0] < 0.0 || sim->phi[0] > 1.0) + warn_parameter_value("sim.phi[0] is not within [0;1]", + sim->phi[0], &return_status); + + if (sim->fluid != 0 && sim->fluid != 1) + warn_parameter_value("sim.fluid has an invalid value", + (double)sim->fluid, &return_status); + + if (sim->fluid) { + + check_float("sim.p_f_mod_ampl", sim->p_f_mod_ampl, &return_status); + if (sim->p_f_mod_ampl < 0.0) + warn_parameter_value("sim.p_f_mod_ampl is not a zero or positive", + sim->p_f_mod_ampl, &return_status); + + check_float("sim.p_f_mod_freq", sim->p_f_mod_freq, &return_status); + if (sim->p_f_mod_freq < 0.0) + warn_parameter_value("sim.p_f_mod_freq is not a zero or positive", + sim->p_f_mod_freq, &return_status); + + check_float("sim.beta_f", sim->beta_f, &return_status); + if (sim->beta_f <= 0.0) + warn_parameter_value("sim.beta_f is not positive", + sim->beta_f, &return_status); + + check_float("sim.mu_f", sim->mu_f, &return_status); + if (sim->mu_f <= 0.0) + warn_parameter_value("sim.mu_f is not positive", + sim->mu_f, &return_status); + + check_float("sim.rho_f", sim->rho_f, &return_status); + if (sim->rho_f <= 0.0) + warn_parameter_value("sim.rho_f is not positive", + sim->rho_f, &return_status); + + check_float("sim.k[0]", sim->k[0], &return_status); + if (sim->k[0] <= 0.0) + warn_parameter_value("sim.k[0] is not positive", + sim->k[0], &return_status); + + } + + if (return_status != 0) { + fprintf(stderr, "error: aborting due to invalid parameter choices\n"); + exit(return_status); + } +} + +void lithostatic_pressure_distribution(struct simulation* sim) +{ + for (int i=0; i<sim->nz; ++i) + sim->sigma_n[i] = sim->P_wall + + (1.0 - sim->phi[i])*sim->rho_s*sim->G*(sim->L_z - sim->z[i]); +} + +void compute_friction(struct simulation* sim) +{ + if (sim->fluid) + for (int i=0; i<sim->nz; ++i) + sim->mu[i] = sim->mu_wall/ + (sim->sigma_n_eff[i]/(sim->P_wall - sim->p_f_top)); + + else + for (int i=0; i<sim->nz; ++i) + sim->mu[i] = sim->mu_wall / + (1.0 + (1.0 - sim->phi[i])*sim->rho_s*sim->G*(sim->L_z - + sim->z[i]) + /sim->P_wall); + +} + double shear_strain_rate_plastic( const double fluidity, const double friction) @@ -52,6 +241,16 @@ void compute_shear_velocity(struct simulation* sim) sim->v_x[i] = sim->v_x[i-1] + sim->gamma_dot_p[i]*sim->dz; } +void compute_effective_stress(struct simulation* sim) +{ + if (sim->fluid) + for (int i=0; i<sim->nz; ++i) + sim->sigma_n_eff[i] = sim->sigma_n[i] - sim->p_f_ghost[idx1g(i)]; + else + for (int i=0; i<sim->nz; ++i) + sim->sigma_n_eff[i] = sim->sigma_n[i]; +} + double cooperativity_length( const double A, const double d, @@ -86,7 +285,12 @@ void compute_local_fluidity(struct simulation* sim) { for (int i=0; i<sim->nz; ++i) sim->g_ghost[idx1g(i)] = local_fluidity( - sim->p[i], sim->mu[i], sim->mu_s, sim->b, sim->rho_s, sim->d); + sim->sigma_n_eff[i], + sim->mu[i], + sim->mu_s, + sim->b, + sim->rho_s, + sim->d); } void set_bc_neumann(double* g_ghost, const int nz, const int boundary) @@ -180,7 +384,7 @@ int implicit_1d_jacobian_poisson_solver( r_norm, sim->dz, sim->mu, - sim->p, + sim->sigma_n_eff, sim->xi, sim->mu_s, sim->b, @@ -192,7 +396,7 @@ int implicit_1d_jacobian_poisson_solver( if (r_norm_max <= rel_tol) { set_bc_dirichlet(sim->g_ghost, sim->nz, -1, 0.0); - set_bc_neumann(sim->g_ghost, sim->nz, +1); + set_bc_dirichlet(sim->g_ghost, sim->nz, +1, 0.0); free(g_ghost_out); free(r_norm); /* printf(".. Solution converged after %d iterations\n", iter); */ @@ -207,3 +411,25 @@ int implicit_1d_jacobian_poisson_solver( fprintf(stderr, ".. Residual normalized error: %f\n", r_norm_max); return 1; } + +void write_output_file(struct simulation* sim) +{ + + char outfile[200]; + FILE *fp; + sprintf(outfile, "%s.output%05d.txt", sim->name, sim->n_file++); + + fp = fopen(outfile, "w"); + if (sim->fluid) + for (int i=0; i<sim->nz; ++i) + fprintf(fp, "%.17g\t%.17g\t%.17g\t%.17g\t%.17g\n", + sim->z[i], + sim->v_x[i], + sim->sigma_n_eff[i], + sim->p_f_ghost[idx1g(i)], + sim->mu[i]); + else + fprint_three_arrays(fp, sim->z, sim->v_x, sim->sigma_n_eff, sim->nz); + + fclose(fp); +} diff --git a/simulation.h b/simulation.h @@ -1,12 +1,17 @@ #ifndef SIMULATION_ #define SIMULATION_ -#include <math.h> #include "arrays.h" +#define PI 3.14159265358979323846 +#define DEG2RAD(x) (x*PI/180.0) + /* Simulation settings */ struct simulation { + /* simulation name to use for output files */ + char name[100]; + /* gravitational acceleration magnitude [m/s^2] */ double G; @@ -28,9 +33,6 @@ struct simulation { /* bulk and critical state static yield friction coefficient [-] */ double mu_s; - /* porosity [-] */ - double phi; - /* representative grain size [m] */ double d; @@ -52,9 +54,38 @@ struct simulation { /* cell spacing [m] */ double dz; - /* other arrays */ + /* current time [s] */ + double t; + + /* end time [s] */ + double t_end; + + /* time step length [s] */ + double dt; + + /* interval between output files [s] */ + double file_dt; + + /* output file number */ + int n_file; + + /* Fluid parameters */ + int fluid; /* flag to switch fluid on (1) or off (0) */ + double p_f_top; /* fluid pressure at the top [Pa] */ + double p_f_mod_ampl; /* amplitude of fluid pressure variations [Pa] */ + double p_f_mod_freq; /* frequency of fluid pressure variations [s^-1] */ + double p_f_mod_phase; /* phase of fluid pressure variations [s^-1] */ + double beta_f; /* adiabatic fluid compressibility [Pa^-1] */ + double mu_f; /* fluid dynamic viscosity [Pa*s] */ + double rho_f; /* fluid density [kg/m^3] */ + + /* arrays */ double* mu; /* static yield friction [-] */ - double* p; /* effective normal pressure [Pa] */ + double* sigma_n_eff; /* effective normal pressure [Pa] */ + double* sigma_n; /* normal stress [Pa] */ + double* p_f_ghost; /* fluid pressure [Pa] */ + double* k; /* hydraulic permeability [m^2] */ + double* phi; /* porosity [-] */ double* xi; /* cooperativity length */ double* gamma_dot_p; /* plastic shear strain rate [1/s] */ double* v_x; /* shear velocity [m/s] */ @@ -65,6 +96,10 @@ struct simulation { void prepare_arrays(struct simulation* sim); void free_arrays(struct simulation* sim); +void check_simulation_parameters(const struct simulation* sim); + +void lithostatic_pressure_distribution(struct simulation* sim); + void set_bc_neumann( double* g_ghost, const int nz, @@ -79,10 +114,14 @@ void set_bc_dirichlet( void compute_cooperativity_length(struct simulation* sim); void compute_shear_strain_rate_plastic(struct simulation* sim); void compute_shear_velocity(struct simulation* sim); +void compute_effective_stress(struct simulation* sim); +void compute_friction(struct simulation* sim); int implicit_1d_jacobian_poisson_solver( struct simulation* sim, const int max_iter, const double rel_tol); +void write_output_file(struct simulation* sim); + #endif