void tillage (const Geometry& geo, const double from, const double to, const double surface_loose, const double RR0, const SoilWater& soil_water, const OrganicMatter& organic_matter) { // Water content. std::map<const Horizon*, double> water; for (std::size_t c = 0; c < geo.cell_size (); c++) if (geo.fraction_in_z_interval (c, from, to) > 0.01) water[horizon_[c]] += soil_water.Theta (c) * geo.cell_volume (c); static const double c_fraction_in_humus = 0.587; static const double aom_from = 0.0; // [cm] static const double aom_to = -15.0; // [cm] const double AOM_C = organic_matter.AOM_C (geo, aom_from, aom_to) / geo.surface_area (); // [g C/cm^2] const double AOM15 = 10.0 * AOM_C / c_fraction_in_humus; // [kg/m^2] for (std::map<const Horizon*, double>::const_iterator i = water.begin (); i != water.end (); i++) { const Horizon *const hor = (*i).first; const double total_water = (*i).second; const double total_volume = volume[hor]; daisy_assert (total_water < total_volume); const double Theta = total_water / total_volume; hor->hydraulic->tillage (surface_loose, RR0, Theta, AOM15); } }
void ReactionDenit::tick_soil (const Units&, const Geometry& geo, const Soil& soil, const SoilWater& soil_water, const SoilHeat& soil_heat, const OrganicMatter& organic_matter, Chemistry& chemistry, const double dt, Treelog&) { const size_t cell_size = geo.cell_size (); const std::vector<bool> active = organic_matter.active (); Chemical& soil_NO3 = chemistry.find (Chemical::NO3 ()); for (size_t i = 0; i < cell_size; i++) { if (!active[i]) { converted[i] = converted_fast[i] = converted_redox[i] = potential[i] = potential_fast[i] = 0.0; continue; } const double CO2_fast = organic_matter.CO2_fast (i); const double CO2_slow = organic_matter.CO2 (i) - CO2_fast; const double Theta = soil_water.Theta (i); const double Theta_sat = soil_water.Theta_ice (soil, i, 0.0); const double Theta_fraction = Theta / Theta_sat; const double NO3 = soil_NO3.C_primary (i) * Theta; const double T = soil_heat.T (i); const double height = geo.cell_z (i); const double T_factor = (heat_factor.size () < 1) ? Abiotic::f_T2 (T) : heat_factor (T); const double pot = T_factor * alpha * CO2_slow; const double w_factor = water_factor (Theta_fraction); const double rate = w_factor * pot; const double pot_fast = T_factor * alpha_fast * CO2_fast; const double w_factor_fast = water_factor_fast (Theta_fraction); const double rate_fast = w_factor_fast * pot_fast; const double M = (std::min (rate, K * NO3) + std::min (rate_fast, K_fast * NO3)) * dt; if (redox_height <= 0 && height < redox_height) { converted[i] = NO3 / dt; converted_redox[i] = (NO3 - M) / dt; } else { converted[i] = M / dt; converted_redox[i] = 0.0; } converted_fast[i] = (M / dt > rate ? M / dt - rate : 0.0); potential[i] = pot; potential_fast[i] = pot_fast; } soil_NO3.add_to_transform_sink (converted); }
void Movement1D::tick (const Soil& soil, SoilWater& soil_water, const SoilHeat& soil_heat, Surface& surface, Groundwater& groundwater, const Time& time, const Weather& weather, const double dt, Treelog& msg) { const size_t edge_size = geo->edge_size (); const size_t cell_size = geo->cell_size (); TREELOG_MODEL (msg); // Cells. std::vector<double> S_sum (cell_size); std::vector<double> h_old (cell_size); std::vector<double> Theta_old (cell_size); std::vector<double> h_ice (cell_size); std::vector<double> h (cell_size); std::vector<double> Theta (cell_size); for (size_t c = 0; c < cell_size; c++) { S_sum[c] = soil_water.S_sum (c); h_old[c] = soil_water.h_old (c); Theta_old[c] = soil_water.Theta_old (c); h_ice[c] = soil_water.h_ice (c); h[c] = soil_water.h (c); Theta[c] = soil_water.Theta (c); } // Edges. std::vector<double> q (edge_size, 0.0); std::vector<double> q_p (edge_size, 0.0); for (size_t e = 0; e < edge_size; e++) { q[e] = soil_water.q_matrix (e); q_p[e] = soil_water.q_tertiary (e); } tick_water (*geo, soil, soil_heat, surface, groundwater, S_sum, h_old, Theta_old, h_ice, h, Theta, q, q_p, dt, msg); soil_water.set_matrix (h, Theta, q); }
void Ridge::Implementation::initialize (const Geometry1D& geo, const Soil& soil, const SoilWater& soil_water) { // Find values depending on soil numerics. last_cell = geo.interval_plus (lowest); daisy_assert (last_cell+1 < soil.size ()); dz = 0 - geo.zplus (last_cell); K_sat_below = soil.K (last_cell+1, 0.0, 0.0, 20.0); // Initialize water content. Theta = 0.0; for (int i = 0; i <= last_cell; i++) Theta += soil_water.Theta (i) * geo.dz (i); Theta /= dz; Theta_pre = Theta; daisy_assert (Theta < soil.Theta (0, 0.0, 0.0)); h = soil.h (0, Theta); K = soil.K (0, 0.0, 0.0, 20.0); }
double Movement1D::surface_snow_T (const Soil& soil, const SoilWater& soil_water, const SoilHeat& soil_heat, const double T_snow, const double K_snow, const double dZs) const { // Information about soil. const double K_soil = soil.heat_conductivity (0, soil_water.Theta (0), soil_water.X_ice (0)) * 1e-7 * 100.0 / 3600.0; // [erg/cm/h/dg C] -> [W/m/dg C] const double Z = -geo->cell_z (0) / 100.0; // [cm] -> [m] const double T_soil = soil_heat.T (0); // [dg C] daisy_assert (T_soil > -100.0); daisy_assert (T_soil < 50.0); const double T = (K_soil / Z * T_soil + K_snow / dZs * T_snow) / (K_soil / Z + K_snow / dZs); daisy_assert (T > -100.0); daisy_assert (T < 50.0); return T; }
void UZRectMollerup::tick (const GeometryRect& geo, const std::vector<size_t>& drain_cell, const double drain_water_level, const Soil& soil, SoilWater& soil_water, const SoilHeat& soil_heat, const Surface& surface, const Groundwater& groundwater, const double dt, Treelog& msg) { daisy_assert (K_average.get ()); const size_t edge_size = geo.edge_size (); // number of edges const size_t cell_size = geo.cell_size (); // number of cells // Insert magic here. ublas::vector<double> Theta (cell_size); // water content ublas::vector<double> Theta_previous (cell_size); // at start of small t-step ublas::vector<double> h (cell_size); // matrix pressure ublas::vector<double> h_previous (cell_size); // at start of small timestep ublas::vector<double> h_ice (cell_size); // ublas::vector<double> S (cell_size); // sink term ublas::vector<double> S_vol (cell_size); // sink term #ifdef TEST_OM_DEN_ER_BRUGT ublas::vector<double> S_macro (cell_size); // sink term std::vector<double> S_drain (cell_size, 0.0); // matrix-> macro -> drain flow std::vector<double> S_drain_sum (cell_size, 0.0); // For large timestep const std::vector<double> S_matrix (cell_size, 0.0); // matrix -> macro std::vector<double> S_matrix_sum (cell_size, 0.0); // for large timestep #endif ublas::vector<double> T (cell_size); // temperature ublas::vector<double> Kold (edge_size); // old hydraulic conductivity ublas::vector<double> Ksum (edge_size); // Hansen hydraulic conductivity ublas::vector<double> Kcell (cell_size); // hydraulic conductivity ublas::vector<double> Kold_cell (cell_size); // old hydraulic conductivity ublas::vector<double> Ksum_cell (cell_size); // Hansen hydraulic conductivity ublas::vector<double> h_lysimeter (cell_size); std::vector<bool> active_lysimeter (cell_size); const std::vector<size_t>& edge_above = geo.cell_edges (Geometry::cell_above); const size_t edge_above_size = edge_above.size (); ublas::vector<double> remaining_water (edge_above_size); std::vector<bool> drain_cell_on (drain_cell.size (),false); for (size_t i = 0; i < edge_above_size; i++) { const size_t edge = edge_above[i]; remaining_water (i) = surface.h_top (geo, edge); } ublas::vector<double> q; // Accumulated flux q = ublas::zero_vector<double> (edge_size); ublas::vector<double> dq (edge_size); // Flux in small timestep. dq = ublas::zero_vector<double> (edge_size); //Make Qmat area diagonal matrix //Note: This only needs to be calculated once... ublas::banded_matrix<double> Qmat (cell_size, cell_size, 0, 0); for (int c = 0; c < cell_size; c++) Qmat (c, c) = geo.cell_volume (c); // make vectors for (size_t cell = 0; cell != cell_size ; ++cell) { Theta (cell) = soil_water.Theta (cell); h (cell) = soil_water.h (cell); h_ice (cell) = soil_water.h_ice (cell); S (cell) = soil_water.S_sum (cell); S_vol (cell) = S (cell) * geo.cell_volume (cell); if (use_forced_T) T (cell) = forced_T; else T (cell) = soil_heat.T (cell); h_lysimeter (cell) = geo.zplus (cell) - geo.cell_z (cell); } // Remember old value. Theta_error = Theta; // Start time loop double time_left = dt; // How much of the large time step left. double ddt = dt; // We start with small == large time step. int number_of_time_step_reductions = 0; int iterations_with_this_time_step = 0; int n_small_time_steps = 0; while (time_left > 0.0) { if (ddt > time_left) ddt = time_left; std::ostringstream tmp_ddt; tmp_ddt << "Time t = " << (dt - time_left) << "; ddt = " << ddt << "; steps " << n_small_time_steps << "; time left = " << time_left; Treelog::Open nest (msg, tmp_ddt.str ()); if (n_small_time_steps > 0 && (n_small_time_steps%msg_number_of_small_time_steps) == 0) { msg.touch (); msg.flush (); } n_small_time_steps++; if (n_small_time_steps > max_number_of_small_time_steps) { msg.debug ("Too many small timesteps"); throw "Too many small timesteps"; } // Initialization for each small time step. if (debug > 0) { std::ostringstream tmp; tmp << "h = " << h << "\n"; tmp << "Theta = " << Theta; msg.message (tmp.str ()); } int iterations_used = 0; h_previous = h; Theta_previous = Theta; if (debug == 5) { std::ostringstream tmp; tmp << "Remaining water at start: " << remaining_water; msg.message (tmp.str ()); } ublas::vector<double> h_conv; for (size_t cell = 0; cell != cell_size ; ++cell) active_lysimeter[cell] = h (cell) > h_lysimeter (cell); for (size_t edge = 0; edge != edge_size ; ++edge) { Kold[edge] = find_K_edge (soil, geo, edge, h, h_ice, h_previous, T); Ksum [edge] = 0.0; } std::vector<top_state> state (edge_above.size (), top_undecided); // We try harder with smaller timesteps. const int max_loop_iter = max_iterations * (number_of_time_step_reductions * max_iterations_timestep_reduction_factor + 1); do // Start iteration loop { h_conv = h; iterations_used++; std::ostringstream tmp_conv; tmp_conv << "Convergence " << iterations_used; Treelog::Open nest (msg, tmp_conv.str ()); if (debug == 7) msg.touch (); // Calculate conductivity - The Hansen method for (size_t e = 0; e < edge_size; e++) { Ksum[e] += find_K_edge (soil, geo, e, h, h_ice, h_previous, T); Kedge[e] = (Ksum[e] / (iterations_used + 0.0)+ Kold[e]) / 2.0; } //Initialize diffusive matrix Solver::Matrix diff (cell_size); // diff = ublas::zero_matrix<double> (cell_size, cell_size); diffusion (geo, Kedge, diff); //Initialize gravitational matrix ublas::vector<double> grav (cell_size); //ublass compatibility grav = ublas::zero_vector<double> (cell_size); gravitation (geo, Kedge, grav); // Boundary matrices and vectors ublas::banded_matrix<double> Dm_mat (cell_size, cell_size, 0, 0); // Dir bc Dm_mat = ublas::zero_matrix<double> (cell_size, cell_size); ublas::vector<double> Dm_vec (cell_size); // Dir bc Dm_vec = ublas::zero_vector<double> (cell_size); ublas::vector<double> Gm (cell_size); // Dir bc Gm = ublas::zero_vector<double> (cell_size); ublas::vector<double> B (cell_size); // Neu bc B = ublas::zero_vector<double> (cell_size); lowerboundary (geo, groundwater, active_lysimeter, h, Kedge, dq, Dm_mat, Dm_vec, Gm, B, msg); upperboundary (geo, soil, T, surface, state, remaining_water, h, Kedge, dq, Dm_mat, Dm_vec, Gm, B, ddt, debug, msg, dt); Darcy (geo, Kedge, h, dq); //for calculating drain fluxes //Initialize water capacity matrix ublas::banded_matrix<double> Cw (cell_size, cell_size, 0, 0); for (size_t c = 0; c < cell_size; c++) Cw (c, c) = soil.Cw2 (c, h[c]); std::vector<double> h_std (cell_size); //ublas vector -> std vector std::copy(h.begin (), h.end (), h_std.begin ()); #ifdef TEST_OM_DEN_ER_BRUGT for (size_t cell = 0; cell != cell_size ; ++cell) { S_macro (cell) = (S_matrix[cell] + S_drain[cell]) * geo.cell_volume (cell); } #endif //Initialize sum matrix Solver::Matrix summat (cell_size); noalias (summat) = diff + Dm_mat; //Initialize sum vector ublas::vector<double> sumvec (cell_size); sumvec = grav + B + Gm + Dm_vec - S_vol #ifdef TEST_OM_DEN_ER_BRUGT - S_macro #endif ; // QCw is shorthand for Qmatrix * Cw Solver::Matrix Q_Cw (cell_size); noalias (Q_Cw) = prod (Qmat, Cw); //Initialize A-matrix Solver::Matrix A (cell_size); noalias (A) = (1.0 / ddt) * Q_Cw - summat; // Q_Cw_h is shorthand for Qmatrix * Cw * h const ublas::vector<double> Q_Cw_h = prod (Q_Cw, h); //Initialize b-vector ublas::vector<double> b (cell_size); //b = sumvec + (1.0 / ddt) * (Qmatrix * Cw * h + Qmatrix *(Wxx-Wyy)); b = sumvec + (1.0 / ddt) * (Q_Cw_h + prod (Qmat, Theta_previous-Theta)); // Force active drains to zero h. drain (geo, drain_cell, drain_water_level, h, Theta_previous, Theta, S_vol, #ifdef TEST_OM_DEN_ER_BRUGT S_macro, #endif dq, ddt, drain_cell_on, A, b, debug, msg); try { solver->solve (A, b, h); // Solve Ah=b with regard to h. } catch (const char *const error) { std::ostringstream tmp; tmp << "Could not solve equation system: " << error; msg.warning (tmp.str ()); // Try smaller timestep. iterations_used = max_loop_iter + 100; break; } for (int c=0; c < cell_size; c++) // update Theta Theta (c) = soil.Theta (c, h (c), h_ice (c)); if (debug > 1) { std::ostringstream tmp; tmp << "Time left = " << time_left << ", ddt = " << ddt << ", iteration = " << iterations_used << "\n"; tmp << "B = " << B << "\n"; tmp << "h = " << h << "\n"; tmp << "Theta = " << Theta; msg.message (tmp.str ()); } for (int c=0; c < cell_size; c++) { if (h (c) < min_pressure_potential || h (c) > max_pressure_potential) { std::ostringstream tmp; tmp << "Pressure potential out of realistic range, h[" << c << "] = " << h (c); msg.debug (tmp.str ()); iterations_used = max_loop_iter + 100; break; } } } while (!converges (h_conv, h) && iterations_used <= max_loop_iter); if (iterations_used > max_loop_iter) { number_of_time_step_reductions++; if (number_of_time_step_reductions > max_time_step_reductions) { msg.debug ("Could not find solution"); throw "Could not find solution"; } iterations_with_this_time_step = 0; ddt /= time_step_reduction; h = h_previous; Theta = Theta_previous; } else { // Update dq for new h. ublas::banded_matrix<double> Dm_mat (cell_size, cell_size, 0, 0); // Dir bc Dm_mat = ublas::zero_matrix<double> (cell_size, cell_size); ublas::vector<double> Dm_vec (cell_size); // Dir bc Dm_vec = ublas::zero_vector<double> (cell_size); ublas::vector<double> Gm (cell_size); // Dir bc Gm = ublas::zero_vector<double> (cell_size); ublas::vector<double> B (cell_size); // Neu bc B = ublas::zero_vector<double> (cell_size); lowerboundary (geo, groundwater, active_lysimeter, h, Kedge, dq, Dm_mat, Dm_vec, Gm, B, msg); upperboundary (geo, soil, T, surface, state, remaining_water, h, Kedge, dq, Dm_mat, Dm_vec, Gm, B, ddt, debug, msg, dt); Darcy (geo, Kedge, h, dq); #ifdef TEST_OM_DEN_ER_BRUGT // update macropore flow components for (int c = 0; c < cell_size; c++) { S_drain_sum[c] += S_drain[c] * ddt/dt; S_matrix_sum[c] += S_matrix[c] * ddt/dt; } #endif std::vector<double> h_std_new (cell_size); std::copy(h.begin (), h.end (), h_std_new.begin ()); // Update remaining_water. for (size_t i = 0; i < edge_above.size (); i++) { const int edge = edge_above[i]; const int cell = geo.edge_other (edge, Geometry::cell_above); const double out_sign = (cell == geo.edge_from (edge)) ? 1.0 : -1.0; remaining_water[i] += out_sign * dq (edge) * ddt; daisy_assert (std::isfinite (dq (edge))); } if (debug == 5) { std::ostringstream tmp; tmp << "Remaining water at end: " << remaining_water; msg.message (tmp.str ()); } // Contribution to large time step. daisy_assert (std::isnormal (dt)); daisy_assert (std::isnormal (ddt)); q += dq * ddt / dt; for (size_t e = 0; e < edge_size; e++) { daisy_assert (std::isfinite (dq (e))); daisy_assert (std::isfinite (q (e))); } for (size_t e = 0; e < edge_size; e++) { daisy_assert (std::isfinite (dq (e))); daisy_assert (std::isfinite (q (e))); } time_left -= ddt; iterations_with_this_time_step++; if (iterations_with_this_time_step > time_step_reduction) { number_of_time_step_reductions--; iterations_with_this_time_step = 0; ddt *= time_step_reduction; } } // End of small time step. } // Mass balance. // New = Old - S * dt + q_in * dt - q_out * dt + Error => // 0 = Old - New - S * dt + q_in * dt - q_out * dt + Error Theta_error -= Theta; // Old - New Theta_error -= S * dt; #ifdef TEST_OM_DEN_ER_BRUGT for (size_t c = 0; c < cell_size; c++) Theta_error (c) -= (S_matrix_sum[c] + S_drain_sum[c]) * dt; #endif for (size_t edge = 0; edge != edge_size; ++edge) { const int from = geo.edge_from (edge); const int to = geo.edge_to (edge); const double flux = q (edge) * geo.edge_area (edge) * dt; if (geo.cell_is_internal (from)) Theta_error (from) -= flux / geo.cell_volume (from); if (geo.cell_is_internal (to)) Theta_error (to) += flux / geo.cell_volume (to); } // Find drain sink from mass balance. #ifdef TEST_OM_DEN_ER_BRUGT std::fill(S_drain.begin (), S_drain.end (), 0.0); #else std::vector<double> S_drain (cell_size); #endif for (size_t i = 0; i < drain_cell.size (); i++) { const size_t cell = drain_cell[i]; S_drain[cell] = Theta_error (cell) / dt; Theta_error (cell) -= S_drain[cell] * dt; } if (debug == 2) { double total_error = 0.0; double total_abs_error = 0.0; double max_error = 0.0; int max_cell = -1; for (size_t cell = 0; cell != cell_size; ++cell) { const double volume = geo.cell_volume (cell); const double error = Theta_error (cell); total_error += volume * error; total_abs_error += std::fabs (volume * error); if (std::fabs (error) > std::fabs (max_error)) { max_error = error; max_cell = cell; } } std::ostringstream tmp; tmp << "Total error = " << total_error << " [cm^3], abs = " << total_abs_error << " [cm^3], max = " << max_error << " [] in cell " << max_cell; msg.message (tmp.str ()); } // Make it official. for (size_t cell = 0; cell != cell_size; ++cell) soil_water.set_content (cell, h (cell), Theta (cell)); #ifdef TEST_OM_DEN_ER_BRUGT soil_water.add_tertiary_sink (S_matrix_sum); soil_water.drain (S_drain_sum, msg); #endif for (size_t edge = 0; edge != edge_size; ++edge) { daisy_assert (std::isfinite (q[edge])); soil_water.set_flux (edge, q[edge]); } soil_water.drain (S_drain, msg); // End of large time step. }
void Ridge::Implementation::tick (const Geometry1D& geo, const Soil& soil, const SoilWater& soil_water, const double external_ponding, const double dt) { // First, we need to find the internal ponding height. // The external ponding assumes a flat surface, we need to find the // point (x_pond, z_pond) in the ridge geometry where air, soil and // surface water all meet. if (external_ponding < 1.0e-5) { // No ponding. x_pond = 0.0; z_pond = lowest; } else if (external_ponding > highest - 1.0e-5) { // Above top of ridge (ridge geometry doesn't matter then). x_pond = 1.0; z_pond = external_ponding; } else { // Somewhere in between. // // For a given point (x, z), x * z is the amount of space // available had all the soil from the ridge been removed. If // you remove the space occupied by the ridge soil, the // remaining space is available to free water and air. So we // need to find the point where x * z minus the ridge soil is // equal to the amount of water in the pond. // // We can't solve this analytically in general, but we can solve // it when the ridge geometry is a straight line. Since the // geometry is represented by a PLF, we can find the relevant // piece and solve it there. // Note that x * z can be negative because our zero point is at // the original soil surface, not the bottom of the ridge, this // doesn't matter since we are only interested in differences, // not absolute number. // Ridge soil until now. double integral = 0.0; // Last point in the PLF. double x0 = 0.0; double z0 = lowest; for (unsigned int i = 1; ; i++) { // We already know the answer must lie between two points. daisy_assert (i < z.size ()); // New point in the PLF. const double x1 = z.x (i); const double z1 = z.y (i); // Difference. const double delta_z = z1 - z0; const double delta_x = x1 - x0; // Average z height. const double average_z = 0.5 * (z0 + z1); // Ridge soil for this step. const double this_step = delta_x * average_z; // Total space for this step. const double total = x1 * z1; // Check if this interval can contain all the ponded water. if (total - (integral + this_step) >= external_ponding) { // Find slant in this interval. const double slant = delta_z / delta_x; // We now need to find the point (x_p, z_p) where // total - (integral + this_step) = external_ponding) // where // total = x_p * z_p // this_step = 0.5 * (x_p - x0) * (z0 + z_p) // // Substitute // z_p = z0 + slant * (x_p - x0) // and we get // x_p * (z0 + slant * (x_p - x0)) // - (integral // + 0.5 * (x_p - x0) * (z0 + z0 + slant * (x_p - x0)) // = external_ponding // <=> // x_p * z0 + slant * x_p^2 - slant * x0 * x_p // - integral // - (x_p - x0) * ( z0 + 0.5 * slant * (x_p - x0)) // - external_ponding // = 0 // <=> // x_p * z0 + slant * x_p^2 - slant * x0 * x_p // - integral // - x_p * z0 // - 0.5 * x_p * slant * (x_p - x0) // + x0 * z0 // + 0.5 * x0 * slant * (x_p - x0) // - external_ponding // = 0 // <=> // x_p * z0 + slant * x_p^2 - slant * x0 * x_p // - integral // - x_p * z0 // - 0.5 * x_p * slant * x_p // + 0.5 * x_p * slant * x0 // + x0 * z0 // + 0.5 * x0 * slant * x_p // - 0.5 * x0 * slant * x0 // - external_ponding // = 0 // <=> // (slant - 0.5 * slant) * x_p^2 // (z0 - slant * x0 - z0 + 0.5 * slant * x0 // + 0.5 * slant * x0) * x_p // - integral + x0 * z0 - 0.5 * x0 * slant * x0 // - external_ponding // = 0 // <=> // 0.5 * slant * x_p^2 // = integral - x0 * z0 + 0.5 * x0 * slant * x0 // + external_ponding // <=> // x_p^2 = 2 * (integral - x0 * z0 + external_ponding) // / slant + x0 * x0 // <=> // x_p = sqrt (2.0 * (integral - x0 * z0 // + external_ponding) / slant // + x0 * x0) x_pond = sqrt (2.0 * (integral - x0 * z0 + external_ponding) / slant + x0 * x0); z_pond = z (x_pond); // Check that we got the right result. daisy_assert (x_pond > 0.0); daisy_assert (x_pond < 1.0); const double total = x_pond * z_pond; const double this_step = 0.5 * (x_pond - x0) * (z0 + z_pond); daisy_assert (approximate (total - (integral + this_step), external_ponding)); break; } // Prepare next step integral += this_step; x0 = x1; z0 = z1; } } internal_ponding = z_pond - lowest; if (external_ponding < 0.0) // Exfiltration { R_bottom = -42.42e42; I_bottom = external_ponding; // R_wall meaningless. I_wall = 0.0; R_wall = -42.42e42; } else { // Find maximal infiltration. const double Theta_sat = soil.Theta (0, 0.0, 0.0); const double available_space = (Theta_sat - Theta) * dz; daisy_assert (available_space > 0.0); const double I_max = std::min (external_ponding, available_space - 1.0e-8) / dt; // Find resistance and infiltration for bottom regime. const double x_bottom = std::min (x_pond, x_switch); const double bottom_width = x_bottom - 0.0; if (bottom_width > 0.0) { const double K_bottom = std::min (K, K_sat_below); const double dz_bottom = z.integrate (0.0, x_bottom) / bottom_width + dz; #if 0 cerr << "dz_bottom = " << dz_bottom << ", x_bottom = " << x_bottom << ", bottom_width = " << bottom_width << ", dz = " << dz << "\n"; #endif R_bottom = (x_width / bottom_width) * (dz_bottom / K_bottom + R_crust); I_bottom = std::min (internal_ponding / R_bottom, I_max); } else { R_bottom = -42.42e42; I_bottom = 0.0; } // Find resistance and infiltration for wall regime. if (z_pond > z_switch + 1e-5) { const double wall_width = x_pond - x_switch; daisy_assert (wall_width > 0.0); const double dz_wall = z.integrate (x_switch, x_pond) / wall_width + dz; #if 0 cerr << "dz_wall = " << dz_wall << ", wall_with = " << wall_width << "\n"; #endif R_wall = (x_width / wall_width) * (dz_wall / K); #if 0 cerr << "R_wall = " << R_wall << ", x_width = " <<x_width << ", dz_wall = " <<dz_wall << ", K = " << K << "\n"; #endif I_wall = std::min ((z_pond - z_switch) / R_wall, I_max - I_bottom); } else { // R_wall meaningless. I_wall = 0.0; R_wall = -42.42e42; } } // Total infiltration. I = I_bottom + I_wall; daisy_assert (I < external_ponding + 1.0e-8); #if 0 cerr << "switch = (" << x_switch << ", " << z_switch << "), pond = (" << x_pond << ", " << z_pond << ") I = " << I << " (bottom = " << I_bottom << ", wall = " << I_wall << "), internal ponding = " << internal_ponding << ",external ponding = " << external_ponding << "\n"; #endif // Update water. Theta = I * dt; for (int i = 0; i <= last_cell; i++) Theta += soil_water.Theta (i) * geo.dz (i); Theta /= dz; Theta_pre = Theta; daisy_assert (Theta < soil.Theta (0, 0.0, 0.0)); h = soil.h (0, Theta); }