-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMoT-Voellmy.2026-05-04.c
More file actions
2549 lines (2216 loc) · 93.9 KB
/
Copy pathMoT-Voellmy.2026-05-04.c
File metadata and controls
2549 lines (2216 loc) · 93.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*******************************************************************************
File: MoT-Voellmy.2026-05-04.c Dieter Issler, 2026-05-04
Simulation of the motion of a gravity mass flow on a surface composed of
regular quadrangles that project onto rectangles in the horizontal plane.
In this version, a simplified variant of Fey's cell-centered Method of
Transport is implemented, where the decomposition according to the
eigenvectors of the Jacobians of the fluxes is omitted and only the convective
mode is considered. The scheme is first-order in time.
The code solves the shallow-water equations with friction, using the
conservative formulation. The pressure distribution is hydrostatic and the
earth pressure coefficients are 1. The Voellmy bed friction law is used in
this version, but this is easy to modify. Entrainment can be included by
modifying the source term for the flow height and removing the eroded mass
from the bed.
The program is started with the name of the command file as argument. In the
latter, the names of the grid, initialization and output files are specified
along with all simulation parameters. The grid file has to contain all
pertinent geometrical information while the distribution of bed depth and
strength, initial mass and velocity are specified in the initialization file.
In this version, formatted ASCII input and ASCII or BinaryTerrain 1.3 output
is used. The time steps are determined adaptively within given bounds. If
negative flow heights occur, the CFL number is reduced and the time step
repeated. The computation is limited to the area where the flow exceeds a
(low) threshold and is stopped when the total flow momentum drops below a
user-specified threshold value or the maximum simulated time is exceeded.
*******************************************************************************/
/** Compiler flags */
#define GCC
#if defined(__linux__) || defined(__APPLE__)
#define LINUX
#define DIRSEP "/"
#define ST "%lu"
#define ST3 "%3lu"
#define ST04 "%04lu"
#endif
#ifdef _WIN32
#define WINDOWS
#define DIRSEP "\\"
#define ST "%llu"
#define ST3 "%3llu"
#define ST04 "%04llu"
#endif
/** Code version */
#define VERSION "2026-05-04"
#define INPUT_VERSION "2026-04-20"
/** Include files */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <float.h>
#include <time.h>
#include <unistd.h>
#include <locale.h>
#include <libgen.h>
#include <stdbool.h>
#include <sys/stat.h>
/** General constants and simple functions */
#define SQ(A) ((A) * (A))
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define TRIES_MAX 30 /**< Max. # attempts at memory allocation */
#define TRY_WAIT 3 /**< Wait (s) between allocation attempts */
/** The following variables are declared before main(...) to make them
accessible to other subroutines within the same file, in particular the
ones responsible for reading the input file and for memory allocation. */
/** File names and handles */
char version[11] = VERSION; /**< Designation of program version */
char topo_name[512]; /**< Name of avalanche path */
char run_name[512]; /**< Name of this specific simulation */
char grid_fn[512]; /**< Name of grid file */
char h_fn[512]; /**< Name of release depth raster file*/
char u_fn[512]; /**< Name of initial velocity raster file */
char v_fn[512]; /**< Name of initial velocity raster file */
char b_fn[512]; /**< Name of bed depth raster file */
char tauc_fn[512]; /**< Name of bed shear strength raster file */
char mu_s_fn[512]; /**< Name of bed frict. coeff. raster file */
char mu_fn[512]; /**< Name of dry-friction-coefficient file */
char k_fn[512]; /**< Name of drag-coefficient file */
char nD_fn[512]; /**< Name of file with forest density nD */
char tD_fn[512]; /**< Name of file with tree diameter tD */
char erod_tab_fn[512]; /**< Name of file with IsPa erosion table */
char out_fn[512]; /**< Name of output file */
char max_fn[512]; /**< Name of file with maximum values */
char comment[880-5*sizeof(int)]; /**< Can be used for comments */
char field_names[5][16]; /**< Array with names of output fields */
FILE *cfp; /**< Pointer to run set-up file */
FILE *gfp; /**< Pointer to grid file */
/** Variables concerning the numerics */
int ifv; /**< Input File Version (1, 2, 3, 4) */
long loc_dump_num; /**< Location of # dumps in output file */
size_t n_dump; /**< Number of time slices written */
size_t nts = 0; /**< Time step number */
double t; /**< Simulated time (s) */
double dt; /**< Time step (s) */
double dt_min = 0.0001; /**< Minimum admissible time step */
double dt_max = 0.2; /**< Maximum admissible time step */
double t_max = 1000.0; /**< Maximum time for a model run (s) */
double t_dump = 0.0; /**< Next time for writing field values */
double dt_dump = 1.0; /**< Write interval for results (s) */
double cfl = 0.7; /**< Courant-Friedrichs-Levy number */
double mov_vol; /**< Total volume in motion */
double h_lim = 5.0; /**< Max. effective flow depth for drag term,
reasonable range is 5–10 m. */
double h_min = 0.05; /**< Minimum flow height in active cells */
double u_min = 0.01; /**< Minimum flow speed in active cells */
double mom_thr; /**< Old stop criterion for flow momentum */
double mom_fraction = 0.05; /**< New stop criterion for flow momentum */
double mom_max = 0.0; /**< Running max. of quantity of movement */
char *fmt; /**< w: ESRI_ASCII_Grid, wb: BinaryTerrain */
char write_vectors[4]; /**< Switch for writing u, v components */
char write_max_press[4]; /**< Switch for writing maximum pressure */
char write_press[4]; /**< Switch for writing press. time slices */
char header[512]; /**< Header of output raster files */
char header_nD[512]; /**< Header of forest-permeability raster */
/** Grid-related variables */
size_t m; /**< Number of grid nodes in W-E direction*/
size_t n; /**< Number of grid nodes in S-N direction*/
size_t i_min; /**< W boundary of active grid domain */
size_t i_max; /**< E boundary of active grid domain */
size_t j_min; /**< S boundary of active grid domain */
size_t j_max; /**< N boundary of active grid domain */
double xllcorner; /**< x-coord. lower left corner of grid */
double yllcorner; /**< y-coord. lower left corner of grid */
double cellsize; /**< Projected size of square cell */
double **dx; /**< Oblique W-E length of a cell */
double **dy; /**< Oblique S-N length of a cell */
double **dA; /**< Oblique area of a cell */
/** Physical constants and material properties */
double g = 9.81; /**< Gravitational acceleration (m/s^2) */
double **gx; /**< x-comp. of gravitat. acceleration */
double **gy; /**< y-comp. of gravitat. acceleration */
double **gz0; /**< z-comp. of gravitat. acceleration */
double **gz; /**< Bed-normal gravitational acceleration
incl. centrifugal acceleration */
double **kxx;
double **kxy; /**< Components of curvature tensor*/
double **kyy;
double **G_xy; /**< Rescaled off-diagonal metric tensor */
double rho = 250.0; /**< Flow density (kg/m^3) */
double rho_b = 200.0; /**< Bed (snow cover) density */
double rho_d = 200.0; /**< Deposit density */
double rrb = 1.25; /**< Ratio rho / rho_b */
double rrd = 1.25; /**< Ratio rho / rho_d */
double sigma = 1.0; /**< Factor in Grigorian–Ostroumov model */
double mu_g; /**< Tangent of friction angle in flow */
double mu_s0; /**< Constant bed friction coefficient */
double k_g; /**< Dimensionless turb. friction coeff. */
double kp = 1.0; /**< Passive earth-pressure coefficient */
double cD = 1.0; /**< Drag coefficient of a tree */
double nD_min = 0.001; /**< Resid. forest density after destruction*/
double MoR = 5.0e7; /**< Modulus of rupture of trees (Pa) */
double decay_coeff = 0.1; /**< Tree destruction coefficient (m/s) */
double h_drag = 0.0; /**< Effective max. flow depth in drag term */
double k_erod; /**< Erosion coefficient à la RAMMS */
char rheology[16]; /**< Type of friction law / rheology */
char params[9]; /**< Friction parameters can be "constant"
or "variable" */
int restart = 0; /**< Flag for non-zero initial velocity */
int para = 0; /**< Const./variable friction param. (0/1) */
int curve = 0; /**< Curvature effects (1/0) */
int forest = 0; /**< Drag in forest (0/1/2) */
int dyn_surf = 0; /**< Dynamic surface flag (1/0) */
int dep = 0; /**< Deposition flag (1/0) */
int eromod; /**< Numerical code for erosion model */
int grad; /**< Switch for shear strength profile */
long utm_code; /**< Coded UTM zone, between -60 and 60 */
int epsg; /**< EPSG code for geodetic datum */
/** Field variables, declared as pointers for dynamic memory allocation. */
double **h; /**< Flow height field */
double **u; /**< x-velocity field */
double **v; /**< y-velocity field */
double **s; /**< Speed field */
double **p_imp; /**< Impact pressure field */
double ***f_old; /**< Old values of conserved fields */
double ***f_new; /**< Time-stepped values of conserv. flds */
double ***src; /**< Area-integrated source terms */
double **d; /**< Field of deposition depth (m) */
double **z0; /**< Terrain surface elevation */
double **z; /**< Surface elevation incl. snow/deposit */
double **b; /**< Snow-cover (bed) depth (m) */
double **tau_c; /**< Bed shear strength (Pa) */
double **mu; /**< Dry-friction coefficient */
double **k; /**< Drag-friction coefficient */
double **mu_s; /**< Bed friction coefficient */
double **h_max; /**< Field of max. attained flow depths */
double **s_max; /**< Field of max. attained flow speeds */
double **p_max; /**< Field of max. attained impact press. */
double **u_max; /**< Field of max. attained u-velocity */
double **v_max; /**< Field of max. attained v-velocity */
double **b_min; /**< Minimum bed depth (due to erosion) */
double **d_max; /**< Max. deposition depth */
double **nD; /**< Field of forest opacity (n·D) */
double **tD; /**< Field of avg. tree diameter */
double **decay_const; /**< Coeff. in tree fall-down rate */
float *data; /**< Array holding data to be written */
/** Subroutines */
double **allocate2(size_t, size_t); /**< Dynamically allocate 2D array */
double ***allocate3(size_t, size_t, size_t); /**< 3D array */
void deallocate2(double **, size_t); /**< Deallocate 2D array */
void deallocate3(double ***, size_t, size_t); /**< Deallocate 3D array */
void allocate(void); /**< Dynamically allocate arrays */
void deallocate(void); /**< Deallocate dynamic arrays*/
void read_command_file(char *); /**< Does what it says! */
void read_grid_file(void); /**< Reads DTM into array */
void update_surface(double **); /**< Adapts surface to erosion/deposition,
(re-)calculates slope and curvature */
void read_init_file(void); /**< Set initial conditions from input */
int read_raster(char *, double **, double, double, double, double, int);
/**< Read data from AAIGrid file to array */
void write_data(double, double **, double **, double **, double **,
double **, double **, double **, double **, double **,
size_t, size_t, size_t, size_t, int, char *);
/**< Handles output from a time slice */
void writeout(double **, char *, char *, size_t, size_t, size_t, size_t,
char *, char *, char *); /**< Writes output data to files */
void primivar(double ***); /**< Computes primitive variables h,u,v,s
from conserved fields h, hu, hv */
double ***source_terms(void); /**< Computes source terms mass, momentum */
double find_dt(void); /**< New time step from CFL condition */
void update_boundaries(double ***, double *, double *);
/**< Determine new active region and
quantity of movement */
void create_dir(char *, char *); /**< Create output directories as needed */
/*******************/
/* */
/* Main function */
/* */
/*******************/
int main(int argc, char *argv[])
{
char reason[80];
size_t i, j, p, q;
int di, dj;
int n_step = 0;
int repeat_flag; /**< Time step needs to be repeated */
int stop_code = 0; /**< Reason why simulation terminated */
double aux, auy; /**< Auxiliary quantities */
double U, V; /**< Local velocity components */
double dAx, dAy, dAd; /**< Area fluxes to neighboring cells */
double qhx, qhy, qhd; /**< Mass fluxes between cells */
double qxx, qxy, qxd, qyx, qyy, qyd; /**< Momentum fluxes to neighbor cells */
double pWx, pEx, pSy, pNy; /**< Earth pressure at cell boundaries */
double F_drive_x, F_drive_y; /**< Gravity and earth-pressure grad. */
double F_drive_2, F_fric_2; /**< Driving & retarding forces squared */
double dir_cos, dir_sin;
double mom_tot; /**< Approx. total avalanche momentum */
double t_dmpp = 0.0; /**< Time of last write-out */
printf("\n");
printf("*****************************************************************\n");
printf("* *\n");
printf("* MoT-Voellmy v. %10s Dieter Issler, NGI *\n",
version);
printf("* *\n");
printf("* Quasi-3D simulation of snow avalanches over complex terrain, *\n");
printf("* based on the Voellmy friction law and the cell-centered for- *\n");
printf("* mulation of the Method of Transport (with wave effects cur- *\n");
printf("* rently neglected). Various erosion models are implemented. *\n");
printf("* Curvature-induced friction, braking by/breaking of forest as *\n");
printf("* well as dynamic surface evolution can be simulated. *\n");
printf("* *\n");
printf("*****************************************************************\n");
printf("\n\n");
if (argc != 2) {
printf(" Usage: MoT-Voellmy <input filename>\n\n");
exit(3);
}
/* Set up the calculation. */
read_command_file(argv[1]);
read_grid_file(); /* Load z0 and reference raster header. */
read_init_file(); /* Initializes all field variables, too. */
printf(" main: read_init_file completed.\n");
t = 0.0;
t_dump = -dt_dump;
n_dump = 0;
i_min = j_min = 0;
i_max = m; /* m×n nodes, (m-1)×(n-1) cells! */
j_max = n;
strncpy(reason, "time limit was reached", 23);
repeat_flag = 0; /* repeat initially false */
if (dyn_surf) {
for (i = i_min; i < i_max; i++)
for (j = j_min; j < j_max; j++)
z[i][j] = z0[i][j];
}
/* Time loop: */
while (t < t_max) {
printf(" main: Step %5d, t = %8.4f s, %7.0f m^3, ["ST","ST"]x["ST","ST"]\n",
n_step, t, mov_vol, i_min, i_max, j_min, j_max);
if (t >= t_dump + dt_dump && t_max >= dt_dump) {
write_data(t, h, h, b, d, s, u, v, p_imp, nD,
i_min, i_max, j_min, j_max, 1, fmt);
t_dmpp = t;
t_dump += dt_dump;
n_dump++;
}
if (curve == 0) /* Without curvature effects */
for (i = i_min; i < i_max; i++)
for (j = j_min; j < j_max; j++)
memcpy(f_old[i][j], f_new[i][j], 3*sizeof(double));
/**< NB. f_old is needed in case the timestep needs to be repeated. */
else if (curve == 1) { /* With curvature effects */
for (i = i_min; i < i_max; i++) {
for (j = j_min; j < j_max; j++) {
/**< NB. f_old is needed in case the timestep needs to be repeated. */
memcpy(f_old[i][j], f_new[i][j], 3*sizeof(double));
/* Normal force corrected for curvature effects, with gz limited to
non-negative values to prevent lift-off on convex terrain.
Contributed by Hervé Vicari, 2023. */
U = u[i][j]; V = v[i][j];
gz[i][j] = MAX(0.0, gz0[i][j] + kxx[i][j]*U*U + kyy[i][j]*V*V
+ 2.0*kxy[i][j]*U*V);
}
}
}
if ((dt = find_dt()) < dt_min) {
strncpy(reason, "timestep fell below lower bound", 32);
stop_code = 2;
printf(" main: dt set to %.5f s.\n", dt);
break; /* Leave time loop to shut down. */
}
src = source_terms();
for (i = i_min; i < i_max; i++) {
if (repeat_flag == 1) {
printf(".");
for (p = i_min; p < i_max; p++) /* Restore old field values */
for (q = j_min; q < j_max; q++)
memcpy(f_new[p][q], f_old[p][q], 3*sizeof(double));
repeat_flag = 0; /* Start i-loop again with reduced */
i = i_min - 1; /* timestep and f_new reset to f_old */
dt *= 0.8; /* by resetting i and `continue'. */
if (dt < dt_min) {
strncpy(reason, "timestep fell below lower bound", 32);
repeat_flag = -1; /* Signals failure by setting flag. */
stop_code = 2; /* Use as exit code at shut-down. */
break; /* Break out of i-loop; flag triggers*/
/* break statement below i-loop. */
}
continue; /* Start the i-loop over again; i is */
/* incremented from i_min-1 to i_min */
}
for (j = j_min; j < j_max; j++) {
/* Quantities used in all field components: */
di = (u[i][j] >= 0.0 ? 1 : -1);
dj = (v[i][j] >= 0.0 ? 1 : -1);
aux = fabs(u[i][j]) * dt;
auy = fabs(v[i][j]) * dt;
dAx = aux * (dy[i][j] - auy); /* Area flowing out in x-direction */
dAy = auy * (dx[i][j] - aux); /* Area flowing out in y-direction */
dAd = aux * auy; /* Outflow in diagonal direction */
/* Bed depth limits erosion, flow depth limits deposition: */
if (eromod > 0 && src[i][j][0] > 0.0) { /* Erosion */
/* Check erosion rate limit: */
src[i][j][0] = MIN( src[i][j][0], b[i][j]*dA[i][j]/(rrb*dt) );
/* Update erosion reservoir: */
b[i][j] = MAX(0.0, b[i][j] - src[i][j][0]*rrb*dt/dA[i][j]);
/* MAX(...) used to prevent spurious −0.0 rounding errors.
Contributed by Hervé Vicari and Callum Tregaskis. */
}
else if (dep > 0 && src[i][j][0] < 0) { /* Deposition */
/* Check deposition rate limit: */
src[i][j][0] = MAX( src[i][j][0], -f_old[i][j][0]/dt );
/* Update deposit reservoir: */
d[i][j] -= src[i][j][0] * rrd * dt / dA[i][j];
}
else src[i][j][0] = 0.0;
/* Advective mass fluxes: */
qhx = h[i][j] * dAx;
qhy = h[i][j] * dAy;
qhd = h[i][j] * dAd;
/* Advective momentum fluxes: */
qxx = qhx * u[i][j];
qxy = qhy * u[i][j];
qxd = qhd * u[i][j];
qyx = qhx * v[i][j];
qyy = qhy * v[i][j];
qyd = qhd * v[i][j];
f_new[i][j][0] -= (qhx + qhy + qhd - src[i][j][0]*dt);
f_new[i][j][1] -= qxx + qxy + qxd; /* Flowing out of cell (i,j) */
f_new[i][j][2] -= qyx + qyy + qyd;
if ((int) i + di >= 0 && (int) i + di < (int) m) {
f_new[(size_t) ((int)i+di)][j][0] += qhx; /* Can be ahead or behind */
f_new[(size_t) ((int)i+di)][j][1] += qxx; /* (i,j) depending on di */
f_new[(size_t) ((int)i+di)][j][2] += qyx;
}
if ((int) j + dj >= 0 && (int) j + dj < (int) n) {
f_new[i][(size_t) ((int)j+dj)][0] += qhy;
f_new[i][(size_t) ((int)j+dj)][1] += qxy;
f_new[i][(size_t) ((int)j+dj)][2] += qyy;
}
if ((int) i + di >= 0 && (int) i + di < (int) m
&& (int) j + dj >= 0 && (int) j + dj < (int) n) {
f_new[(size_t) ((int)i+di)][(size_t) ((int)j+dj)][0] += qhd;
f_new[(size_t) ((int)i+di)][(size_t) ((int)j+dj)][1] += qxd;
f_new[(size_t) ((int)i+di)][(size_t) ((int)j+dj)][2] += qyd;
}
/* Test for negative flow heights: */
if (f_new[i][j][0] < 0.0) {
repeat_flag = 1;
break; /* Break out of j-loop, start next */
} /* iteration of i-loop. */
/* Momentum fluxes due to pressure gradients:
Need to distinguish between empty cells (no pressure transmission),
non-empty cells in movement (friction forces fully activated), and
non-empty cells at rest, where the static friction force may or may
not be fully activated. */
/* Pressure gradient in x-direction */
if (i > i_min && i < i_max-1) {
pEx = 0.25 * kp * dy[i+1][j]
* (gz[i][j]+gz[i+1][j]) * h[i][j]*h[i+1][j];
pWx = 0.25 * kp * dy[i][j]
* (gz[i-1][j]+gz[i][j]) * h[i-1][j]*h[i][j];
}
/* Von Neumann boundary conditions for outermost cells: */
else if (i == i_min)
pWx = pEx = 0.25 * kp * dy[i+1][j]
* (gz[i][j]+gz[i+1][j]) * h[i][j]*h[i+1][j];
else if (i == i_max-1)
pEx = pWx = 0.25 * kp * dy[i][j]
* (gz[i-1][j]+gz[i][j]) * h[i-1][j]*h[i][j];
/* Include (superfluous) ELSE for sake of some compilers. */
else
pEx = pWx = 0.0;
/* Pressure gradient in y-direction */
if (j > j_min && j < j_max-1) {
pNy = 0.25 * kp * dx[i][j+1]
* (gz[i][j]+gz[i][j+1]) * h[i][j]*h[i][j+1];
pSy = 0.25 * kp * dx[i][j]
* (gz[i][j-1]+gz[i][j]) * h[i][j-1]*h[i][j];
}
/* Von Neumann boundary conditions for outermost cells: */
else if (j == j_min)
pSy = pNy = 0.25 * kp * dx[i][j+1]
* (gz[i][j]+gz[i][j+1]) * h[i][j]*h[i][j+1];
else if (j == j_max-1)
pNy = pSy = 0.25 * kp * dx[i][j]
* (gz[i][j-1]+gz[i][j]) * h[i][j-1]*h[i][j];
/* Include (superfluous) ELSE for sake of some compilers. */
else
pNy = pSy = 0.0;
/* Test whether non-empty cells at rest will start moving. */
if (s[i][j] <= u_min) {
/* Gravity and pressure gradient combined: */
F_drive_x = gx[i][j] * f_old[i][j][0] + pWx - pEx;
F_drive_y = gy[i][j] * f_old[i][j][0] + pSy - pNy;
F_drive_2 = SQ(F_drive_x) + SQ(F_drive_y)
+ 2.0 * G_xy[i][j] * F_drive_x * F_drive_y;
/* Is dry friction fully activated? If not, the driving and
resisting forces cancel and there is no need to add to f_new. */
F_fric_2 = SQ(mu[i][j] * gz[i][j] * f_old[i][j][0]);
if (F_drive_2 > F_fric_2) {
dir_cos = F_drive_x / sqrt(F_drive_2);
dir_sin = F_drive_y / sqrt(F_drive_2);
f_new[i][j][1] += (F_drive_x - dir_cos*sqrt(F_fric_2)) * dt;
f_new[i][j][2] += (F_drive_y - dir_sin*sqrt(F_fric_2)) * dt;
}
}
else {
f_new[i][j][1] += (pWx - pEx + src[i][j][1]) * dt;
f_new[i][j][2] += (pSy - pNy + src[i][j][2]) * dt;
}
}
}
if (repeat_flag == -1) break; /* Break out of time loop. */
/* If the momentum vector in a cell reverses direction, arrest
the cell unless the new direction is downhill: */
for (i = i_min; i < i_max; i++)
for (j = j_min; j < j_max; j++)
if (f_old[i][j][1]*f_new[i][j][1]
+ f_old[i][j][2]*f_new[i][j][2] < 0.0
&& f_new[i][j][1]*gx[i][j] + f_new[i][j][2]*gy[i][j] < 0.0) {
if (dep == 1) {
d[i][j] += f_new[i][j][0] / dA[i][j];
f_new[i][j][0] = 0.0;
}
f_new[i][j][1] = 0.0;
f_new[i][j][2] = 0.0;
}
/* Update surface elevation for dynamic bed computation: */
if (dyn_surf) {
for (i = i_min; i <= i_max; i++)
for (j = j_min; j <= j_max; j++)
/* Add bed layer and deposited layer to surface. */
z[i][j] = z0[i][j] + (b[i][j] + d[i][j]) * g / gz0[i][j];
update_surface(z);
}
/* Update boundaries and test if avalanche still moves: */
primivar(f_new);
update_boundaries(f_new, &mom_tot, &mom_max);
if ( ( (ifv >= 4 && mom_tot < mom_fraction*mom_max)
|| ((ifv == 2 || ifv == 3) && mom_tot < mom_thr) )
&& (n_step > 10) ) {
strncpy(reason, "avalanche has stopped or left the domain", 43);
stop_code = 1;
break; /* Break out of time loop. */
}
/* Update time: */
t += dt;
n_step++;
}
printf(" main: Finished time loop.\n");
/* End of time loop */
/* Write out last time step only if there is new data! */
if (t > t_dmpp && t_max >= dt_dump)
write_data(t, d, h, b, d, s, u, v, p_imp, nD, 0, m, 0, n,
1, fmt);
/* Write maximum fields over entire simulation (incl. deposit depth). */
write_data(t, d, h_max, b_min, d_max, s_max, u_max, v_max, p_max, nD,
0, m, 0, n, 2, fmt);
deallocate();
printf("\n Simulation terminated because %s.\n\n", reason);
exit(stop_code);
}
/**********************/
/* End of main(...) */
/**********************/
/*******************/
/* */
/* primivar(...) */
/* */
/*******************/
/** Computes the primitive variables h, u, v from the conservative quantities
h dA, h u dA, h v dA. Also, the speed s is computed for a non-orthogonal
coordinate system. */
void primivar(double ***f)
{
size_t i, j;
double aux1, aux2;
for (i = i_min; i < i_max; i++) {
for (j = j_min; j < j_max; j++) {
aux1 = 1.0 / dA[i][j];
aux2 = (f[i][j][0] > 0.0 ? 1.0 / MAX(f[i][j][0], h_min*dA[i][j]) : 0.0);
h[i][j] = f[i][j][0] * aux1;
u[i][j] = f[i][j][1] * aux2;
v[i][j] = f[i][j][2] * aux2;
p_imp[i][j] = SQ(u[i][j]) + SQ(v[i][j]) + 2.0*G_xy[i][j]*u[i][j]*v[i][j];
s[i][j] = sqrt(p_imp[i][j]);
p_imp[i][j] *= (0.001*rho); /* Pressures in kPa */
}
}
}
/**************************/
/* End of primivar(...) */
/**************************/
/***********************/
/* */
/* source_terms(...) */
/* */
/***********************/
/** Computes source terms in the equations for the conservative fields.
For the erosion rate in the TJEM model, note that tau_c is scaled with
the flow density in read_init_file(). */
double ***source_terms(void)
{
size_t i, j;
int variant;
double speed, dir_cos, dir_sin, cos_th;
double tau_b, tau_c_loc; /* Bed shear stress over density,
local bed shear strength */
double mu_loc, k_loc; /* Including forest effects */
double hs = 0.0; /* Snow cover depth for torque */
double bend_mom; /* Bending moment on tree */
double dp; /* Pressure à la Grigorian–Ostroumov */
double U, V, gxy; /* Local velocity, off-diag. metric */
double dbdx, dbdy; /* Change of snow depth in x, y-dir. */
double calpha, salpha, talpha; /* Cosine, sine, tangent of snow surface
slope angle in flow direction */
variant = 2 * para + forest; /* 0: constant, no forest
1: constant, with forest
2: variable, no forest
3: variable, with forest */
for (i = i_min; i < i_max; i++) {
for (j = j_min; j < j_max; j++) {
/* Local values that will come in handy: */
speed = s[i][j];
U = u[i][j];
V = v[i][j];
gxy = G_xy[i][j];
cos_th = SQ(cellsize) / dA[i][j];
/* Set friction parameters according to chosen variant: */
switch(variant) {
case 0 :
mu_loc = mu_g;
k_loc = k_g;
break;
case 1 :
mu_loc = mu_g + 1.25 * cos_th * nD[i][j]*h[i][j];
k_loc = k_g + 0.5*cD*cos_th*nD[i][j]*h[i][j];
break;
case 2 :
mu_loc = mu[i][j];
k_loc = k[i][j];
break;
case 3 :
mu_loc = mu[i][j] + 1.25 * cos_th * nD[i][j]*h[i][j];
k_loc = k[i][j] + 0.5*cD*cos_th*nD[i][j]*h[i][j];
break;
default: /* Cannot be reached, for compiler's sake. */
printf("\nIllegal value %d of \'variant\' --- STOP!\n\n", variant);
exit(21);
}
/* Option to increase the drag term by a factor so that it does not
vanish if the flow depth becomes excessive in channelized areas. No
changes as h → 0, but as h → ∞, the drag deceleration is like for
h = h_drag. */
if (h_drag > 0.0)
k_loc /= (1.0 - exp(-h_drag / MAX(h[i][j], h_min)));
/* Maximum shear stress at the bottom of the flow and shear strength
of bed including Coulombic contribution. This must be computed before
adding the braking effect of forest: */
tau_b = mu_loc*gz[i][j]*h[i][j] + k_loc*SQ(speed);
/* Erosion term: */
switch(eromod) {
case 0 : /* No erosion */
src[i][j][0] = 0.0;
break;
case 1 : /* RAMMS erosion model */
if (h[i][j] > h_min && speed > 1.0)
src[i][j][0] = k_erod * speed * dA[i][j];
else
src[i][j][0] = 0.0;
break;
case 2 : /* Tangential-jump erosion model */
/* grad = 0 if snow-cover strength assumed constant with depth,
grad = 1 if vertical strength gradient is spatially constant,
grad = 2 if vertical strength gradient is read from file. */
tau_c_loc = (grad < 2 ? tau_c[i][j] + mu_s0*gz[i][j]*h[i][j] \
: tau_c[i][j] + mu_s[i][j]*gz[i][j]*h[i][j]);
/* Erosion rate prop. to the excess of rheological stress over bed
shear strength: */
src[i][j][0] = (speed > 10.0*u_min && h[i][j] > 10.0*h_min ? \
MAX(0.0, tau_b - tau_c_loc) * dA[i][j] / speed : \
0.0);
/* In the TJEM, the bed shear stress is limited to τ_c if erodible
bed material is present: */
if (src[i][j][0] > 0.0 && b[i][j] > 0.0)
tau_b = MIN(tau_c_loc, tau_b);
break;
case 3 : /* com1DFA erosion model (AvaFrame) */
/* τ_c is here taken to represent the specific erosion energy e_b
in the com1DFA entrainment module. It has the same dimensions
m²/s² as the specific bed shear stress μ·g_z·h + k·u². There are
no indicative values quoted in the com1DFA manual, but one may
assume that e_b is roughly 100 times larger than typical values
of μ·g_z·h + k·u², i.e., in the range 200–10,000 m²/s². */
if (h[i][j] > h_min && speed > 1.0)
src[i][j][0] = speed * dA[i][j] / tau_c[i][j] \
* (mu[i][j]*gz[i][j]*h[i][j] + k[i][j]*SQ(speed));
else
src[i][j][0] = 0.0;
break;
case 4 : /* Grigorian–Ostroumov (GOEM) */
/* Gradient of snow surface relative to terrain: */
dbdx = (i > 0 && i < m-1 ? 0.5 * (b[i+1][j]-b[i-1][j]) / dx[i][j] \
: (i == 0 ? (b[1][j]-b[0][j]) / dx[0][j] \
: (b[m-1][j]-b[m-2][j])/dx[m-2][j]
)
);
dbdy = (j > 0 && j < n-1 ? 0.5 * (b[i][j+1]-b[i][j-1]) / dy[i][j] \
: (j == 0 ? (b[i][1]-b[i][0]) / dy[i][0] \
: (b[i][n-1]-b[i][n-2])/dy[i][n-2]
)
);
/* Slope angle of snow surface rel. to terrain in flow direction: */
talpha = ((U + V*gxy)*dbdx + (V + U*gxy)*dbdy) / MAX(0.01, speed);
calpha = 1.0 / sqrt(1.0 + SQ(talpha));
salpha = talpha * calpha;
/* Excess pressure dp and strength τ_c are scaled by ρ! Include
depth-dependent bed strength as in TJEM */
tau_c_loc = (grad < 2 ? tau_c[i][j] + mu_s0*gz[i][j]*h[i][j] \
: tau_c[i][j] + mu_s[i][j]*gz[i][j]*h[i][j]);
dp = MAX(0.0, gz[i][j]*h[i][j]*calpha + k_erod*SQ(speed)*salpha \
- tau_c[i][j]);
src[i][j][0] = sigma * sqrt(dp) * dA[i][j] * calpha;
break;
default : /* To satisfy purists... */
printf("\n Erosion model #%d not implemented. STOP!\n\n", eromod);
exit(29);
}
/* Momentum sources (gravity and friction): */
if (speed > u_min) { /* Friction opposing flow direction */
dir_cos = u[i][j] / speed;
dir_sin = v[i][j] / speed;
src[i][j][1] = (gx[i][j]*h[i][j] - dir_cos*tau_b) * dA[i][j];
src[i][j][2] = (gy[i][j]*h[i][j] - dir_sin*tau_b) * dA[i][j];
}
/* What is the fate of the forest?
forest=0: no forest; forest=1: braking effect, can be destroyed
If forest density is below residual value, do nothing. Otherwise: */
if ((forest == 1) && (nD[i][j] > nD_min)) {
/* If no erosion, assume 1.0 m snow depth for moment calculation: */
hs = (eromod > 0 ? b[i][j] : 1.0);
/* Check whether the forest in the cell is still intact: */
if (decay_const[i][j] == 0.0) { /* No destruction yet */
/* Compare bending moment on average tree to its strength: */
bend_mom = 0.25 * cD * rho * (SQ(speed) + 5.0*g*h[i][j]*cos_th)
* tD[i][j] * h[i][j] * (h[i][j] + 2.0*hs);
if (bend_mom > MoR * tD[i][j]*tD[i][j]*tD[i][j])
/* Tree breaks or is uprooted. Estimate time for it to fall and
lose braking effect on avalanche: */
decay_const[i][j] = decay_coeff / tD[i][j];
}
else { /* Destruction ongoing */
/* Approximate an exponential decay by first two terms: */
nD[i][j] *= MAX(0.0, 1.0 - decay_const[i][j]*dt);
}
}
}
}
return(src);
}
/******************************/
/* End of source_terms(...) */
/******************************/
/******************/
/* */
/* find_dt(...) */
/* */
/******************/
/** The time step is computed on the basis of the maximum velocity of the
forward "acoustic" wave. Note that source terms have the potential to
"empty" a cell more quickly; the main routine checks for this and repeats
a time step with reduced dt if necessary. */
double find_dt(void)
{
size_t i, j;
double aux;
dt = 1000.0;
for (i = i_min; i < i_max; i++) {
for (j = j_min; j < j_max; j++) {
aux = MAX(sqrt(SQ(u[i][j])+SQ(v[i][j])) + sqrt(gz[i][j]*h[i][j]), u_min);
dt = MIN(cfl * MIN(dx[i][j], dy[i][j]) / aux, dt);
}
}
dt = MIN(dt, dt_max);
return(dt);
}
/*************************/
/* End of find_dt(...) */
/*************************/
/****************************/
/* */
/* update_boundaries(...) */
/* */
/****************************/
/** This function is called to (i) determine whether the flow has effectively
stopped, (ii) update the fields of maximum values, and (iii) determine the
"active" region of the computational domain, i.e., the smallest rectangle
outside of which the flow height or the speed are below user-specified
thresholds. (One row of cells is added in every direction to prevent
spurious effects.) */
void update_boundaries(double ***f, double *mom_tot, double *mom_max)
{
size_t i, j;
int west = (int) m, east = 0, south = (int) n, north = 0;
double mom = 0.0, speed, vol_min, tot_vol = 0.0;
mov_vol = 0.0;
for (i = i_min; i < i_max; i++) {
for (j = j_min; j < j_max; j++) {
vol_min = h_min * dA[i][j];
speed = s[i][j];
/* Boundaries of active domain */
if (f[i][j][0] > vol_min && speed > u_min) {
west = MIN(west, ( int) i-1);
east = MAX(east, (int) i+1);
south = MIN(south, (int) j-1);
north = MAX(north, (int) j+1);
mov_vol += f[i][j][0];
}
/* Maximum fields, written to file(s) at end of run. Note u_max, v_max
are components of max. speed and not actual max(u) and max(v). */
h_max[i][j] = MAX(h_max[i][j], h[i][j]);
if (speed > s_max[i][j]) {
s_max[i][j] = speed;
u_max[i][j] = u[i][j];
v_max[i][j] = v[i][j];
p_max[i][j] = 0.001 * rho * SQ(speed);
}
mom += speed * f[i][j][0];
if (eromod > 0) /* Update erodible snow depth */
b_min[i][j] = MIN(b[i][j], b_min[i][j]);
if (dep > 0)
d_max[i][j] = MAX(d[i][j], d_max[i][j]);
}
}
i_min = (size_t) MAX(0, west);
i_max = MIN(m, (size_t) east + 1); /* m×n nodes, (m−1)×(n−1) cells */
j_min = (size_t) MAX(0, south);
j_max = MIN(n, (size_t) north + 1);
for (i = 0; i < m; i++)
for (j = 0; j < n; j++)
tot_vol += f[i][j][0];
*mom_tot = mom; /* Update instant. qty. of movement */
*mom_max = MAX(*mom_max, *mom_tot); /* Update max. qty. of movement */
}
/***********************************/
/* End of update_boundaries(...) */
/***********************************/
/*************************/
/* */
/* read_grid_file(...) */
/* */
/*************************/
/** Reads the grid file and computes the needed geometrical information. */
void read_grid_file(void)
{
char nodeline[256], xll[10], yll[10];
int lest = 0;
short shorty;
float floaty;
double doubl, NaN;
if ((gfp = fopen(grid_fn, "r")) == NULL) {
printf(" read_grid_file: Failed to open %s. STOP!\n\n", grid_fn);
exit(30);
}
/* Read the header, which will be used in the output files: */
lest += fscanf(gfp, "ncols "ST"\nnrows "ST"\n%s %lf\n%s %lf\n",
&m, &n, xll, &xllcorner, yll, &yllcorner);
lest += fscanf(gfp, "cellsize %lf\nNODATA_value %lf\n", &cellsize, &NaN);
if (lest != 8) {
printf("\n read_grid_file: Incorrect grid file header. STOP!\n\n");
exit(31);
}
if (!strcmp(xll, "xllcenter")) /* If necessary, convert from */
xllcorner -= (0.5*cellsize); /* cell center to cell corner */
if (!strcmp(yll, "yllcenter")) /* coordinates */
yllcorner -= (0.5*cellsize);
if (!strncmp(fmt, "wb", 2)) { /* BinaryTerrain */
strncpy(header, "binterr1.3", 11);
memcpy(header+10, &m, 4); /* bth.xdim */
memcpy(header+14, &n, 4); /* bth.ydim */
shorty = (short) 4;
memcpy(header+18, &shorty, 2); /* bth.datasize */
shorty = (short) 1;
memcpy(header+20, &shorty, 2); /* bth.fp_flag */
memcpy(header+22, &shorty, 2); /* bth.horiz_unit */
shorty = (short) utm_code;
memcpy(header+24, &shorty, 2); /* bth.utm */
shorty = (short) epsg;
memcpy(header+26, &shorty, 2); /* bth.epsg */
memcpy(header+28, &xllcorner, 8); /* bth.W_ext */
doubl = (double) (xllcorner + (double) m * cellsize);
memcpy(header+36, &doubl, 8); /* bth.E_ext */
memcpy(header+44, &yllcorner, 8); /* bth.S_ext */
doubl = (double) (yllcorner + (double) n * cellsize);
memcpy(header+52, &doubl, 8); /* bth.N_ext */
shorty = (short) 0;
memcpy(header+60, &shorty, 2); /* bth.prj_flag */
floaty = (float) 1.0;
memcpy(header+62, &floaty, 4); /* bth.scale */
strncpy(header+66, "MoT-Voellmy "VERSION, 24);