Esempio n. 1
0
 double operator() (double value) const
 { return pF2h (value); }
Esempio n. 2
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 ());
}
Esempio n. 3
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);
}