Пример #1
0
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;
}
Пример #2
0
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];
    }
}
Пример #3
0
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);
}
Пример #4
0
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;
}
Пример #5
0
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);
}
Пример #6
0
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_;
}
Пример #7
0
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];
        }
    }
}
Пример #8
0
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"; 
}
Пример #9
0
 void doIt (Daisy&, const Scope&, Treelog& out)
 { 
   out.warning (message.name ());
 }
Пример #10
0
 void doIt (Daisy& daisy, const Scope&, Treelog& out)
   { 
     out.message ("Ridging");      
     daisy.field ().ridge (ridge); 
   }
Пример #11
0
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";
}
Пример #12
0
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 + "'");
    } 
}
Пример #13
0
void 
UZRectMollerup::tick (const GeometryRect& geo,
		      const std::vector<size_t>& drain_cell,
		      const double drain_water_level,
		      const Soil& soil, 
		      SoilWater& soil_water, const SoilHeat& soil_heat,
		      const Surface& surface, const Groundwater& groundwater,
		      const double dt, Treelog& msg)

{
  daisy_assert (K_average.get ());
  const size_t edge_size = geo.edge_size (); // number of edges 
  const size_t cell_size = geo.cell_size (); // number of cells 

  // Insert magic here.
  
  ublas::vector<double> Theta (cell_size); // water content 
  ublas::vector<double> Theta_previous (cell_size); // at start of small t-step
  ublas::vector<double> h (cell_size); // matrix pressure
  ublas::vector<double> h_previous (cell_size); // at start of small timestep
  ublas::vector<double> h_ice (cell_size); // 
  ublas::vector<double> S (cell_size); // sink term
  ublas::vector<double> S_vol (cell_size); // sink term
#ifdef TEST_OM_DEN_ER_BRUGT
  ublas::vector<double> S_macro (cell_size);  // sink term
  std::vector<double> S_drain (cell_size, 0.0); // matrix-> macro -> drain flow 
  std::vector<double> S_drain_sum (cell_size, 0.0); // For large timestep
  const std::vector<double> S_matrix (cell_size, 0.0);  // matrix -> macro 
  std::vector<double> S_matrix_sum (cell_size, 0.0); // for large timestep
#endif
  ublas::vector<double> T (cell_size); // temperature 
  ublas::vector<double> Kold (edge_size); // old hydraulic conductivity
  ublas::vector<double> Ksum (edge_size); // Hansen hydraulic conductivity
  ublas::vector<double> Kcell (cell_size); // hydraulic conductivity
  ublas::vector<double> Kold_cell (cell_size); // old hydraulic conductivity
  ublas::vector<double> Ksum_cell (cell_size); // Hansen hydraulic conductivity
  ublas::vector<double> h_lysimeter (cell_size);
  std::vector<bool> active_lysimeter (cell_size);
  const std::vector<size_t>& edge_above = geo.cell_edges (Geometry::cell_above);
  const size_t edge_above_size = edge_above.size ();
  ublas::vector<double> remaining_water (edge_above_size);
  std::vector<bool> drain_cell_on (drain_cell.size (),false); 
  

  for (size_t i = 0; i < edge_above_size; i++)
    {
      const size_t edge = edge_above[i];
      remaining_water (i) = surface.h_top (geo, edge);
    }
  ublas::vector<double> q;	// Accumulated flux
  q = ublas::zero_vector<double> (edge_size);
  ublas::vector<double> dq (edge_size); // Flux in small timestep.
  dq = ublas::zero_vector<double> (edge_size);

  //Make Qmat area diagonal matrix 
  //Note: This only needs to be calculated once... 
  ublas::banded_matrix<double> Qmat (cell_size, cell_size, 0, 0);
  for (int c = 0; c < cell_size; c++)
    Qmat (c, c) = geo.cell_volume (c);
 
  // make vectors 
  for (size_t cell = 0; cell != cell_size ; ++cell) 
    {				
      Theta (cell) = soil_water.Theta (cell);
      h (cell) =  soil_water.h (cell);
      h_ice (cell) = soil_water.h_ice (cell);
      S (cell) =  soil_water.S_sum (cell);
      S_vol (cell) = S (cell) * geo.cell_volume (cell);
      if (use_forced_T)
	T (cell) = forced_T;
      else 
	T (cell) = soil_heat.T (cell); 
      h_lysimeter (cell) = geo.zplus (cell) - geo.cell_z (cell);
    }

  // Remember old value.
  Theta_error = Theta;

  // Start time loop 
  double time_left = dt;	// How much of the large time step left.
  double ddt = dt;		// We start with small == large time step.
  int number_of_time_step_reductions = 0;
  int iterations_with_this_time_step = 0;
  

  int n_small_time_steps = 0;
  
  while (time_left > 0.0)
    {
      if (ddt > time_left)
	ddt = time_left;

      std::ostringstream tmp_ddt;
      tmp_ddt << "Time t = " << (dt - time_left) 
              << "; ddt = " << ddt
              << "; steps " << n_small_time_steps 
              << "; time left = " << time_left;
      Treelog::Open nest (msg, tmp_ddt.str ());

      if (n_small_time_steps > 0
          && (n_small_time_steps%msg_number_of_small_time_steps) == 0)
        {
          msg.touch ();
          msg.flush ();
        }
      
      n_small_time_steps++;
      if (n_small_time_steps > max_number_of_small_time_steps) 
        {
          msg.debug ("Too many small timesteps");
          throw "Too many small timesteps";
        }
      
      // Initialization for each small time step.

      if (debug > 0)
	{
	  std::ostringstream tmp;
	  tmp << "h = " << h << "\n";
	  tmp << "Theta = " << Theta;
	  msg.message (tmp.str ());
	}

      int iterations_used = 0;
      h_previous = h;
      Theta_previous = Theta;

      if (debug == 5)
	{
	  std::ostringstream tmp;
	  tmp << "Remaining water at start: " << remaining_water;
	  msg.message (tmp.str ());
	}

      ublas::vector<double> h_conv;

      for (size_t cell = 0; cell != cell_size ; ++cell)
        active_lysimeter[cell] = h (cell) > h_lysimeter (cell);

      for (size_t edge = 0; edge != edge_size ; ++edge)
        {
          Kold[edge] = find_K_edge (soil, geo, edge, h, h_ice, h_previous, T);
          Ksum [edge] = 0.0;
        }

      std::vector<top_state> state (edge_above.size (), top_undecided);
      
      // We try harder with smaller timesteps.
      const int max_loop_iter 
        = max_iterations * (number_of_time_step_reductions 
                            * max_iterations_timestep_reduction_factor + 1);
      do // Start iteration loop
	{
	  h_conv = h;
	  iterations_used++;
          

          std::ostringstream tmp_conv;
          tmp_conv << "Convergence " << iterations_used; 
          Treelog::Open nest (msg, tmp_conv.str ());
          if (debug == 7)
            msg.touch ();

	  // Calculate conductivity - The Hansen method
	  for (size_t e = 0; e < edge_size; e++)
	    {
              Ksum[e] += find_K_edge (soil, geo, e, h, h_ice, h_previous, T);
              Kedge[e] = (Ksum[e] / (iterations_used  + 0.0)+ Kold[e]) / 2.0;
	    }

	  //Initialize diffusive matrix
	  Solver::Matrix diff (cell_size);
	  // diff = ublas::zero_matrix<double> (cell_size, cell_size);
	  diffusion (geo, Kedge, diff);

	  //Initialize gravitational matrix
	  ublas::vector<double> grav (cell_size); //ublass compatibility
	  grav = ublas::zero_vector<double> (cell_size);
	  gravitation (geo, Kedge, grav);

	  // Boundary matrices and vectors
	  ublas::banded_matrix<double>  Dm_mat (cell_size, cell_size, 
                                                0, 0); // Dir bc
	  Dm_mat = ublas::zero_matrix<double> (cell_size, cell_size);
	  ublas::vector<double>  Dm_vec (cell_size); // Dir bc
	  Dm_vec = ublas::zero_vector<double> (cell_size);
	  ublas::vector<double> Gm (cell_size); // Dir bc
	  Gm = ublas::zero_vector<double> (cell_size);
	  ublas::vector<double> B (cell_size); // Neu bc 
	  B = ublas::zero_vector<double> (cell_size);

	  lowerboundary (geo, groundwater, active_lysimeter, h,
                         Kedge,
                         dq, Dm_mat, Dm_vec, Gm, B, msg);
	  upperboundary (geo, soil, T, surface, state, remaining_water, h,
                         Kedge,
                         dq, Dm_mat, Dm_vec, Gm, B, ddt, debug, msg, dt);
          Darcy (geo, Kedge, h, dq); //for calculating drain fluxes 


	  //Initialize water capacity  matrix
	  ublas::banded_matrix<double> Cw (cell_size, cell_size, 0, 0);
	  for (size_t c = 0; c < cell_size; c++)
	    Cw (c, c) = soil.Cw2 (c, h[c]);
	  
          std::vector<double> h_std (cell_size);
          //ublas vector -> std vector 
          std::copy(h.begin (), h.end (), h_std.begin ());

#ifdef TEST_OM_DEN_ER_BRUGT
          for (size_t cell = 0; cell != cell_size ; ++cell) 
            {				
              S_macro (cell) = (S_matrix[cell] + S_drain[cell]) 
                * geo.cell_volume (cell);
            }
#endif

	  //Initialize sum matrix
	  Solver::Matrix summat (cell_size);  
	  noalias (summat) = diff + Dm_mat;

	  //Initialize sum vector
	  ublas::vector<double> sumvec (cell_size);  
	  sumvec = grav + B + Gm + Dm_vec - S_vol
#ifdef TEST_OM_DEN_ER_BRUGT
            - S_macro
#endif
            ; 

	  // QCw is shorthand for Qmatrix * Cw
	  Solver::Matrix Q_Cw (cell_size);
	  noalias (Q_Cw) = prod (Qmat, Cw);

	  //Initialize A-matrix
	  Solver::Matrix A (cell_size);  
	  noalias (A) = (1.0 / ddt) * Q_Cw - summat;  

	  // Q_Cw_h is shorthand for Qmatrix * Cw * h
	  const ublas::vector<double> Q_Cw_h = prod (Q_Cw, h);

	  //Initialize b-vector
	  ublas::vector<double> b (cell_size);  
	  //b = sumvec + (1.0 / ddt) * (Qmatrix * Cw * h + Qmatrix *(Wxx-Wyy));
	  b = sumvec + (1.0 / ddt) * (Q_Cw_h
				      + prod (Qmat, Theta_previous-Theta));

	  // Force active drains to zero h.
          drain (geo, drain_cell, drain_water_level,
		 h, Theta_previous, Theta, S_vol,
#ifdef TEST_OM_DEN_ER_BRUGT
                 S_macro,
#endif
                 dq, ddt, drain_cell_on, A, b, debug, msg);  
          
          try {
            solver->solve (A, b, h); // Solve Ah=b with regard to h.
          } catch (const char *const error) {
              std::ostringstream tmp;
              tmp << "Could not solve equation system: " << error;
              msg.warning (tmp.str ());
              // Try smaller timestep.
              iterations_used = max_loop_iter + 100;
              break;
          }

	  for (int c=0; c < cell_size; c++) // update Theta 
	    Theta (c) = soil.Theta (c, h (c), h_ice (c)); 

	  if (debug > 1)
	    {
	      std::ostringstream tmp;
	      tmp << "Time left = " << time_left << ", ddt = " << ddt 
		  << ", iteration = " << iterations_used << "\n";
	      tmp << "B = " << B << "\n";
	      tmp << "h = " << h << "\n";
	      tmp << "Theta = " << Theta;
	      msg.message (tmp.str ());
	    }
          
          for (int c=0; c < cell_size; c++)
            {
              if (h (c) < min_pressure_potential || h (c) > max_pressure_potential)
                {
                  std::ostringstream tmp;
                  tmp << "Pressure potential out of realistic range, h[" 
                      << c << "] = " << h (c);
                  msg.debug (tmp.str ());
                  iterations_used = max_loop_iter + 100;
                  break;
                } 
            }
        }

      while (!converges (h_conv, h) && iterations_used <= max_loop_iter);
      

      if (iterations_used > max_loop_iter)
	{
          number_of_time_step_reductions++;
          
	  if (number_of_time_step_reductions > max_time_step_reductions)
            {
              msg.debug ("Could not find solution");
              throw "Could not find solution";
            }

          iterations_with_this_time_step = 0;
	  ddt /= time_step_reduction;
	  h = h_previous;
	  Theta = Theta_previous;
	}
      else
	{
          // Update dq for new h.
	  ublas::banded_matrix<double>  Dm_mat (cell_size, cell_size, 
                                                0, 0); // Dir bc
	  Dm_mat = ublas::zero_matrix<double> (cell_size, cell_size);
	  ublas::vector<double>  Dm_vec (cell_size); // Dir bc
	  Dm_vec = ublas::zero_vector<double> (cell_size);
	  ublas::vector<double> Gm (cell_size); // Dir bc
	  Gm = ublas::zero_vector<double> (cell_size);
	  ublas::vector<double> B (cell_size); // Neu bc 
	  B = ublas::zero_vector<double> (cell_size);
	  lowerboundary (geo, groundwater, active_lysimeter, h,
                         Kedge,
                         dq, Dm_mat, Dm_vec, Gm, B, msg);
	  upperboundary (geo, soil, T, surface, state, remaining_water, h,
                         Kedge,
                         dq, Dm_mat, Dm_vec, Gm, B, ddt, debug, msg, dt);
          Darcy (geo, Kedge, h, dq);

#ifdef TEST_OM_DEN_ER_BRUGT
          // update macropore flow components 
          for (int c = 0; c < cell_size; c++)
            {
              S_drain_sum[c] += S_drain[c] * ddt/dt;
              S_matrix_sum[c] += S_matrix[c] * ddt/dt;
            }
#endif

          std::vector<double> h_std_new (cell_size);
          std::copy(h.begin (), h.end (), h_std_new.begin ());

	  // Update remaining_water.
	  for (size_t i = 0; i < edge_above.size (); i++)
	    {
	      const int edge = edge_above[i];
	      const int cell = geo.edge_other (edge, Geometry::cell_above);
	      const double out_sign = (cell == geo.edge_from (edge))
		? 1.0 : -1.0;
	      remaining_water[i] += out_sign * dq (edge) * ddt;
              daisy_assert (std::isfinite (dq (edge)));
	    }

	  if (debug == 5)
	    {
	      std::ostringstream tmp;
	      tmp << "Remaining water at end: " << remaining_water;
	      msg.message (tmp.str ());
	    }

	  // Contribution to large time step.
          daisy_assert (std::isnormal (dt));
          daisy_assert (std::isnormal (ddt));
	  q += dq * ddt / dt;
          for (size_t e = 0; e < edge_size; e++)
            {
              daisy_assert (std::isfinite (dq (e)));
              daisy_assert (std::isfinite (q (e)));
            }
          for (size_t e = 0; e < edge_size; e++)
            {
              daisy_assert (std::isfinite (dq (e)));
              daisy_assert (std::isfinite (q (e)));
            }

	  time_left -= ddt;
	  iterations_with_this_time_step++;

	  if (iterations_with_this_time_step > time_step_reduction)
	    {
	      number_of_time_step_reductions--;
	      iterations_with_this_time_step = 0;
	      ddt *= time_step_reduction;
	    }
	}
      // End of small time step.
    }

  // Mass balance.
  // New = Old - S * dt + q_in * dt - q_out * dt + Error =>
  // 0 = Old - New - S * dt + q_in * dt - q_out * dt + Error
  Theta_error -= Theta;         // Old - New
  Theta_error -= S * dt;
#ifdef TEST_OM_DEN_ER_BRUGT
  for (size_t c = 0; c < cell_size; c++)
    Theta_error (c) -= (S_matrix_sum[c] + S_drain_sum[c]) * dt;
#endif
  
  for (size_t edge = 0; edge != edge_size; ++edge) 
    {
      const int from = geo.edge_from (edge);
      const int to = geo.edge_to (edge);
      const double flux = q (edge) * geo.edge_area (edge) * dt;
      if (geo.cell_is_internal (from))
        Theta_error (from) -= flux / geo.cell_volume (from);
      if (geo.cell_is_internal (to))
        Theta_error (to) += flux / geo.cell_volume (to);
    }

  // Find drain sink from mass balance.
#ifdef TEST_OM_DEN_ER_BRUGT
  std::fill(S_drain.begin (), S_drain.end (), 0.0);
#else
  std::vector<double> S_drain (cell_size);
#endif
  for (size_t i = 0; i < drain_cell.size (); i++)
    {
      const size_t cell = drain_cell[i];
      S_drain[cell] = Theta_error (cell) / dt;
      Theta_error (cell) -= S_drain[cell] * dt;
    }

  if (debug == 2)
    {
      double total_error = 0.0;
      double total_abs_error = 0.0;
      double max_error = 0.0;
      int max_cell = -1;
      for (size_t cell = 0; cell != cell_size; ++cell) 
        {
          const double volume = geo.cell_volume (cell);
          const double error = Theta_error (cell);
          total_error += volume * error;
          total_abs_error += std::fabs (volume * error);
          if (std::fabs (error) > std::fabs (max_error))
            {
              max_error = error;
              max_cell = cell;
            }
        }
      std::ostringstream tmp;
      tmp << "Total error = " << total_error << " [cm^3], abs = " 
	  << total_abs_error << " [cm^3], max = " << max_error << " [] in cell " 
	  << max_cell;
      msg.message (tmp.str ());
    }
  
  // Make it official.
  for (size_t cell = 0; cell != cell_size; ++cell) 
    soil_water.set_content (cell, h (cell), Theta (cell));
  
#ifdef TEST_OM_DEN_ER_BRUGT
  soil_water.add_tertiary_sink (S_matrix_sum);
  soil_water.drain (S_drain_sum, msg);
#endif


  for (size_t edge = 0; edge != edge_size; ++edge) 
    {
      daisy_assert (std::isfinite (q[edge]));
      soil_water.set_flux (edge, q[edge]);
    }

  soil_water.drain (S_drain, msg);

  // End of large time step.
}
Пример #14
0
 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); 
 }
Пример #15
0
  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;
  }
Пример #16
0
  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;
  }
Пример #17
0
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]
}
Пример #18
0
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;
}
Пример #19
0
 // Simulation.
 void doIt (Daisy& daisy, const Scope&, Treelog& out)
 {
   out.message ("Adjusting surface detention capacity");
   daisy.field ().set_surface_detention_capacity (height);
 }
Пример #20
0
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);
}
Пример #21
0
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));
}
Пример #22
0
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];
        }
    }
}
Пример #23
0
 void doIt (Daisy&, const Scope&, Treelog& msg)
 { 
   msg.touch ();
   throw message; 
 }
Пример #24
0
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 ());
}
Пример #25
0
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);
}
Пример #26
0
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");
	}
    }
}
Пример #27
0
 void doIt (Daisy&, const Scope&, Treelog& out)
 { 
   out.error (message.name ());
 }
Пример #28
0
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
}
Пример #29
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;
}