N_solute_transport.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  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: solute transport in porous media
  8. * part of the gpde library
  9. *
  10. * COPYRIGHT: (C) 2007 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 <math.h>
  18. #include <grass/N_solute_transport.h>
  19. /* ************************************************************************* *
  20. * ************************************************************************* *
  21. * ************************************************************************* */
  22. /*! \brief This is just a placeholder
  23. *
  24. * */
  25. N_data_star *N_callback_solute_transport_3d(void *solutedata,
  26. N_geom_data * geom, int col,
  27. int row, int depth)
  28. {
  29. double Df_e = 0, Df_w = 0, Df_n = 0, Df_s = 0, Df_t = 0, Df_b = 0;
  30. double dx, dy, dz, Az;
  31. double diff_x, diff_y, diff_z;
  32. double diff_xw, diff_yn;
  33. double diff_xe, diff_ys;
  34. double diff_zt, diff_zb;
  35. double cin = 0, cg, cg_start;
  36. double R, nf, cs, q;
  37. double C, W, E, N, S, T, B, V;
  38. double vw = 0, ve = 0, vn = 0, vs = 0, vt = 0, vb = 0;
  39. double Ds_w = 0, Ds_e = 0, Ds_n = 0, Ds_s = 0, Ds_t = 0, Ds_b = 0;
  40. double Dw = 0, De = 0, Dn = 0, Ds = 0, Dt = 0, Db = 0;
  41. double rw = 0.5, re = 0.5, rn = 0.5, rs = 0.5, rt = 0.5, rb = 0.5;
  42. N_solute_transport_data3d *data = NULL;
  43. N_data_star *mat_pos;
  44. N_gradient_3d grad;
  45. /*cast the void pointer to the right data structure */
  46. data = (N_solute_transport_data3d *) solutedata;
  47. N_get_gradient_3d(data->grad, &grad, col, row, depth);
  48. dx = geom->dx;
  49. dy = geom->dy;
  50. dz = geom->dz;
  51. Az = N_get_geom_data_area_of_cell(geom, row);
  52. /*read the data from the arrays */
  53. cg_start = N_get_array_3d_d_value(data->c_start, col, row, depth);
  54. cg = N_get_array_3d_d_value(data->c, col, row, depth);
  55. /*get the surrounding diffusion tensor entries */
  56. diff_x = N_get_array_3d_d_value(data->diff_x, col, row, depth);
  57. diff_y = N_get_array_3d_d_value(data->diff_y, col, row, depth);
  58. diff_z = N_get_array_3d_d_value(data->diff_z, col, row, depth);
  59. diff_xw = N_get_array_3d_d_value(data->diff_x, col - 1, row, depth);
  60. diff_xe = N_get_array_3d_d_value(data->diff_x, col + 1, row, depth);
  61. diff_yn = N_get_array_3d_d_value(data->diff_y, col, row - 1, depth);
  62. diff_ys = N_get_array_3d_d_value(data->diff_y, col, row + 1, depth);
  63. diff_zt = N_get_array_3d_d_value(data->diff_z, col, row, depth + 1);
  64. diff_zb = N_get_array_3d_d_value(data->diff_z, col, row, depth - 1);
  65. /* calculate the diffusion on the cell borders using the harmonical mean */
  66. Df_w = N_calc_harmonic_mean(diff_xw, diff_x);
  67. Df_e = N_calc_harmonic_mean(diff_xe, diff_x);
  68. Df_n = N_calc_harmonic_mean(diff_yn, diff_y);
  69. Df_s = N_calc_harmonic_mean(diff_ys, diff_y);
  70. Df_t = N_calc_harmonic_mean(diff_zt, diff_z);
  71. Df_b = N_calc_harmonic_mean(diff_zb, diff_z);
  72. /* calculate the dispersion */
  73. /*todo */
  74. /* calculate the velocity parts with full upwinding scheme */
  75. vw = grad.WC;
  76. ve = grad.EC;
  77. vn = grad.NC;
  78. vs = grad.SC;
  79. vt = grad.TC;
  80. vb = grad.BC;
  81. /* put the diffusion and dispersion together */
  82. Dw = ((Df_w + Ds_w)) / dx;
  83. De = ((Df_e + Ds_e)) / dx;
  84. Dn = ((Df_n + Ds_n)) / dy;
  85. Ds = ((Df_s + Ds_s)) / dy;
  86. Dt = ((Df_t + Ds_t)) / dz;
  87. Db = ((Df_b + Ds_b)) / dz;
  88. rw = N_exp_upwinding(-1 * vw, dx, Dw);
  89. re = N_exp_upwinding(ve, dx, De);
  90. rs = N_exp_upwinding(-1 * vs, dy, Ds);
  91. rn = N_exp_upwinding(vn, dy, Dn);
  92. rb = N_exp_upwinding(-1 * vb, dz, Dn);
  93. rt = N_exp_upwinding(vt, dz, Dn);
  94. /*mass balance center cell to western cell */
  95. W = -1 * (Dw) * dy * dz - vw * (1 - rw) * dy * dz;
  96. /*mass balance center cell to eastern cell */
  97. E = -1 * (De) * dy * dz + ve * (1 - re) * dy * dz;
  98. /*mass balance center cell to southern cell */
  99. S = -1 * (Ds) * dx * dz - vs * (1 - rs) * dx * dz;
  100. /*mass balance center cell to northern cell */
  101. N = -1 * (Dn) * dx * dz + vn * (1 - rn) * dx * dz;
  102. /*mass balance center cell to bottom cell */
  103. B = -1 * (Db) * Az - vb * (1 - rb) * Az;
  104. /*mass balance center cell to top cell */
  105. T = -1 * (Dt) * Az + vt * (1 - rt) * Az;
  106. /* Retardation */
  107. R = N_get_array_3d_d_value(data->R, col, row, depth);
  108. /* Inner sources */
  109. cs = N_get_array_3d_d_value(data->cs, col, row, depth);
  110. /* effective porosity */
  111. nf = N_get_array_3d_d_value(data->nf, col, row, depth);
  112. /* groundwater sources and sinks */
  113. q = N_get_array_3d_d_value(data->q, col, row, depth);
  114. /* concentration of influent water */
  115. cin = N_get_array_3d_d_value(data->cin, col, row, depth);
  116. /*the diagonal entry of the matrix */
  117. C = ((Dw - vw) * dy * dz +
  118. (De + ve) * dy * dz +
  119. (Ds - vs) * dx * dz +
  120. (Dn + vn) * dx * dz +
  121. (Db - vb) * Az + (Dt + vt) * Az + Az * dz * R / data->dt - q / nf);
  122. /*the entry in the right side b of Ax = b */
  123. V = (cs + cg_start * Az * dz * R / data->dt - q / nf * cin);
  124. /*
  125. * printf("nf %g\n", nf);
  126. * printf("q %g\n", q);
  127. * printf("cs %g\n", cs);
  128. * printf("cin %g\n", cin);
  129. * printf("cg %g\n", cg);
  130. * printf("cg_start %g\n", cg_start);
  131. * printf("Az %g\n", Az);
  132. * printf("z %g\n", z);
  133. * printf("R %g\n", R);
  134. * printf("dt %g\n", data->dt);
  135. */
  136. G_debug(6, "N_callback_solute_transport_3d: called [%i][%i][%i]", row,
  137. col, depth);
  138. /*create the 7 point star entries */
  139. mat_pos = N_create_7star(C, W, E, N, S, T, B, V);
  140. return mat_pos;
  141. }
  142. /* ************************************************************************* *
  143. * ************************************************************************* *
  144. * ************************************************************************* */
  145. /*!
  146. * \brief This callback function creates the mass balance of a 5 point star
  147. *
  148. * The mass balance is based on the common solute transport equation:
  149. *
  150. * \f[\frac{\partial c_g}{\partial t} R = \nabla \cdot ({\bf D} \nabla c_g - {\bf u} c_g) + \sigma + \frac{q}{n_f}(c_g - c_in) \f]
  151. *
  152. * This equation is discretizised with the finite volume method in two dimensions.
  153. *
  154. *
  155. * \param solutedata * N_solute_transport_data2d - a void pointer to the data structure
  156. * \param geom N_geom_data *
  157. * \param col int
  158. * \param row int
  159. * \return N_data_star * - a five point data star
  160. *
  161. * */
  162. N_data_star *N_callback_solute_transport_2d(void *solutedata,
  163. N_geom_data * geom, int col,
  164. int row)
  165. {
  166. double Df_e = 0, Df_w = 0, Df_n = 0, Df_s = 0;
  167. double z_e = 0, z_w = 0, z_n = 0, z_s = 0;
  168. double dx, dy, Az;
  169. double diff_x, diff_y;
  170. double disp_x, disp_y;
  171. double z;
  172. double diff_xw, diff_yn;
  173. double disp_xw, disp_yn;
  174. double z_xw, z_yn;
  175. double diff_xe, diff_ys;
  176. double disp_xe, disp_ys;
  177. double z_xe, z_ys;
  178. double cin = 0, cg, cg_start;
  179. double R, nf, cs, q;
  180. double C, W, E, N, S, V, NE, NW, SW, SE;
  181. double vw = 0, ve = 0, vn = 0, vs = 0;
  182. double Ds_w = 0, Ds_e = 0, Ds_n = 0, Ds_s = 0;
  183. double Dw = 0, De = 0, Dn = 0, Ds = 0;
  184. double rw = 0.5, re = 0.5, rn = 0.5, rs = 0.5;
  185. N_solute_transport_data2d *data = NULL;
  186. N_data_star *mat_pos;
  187. N_gradient_2d grad;
  188. /*cast the void pointer to the right data structure */
  189. data = (N_solute_transport_data2d *) solutedata;
  190. N_get_gradient_2d(data->grad, &grad, col, row);
  191. dx = geom->dx;
  192. dy = geom->dy;
  193. Az = N_get_geom_data_area_of_cell(geom, row);
  194. /*read the data from the arrays */
  195. cg_start = N_get_array_2d_d_value(data->c_start, col, row);
  196. cg = N_get_array_2d_d_value(data->c, col, row);
  197. /* calculate the cell height */
  198. z = N_get_array_2d_d_value(data->top, col,
  199. row) -
  200. N_get_array_2d_d_value(data->bottom, col, row);
  201. z_xw =
  202. N_get_array_2d_d_value(data->top, col - 1,
  203. row) -
  204. N_get_array_2d_d_value(data->bottom, col - 1, row);
  205. z_xe =
  206. N_get_array_2d_d_value(data->top, col + 1,
  207. row) -
  208. N_get_array_2d_d_value(data->bottom, col + 1, row);
  209. z_yn =
  210. N_get_array_2d_d_value(data->top, col,
  211. row - 1) -
  212. N_get_array_2d_d_value(data->bottom, col, row - 1);
  213. z_ys =
  214. N_get_array_2d_d_value(data->top, col,
  215. row + 1) -
  216. N_get_array_2d_d_value(data->bottom, col, row + 1);
  217. /*geometrical mean of cell height */
  218. z_w = N_calc_geom_mean(z_xw, z);
  219. z_e = N_calc_geom_mean(z_xe, z);
  220. z_n = N_calc_geom_mean(z_yn, z);
  221. z_s = N_calc_geom_mean(z_ys, z);
  222. /*get the surrounding diffusion tensor entries */
  223. diff_x = N_get_array_2d_d_value(data->diff_x, col, row);
  224. diff_y = N_get_array_2d_d_value(data->diff_y, col, row);
  225. diff_xw = N_get_array_2d_d_value(data->diff_x, col - 1, row);
  226. diff_xe = N_get_array_2d_d_value(data->diff_x, col + 1, row);
  227. diff_yn = N_get_array_2d_d_value(data->diff_y, col, row - 1);
  228. diff_ys = N_get_array_2d_d_value(data->diff_y, col, row + 1);
  229. /* calculate the diffusion at the cell borders using the harmonical mean */
  230. Df_w = N_calc_harmonic_mean(diff_xw, diff_x);
  231. Df_e = N_calc_harmonic_mean(diff_xe, diff_x);
  232. Df_n = N_calc_harmonic_mean(diff_yn, diff_y);
  233. Df_s = N_calc_harmonic_mean(diff_ys, diff_y);
  234. /* calculate the dispersion */
  235. /*get the surrounding dispersion tensor entries */
  236. disp_x = N_get_array_2d_d_value(data->disp_xx, col, row);
  237. disp_y = N_get_array_2d_d_value(data->disp_yy, col, row);
  238. if (N_get_array_2d_d_value(data->status, col - 1, row) ==
  239. N_CELL_TRANSMISSION) {
  240. disp_xw = disp_x;
  241. }
  242. else {
  243. disp_xw = N_get_array_2d_d_value(data->disp_xx, col - 1, row);
  244. }
  245. if (N_get_array_2d_d_value(data->status, col + 1, row) ==
  246. N_CELL_TRANSMISSION) {
  247. disp_xe = disp_x;
  248. }
  249. else {
  250. disp_xe = N_get_array_2d_d_value(data->disp_xx, col + 1, row);
  251. }
  252. if (N_get_array_2d_d_value(data->status, col, row - 1) ==
  253. N_CELL_TRANSMISSION) {
  254. disp_yn = disp_y;
  255. }
  256. else {
  257. disp_yn = N_get_array_2d_d_value(data->disp_yy, col, row - 1);
  258. }
  259. if (N_get_array_2d_d_value(data->status, col, row + 1) ==
  260. N_CELL_TRANSMISSION) {
  261. disp_ys = disp_y;
  262. }
  263. else {
  264. disp_ys = N_get_array_2d_d_value(data->disp_yy, col, row + 1);
  265. }
  266. /* calculate the dispersion at the cell borders using the harmonical mean */
  267. Ds_w = N_calc_harmonic_mean(disp_xw, disp_x);
  268. Ds_e = N_calc_harmonic_mean(disp_xe, disp_x);
  269. Ds_n = N_calc_harmonic_mean(disp_yn, disp_y);
  270. Ds_s = N_calc_harmonic_mean(disp_ys, disp_y);
  271. /* put the diffusion and dispersion together */
  272. Dw = ((Df_w + Ds_w)) / dx;
  273. De = ((Df_e + Ds_e)) / dx;
  274. Ds = ((Df_s + Ds_s)) / dy;
  275. Dn = ((Df_n + Ds_n)) / dy;
  276. vw = -1.0 * grad.WC;
  277. ve = grad.EC;
  278. vs = -1.0 * grad.SC;
  279. vn = grad.NC;
  280. if (data->stab == N_UPWIND_FULL) {
  281. rw = N_full_upwinding(vw, dx, Dw);
  282. re = N_full_upwinding(ve, dx, De);
  283. rs = N_full_upwinding(vs, dy, Ds);
  284. rn = N_full_upwinding(vn, dy, Dn);
  285. }
  286. else if (data->stab == N_UPWIND_EXP) {
  287. rw = N_exp_upwinding(vw, dx, Dw);
  288. re = N_exp_upwinding(ve, dx, De);
  289. rs = N_exp_upwinding(vs, dy, Ds);
  290. rn = N_exp_upwinding(vn, dy, Dn);
  291. }
  292. /*mass balance center cell to western cell */
  293. W = -1 * (Dw) * dy * z_w + vw * (1 - rw) * dy * z_w;
  294. /*mass balance center cell to eastern cell */
  295. E = -1 * (De) * dy * z_e + ve * (1 - re) * dy * z_e;
  296. /*mass balance center cell to southern cell */
  297. S = -1 * (Ds) * dx * z_s + vs * (1 - rs) * dx * z_s;
  298. /*mass balance center cell to northern cell */
  299. N = -1 * (Dn) * dx * z_n + vn * (1 - rn) * dx * z_n;
  300. NW = 0.0;
  301. SW = 0.0;
  302. NE = 0.0;
  303. SE = 0.0;
  304. /* Retardation */
  305. R = N_get_array_2d_d_value(data->R, col, row);
  306. /* Inner sources */
  307. cs = N_get_array_2d_d_value(data->cs, col, row);
  308. /* effective porosity */
  309. nf = N_get_array_2d_d_value(data->nf, col, row);
  310. /* groundwater sources and sinks */
  311. q = N_get_array_2d_d_value(data->q, col, row);
  312. /* concentration of influent water */
  313. cin = N_get_array_2d_d_value(data->cin, col, row);
  314. /*the diagonal entry of the matrix */
  315. C = (Dw + vw * rw) * dy * z_w +
  316. (De + ve * re) * dy * z_e +
  317. (Ds + vs * rs) * dx * z_s +
  318. (Dn + vn * rn) * dx * z_n + Az * z * R / data->dt - q / nf;
  319. /*the entry in the right side b of Ax = b */
  320. V = (cs + cg_start * Az * z * R / data->dt + q / nf * cin);
  321. /*
  322. fprintf(stderr, "nf %g\n", nf);
  323. fprintf(stderr, "q %g\n", q);
  324. fprintf(stderr, "cs %g\n", cs);
  325. fprintf(stderr, "cin %g\n", cin);
  326. fprintf(stderr, "cg %g\n", cg);
  327. fprintf(stderr, "cg_start %g\n", cg_start);
  328. fprintf(stderr, "Az %g\n", Az);
  329. fprintf(stderr, "z %g\n", z);
  330. fprintf(stderr, "R %g\n", R);
  331. fprintf(stderr, "dt %g\n", data->dt);
  332. */
  333. G_debug(6, "N_callback_solute_transport_2d: called [%i][%i]", row, col);
  334. /*create the 9 point star entries */
  335. mat_pos = N_create_9star(C, W, E, N, S, NW, SW, NE, SE, V);
  336. return mat_pos;
  337. }
  338. /* ************************************************************************* *
  339. * ************************************************************************* *
  340. * ************************************************************************* */
  341. /*!
  342. * \brief Alllocate memory for the solute transport data structure in three dimensions
  343. *
  344. * The solute transport data structure will be allocated including
  345. * all appendant 3d arrays. The offset for the 3d arrays is one
  346. * to establish homogeneous Neumann boundary conditions at the calculation area border.
  347. * This data structure is used to create a linear equation system based on the computation of
  348. * solute transport in porous media with the finite volume method.
  349. *
  350. * \param cols int
  351. * \param rows int
  352. * \param depths int
  353. * \return N_solute_transport_data3d *
  354. * */
  355. N_solute_transport_data3d *N_alloc_solute_transport_data3d(int cols, int rows,
  356. int depths)
  357. {
  358. N_solute_transport_data3d *data = NULL;
  359. data =
  360. (N_solute_transport_data3d *) G_calloc(1,
  361. sizeof
  362. (N_solute_transport_data3d));
  363. data->c = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  364. data->c_start = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  365. data->status = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  366. data->diff_x = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  367. data->diff_y = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  368. data->diff_z = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  369. data->q = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  370. data->cs = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  371. data->R = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  372. data->nf = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  373. data->cin = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  374. /*Allocate the dispersivity tensor */
  375. data->disp_xx = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  376. data->disp_yy = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  377. data->disp_zz = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  378. data->disp_xy = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  379. data->disp_xz = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  380. data->disp_yz = N_alloc_array_3d(cols, rows, depths, 1, DCELL_TYPE);
  381. data->grad = N_alloc_gradient_field_3d(cols, rows, depths);
  382. data->stab = N_UPWIND_EXP;
  383. return data;
  384. }
  385. /* ************************************************************************* *
  386. * ************************************************************************* *
  387. * ************************************************************************* */
  388. /*!
  389. * \brief Alllocate memory for the solute transport data structure in two dimensions
  390. *
  391. * The solute transport data structure will be allocated including
  392. * all appendant 2d arrays. The offset for the 2d arrays is one
  393. * to establish homogeneous Neumann boundary conditions at the calculation area border.
  394. * This data structure is used to create a linear equation system based on the computation of
  395. * solute transport in porous media with the finite volume method.
  396. *
  397. * \param cols int
  398. * \param rows int
  399. * \return N_solute_transport_data2d *
  400. * */
  401. N_solute_transport_data2d *N_alloc_solute_transport_data2d(int cols, int rows)
  402. {
  403. N_solute_transport_data2d *data = NULL;
  404. data =
  405. (N_solute_transport_data2d *) G_calloc(1,
  406. sizeof
  407. (N_solute_transport_data2d));
  408. data->c = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  409. data->c_start = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  410. data->status = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  411. data->diff_x = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  412. data->diff_y = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  413. data->q = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  414. data->cs = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  415. data->R = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  416. data->nf = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  417. data->cin = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  418. data->top = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  419. data->bottom = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  420. /*Allocate the dispersivity tensor */
  421. data->disp_xx = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  422. data->disp_yy = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  423. data->disp_xy = N_alloc_array_2d(cols, rows, 1, DCELL_TYPE);
  424. data->grad = N_alloc_gradient_field_2d(cols, rows);
  425. data->stab = N_UPWIND_EXP;
  426. return data;
  427. }
  428. /* ************************************************************************* *
  429. * ************************************************************************* *
  430. * ************************************************************************* */
  431. /*!
  432. * \brief Release the memory of the solute transport data structure in three dimensions
  433. *
  434. * \param data N_solute_transport_data2d *
  435. * \return void *
  436. * */
  437. void N_free_solute_transport_data3d(N_solute_transport_data3d * data)
  438. {
  439. N_free_array_3d(data->c);
  440. N_free_array_3d(data->c_start);
  441. N_free_array_3d(data->status);
  442. N_free_array_3d(data->diff_x);
  443. N_free_array_3d(data->diff_y);
  444. N_free_array_3d(data->diff_z);
  445. N_free_array_3d(data->q);
  446. N_free_array_3d(data->cs);
  447. N_free_array_3d(data->R);
  448. N_free_array_3d(data->nf);
  449. N_free_array_3d(data->cin);
  450. N_free_array_3d(data->disp_xx);
  451. N_free_array_3d(data->disp_yy);
  452. N_free_array_3d(data->disp_zz);
  453. N_free_array_3d(data->disp_xy);
  454. N_free_array_3d(data->disp_xz);
  455. N_free_array_3d(data->disp_yz);
  456. G_free(data);
  457. data = NULL;
  458. return;
  459. }
  460. /* ************************************************************************* *
  461. * ************************************************************************* *
  462. * ************************************************************************* */
  463. /*!
  464. * \brief Release the memory of the solute transport data structure in two dimensions
  465. *
  466. * \param data N_solute_transport_data2d *
  467. * \return void *
  468. * */
  469. void N_free_solute_transport_data2d(N_solute_transport_data2d * data)
  470. {
  471. N_free_array_2d(data->c);
  472. N_free_array_2d(data->c_start);
  473. N_free_array_2d(data->status);
  474. N_free_array_2d(data->diff_x);
  475. N_free_array_2d(data->diff_y);
  476. N_free_array_2d(data->q);
  477. N_free_array_2d(data->cs);
  478. N_free_array_2d(data->R);
  479. N_free_array_2d(data->nf);
  480. N_free_array_2d(data->cin);
  481. N_free_array_2d(data->top);
  482. N_free_array_2d(data->bottom);
  483. N_free_array_2d(data->disp_xx);
  484. N_free_array_2d(data->disp_yy);
  485. N_free_array_2d(data->disp_xy);
  486. G_free(data);
  487. data = NULL;
  488. return;
  489. }
  490. /*!
  491. * \brief Compute the transmission boundary condition in 2d
  492. *
  493. * This function calculates the transmission boundary condition
  494. * for each cell with status N_CELL_TRANSMISSION. The surrounding
  495. * gradient field is used to verfiy the flow direction. If a flow
  496. * goes into a cell, the concentration (data->c) from the neighbour cell is
  497. * added to the transmission cell. If the flow from several neighbour
  498. * cells goes into the cell, the concentration mean is calculated.
  499. *
  500. * The new concentrations are written into the data->c_start array,
  501. * so they can be handled by the matrix assembling function.
  502. *
  503. * \param data N_solute_transport_data2d *
  504. * \return void *
  505. * */
  506. void N_calc_solute_transport_transmission_2d(N_solute_transport_data2d * data)
  507. {
  508. int i, j, count = 1;
  509. int cols, rows;
  510. double c;
  511. N_gradient_2d grad;
  512. cols = data->grad->cols;
  513. rows = data->grad->rows;
  514. G_debug(2,
  515. "N_calc_solute_transport_transmission_2d: calculating transmission boundary");
  516. for (j = 0; j < rows; j++) {
  517. for (i = 0; i < cols; i++) {
  518. if (N_get_array_2d_d_value(data->status, i, j) ==
  519. N_CELL_TRANSMISSION) {
  520. count = 0;
  521. /*get the gradient neighbours */
  522. N_get_gradient_2d(data->grad, &grad, i, j);
  523. c = 0;
  524. /*
  525. c = N_get_array_2d_d_value(data->c_start, i, j);
  526. if(c > 0)
  527. count++;
  528. */
  529. if (grad.WC > 0 &&
  530. !N_is_array_2d_value_null(data->c, i - 1, j)) {
  531. c += N_get_array_2d_d_value(data->c, i - 1, j);
  532. count++;
  533. }
  534. if (grad.EC < 0 &&
  535. !N_is_array_2d_value_null(data->c, i + 1, j)) {
  536. c += N_get_array_2d_d_value(data->c, i + 1, j);
  537. count++;
  538. }
  539. if (grad.NC < 0 &&
  540. !N_is_array_2d_value_null(data->c, i, j - 1)) {
  541. c += N_get_array_2d_d_value(data->c, i, j - 1);
  542. count++;
  543. }
  544. if (grad.SC > 0 &&
  545. !N_is_array_2d_value_null(data->c, i, j + 1)) {
  546. c += N_get_array_2d_d_value(data->c, i, j + 1);
  547. count++;
  548. }
  549. if (count != 0)
  550. c = c / (double)count;
  551. /*make sure it is not NAN */
  552. if (c > 0 || c == 0 || c < 0)
  553. N_put_array_2d_d_value(data->c_start, i, j, c);
  554. }
  555. }
  556. }
  557. return;
  558. }
  559. /*!
  560. * \brief Compute the dispersivity tensor based on the solute transport data in 2d
  561. *
  562. * The dispersivity tensor is stored in the data structure.
  563. * To compute the dispersivity tensor, the dispersivity lentghs and the gradient field
  564. * must be present.
  565. *
  566. * This is just a simple tensor computation which should be extended.
  567. *
  568. * \todo Change the tensor calculation to a mor realistic algorithm
  569. *
  570. * \param data N_solute_transport_data2d *
  571. * \return void *
  572. * */
  573. void N_calc_solute_transport_disptensor_2d(N_solute_transport_data2d * data)
  574. {
  575. int i, j;
  576. int cols, rows;
  577. double vx, vy, vv;
  578. double disp_xx, disp_yy, disp_xy;
  579. N_gradient_2d grad;
  580. cols = data->grad->cols;
  581. rows = data->grad->rows;
  582. G_debug(2,
  583. "N_calc_solute_transport_disptensor_2d: calculating the dispersivity tensor");
  584. for (j = 0; j < rows; j++) {
  585. for (i = 0; i < cols; i++) {
  586. disp_xx = 0;
  587. disp_yy = 0;
  588. disp_xy = 0;
  589. /*get the gradient neighbours */
  590. N_get_gradient_2d(data->grad, &grad, i, j);
  591. vx = (grad.WC + grad.EC) / 2;
  592. vy = (grad.NC + grad.SC) / 2;
  593. vv = sqrt(vx * vx + vy * vy);
  594. if (vv != 0) {
  595. disp_xx = data->al * vx * vx / vv + data->at * vy * vy / vv;
  596. disp_yy = data->at * vx * vx / vv + data->al * vy * vy / vv;
  597. disp_xy = (data->al - data->at) * vx * vy / vv;
  598. }
  599. G_debug(5,
  600. "N_calc_solute_transport_disptensor_2d: [%i][%i] disp_xx %g disp_yy %g disp_xy %g",
  601. i, j, disp_xx, disp_yy, disp_xy);
  602. N_put_array_2d_d_value(data->disp_xx, i, j, disp_xx);
  603. N_put_array_2d_d_value(data->disp_yy, i, j, disp_yy);
  604. N_put_array_2d_d_value(data->disp_xy, i, j, disp_xy);
  605. }
  606. }
  607. return;
  608. }
  609. /*!
  610. * \brief Compute the dispersivity tensor based on the solute transport data in 3d
  611. *
  612. * The dispersivity tensor is stored in the data structure.
  613. * To compute the dispersivity tensor, the dispersivity lentghs and the gradient field
  614. * must be present.
  615. *
  616. * This is just a simple tensor computation which should be extended.
  617. *
  618. * \todo Change the tensor calculation to a mor realistic algorithm
  619. *
  620. * \param data N_solute_transport_data3d *
  621. * \return void *
  622. * */
  623. void N_calc_solute_transport_disptensor_3d(N_solute_transport_data3d * data)
  624. {
  625. int i, j, k;
  626. int cols, rows, depths;
  627. double vx, vy, vz, vv;
  628. double disp_xx, disp_yy, disp_zz, disp_xy, disp_xz, disp_yz;
  629. N_gradient_3d grad;
  630. cols = data->grad->cols;
  631. rows = data->grad->rows;
  632. depths = data->grad->depths;
  633. G_debug(2,
  634. "N_calc_solute_transport_disptensor_3d: calculating the dispersivity tensor");
  635. for (k = 0; k < depths; k++) {
  636. for (j = 0; j < rows; j++) {
  637. for (i = 0; i < cols; i++) {
  638. disp_xx = 0;
  639. disp_yy = 0;
  640. disp_zz = 0;
  641. disp_xy = 0;
  642. disp_xz = 0;
  643. disp_yz = 0;
  644. /*get the gradient neighbours */
  645. N_get_gradient_3d(data->grad, &grad, i, j, k);
  646. vx = (grad.WC + grad.EC) / 2;
  647. vy = (grad.NC + grad.SC) / 2;
  648. vz = (grad.BC + grad.TC) / 2;
  649. vv = sqrt(vx * vx + vy * vy + vz * vz);
  650. if (vv != 0) {
  651. disp_xx =
  652. data->al * vx * vx / vv + data->at * vy * vy / vv +
  653. data->at * vz * vz / vv;
  654. disp_yy =
  655. data->at * vx * vx / vv + data->al * vy * vy / vv +
  656. data->at * vz * vz / vv;
  657. disp_zz =
  658. data->at * vx * vx / vv + data->at * vy * vy / vv +
  659. data->al * vz * vz / vv;
  660. disp_xy = (data->al - data->at) * vx * vy / vv;
  661. disp_xz = (data->al - data->at) * vx * vz / vv;
  662. disp_yz = (data->al - data->at) * vy * vz / vv;
  663. }
  664. G_debug(5,
  665. "N_calc_solute_transport_disptensor_3d: [%i][%i][%i] disp_xx %g disp_yy %g disp_zz %g disp_xy %g disp_xz %g disp_yz %g ",
  666. i, j, k, disp_xx, disp_yy, disp_zz, disp_xy, disp_xz,
  667. disp_yz);
  668. N_put_array_3d_d_value(data->disp_xx, i, j, k, disp_xx);
  669. N_put_array_3d_d_value(data->disp_yy, i, j, k, disp_yy);
  670. N_put_array_3d_d_value(data->disp_zz, i, j, k, disp_zz);
  671. N_put_array_3d_d_value(data->disp_xy, i, j, k, disp_xy);
  672. N_put_array_3d_d_value(data->disp_xz, i, j, k, disp_xz);
  673. N_put_array_3d_d_value(data->disp_yz, i, j, k, disp_yz);
  674. }
  675. }
  676. }
  677. return;
  678. }