void UZRectMollerup::lowerboundary (const GeometryRect& geo, const Groundwater& groundwater, const std::vector<bool>& active_lysimeter, const ublas::vector<double>& h, const ublas::vector<double>& Kedge, ublas::vector<double>& dq, ublas::banded_matrix<double>& Dm_mat, ublas::vector<double>& Dm_vec, ublas::vector<double>& Gm, ublas::vector<double>& B, Treelog& msg) { const std::vector<size_t>& edge_below = geo.cell_edges (Geometry::cell_below); const size_t edge_below_size = edge_below.size (); for (size_t i = 0; i < edge_below_size; i++) { const size_t edge = edge_below[i]; const int cell = geo.edge_other (edge, Geometry::cell_below); daisy_assert (geo.cell_is_internal (cell)); const double in_sign = geo.cell_is_internal (geo.edge_to (edge)) ? 1.0 : -1.0; daisy_assert (in_sign > 0); const double area = geo.edge_area (edge); const double sin_angle = geo.edge_sin_angle (edge); switch (groundwater.bottom_type ()) { case Groundwater::free_drainage: { const double sin_angle = geo.edge_sin_angle (edge); //const double flux = -in_sign * sin_angle * K (cell) * area; //old const double flux = -in_sign * sin_angle * Kedge (edge); Neumann (edge, cell, area, in_sign, flux, dq, B); } break; case Groundwater::forced_flux: { const double flux = groundwater.q_bottom (edge); Neumann (edge, cell, area, in_sign, flux, dq, B); } break; case Groundwater::pressure: { const double value = -Kedge (edge) * geo.edge_area_per_length (edge); const double pressure = groundwater.table () - geo.zplus (cell); Dirichlet (edge, cell, area, in_sign, sin_angle, Kedge (edge), h (cell), value, pressure, dq, Dm_mat, Dm_vec, Gm); } break; case Groundwater::lysimeter: { if (active_lysimeter[cell]) { //Neumann - not so good //const double flux = -in_sign * sin_angle * K (cell); //Neumann (edge, cell, area, in_sign, flux, dq, B); //Dirichlet - better const double value = -Kedge (edge) * geo.edge_area_per_length (edge); const double pressure = 0.0; Dirichlet (edge, cell, area, in_sign, sin_angle, Kedge (edge), h (cell), value, pressure, dq, Dm_mat, Dm_vec, Gm); } else // Indsat af [email protected] Fri Jul 10 11:21:14 2009 { const double flux = 0.0; Neumann (edge, cell, area, in_sign, flux, dq, B); } } break; default: daisy_panic ("Unknown groundwater type"); } } }
void UZlr::tick (Treelog& msg, const GeometryVert& geo, const Soil& soil, const SoilHeat& soil_heat, unsigned int first, const Surface& top, const size_t top_edge, unsigned int last, const Groundwater& bottom, const size_t bottom_edge, const std::vector<double>& S, const std::vector<double>& h_old, const std::vector<double>& Theta_old, const std::vector<double>& h_ice, std::vector<double>& h, std::vector<double>& Theta, const size_t q_offset, std::vector<double>& q_base, const double dt) { double *const q = &q_base[q_offset]; double q_up = 0.0; double q_down = 0.0; const Surface::top_t top_type = top.top_type (geo, top_edge); if (top_type == Surface::soil) { // We have a forced pressure top, in the form of a ridge system. // Since LR only works with flux top, we use Darcy to simulate a // flux top between the first cell (with a forced pressure) and // the second cell, and then continue calculating with a flux // top from the second cell. const double dz = geo.cell_z (first) - geo.cell_z (first+1); const double dh = (h_old[first] - h_old[first+1]); const double K = std::min (soil.K (first, h_old[first], h_ice[first], soil_heat.T (first)), soil.K (first, h_old[first+1], h_ice[first+1], soil_heat.T (first+1))); q_up = -K * (dh/dz + 1.0); // We can safely ignore S[first], since the ridge system has // already incorporated it. first++; // New upper limit. q[first] = q_up; } else { // Limit flux by soil capacity. const double K_sat = soil.K (first, 0.0, h_ice[first], soil_heat.T (first)); daisy_assert (K_sat > 0.0); if (top_type == Surface::forced_pressure) { const double dz = 0.0 - geo.cell_z (first); const double dh = top.h_top (geo, top_edge) - h_old[first]; q_up = q[first] = -K_sat * (dh/dz + 1.0); } else // Limited water or forced flux. q_up = q[first] = std::max (top.q_top (geo, top_edge, dt), -K_sat); } // Use darcy for upward movement in the top. const bool use_darcy = (h_old[first] < h_fc) && (q_up > 0.0); // Intermediate cells. for (int i = first; i <= last; i++) { const double z = geo.cell_z (i); const double dz = geo.dz (i); const double Theta_sat = soil.Theta (i, 0.0, h_ice[i]); const double Theta_res = soil.Theta_res (i); const double h_min = pF2h (10.0); const double Theta_min = soil.Theta (i, h_min, h_ice[i]); double Theta_new = Theta_old[i] - q[i] * dt / dz - S[i] * dt; if (Theta_new < Theta_min) { // Extreme dryness. q[i+1] = (Theta_min - Theta_new) * dz / dt; Theta[i] = Theta_min; h[i] = h_min; daisy_assert (std::isfinite (h[i])); continue; } daisy_assert (std::isfinite (h_old[i])); const double h_new = Theta_new >= Theta_sat ? std::max (h_old[i], 0.0) : soil.h (i, Theta_new); daisy_assert (std::isfinite (h_new)); double K_new = soil.K (i, h_new, h_ice[i], soil_heat.T (i)); // If we have free drainage bottom, we go for field capacity all // the way. Otherwise, we assume groundwater start at the // bottom of the last cell, and attempt equilibrium from there. // This asumption is correct for lysimeter bottom, adequate for // pressure bottom (where groundwater table is in the last // cell), and wrong for forced flux (= pipe drained soil) where // the groundwater is usually much higher. Still, it is better // than using h_fc. double h_lim; switch (bottom.bottom_type ()) { case Groundwater::free_drainage: h_lim = h_fc; break; case Groundwater::pressure: h_lim = std::max (bottom.table () - z, h_fc); break; case Groundwater::lysimeter: default: h_lim = std::max (geo.zplus (last) - z, h_fc); break; } if (use_darcy && z > z_top && i < last && h_fc < h_ice[i] ) // Dry earth, near top. Use darcy to move water up. { const double dist = z - geo.cell_z (i+1); q[i+1] = std::max (K_new * ((h_old[i+1] - h_new) / dist - 1.0), 0.0); const double Theta_next = Theta_new + q[i+1] * dt / dz; if (Theta_next > Theta_sat) { q[i+1] = (Theta_sat - Theta_new) * dz / dt; Theta[i] = Theta_sat; h[i] = std::max (0.0, h_new); daisy_assert (std::isfinite (h[i])); } else { Theta[i] = Theta_next; h[i] = soil.h (i, Theta[i]); daisy_assert (std::isfinite (h[i])); } } else if (h_new <= h_lim) // Dry earth, no water movement. { if (Theta_new <= Theta_sat) { q[i+1] = 0.0; Theta[i] = Theta_new; h[i] = h_new; daisy_assert (std::isfinite (h[i])); } else { q[i+1] = (Theta_sat - Theta_new) * dz / dt; Theta[i] = Theta_sat; h[i] = std::max (0.0, h_new); daisy_assert (std::isfinite (h[i])); } } else // Gravitational water movement. { if (i < last) { // Geometric average K. if (h_ice[i+1] < h_fc) // Blocked by ice. K_new = 0.0; else K_new = sqrt (K_new * soil.K (i+1, h_old[i+1], h_ice[i+1], soil_heat.T (i+1))); } else if (bottom.bottom_type () == Groundwater::forced_flux) K_new = -bottom.q_bottom (bottom_edge); // else keep K_new from the node. const double Theta_lim = soil.Theta (i, h_lim, h_ice[i]); const double Theta_next = Theta_new - K_new * dt / dz; if (Theta_next < Theta_lim) { if (Theta_lim < Theta_new) { q[i+1] = (Theta_lim - Theta_new) * dz / dt; Theta[i] = Theta_lim; h[i] = h_lim; daisy_assert (std::isfinite (h[i])); } else { q[i+1] = 0.0; Theta[i] = Theta_new; h[i] = h_new; daisy_assert (std::isfinite (h[i])); } } else if (Theta_next >= Theta_sat) { q[i+1] = (Theta_sat - Theta_new) * dz / dt; Theta[i] = Theta_sat; h[i] = std::max (h_old[i], 0.0); daisy_assert (std::isfinite (h[i])); } else { q[i+1] = -K_new; Theta[i] = Theta_next; h[i] = soil.h (i, Theta[i]); daisy_assert (std::isfinite (h[i])); } } daisy_assert (std::isfinite (h[i])); daisy_assert (std::isfinite (Theta[i])); daisy_assert (std::isfinite (q[i+1])); daisy_assert (Theta[i] <= Theta_sat); daisy_assert (Theta[i] > Theta_res); } // Lower border. q_down = q[last + 1]; if (bottom.bottom_type () == Groundwater::forced_flux) // Ensure forced bottom. { double extra_water = (bottom.q_bottom (bottom_edge) - q_down) * dt; for (int i = last; true; i--) { q[i+1] += extra_water / dt; if (i < static_cast<int> (first)) { if (extra_water > 0.0 && overflow_warn) { msg.warning ("Soil profile saturated, water flow to surface"); overflow_warn = false; } break; } const double dz = geo.dz (i); const double h_min = pF2h (10.0); const double Theta_min = soil.Theta (i, h_min, h_ice[i]); const double Theta_sat = soil.Theta (i, 0.0, h_ice[i]); Theta[i] += extra_water / dz; if (Theta[i] <= Theta_min) { extra_water = (Theta[i] - Theta_min) * dz; Theta[i] = Theta_min; h[i] = h_min; } else if (Theta[i] <= Theta_sat) { extra_water = 0; h[i] = soil.h (i, Theta[i]); break; } else { extra_water = (Theta[i] - Theta_sat) * dz; Theta[i] = Theta_sat; h[i] = 0.0; } } q_up = q[first]; q_down = q[last + 1]; } // Saturated pressure. double table = geo.cell_z (last) + h[last]; for (int i = last; i > first; i--) if (h[i] < 0.0) { table = geo.cell_z (i) + h[i]; break; } for (int i = last; i > first; i--) if (geo.cell_z (i) < table) { daisy_assert (h[i] >= 0.0); h[i] = table - geo.cell_z (i); daisy_assert (h[i] >= 0.0); } else break; // Check mass conservation. double total_old = 0.0; double total_new = 0.0; double total_S = 0.0; for (unsigned int i = first; i <= last; i++) { const double Theta_sat = soil.Theta (i, 0.0, 0.0); daisy_assert (Theta[i] <= Theta_sat + 1e-10); total_old += geo.dz (i) * Theta_old[i]; total_new += geo.dz (i) * Theta[i]; total_S += geo.dz (i) * S[i]; daisy_assert (std::isfinite (Theta[i])); if (Theta[i] <= 0.0) { std::ostringstream tmp; tmp << "Theta[" << i << "] = " << Theta[i]; daisy_bug (tmp.str ()); Theta[i] = 1e-9; } } daisy_balance (total_old, total_new, (-q_up + q_down - total_S) * dt); }
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 Movement1D::tick_water (const Geometry1D& geo, const Soil& soil, const SoilHeat& soil_heat, Surface& surface, Groundwater& groundwater, const std::vector<double>& S, std::vector<double>& h_old, const std::vector<double>& Theta_old, const std::vector<double>& h_ice, std::vector<double>& h, std::vector<double>& Theta, std::vector<double>& q, std::vector<double>& q_p, const double dt, Treelog& msg) { const size_t top_edge = 0U; const size_t bottom_edge = geo.edge_size () - 1U; // Limit for groundwater table. size_t last = soil.size () - 1; // Limit for ridging. const size_t first = (surface.top_type (geo, 0U) == Surface::soil) ? surface.last_cell (geo, 0U) : 0U; // Calculate matrix flow next. for (size_t m = 0; m < matrix_water.size (); m++) { water_attempt (m); Treelog::Open nest (msg, matrix_water[m]->name); try { matrix_water[m]->tick (msg, geo, soil, soil_heat, first, surface, top_edge, last, groundwater, bottom_edge, S, h_old, Theta_old, h_ice, h, Theta, 0U, q, dt); for (size_t i = last + 2; i <= soil.size (); i++) { q[i] = q[i-1]; q_p[i] = q_p[i-1]; } // Update surface and groundwater reservoirs. surface.accept_top (q[0] * dt, geo, 0U, dt, msg); surface.update_pond_average (geo); const double q_down = q[soil.size ()] + q_p[soil.size ()]; groundwater.accept_bottom (q_down * dt, geo, soil.size ()); if (m > 0) msg.debug ("Reserve model succeeded"); return; } catch (const char* error) { msg.debug (std::string ("UZ problem: ") + error); } catch (const std::string& error) { msg.debug (std::string ("UZ trouble: ") + error); } water_failure (m); } throw "Water matrix transport failed"; }