7.2
general documentation
Groundwater flow module using CDO schemes

Introduction

The Hydrogeology module of code_saturne is a numerical model for groundwater flow and solute transport in continuous porous media. The flow part is based on the Richards equation, derived from the Darcy law and the conservation of mass. The transport part is based on the the classical advection-diffusion equation of tracers, slightly modified to account the specificities of groundwater transport.

This module can be used to simulate transfers of water and solutes in several saturated and/or unsaturated porous media. The flow part can be steady or unsteady, with isotropic or anisotropic permeabilities and allows for any type of soil water retention model thanks to a user-defined model. Two classical models are predefined: the saturated model and the van Genuchten-Mualen model. The transport part considers dispersion, sorption and radioactive decay. The partition between soil and water phases can be modeled by a classical Kd approach model. Additionaly solute precipitation/dissolution phenomena can also be taken into account by an instantaneous model.

Physical concepts and equations are presented in the theory guide

The groundwater flow module (GWF) relies on CDO vertex-based or CDO vertex+cell-based discretization schemes. Here is listed a set of references useful for the reader willing to get a better understanding of the mathematical concepts underpinning CDO schemes.

To set-up a GWF computation, one has to update the cs_user_parameters.c file and edit the function cs_user_model at least in simple cases. In more complex cases, editing cs_user_finalize_setup should be necessary.

Activate the GWF module

The first step is to activate the CDO module in the function cs_user_model (please refer to Activation of CDO/HHO schemes).

Then, one has to activate the groundwater flow (GWF) module in the function cs_user_model. The function to call is cs_gwf_activate.

There are three parameters:

  1. The main hydraulic model to consider (i.e. which equations have to be solved). Please refer to cs_gwf_model_type_t. There are currently two models available :
  2. An optional flag to specify a physical phenomena to take into account or to specify a numerical treatment to apply. If no option is needed, then simply set 0. Here are listed the available option flags:
  3. An optional flag to specify the activation of automatic postprocessing for quantities of interest related to groundwater flows. Set 0 if no additional postprocessing is requested. Here are listed the available options:

Remark: If a CS_GWF_MODEL_SATURATED_SINGLE_PHASE is set at the activation step, then one expects that all soil models are defined by the type CS_GWF_SOIL_SATURATED


Examples of activation of the GWF module

Example 1: Activate the GWF model with a fully saturated single-phase flow model and no other option.

{
/* For the groundwater flow module:
cs_gwf_activate(model_type, option_flag, post_flag);
If option_flag or post_flag = 0, then there is nothing to set. */
cs_flag_t option_flag = 0, post_flag = 0;
option_flag, post_flag);
}
unsigned short int cs_flag_t
Definition: cs_defs.h:321
cs_gwf_t * cs_gwf_activate(cs_gwf_model_type_t model, cs_flag_t option_flag, cs_flag_t post_flag)
Initialize the module dedicated to groundwater flows.
Definition: cs_gwf.c:2975
@ CS_GWF_MODEL_SATURATED_SINGLE_PHASE
Single phase (liquid phase) modelling in a porous media.
Definition: cs_gwf_param.h:113

Example 2: Second example: Activate the GWF model with an unsaturated single-phase flow model without any additional option.

0, /* No option to set */
CS_GWF_POST_PERMEABILITY); /* Post-processing options */
#define CS_GWF_POST_PERMEABILITY
Activate the post-processing of the permeability field.
Definition: cs_gwf_param.h:80
@ CS_GWF_MODEL_UNSATURATED_SINGLE_PHASE
Single phase (liquid phase) modelling in a porous media.
Definition: cs_gwf_param.h:127

Example 3: Activate the GWF model with an unsaturated single-phase flow model. Moreover, one takes into account the gravity effect and the postprocessing of the soil permeability as well as the Darcy flux across domain boundaries.

{
/* Physical modelling option or numerical option */
CS_GWF_GRAVITATION,/* Take into account the gravity */
/* Automatic postprocessing options */
/* In this case, the gravity vector has to be defined (either using the GUI
or in cs_user_parameters() function */
}
#define CS_GWF_POST_DARCY_FLUX_BALANCE
Compute the overall balance at the different boundaries of the Darcy flux.
Definition: cs_gwf_param.h:81
@ CS_GWF_GRAVITATION
Definition: cs_gwf_param.h:176

Soils

