bool unique_validate (const std::vector<T>& list, Treelog& msg) { bool ok = true; std::map<T, size_t> found; for (size_t i = 0; i < list.size (); i++) { T v = list[i]; typename std::map<T, size_t>::const_iterator f = found.find (v); if (f != found.end ()) { std::ostringstream tmp; tmp << "Entry " << (*f).second << " and " << i << " are both '" << v << "'"; msg.error (tmp.str ()); ok = false; } found[v] = i; } return ok; }
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 ChemistryMulti::deposit (const symbol chem, const double flux, Treelog& msg) { bool found = false; for (size_t c = 0; c < combine.size (); c++) if (combine[c]->know (chem)) { if (found) msg.error ("Duplicate chemical '" + chem + "' detected"); Chemical& chemical = combine[c]->find (chem); chemical.deposit (flux); found = true; } if (found) return; check_ignore (chem, msg); }
bool Frame::check (const Metalib& metalib, const Frame& frame, const symbol key, Treelog& msg) const { if (lookup (key) == Attribute::Error) { msg.error ("'" + key + "': not defined"); return false; } const Type& type = impl->find_type (frame, key); bool ok = true; if (!impl->check (metalib, frame, type, key, msg)) ok = false; else if (parent () && parent ()->lookup (key) != Attribute::Error && !parent ()->check (metalib, frame, key, msg)) ok = false; return ok; }
void ChemistryMulti::incorporate (const Geometry& geo, const symbol chem, const double amount, const Volume& volume, Treelog& msg) { bool found = false; for (size_t c = 0; c < combine.size (); c++) if (combine[c]->know (chem)) { if (found) msg.error ("Duplicate chemical '" + chem + "' detected"); Chemical& chemical = combine[c]->find (chem); chemical.incorporate (geo, amount, volume); found = true; } if (found) return; check_ignore (chem, msg); }
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]; } } }
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"; }
void doIt (Daisy&, const Scope&, Treelog& out) { out.warning (message.name ()); }
void doIt (Daisy& daisy, const Scope&, Treelog& out) { out.message ("Ridging"); daisy.field ().ridge (ridge); }
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 SummaryBalance::summarize (Treelog& msg) const { TREELOG_MODEL (msg); // We write the summary to a string at first. std::ostringstream tmp; tmp.precision (precision); tmp.flags (std::ios::right | std::ios::fixed); if (description.name () != default_description) tmp << description << "\n\n"; // Find width of tags. const std::string total_title = "Balance (= In - Out - Increase)"; const std::string content_title = "Total increase in content"; size_t max_size = std::max (total_title.size (), content_title.size ()); for (unsigned int i = 0; i < fetch.size (); i++) max_size = std::max (max_size, fetch[i]->name_size ()); // Find width and total values int max_digits = 0; const double total_input = find_total (input, max_digits); const double total_output = find_total (output, max_digits); const double total_content = find_total (content, max_digits); const double total = total_input - total_output - total_content; max_digits = std::max (max_digits, FetchPretty::width (total_input)); max_digits = std::max (max_digits, FetchPretty::width (total_output)); max_digits = std::max (max_digits, FetchPretty::width (total_content)); max_digits = std::max (max_digits, FetchPretty::width (total)); // Find total width. const int width = max_digits + (precision > 0 ? 1 : 0) + precision; // Find width of dimensions. size_t dim_size = 0; for (unsigned int i = 0; i < fetch.size (); i++) dim_size = std::max (dim_size, fetch[i]->dimension ().name ().size ()); // Print all entries. symbol shared_dim = Attribute::User (); if (input.size () > 0) { const symbol dim = print_entries (tmp, input, max_size, width); print_balance (tmp, "Total input", total_input, dim, dim_size, max_size, width); shared_dim = dim; tmp << "\n"; } if (output.size () > 0) { const symbol dim = print_entries (tmp, output, max_size, width); print_balance (tmp, "Total output", total_output, dim, dim_size, max_size, width); if (shared_dim == Attribute::User ()) shared_dim = dim; else if (dim != shared_dim) shared_dim = Attribute::Unknown (); tmp << "\n"; } if (content.size () > 0) { const symbol dim = print_entries (tmp, content, max_size, width); print_balance (tmp, content_title, total_content, dim, dim_size, max_size, width); if (shared_dim == Attribute::User ()) shared_dim = dim; else if (dim != shared_dim) shared_dim = Attribute::Unknown (); tmp << "\n"; } print_balance (tmp, total_title, total, shared_dim, dim_size, max_size, width); tmp << std::string (max_size + 3, ' ') << std::string (width, '='); // Where? if (file == "") msg.message (tmp.str ()); else { std::ofstream out (file.name ().c_str ()); out << tmp.str (); if (! out.good ()) msg.error ("Could not write to '" + file + "'"); } }
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 doIt (Daisy& daisy, const Scope&, Treelog& msg) { msg.message ("Sowing " + crop->type_name ()); daisy.field ().sow (metalib, *crop, row_width, row_pos, seed, daisy.time (), msg); }
double solve_2D (std::vector<Iterative::PointValue>& obs, Treelog& msg) { // Find best fit. struct ToMinimize : Iterative::PointFunction { const std::vector<Iterative::PointValue>& obs; GP2Dfun& fun; const double fixed_SoilDepth; // [cm] const bool find_SoilDepth; const int debug; Treelog& msg; double value (const Iterative::Point& p) const { Treelog::Open nest (msg, "minimize"); if (find_SoilDepth) daisy_assert (p.size () == 4); else daisy_assert (p.size () == 3); const double CropDepth = p[0]; const double CropWidth = p[1]; const double WRoot = p[2]; const double SoilDepth = find_SoilDepth ? p[3] : fixed_SoilDepth; if (debug > 0) { std::ostringstream tmp; tmp << "CropDepth = " << CropDepth << " cm\n" << "CropWidth = " << CropWidth << " cm\n" << "WRoot = " << (0.01 * WRoot) << " Mg DM/ha\n"; if (find_SoilDepth) tmp << "SoilDepth = " << SoilDepth << " cm"; msg.message (tmp.str ()); } // Restrictions. const double LARGE_NUMBER = 42.42e42; if (CropDepth <= 0) return LARGE_NUMBER; if (CropWidth <= 0) return LARGE_NUMBER; if (WRoot <= 0) return LARGE_NUMBER; if (find_SoilDepth && SoilDepth <= 0) return LARGE_NUMBER; bool ok = fun.root.set_dynamic (SoilDepth, CropDepth, CropWidth, WRoot, debug, msg); if (!ok) return LARGE_NUMBER; const double Rsqr = Iterative::RSquared (obs, fun); if (debug > 0) { std::ostringstream tmp; tmp << "R^2 = " << Rsqr; msg.message (tmp.str ()); } return -Rsqr; } ToMinimize (const std::vector<Iterative::PointValue>& o, GP2Dfun& f, const double sd, const int d, Treelog& m) : obs (o), fun (f), fixed_SoilDepth (sd), find_SoilDepth (!std::isfinite (fixed_SoilDepth)), debug (d), msg (m) { } }; ToMinimize to_minimize (obs, gp2d, SoilDepth, debug, msg); // Initialial guess. const double default_CropDepth = 70; // [cm] const double default_CropWidth = 100; // [cm] const double default_WRoot = 50; // 150 [g DM/m^2] = 1.5 [Mg DM/ha] const double default_SoilDepth = 150; // [cm] Iterative::Point start; start.push_back (default_CropDepth); start.push_back (default_CropWidth); start.push_back (default_WRoot); if (!std::isfinite (SoilDepth)) start.push_back (default_SoilDepth); // [cm] daisy_assert (start.size () == 4 || start.size () == 3); const double epsilon = 0.01; const size_t min_iter = 10000; const size_t max_iter = 300000; Iterative::Point result; const bool solved = Iterative::NelderMead (min_iter, max_iter, epsilon, to_minimize, start, result, Treelog::null ()); const double Rsqr = -to_minimize.value (result); store ("2D R^2", Rsqr, solved ? "(solved)" : "(no solution)"); store ("2D CropDepth", result[0], "cm"); store ("2D CropWidth", result[1], "cm"); store ("2D WRoot", (0.01 * result[2]) , "Mg DM/ha");; if (result.size () == 4) store ("2D SoilDepth", result[3], "cm");; store ("2D L00", gp2d.root.L00, "cm/cm^3"); store ("2D a_x", gp2d.root.a_x, "cm^-1"); store ("2D a_z", gp2d.root.a_z, "cm^-1");; if (gp2d.root.d_a > 0.0) { store ("2D d_a", gp2d.root.d_a, "cm"); store ("2D k*", gp2d.root.kstar , "");; } // Show data. if (show_data) { std::ostringstream out; out << "X\tZ\tobs\tsim\n" << Units::cm () << "\t" << Units::cm () << "\t" << dens_dim_to << "\t" << dens_dim_to; for (size_t i = 0; i < obs.size (); i++) { const double x = obs[i].point[0]; const double z = obs[i].point[1]; const double o = obs[i].value; const double f = gp2d.root.density (x, z); out << "\n" << x << "\t" << z << "\t" << o << "\t" << f; } msg.message (out.str ()); } // Show errorbars if (show_match) { std::ostringstream out; typedef std::map<double, std::map<double, double>/**/> ddmap ; ddmap sum; ddmap count; ddmap var; ddmap avg; // Clear all. for (size_t i = 0; i < obs.size (); i++) { const double x = std::fabs (obs[i].point[0] - x_offset); const double z = obs[i].point[1]; sum[x][z] = 0.0; count[x][z] = 0.0; var[x][z] = 0.0; } // Count all. for (size_t i = 0; i < obs.size (); i++) { const double x = std::fabs (obs[i].point[0] - x_offset); const double z = obs[i].point[1]; const double o = obs[i].value; sum[x][z] += o; count[x][z] += 1.0; } // Find mean. for (ddmap::iterator i = sum.begin (); i != sum.end (); i++) { const double x = (*i).first; std::map<double, double> zmap = (*i).second; for (std::map<double, double>::iterator j = zmap.begin (); j != zmap.end (); j++) { const double z = (*j).first; avg[x][z] = sum[x][z] / count[x][z]; } } // Find variation for (size_t i = 0; i < obs.size (); i++) { const double x = std::fabs (obs[i].point[0] - x_offset); const double z = obs[i].point[1]; const double o = obs[i].value; var[x][z] += sqr (avg[x][z] - o); } for (ddmap::iterator i = var.begin (); i != var.end (); i++) { const double x = (*i).first; out << "set output \"" << objid << "-" << x << ".tex\"\n\ plot '-' using 2:1:3 notitle with xerrorbars, '-' using 2:1 notitle with lines\n"; std::map<double, double> zmap = (*i).second; for (std::map<double, double>::iterator j = zmap.begin (); j != zmap.end (); j++) { const double z = (*j).first; const double dev = sqrt (var[x][z] / count[x][z]); out << -z << "\t" << avg[x][z] << "\t" << dev << "\n"; } out << "e\n\n"; for (double z = 0.0; z < 100.1; z++) out << -z << "\t" << gp2d.root.density (x + x_offset, z) << "\n"; out << "e\n\n"; } msg.message (out.str ()); } // Find RSS. double RSS = 0.0; for (size_t i = 0; i < obs.size (); i++) { const double x = obs[i].point[0]; const double z = obs[i].point[1]; const double o = obs[i].value; const double f = gp2d.root.density (x, z); RSS += sqr (o - f); } return RSS; }
double solve_1D (std::vector<Iterative::PointValue>& obs, Treelog& msg) { // Find best fit. struct ToMinimize : Iterative::PointFunction { const std::vector<Iterative::PointValue>& obs; GP1Dfun& fun; const double fixed_SoilDepth; // [cm] const bool find_SoilDepth; const int debug; Treelog& msg; double value (const Iterative::Point& p) const { Treelog::Open nest (msg, "minimize"); if (find_SoilDepth) daisy_assert (p.size () == 3); else daisy_assert (p.size () == 2); const double CropDepth = p[0]; const double WRoot = p[1]; const double SoilDepth = find_SoilDepth ? p[2] : fixed_SoilDepth; if (debug > 0) { std::ostringstream tmp; tmp << "CropDepth = " << CropDepth << " cm\n" << "WRoot = " << (0.01 * WRoot) << " Mg DM/ha\n"; if (find_SoilDepth) tmp << "SoilDepth = " << SoilDepth << " cm"; msg.message (tmp.str ()); } // Restrictions. const double LARGE_NUMBER = 42.42e42; if (CropDepth <= 0) return LARGE_NUMBER; if (WRoot <= 0) return LARGE_NUMBER; if (find_SoilDepth && SoilDepth <= 0) return LARGE_NUMBER; bool ok = fun.root.set_dynamic (SoilDepth, CropDepth, WRoot, debug, msg); if (!ok) return LARGE_NUMBER; const double Rsqr = Iterative::RSquared (obs, fun); if (debug > 0) { std::ostringstream tmp; tmp << "R^2 = " << Rsqr; msg.message (tmp.str ()); } return -Rsqr; } ToMinimize (const std::vector<Iterative::PointValue>& o, GP1Dfun& f, const double sd, const int d, Treelog& m) : obs (o), fun (f), fixed_SoilDepth (sd), find_SoilDepth (!std::isfinite (fixed_SoilDepth)), debug (d), msg (m) { } }; ToMinimize to_minimize (obs, gp1d, SoilDepth, debug, msg); // Initialial guess. const double default_CropDepth = 70; // [cm] const double default_WRoot = 50; // 150 [g DM/m^2] = 1.5 [Mg DM/ha] const double default_SoilDepth = 150; // [cm] Iterative::Point start; start.push_back (default_CropDepth); start.push_back (default_WRoot); if (!std::isfinite (SoilDepth)) start.push_back (default_SoilDepth); // [cm] daisy_assert (start.size () == 3 || start.size () == 2); const double epsilon = 0.01; const size_t min_iter = 10000; const size_t max_iter = 300000; Iterative::Point result; const bool solved = Iterative::NelderMead (min_iter, max_iter, epsilon, to_minimize, start, result, Treelog::null ()); const double Rsqr = -to_minimize.value (result); store ("1D R^2", Rsqr, solved ? "(solved)" : "(no solution)"); store ("1D CropDepth", result[0], "cm"); store ("1D WRoot", (0.01 * result[1]) , "Mg DM/ha");; if (result.size () == 3) store ("1D SoilDepth", result[2], "cm");; store ("1D L0", gp1d.root.L0, "cm/cm^3"); store ("1D a", gp1d.root.a, "cm^-1");; if (gp1d.root.d_a > 0.0) { store ("1D d_a", gp1d.root.d_a, "cm"); store ("1D k*", gp1d.root.kstar, ""); } std::ostringstream out; if (show_data) out << "Z\tobs\tsim\n" << Units::cm () << "\t" << Units::cm () << "\t" << dens_dim_to << "\t" << dens_dim_to; double RSS = 0.0; for (size_t i = 0; i < obs.size (); i++) { const double z = obs[i].point[0]; const double o = obs[i].value; const double f = gp1d.root.density (z); RSS += sqr (o - f); if (show_data) out << "\n" << z << "\t" << o << "\t" << f; } if (show_data) msg.message (out.str ()); return RSS; }
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] }
bool Frame::Implementation::check (const Metalib& metalib, const Frame& frame, const Type& type, const symbol key, Treelog& msg) const { // Has value? if (!frame.check (key)) { if (type.is_mandatory ()) { msg.error (key + " is missing"); return false; } return true; } // Now, the real checks. bool ok = true; // Genenic check. vcheck_map::const_iterator i = val_checks.find (key); if (i != val_checks.end ()) { if (!(*i).second->verify (metalib, frame, key, msg)) ok = false;; } // Spcial handling of various types. switch (type.type ()) { case Attribute::Number: // This should already be checked by the file parser, but you // never know... Well, theoretically the alist could come from // another source (like one of the many other parsers :/), or // be one of the build in ones. if (type.size () != Attribute::Singleton) { const std::vector<double>& array = frame.number_sequence (key); for (size_t i = 0; i < array.size (); i++) { std::ostringstream tmp; tmp << key << "[" << i << "]"; Treelog::Open nest (msg, tmp.str ()); if (!type.verify (array[i], msg)) ok = false; } } else { Treelog::Open nest (msg, key); if (!type.verify (frame.number (key), msg)) ok = false; } break; case Attribute::Model: if (type.size () != Attribute::Singleton) { const ::Library& lib = metalib.library (type.component ()); const std::vector<boost::shared_ptr<const FrameModel>/**/>& seq = frame.model_sequence (key); int j_index = 0; for (std::vector<boost::shared_ptr<const FrameModel>/**/>::const_iterator j = seq.begin (); j != seq.end (); j++) { std::ostringstream tmp; tmp << key << "[" << j_index << "]: "; j_index++; const FrameModel& al = **j; if (!lib.check (al.type_name ())) { tmp << "Unknown library member '" << al.type_name () << "'"; msg.error (tmp.str ()); ok = false; } else { tmp << al.type_name (); Treelog::Open nest (msg, tmp.str ()); if (!al.check (metalib, msg)) ok = false; } } } else { const FrameModel& al = frame.model (key); Treelog::Open nest (msg, key + ": " + al.type_name ()); if (!al.check (metalib, msg)) ok = false; } break; case Attribute::Submodel: if (type.size () != Attribute::Singleton) { daisy_assert (frame.type_size (key) != Attribute::Singleton); const std::vector<boost::shared_ptr<const FrameSubmodel>/**/>& seq = frame.submodel_sequence (key); int j_index = 0; for (std::vector<boost::shared_ptr<const FrameSubmodel>/**/>::const_iterator j = seq.begin (); j != seq.end (); j++) { std::ostringstream tmp; tmp << key << " [" << j_index << "]"; Treelog::Open nest (msg, tmp.str ()); j_index++; const FrameSubmodel& al = **j; if (!al.check (metalib, msg)) ok = false; } } else { Treelog::Open nest (msg, key); const FrameSubmodel& al = frame.submodel (key); if (!al.check (metalib, msg)) ok = false; } break; default: /* Do nothing */; } return ok; }
// Simulation. void doIt (Daisy& daisy, const Scope&, Treelog& out) { out.message ("Adjusting surface detention capacity"); daisy.field ().set_surface_detention_capacity (height); }
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); }
void EquilibriumGoal_A::find (const Units& units, const Scope& scope, int cell, const double has_A, const double has_B, double& want_A, double& want_B, Treelog& msg) const { daisy_assert (has_A >= 0.0); daisy_assert (has_B >= 0.0); const double M = has_A + has_B; double goal_A = 1.0; if (!goal_A_expr->tick_value (units, goal_A, goal_unit, scope, msg)) msg.error ("Could not evaluate 'goal_A'"); daisy_assert (std::isfinite (goal_A)); if (goal_A < 0.0) { std::ostringstream tmp; tmp << "Goal A is " << goal_A << ", should be non-negative"; msg.error (tmp.str ()); goal_A = 0.0; } double min_B = 1.0; if (!min_B_expr->tick_value (units, min_B, goal_unit, scope, msg)) msg.error ("Could not evaluate 'min_B'"); daisy_assert (min_B >= 0.0); static const symbol Theta_name ("Theta"); const double Theta = scope.number (Theta_name); daisy_assert (Theta > 0.0); daisy_assert (Theta < 1.0); const double goal_A_dry = goal_A * (A_solute ? Theta : 1.0); const double min_B_dry = min_B * (B_solute ? Theta : 1.0); std::ostringstream tmp; if (has_A > goal_A_dry) { tmp << "A->B"; // We have too much A, convert surplus to B. want_A = goal_A_dry; want_B = M - want_A; } else if (has_B < min_B_dry) { tmp << "min_B"; // We have too little A and too little B, do nothing. want_A = has_A; want_B = has_B; } else { tmp << "B->A"; // We have too little A and too much B, convert just enough to fit one. want_B = std::max (min_B_dry, M - goal_A_dry); want_A = M - want_B; } tmp << "\t" << has_A << "\t" << goal_A_dry << "\t" << want_A << "\t" << has_B << "\t" << min_B_dry << "\t" << want_B << "\t" << M; if (cell == debug_cell) msg.message (tmp.str ()); daisy_assert (want_A >= 0.0); daisy_assert (want_B >= 0.0); daisy_assert (approximate (want_A + want_B, M)); }
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& msg) { msg.touch (); throw message; }
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 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 UZRectMollerup::upperboundary (const GeometryRect& geo, const Soil& soil, const ublas::vector<double>& T, const Surface& surface, std::vector<top_state>& state, const ublas::vector<double>& remaining_water, 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, const double ddt, const int debug, Treelog& msg, const double BIG_DT) { const std::vector<size_t>& edge_above = geo.cell_edges (Geometry::cell_above); const size_t edge_above_size = edge_above.size (); for (size_t i = 0; i < edge_above_size; i++) { const size_t edge = edge_above[i]; const int cell = geo.edge_other (edge, Geometry::cell_above); 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 (surface.top_type (geo, edge)) { case Surface::forced_flux: { const double flux = -surface.q_top (geo, edge, BIG_DT); Neumann (edge, cell, area, in_sign, flux, dq, B); } break; case Surface::forced_pressure: { const double value = -Kedge (edge) * geo.edge_area_per_length (edge); const double pressure = surface.h_top (geo, edge); Dirichlet (edge, cell, area, in_sign, sin_angle, Kedge (edge), h (cell), value, pressure, dq, Dm_mat, Dm_vec, Gm); } break; case Surface::limited_water: { const double h_top = remaining_water (i); // We pretend that the surface is particlaly saturated. const double K_sat = soil.K (cell, 0.0, 0.0, T (cell)); const double K_cell = Kedge (edge); const double K_edge = 0.5 * (K_cell + K_sat); const double dz = geo.edge_length (edge); daisy_assert (approximate (dz, -geo.cell_z (cell))); double q_in_avail = h_top / ddt; const double q_in_pot = K_edge * (h_top - h (cell) + dz) / dz; // Decide type. bool is_flux = h_top <= 0.0 || q_in_pot > q_in_avail; if (is_flux) { state[i] = top_flux; Neumann (edge, cell, area, in_sign, q_in_avail, dq, B); } else // Pressure { state[i] = top_pressure; if (debug > 0 && q_in_pot < 0.0) { std::ostringstream tmp; tmp << "q_in_pot = " << q_in_pot << ", q_avail = " << q_in_avail << ", h_top = " << h_top << ", h (cell) = " << h (cell) << " K (edge) = " << Kedge (edge) << ", K_sat = " << K_sat << ", K_edge = " << K_edge <<", dz = " << dz << ", ddt = " << ddt << ", is_flux = " << is_flux << "\n"; msg.message (tmp.str ()); } const double value = -K_edge * geo.edge_area_per_length (edge); const double pressure = h_top; Dirichlet (edge, cell, area, in_sign, sin_angle, K_edge, h (cell), value, pressure, dq, Dm_mat, Dm_vec, Gm); } if (debug == 3) { std::ostringstream tmp; tmp << "edge = " << edge << ", K_edge = " << K_edge << ", h_top = " << h_top << ", dz = " << dz << ", q_avail = " << q_in_avail << ", q_pot = " << q_in_pot << ", is_flux = " << is_flux; msg.message (tmp.str ()); } } break; case Surface::soil: throw "Don't know how to handle this surface type"; default: daisy_panic ("Unknown surface type"); } } }
void doIt (Daisy&, const Scope&, Treelog& out) { out.error (message.name ()); }
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 }
double PhotoGL::assimilate (const Units&, const double, const double, const double, const double, const double, const double, const double, const double Ta, const double, const double, const double, const std::vector<double>& PAR, const std::vector<double>& PAR_height, const double PAR_LAI, const std::vector<double>&, const double, CanopyStandard& canopy, Phenology& development, Treelog& msg) { // sugar production [gCH2O/m2/h] by canopy photosynthesis. const PLF& LAIvsH = canopy.LAIvsH; const double DS = development.DS; const double DAP = development.DAP; // Temperature effect and development stage effect const double Teff = TempEff (Ta) * DSEff (DS) * DAPEff (DAP); // 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 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); // 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); if (LA > 0) { prevLA = LAIvsH (height); accCAI += LA; const double dPAR = (PAR[i] - PAR[i+1]) / dCAI; // Leaf Photosynthesis [gCO2/m2/h] const double F = Fm * (1.0 - exp (- (Qeff * dPAR / Fm))); Ass += LA * F; } } daisy_assert (approximate (accCAI, canopy.CAI)); return (molWeightCH2O / molWeightCO2) * Teff * Ass; }