static void propagate_debug (Treelog& msg, int nest, const std::string& text) { switch (nest) { case is_unknown: case is_debug: case is_plain: case is_warning: case is_error: case is_bug: msg.debug (text); break; case is_close: msg.close (); break; case is_touch: msg.touch (); break; case is_flush: msg.flush (); break; default: msg.open (text); } }
void Horizon::Implementation::initialize (Hydraulic& hydraulic, const Texture& texture, const double quarts, int som_size, const bool top_soil, const double center_z, Treelog& msg) { if (CEC < 0.0) { CEC = default_CEC (texture); std::ostringstream tmp; tmp << "(CEC " << CEC << " [cmolc/kg]) ; Estimated from clay and humus."; msg.debug (tmp.str ()); } hydraulic.initialize (texture, dry_bulk_density, top_soil, CEC, center_z, msg); if (som_size > 0) { // Fill out SOM_fractions and SOM_C_per_N. if (SOM_C_per_N.size () > 0 && SOM_C_per_N.size () < som_size + 0U) SOM_C_per_N.insert (SOM_C_per_N.end (), som_size - SOM_C_per_N.size (), SOM_C_per_N.back ()); if (SOM_fractions.size () > 0 && SOM_fractions.size () < som_size + 0U) SOM_fractions.insert (SOM_fractions.end (), som_size - SOM_fractions.size (), 0.0); } // Did we specify 'dry_bulk_density'? Else calculate it now. if (dry_bulk_density < 0.0) { dry_bulk_density = texture.rho_soil_particles () * (1.0 - hydraulic.porosity ()); std::ostringstream tmp; tmp << "(dry_bulk_density " << dry_bulk_density << " [g/cm^3]) ; Estimated from porosity and texture."; msg.debug (tmp.str ()); } hor_heat.initialize (hydraulic, texture, quarts, msg); }
void initialize (const Texture& texture, double rho_b, const bool top_soil, const double CEC, const double center_z, Treelog& msg) { TREELOG_MODEL (msg); std::ostringstream tmp; // Find Theta_sat. if (Theta_sat < 0.0) { if (rho_b < 0.0) { msg.error ("You must specify either dry bulk density or porosity"); rho_b = 1.5; tmp << "Forcing rho_b = " << rho_b << " g/cm^3\n"; } Theta_sat = 1.0 - rho_b / texture.rho_soil_particles (); tmp << "(Theta_sat " << Theta_sat << " [])\n"; daisy_assert (Theta_sat < 1.0); } if (Theta_sat <= Theta_fc) { msg.error ("Field capacity must be below saturation point"); Theta_sat = (1.0 + 4.0 * Theta_fc) / 5.0; tmp << "Forcing Theta_sat = " << Theta_sat << " []\n"; } // Find Theta_wp. if (Theta_wp < 0.0) { const double clay_lim // USDA Clay = texture.fraction_of_minerals_smaller_than ( 2.0 /* [um] */); const double silt_lim // USDA Silt = texture.fraction_of_minerals_smaller_than (50.0 /* [um] */); daisy_assert (clay_lim >= 0.0); daisy_assert (silt_lim >= clay_lim); daisy_assert (silt_lim <= 1.0); const double mineral = texture.mineral (); const double clay = mineral * clay_lim * 100 /* [%] */; const double silt = mineral * (silt_lim - clay_lim) * 100 /* [%] */; const double humus = texture.humus * 100 /* [%] */; // Madsen and Platou (1983). Theta_wp = 0.758 * humus + 0.520 * clay + 0.075 * silt + 0.42; Theta_wp /= 100.0; // [%] -> [] } b = find_b (Theta_wp, Theta_fc); h_b = find_h_b (Theta_wp, Theta_fc, Theta_sat, b); tmp << "(b " << b << " [])\n" << "(h_b " << h_b << " [cm])"; msg.debug (tmp.str ()); // Must be called last (K_init depends on the other parameters). Hydraulic::initialize (texture, rho_b, top_soil, CEC, center_z, msg); }
void SoilWater::drain (const std::vector<double>& v, Treelog& msg) { forward_sink (v, v); daisy_assert (S_sum_.size () == v.size ()); daisy_assert (S_drain_.size () == v.size ()); daisy_assert (S_soil_drain_.size () == v.size ()); for (unsigned i = 0; i < v.size (); i++) { if (v[i] < -1e-8) { std::ostringstream tmp; tmp << "draining " << v[i] << " [h^-1] from cell " << i; msg.debug (tmp.str ()); } S_sum_[i] += v[i]; S_drain_[i] += v[i]; S_soil_drain_[i] += v[i]; } }
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 Horizon::initialize_base (const bool top_soil, const int som_size, const double center_z, const Texture& texture, Treelog& msg) { TREELOG_MODEL (msg); const double clay_lim = texture_below ( 2.0 /* [um] USDA Clay */); fast_clay = texture.mineral () * clay_lim; fast_humus = texture.humus; impl->initialize (*hydraulic, texture, quartz () * texture.mineral (), som_size, top_soil, center_z, msg); impl->secondary->initialize (msg); std::ostringstream tmp; const double h_lim = secondary_domain ().h_lim (); if (h_lim >= 0.0) impl->primary_sorption_fraction = 1.0; else { const double h_sat = 0.0; const double Theta_sat = hydraulic->Theta (h_sat); // Integrate h over Theta. PLF h_int; const double h_min = Hydraulic::r2h (impl->r_pore_min); const double Theta_min =hydraulic->Theta (h_min); h_int.add (Theta_min, 0.0); static const int intervals = 100; double delta = (Theta_sat - Theta_min) / (intervals + 0.0); double sum = 0.0; for (double Theta = Theta_min + delta; Theta < Theta_sat; Theta += delta) { double my_h = hydraulic->h (Theta); sum += my_h * delta; h_int.add (Theta, sum); } const double h_wp = -15000.0; const double Theta_wp = hydraulic->Theta (h_wp); const double Theta_lim = hydraulic->Theta (h_lim); tmp << "A saturated secondary domain contain " << 100.0 * (Theta_sat - Theta_lim) / (Theta_sat - Theta_wp) << " % of plant available water\n"; impl->primary_sorption_fraction = h_int (Theta_lim) / h_int (Theta_sat); tmp << "Primary domain contains " << 100.0 * impl->primary_sorption_fraction << " % of the available sorption sites\n"; } tmp << "h\th\tTheta\tK\n" << "cm\tpF\t\tcm/h\n"; const double h_Sat = 0; tmp << h_Sat << "\t" << "\t" << hydraulic->Theta (h_Sat) << "\t" << hydraulic->K (h_Sat) << "\n"; const double pF_Zero = 0; const double h_Zero = pF2h (pF_Zero); tmp << h_Zero << "\t" << pF_Zero << "\t" << hydraulic->Theta (h_Zero) << "\t" << hydraulic->K (h_Zero) << "\n"; const double pF_One = 1; const double h_One = pF2h (pF_One); tmp << h_One << "\t" << pF_One << "\t" << hydraulic->Theta (h_One) << "\t" << hydraulic->K (h_One) << "\n"; const double pF_FC = 2.0; const double h_FC = pF2h (pF_FC); tmp << h_FC << "\t" << pF_FC << "\t" << hydraulic->Theta (h_FC) << "\t" << hydraulic->K (h_FC) << "\n"; const double pF_Three = 3; const double h_Three = pF2h (pF_Three); tmp << h_Three << "\t" << pF_Three << "\t" << hydraulic->Theta (h_Three) << "\t" << hydraulic->K (h_Three) << "\n"; const double pF_WP = 4.2; const double h_WP = pF2h (pF_WP); tmp << h_WP << "\t" << pF_WP << "\t" << hydraulic->Theta (h_WP) << "\t" << hydraulic->K (h_WP); msg.debug (tmp.str ()); }
void MovementSolute::solute (const Soil& soil, const SoilWater& soil_water, const double J_above, Chemical& chemical, const double dt, const Scope& scope, Treelog& msg) { daisy_assert (std::isfinite (J_above)); const size_t cell_size = geometry ().cell_size (); const size_t edge_size = geometry ().edge_size (); // Source term transfered from secondary to primary domain. std::vector<double> S_extra (cell_size, 0.0); // Divide top solute flux according to water. std::map<size_t, double> J_tertiary; std::map<size_t, double> J_secondary; std::map<size_t, double> J_primary; if (J_above > 0.0) // Outgoing, divide according to content in primary domain only. divide_top_outgoing (geometry (), chemical, J_above, J_primary, J_secondary, J_tertiary); else if (J_above < 0.0) // Incomming, divide according to all incomming water. divide_top_incomming (geometry (), soil_water, J_above, J_primary, J_secondary, J_tertiary); else // No flux. zero_top (geometry (), J_primary, J_secondary, J_tertiary); // Check result. { const std::vector<size_t>& edge_above = geometry ().cell_edges (Geometry::cell_above); const size_t edge_above_size = edge_above.size (); double J_sum = 0.0; for (size_t i = 0; i < edge_above_size; i++) { const size_t edge = edge_above[i]; const double in_sign = geometry ().cell_is_internal (geometry ().edge_to (edge)) ? 1.0 : -1.0; const double area = geometry ().edge_area (edge); // [cm^2 S] const double J_edge // [g/cm^2 S/h] = J_tertiary[edge] + J_secondary[edge] + J_primary[edge]; J_sum += in_sign * J_edge * area; // [g/h] if (in_sign * J_tertiary[edge] < 0.0) { std::ostringstream tmp; tmp << "J_tertiary[" << edge << "] = " << J_tertiary[edge] << ", in_sign = " << in_sign << ", J_above = " << J_above; msg.bug (tmp.str ()); } if (in_sign * J_secondary[edge] < 0.0) { std::ostringstream tmp; tmp << "J_secondary[" << edge << "] = " << J_secondary[edge] << ", in_sign = " << in_sign << ", J_above = " << J_above; msg.bug (tmp.str ()); } } J_sum /= geometry ().surface_area (); // [g/cm^2 S/h] daisy_approximate (-J_above, J_sum); } // We set a fixed concentration below lower boundary, if specified. std::map<size_t, double> C_border; const double C_below = chemical.C_below (); if (C_below >= 0.0) { const std::vector<size_t>& edge_below = geometry ().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]; C_border[edge] = C_below; } } // Tertiary transport. tertiary->solute (geometry (), soil_water, J_tertiary, dt, chemical, msg); // Fully adsorbed. if (chemical.adsorption ().full ()) { static const symbol solid_name ("immobile transport"); Treelog::Open nest (msg, solid_name); if (!iszero (J_above)) { std::ostringstream tmp; tmp << "J_above = " << J_above << ", expected 0 for full sorbtion"; msg.error (tmp.str ()); } // Secondary "transport". std::vector<double> J2 (edge_size, 0.0); // Flux delivered by flow. std::vector<double> Mn (cell_size); // New content. for (size_t c = 0; c < cell_size; c++) { Mn[c] = chemical.M_secondary (c) + chemical.S_secondary (c) * dt; if (Mn[c] < 0.0) { S_extra[c] = Mn[c] / dt; Mn[c] = 0.0; } else S_extra[c] = 0.0; } chemical.set_secondary (soil, soil_water, Mn, J2); // Primary "transport". primary_transport (geometry (), soil, soil_water, *matrix_solid, sink_sorbed, 0, J_primary, C_border, chemical, S_extra, dt, scope, msg); return; } // Secondary transport activated. secondary_transport (geometry (), soil, soil_water, J_secondary, C_border, chemical, S_extra, dt, scope, msg); // Solute primary transport. for (size_t transport_iteration = 0; transport_iteration < 2; transport_iteration++) for (size_t i = 0; i < matrix_solute.size (); i++) { solute_attempt (i); static const symbol solute_name ("solute"); Treelog::Open nest (msg, solute_name, i, matrix_solute[i]->objid); try { primary_transport (geometry (), soil, soil_water, *matrix_solute[i], sink_sorbed, transport_iteration, J_primary, C_border, chemical, S_extra, dt, scope, msg); if (i > 0) msg.debug ("Succeeded"); return; } catch (const char* error) { msg.debug (std::string ("Solute problem: ") + error); } catch (const std::string& error) { msg.debug(std::string ("Solute trouble: ") + error); } solute_failure (i); } throw "Matrix solute transport failed"; }
void MovementSolute::primary_transport (const Geometry& geo, const Soil& soil, const SoilWater& soil_water, const Transport& transport, const bool sink_sorbed, const size_t transport_iteration, const std::map<size_t, double>& J_forced, const std::map<size_t, double>& C_border, Chemical& solute, const std::vector<double>& S_extra, const double dt, const Scope& scope, Treelog& msg) { // Edges. const size_t edge_size = geo.edge_size (); std::vector<double> q (edge_size); // Water flux [cm]. std::vector<double> J (edge_size); // Flux delivered by flow. for (size_t e = 0; e < edge_size; e++) { q[e] = soil_water.q_primary (e); daisy_assert (std::isfinite (q[e])); J[e] = 0.0; } // Cells. const size_t cell_size = geo.cell_size (); std::vector<double> Theta_old (cell_size); // Water content at start... std::vector<double> Theta_new (cell_size); // ...and end of timestep. std::vector<double> C (cell_size); // Concentration given to flow. std::vector<double> A (cell_size); // Sorbed mass not given to flow. std::vector<double> S (cell_size); // Source given to flow. for (size_t c = 0; c < cell_size; c++) { Theta_old[c] = soil_water.Theta_primary_old (c); daisy_assert (Theta_old[c] > 0.0); Theta_new[c] = soil_water.Theta_primary (c); daisy_assert (Theta_new[c] > 0.0); C[c] = solute.C_primary (c); daisy_assert (C[c] >= 0.0); const double M = solute.M_primary (c); daisy_assert (M >= 0.0); A[c] = M - C[c] * Theta_old[c]; daisy_assert (std::isfinite (A[c])); if (A[c] < 0.0) { daisy_approximate (M, C[c] * Theta_old[c]); A[c] = 0.0; } daisy_assert (A[c] >= 0.0); S[c] = solute.S_primary (c) + S_extra[c]; if (sink_sorbed && S[c] < 0.0) { A[c] += S[c] * dt; S[c] = 0.0; if (A[c] < 0.0) { S[c] = A[c] / dt; A[c] = 0.0; } } daisy_assert (std::isfinite (S[c])); } // Flow. transport.flow (geo, soil, Theta_old, Theta_new, q, solute.objid, S, J_forced, C_border, C, J, solute.diffusion_coefficient (), dt, msg); // Check fluxes. for (size_t e = 0; e < edge_size; e++) daisy_assert (std::isfinite (J[e])); // Update with new content. std::vector<double> M (cell_size); for (size_t c = 0; c < cell_size; c++) { daisy_assert (std::isfinite (C[c])); M[c] = A[c] + C[c] * Theta_new[c]; if (M[c] < 0.0) { std::ostringstream tmp; tmp << "M[" << c << "] = " << M[c] << " @ " << geo.cell_name (c) << ", C = " << C[c] << ", A = " << A[c] << ", M_new = " << M[c] << ", M_old = " << solute.M_primary (c) << ", dt " << dt << ", S = " << S[c] << ", S_extra = " << S_extra[c]; solute.debug_cell (tmp, c); tmp << ", Theta_old " << Theta_old[c] << ", Theta_new " << Theta_new[c] << ", root " << soil_water.S_root (c) << ", drain " << soil_water.S_drain (c) << ", B2M " << soil_water.S_B2M (c) << ", M2B " << soil_water.S_M2B (c) << ", forward_total " << soil_water.S_forward_total (c) << ", forward_sink " << soil_water.S_forward_sink (c) << ", sum " << soil_water.S_sum (c) << ", v1 " << soil_water.velocity_cell_primary (geo, c) << ", v2 " << soil_water.velocity_cell_secondary (geo, c); const std::vector<size_t>& edges = geo.cell_edges (c); for (size_t i = 0; i < edges.size (); i++) { const size_t e = edges[i]; tmp << "\n" << geo.edge_name (e) << ": q = " << q[e] << ", J = " << J[e]; } msg.debug (tmp.str ()); if (transport_iteration == 0) throw "Negative concentration"; } } solute.set_primary (soil, soil_water, M, J); }
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] }
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"; }