n_gwflow.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. /*****************************************************************************
  2. *
  3. * MODULE: Grass PDE Numerical Library
  4. * AUTHOR(S): Soeren Gebbert, Berlin (GER) Dec 2006
  5. * soerengebbert <at> gmx <dot> de
  6. *
  7. * PURPOSE: groundwater flow in porous media
  8. * part of the gpde library
  9. *
  10. * COPYRIGHT: (C) 2000 by the GRASS Development Team
  11. *
  12. * This program is free software under the GNU General Public
  13. * License (>=v2). Read the file COPYING that comes with GRASS
  14. * for details.
  15. *
  16. *****************************************************************************/
  17. #include <grass/N_gwflow.h>
  18. /* *************************************************************** */
  19. /* ***************** N_gwflow_data3d ***************************** */
  20. /* *************************************************************** */
  21. /*!
  22. * \brief Alllocate memory for the groundwater calculation data structure in 3 dimensions
  23. *
  24. * The groundwater calculation data structure will be allocated including
  25. * all appendant 3d and 2d arrays. The offset for the 3d arrays is one
  26. * to establish homogeneous Neumann boundary conditions at the calculation area border.
  27. * This data structure is used to create a linear equation system based on the computation of
  28. * groundwater flow in porous media with the finite volume method.
  29. *
  30. * \param cols int
  31. * \param rows int
  32. * \param depths int
  33. * \return N_gwflow_data3d *
  34. * */
  35. N_gwflow_data3d *N_alloc_gwflow_data3d(int cols, int rows, int depths,
  36. int river, int drain)
  37. {
  38. N_gwflow_data3d *data;
  39. data = (N_gwflow_data3d *) G_calloc(1, sizeof(N_gwflow_data3d));
  40. data->phead = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  41. data->phead_start = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  42. data->status = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  43. data->hc_x = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  44. data->hc_y = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  45. data->hc_z = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  46. data->q = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  47. data->s = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  48. data->nf = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  49. data->r = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  50. if (river) {
  51. data->river_head =
  52. N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  53. data->river_leak =
  54. N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  55. data->river_bed = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  56. }
  57. else {
  58. data->river_head = NULL;
  59. data->river_leak = NULL;
  60. data->river_bed = NULL;
  61. }
  62. if (drain) {
  63. data->drain_leak =
  64. N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  65. data->drain_bed = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  66. }
  67. else {
  68. data->drain_leak = NULL;
  69. data->drain_bed = NULL;
  70. }
  71. return data;
  72. }
  73. /* *************************************************************** */
  74. /* ********************* N_free_gwflow_data3d ******************** */
  75. /* *************************************************************** */
  76. /*!
  77. * \brief Release the memory of the groundwater flow data structure in three dimensions
  78. *
  79. * \param data N_gwflow_data3d *
  80. * \return void *
  81. * */
  82. void N_free_gwflow_data3d(N_gwflow_data3d * data)
  83. {
  84. if (data->phead)
  85. N_free_array_3d(data->phead);
  86. if (data->phead_start)
  87. N_free_array_3d(data->phead_start);
  88. if (data->status)
  89. N_free_array_3d(data->status);
  90. if (data->hc_x)
  91. N_free_array_3d(data->hc_x);
  92. if (data->hc_y)
  93. N_free_array_3d(data->hc_y);
  94. if (data->hc_z)
  95. N_free_array_3d(data->hc_z);
  96. if (data->q)
  97. N_free_array_3d(data->q);
  98. if (data->s)
  99. N_free_array_3d(data->s);
  100. if (data->nf)
  101. N_free_array_3d(data->nf);
  102. if (data->r)
  103. N_free_array_2d(data->r);
  104. if (data->river_head)
  105. N_free_array_3d(data->river_head);
  106. if (data->river_leak)
  107. N_free_array_3d(data->river_leak);
  108. if (data->river_bed)
  109. N_free_array_3d(data->river_bed);
  110. if (data->drain_leak)
  111. N_free_array_3d(data->drain_leak);
  112. if (data->drain_bed)
  113. N_free_array_3d(data->drain_bed);
  114. G_free(data);
  115. data = NULL;
  116. return;
  117. }
  118. /* *************************************************************** */
  119. /* ******************** N_alloc_gwflow_data2d ******************** */
  120. /* *************************************************************** */
  121. /*!
  122. * \brief Alllocate memory for the groundwater calculation data structure in 2 dimensions
  123. *
  124. * The groundwater calculation data structure will be allocated including
  125. * all appendant 2d arrays. The offset for the 3d arrays is one
  126. * to establish homogeneous Neumann boundary conditions at the calculation area border.
  127. * This data structure is used to create a linear equation system based on the computation of
  128. * groundwater flow in porous media with the finite volume method.
  129. *
  130. * \param cols int
  131. * \param rows int
  132. * \return N_gwflow_data2d *
  133. * */
  134. N_gwflow_data2d *N_alloc_gwflow_data2d(int cols, int rows, int river,
  135. int drain)
  136. {
  137. N_gwflow_data2d *data;
  138. data = (N_gwflow_data2d *) G_calloc(1, sizeof(N_gwflow_data2d));
  139. data->phead = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  140. data->phead_start = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  141. data->status = N_alloc_array_2d(cols, rows, 1, CELL_TYPE);
  142. data->hc_x = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  143. data->hc_y = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  144. data->q = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  145. data->s = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  146. data->nf = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  147. data->r = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  148. data->top = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  149. data->bottom = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  150. if (river) {
  151. data->river_head = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  152. data->river_leak = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  153. data->river_bed = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  154. }
  155. else {
  156. data->river_head = NULL;
  157. data->river_leak = NULL;
  158. data->river_bed = NULL;
  159. }
  160. if (drain) {
  161. data->drain_leak = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  162. data->drain_bed = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  163. }
  164. else {
  165. data->drain_leak = NULL;
  166. data->drain_bed = NULL;
  167. }
  168. return data;
  169. }
  170. /* *************************************************************** */
  171. /* ****************** N_free_gwflow_data2d *********************** */
  172. /* *************************************************************** */
  173. /*!
  174. * \brief Release the memory of the groundwater flow data structure in two dimensions
  175. *
  176. * \param data N_gwflow_data2d *
  177. * \return void
  178. * */
  179. void N_free_gwflow_data2d(N_gwflow_data2d * data)
  180. {
  181. if (data->phead)
  182. N_free_array_2d(data->phead);
  183. if (data->phead_start)
  184. N_free_array_2d(data->phead_start);
  185. if (data->status)
  186. N_free_array_2d(data->status);
  187. if (data->hc_x)
  188. N_free_array_2d(data->hc_x);
  189. if (data->hc_y)
  190. N_free_array_2d(data->hc_y);
  191. if (data->q)
  192. N_free_array_2d(data->q);
  193. if (data->s)
  194. N_free_array_2d(data->s);
  195. if (data->nf)
  196. N_free_array_2d(data->nf);
  197. if (data->r)
  198. N_free_array_2d(data->r);
  199. if (data->top)
  200. N_free_array_2d(data->top);
  201. if (data->bottom)
  202. N_free_array_2d(data->bottom);
  203. if (data->river_head)
  204. N_free_array_2d(data->river_head);
  205. if (data->river_leak)
  206. N_free_array_2d(data->river_leak);
  207. if (data->river_bed)
  208. N_free_array_2d(data->river_bed);
  209. if (data->drain_leak)
  210. N_free_array_2d(data->drain_leak);
  211. if (data->drain_bed)
  212. N_free_array_2d(data->drain_bed);
  213. G_free(data);
  214. data = NULL;;
  215. return;
  216. }
  217. /* *************************************************************** */
  218. /* ***************** N_callback_gwflow_3d ************************ */
  219. /* *************************************************************** */
  220. /*!
  221. * \brief This callback function creates the mass balance of a 7 point star
  222. *
  223. * The mass balance is based on the common groundwater flow equation:
  224. *
  225. * \f[Ss \frac{\partial h}{\partial t} = \nabla {\bf K} \nabla h + q \f]
  226. *
  227. * This equation is discretizised with the finite volume method in three dimensions.
  228. *
  229. *
  230. * \param gwdata N_gwflow_data3d *
  231. * \param geom N_geom_data *
  232. * \param col int
  233. * \param row int
  234. * \param depth int
  235. * \return N_data_star *
  236. *
  237. * */
  238. N_data_star *N_callback_gwflow_3d(void *gwdata, N_geom_data * geom, int col,
  239. int row, int depth)
  240. {
  241. double hc_e = 0, hc_w = 0, hc_n = 0, hc_s = 0, hc_t = 0, hc_b = 0;
  242. double dx, dy, dz, Ax, Ay, Az;
  243. double hc_x, hc_y, hc_z;
  244. double hc_xw, hc_yn, hc_zt;
  245. double hc_xe, hc_ys, hc_zb;
  246. double hc_start;
  247. double Ss, r, nf, q;
  248. double C, W, E, N, S, T, B, V;
  249. N_data_star *mat_pos;
  250. N_gwflow_data3d *data;
  251. /*cast the void pointer to the right data structure */
  252. data = (N_gwflow_data3d *) gwdata;
  253. dx = geom->dx;
  254. dy = geom->dy;
  255. dz = geom->dz;
  256. Az = N_get_geom_data_area_of_cell(geom, row);
  257. Ay = geom->dx * geom->dz;
  258. Ax = geom->dz * geom->dy;
  259. /*read the data from the arrays */
  260. hc_start = N_get_array_3d_d_value(data->phead_start, col, row, depth);
  261. hc_x = N_get_array_3d_d_value(data->hc_x, col, row, depth);
  262. hc_y = N_get_array_3d_d_value(data->hc_y, col, row, depth);
  263. hc_z = N_get_array_3d_d_value(data->hc_z, col, row, depth);
  264. hc_xw = N_get_array_3d_d_value(data->hc_x, col - 1, row, depth);
  265. hc_xe = N_get_array_3d_d_value(data->hc_x, col + 1, row, depth);
  266. hc_yn = N_get_array_3d_d_value(data->hc_y, col, row - 1, depth);
  267. hc_ys = N_get_array_3d_d_value(data->hc_y, col, row + 1, depth);
  268. hc_zt = N_get_array_3d_d_value(data->hc_z, col, row, depth + 1);
  269. hc_zb = N_get_array_3d_d_value(data->hc_z, col, row, depth - 1);
  270. hc_w = N_calc_harmonic_mean(hc_xw, hc_x);
  271. hc_e = N_calc_harmonic_mean(hc_xe, hc_x);
  272. hc_n = N_calc_harmonic_mean(hc_yn, hc_y);
  273. hc_s = N_calc_harmonic_mean(hc_ys, hc_y);
  274. hc_t = N_calc_harmonic_mean(hc_zt, hc_z);
  275. hc_b = N_calc_harmonic_mean(hc_zb, hc_z);
  276. /*inner sources */
  277. q = N_get_array_3d_d_value(data->q, col, row, depth);
  278. /*storativity */
  279. Ss = N_get_array_3d_d_value(data->s, col, row, depth);
  280. /*porosity */
  281. nf = N_get_array_3d_d_value(data->nf, col, row, depth);
  282. /*mass balance center cell to western cell */
  283. W = -1 * Ax * hc_w / dx;
  284. /*mass balance center cell to eastern cell */
  285. E = -1 * Ax * hc_e / dx;
  286. /*mass balance center cell to northern cell */
  287. N = -1 * Ay * hc_n / dy;
  288. /*mass balance center cell to southern cell */
  289. S = -1 * Ay * hc_s / dy;
  290. /*mass balance center cell to top cell */
  291. T = -1 * Az * hc_t / dz;
  292. /*mass balance center cell to bottom cell */
  293. B = -1 * Az * hc_b / dz;
  294. /*storativity */
  295. Ss = Az * dz * Ss;
  296. /*the diagonal entry of the matrix */
  297. C = -1 * (W + E + N + S + T + B - Ss / data->dt * Az);
  298. /*the entry in the right side b of Ax = b */
  299. V = (q + hc_start * Ss / data->dt * Az);
  300. /*only the top cells will have recharge */
  301. if (depth == geom->depths - 2) {
  302. r = N_get_array_2d_d_value(data->r, col, row);
  303. V += r * Az;
  304. }
  305. G_debug(5, "N_callback_gwflow_3d: called [%i][%i][%i]", depth, col, row);
  306. /*create the 7 point star entries */
  307. mat_pos = N_create_7star(C, W, E, N, S, T, B, V);
  308. return mat_pos;
  309. }
  310. /* *************************************************************** */
  311. /* ****************** N_gwflow_3d_calc_water_budget ************** */
  312. /* *************************************************************** */
  313. /*!
  314. * \brief This function computes the water budget of the entire groundwater
  315. *
  316. * The water budget is calculated for each active and dirichlet cell from
  317. * its surrounding neighbours. This is based on the 7 star mass balance computation
  318. * of N_callback_gwflow_3d and the gradient of the water heights in the cells.
  319. * The sum of the water budget of each active/dirichlet cell must be near zero
  320. * due the effect of numerical inaccuracy of cpu's.
  321. *
  322. * \param gwdata N_gwflow_data3d *
  323. * \param geom N_geom_data *
  324. * \param budget N_array_3d
  325. * \return void
  326. *
  327. * */
  328. void
  329. N_gwflow_3d_calc_water_budget(N_gwflow_data3d * data, N_geom_data * geom, N_array_3d * budget)
  330. {
  331. int z, y, x, stat;
  332. double h, hc;
  333. double val;
  334. double sum;
  335. N_data_star *dstar;
  336. int rows = data->status->rows;
  337. int cols = data->status->cols;
  338. int depths = data->status->depths;
  339. sum = 0;
  340. for (z = 0; z < depths; z++) {
  341. for (y = 0; y < rows; y++) {
  342. G_percent(y, rows - 1, 10);
  343. for (x = 0; x < cols; x++) {
  344. stat = (int)N_get_array_3d_d_value(data->status, x, y, z);
  345. val = 0.0;
  346. if (stat != N_CELL_INACTIVE ) { /*all active/dirichlet cells */
  347. /* Compute the flow parameter */
  348. dstar = N_callback_gwflow_3d(data, geom, x, y, z);
  349. /* Compute the gradient in each direction pointing from the center */
  350. hc = N_get_array_3d_d_value(data->phead, x, y, z);
  351. if((int)N_get_array_3d_d_value(data->status, x + 1, y , z) != N_CELL_INACTIVE) {
  352. h = N_get_array_3d_d_value(data->phead, x + 1, y , z);
  353. val += dstar->E * (hc - h);
  354. }
  355. if((int)N_get_array_3d_d_value(data->status, x - 1, y , z) != N_CELL_INACTIVE) {
  356. h = N_get_array_3d_d_value(data->phead, x - 1, y , z);
  357. val += dstar->W * (hc - h);
  358. }
  359. if((int)N_get_array_3d_d_value(data->status, x , y + 1, z) != N_CELL_INACTIVE) {
  360. h = N_get_array_3d_d_value(data->phead, x , y + 1, z);
  361. val += dstar->S * (hc - h);
  362. }
  363. if((int)N_get_array_3d_d_value(data->status, x , y - 1, z) != N_CELL_INACTIVE) {
  364. h = N_get_array_3d_d_value(data->phead, x , y - 1, z);
  365. val += dstar->N * (hc - h);
  366. }
  367. if((int)N_get_array_3d_d_value(data->status, x , y , z + 1) != N_CELL_INACTIVE) {
  368. h = N_get_array_3d_d_value(data->phead, x , y , z + 1);
  369. val += dstar->T * (hc - h);
  370. }
  371. if((int)N_get_array_3d_d_value(data->status, x , y , z - 1) != N_CELL_INACTIVE) {
  372. h = N_get_array_3d_d_value(data->phead, x , y , z - 1);
  373. val += dstar->B * (hc - h);
  374. }
  375. sum += val;
  376. G_free(dstar);
  377. }
  378. else {
  379. Rast_set_null_value(&val, 1, DCELL_TYPE);
  380. }
  381. N_put_array_3d_d_value(budget, x, y, z, val);
  382. }
  383. }
  384. }
  385. if(fabs(sum) < 0.0000000001)
  386. G_message(_("The total sum of the water budget: %g\n"), sum);
  387. else
  388. G_warning(_("The total sum of the water budget is significantly larger then 0: %g\n"), sum);
  389. return;
  390. }
  391. /* *************************************************************** */
  392. /* ****************** N_callback_gwflow_2d *********************** */
  393. /* *************************************************************** */
  394. /*!
  395. * \brief This callback function creates the mass balance of a 5 point star
  396. *
  397. * The mass balance is based on the common groundwater flow equation:
  398. *
  399. * \f[Ss \frac{\partial h}{\partial t} = \nabla {\bf K} \nabla h + q \f]
  400. *
  401. * This equation is discretizised with the finite volume method in two dimensions.
  402. *
  403. * \param gwdata N_gwflow_data2d *
  404. * \param geom N_geom_data *
  405. * \param col int
  406. * \param row int
  407. * \return N_data_star *
  408. *
  409. * */
  410. N_data_star *N_callback_gwflow_2d(void *gwdata, N_geom_data * geom, int col,
  411. int row)
  412. {
  413. double T_e = 0, T_w = 0, T_n = 0, T_s = 0;
  414. double z_e = 0, z_w = 0, z_n = 0, z_s = 0;
  415. double dx, dy, Az;
  416. double hc_x, hc_y;
  417. double z, top;
  418. double hc_xw, hc_yn;
  419. double z_xw, z_yn;
  420. double hc_xe, hc_ys;
  421. double z_xe, z_ys;
  422. double hc, hc_start;
  423. double Ss, r, q;
  424. double C, W, E, N, S, V;
  425. N_gwflow_data2d *data;
  426. N_data_star *mat_pos;
  427. double river_vect = 0; /*entry in vector */
  428. double river_mat = 0; /*entry in matrix */
  429. double drain_vect = 0; /*entry in vector */
  430. double drain_mat = 0; /*entry in matrix */
  431. /*cast the void pointer to the right data structure */
  432. data = (N_gwflow_data2d *) gwdata;
  433. dx = geom->dx;
  434. dy = geom->dy;
  435. Az = N_get_geom_data_area_of_cell(geom, row);
  436. /*read the data from the arrays */
  437. hc_start = N_get_array_2d_d_value(data->phead_start, col, row);
  438. hc = N_get_array_2d_d_value(data->phead, col, row);
  439. top = N_get_array_2d_d_value(data->top, col, row);
  440. /* Inner sources */
  441. q = N_get_array_2d_d_value(data->q, col, row);
  442. /* storativity or porosity of current cell face [-]*/
  443. Ss = N_get_array_2d_d_value(data->s, col, row);
  444. /* recharge */
  445. r = N_get_array_2d_d_value(data->r, col, row) * Az;
  446. if (hc > top) { /*If the aquifer is confined */
  447. z = N_get_array_2d_d_value(data->top, col,
  448. row) -
  449. N_get_array_2d_d_value(data->bottom, col, row);
  450. z_xw =
  451. N_get_array_2d_d_value(data->top, col - 1,
  452. row) -
  453. N_get_array_2d_d_value(data->bottom, col - 1, row);
  454. z_xe =
  455. N_get_array_2d_d_value(data->top, col + 1,
  456. row) -
  457. N_get_array_2d_d_value(data->bottom, col + 1, row);
  458. z_yn =
  459. N_get_array_2d_d_value(data->top, col,
  460. row - 1) -
  461. N_get_array_2d_d_value(data->bottom, col, row - 1);
  462. z_ys =
  463. N_get_array_2d_d_value(data->top, col,
  464. row + 1) -
  465. N_get_array_2d_d_value(data->bottom, col, row + 1);
  466. }
  467. else { /* the aquifer is unconfined */
  468. /* If the aquifer is unconfied use an explicite scheme to solve
  469. * the nonlinear equation. We use the phead from the first iteration */
  470. z = N_get_array_2d_d_value(data->phead, col, row) -
  471. N_get_array_2d_d_value(data->bottom, col, row);
  472. z_xw = N_get_array_2d_d_value(data->phead, col - 1, row) -
  473. N_get_array_2d_d_value(data->bottom, col - 1, row);
  474. z_xe = N_get_array_2d_d_value(data->phead, col + 1, row) -
  475. N_get_array_2d_d_value(data->bottom, col + 1, row);
  476. z_yn = N_get_array_2d_d_value(data->phead, col, row - 1) -
  477. N_get_array_2d_d_value(data->bottom, col, row - 1);
  478. z_ys = N_get_array_2d_d_value(data->phead, col, row + 1) -
  479. N_get_array_2d_d_value(data->bottom, col, row + 1);
  480. }
  481. /*geometrical mean of cell height */
  482. if (z_w > 0 || z_w < 0 || z_w == 0)
  483. z_w = N_calc_arith_mean(z_xw, z);
  484. else
  485. z_w = z;
  486. if (z_e > 0 || z_e < 0 || z_e == 0)
  487. z_e = N_calc_arith_mean(z_xe, z);
  488. else
  489. z_e = z;
  490. if (z_n > 0 || z_n < 0 || z_n == 0)
  491. z_n = N_calc_arith_mean(z_yn, z);
  492. else
  493. z_n = z;
  494. if (z_s > 0 || z_s < 0 || z_s == 0)
  495. z_s = N_calc_arith_mean(z_ys, z);
  496. else
  497. z_s = z;
  498. /*get the surrounding permeabilities */
  499. hc_x = N_get_array_2d_d_value(data->hc_x, col, row);
  500. hc_y = N_get_array_2d_d_value(data->hc_y, col, row);
  501. hc_xw = N_get_array_2d_d_value(data->hc_x, col - 1, row);
  502. hc_xe = N_get_array_2d_d_value(data->hc_x, col + 1, row);
  503. hc_yn = N_get_array_2d_d_value(data->hc_y, col, row - 1);
  504. hc_ys = N_get_array_2d_d_value(data->hc_y, col, row + 1);
  505. /* calculate the transmissivities */
  506. T_w = N_calc_harmonic_mean(hc_xw, hc_x) * z_w;
  507. T_e = N_calc_harmonic_mean(hc_xe, hc_x) * z_e;
  508. T_n = N_calc_harmonic_mean(hc_yn, hc_y) * z_n;
  509. T_s = N_calc_harmonic_mean(hc_ys, hc_y) * z_s;
  510. /* Compute the river leakage, this is an explicit method
  511. * Influent and effluent flow is computed.
  512. */
  513. if (data->river_leak &&
  514. (N_get_array_2d_d_value(data->river_leak, col, row) != 0) &&
  515. N_get_array_2d_d_value(data->river_bed, col, row) <= top) {
  516. /* Groundwater surface is above the river bed*/
  517. if (hc > N_get_array_2d_d_value(data->river_bed, col, row)) {
  518. river_vect = N_get_array_2d_d_value(data->river_head, col, row) *
  519. N_get_array_2d_d_value(data->river_leak, col, row);
  520. river_mat = N_get_array_2d_d_value(data->river_leak, col, row);
  521. } /* Groundwater surface is below the river bed */
  522. else if (hc < N_get_array_2d_d_value(data->river_bed, col, row)) {
  523. river_vect = (N_get_array_2d_d_value(data->river_head, col, row) -
  524. N_get_array_2d_d_value(data->river_bed, col, row))
  525. * N_get_array_2d_d_value(data->river_leak, col, row);
  526. river_mat = 0;
  527. }
  528. }
  529. /* compute the drainage, this is an explicit method
  530. * Drainage is only enabled, if the drain bed is lower the groundwater surface
  531. */
  532. if (data->drain_leak &&
  533. (N_get_array_2d_d_value(data->drain_leak, col, row) != 0) &&
  534. N_get_array_2d_d_value(data->drain_bed, col, row) <= top) {
  535. if (hc > N_get_array_2d_d_value(data->drain_bed, col, row)) {
  536. drain_vect = N_get_array_2d_d_value(data->drain_bed, col, row) *
  537. N_get_array_2d_d_value(data->drain_leak, col, row);
  538. drain_mat = N_get_array_2d_d_value(data->drain_leak, col, row);
  539. }
  540. else if (hc <= N_get_array_2d_d_value(data->drain_bed, col, row)) {
  541. drain_vect = 0;
  542. drain_mat = 0;
  543. }
  544. }
  545. /*mass balance center cell to western cell */
  546. W = -1 * T_w * dy / dx;
  547. /*mass balance center cell to eastern cell */
  548. E = -1 * T_e * dy / dx;
  549. /*mass balance center cell to northern cell */
  550. N = -1 * T_n * dx / dy;
  551. /*mass balance center cell to southern cell */
  552. S = -1 * T_s * dx / dy;
  553. /*the diagonal entry of the matrix */
  554. C = -1 * (W + E + N + S - Az *Ss / data->dt - river_mat * Az -
  555. drain_mat * Az);
  556. /*the entry in the right side b of Ax = b */
  557. V = (q + hc_start * Az * Ss / data->dt) + r + river_vect * Az +
  558. drain_vect * Az;
  559. G_debug(5, "N_callback_gwflow_2d: called [%i][%i]", row, col);
  560. /*create the 5 point star entries */
  561. mat_pos = N_create_5star(C, W, E, N, S, V);
  562. return mat_pos;
  563. }
  564. /* *************************************************************** */
  565. /* ****************** N_gwflow_2d_calc_water_budget ************** */
  566. /* *************************************************************** */
  567. /*!
  568. * \brief This function computes the water budget of the entire groundwater
  569. *
  570. * The water budget is calculated for each active and dirichlet cell from
  571. * its surrounding neighbours. This is based on the 5 star mass balance computation
  572. * of N_callback_gwflow_2d and the gradient of the water heights in the cells.
  573. * The sum of the water budget of each active/dirichlet cell must be near zero
  574. * due the effect of numerical inaccuracy of cpu's.
  575. *
  576. * \param gwdata N_gwflow_data2d *
  577. * \param geom N_geom_data *
  578. * \param budget N_array_2d
  579. * \return void
  580. *
  581. * */
  582. void
  583. N_gwflow_2d_calc_water_budget(N_gwflow_data2d * data, N_geom_data * geom, N_array_2d * budget)
  584. {
  585. int y, x, stat;
  586. double h, hc;
  587. double val;
  588. double sum;
  589. N_data_star *dstar;
  590. int rows = data->status->rows;
  591. int cols = data->status->cols;
  592. sum = 0;
  593. for (y = 0; y < rows; y++) {
  594. G_percent(y, rows - 1, 10);
  595. for (x = 0; x < cols; x++) {
  596. stat = N_get_array_2d_c_value(data->status, x, y);
  597. val = 0.0;
  598. if (stat != N_CELL_INACTIVE ) { /*all active/dirichlet cells */
  599. /* Compute the flow parameter */
  600. dstar = N_callback_gwflow_2d(data, geom, x, y);
  601. /* Compute the gradient in each direction pointing from the center */
  602. hc = N_get_array_2d_d_value(data->phead, x, y);
  603. if((int)N_get_array_2d_d_value(data->status, x + 1, y ) != N_CELL_INACTIVE) {
  604. h = N_get_array_2d_d_value(data->phead, x + 1, y);
  605. val += dstar->E * (hc - h);
  606. }
  607. if((int)N_get_array_2d_d_value(data->status, x - 1, y ) != N_CELL_INACTIVE) {
  608. h = N_get_array_2d_d_value(data->phead, x - 1, y);
  609. val += dstar->W * (hc - h);
  610. }
  611. if((int)N_get_array_2d_d_value(data->status, x , y + 1) != N_CELL_INACTIVE) {
  612. h = N_get_array_2d_d_value(data->phead, x , y + 1);
  613. val += dstar->S * (hc - h);
  614. }
  615. if((int)N_get_array_2d_d_value(data->status, x , y - 1) != N_CELL_INACTIVE) {
  616. h = N_get_array_2d_d_value(data->phead, x , y - 1);
  617. val += dstar->N * (hc - h);
  618. }
  619. sum += val;
  620. G_free(dstar);
  621. }
  622. else {
  623. Rast_set_null_value(&val, 1, DCELL_TYPE);
  624. }
  625. N_put_array_2d_d_value(budget, x, y, val);
  626. }
  627. }
  628. if(fabs(sum) < 0.0000000001)
  629. G_message(_("The total sum of the water budget: %g\n"), sum);
  630. else
  631. G_warning(_("The total sum of the water budget is significantly larger then 0: %g\n"), sum);
  632. return;
  633. }