tMerge branch 'darcy' into 'master' - cngf-pf - continuum model for granular flows with pore-pressure dynamics (renamed from 1d_fd_simple_shear)
 (HTM) git clone git://src.adamsgaard.dk/cngf-pf
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 76f099629e26cfd4d8338b5862856ebc974d4e74
 (DIR) parent 9e079cfadd9105a19d6d651546fd09fe069612e3
 (HTM) 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 +-
         M 1d_fd_simple_shear.png              |       0 
         D 1d_fd_simple_shear_damsgaard2013.h  |      66 -------------------------------
         A 1d_fd_simple_shear_fluid.gp         |      35 +++++++++++++++++++++++++++++++
         M 1d_fd_simple_shear_henann_kamrin20… |       2 +-
         M 1d_fd_simple_shear_rheology.png     |       0 
         M 1d_fd_simple_shear_rheology_iverso… |       0 
         M 1d_fd_simple_shear_rheology_kamb.p… |       0 
         M 1d_fd_simple_shear_rheology_tulacz… |       0 
         M Makefile                            |      19 ++++++++++++++++++-
         M README.md                           |      46 ++++++++++++++++++++++++++++++-
         M arrays.c                            |      46 +++++++++++++++++++++++++++++--
         M arrays.h                            |      23 ++++++++++++++++++++---
         A diurnal.gif                         |       0 
         A fluid.c                             |     185 ++++++++++++++++++++++++++++++
         A fluid.h                             |      13 +++++++++++++
         D julia/1d_fd_simple_shear.jl         |     277 -------------------------------
         D julia/1d_fd_simple_shear.png        |       0 
         D julia/Makefile                      |       7 -------
         M main.c                              |     203 +++++++++++++++++++++++++------
         A parameter_defaults.h                |      71 +++++++++++++++++++++++++++++++
         M simulation.c                        |     244 +++++++++++++++++++++++++++++--
         M simulation.h                        |      51 +++++++++++++++++++++++++++----
       
       23 files changed, 878 insertions(+), 412 deletions(-)
       ---
 (DIR) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
       t@@ -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
 (DIR) diff --git a/1d_fd_simple_shear.png b/1d_fd_simple_shear.png
       Binary files differ.
 (DIR) diff --git a/1d_fd_simple_shear_damsgaard2013.h b/1d_fd_simple_shear_damsgaard2013.h
       t@@ -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
 (DIR) diff --git a/1d_fd_simple_shear_fluid.gp b/1d_fd_simple_shear_fluid.gp
       t@@ -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
 (DIR) diff --git a/1d_fd_simple_shear_henann_kamrin2016.h b/1d_fd_simple_shear_henann_kamrin2016.h
       t@@ -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;
        
 (DIR) diff --git a/1d_fd_simple_shear_rheology.png b/1d_fd_simple_shear_rheology.png
       Binary files differ.
 (DIR) diff --git a/1d_fd_simple_shear_rheology_iverson.png b/1d_fd_simple_shear_rheology_iverson.png
       Binary files differ.
 (DIR) diff --git a/1d_fd_simple_shear_rheology_kamb.png b/1d_fd_simple_shear_rheology_kamb.png
       Binary files differ.
 (DIR) diff --git a/1d_fd_simple_shear_rheology_tulaczyk.png b/1d_fd_simple_shear_rheology_tulaczyk.png
       Binary files differ.
 (DIR) diff --git a/Makefile b/Makefile
       t@@ -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 $@
        
 (DIR) diff --git a/README.md b/README.md
       t@@ -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
       t@@ -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 
       t@@ -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) 
 (DIR) diff --git a/arrays.c b/arrays.c
       t@@ -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)
       t@@ -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)
       t@@ -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));
        }
       t@@ -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)
 (DIR) diff --git a/arrays.h b/arrays.h
       t@@ -1,3 +1,5 @@
       +#include <stdio.h>
       +
        #ifndef ARRAYS_
        #define ARRAYS_
        
       t@@ -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);
       t@@ -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);
        
 (DIR) diff --git a/diurnal.gif b/diurnal.gif
       Binary files differ.
 (DIR) diff --git a/fluid.c b/fluid.c
       t@@ -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;
       +}
 (DIR) diff --git a/fluid.h b/fluid.h
       t@@ -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
 (DIR) diff --git a/julia/1d_fd_simple_shear.jl b/julia/1d_fd_simple_shear.jl
       t@@ -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
 (DIR) diff --git a/julia/1d_fd_simple_shear.png b/julia/1d_fd_simple_shear.png
       Binary files differ.
 (DIR) diff --git a/julia/Makefile b/julia/Makefile
       t@@ -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) $<
 (DIR) diff --git a/main.c b/main.c
       t@@ -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);
        }
        
       t@@ -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'},
       t@@ -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 */
       t@@ -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;
       t@@ -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);
       t@@ -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);
       t@@ -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;
 (DIR) diff --git a/parameter_defaults.h b/parameter_defaults.h
       t@@ -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
 (DIR) diff --git a/simulation.c b/simulation.c
       t@@ -1,5 +1,6 @@
        #include <stdio.h>
        #include <stdlib.h>
       +#include <math.h>
        #include "arrays.h"
        #include "simulation.h"
        
       t@@ -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)
       t@@ -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,
       t@@ -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)
       t@@ -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,
       t@@ -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); */
       t@@ -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);
       +}
 (DIR) diff --git a/simulation.h b/simulation.h
       t@@ -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;
        
       t@@ -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;
        
       t@@ -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] */
       t@@ -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,
       t@@ -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