The second step is to add at least one new soil. The add of soil(s) should be done before adding tracers inside the function cs_user_model and after having activated the GWF module. Two functions are available to add a new soil:

  • cs_gwf_add_iso_soil when the soil is modelled by an isotropic absolute (or intrinsic) permeability
  • cs_gwf_add_aniso_soil when the soil is modelled by an anisotropic absolute (or intrinsic) permeability (i.e. one has to specific a 3x3 tensor)

These two functions have a similar set of parameters:

  1. The name of the volume zone associated to this soil (to add a volume, one can either use the GUI or use cs_volume_zone_define inside cs_user_zones ; see Simple volume zone example for more details)
  2. The value of the bulk mass density of the soil. This is only useful when a tracer is considered. If there is no tracer, one can set 1 for instance.
  3. The value of the absolute permeability. In case of an isotropic soil, this is a scalar and in case of an anisotropic soil, one expects a tensor.
  4. The value of the soil porosity (which is equivalent to the saturated moisture or the max. liquid saturation in single-phase flows)
  5. The type of soil model. There are two predefined soil models and one user-defined soil model

Examples of settings for a predefined soil model

Case of a saturated model

The saturated model is the simplest model.

Example 1: A saturated soils defined by an isotropic permeability on all the computational domain.

{
/* Example 1: Simplest case. A "saturated" isotropic soils */
/* saturated isotropic permeability */
const double iso_val = 1e-10;
const double theta_s = 0.85;
const cs_real_t bulk_density = 2500; /* useless if no tracer is
considered */
cs_gwf_add_iso_soil("cells", bulk_density, iso_val, theta_s,
}
double cs_real_t
Floating-point value.
Definition: cs_defs.h:319
cs_gwf_soil_t * cs_gwf_add_iso_soil(const char *z_name, double density, double k_abs, double porosity, cs_gwf_soil_model_t model)
Create and add a new cs_gwf_soil_t structure. An initialization by default of all members is performe...
Definition: cs_gwf.c:3439
@ CS_GWF_SOIL_SATURATED
Definition: cs_gwf_param.h:243

Example 2: Two saturated soils defined by an anisotropic (saturated) permeability

{
/* Example 2: Two "saturated" and anisotropic soils */
/* saturated anisotropic permeability */
double aniso_val1[3][3] = {{1e-3, 0, 0},
{ 0, 1, 0},
{ 0, 0, 1e-1}};
double aniso_val2[3][3] = {{1e-5, 0, 0},
{ 0, 1, 0},
{ 0, 0, 1e-2}};
const double theta_s = 1;
const double bulk_density = 1.0; /* useless if no tracer is considered */
/* One assumes that two (volume) zones have be defined called "soil1" and
"soil2". */
cs_gwf_add_aniso_soil("soil1", bulk_density, aniso_val1, theta_s,
cs_gwf_add_aniso_soil("soil2", bulk_density, aniso_val2, theta_s,
}
cs_gwf_soil_t * cs_gwf_add_aniso_soil(const char *z_name, double density, double k_abs[3][3], double porosity, cs_gwf_soil_model_t model)
Create and add a new cs_gwf_soil_t structure. An initialization by default of all members is performe...
Definition: cs_gwf.c:3491

Case of a Van Genuchten-Mualen model

Soils which behave according to a Van Genuchten-Mualen model are specified in two steps: a call to cs_gwf_add_iso_soil and then a call to cs_gwf_soil_set_genuchten_param to specifiy the parameters associated to this model.

Example 3: Soil relying on a Van Genuchten-Mualen and considering a isotropic permeability

{
/* Example 3: Add a new user-defined soil for all the cells */
cs_gwf_soil_t *s = cs_gwf_add_iso_soil("cells", /* volume zone name */
1800, /* bulk mass density */
3e-1, /* absolute permeability */
0.9, /* porosity */
CS_GWF_SOIL_GENUCHTEN); /* model */
0.078, /* residual moisture */
0.036, /* scaling parameter */
1.56, /* (n) shape parameter */
0.5); /* (L) tortuosity */
}
@ CS_GWF_SOIL_GENUCHTEN
Definition: cs_gwf_param.h:242
void cs_gwf_soil_set_genuchten_param(cs_gwf_soil_t *soil, double theta_r, double alpha, double n, double L)
Set a soil defined by a Van Genuchten-Mualen model.
Definition: cs_gwf_soil.c:744

Example of settings for a user-defined soil

If the predefined models are not sufficient, it is possible to add a user-defined soil. In this case, the add of the new soil is made as follows

  1. Define a structure to get an access to the parameters defining the soil model
  2. Add a new soil (cs_gwf_add_iso_soil or cs_gwf_add_aniso_soil )
  3. Call cs_gwf_soil_set_user to specify the structure to use, the function to update quantities related to the hydraulic model and if needed a function to free the structure storing the soil parameters.

These three steps are performed inside the function cs_user_model

Here is a complete example of a user-defined soil model (called hereafter Tracy since it has been designed by F. T. Tracy in this article).

Define a structure to store the model parameters

Example of the structure used to handle the soil model parameters

/* Parameters defining the unsaturated soil model devised by Tracy */
typedef struct {
double L; /* column length */
double h_s; /* saturated head reference */
double h_r; /* residual head reference */
double theta_r; /* residual moisture */
double theta_s; /* saturated moisture */
} cs_tracy_param_t;

Add a user-defined soil

cs_gwf_soil_t *s =
1.0, /* bulk mass density (useless here) */
1.15741e-4, /* absolute permeability */
0.45, /* porosity */
/* 2.a Create and define a structure of parameters to manage the soil */
cs_tracy_param_t *tp = NULL;
BFT_MALLOC(tp, 1, cs_tracy_param_t);
tp->L = 200;
tp->h_s = 0.;
tp->h_r = -100;
tp->theta_r = 0.15;
/* 2.b Associate the parameter structure and the user-defined functions
* to manage the soil */
cs_gwf_soil_set_user(s, /* soil structure */
tp, /* soil parameter structure */
tracy_update, /* function to update the soil */
tracy_free_param); /* function to free the structure */
#define BFT_MALLOC(_ptr, _ni, _type)
Allocate memory for _ni elements of type _type.
Definition: bft_mem.h:62
@ CS_GWF_SOIL_USER
Definition: cs_gwf_param.h:244
void cs_gwf_soil_set_user(cs_gwf_soil_t *soil, void *param, cs_gwf_soil_update_t *update_func, cs_gwf_soil_free_param_t *free_param_func)
Set a soil defined by a user-defined model.
Definition: cs_gwf_soil.c:787

with the two requested functions (defined for instance as a static function in the file cs_user_parameters.c). These functions have to fullfill the prototype defined in cs_gwf_soil_update_t (for the update of the soil properties) and in cs_gwf_soil_free_param_t (for the free of the soil parameter structure).

Here is an example of how to update soil properties (function called tracy_update)

static void
tracy_update(const cs_real_t t_eval,
const cs_mesh_t *mesh,
const cs_cdo_connect_t *connect,
const cs_cdo_quantities_t *quant,
const cs_zone_t *zone,
cs_gwf_soil_t *soil)
{
CS_UNUSED(connect);
CS_UNUSED(quant);
CS_UNUSED(t_eval);
/* Retrieve the soil parameters */
const cs_tracy_param_t *sp = (cs_tracy_param_t *)soil->model_param;
/* Retrieve the hydraulic context */
cs_gwf_unsaturated_single_phase_t *hc = soil->hydraulic_context;
/* Additional parameters */
const cs_real_t *head_values = hc->head_in_law;
const double delta_m = soil->porosity - sp->theta_r;
const double k_s = soil->abs_permeability[0][0];
/* Retrieve field values associated to properties to update */
cs_real_t *permeability = hc->permeability_field->val;
cs_real_t *moisture = hc->moisture_field->val;
cs_real_t *capacity = hc->capacity_field->val;
for (cs_lnum_t i = 0; i < zone->n_elts; i++) {
const cs_lnum_t c_id = zone->elt_ids[i];
const cs_real_t h = head_values[c_id];
const cs_real_t k_r = (h - sp->h_r)/(sp->h_s - sp->h_r);
/* Set the permeability value */
permeability[c_id] = k_s * k_r;
/* Set the moisture content (Se = 1 in this case)*/
moisture[c_id] = sp->theta_r + k_r * delta_m;
/* Set the capacity values */
capacity[c_id] = delta_m /(sp->h_s - sp->h_r);
} /* Loop on selected cells */
}
#define CS_UNUSED(x)
Definition: cs_defs.h:493
int cs_lnum_t
local mesh entity id
Definition: cs_defs.h:313
@ h
Definition: cs_field_pointer.h:91
Definition: mesh.f90:26
Definition: cs_cdo_connect.h:61
Definition: cs_cdo_quantities.h:132
Structure to handle the modelling of a single-phase flows in a porous media considered as saturated o...
Definition: cs_gwf_priv.h:161
cs_real_t * head_in_law
Definition: cs_gwf_priv.h:250
Definition: cs_mesh.h:84
Definition: cs_zone.h:55
const cs_lnum_t * elt_ids
Definition: cs_zone.h:65
cs_lnum_t n_elts
Definition: cs_zone.h:64

and an example of how to free the soil parameter structure (function called tracy_free_param)

static void
tracy_free_param(void **p_soil_param)
{
cs_tracy_param_t *sp = (cs_tracy_param_t *)(*p_soil_param);
BFT_FREE(sp);
*p_soil_param = NULL;
}
#define BFT_FREE(_ptr)
Free allocated memory.
Definition: bft_mem.h:101

Further settings (initial and boundary conditions)

In this example, we also give some highlights on how this soil structure can be used to further set the problem for instance to specify the initial and boundary conditions. This step is made in the function cs_user_finalize_setup

/* Final settings for the Richards equation */
/* 1. Retrieve the soil by its name and then its parameter structure */
cs_gwf_soil_t *soil = cs_gwf_soil_by_name("cells");
cs_tracy_param_t *tp = soil->model_param;
/* Define the boundary conditions */
"left", // boundary zone name
get_bc,
soil);
"right", // boundary zone name
&(tp->h_r));
/* Set the initial condition */
NULL,
get_ic,
tp);
cs_equation_param_t * cs_equation_param_by_name(const char *eqname)
Return the cs_equation_param_t structure associated to a cs_equation_t structure based on the equatio...
Definition: cs_equation.c:572
cs_xdef_t * cs_equation_add_ic_by_analytic(cs_equation_param_t *eqp, const char *z_name, cs_analytic_func_t *analytic, void *input)
Define the initial condition for the unknown related to this equation. This definition applies to a v...
Definition: cs_equation_param.c:2207
cs_xdef_t * cs_equation_add_bc_by_analytic(cs_equation_param_t *eqp, const cs_param_bc_type_t bc_type, const char *z_name, cs_analytic_func_t *analytic, void *input)
Define and initialize a new structure to set a boundary condition related to the given equation param...
Definition: cs_equation_param.c:2568
cs_xdef_t * cs_equation_add_bc_by_value(cs_equation_param_t *eqp, const cs_param_bc_type_t bc_type, const char *z_name, cs_real_t *values)
Define and initialize a new structure to set a boundary condition related to the given equation struc...
Definition: cs_equation_param.c:2340
cs_gwf_soil_t * cs_gwf_soil_by_name(const char *name)
Retrieve a soil structure from its name.
Definition: cs_gwf_soil.c:269
@ CS_PARAM_BC_DIRICHLET
Definition: cs_param_types.h:473
Set of parameters to handle an unsteady convection-diffusion-reaction equation with term sources.
Definition: cs_equation_param.h:186

