bool check (Treelog& msg) { if (!lex.good ()) return false; bool ok = true; if (c_x_pos < 0) { msg.error ("Position X: tag missing"); ok = false; } if (c_z_min < 0) { msg.error ("Position Z minimum: tag missing"); ok = false; } if (c_z_max < 0) { msg.error ("Position Z maximum: tag missing"); ok = false; } if (c_density < 0) { msg.error ("Root lenght density: tag missing"); ok = false; } return ok; }
bool valid (const PLF& plf, Treelog& msg) const { if (plf.size () < 1) { msg.error ("PLF must be non-empty"); return false; } const size_t first = 0; const size_t last = plf.size () - 1; const double first_x = plf.x (first); const double first_y = plf.y (first); const double last_x = plf.x (last); const double last_y = plf.y (last); if (first_x < 1 || last_x > 366) { msg.error ("Julian day must be between 1 and 366"); return false; } if (!approximate (first_y, last_y)) { std::ostringstream tmp; tmp << "First (" << first_y << ") and last (" << last_y << ") value must be identical"; msg.error (tmp.str ()); return false; } return true; }
bool verify (const Metalib&, const Frame& frame, const symbol key, Treelog& msg) const { daisy_assert (key == "SOM_fractions"); daisy_assert (frame.check (key)); daisy_assert (frame.lookup (key) == Attribute::Number); daisy_assert (frame.type_size (key) == Attribute::Variable); std::vector<double> fractions = frame.number_sequence ("SOM_fractions"); bool has_negative = false; double sum = 0.0; for (unsigned int i = 0; i < fractions.size (); i++) { if (fractions[i] < 0) has_negative = true; else sum += fractions[i]; } if (!has_negative && !approximate (sum, 1.0)) { msg.error ("sum must be 1.0"); return false; } if (sum > 1.0 && !approximate (sum, 1.0)) { msg.error ("sum must be at most 1.0"); return false; } return true; };
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); }
static void propagate (Treelog& msg, int nest, const std::string& text) { switch (nest) { case is_unknown: msg.entry (text); break; case is_debug: msg.debug (text); break; case is_plain: msg.message (text); break; case is_warning: msg.warning (text); break; case is_error: msg.error (text); break; case is_bug: msg.bug (text); break; case is_close: msg.close (); break; case is_touch: msg.touch (); break; case is_flush: msg.flush (); break; default: msg.open (text); } }
bool VCheck::MultiSize::verify (const Metalib&, const Frame& frame, const symbol key, Treelog& msg) const { daisy_assert (frame.check (key)); daisy_assert (!frame.is_log (key)); daisy_assert (frame.type_size (key) != Attribute::Singleton); if (sizes.find (frame.value_size (key)) != sizes.end ()) return true; std::ostringstream tmp; tmp << "'" << key << "' has " << frame.value_size (key) << " elements, expected one of { "; bool first = true; for (std::set<size_t>::const_iterator i = sizes.begin (); i != sizes.end (); i++) { if (first) first = false; else tmp << ", "; tmp << *i; } tmp << " } elements"; msg.error (tmp.str ()); return false; }
bool ChemistryMulti::check (const Scope& scope, const Geometry& geo, const Soil& soil, const SoilWater& soil_water, const SoilHeat& soil_heat, const Chemistry& chemistry, Treelog& msg) const { bool ok = true; for (size_t c = 0; c < combine.size (); c++) { Treelog::Open nest (msg, "Chemistry: '" + combine[c]->objid + "'"); if (!combine[c]->check (scope, geo, soil, soil_water, soil_heat, chemistry, msg)) ok = false; } // Check for duplicate chemicals. std::map<symbol, size_t> found; for (size_t i = 0; i < chemicals.size (); i++) { const symbol type = chemicals[i]->objid; std::map<symbol, size_t>::const_iterator f = found.find (type); if (f != found.end ()) { std::ostringstream tmp; tmp << "Chemical '" << type << "' definded in multiple chemistries:"; for (size_t j = 0; j < combine.size (); j++) if (combine[j]->know (type)) tmp << " '" << combine[j]->objid << "'"; msg.error (tmp.str ()); ok = false; } found[type] = i; } return ok; }
bool Frame::Implementation::check (const Metalib& metalib, const Frame& frame, Treelog& msg) const { bool ok = true; const FrameModel* model = dynamic_cast<const FrameModel*> (&frame); if (model && !model->buildable ()) { msg.error ("'" + frame.type_name () + "' is a base model, for internal use only"); ok = false; } for (type_map::const_iterator i = types.begin (); i != types.end (); i++) { const symbol key = (*i).first; const Type& type = *(*i).second; if (!frame.is_reference (key) && !check (metalib, frame, type, key, msg)) ok = false; } if (!ok) return false; if (!frame.has_references ()) for (size_t j = 0; j < checker.size (); j++) if (!checker[j] (metalib, frame, msg)) return false; return true; }
bool VolumeBox::limit (const Volume& other, Treelog& msg) { if (const VolumeBox* limit = dynamic_cast<const VolumeBox*> (&other)) { for (size_t i = 0; i < bounds_size; i++) { Bound& bound = *(this->*(bounds[i].bound)); if (bound.type () == Bound::none) { const Bound& lim = *(limit->*(bounds[i].bound)); switch (lim.type ()) { case Bound::none: /* do nothing */; break; case Bound::full: bound.set_full (); break; case Bound::finite: bound.set_finite (lim.value ()); break; } } } return true; } msg.error ("Don't know how to limit a '" + objid + "' to a '" + other.objid + "'"); return false; }
bool VCheck::Enum::valid (const symbol value, Treelog& msg) const { if (ids.find (value) != ids.end ()) return true; msg.error ("Invalid value '" + value + "'"); return false; }
bool ReactionNitrification::check (const Units&, const Geometry&, const Soil&, const SoilWater&, const SoilHeat&, const Chemistry& chemistry, Treelog& msg) const { bool ok = true; if (!chemistry.know (Chemical::NO3 ())) { msg.error ("Nitrification requires NO3 to be tracked"); ok = false; } if (!chemistry.know (Chemical::NH4 ())) { msg.error ("Nitrification requires NH4 to be tracked"); ok = false; } return ok; }
bool VCheck::IRange::valid (const int value, Treelog& msg) const { if (value < min) { std::ostringstream tmp; tmp << "Value is " << value << " but should be >= " << min; msg.error (tmp.str ()); return false; } if (value > max) { std::ostringstream tmp; tmp << "Value is " << value << " but should be <= " << max; msg.error (tmp.str ()); return false; } return true; }
bool valid (double last, double next, Treelog& msg) const { if (last > next) return true; std::ostringstream tmp; tmp << last << " <= " << next << ", must be decreasing"; msg.error (tmp.str ()); return false; }
bool VCheck::SumEqual::valid (double value, Treelog& msg) const { if (approximate (value, sum)) return true; std::ostringstream tmp; tmp << "Sum is " << value << " but should be " << sum; msg.error (tmp.str ()); return false; }
bool VCheck::Compatible::valid (const Units& units, symbol value, Treelog& msg) const { if (units.can_convert (dimension, value)) return true; std::ostringstream tmp; tmp << "Cannot convert [" << dimension << "] to [" << value << "]"; msg.error (tmp.str ()); return false; }
bool VCheck::EndValue::valid (double value, Treelog& msg) const { if (approximate (value, fixed)) return true; std::ostringstream tmp; tmp << "End value is " << value << " but should be " << fixed; msg.error (tmp.str ()); return false; }
static bool valid (int year, Treelog& msg) { if (!Time::valid (year, 1, 1, 1)) { std::ostringstream tmp; tmp << year << " is not a valid year"; msg.error (tmp.str ()); return false; } return true; }
bool has_attribute (const symbol name, Treelog& msg) const { bool missing = false; for (size_t i = 0; i < layers.size (); i++) if (!layers[i]->horizon->has_attribute (name)) { msg.error ("Required attribute '" + name + "' is missing from the soil horizon '" + layers[i]->horizon->objid + "'"); missing = true; } for (size_t i = 0; i < zones.size (); i++) if (!zones[i]->horizon->has_attribute (name)) { msg.error ("Required attribute '" + name + "' is missing from the soil zone '" + zones[i]->horizon->objid + "'"); missing = true; } return !missing; }
bool VCheck::InLibrary::valid (const Metalib& metalib, const symbol type, Treelog& msg) const { daisy_assert (metalib.exist (lib_name)); const Library& library = metalib.library (lib_name); if (!library.check (type)) { msg.error ("Unknown '" + lib_name + "' type '" + type + "'"); return false; } const FrameModel& frame = library.model (type); if (!frame.check (metalib, Treelog::null ())) { msg.error ("Incomplete type '" + type + "'"); return false; } return true; }
bool VCheck::FixedPoint::valid (const PLF& plf, Treelog& msg) const { if (approximate (plf (fixed_x), fixed_y)) return true; std::ostringstream tmp; tmp << "Value at " << fixed_x << " should be " << fixed_y << " but is << " << plf (fixed_x); msg.error (tmp.str ()); return false; }
bool VCheck::SumEqual::valid (const PLF& plf, Treelog& msg) const { const int end = plf.size () - 1; daisy_assert (end >= 0); bool ok = true; if (std::isnormal (plf.y (0))) { msg.error ("Value at start of PLF should be 0.0"); ok = false; } if (std::isnormal (plf.y (end))) { msg.error ("Value at end of PLF should be 0.0"); ok = false; } if (!valid (plf.integrate (plf.x (0), plf.x (end)), msg)) ok = false; return ok; }
bool VCheck::LargerThan::valid (const int value, Treelog& msg) const { if (value <= below) { std::ostringstream tmp; tmp << "Value is " << value << " but should be > " << below; msg.error (tmp.str ()); return false; } return true; }
bool VCheck::SmallerThan::valid (const int value, Treelog& msg) const { if (value >= above) { std::ostringstream tmp; tmp << "Value is " << value << " but should be < " << above; msg.error (tmp.str ()); return false; } return true; }
bool SoilWater::check (const size_t n, Treelog& msg) const { bool ok = true; if (Theta_.size () != n) { std::ostringstream tmp; tmp << "You have " << n << " intervals but " << Theta_.size () << " Theta values"; msg.error (tmp.str ()); ok = false; } if (h_.size () != n) { std::ostringstream tmp; tmp << "You have " << n << " intervals but " << h_.size () << " h values"; msg.error (tmp.str ()); ok = false; } if (X_ice_.size () != n) { std::ostringstream tmp; tmp << "You have " << n << " intervals but " << X_ice_.size () << " X_ice values"; msg.error (tmp.str ()); ok = false; } if (X_ice_buffer_.size () != n) { std::ostringstream tmp; tmp << "You have " << n << " intervals but " << X_ice_buffer_.size () << " X_ice_buffer values"; msg.error (tmp.str ()); ok = false; } return ok; }
// Create. bool check (const Units&, const Geometry&, const Soil&, const SoilWater&, const SoilHeat&, const Chemistry& chemistry, Treelog& msg) const { bool ok = true; if (!chemistry.know (immobile)) { msg.error ("'" + immobile + "' not traced"); ok = false; } if (!chemistry.know (bound) && bound != Attribute::None ()) { msg.error ("'" + bound + "' not traced"); ok = false; } if (!chemistry.know (colloid)) { msg.error ("'" + colloid + "' not traced"); ok = false; } return ok; }
void SoilWater::mass_balance (const Geometry& geo, double dt, Treelog& msg) { const size_t edge_size = geo.edge_size (); const double total_sink = 10 * geo.total_surface (S_sum_) * dt; const double total_old = 10 * geo.total_surface (Theta_old_); const double total_new = 10 * geo.total_surface (Theta_); double total_boundary_input = 0.0; for (size_t e = 0; e < edge_size; e++) { if (geo.edge_is_internal (e)) continue; const double in_sign = geo.cell_is_internal (geo.edge_to (e)) ? 1.0 : -1.0; const double q = q_primary_[e] + q_secondary_[e]; const double area = geo.edge_area (e); total_boundary_input += q * area * in_sign * dt; } total_boundary_input /= geo.surface_area (); total_boundary_input *= 10; if (!balance (total_old, total_new, total_boundary_input - total_sink)) { const double total_expected = total_old - total_sink + total_boundary_input; const double total_diff = total_new - total_old; const double total_error = total_expected - total_new; static double accumulated_error = 0.0; accumulated_error += total_error; std::ostringstream tmp; tmp << "Water balance: old (" << total_old << ") - sink (" << total_sink << ") + boundary input (" << total_boundary_input << ") != " << total_expected << " mm, got " << total_new << " mm, difference is " << total_diff << " mm, error is " << (total_expected - total_new) << " mm, accumulated " << accumulated_error << " mm"; for (size_t e = 0; e < edge_size; e++) { if (geo.edge_is_internal (e)) continue; tmp << "\nedge " << geo.edge_name (e) << ": primary " << q_primary_[e] << ", secondary = " << q_secondary_[e]; if (!approximate (q_primary_[e] + q_secondary_[e], q_matrix_[e])) tmp << ", NOT MATCHING matrix = " << q_matrix_[e]; tmp << ", tertiary = " << q_tertiary_[e] << ", area = " << geo.edge_area (e); } msg.error (tmp.str ()); } }
// Create and Destroy. bool initialize (const Units& units, const Geometry& geo, const Scope& scope, const Groundwater& groundwater, Treelog& msg) { bool ok = initialize_base (units, geo, scope, msg); if (pipe_position > 0) { msg.error ("Unknown pipe position"); ok = false; } const size_t cell_size = geo.cell_size (); S_from_drain.insert (S_from_drain.begin (), cell_size, 0.0); daisy_assert (S_from_drain.size () == cell_size); return ok; }
bool GnuplotProfile::initialize (const Units& units, Treelog& msg) { const Soil& soil = column->get_soil (); const Geometry& geo1 = column->get_geometry (); if (!dynamic_cast<const GeometryRect*> (&geo1)) { msg.error ("Can only show profile for rectangular grid geometries"); return false; } const GeometryRect& geo = dynamic_cast<const GeometryRect&> (geo1); std::map<symbol, int> all; int next = 0; for (size_t row = 0; row < geo.cell_rows (); row++) { for (size_t col = 0; col < geo.cell_columns (); col++) { const size_t cell = geo.cell_index (row, col); if (col == 0) zplus.push_back (geo.zplus (cell)); if (row == 0) xplus.push_back (geo.xplus (cell)); const Horizon& horizon = soil.horizon (cell); const symbol name = horizon.objid; std::map<symbol, int>::const_iterator i = all.find (name); if (i == all.end ()) { value.push_back (next); all[name] = next; next++; } else { const int val = (*i).second; value.push_back (val); } } } daisy_assert (next > 0); max_value = next - 1; daisy_assert (value.size () == geo.cell_size ()); daisy_assert (zplus.size () == geo.cell_rows ()); daisy_assert (xplus.size () == geo.cell_columns ()); // Done. return true; }
bool LogTable::check (const Border& border, Treelog& msg) const { TREELOG_MODEL (msg); bool ok = LogSelect::check (border, msg); if (!destination.check (msg)) { ok = false; std::ostringstream tmp; tmp << "Write error for '" << file << "'"; msg.error (tmp.str ()); } return ok; }
bool VCheck::MinSize::verify (const Metalib&, const Frame& frame, const symbol key, Treelog& msg) const { daisy_assert (frame.check (key)); daisy_assert (!frame.is_log (key)); daisy_assert (Attribute::flexible_size (frame.type_size (key))); if (frame.value_size (key) >= min_size) return true; std::ostringstream tmp; tmp << "Need at least " << min_size << " elements, got " << frame.value_size (key); msg.error (tmp.str ()); return false; }