summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIlhan Özgen Xian <iozgen@lbl.gov>2021-09-01 15:15:50 -0700
committerIlhan Özgen Xian <iozgen@lbl.gov>2021-09-01 15:15:50 -0700
commit14e2736fba65b87d81063f004c75770354b95ca9 (patch)
treea2d21b3cdf0e38a1ed967a36fc290c417ed75219
parent80336763aafe3aa565c24e4eedaf53e74698efb7 (diff)
add advecting with different time horizons
-rw-r--r--environment.hpp94
-rw-r--r--header.hpp1
-rw-r--r--input/options.input11
-rw-r--r--input/subtimes.txt84
-rw-r--r--input/times.txt149
-rw-r--r--main.cpp73
-rw-r--r--parser.hpp118
-rw-r--r--particle.hpp114
-rw-r--r--tools/extract.sh2
-rw-r--r--tools/plot-paths-integrated.gp4
10 files changed, 377 insertions, 273 deletions
diff --git a/environment.hpp b/environment.hpp
index fb2eec6..dedf017 100644
--- a/environment.hpp
+++ b/environment.hpp
@@ -63,32 +63,39 @@ public:
Environment(Parser &value)
{
parser = value;
- timestep = 0;
+ timestep_surface = 0;
+ timestep_subsurface = 0;
std::cout << "[env] building the environment" << std::endl;
read_grid();
- read_environment(timestep);
+ read_environment(timestep_surface);
std::cout << std::endl;
std::cout << "[env] initialised 0th environment" << std::endl;
}
- void update()
- {
-
- timestep = timestep + 1;
- read_environment(timestep);
- std::cout << "[env] updated to " << timestep << "th environment" << std::endl;
+ void update_surface()
+ {
+ timestep_surface = timestep_surface + 1;
+ read_environment_surface(timestep_surface);
+ std::cout << "[env] updated surface to " << timestep_surface << "th environment" << std::endl;
+ }
+ void update_subsurface()
+ {
+ timestep_subsurface = timestep_subsurface + 1;
+ read_environment_subsurface(timestep_subsurface);
+ std::cout << "[env] updated subsurface to " << timestep_subsurface << "th environment" << std::endl;
}
private:
Parser parser;
- int timestep;
+ int timestep_surface;
+ int timestep_subsurface;
// ------------------------------------------------------------------------
// generic read function for dataset
@@ -271,15 +278,34 @@ private:
}
- // ------------------------------------------------------------------------
- // read in environment variables
- // ------------------------------------------------------------------------
- void read_environment(int timestep)
+ void read_environment_subsurface(int timestep)
{
+ H5::H5File fp_subsurface = H5::H5File(parser.subsurface_data_file.c_str(),
+ H5F_ACC_RDONLY);
- //
- // we start with surface environment variables
- //
+ // darcy velocity in x-direction
+ read_variable(fp_subsurface,
+ DARCY_VELOCITY_X,
+ parser.subsurface_data_group[timestep],
+ darcy_velocity_x);
+
+ // darcy velocity in y-direction
+ read_variable(fp_subsurface,
+ DARCY_VELOCITY_Y,
+ parser.subsurface_data_group[timestep],
+ darcy_velocity_y);
+
+ // darcy velocity in z-direction
+ read_variable(fp_subsurface,
+ DARCY_VELOCITY_Z,
+ parser.subsurface_data_group[timestep],
+ darcy_velocity_z);
+
+ fp_subsurface.close();
+ }
+
+ void read_environment_surface(int timestep)
+ {
H5::H5File fp_surface = H5::H5File(parser.surface_data_file.c_str(),
H5F_ACC_RDONLY);
@@ -316,33 +342,23 @@ private:
evaporation_flux);
fp_surface.close();
+ }
+
+ // ------------------------------------------------------------------------
+ // read in environment variables
+ // ------------------------------------------------------------------------
+ void read_environment(int timestep)
+ {
//
- // now we read in subsurface
+ // we start with surface environment variables
//
- H5::H5File fp_subsurface = H5::H5File(parser.subsurface_data_file.c_str(),
- H5F_ACC_RDONLY);
-
- // darcy velocity in x-direction
- read_variable(fp_subsurface,
- DARCY_VELOCITY_X,
- parser.subsurface_data_group[timestep],
- darcy_velocity_x);
-
- // darcy velocity in y-direction
- read_variable(fp_subsurface,
- DARCY_VELOCITY_Y,
- parser.subsurface_data_group[timestep],
- darcy_velocity_y);
-
- // darcy velocity in z-direction
- read_variable(fp_subsurface,
- DARCY_VELOCITY_Z,
- parser.subsurface_data_group[timestep],
- darcy_velocity_z);
-
- fp_subsurface.close();
+ read_environment_surface(timestep);
+ //
+ // now we read in subsurface
+ //
+ read_environment_subsurface(timestep);
}
};
diff --git a/header.hpp b/header.hpp
index 0575269..d4f7e00 100644
--- a/header.hpp
+++ b/header.hpp
@@ -46,6 +46,7 @@
#define OUTLET_FLAG 3
#define EVAPORATION_FLAG 4
+#define TIME_FUZZ 1.0e-3
#define WATERDEPTH_DRY 1.0e-8
#define ZERO_VELOCITY 1.0e-10
#define DT_MAX 432000.0
diff --git a/input/options.input b/input/options.input
index f48010d..2ec212f 100644
--- a/input/options.input
+++ b/input/options.input
@@ -6,13 +6,14 @@ seed : 108 // Random seed
n_iteration : 1000 // Number of iteration steps
subgrid_dx : 5.0 // Subgrid cell size
-surfacemesh : ./input/test/ats_vis_surface_mesh.h5 // Surface mesh file
-surfacedata : ./input/test/ats_vis_surface_data.h5 // Surface data file
-surfacetime : ./input/times.txt // Time levels for surface
+surfacemesh : ./example/ats_vis_surface_mesh.h5 // Surface mesh file
+surfacedata : ./example/ats_vis_surface_data.h5 // Surface data file
+surfacetime : ./input/times.txt // Time levels for surface
surfacegroup : ./input/groups.txt // Group names for surface
-subsurfacemesh : ./input/test/ats_vis_mesh.h5 // Subsurface mesh file
-subsurfacedata : ./input/test/ats_vis_data.h5 // Subsurface data file
+subsurfacemesh : ./example/ats_vis_mesh.h5 // Subsurface mesh file
+subsurfacedata : ./example/ats_vis_data.h5 // Subsurface data file
subsurfacetime : ./input/subtimes.txt // Time levels for subsurface
subsurfacegroup : ./input/subgroups.txt // Group names for subsurface
+restartfile : none \ No newline at end of file
diff --git a/input/subtimes.txt b/input/subtimes.txt
index 45adcab..d4bdb70 100644
--- a/input/subtimes.txt
+++ b/input/subtimes.txt
@@ -1,43 +1,43 @@
0
-118276
-236550
-354826
-473100
-591376
-709651
-827926
-946201
-1.06447e+06
-1.18276e+06
-1.30103e+06
-1.4193e+06
-1.53757e+06
-1655856
-1.77413e+06
-1.8924e+06
-2.01067e+06
-2.12896e+06
-2.24723e+06
-2.3655e+06
-2.48378e+06
-2.60206e+06
-2.72033e+06
-2.8386e+06
-2.95688e+06
-3.07516e+06
-3.19343e+06
-3.3117e+06
-3.42998e+06
-3.54826e+06
-3.66653e+06
-3.7848e+06
-3.90308e+06
-4.02136e+06
-4.13963e+06
-4.2579e+06
-4.37618e+06
-4.49446e+06
-4.61273e+06
-4.731e+06
-4.84928e+06
-4.96756e+06
+432001
+864000
+1.296e+06
+1.728e+06
+2.16e+06
+2.592e+06
+3.024e+06
+3.456e+06
+3.88799e+06
+4.32001e+06
+4.75201e+06
+5.184e+06
+5.61599e+06
+6.04801e+06
+6.48001e+06
+6.912e+06
+7.34399e+06
+7.77601e+06
+8.20801e+06
+8.64e+06
+9.07199e+06
+9.50401e+06
+9.93601e+06
+1.0368e+07
+1.08e+07
+1.1232e+07
+1.1664e+07
+1.2096e+07
+1.2528e+07
+1.296e+07
+1.3392e+07
+1.3824e+07
+1.4256e+07
+1.4688e+07
+1.512e+07
+1.5552e+07
+1.5984e+07
+1.6416e+07
+1.6848e+07
+1.728e+07
+1.7712e+07
+1.8144e+07
diff --git a/input/times.txt b/input/times.txt
index 45adcab..dbbe0ca 100644
--- a/input/times.txt
+++ b/input/times.txt
@@ -1,43 +1,108 @@
0
-118276
-236550
-354826
-473100
-591376
-709651
-827926
-946201
-1.06447e+06
-1.18276e+06
-1.30103e+06
-1.4193e+06
-1.53757e+06
-1655856
-1.77413e+06
-1.8924e+06
-2.01067e+06
-2.12896e+06
-2.24723e+06
-2.3655e+06
-2.48378e+06
-2.60206e+06
-2.72033e+06
-2.8386e+06
-2.95688e+06
-3.07516e+06
-3.19343e+06
-3.3117e+06
-3.42998e+06
-3.54826e+06
-3.66653e+06
-3.7848e+06
-3.90308e+06
-4.02136e+06
-4.13963e+06
-4.2579e+06
-4.37618e+06
-4.49446e+06
-4.61273e+06
-4.731e+06
-4.84928e+06
-4.96756e+06
+172800
+345600
+518400
+691200
+864000
+1.0368e+06
+1.2096e+06
+1.3824e+06
+1.5552e+06
+1.728e+06
+1.9008e+06
+2.0736e+06
+2.2464e+06
+2.4192e+06
+2.592e+06
+2.7648e+06
+2.9376e+06
+3.1104e+06
+3.28319e+06
+3.456e+06
+3.62881e+06
+3.80159e+06
+3.9744e+06
+4.14721e+06
+4.32001e+06
+4.49279e+06
+4.6656e+06
+4.83841e+06
+5.01119e+06
+5.184e+06
+5.35681e+06
+5.52959e+06
+5.7024e+06
+5.8752e+06
+6.04801e+06
+6.22079e+06
+6.3936e+06
+6.56641e+06
+6.73919e+06
+6.912e+06
+7.08481e+06
+7.25759e+06
+7.43039e+06
+7.6032e+06
+7.77601e+06
+7.94879e+06
+8.1216e+06
+8.29441e+06
+8.46719e+06
+8.64e+06
+8.81281e+06
+8.98558e+06
+9.15839e+06
+9.3312e+06
+9.50401e+06
+9.67679e+06
+9.8496e+06
+1.00224e+07
+1.01952e+07
+1.0368e+07
+1.05408e+07
+1.07136e+07
+1.08864e+07
+1.10592e+07
+1.1232e+07
+1.14048e+07
+1.15776e+07
+1.17504e+07
+1.19232e+07
+1.2096e+07
+1.22688e+07
+1.24416e+07
+1.26144e+07
+1.27872e+07
+1.296e+07
+1.31328e+07
+1.33056e+07
+1.34784e+07
+1.36512e+07
+1.3824e+07
+1.39968e+07
+1.41696e+07
+1.43424e+07
+1.45152e+07
+1.4688e+07
+1.48608e+07
+1.50336e+07
+1.52064e+07
+1.53792e+07
+1.5552e+07
+1.57248e+07
+1.58976e+07
+1.60704e+07
+1.62432e+07
+1.6416e+07
+1.65888e+07
+1.67616e+07
+1.69344e+07
+1.71072e+07
+1.728e+07
+1.74528e+07
+1.76256e+07
+1.77984e+07
+1.79712e+07
+1.8144e+07
+1.83168e+07
+1.84896e+07
diff --git a/main.cpp b/main.cpp
index 45d3cb1..76277cf 100644
--- a/main.cpp
+++ b/main.cpp
@@ -44,8 +44,6 @@ int main(int argc, char* argv[])
if (flag == "-c") {
Mesh mesh = Mesh(parser);
mesh.compile();
- // mesh.subgrid();
- // mesh.map();
mesh.dump();
std::cout << "[ ] meshing completed, enjoy the subgrid" << std::endl;
std::cout << std::endl;
@@ -59,11 +57,76 @@ int main(int argc, char* argv[])
ParticleManager pmanager = ParticleManager(parser);
- for (size_t t = 1; t < parser.surface_time.size(); ++t) {
- pmanager.advect(parser.surface_time[t]);
- pmanager.environment.update();
+ size_t nstep_surface = parser.surface_time.size();
+ size_t nstep_subsurface = parser.subsurface_time.size();
+
+ size_t step_surface = 1;
+ size_t step_subsurface = 1;
+
+ // bootstrap
+ double t_surface = parser.surface_time[0];
+ double t_subsurface = parser.subsurface_time[0];
+ double t_combined = std::min(t_surface, t_subsurface);
+
+ // we compute the difference between the next time level
+ // and the combined time level to check if we need to
+ // update the environment. the first step is "free".
+ double dt_surface = 0.0;
+ double dt_subsurface = 0.0;
+
+ bool fin_surface = false;
+ bool fin_subsurface = false;
+
+ // (step_surface < nstep_surface) || (step_subsurface < nstep_subsurface)
+ while (!fin_surface || !fin_subsurface) {
+
+ // advect all particles
+ pmanager.advect(t_combined);
+
+ // check if the surface environment needs to be updated
+ if (!fin_surface && dt_surface <= TIME_FUZZ) {
+
+ step_surface ++;
+ t_surface = parser.surface_time[step_surface]; // get new time step
+ pmanager.environment.update_surface(); // update environment
+ t_combined = std::min(t_surface, t_subsurface);
+
+ if (step_surface == nstep_surface - 1) {
+ fin_surface = true;
+ }
+
+ }
+
+ // check if the subsurface environment needs to be updated
+ if (!fin_subsurface && dt_subsurface <= TIME_FUZZ) {
+
+ step_subsurface ++;
+ t_subsurface = parser.subsurface_time[step_subsurface];
+ pmanager.environment.update_subsurface();
+ t_combined = std::min(t_surface, t_subsurface);
+
+ if (step_subsurface == nstep_subsurface - 1) {
+ fin_subsurface = true;
+ }
+
+ }
+
+ dt_surface = std::fabs(t_combined - t_surface);
+ dt_subsurface = std::fabs(t_combined - t_subsurface);
+
+ if (fin_surface) {
+ t_combined = t_subsurface;
+ }
+
+ if (fin_subsurface) {
+ t_combined = t_surface;
+ }
+
}
+ std::cout << "[ ] particle advecting completed, enjoy the results!" << std::endl;
+ std::cout << std::endl;
+
for (size_t i = 0; i < pmanager.particles.size(); ++i) {
pmanager.particles[i].dump(i);
}
diff --git a/parser.hpp b/parser.hpp
index fa4e3ab..f0142a0 100644
--- a/parser.hpp
+++ b/parser.hpp
@@ -72,6 +72,7 @@ public:
std::string surface_data_file;
std::string subsurface_mesh_file;
std::string subsurface_data_file;
+ std::string checkfile;
std::vector<std::string> surface_data_group;
std::vector<double> surface_time;
@@ -119,6 +120,7 @@ public:
for (uint i = 0; i < subsurface_data_group.size(); i ++)
std::cout << subsurface_data_group[i] << " ";
std::cout << std::endl;
+ std::cout << "[prs] restart file: " << checkfile << std::endl;
std::cout << std::endl;
}
@@ -151,41 +153,45 @@ public:
pline.value >> surface_data_file;
} else if (!strcmp("surfacetime", pline.key.c_str())) {
- std::string timefile;
- pline.value >> timefile;
+ std::string timefile;
+ pline.value >> timefile;
- std::ifstream in(timefile);
- double time;
+ std::ifstream in(timefile);
+ double time;
- if (in.is_open()) {
-
- while (!in.eof()) {
- in >> time;
- surface_time.push_back(time);
- }
+ if (in.is_open()) {
+
+ while (!in.eof()) {
+ in >> time;
+ if (in.good()) {
+ surface_time.push_back(time);
+ }
+ }
+
+ } else {
+ std::cerr << "[err] file " << timefile << " can't be opened" << std::endl;
+ }
- } else {
- std::cerr << "[err] file " << timefile << " can't be opened" << std::endl;
- }
-
} else if (!strcmp("surfacegroup", pline.key.c_str())) {
- std::string groupfile;
- pline.value >> groupfile;
+ std::string groupfile;
+ pline.value >> groupfile;
- std::ifstream in(groupfile);
- std::string group;
+ std::ifstream in(groupfile);
+ std::string group;
- if (in.is_open()) {
+ if (in.is_open()) {
- while (!in.eof()) {
- in >> group;
- surface_data_group.push_back(group);
- }
+ while (!in.eof()) {
+ in >> group;
+ if (in.good()) {
+ surface_data_group.push_back(group);
+ }
+ }
- } else {
- std::cerr << "[err] file " << groupfile << " can't be opened" << std::endl;
- }
+ } else {
+ std::cerr << "[err] file " << groupfile << " can't be opened" << std::endl;
+ }
} else if (!strcmp("subsurfacemesh", pline.key.c_str())) {
pline.value >> subsurface_mesh_file;
@@ -193,42 +199,46 @@ public:
pline.value >> subsurface_data_file;
} else if (!strcmp("subsurfacetime", pline.key.c_str())) {
- std::string timefile;
- pline.value >> timefile;
+ std::string timefile;
+ pline.value >> timefile;
+
+ std::ifstream in(timefile);
+ double time;
- std::ifstream in(timefile);
- double time;
+ if (in.is_open()) {
- if (in.is_open()) {
-
- while (!in.eof()) {
- in >> time;
- subsurface_time.push_back(time);
- }
+ while (!in.eof()) {
+ in >> time;
+ if (in.good()) {
+ subsurface_time.push_back(time);
+ }
+ }
+
+ } else {
+ std::cerr << "[err] file " << timefile << " can't be opened" << std::endl;
+ }
- } else {
- std::cerr << "[err] file " << timefile << " can't be opened" << std::endl;
- }
-
} else if (!strcmp("subsurfacegroup", pline.key.c_str())) {
- std::string groupfile;
- pline.value >> groupfile;
+ std::string groupfile;
+ pline.value >> groupfile;
+
+ std::ifstream in(groupfile);
+ std::string group;
- std::ifstream in(groupfile);
- std::string group;
+ if (in.is_open()) {
- if (in.is_open()) {
+ while (!in.eof()) {
+ in >> group;
+ if (in.good()) {
+ subsurface_data_group.push_back(group);
+ }
+ }
- while (!in.eof()) {
- in >> group;
- subsurface_data_group.push_back(group);
- }
+ } else {
+ std::cerr << "[err] file " << groupfile << " can't be opened" << std::endl;
+ }
- } else {
- std::cerr << "[err] file " << groupfile << " can't be opened" << std::endl;
- }
-
} else if (!strcmp("n_agent", pline.key.c_str())) {
pline.value >> n_agent;
} else if (!strcmp("fuzz", pline.key.c_str())) {
@@ -239,6 +249,8 @@ public:
pline.value >> seed;
} else if (!strcmp("n_iteration", pline.key.c_str())) {
pline.value >> n_iteration;
+ } else if (!strcmp("restartfile", pline.key.c_str())) {
+ pline.value >> checkfile;
} else {
std::cerr << "[err] unrecognised option key" << std::endl;
}
diff --git a/particle.hpp b/particle.hpp
index 0f5026a..5f95ff5 100644
--- a/particle.hpp
+++ b/particle.hpp
@@ -116,41 +116,45 @@ public:
rng = std::default_random_engine(parser.seed);
dist = std::uniform_real_distribution<double>(0.0, 1.0);
- for (int i = 0; i < parser.n_agent; ++ i) {
+ if (!strcmp("none", parser.checkfile.c_str())) {
- Particle p;
+ for (int i = 0; i < parser.n_agent; ++ i) {
- p.cell_index = -9999;
- int ix, iy, iz;
+ Particle p;
- while (p.cell_index == -9999) {
+ p.cell_index = -9999;
+ int ix, iy, iz;
- ix = xdist(rng);
- iy = ydist(rng);
- iz = zdist(rng) * 0;
+ while (p.cell_index == -9999) {
- p.cell_index = environment.indices[ix][iy][iz];
+ ix = xdist(rng);
+ iy = ydist(rng);
+ iz = zdist(rng) * 0;
- }
+ p.cell_index = environment.indices[ix][iy][iz];
- // get the coordinates
- p.x = environment.xll_corner + 0.5 * dx + ix * dx;
- p.y = environment.yhl_corner - 0.5 * dx - iy * dx;
- p.z = environment.elevations[ix][iy][iz];
- p.t = parser.start_time;
-
- if (iz > 0) {
- // if the particle is not in the top layer, it's in the subsurface
- p.domain.push_back(SUBSURFACE_FLAG);
- } else {
- // otherwise, it's at the surface
- p.domain.push_back(SURFACE_FLAG);
- }
+ }
- p.coordinates.push_back({ p.x, p.y, p.z });
- p.time.push_back(p.t);
+ // get the coordinates
+ p.x = environment.xll_corner + 0.5 * dx + ix * dx;
+ p.y = environment.yhl_corner - 0.5 * dx - iy * dx;
+ p.z = environment.elevations[ix][iy][iz];
+ p.t = parser.start_time;
+
+ if (iz > 0) {
+ // if the particle is not in the top layer, it's in the subsurface
+ p.domain.push_back(SUBSURFACE_FLAG);
+ } else {
+ // otherwise, it's at the surface
+ p.domain.push_back(SURFACE_FLAG);
+ }
- particles.push_back(p);
+ p.coordinates.push_back({ p.x, p.y, p.z });
+ p.time.push_back(p.t);
+
+ particles.push_back(p);
+
+ }
}
@@ -369,64 +373,6 @@ public:
}
-
- // // move particle in the subsurface
- // void advectv()
- // {
-
- // std::cout << "[ptl] advecting particles" << std::endl;
-
- // size_t i;
- // double dx = parser.subgrid_dx;
-
- // #pragma omp parallel
- // {
-
- // #pragma omp for private(i)
- // for (i = 0; i < particles.size(); ++i) {
-
- // while (particles[i].time.back() < timehorizon) {
-
- // int flag = particles[i].domain.back();
-
- // double vx, vy, vz, vmag, dt;
-
- // vx = environment.velocity_x[particles[i].cell_index];
- // vy = environment.velocity_y[particles[i].cell_index];
-
- // vmag = std::sqrt(vx * vx + vy * vy);
-
- // dt = CFL * dx / (vmag + 1.0e-10);
- // dt = std::min(dt, DT_MAX);
- // dt = std::min(dt, timehorizon - particles[i].time.back());
-
- // particles[i].advect(vx, vy, 0.0, dt);
-
- // // get the indices
- // int ix, iy, iz;
- // ix = (int) floor((particles[i].x - environment.xll_corner) / dx);
- // iy = (int) floor((environment.yhl_corner - particles[i].y) / dx);
- // iz = 0;
-
- // // update the particle
- // particles[i].time.push_back(particles[i].t);
- // particles[i].cell_index = environment.indices[ix][iy][iz];
- // particles[i].z = environment.elevations[ix][iy][iz];
- // particles[i].coordinates.push_back({particles[i].x,
- // particles[i].y,
- // particles[i].z});
- // particles[i].domain.push_back(flag);
-
- // }
-
- // }
-
- // }
-
- // }
-
-
-
private:
Parser parser;
diff --git a/tools/extract.sh b/tools/extract.sh
index 7222f83..79cd72b 100644
--- a/tools/extract.sh
+++ b/tools/extract.sh
@@ -5,4 +5,4 @@ groupname=$2
./extract-groups.sh ${filename} ${groupname} > _tmpgroups.txt
cat _tmpgroups.txt | ./extract-times.sh ${filename} ${groupname} > _tmptimes.txt
-cat _tmptimes.txt | awk '{ print $1 * 360000.0 * 24 }'
+cat _tmptimes.txt | awk '{ print $1 * 365.25 * 3600 * 24 }'
diff --git a/tools/plot-paths-integrated.gp b/tools/plot-paths-integrated.gp
index 8fe4a06..723c693 100644
--- a/tools/plot-paths-integrated.gp
+++ b/tools/plot-paths-integrated.gp
@@ -1,7 +1,7 @@
#!/usr/bin/env gnuplot
set term png font "hack"
-set output "output/figure.png"
+set output "../output/figure.png"
set border linecolor rgbcolor "white"
set key textcolor rgbcolor "white"
@@ -20,7 +20,7 @@ set ytics 1
set size ratio -1
splot \
- for [i = 100:999] "output/p-000".i.".csv" \
+ for [i = 100:999] "../output/p-000".i.".csv" \
u ($3/1000):($4/1000):($5/1000)\
with lines\
notitle