void Fetch::initialize (std::vector<Select*>& select, Treelog& msg) { Treelog::Open nest (msg, this->tag); bool found = false; for (size_t j = 0; j != select.size (); j++) { Treelog::Open nest (msg, select[j]->tag ()); if (this->tag == select[j]->tag ()) { if (found) msg.warning ("Duplicate tag ignored"); else { select[j]->add_dest (this); this->select_dimension = select[j]->dimension (); this->type = ((select[j]->handle != Select::Handle::current) && !select[j]->accumulate) ? Fetch::Flux : Fetch::NewContent; found = true; } } } if (!found) msg.warning ("No tag found"); }
void MovementSolute::element (const Soil& soil, const SoilWater& soil_water, DOE& element, const double diffusion_coefficient, double dt, Treelog& msg) { for (size_t i = 0; i < matrix_solute.size (); i++) { Treelog::Open nest (msg, "element", i, matrix_solute[i]->library_id ()); try { matrix_solute[i]->element (geometry (), soil, soil_water, element, diffusion_coefficient, dt, msg); if (i > 0) msg.message ("Succeeded"); return; } catch (const char* error) { msg.warning (std::string ("DOM problem: ") + error); } catch (const std::string& error) { msg.warning (std::string ("DOM trouble: ") + error); } } throw "Matrix element transport failed"; }
static void propagate (Treelog& msg, int nest, const std::string& text) { switch (nest) { case is_unknown: msg.entry (text); break; case is_debug: msg.debug (text); break; case is_plain: msg.message (text); break; case is_warning: msg.warning (text); break; case is_error: msg.error (text); break; case is_bug: msg.bug (text); break; case is_close: msg.close (); break; case is_touch: msg.touch (); break; case is_flush: msg.flush (); break; default: msg.open (text); } }
bool GnuplotProfile::plot (std::ostream& out, Treelog& msg) { if (xplus.size () < 1 || zplus.size () < 1) { msg.warning ("Nothing to plot"); return false; } // Header. plot_header (out); out << "\ set pm3d map\n\ set pm3d corners2color c4\n\ unset colorbox\n"; // Same size axes. out << "\ set size ratio -1\n"; // Legend. if (legend != "auto") out << "set key " << legend_table[legend] << "\n"; // Extra. for (size_t i = 0; i < extra.size (); i++) out << extra[i].name () << "\n"; // Plot. out << "splot '-' using 2:1:3 title \"\"\n"; // Data. daisy_assert (value.size () == xplus.size () * zplus.size ()); // Cell corners only. daisy_assert (value.size () > 0); out << "0 0 " << value[0] << "\n"; daisy_assert (value.size () >= zplus.size ()); for (size_t iz = 0; iz < zplus.size (); iz++) out << zplus[iz] << " 0 " << value[iz * xplus.size ()] << "\n"; for (size_t ix = 0; ix < xplus.size (); ix++) { out << "\n0 " << xplus[ix] << " " << value[ix] << "\n"; for (size_t iz = 0; iz < zplus.size (); iz++) { const size_t c = ix + iz * xplus.size (); daisy_assert (c < value.size ()); out << zplus[iz] << " " << xplus[ix] << " " << value[c] << "\n"; } } out << "e\n"; // The end. if (interactive ()) out << "pause mouse\n"; return true; }
void ABAProdUptake::production (const Units& units, const Geometry& geo, const SoilWater& soil_water, const std::vector<double>& S /* [cm^3/cm^3/h] */, const std::vector<double>& /* l [cm/cm^3] */, std::vector<double>& ABA /* [g/cm^3/h] */, Treelog& msg) const { // Check input. const size_t cell_size = geo.cell_size (); daisy_assert (ABA.size () == cell_size); daisy_assert (S.size () == cell_size); // For all cells. for (size_t c = 0; c < cell_size; c++) { const double h = soil_water.h (c); // Set up 'h' in scope. scope.set (h_name, soil_water.h (c)); // Find soil value. double value = 0.0; if (!expr->tick_value (units, value, ABA_unit, scope, msg)) msg.error ("No ABA production value found"); if (!std::isfinite (value) || value < 0.0) { if (h > -14000) // We are not concerned with ABA below wilting point. { std::ostringstream tmp; tmp << "ABA in cell " << c << " with h = " << h << " was " << value << " [" << ABA_unit << "], using 0 instead"; msg.warning (tmp.str ()); } value = 0.0; } daisy_assert (std::isfinite (S[c])); // Find ABA uptake. ABA[c] = value * S[c]; // [g/cm^3 S/h] = [g/cm^3 W] * [cm^3 W/cm^3 S/h] daisy_assert (std::isfinite (ABA[c])); } }
void Rootdens_G_P::set_density (const Geometry& geo, double SoilDepth, double CropDepth, const double /* CropWidth [cm] */, double WRoot, double /* DS */, std::vector<double>& Density, Treelog& msg) { const double Depth = std::min (SoilDepth, CropDepth); // Dimensional conversion. static const double m_per_cm = 0.01; const double MinLengthPrArea = (DensRtTip * 1.2) * CropDepth; const double LengthPrArea = std::max (m_per_cm * SpRtLength * WRoot, MinLengthPrArea); // [cm/cm^2] // We find a * depth first. const double ad = density_distribution_parameter (LengthPrArea / (CropDepth * DensRtTip)); // We find L0 from a d. // // L0 * exp (-a d) = DensRtTip // => L0 = DensRtTip / exp (-a d) L0 = DensRtTip * exp (ad); // 1 / exp (-x) = exp (x) a = ad / CropDepth; if (Depth < CropDepth) { double Lz = L0 * exp (-a * Depth); a = density_distribution_parameter (LengthPrArea / (Depth * Lz)) / Depth; } // Check minimum density double extra = 0.0; if (MinDens > 0.0 && WRoot > 0.0) { daisy_assert (L0 > 0.0); daisy_assert (a > 0.0); const double too_low = -log (MinDens / L0) / a; // [cm] if (too_low < Depth) { // We don't have MinDens all the way down. const double NewLengthPrArea = LengthPrArea - MinDens * Depth; // [cm/cm^2] #if 1 Treelog::Open nest (msg, "RootDens G+P"); std::ostringstream tmp; tmp << "too_low = " << too_low << ", NewLengthPrArea = " << NewLengthPrArea << "MinLengthPrArea = " << MinLengthPrArea; msg.warning (tmp.str ()); #endif if (too_low > 0.0 && NewLengthPrArea > too_low * DensRtTip * 1.2) { // There is enough to have MinDens all the way, spend // the rest using the standard model until the point // where the standard model would give too little.. a = density_distribution_parameter (NewLengthPrArea / (too_low * DensRtTip)); L0 = DensRtTip * exp (a); a /= too_low; extra = MinDens; } else { // There is too little, use uniform density all the way. L0 = 0.0; extra = LengthPrArea / Depth; } } } const size_t size = geo.cell_size (); daisy_assert (Density.size () == size); #if 1 for (size_t i = 0; i < size; i++) { const double f = geo.fraction_in_z_interval (i, 0.0, -Depth); const double d = -geo.cell_z (i); if (f > 0.01) Density[i] = f * (extra + L0 * exp (- a * d)); else Density[i] = 0.0; } #else // 0 unsigned int i = 0; for (; i == 0 || -geo.zplus (i-1) < Depth; i++) { daisy_assert (i < geo.size ()); Density[i] = extra + L0 * exp (a * geo.cell_z (i)); } for (; i < geo.size (); i++) Density[i] = 0.0; #endif // 0 }
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 MovementSolute::secondary_flow (const Geometry& geo, const std::vector<double>& Theta_old, const std::vector<double>& Theta_new, const std::vector<double>& q, const symbol name, const std::vector<double>& S, const std::map<size_t, double>& J_forced, const std::map<size_t, double>& C_border, std::vector<double>& M, std::vector<double>& J, const double dt, Treelog& msg) { const size_t cell_size = geo.cell_size (); const size_t edge_size = geo.edge_size (); // Full timstep left. daisy_assert (dt > 0.0); double time_left = dt; // Initial water content. std::vector<double> Theta (cell_size); for (size_t c = 0; c < cell_size; c++) Theta[c] = Theta_old[c]; // Small timesteps. for (;;) { // Are we done yet? const double min_timestep_factor = 1e-19; if (time_left < 0.1 * min_timestep_factor * dt) break; // Find new timestep. double ddt = time_left; // Limit timestep based on water flux. for (size_t e = 0; e < edge_size; e++) { const int cell = (q[e] > 0.0 ? geo.edge_from (e) : geo.edge_to (e)); if (geo.cell_is_internal (cell) && Theta[cell] > 1e-6 && M[cell] > 0.0) { const double loss_rate = std::fabs (q[e]) * geo.edge_area (e); const double content = Theta[cell] * geo.cell_volume (cell); const double time_to_empty = content / loss_rate; if (time_to_empty < min_timestep_factor * dt) { msg.warning ("Too fast water movement in secondary domain"); ddt = min_timestep_factor * dt; break; } // Go down in timestep while it takes less than two to empty cell. while (time_to_empty < 2.0 * ddt) ddt *= 0.5; } } // Cell source. Must be before transport to avoid negative values. for (size_t c = 0; c < cell_size; c++) M[c] += S[c] * ddt; // Find fluxes using new values (more stable). std::vector<double> dJ (edge_size, -42.42e42); for (size_t e = 0; e < edge_size; e++) { std::map<size_t, double>::const_iterator i = J_forced.find (e); if (i != J_forced.end ()) // Forced flux. { dJ[e] = (*i).second; daisy_assert (std::isfinite (dJ[e])); continue; } const int edge_from = geo.edge_from (e); const int edge_to = geo.edge_to (e); const bool in_flux = q[e] > 0.0; int flux_from = in_flux ? edge_from : edge_to; double C_flux_from = -42.42e42; if (geo.cell_is_internal (flux_from)) // Internal cell, use its concentration. { if (Theta[flux_from] > 1e-6 && M[flux_from] > 0.0) // Positive content in positive water. C_flux_from = M[flux_from] / Theta[flux_from]; else // You can't cut the hair of a bald guy. C_flux_from = 0.0; } else { i = C_border.find (e); if (i != C_border.end ()) // Specified by C_border. C_flux_from = (*i).second; else // Assume no gradient. { const int flux_to = in_flux ? edge_to : edge_from; daisy_assert (geo.cell_is_internal (flux_to)); if (Theta[flux_to] > 1e-6 && M[flux_to] > 0.0) // Positive content in positive water. C_flux_from = M[flux_to] / Theta[flux_to]; else // You can't cut the hair of a bald guy. C_flux_from = 0.0; } } // Convection. daisy_assert (std::isfinite (q[e])); daisy_assert (C_flux_from >= 0.0); dJ[e] = q[e] * C_flux_from; daisy_assert (std::isfinite (dJ[e])); } // Update values for fluxes. for (size_t e = 0; e < edge_size; e++) { const double value = ddt * dJ[e] * geo.edge_area (e); const int from = geo.edge_from (e); if (geo.cell_is_internal (from)) M[from] -= value / geo.cell_volume (from); const int to = geo.edge_to (e); if (geo.cell_is_internal (to)) M[to] += value / geo.cell_volume (to); J[e] += dJ[e] * ddt / dt; } // Update time left. time_left -= ddt; // Interpolate Theta. for (size_t c = 0; c < cell_size; c++) { const double left = time_left / dt; const double done = 1.0 - left; Theta[c] = left * Theta_old[c] + done * Theta_new[c]; } } }
void doIt (Daisy&, const Scope&, Treelog& out) { out.warning (message.name ()); }
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); }
double PhotoFarquhar::assimilate (const Units& units, const double ABA, const double psi_c, const double ec /* Canopy Vapour Pressure [Pa] */, const double gbw_ms /* Boundary layer [m/s] */, const double CO2_atm, const double O2_atm, const double Ptot /* [Pa] */, const double, const double Tc, const double Tl, const double cropN, const std::vector<double>& PAR, const std::vector<double>& PAR_height, const double PAR_LAI, const std::vector<double>& fraction, const double, CanopyStandard& canopy, Phenology& development, Treelog& msg) { const double h_x = std::fabs (psi_c) * 1.0e-4 /* [MPa/cm] */; // MPa // sugar production [gCH2O/m2/h] by canopy photosynthesis. const PLF& LAIvsH = canopy.LAIvsH; const double DS = development.DS; // One crop: daisy_assert (approximate (canopy.CAI, bioclimate.CAI ())); if (!approximate (LAIvsH (canopy.Height), canopy.CAI)) { std::ostringstream tmp; tmp << "Bug: CAI below top: " << LAIvsH (canopy.Height) << " Total CAI: " << canopy.CAI << "\n"; canopy.CanopyStructure (DS); tmp << "Adjusted: CAI below top: " << LAIvsH (canopy.Height) << " Total CAI: " << canopy.CAI; msg.error (tmp.str ()); } // CAI (total) below the current leaf layer. double prevLA = LAIvsH (PAR_height[0]); // Assimilate produced by canopy photosynthesis double Ass_ = 0.0; // Accumulated CAI, for testing purposes. double accCAI =0.0; // Number of computational intervals in the canopy. const int No = PAR.size () - 1; daisy_assert (No > 0); daisy_assert (No == PAR_height.size () - 1); // N-distribution and photosynthetical capacity std::vector<double> rubisco_Ndist (No, 0.0); std::vector<double> crop_Vm_total (No, 0.0); // Photosynthetic capacity (for logging) while (Vm_vector.size () < No) Vm_vector.push_back (0.0); // Potential electron transport rate (for logging) while (Jm_vector.size () < No) Jm_vector.push_back (0.0); // Photosynthetic N-leaf distribution (for logging) while (Nleaf_vector.size () < No) Nleaf_vector.push_back (0.0); // Brutto assimilate production (for logging) while (Ass_vector.size () < No) Ass_vector.push_back (0.0); // LAI (for logging) while (LAI_vector.size () < No) LAI_vector.push_back (0.0); rubiscoNdist->rubiscoN_distribution (units, PAR_height, prevLA, DS, rubisco_Ndist/*[mol/m²leaf]*/, cropN /*[g/m²area]*/, msg); crop_Vmax_total (rubisco_Ndist, crop_Vm_total); // Net photosynthesis (for logging) while (pn_vector.size () < No) pn_vector.push_back (0.0);// // Stomata CO2 pressure (for logging) while (ci_vector.size () < No) ci_vector.push_back (0.0);//[Pa] // Leaf surface CO2 pressure (for logging) while (cs_vector.size () < No) cs_vector.push_back (0.0);// // Leaf surface relative humidity (for logging) while (hs_vector.size () < No) hs_vector.push_back (0.0);//[] // Stomata conductance (for logging) while (gs_vector.size () < No) gs_vector.push_back (0.0);//[m/s] // Photosynthetic effect of Xylem ABA and crown water potential. Gamma = Arrhenius (Gamma25, Ea_Gamma, Tl); // [Pa] const double estar = FAO::SaturationVapourPressure (Tl); // [Pa] daisy_assert (gbw_ms >= 0.0); gbw = Resistance::ms2molly (Tl, Ptot, gbw_ms); daisy_assert (gbw >= 0.0); // CAI in each interval. const double dCAI = PAR_LAI / No; for (int i = 0; i < No; i++) { const double height = PAR_height[i+1]; daisy_assert (height < PAR_height[i]); // Leaf Area index for a given leaf layer const double LA = prevLA - LAIvsH (height); daisy_assert (LA >= 0.0); prevLA = LAIvsH (height); accCAI += LA; if (LA * fraction [i] > 0) { // PAR in mol/m2/s = PAR in W/m2 * 0.0000046 const double dPAR = (PAR[i] - PAR[i+1])/dCAI * 0.0000046; //W/m2->mol/m²leaf/s if (dPAR < 0) { std::stringstream tmp; tmp << "Negative dPAR (" << dPAR << " [mol/m^2 leaf/h])" << " PAR[" << i << "] = " << PAR[i] << " PAR[" << i+1 << "] = " << PAR[i+1] << " dCAI = " << dCAI << " LA = " << LA << " fraction[" << i << "] = " << fraction [i]; msg.debug (tmp.str ()); continue; } // log variable PAR_ += dPAR * dCAI * 3600.0; //mol/m²area/h/fraction // Photosynthetic rubisco capacity const double vmax25 = crop_Vm_total[i]*fraction[i];//[mol/m²leaf/s/fracti.] daisy_assert (vmax25 >= 0.0); // leaf respiration const double rd = respiration_rate(vmax25, Tl); daisy_assert (rd >= 0.0); //solving photosynthesis and stomatacondctance model for each layer double& pn = pn_vector[i]; double ci = 0.5 * CO2_atm;//first guess for ci, [Pa] double& hs = hs_vector[i]; hs = 0.5; // first guess of hs [] double& cs = cs_vector[i]; //first gues for stomatal cond,[mol/s/m²leaf] double gsw = Stomatacon->minimum () * 2.0; // double gsw = 2 * b; // old value const int maxiter = 150; int iter = 0; double lastci; double lasths; double lastgs; do { lastci = ci; //Stomata CO2 pressure lasths = hs; lastgs = gs; //Calculating ci and "net"photosynthesis CxModel(CO2_atm, O2_atm, Ptot, pn, ci, dPAR /*[mol/m²leaf/s]*/, gsw, gbw, Tl, vmax25, rd, msg);//[mol/m²leaf/s/fraction] // Vapour pressure at leaf surface. [Pa] const double es = (gsw * estar + gbw * ec) / (gsw + gbw); // Vapour defecit at leaf surface. [Pa] const double Ds = bound (0.0, estar - es, estar); // Relative humidity at leaf surface. [] hs = es / estar; const double hs_use = bound (0.0, hs, 1.0); // Boundary layer resistance. [s*m2 leaf/mol] daisy_assert (gbw >0.0); const double rbw = 1./gbw; //[s*m2 leaf/mol] // leaf surface CO2 [Pa] // We really should use CO2_canopy instead of CO2_atm // below. Adding the resitence from canopy point to // atmostphere is not a good workaround, as it will // ignore sources such as the soil and stored CO2 from // night respiration. cs = CO2_atm - (1.4 * pn * Ptot * rbw); //[Pa] daisy_assert (cs > 0.0); //stomatal conductance gsw = Stomatacon->stomata_con (ABA /*g/cm^3*/, h_x /* MPa */, hs_use /*[]*/, pn /*[mol/m²leaf/s]*/, Ptot /*[Pa]*/, cs /*[Pa]*/, Gamma /*[Pa]*/, Ds/*[Pa]*/, msg); //[mol/m²leaf/s] iter++; if(iter > maxiter) { std::ostringstream tmp; tmp << "total iterations in assimilation model exceed " << maxiter; msg.warning (tmp.str ()); break; } } // while (std::fabs (lastci-ci)> 0.01); while (std::fabs (lastci-ci)> 0.01 || std::fabs (lasths-hs)> 0.01 || std::fabs (lastgs-gs)> 0.01); // Leaf brutto photosynthesis [gCO2/m2/h] /*const*/ double pn_ = (pn+rd) * molWeightCO2 * 3600.0;//mol CO2/m²leaf/s->g CO2/m²leaf/h const double rd_ = (rd) * molWeightCO2 * 3600.0; //mol CO2/m²/s->g CO2/m²/h const double Vm_ = V_m(vmax25, Tl); //[mol/m² leaf/s/fraction] const double Jm_ = J_m(vmax25, Tl); //[mol/m² leaf/s/fraction] if (pn_ < 0.0) { std::stringstream tmp; tmp << "Negative brutto photosynthesis (" << pn_ << " [g CO2/m²leaf/h])" << " pn " << pn << " rd " << rd << " CO2_atm " << CO2_atm << " O2_atm " << O2_atm << " Ptot " << Ptot << " pn " << pn << " ci " << ci << " dPAR " << dPAR << " gsw " << gsw << " gbw " << gbw << " Tl " << Tl << " vmax25 " << vmax25 << " rd " << rd; msg.error (tmp.str ()); pn_ = 0.0; } Ass_ += LA * pn_; // [g/m²area/h] Res += LA * rd_; // [g/m²area/h] daisy_assert (Ass_ >= 0.0); //log variables: Ass_vector[i]+= pn_* (molWeightCH2O / molWeightCO2) * LA;//[g CH2O/m²area/h] Nleaf_vector[i]+= rubisco_Ndist[i] * LA * fraction[i]; //[mol N/m²area]OK gs_vector[i]+= gsw /* * LA * fraction[i] */; //[mol/m² area/s] ci_vector[i]+= ci /* * fraction[i] */; //[Pa] OK Vm_vector[i]+= Vm_ * 1000.0 * LA * fraction[i]; //[mmol/m² area/s]OK Jm_vector[i]+= Jm_ * 1000.0 * LA * fraction[i]; //[mmol/m² area/s]OK LAI_vector[i] += LA * fraction[i];//OK ci_middel += ci * fraction[i]/(No + 0.0);// [Pa] OK gs += LA * gsw * fraction[i]; Ass += LA * pn_ * (molWeightCH2O / molWeightCO2);//[g CH2O/m2 area/h] OK LAI += LA * fraction[i];//OK Vmax += 1000.0 * LA * fraction[i] * Vm_; //[mmol/m² area/s] jm += 1000.0 * LA * fraction[i] * Jm_; //[mmol/m² area/s] leafPhotN += rubisco_Ndist[i] * LA *fraction[i]; //[mol N/m²area]; fraction_total += fraction[i]/(No + 0.0); } } daisy_assert (approximate (accCAI, canopy.CAI)); daisy_assert (Ass_ >= 0.0); // Omregning af gs(mol/(m2s)) til gs_ms (m/s) foretages ved // gs_ms = gs * (R * T)/P: gs_ms = Resistance::molly2ms (Tl, Ptot, gs); return (molWeightCH2O / molWeightCO2)* Ass_; // Assimilate [g CH2O/m2/h] }