static void propagate (Treelog& msg, int nest, const std::string& text) { switch (nest) { case is_unknown: msg.entry (text); break; case is_debug: msg.debug (text); break; case is_plain: msg.message (text); break; case is_warning: msg.warning (text); break; case is_error: msg.error (text); break; case is_bug: msg.bug (text); break; case is_close: msg.close (); break; case is_touch: msg.touch (); break; case is_flush: msg.flush (); break; default: msg.open (text); } }
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 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]; } } }