double SoilWater::overflow (const Geometry& geo, const Soil& soil, const SoilHeat& soil_heat, Treelog& msg) { const size_t cell_size = geo.cell_size (); double extra = 0.0; // [cm^3] for (size_t c = 0; c < cell_size; c++) { const double h_sat = 0.0; const double Theta_sat = soil.Theta (c, h_sat, h_ice (c)); const double Theta_extra = std::max (Theta_[c] - Theta_sat, 0.0); Theta_[c] -= Theta_extra; tillage_[c] -= Theta_extra; extra += Theta_extra * geo.cell_volume (c); const double new_h = soil.h (c, Theta_[c]); if (h_[c] < 0.0 || new_h < 0) h_[c] = new_h; } tick_after (geo, soil, soil_heat, false, msg); extra /= geo.surface_area (); // [cm] extra *= 10.0; // [mm] return extra; }
double SoilWater::MaxExfiltration (const Geometry& geo, const size_t edge, const Soil& soil, const double T) const { const size_t n = geo.edge_other (edge, Geometry::cell_above); const double h0 = h (n); const double K0 = soil.K (n, h0, h_ice (n), T); if (max_exfiltration_gradient > 0.0) return K0 * max_exfiltration_gradient; const double Cw2 = soil.Cw2 (n, h0); const double Theta0 = Theta (n); const double Theta_surf = soil.Theta_res (n); const double delta_Theta = Theta0 - Theta_surf; const double z0 = geo.cell_z (n); // Darcy formulated for Theta between middle of node and soil surface. return - (K0 / Cw2) * (delta_Theta / z0); }
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 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 SoilWater::initialize (const FrameSubmodel& al, const Geometry& geo, const Soil& soil, const SoilHeat& soil_heat, const Groundwater& groundwater, Treelog& msg) { Treelog::Open nest (msg, "SoilWater"); const size_t cell_size = geo.cell_size (); const size_t edge_size = geo.edge_size (); // Ice must be first. if (al.check ("X_ice")) { X_ice_ = al.number_sequence ("X_ice"); if (X_ice_.size () == 0) X_ice_.push_back (0.0); while (X_ice_.size () < cell_size) X_ice_.push_back (X_ice_[X_ice_.size () - 1]); } else X_ice_.insert (X_ice_.begin (), cell_size, 0.0); if (al.check ("X_ice_buffer")) { X_ice_buffer_ = al.number_sequence ("X_ice_buffer"); if (X_ice_buffer_.size () == 0) X_ice_buffer_.push_back (0.0); while (X_ice_buffer_.size () < cell_size) X_ice_buffer_.push_back (X_ice_buffer_[X_ice_buffer_.size () - 1]); } else X_ice_buffer_.insert (X_ice_buffer_.begin (), cell_size, 0.0); for (size_t i = 0; i < cell_size; i++) { const double Theta_sat = soil.Theta (i, 0.0, 0.0); daisy_assert (Theta_sat >= X_ice_[i]); h_ice_.push_back (soil.h (i, Theta_sat - X_ice_[i])); } daisy_assert (h_ice_.size () == cell_size); geo.initialize_layer (Theta_, al, "Theta", msg); geo.initialize_layer (h_, al, "h", msg); for (size_t i = 0; i < Theta_.size () && i < h_.size (); i++) { const double Theta_h = soil.Theta (i, h_[i], h_ice (i)); if (!approximate (Theta_[i], Theta_h)) { std::ostringstream tmp; tmp << "Theta[" << i << "] (" << Theta_[i] << ") != Theta (" << h_[i] << ") (" << Theta_h << ")"; msg.error (tmp.str ()); } Theta_[i] = Theta_h; } if (Theta_.size () > 0) { while (Theta_.size () < cell_size) Theta_.push_back (Theta_[Theta_.size () - 1]); if (h_.size () == 0) for (size_t i = 0; i < cell_size; i++) h_.push_back (soil.h (i, Theta_[i])); } if (h_.size () > 0) { while (h_.size () < cell_size) h_.push_back (h_[h_.size () - 1]); if (Theta_.size () == 0) for (size_t i = 0; i < cell_size; i++) Theta_.push_back (soil.Theta (i, h_[i], h_ice (i))); } daisy_assert (h_.size () == Theta_.size ()); // Groundwater based pressure. if (h_.size () == 0) { if (groundwater.table () > 0.0) { const double h_pF2 = -100.0; // pF 2.0; for (size_t i = 0; i < cell_size; i++) { h_.push_back (h_pF2); Theta_.push_back (soil.Theta (i, h_pF2, h_ice_[i])); } } else { const double table = groundwater.table (); for (size_t i = 0; i < cell_size; i++) { h_.push_back (std::max (-100.0, table - geo.cell_z (i))); Theta_.push_back (soil.Theta (i, h_[i], h_ice_[i])); } } } daisy_assert (h_.size () == cell_size); // Sources. S_sum_.insert (S_sum_.begin (), cell_size, 0.0); S_root_.insert (S_root_.begin (), cell_size, 0.0); S_drain_.insert (S_drain_.begin (), cell_size, 0.0); S_indirect_drain_.insert (S_indirect_drain_.begin (), cell_size, 0.0); S_soil_drain_.insert (S_soil_drain_.begin (), cell_size, 0.0); S_incorp_.insert (S_incorp_.begin (), cell_size, 0.0); tillage_.insert (tillage_.begin (), cell_size, 0.0); S_B2M_.insert (S_B2M_.begin (), cell_size, 0.0); S_M2B_.insert (S_M2B_.begin (), cell_size, 0.0); S_p_drain_.insert (S_p_drain_.begin (), cell_size, 0.0); if (S_permanent_.size () < cell_size) S_permanent_.insert (S_permanent_.end (), cell_size - S_permanent_.size (), 0.0); S_ice_ice.insert (S_ice_ice.begin (), cell_size, 0.0); S_ice_water_.insert (S_ice_water_.begin (), cell_size, 0.0); S_forward_total_.insert (S_forward_total_.begin (), cell_size, 0.0); S_forward_sink_.insert (S_forward_sink_.begin (), cell_size, 0.0); // Fluxes. q_primary_.insert (q_primary_.begin (), edge_size, 0.0); q_secondary_.insert (q_secondary_.begin (), edge_size, 0.0); q_matrix_.insert (q_matrix_.begin (), edge_size, 0.0); q_tertiary_.insert (q_tertiary_.begin (), edge_size, 0.0); // Update conductivity and primary/secondary water. Theta_primary_.insert (Theta_primary_.begin (), cell_size, -42.42e42); Theta_secondary_.insert (Theta_secondary_.begin (), cell_size, -42.42e42); Theta_tertiary_.insert (Theta_tertiary_.begin (), cell_size, 0.0); K_cell_.insert (K_cell_.begin (), cell_size, 0.0); Cw2_.insert (Cw2_.begin (), cell_size, -42.42e42); tick_after (geo, soil, soil_heat, true, msg); // We just assume no changes. h_old_ = h_; Theta_old_ = Theta_; X_ice_old_ = X_ice_; }
void SoilWater::tick_after (const Geometry& geo, const Soil& soil, const SoilHeat& soil_heat, const bool initial, Treelog& msg) { TREELOG_SUBMODEL (msg, "SoilWater"); // We need old K for primary/secondary flux division. std::vector<double> K_old = K_cell_; // Update cells. const size_t cell_size = geo.cell_size (); daisy_assert (K_cell_.size () == cell_size); daisy_assert (Cw2_.size () == cell_size); daisy_assert (h_.size () == cell_size); daisy_assert (h_ice_.size () == cell_size); daisy_assert (K_old.size () == cell_size); daisy_assert (Theta_.size () == cell_size); daisy_assert (Theta_primary_.size () == cell_size); daisy_assert (Theta_secondary_.size () == cell_size); daisy_assert (Theta_tertiary_.size () == cell_size); double z_low = geo.top (); table_low = NAN; double z_high = geo.bottom (); table_high = NAN; for (size_t c = 0; c < cell_size; c++) { // Groundwater table. const double z = geo.cell_top (c); const double h = h_[c]; const double table = z + h; if (h < 0) { if (approximate (z, z_low)) { if (!std::isnormal (table_low) || table < table_low) table_low = table; } else if (z < z_low) table_low = table; } else if (approximate (z, z_high)) { if (!std::isnormal (table_high) || table > table_high) table_high = table; } else if (z > z_high) table_high = table; // Conductivity. K_cell_[c] = soil.K (c, h_[c], h_ice_[c], soil_heat.T (c)); // Specific water capacity. Cw2_[c] = soil.Cw2 (c, h_[c]); // Primary and secondary water. if (Theta_[c] <= 0.0) { std::ostringstream tmp; tmp << "Theta[" << c << "] = " << Theta_[c]; daisy_bug (tmp.str ()); Theta_[c] = 1e-9; } const double h_lim = soil.h_secondary (c); if (h_lim >= 0.0 || h_[c] <= h_lim) { // No active secondary domain. Theta_primary_[c] = Theta_[c]; Theta_secondary_[c] = 0.0; } else { // Secondary domain activated. const double Theta_lim = soil.Theta (c, h_lim, h_ice_[c]); daisy_assert (Theta_lim > 0.0); if (Theta_[c] >= Theta_lim) { Theta_primary_[c] = Theta_lim; Theta_secondary_[c] = Theta_[c] - Theta_lim; } else { std::ostringstream tmp; tmp << "h[" << c << "] = " << h_[c] << "; Theta[" << c << "] = " << Theta_[c] << "\nh_lim = " << h_lim << "; Theta_lim = " << Theta_lim << "\nStrenge h > h_lim, yet Theta <= Theta_lim"; msg.bug (tmp.str ()); Theta_primary_[c] = Theta_[c]; Theta_secondary_[c] = 0.0; } } } if (!std::isnormal (table_high)) { // No saturated cell, use lowest unsaturated. daisy_assert (std::isnormal (table_low)); table_high = table_low; } else if (!std::isnormal (table_low)) { // No unsaturated cell, use highest saturated. daisy_assert (std::isnormal (table_high)); table_low = table_high; } // Initialize if (initial) { K_old = K_cell_; Theta_primary_old_ = Theta_primary_; Theta_secondary_old_ = Theta_secondary_; } daisy_assert (K_old.size () == cell_size); daisy_assert (Theta_primary_old_.size () == cell_size); daisy_assert (Theta_secondary_old_.size () == cell_size); // Update edges. const size_t edge_size = geo.edge_size (); daisy_assert (q_matrix_.size () == edge_size); daisy_assert (q_primary_.size () == edge_size); daisy_assert (q_secondary_.size () == edge_size); daisy_assert (q_matrix_.size () == edge_size); daisy_assert (q_matrix_.size () == edge_size); for (size_t e = 0; e < edge_size; e++) { // By default, all flux is in primary domain. q_primary_[e] = q_matrix_[e]; q_secondary_[e] = 0.0; // Find average K. double K_edge = 0.0; double K_lim = 0.0; // K may be discontinious at h_lim in case of cracks. const double h_fudge = 0.01; // Contributions from target. const int to = geo.edge_to (e); if (geo.cell_is_internal (to)) { if (iszero (Theta_secondary_old (to)) || iszero (Theta_secondary (to))) continue; K_edge += 0.5 * (K_old[to] + K_cell (to)); const double h_lim = soil.h_secondary (to) - h_fudge; K_lim += soil.K (to, h_lim, h_ice (to), soil_heat.T (to)); } // Contributions from source. const int from = geo.edge_from (e); if (geo.cell_is_internal (from)) { if (iszero (Theta_secondary_old (from)) || iszero (Theta_secondary (from))) continue; K_edge += 0.5 * (K_old[from] + K_cell (from)); const double h_lim = soil.h_secondary (from) - h_fudge; K_lim += soil.K (from, h_lim, h_ice (from), soil_heat.T (from)); } daisy_assert (K_lim > 0.0); daisy_assert (K_edge > 0.0); // BUGLET: We use in effect arithmetic average here for K. daisy_assert (std::isnormal (K_edge)); // This may not have been what was used for calculating q matrix. const double K_factor = K_lim / K_edge; daisy_assert (std::isfinite (K_factor)); if (K_factor < 0.99999) { q_primary_[e] = q_matrix_[e] * K_factor; q_secondary_[e] = q_matrix_[e] - q_primary_[e]; } } }
double SoilWater::Theta_ice (const Soil& soil, const size_t i, const double h) const { return soil.Theta (i, h, h_ice (i)); }