/***************************************************************************** * * MODULE: Grass PDE Numerical Library * AUTHOR(S): Soeren Gebbert, Berlin (GER) Dec 2006 * soerengebbert gmx de * * PURPOSE: gradient management functions * part of the gpde library * * COPYRIGHT: (C) 2000 by the GRASS Development Team * * This program is free software under the GNU General Public * License (>=v2). Read the file COPYING that comes with GRASS * for details. * *****************************************************************************/ #include /*! \brief Calculate basic statistics of a gradient field * * The statistic is stored in the gradient field struct * * \param field N_gradient_2d_field * * \return void * * */ void N_calc_gradient_field_2d_stats(N_gradient_field_2d * field) { double minx, miny; double maxx, maxy; double sumx, sumy; int nonullx, nonully; G_debug(3, "N_calc_gradient_field_2d_stats: compute gradient field stats"); N_calc_array_2d_stats(field->x_array, &minx, &maxx, &sumx, &nonullx, 0); N_calc_array_2d_stats(field->y_array, &miny, &maxy, &sumy, &nonully, 0); if (minx < miny) field->min = minx; else field->min = miny; if (maxx > maxy) field->max = maxx; else field->max = maxy; field->sum = sumx + sumy; field->nonull = nonullx + nonully; field->mean = field->sum / (double)field->nonull; return; } /*! * \brief This function computes the gradient based on the input N_array_2d pot * (potential), a weighting factor N_array_2d named weight and the distance between two cells * saved in the N_geom_data struct. * * The gradient is calculated between cells for each cell and direction. * An existing gradient field can be filled with new data or, if a NULL pointer is * given, a new gradient field will be allocated with the appropriate size. * * \verbatim ______________ | | | | | | | | |----|-NC-|----| | | | | | WC EC | | | | | |----|-SC-|----| | | | | |____|____|____| x - direction: r = 2 * weight[row][col]*weight[row][col + 1] / (weight[row][col]*weight[row][col + 1]) EC = r * (pot[row][col] - pot[row][col + 1])/dx y - direction: r = 2 * weight[row][col]*weight[row + 1][col] / (weight[row][col]*weight[row + 1][col]) SC = r * (pot[row][col] - pot[row + 1][col])/dy the values SC and EC are the values of the next row/col \endverbatim * \param pot N_array_2d * - the potential N_array_2d * \param weight_x N_array_2d * - the weighting factor N_array_2d used to modify the gradient in x-direction * \param weight_y N_array_2d * - the weighting factor N_array_2d used to modify the gradient in y-direction * \param geom N_geom_data * - geometry data structure * \param gradfield N_gradient_field_2d * - a gradient field of the correct size, if a NULL pointer is provided this gradient field will be new allocated * \return N_gradient_field_2d * - the pointer to the computed gradient field * * */ N_gradient_field_2d *N_compute_gradient_field_2d(N_array_2d * pot, N_array_2d * weight_x, N_array_2d * weight_y, N_geom_data * geom, N_gradient_field_2d * gradfield) { int i, j; int rows, cols; double dx, dy, p1, p2, r1, r2, mean, grad, res; N_gradient_field_2d *field = gradfield; if (pot->cols != weight_x->cols || pot->cols != weight_y->cols) G_fatal_error ("N_compute_gradient_field_2d: the arrays are not of equal size"); if (pot->rows != weight_x->rows || pot->rows != weight_y->rows) G_fatal_error ("N_compute_gradient_field_2d: the arrays are not of equal size"); if (pot->cols != geom->cols || pot->rows != geom->rows) G_fatal_error ("N_compute_gradient_field_2d: array sizes and geometry data are different"); G_debug(3, "N_compute_gradient_field_2d: compute gradient field"); rows = pot->rows; cols = pot->cols; dx = geom->dx; dy = geom->dy; if (field == NULL) { field = N_alloc_gradient_field_2d(cols, rows); } else { if (field->cols != geom->cols || field->rows != geom->rows) G_fatal_error ("N_compute_gradient_field_2d: gradient field sizes and geometry data are different"); } for (j = 0; j < rows; j++) for (i = 0; i < cols - 1; i++) { grad = 0; mean = 0; /* Only compute if the arrays are not null */ if (!N_is_array_2d_value_null(pot, i, j) && !N_is_array_2d_value_null(pot, i + 1, j)) { p1 = N_get_array_2d_d_value(pot, i, j); p2 = N_get_array_2d_d_value(pot, i + 1, j); grad = (p1 - p2) / dx; /* gradient */ } if (!N_is_array_2d_value_null(weight_x, i, j) && !N_is_array_2d_value_null(weight_x, i + 1, j)) { r1 = N_get_array_2d_d_value(weight_x, i, j); r2 = N_get_array_2d_d_value(weight_x, i + 1, j); mean = N_calc_harmonic_mean(r1, r2); /*harmonical mean */ } res = mean * grad; N_put_array_2d_d_value(field->x_array, i + 1, j, res); } for (j = 0; j < rows - 1; j++) for (i = 0; i < cols; i++) { grad = 0; mean = 0; /* Only compute if the arrays are not null */ if (!N_is_array_2d_value_null(pot, i, j) && !N_is_array_2d_value_null(pot, i, j + 1)) { p1 = N_get_array_2d_d_value(pot, i, j); p2 = N_get_array_2d_d_value(pot, i, j + 1); grad = (p1 - p2) / dy; /* gradient */ } if (!N_is_array_2d_value_null(weight_y, i, j) && !N_is_array_2d_value_null(weight_y, i, j + 1)) { r1 = N_get_array_2d_d_value(weight_y, i, j); r2 = N_get_array_2d_d_value(weight_y, i, j + 1); mean = N_calc_harmonic_mean(r1, r2); /*harmonical mean */ } res = -1 * mean * grad; N_put_array_2d_d_value(field->y_array, i, j + 1, res); } /*Compute gradient field statistics */ N_calc_gradient_field_2d_stats(field); return field; } /*! * \brief Calculate the x and y vector components from a gradient field for each * cell and stores them in the provided N_array_2d structures * * The arrays must have the same size as the gradient field. \verbatim Based on this storages scheme the gradient vector for each cell is calculated and stored in the provided N_array_2d structures ______________ | | | | | | | | |----|-NC-|----| | | | | | WC EC | | | | | |----|-SC-|----| | | | | |____|____|____| x vector component: x = (WC + EC) / 2 y vector component: y = (NC + SC) / 2 \endverbatim * * \param field N_gradient_field_2d * * \param x_comp N_array_2d * - the array in which the x component will be written * \param y_comp N_array_2d * - the array in which the y component will be written * * \return void * */ void N_compute_gradient_field_components_2d(N_gradient_field_2d * field, N_array_2d * x_comp, N_array_2d * y_comp) { int i, j; int rows, cols; double vx, vy; N_array_2d *x = x_comp; N_array_2d *y = y_comp; N_gradient_2d grad; if (!x) G_fatal_error("N_compute_gradient_components_2d: x array is empty"); if (!y) G_fatal_error("N_compute_gradient_components_2d: y array is empty"); cols = field->x_array->cols; rows = field->x_array->rows; /*Check the array sizes */ if (x->cols != cols || x->rows != rows) G_fatal_error ("N_compute_gradient_components_2d: the size of the x array doesn't fit the gradient field size"); if (y->cols != cols || y->rows != rows) G_fatal_error ("N_compute_gradient_components_2d: the size of the y array doesn't fit the gradient field size"); for (j = 0; j < rows; j++) for (i = 0; i < cols; i++) { N_get_gradient_2d(field, &grad, i, j); /* in case a gradient is zero, we expect a no flow boundary */ if (grad.WC == 0.0 || grad.EC == 0.0) vx = (grad.WC + grad.EC); else vx = (grad.WC + grad.EC) / 2; if (grad.NC == 0.0 || grad.SC == 0.0) vy = (grad.NC + grad.SC); else vy = (grad.NC + grad.SC) / 2; N_put_array_2d_d_value(x, i, j, vx); N_put_array_2d_d_value(y, i, j, vy); } return; } /*! \brief Calculate basic statistics of a gradient field * * The statistic is stored in the gradient field struct * * \param field N_gradient_3d_field * * \return void * * */ void N_calc_gradient_field_3d_stats(N_gradient_field_3d * field) { double minx, miny, minz; double maxx, maxy, maxz; double sumx, sumy, sumz; int nonullx, nonully, nonullz; G_debug(3, "N_calc_gradient_field_3d_stats: compute gradient field stats"); N_calc_array_3d_stats(field->x_array, &minx, &maxx, &sumx, &nonullx, 0); N_calc_array_3d_stats(field->y_array, &miny, &maxy, &sumy, &nonully, 0); N_calc_array_3d_stats(field->z_array, &minz, &maxz, &sumz, &nonullz, 0); if (minx <= minz && minx <= miny) field->min = minx; if (miny <= minz && miny <= minx) field->min = miny; if (minz <= minx && minz <= miny) field->min = minz; if (maxx >= maxz && maxx >= maxy) field->max = maxx; if (maxy >= maxz && maxy >= maxx) field->max = maxy; if (maxz >= maxx && maxz >= maxy) field->max = maxz; field->sum = sumx + sumy + sumz; field->nonull = nonullx + nonully + nonullz; field->mean = field->sum / (double)field->nonull; return; } /*! * \brief This function computes the gradient based on the input N_array_3d pot * (that means potential), a weighting factor N_array_3d named weight and the distance between two cells * saved in the N_geom_data struct. * * The gradient is calculated between cells for each cell and direction. * An existing gradient field can be filled with new data or, if a NULL pointer is * given, a new gradient field will be allocated with the appropriate size. * * * * \verbatim | / TC NC |/ --WC-----EC-- /| SC BC / | x - direction: r = 2 * weight_x[depth][row][col]*weight_x[depth][row][col + 1] / (weight_X[depth][row][col]*weight_x[depth][row][col + 1]) EC = r * (pot[depth][row][col] - pot[depth][row][col + 1])/dx y - direction: r = 2 * weight_y[depth][row][col]*weight_y[depth][row + 1][col] / (weight_y[depth][row][col]*weight_y[depth][row + 1][col]) SC = r * (pot[depth][row][col] - pot[depth][row + 1][col])/dy z - direction: r = 2 * weight_z[depth][row][col]*weight_z[depth + 1][row][col] / (weight_z[depth][row][col]*weight_z[depth + 1][row][col]) TC = r * (pot[depth][row][col] - pot[depth + 1][row][col])/dy the values BC, NC, WC are the values of the next depth/row/col \endverbatim * \param pot N_array_3d * - the potential N_array_2d * \param weight_x N_array_3d * - the weighting factor N_array_3d used to modify the gradient in x-direction * \param weight_y N_array_3d * - the weighting factor N_array_3d used to modify the gradient in y-direction * \param weight_z N_array_3d * - the weighting factor N_array_3d used to modify the gradient in z-direction * \param geom N_geom_data * - geometry data structure * \param gradfield N_gradient_field_3d * - a gradient field of the correct size, if a NULL pointer is provided this gradient field will be new allocated * \return N_gradient_field_3d * - the pointer to the computed gradient field * * */ N_gradient_field_3d *N_compute_gradient_field_3d(N_array_3d * pot, N_array_3d * weight_x, N_array_3d * weight_y, N_array_3d * weight_z, N_geom_data * geom, N_gradient_field_3d * gradfield) { int i, j, k; int cols, rows, depths; double dx, dy, dz, p1, p2, r1, r2, mean, grad, res; N_gradient_field_3d *field = gradfield; if (pot->cols != weight_x->cols || pot->cols != weight_y->cols || pot->cols != weight_z->cols) G_fatal_error ("N_compute_gradient_field_3d: the arrays are not of equal size"); if (pot->rows != weight_x->rows || pot->rows != weight_y->rows || pot->rows != weight_z->rows) G_fatal_error ("N_compute_gradient_field_3d: the arrays are not of equal size"); if (pot->depths != weight_x->depths || pot->depths != weight_y->depths || pot->depths != weight_z->depths) G_fatal_error ("N_compute_gradient_field_3d: the arrays are not of equal size"); if (pot->cols != geom->cols || pot->rows != geom->rows || pot->depths != geom->depths) G_fatal_error ("N_compute_gradient_field_3d: array sizes and geometry data are different"); G_debug(3, "N_compute_gradient_field_3d: compute gradient field"); cols = geom->cols; rows = geom->rows; depths = geom->depths; dx = geom->dx; dy = geom->dy; dz = geom->dz; if (gradfield == NULL) { field = N_alloc_gradient_field_3d(cols, rows, depths); } else { if (field->cols != geom->cols || field->rows != geom->rows || field->depths != geom->depths) G_fatal_error ("N_compute_gradient_field_3d: gradient field sizes and geometry data are different"); } for (k = 0; k < depths; k++) for (j = 0; j < rows; j++) for (i = 0; i < cols - 1; i++) { grad = 0; mean = 0; /*Only compute if the arrays are not null */ if (!N_is_array_3d_value_null(pot, i, j, k) && !N_is_array_3d_value_null(pot, i + 1, j, k)) { p1 = N_get_array_3d_d_value(pot, i, j, k); p2 = N_get_array_3d_d_value(pot, i + 1, j, k); grad = (p1 - p2) / dx; /* gradient */ } if (!N_is_array_3d_value_null(weight_x, i, j, k) && !N_is_array_3d_value_null(weight_x, i + 1, j, k)) { r1 = N_get_array_3d_d_value(weight_x, i, j, k); r2 = N_get_array_3d_d_value(weight_x, i + 1, j, k); mean = N_calc_harmonic_mean(r1, r2); /*harmonical mean */ } res = mean * grad; G_debug(6, "N_compute_gradient_field_3d: X-direction insert value %6.5g at %i %i %i ", res, k, j, i + 1); N_put_array_3d_d_value(field->x_array, i + 1, j, k, res); } for (k = 0; k < depths; k++) for (j = 0; j < rows - 1; j++) for (i = 0; i < cols; i++) { grad = 0; mean = 0; /* Only compute if the arrays are not null */ if (!N_is_array_3d_value_null(pot, i, j, k) && !N_is_array_3d_value_null(pot, i, j + 1, k)) { p1 = N_get_array_3d_d_value(pot, i, j, k); p2 = N_get_array_3d_d_value(pot, i, j + 1, k); grad = (p1 - p2) / dy; /* gradient */ } if (!N_is_array_3d_value_null(weight_y, i, j, k) && !N_is_array_3d_value_null(weight_y, i, j + 1, k)) { r1 = N_get_array_3d_d_value(weight_y, i, j, k); r2 = N_get_array_3d_d_value(weight_y, i, j + 1, k); mean = N_calc_harmonic_mean(r1, r2); /*harmonical mean */ } res = -1 * mean * grad; /*invert the direction, because we count from north to south, * but the gradient is defined in y direction */ G_debug(6, "N_compute_gradient_field_3d: Y-direction insert value %6.5g at %i %i %i ", res, k, j + 1, i); N_put_array_3d_d_value(field->y_array, i, j + 1, k, res); } for (k = 0; k < depths - 1; k++) for (j = 0; j < rows; j++) for (i = 0; i < cols; i++) { grad = 0; mean = 0; /* Only compute if the arrays are not null */ if (!N_is_array_3d_value_null(pot, i, j, k) && !N_is_array_3d_value_null(pot, i, j, k + 1)) { p1 = N_get_array_3d_d_value(pot, i, j, k); p2 = N_get_array_3d_d_value(pot, i, j, k + 1); grad = (p1 - p2) / dz; /* gradient */ } if (!N_is_array_3d_value_null(weight_z, i, j, k) && !N_is_array_3d_value_null(weight_z, i, j, k + 1)) { r1 = N_get_array_3d_d_value(weight_z, i, j, k); r2 = N_get_array_3d_d_value(weight_z, i, j, k + 1); mean = N_calc_harmonic_mean(r1, r2); /*harmonical mean */ } res = mean * grad; G_debug(6, "N_compute_gradient_field_3d: Z-direction insert value %6.5g at %i %i %i ", res, k + 1, j, i); N_put_array_3d_d_value(field->z_array, i, j, k + 1, res); } /*Compute gradient field statistics */ N_calc_gradient_field_3d_stats(field); return field; } /*! * \brief Calculate the x, y and z vector components from a gradient field for each cell * and store them in the provided N_array_3d structures * * The arrays must have the same size as the gradient field. * \verbatim Based on this storages scheme the gradient vector for each cell is calculated and stored in the provided N_array_3d structures | / TC NC |/ --WC-----EC-- /| SC BC / | x vector component: x = (WC + EC) / 2 y vector component: y = (NC + SC) / 2 z vector component: z = (TC + BC) / 2 \endverbatim * \param field N_gradient_field_3d * * \param x_comp N_array_3d * - the array in which the x component will be written * \param y_comp N_array_3d * - the array in which the y component will be written * \param z_comp N_array_3d * - the array in which the z component will be written * * \return void * */ void N_compute_gradient_field_components_3d(N_gradient_field_3d * field, N_array_3d * x_comp, N_array_3d * y_comp, N_array_3d * z_comp) { int i, j, k; int rows, cols, depths; double vx, vy, vz; N_array_3d *x = x_comp; N_array_3d *y = y_comp; N_array_3d *z = z_comp; N_gradient_3d grad; if (!x) G_fatal_error("N_compute_gradient_components_3d: x array is empty"); if (!y) G_fatal_error("N_compute_gradient_components_3d: y array is empty"); if (!z) G_fatal_error("N_compute_gradient_components_3d: z array is empty"); cols = field->x_array->cols; rows = field->x_array->rows; depths = field->x_array->depths; /*Check the array sizes */ if (x->cols != cols || x->rows != rows || x->depths != depths) G_fatal_error ("N_compute_gradient_components_3d: the size of the x array doesn't fit the gradient field size"); if (y->cols != cols || y->rows != rows || y->depths != depths) G_fatal_error ("N_compute_gradient_components_3d: the size of the y array doesn't fit the gradient field size"); if (z->cols != cols || z->rows != rows || z->depths != depths) G_fatal_error ("N_compute_gradient_components_3d: the size of the z array doesn't fit the gradient field size"); for (k = 0; k < depths; k++) for (j = 0; j < rows; j++) for (i = 0; i < cols; i++) { N_get_gradient_3d(field, &grad, i, j, k); /* in case a gradient is zero, we expect a no flow boundary */ if (grad.WC == 0.0 || grad.EC == 0.0) vx = (grad.WC + grad.EC); else vx = (grad.WC + grad.EC) / 2; if (grad.NC == 0.0 || grad.SC == 0.0) vy = (grad.NC + grad.SC); else vy = (grad.NC + grad.SC) / 2; if (grad.TC == 0.0 || grad.BC == 0.0) vz = (grad.TC + grad.BC); else vz = (grad.TC + grad.BC) / 2; N_put_array_3d_d_value(x, i, j, k, vx); N_put_array_3d_d_value(y, i, j, k, vy); N_put_array_3d_d_value(z, i, j, k, vz); } return; }