Ejemplo n.º 1
0
void 
UZRectMollerup::lowerboundary (const GeometryRect& geo,
			       const Groundwater& groundwater,
			       const std::vector<bool>& active_lysimeter,
			       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, Treelog& msg)
{
  const std::vector<size_t>& edge_below = geo.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];
      const int cell = geo.edge_other (edge, Geometry::cell_below);
      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 (groundwater.bottom_type ())
        {
        case Groundwater::free_drainage:
          {
            const double sin_angle = geo.edge_sin_angle (edge);
            //const double flux = -in_sign * sin_angle * K (cell) * area; //old
            const double flux = -in_sign * sin_angle * Kedge (edge);
            Neumann (edge, cell, area, in_sign, flux, dq, B);
          }
          break;
          
        case Groundwater::forced_flux:
          {
            const double flux = groundwater.q_bottom (edge);
            Neumann (edge, cell, area, in_sign, flux, dq, B);
          }
          break;
        
        case Groundwater::pressure:
          {
            const double value = -Kedge (edge) * geo.edge_area_per_length (edge);
            const double pressure =  groundwater.table () - geo.zplus (cell);
            
            Dirichlet (edge, cell, area, in_sign, sin_angle, 
                       Kedge (edge), 
                       h (cell),
                       value, pressure,
                       dq, Dm_mat, Dm_vec, Gm);
          }
          break;

        case Groundwater::lysimeter:
          {
            if (active_lysimeter[cell])
              {
                //Neumann - not so good
                //const double flux = -in_sign * sin_angle * K (cell);
                //Neumann (edge, cell, area, in_sign, flux, dq, B);
                //Dirichlet - better
                const double value = -Kedge (edge) * geo.edge_area_per_length (edge);
                const double pressure =  0.0;
                Dirichlet (edge, cell, area, in_sign, sin_angle,
                           Kedge (edge), 
                           h (cell),
                           value, pressure, dq, Dm_mat, Dm_vec, Gm);
              }
            else
              // Indsat af [email protected] Fri Jul 10 11:21:14     2009
              {
                const double flux = 0.0;
                Neumann (edge, cell, area, in_sign, flux, dq, B);
              }

          }
          break;
          
        default:
          daisy_panic ("Unknown groundwater type");
        }
    }
}
Ejemplo n.º 2
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);
}
Ejemplo n.º 3
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_;
}
Ejemplo n.º 4
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"; 
}