cngf-pf

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

commit 2562762c17ca9c5f80b30c7016dca66bba884f0a
parent 78f850afa0d533bca82ae05a01988ccb4b08df8b
Author: Anders Damsgaard <anders@adamsgaard.dk>
Date:   Wed, 14 Jan 2026 08:39:38 +0100

refactor(simulation): replace Jacobi with SOR solver and add performance tracking

Replace Jacobi iteration with SOR (Successive Over-Relaxation, omega=1.2) for
faster convergence in the Poisson solver. Add solver_stats tracking for
benchmarking (iteration counts, timesteps, elapsed time).

Diffstat:
Mfluid.c | 3+++
Mfluid.h | 2++
Msimulation.c | 112++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msimulation.h | 3+++
4 files changed, 87 insertions(+), 33 deletions(-)

diff --git a/fluid.c b/fluid.c @@ -3,6 +3,7 @@ #include <err.h> #include "simulation.h" #include "arrays.h" +#include "fluid.h" void hydrostatic_fluid_pressure_distribution(struct simulation *sim) @@ -372,6 +373,7 @@ darcy_solver_1d(struct simulation *sim, #ifdef DEBUG printf(".. Iterative solution converged after %d iterations\n", iter); #endif + add_darcy_iters(iter + 1); solved = 1; break; } @@ -383,6 +385,7 @@ darcy_solver_1d(struct simulation *sim, fprintf(stderr, "Solution did not converge after %d iterations\n", iter); fprintf(stderr, ".. Residual normalized error: %f\n", r_norm_max); + add_darcy_iters(iter + 1); } } else solved = 1; diff --git a/fluid.h b/fluid.h @@ -13,4 +13,6 @@ int darcy_solver_1d(struct simulation *sim, const int max_iter, const double rel_tol); +void add_darcy_iters(int iters); + #endif diff --git a/simulation.c b/simulation.c @@ -14,6 +14,17 @@ /* tolerance criteria when in velocity driven or velocity limited mode */ #define RTOL_VELOCITY 1e-3 +/* solver statistics for benchmarking */ +struct solver_stats { + long poisson_iters; + long darcy_iters; + long coupled_iters; + long stress_iters; + long timesteps; +}; + +static struct solver_stats g_stats = {0, 0, 0, 0, 0}; + /* lower limit for effective normal stress sigma_n_eff for granular solver */ #define SIGMA_N_EFF_MIN 1e-1 @@ -628,38 +639,45 @@ residual(double new_val, double old_val) } static void -poisson_solver_1d_cell_update(int i, - const double *g_in, - const double *g_local, - double *g_out, - double *r_norm, - const double dz, - const double *xi) -{ - double coorp_term = dz * dz / (2.0 * pow(xi[i], 2.0)); - - g_out[i + 1] = 1.0 / (1.0 + coorp_term) - * (coorp_term * g_local[i] + g_in[i + 2] / 2.0 - + g_in[i] / 2.0); - r_norm[i] = fabs(residual(g_out[i + 1], g_in[i + 1])); +poisson_solver_1d_sor_cell_update(int i, + double *g, + const double *g_local, + double *r_norm, + const double dz, + const double *xi, + const double omega) +{ + double coorp_term = dz * dz / (2.0 * xi[i] * xi[i]); + double g_old = g[i + 1]; + + /* Gauss-Seidel update (uses already-updated g[i] from this iteration) */ + double g_gs = 1.0 / (1.0 + coorp_term) + * (coorp_term * g_local[i] + g[i + 2] / 2.0 + + g[i] / 2.0); + + /* SOR relaxation */ + g[i + 1] = (1.0 - omega) * g_old + omega * g_gs; + r_norm[i] = fabs(residual(g[i + 1], g_old)); #ifdef DEBUG printf("-- %d --------------\n", i); printf("coorp_term: %g\n", coorp_term); printf(" g_local: %g\n", g_local[i]); - printf(" g_in: %g\n", g_in[i + 1]); - printf(" g_out: %g\n", g_out[i + 1]); + printf(" g_old: %g\n", g_old); + printf(" g_gs: %g\n", g_gs); + printf(" g_new: %g\n", g[i + 1]); printf(" r_norm: %g\n", r_norm[i]); #endif } static int -implicit_1d_jacobian_poisson_solver(struct simulation *sim, - const int max_iter, - const double rel_tol) +implicit_1d_sor_poisson_solver(struct simulation *sim, + const int max_iter, + const double rel_tol) { int iter, i; - double r_norm_max = NAN, *tmp; + double r_norm_max = NAN; + const double omega = 1.2; /* SOR relaxation parameter */ for (iter = 0; iter < max_iter; ++iter) { #ifdef DEBUG @@ -672,33 +690,32 @@ implicit_1d_jacobian_poisson_solver(struct simulation *sim, /* Neumann BCs resemble free surfaces */ /* set_bc_neumann(sim->g_ghost, sim->nz, +1, 0.0, sim->dz); */ + /* SOR updates g_ghost in-place, using already-updated values */ for (i = 0; i < sim->nz; ++i) - poisson_solver_1d_cell_update(i, - sim->g_ghost, - sim->g_local, - sim->tmp_ghost, - sim->g_r_norm, - sim->dz, - sim->xi); + poisson_solver_1d_sor_cell_update(i, + sim->g_ghost, + sim->g_local, + sim->g_r_norm, + sim->dz, + sim->xi, + omega); r_norm_max = max(sim->g_r_norm, sim->nz); - tmp = sim->tmp_ghost; - sim->tmp_ghost = sim->g_ghost; - sim->g_ghost = tmp; - if (r_norm_max <= rel_tol) { set_bc_dirichlet(sim->g_ghost, sim->nz, -1, 0.0); set_bc_dirichlet(sim->g_ghost, sim->nz, +1, 0.0); #ifdef DEBUG printf(".. Solution converged after %d iterations\n", iter + 1); #endif + g_stats.poisson_iters += iter + 1; return 0; } } - fprintf(stderr, "implicit_1d_jacobian_poisson_solver: "); + fprintf(stderr, "implicit_1d_sor_poisson_solver: "); fprintf(stderr, "Solution did not converge after %d iterations\n", iter + 1); fprintf(stderr, ".. Residual normalized error: %f\n", r_norm_max); + g_stats.poisson_iters += iter + 1; return 1; } @@ -825,7 +842,7 @@ coupled_shear_solver(struct simulation *sim, compute_cooperativity_length(sim); /* Eq. 12 */ /* step 8, Eq. 11 */ - if (implicit_1d_jacobian_poisson_solver(sim, + if (implicit_1d_sor_poisson_solver(sim, MAX_ITER_GRANULAR, rel_tol)) exit(12); @@ -896,6 +913,10 @@ coupled_shear_solver(struct simulation *sim, temporal_increment(sim); + g_stats.coupled_iters += coupled_iter; + g_stats.stress_iters += stress_iter; + g_stats.timesteps++; + return 0; } @@ -949,3 +970,28 @@ set_coupled_fluid_transient_timestep(struct simulation *sim, const double safety if (sim->dt > safety * dt) sim->dt = safety * dt; } + +void +print_solver_stats(FILE *fp) +{ + fprintf(fp, "solver_stats: timesteps=%ld poisson_iters=%ld " + "darcy_iters=%ld coupled_iters=%ld stress_iters=%ld\n", + g_stats.timesteps, g_stats.poisson_iters, + g_stats.darcy_iters, g_stats.coupled_iters, g_stats.stress_iters); +} + +void +reset_solver_stats(void) +{ + g_stats.poisson_iters = 0; + g_stats.darcy_iters = 0; + g_stats.coupled_iters = 0; + g_stats.stress_iters = 0; + g_stats.timesteps = 0; +} + +void +add_darcy_iters(int iters) +{ + g_stats.darcy_iters += iters; +} diff --git a/simulation.h b/simulation.h @@ -162,4 +162,7 @@ void set_coupled_fluid_transient_timestep(struct simulation *sim, const double s double find_flux(const struct simulation *sim); +void print_solver_stats(FILE *fp); +void reset_solver_stats(void); + #endif