where the two functions used to define either the boundary condition (called "get_bc") in the example or the initial condition (called "get_ic") in the example follow a predefined prototype (see cs_analytic_func_t)

Here are collected two examples of such functions:

static void
get_bc(cs_real_t time,
cs_lnum_t n_pts,
const cs_lnum_t *pt_ids,
const cs_real_t *xyz,
bool dense_output,
void *input,
cs_real_t *retval)
{
const cs_gwf_soil_t *soil = input;
assert(soil != NULL);
const cs_tracy_param_t *tp = soil->model_param;
assert(tp != NULL);
/* Physical parameters */
const double overL = 1./tp->L;
const double dtheta = soil->porosity - tp->theta_r;
const double td =
-5 * tp->L * tp->L * dtheta /( 6 * tp->h_r * soil->abs_permeability[0][0] );
const double alpha = 6 - 5*time/td;
for (cs_lnum_t p = 0; p < n_pts; p++) {
const cs_lnum_t id = (pt_ids == NULL) ? p : pt_ids[p];
const cs_lnum_t r_id = dense_output ? p : id;
const double xll = (xyz[3*id] - tp->L)*overL, beta = xll*xll;
retval[r_id] = tp->h_r*(1 - beta/alpha);
} /* Loop on selected points */
}
@ p
Definition: cs_field_pointer.h:67
double precision, dimension(ncharm), save alpha
Definition: cpincl.f90:99
double precision, dimension(ncharm), save beta
Definition: cpincl.f90:99
static void
get_ic(cs_real_t time,
cs_lnum_t n_pts,
const cs_lnum_t *pt_ids,
const cs_real_t *xyz,
bool dense_output,
void *input,
cs_real_t *retval)
{
CS_UNUSED(time);
cs_tracy_param_t *tp = input;
assert(input != NULL);
const double one6 = 1./6;
for (cs_lnum_t p = 0; p < n_pts; p++) {
const cs_lnum_t id = (pt_ids == NULL) ? p : pt_ids[p];
const cs_lnum_t r_id = (dense_output) ? p : id;
const double x = xyz[3*id], xll = (x - tp->L)/tp->L;
retval[r_id] = tp->h_r*(1-one6*xll*xll);
} /* Loop on selected points */
}

