tFind roots to equation system using Brent's method - 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 13337da5aff9712f2b6e986de9100d93ec5372f7
 (DIR) parent 4b66b1f82965a92a91d4cc3a8a93a14da04c576e
 (HTM) Author: Anders Damsgaard <anders@adamsgaard.dk>
       Date:   Thu, 12 Dec 2019 12:42:57 +0100
       
       Find roots to equation system using Brent's method
       
       Diffstat:
         M max_depth_simple_shear.c            |     189 +++++++++++++++++++------------
       
       1 file changed, 115 insertions(+), 74 deletions(-)
       ---
 (DIR) diff --git a/max_depth_simple_shear.c b/max_depth_simple_shear.c
       t@@ -3,25 +3,25 @@
        #include <stdlib.h>
        #include <math.h>
        #include <getopt.h>
       -#include <string.h>
        #include <time.h>
        
        #include "simulation.h"
       -#include "fluid.h"
       +/* #include "fluid.h" */
        
        #define PROGNAME "max_depth_simple_shear"
        
        #include "parameter_defaults.h"
        
       -/* tolerance criteria in meter for solver */
       -#define RTOL 1e-5
       -/* #define MAX_ITER 10000 */
       -#define MAX_ITER 1000
       +#define EPS 1e-12
       +
       +/* depth accuracy criteria in meter for solver */
       +#define TOL 1e-10
       +#define MAX_ITER 100
        
        #define VERBOSE
        
        /* uncomment to print time spent per time step to stdout */
       -/*#define BENCHMARK_PERFORMANCE*/
       +/* #define BENCHMARK_PERFORMANCE */
        
        static void
        usage(void)
       t@@ -80,18 +80,12 @@ double
        skin_depth(const struct simulation* sim)
        {
                return sqrt(sim->k[0]/
       -                            (sim->phi[0]*sim->mu_f*sim->beta_f*M_PI*sim->p_f_mod_freq));
       +               (sim->phi[0]*sim->mu_f*sim->beta_f*M_PI*sim->p_f_mod_freq));
        }
        
        /* using alternate form: sin(x) + cos(x) = sqrt(2)*sin(x + pi/4) */
        double
       -analytical_solution_lhs(double d_s, double z_)
       -{
       -        return sqrt(2.0) * sin((3.0*M_PI/2.0 - z_/d_s) + M_PI/4.0);
       -}
       -
       -double
       -analytical_solution_rhs(const struct simulation* sim, double d_s, double z_)
       +eff_normal_stress_gradient(const struct simulation* sim, double d_s, double z_)
        {
                if (z_/d_s > 10.0) {
                        fprintf(stderr, "error: %s: unrealistic depth: %g m "
       t@@ -99,19 +93,102 @@ analytical_solution_rhs(const struct simulation* sim, double d_s, double z_)
                        exit(1);
                }
        
       -        return -(sim->rho_s - sim->rho_f)*sim->G*d_s / sim->p_f_mod_ampl
       -                   - exp(z_/d_s);
       +        return sqrt(2.0)*sin((3.0*M_PI/2.0 - z_/d_s) + M_PI/4.0)
       +               + (sim->rho_s - sim->rho_f)*sim->G*d_s/sim->p_f_mod_ampl*exp(z_/d_s);
       +}
       +
       +/* use Brent's method for finding roots (Press et al., 2007) */
       +double zbrent(struct simulation* sim,
       +              double d_s,
       +              double (*f)(struct simulation* sim, double, double),
       +              double x1,
       +              double x2,
       +              double tol)
       +
       +{
       +        int iter;
       +        double a, b, c, d, e, min1, min2, fa, fb, fc, p, q, r, s, tol1, xm;
       +
       +        a = x1; b=x2; c=x2;
       +        fa = (*f)(sim, d_s, a);
       +        fb = (*f)(sim, d_s, b);
       +
       +        if ((fa > 0.0 && fb > 0.0) || (fa < 0.0 && fb < 0.0)) {
       +                fprintf(stderr,
       +                                "error: %s: no root of stress gradient in range %g,%g m\n",
       +                                __func__, x1, x2);
       +                exit(1);
       +        }
       +        fc = fb;
       +        for (iter=0; iter<MAX_ITER; iter++) {
       +                if ((fb > 0.0 && fc > 0.0) || (fb < 0.0 && fc < 0.0)) {
       +                        c = a;
       +                        fc = fa;
       +                        d = b - a;
       +                        e = d;
       +                }
       +                if (fabs(fc) < fabs(fb)) {
       +                        a = b;
       +                        b = c;
       +                        c = a;
       +                        fa = fb;
       +                        fb = fc;
       +                        fc = fa;
       +                }
       +                tol1 = 2.0*EPS*fabs(b) + 0.5*tol;
       +                xm = 0.5*(c - b);
       +                if (fabs(xm) <= tol1 || fb == 0.0)
       +                        return b;
       +                if (fabs(e) >= tol1 && fabs(fa) > fabs(fb)) {
       +                        s = fb/fa;
       +                        if (a == c) {
       +                                p = 2.0*xm*s;
       +                                q = 1.0 - s;
       +                        } else {
       +                                q = fa/fc;
       +                                r = fb/fc;
       +                                p = s*(2.0*xm*q*(q - r) - (b - a)*(r - 1.0));
       +                                q = (q - 1.0)*(r - 1.0)*(s - 1.0);
       +                        }
       +                        if (p > 0.0)
       +                                q = -q;
       +                        p = fabs(p);
       +                        min1 = 3.0*xm*q - fabs(tol1*q);
       +                        min2 = fabs(e*q);
       +                        if (2.0*p < (min1 < min2 ? min1 : min2)) {
       +                                e = d;
       +                                d = p/q;
       +                        } else {
       +                                d = xm;
       +                                e = d;
       +                        }
       +                } else {
       +                        d = xm;
       +                        e = d;
       +                }
       +                a = b;
       +                fa = fb;
       +                if (fabs(d) > tol1)
       +                        b += d;
       +                else
       +                        b += ((xm) >= 0.0 ? fabs(tol1) : -fabs(tol1));
       +                fb = (*f)(sim, d_s, b);
       +        }
       +        fprintf(stderr,
       +                        "error: %s: exceeded maximum number of iterations",
       +                        __func__);
       +        exit(10);
       +        return NAN;
        }
        
        int
        main(int argc, char* argv[])
        {
       -        int norm, opt, i;
       +        int opt, i;
                struct simulation sim;
                const char* optstring;
       -        unsigned long iter;
                double new_phi, new_k;
       -        double diff, d_s, depth;
       +        double d_s, depth, depth_limit1, depth_limit2;
        #ifdef BENCHMARK_PERFORMANCE
                clock_t t_begin, t_end;
                double t_elapsed;
       t@@ -127,8 +204,6 @@ main(int argc, char* argv[])
                /* load with default values */
                sim = init_sim();
        
       -        norm = 0;
       -
                optstring = "hvNn:G:P:m:s:l:V:A:b:f:C:Fp:d:r:o:L:c:i:R:k:O:a:q:H:T:S:t:T:D:I:";
                const struct option longopts[] = {
                        {"help",                      no_argument,       NULL, 'h'},
       t@@ -221,16 +296,15 @@ main(int argc, char* argv[])
                        for (i=0; i<sim.nz; ++i)
                                sim.k[i] = new_k;
        
       -        lithostatic_pressure_distribution(&sim);
       +        /*lithostatic_pressure_distribution(&sim);*/
        
       -        if (sim.fluid) {
       +        /*if (sim.fluid) {
                        hydrostatic_fluid_pressure_distribution(&sim);
                        set_largest_fluid_timestep(&sim, 0.5);
       -        }
       +        }*/
        
                check_simulation_parameters(&sim);
        
       -        iter = 0;
                depth = 0.0;
                d_s = skin_depth(&sim);
        
       t@@ -238,56 +312,24 @@ main(int argc, char* argv[])
                t_begin = clock();
        #endif
        
       -        double depth_prev;
       -        double res_norm;
       -
       -        if (fabs(sim.p_f_mod_ampl) > 1e-6) {
       -                do {
       -                        depth_prev = depth;
       -                        diff = analytical_solution_rhs(&sim, d_s, depth)
       -                                 - analytical_solution_lhs(d_s, depth);
       -                        /* printf("%g\n", diff); */
       -#ifdef VERBOSE
       -                        /* printf("rhs = %g\n", analytical_solution_rhs(&sim, d_s, depth)); */
       -                        /* printf("lhs = %g\n", analytical_solution_lhs(d_s, depth)); */
       -                        /* printf("%ld\t%g\t%g\n", iter, depth, diff); */
       -#endif
       -                        depth -= diff*d_s;
       -
       -                        /*if (diff > 0.0) {
       -                                depth += 1e-5*d_s;
       -                        } else {
       -                                depth -= 1e-5*d_s;
       -                        }*/
       -
       -                        if (depth < 0.0) {
       -                                depth = fabs(depth);
       -#ifdef VERBOSE
       -                                printf("bouncing back from zero depth\n");
       -#endif
       -                        }
       +        /* deep deformatin does not occur with approximately zero amplitude in 
       +         * water pressure forcing, or if the stress gradient is positive at
       +         * zero depth */
       +        if (fabs(sim.p_f_mod_ampl) > 1e-6 && 
       +                eff_normal_stress_gradient(&sim, d_s, 0.0) < 0.0) {
        
       -                        res_norm = fabs((depth - depth_prev)/(depth_prev + 1e-16));
       -                        printf("%g\t%g\n", diff, res_norm);
       +                depth_limit1 = 0.0;
       +                depth_limit2 = 5.0*d_s;
        
       -#ifdef VERBOSE
       -                        /* printf("adjusting depth to %g\n", depth); */
       -#endif
       -
       -                        iter++;
       -                        if (iter > MAX_ITER) {
       -                                fprintf(stderr, "error: %s: solution did not converge\n"
       -                                                "    iteration %ld\n"
       -                                                "    diff = %g\n"
       -                                                "    depth = %g\n",
       -                                                __func__, iter, diff, depth);
       -                                exit(1);
       -                        }
       +                depth = zbrent(&sim,
       +                       d_s,
       +                       (double (*)(struct simulation*, double, double))
       +                                   eff_normal_stress_gradient,
       +                       depth_limit1,
       +                       depth_limit2,
       +                       TOL);
        
       -#ifdef VERBOSE
       -                        /* puts(""); */
       -#endif
       -                } while (fabs(diff) > RTOL);
       +                /* printf("%g\t%g\n", diff, res_norm); */
                }
        
        #ifdef BENCHMARK_PERFORMANCE
       t@@ -296,7 +338,6 @@ main(int argc, char* argv[])
                printf("time spent = %g s\n", t_elapsed);
        #endif
        
       -        printf("\nresult:\n");
                printf("%g\t%g\n", depth, d_s);
        
                free_arrays(&sim);