123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658 |
- /*****************************************************************************
- *
- * MODULE: Grass PDE Numerical Library
- * AUTHOR(S): Soeren Gebbert, Berlin (GER) Dec 2006
- * soerengebbert <at> gmx <dot> 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 <grass/N_pde.h>
- /*! \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;
- }
|