Tacers

The third step (which is not mandatory) is to add tracer(s) thanks to the function cs_gwf_add_tracer This tracer will be advected by the Darcy flux arising from the Richards equation.

There are currently two models :

  • a default model (the predefined one; see cs_gwf_cdo_predef_tracer)
  • a user-defined model (see cs_gwf_cdo_user_tracer)

The first parameter in cs_gwf_add_tracer is a flag which can be built with

Predefined tracers

Here is a simple example for a standard tracer which can be added in the function cs_user_model

{
/*
Add a tracer equation which is unsteady and convected by the darcean flux
This implies the creation of a new equation called eqname along with a
new field called varname.
*/
cs_gwf_tracer_model_t model = 0; /* default model without precipitation
effect */
cs_gwf_tracer_t *tr = cs_gwf_add_tracer(model, /* tracer model */
"Tracer", /* eq. name */
"C"); /* var. name */
/* For "simple" tracer definitions, definition can be made here.
*
* The parameters defining the tracer behavior can be set soil by soil
* (give the soil name as the second argument) or to all soils in one call
* (give NULL as the second argument)
*/
NULL, /* soil name or NULL for all */
0., /* water molecular diffusivity */
1., 0., /* alpha (longi. and transvesal) */
1e-4, /* distribution coef. */
0.01); /* 1st order decay coef. */
}
cs_gwf_tracer_t * cs_gwf_add_tracer(cs_gwf_tracer_model_t tr_model, const char *eq_name, const char *var_name)
Add a new equation related to the groundwater flow module.
Definition: cs_gwf.c:3546
cs_flag_t cs_gwf_tracer_model_t
Definition: cs_gwf_param.h:256
void cs_gwf_tracer_set_main_param(cs_gwf_tracer_t *tracer, const char *soil_name, double wmd, double alpha_l, double alpha_t, double distrib_coef, double reaction_rate)
Set a tracer for a specified soil when the tracer is attached to the default model.
Definition: cs_gwf_tracer.c:1446

Remark: Get a tracer structure.

{
/* If the tracer structure has been added but not already defined, one can
* retrieve it thanks to \ref cs_gwf_tracer_by_name
*
* The name of the tracer is the same as the name of the associated
* equation given at the creation of the tracer
*/
cs_gwf_tracer_t *tr = cs_gwf_tracer_by_name("Tracer_01");
}
cs_gwf_tracer_t * cs_gwf_tracer_by_name(const char *eq_name)
Retrieve the pointer to the cs_gwf_tracer_t structure associated to the name given as parameter.
Definition: cs_gwf_tracer.c:1272

User-defined tracers

TODO

Automatic postprocessings

It is possible to activate or add an automatic post-processing of several quantities of interest related to groundwater flows. Here are available flags to activate through the usage of cs_gwf_set_post_options

/* Specify post-processing options */
false); /* No reset */
void cs_gwf_set_post_options(cs_flag_t post_flag, bool reset)
Set the flag dedicated to the post-processing of the GWF module.
Definition: cs_gwf.c:3368
#define CS_GWF_POST_DARCY_FLUX_AT_BOUNDARY
Define a field at boundary faces for the Darcy flux and activate the post-processing.
Definition: cs_gwf_param.h:83
#define CS_GWF_POST_DARCY_FLUX_DIVERGENCE
Compute in each control volume (vertices or cells w.r.t the space scheme) the divergence of the Darcy...
Definition: cs_gwf_param.h:82

Helper functions

Helper functions for soils

Get a soil structure from its name.

{
/* If a soil structure has been added but not already defined, one can
* retrieve it thanks to \ref cs_gwf_soil_by_name
*
* The name of the soil is the same as the name of the volume zone used at
* the creation of the soil
*/
cs_gwf_soil_t *s1 = cs_gwf_soil_by_name("soil1");
}

There is a similar which retrieve the soil structure from its id (see cs_gwf_soil_by_id).

Helper functions for tracers

Get a tracer structure from its name.

{
/* If the tracer structure has been added but not already defined, one can
* retrieve it thanks to \ref cs_gwf_tracer_by_name
*
* The name of the tracer is the same as the name of the associated
* equation given at the creation of the tracer
*/
cs_gwf_tracer_t *tr = cs_gwf_tracer_by_name("Tracer_01");
}