Example #1
0
void 
ReactionDenit::tick_soil (const Units&, const Geometry& geo,
                          const Soil& soil, const SoilWater& soil_water,
                          const SoilHeat& soil_heat,
                          const OrganicMatter& organic_matter, 
                          Chemistry& chemistry, 
                          const double dt, Treelog&)
{
  const size_t cell_size = geo.cell_size ();
  const std::vector<bool> active = organic_matter.active (); 
  Chemical& soil_NO3 = chemistry.find (Chemical::NO3 ());

  for (size_t i = 0; i < cell_size; i++)
    {
      if (!active[i])
        {
          converted[i] = converted_fast[i] = converted_redox[i]
            = potential[i] = potential_fast[i] = 0.0;
          continue;
        }
      const double CO2_fast = organic_matter.CO2_fast (i);
      const double CO2_slow = organic_matter.CO2 (i) - CO2_fast;
      const double Theta = soil_water.Theta (i);
      const double Theta_sat = soil_water.Theta_ice (soil, i, 0.0);
      const double Theta_fraction = Theta / Theta_sat;
      const double NO3 = soil_NO3.C_primary (i) * Theta;
      const double T = soil_heat.T (i);
      const double height = geo.cell_z (i);
      const double T_factor = (heat_factor.size () < 1)
        ? Abiotic::f_T2 (T)
        : heat_factor (T);
      const double pot = T_factor * alpha * CO2_slow;
      const double w_factor = water_factor (Theta_fraction);
      const double rate = w_factor * pot;

      const double pot_fast = T_factor * alpha_fast * CO2_fast;
      const double w_factor_fast = water_factor_fast (Theta_fraction);
      const double rate_fast = w_factor_fast * pot_fast;

      const double M 
        = (std::min (rate, K * NO3) + std::min (rate_fast, K_fast * NO3)) * dt;
      if (redox_height <= 0 && height < redox_height)
        {
          converted[i] = NO3 / dt;
          converted_redox[i] = (NO3 - M) / dt;
        }
      else
        {
          converted[i] = M / dt;
          converted_redox[i] = 0.0;
        }
      converted_fast[i] = (M / dt > rate ? M / dt - rate : 0.0);
      potential[i] = pot;
      potential_fast[i] = pot_fast;
    }
  soil_NO3.add_to_transform_sink (converted);
}
Example #2
0
void 
DrainLateral::tick (const Time& time, const Scope& scope, 
                    const Geometry& geo, const Soil& soil, 
                    const SoilHeat& soil_heat, const Surface& surface, 
                    SoilWater& soil_water, 
                    Treelog& msg)
{
  // Current pipe level.
  pipe_outlet->tick (time, scope, msg);
  set_pipe_level ();

  const size_t cell_size = geo.cell_size ();

  // Empty source.
  fill (S.begin (), S.end (), 0.0);
  
  // Find groundwater height.
  const double old_height = height;
  height = surface.ponding_average () * 0.1;
  double lowest = 0.0;
  for (size_t i = 0; i < cell_size; i++)
    {
      // Look for an unsaturated node.
      const double h = soil_water.h (i);
      if (h >= 0)
        continue;
      // as low as possible.
      const double z = geo.cell_top (i);
      if (approximate (z, lowest))
        {
          const double new_height = z + h;
 
          // Use closest value to old height;
          if (height >= 0.0
              || (std::fabs (new_height - old_height)
                  < std::fabs (height - old_height)))
            height = new_height;
        }
      else if (z < lowest)
        {
          lowest = z;
          height = z + h;
        }
    }    

  // Find sink term.
  EqDrnFlow = EquilibriumDrainFlow (geo, soil, soil_heat);   
  DrainFlow= geo.total_surface (S);
  soil_water.drain (S, msg);
}
Example #3
0
void 
ReactionNitrification::tick_soil (const Units&, const Geometry& geo,
                                  const Soil& soil, const SoilWater& soil_water,
                                  const SoilHeat& soil_heat,
                                  const OrganicMatter& organic_matter, 
                                  Chemistry& chemistry, 
                                  const double /* dt */, Treelog&)
{
  const size_t cell_size = geo.cell_size ();
  const std::vector<bool> active = organic_matter.active (); 
  Chemical& soil_NO3 = chemistry.find (Chemical::NO3 ());
  Chemical& soil_NH4 = chemistry.find (Chemical::NH4 ());

  daisy_assert (NH4.size () == cell_size);
  daisy_assert (N2O.size () == cell_size);
  daisy_assert (NO3.size () == cell_size);

  for (size_t i = 0; i < cell_size; i++)
    {
      if (active[i])
        soil.nitrification (i, 
                            soil_NH4.M_primary (i), 
			    soil_NH4.C_primary (i), 
                            soil_water.h (i), soil_heat.T (i),
                            NH4[i], N2O[i], NO3[i]);
      else
        NH4[i] = N2O[i] = NO3[i] = 0.0;        
    }


  soil_NH4.add_to_transform_sink (NH4);
  soil_NO3.add_to_transform_source (NO3);
}
Example #4
0
  void tillage (const Geometry& geo, const double from, const double to,
                const double surface_loose, const double RR0, 
                const SoilWater& soil_water, 
                const OrganicMatter& organic_matter)
  {
    // Water content.
    std::map<const Horizon*, double> water;
    for (std::size_t c = 0; c < geo.cell_size (); c++)
      if (geo.fraction_in_z_interval (c, from, to) > 0.01)
        water[horizon_[c]] += soil_water.Theta (c) * geo.cell_volume (c);

    static const double c_fraction_in_humus = 0.587;
    static const double aom_from = 0.0; // [cm]
    static const double aom_to = -15.0; // [cm]
    const double AOM_C = organic_matter.AOM_C (geo, aom_from, aom_to)
      / geo.surface_area ();    // [g C/cm^2]
    const double AOM15 = 10.0 * AOM_C / c_fraction_in_humus;   // [kg/m^2]

    for (std::map<const Horizon*, double>::const_iterator i = water.begin ();
         i != water.end ();
         i++)
      {
        const Horizon *const hor = (*i).first;
        const double total_water = (*i).second;
        const double total_volume = volume[hor];
        daisy_assert (total_water < total_volume);
        const double Theta = total_water / total_volume;
        hor->hydraulic->tillage (surface_loose, RR0, Theta, AOM15);
      }
  }
void
ABAProdUptake::production (const Units& units,
                           const Geometry& geo, const SoilWater& soil_water,
                           const std::vector<double>& S /* [cm^3/cm^3/h] */,
                           const std::vector<double>& /* l [cm/cm^3] */,
                           std::vector<double>& ABA    /* [g/cm^3/h] */,
                           Treelog& msg) const
{
  // Check input.
  const size_t cell_size = geo.cell_size ();
  daisy_assert (ABA.size () == cell_size);
  daisy_assert (S.size () == cell_size);
  
  // For all cells.
  for (size_t c = 0; c < cell_size; c++)
    {
      const double h = soil_water.h (c);
      // Set up 'h' in scope.
      scope.set (h_name, soil_water.h (c));

      // Find soil value.
      double value = 0.0;
      if (!expr->tick_value (units, value, ABA_unit, scope, msg))
	msg.error ("No ABA production value found");
      if (!std::isfinite (value) || value < 0.0)
        {
          if (h > -14000) // We are not concerned with ABA below wilting point.
            {
              std::ostringstream tmp;
              tmp << "ABA in cell " << c << " with h = " << h
                  << " was " << value << " [" << ABA_unit << "], using 0 instead";
              msg.warning (tmp.str ());
            }
          value = 0.0;
        }
      daisy_assert (std::isfinite (S[c]));

      // Find ABA uptake.
      ABA[c] = value * S[c];
      // [g/cm^3 S/h] = [g/cm^3 W] * [cm^3 W/cm^3 S/h]
      daisy_assert (std::isfinite (ABA[c]));
    }
}
Example #6
0
void 
Movement1D::tick (const Soil& soil, SoilWater& soil_water, 
                  const SoilHeat& soil_heat,
                  Surface& surface, Groundwater& groundwater,
                  const Time& time, const Weather& weather, 
                  const double dt, Treelog& msg) 
{
  const size_t edge_size = geo->edge_size ();
  const size_t cell_size = geo->cell_size ();

  TREELOG_MODEL (msg);

  // Cells.
  std::vector<double> S_sum (cell_size);
  std::vector<double> h_old (cell_size);
  std::vector<double> Theta_old (cell_size);
  std::vector<double> h_ice (cell_size);
  std::vector<double> h (cell_size);
  std::vector<double> Theta (cell_size);
  
  for (size_t c = 0; c < cell_size; c++)
    {
      S_sum[c] = soil_water.S_sum (c);
      h_old[c] = soil_water.h_old (c);
      Theta_old[c] = soil_water.Theta_old (c);
      h_ice[c] = soil_water.h_ice (c);
      h[c] = soil_water.h (c);
      Theta[c] = soil_water.Theta (c);
    }

  // Edges.
  std::vector<double> q (edge_size, 0.0);
  std::vector<double> q_p (edge_size, 0.0);

  for (size_t e = 0; e < edge_size; e++)
    {
      q[e] = soil_water.q_matrix (e);
      q_p[e] = soil_water.q_tertiary (e);
    }
  tick_water (*geo, soil, soil_heat, surface, groundwater, 
              S_sum, h_old, Theta_old, h_ice, h, Theta,
              q, q_p, dt, msg);
  soil_water.set_matrix (h, Theta, q);
}
Example #7
0
double 
Movement1D::surface_snow_T (const Soil& soil,
                            const SoilWater& soil_water,
                            const SoilHeat& soil_heat,
                            const double T_snow,
                            const double K_snow,
                            const double dZs) const
{
  // Information about soil.
  const double K_soil 
    = soil.heat_conductivity (0, soil_water.Theta (0),
                              soil_water.X_ice (0)) 
    * 1e-7 * 100.0 / 3600.0; // [erg/cm/h/dg C] -> [W/m/dg C]
  const double Z = -geo->cell_z (0) / 100.0; // [cm] -> [m]
  const double T_soil = soil_heat.T (0); // [dg C]
  daisy_assert (T_soil > -100.0);
  daisy_assert (T_soil < 50.0);

  const double T = (K_soil / Z * T_soil + K_snow / dZs * T_snow) 
    / (K_soil / Z + K_snow / dZs);
  daisy_assert (T > -100.0);
  daisy_assert (T < 50.0);
  return T;
}
Example #8
0
void 
UZRectConst::tick (const GeometryRect& geo, const std::vector<size_t>&,
                   const Soil&, 
                   SoilWater& soil_water, const SoilHeat&,
                   const Surface&, const Groundwater&, 
                   const double, Treelog&)

{
  const size_t edge_size = geo.edge_size (); // number of edges 

  for (size_t edge = 0; edge != edge_size; ++edge) 
    {
      const double sin_angle = geo.edge_sin_angle (edge);
      const double cos_angle = geo.edge_cos_angle (edge);
      const double q = q_z * sin_angle + q_x * cos_angle;
      soil_water.set_flux (edge, q);
    }
}
Example #9
0
 void tick (const double dt, const double rain, const Geometry& geo,
            const SoilWater& soil_water, Treelog& msg)
 {
   // Ice content.
   std::map<const Horizon*, double> ice;
   for (std::size_t c = 0; c < geo.cell_size (); c++)
     ice[horizon_[c]] += soil_water.X_ice (c) * geo.cell_volume (c);
   
   for (std::map<const Horizon*, double>::const_iterator i = ice.begin ();
        i != ice.end ();
        i++)
     {
       const Horizon *const hor = (*i).first;
       const double total_ice = (*i).second;
       const double total_volume = volume[hor];
       const double Ice = total_ice / total_volume;
       hor->hydraulic->tick (dt, rain, Ice, msg);
     }
 }
Example #10
0
void 
Ridge::Implementation::initialize (const Geometry1D& geo,
                                   const Soil& soil,
                                   const SoilWater& soil_water)
{
  // Find values depending on soil numerics.
  last_cell = geo.interval_plus (lowest);
  daisy_assert (last_cell+1 < soil.size ());
  dz = 0 - geo.zplus (last_cell);
  K_sat_below = soil.K (last_cell+1, 0.0, 0.0, 20.0);

  // Initialize water content.
  Theta = 0.0;
  for (int i = 0; i <= last_cell; i++)
    Theta += soil_water.Theta (i) * geo.dz (i);
  Theta /= dz;
  Theta_pre = Theta;
  daisy_assert (Theta < soil.Theta (0, 0.0, 0.0));
  h = soil.h (0, Theta);
  K = soil.K (0, 0.0, 0.0, 20.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.
}
Example #12
0
void
MovementSolute::divide_top_incomming (const Geometry& geo, 
                                      const SoilWater& soil_water, 
                                      const double J_above, // [g/cm^2/h]
                                      std::map<size_t, double>& J_primary,
                                      std::map<size_t, double>& J_secondary,
                                      std::map<size_t, double>& J_tertiary)
{
  daisy_assert (J_above < 0.0); // Negative upward flux.
  const std::vector<size_t>& edge_above 
    = geo.cell_edges (Geometry::cell_above);
  const size_t edge_above_size = edge_above.size ();
  double total_water_in = 0.0;  // [cm^3 W/h]
  double total_area = 0.0;      // [cm^2 S]

  // Find incomming water in all domain.
  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 area = geo.edge_area (edge); // [cm^2 S]
      total_area += area;
      const double in_sign 
        = geo.cell_is_internal (geo.edge_to (edge)) 
        ? 1.0
        : -1.0;
      daisy_assert (in_sign < 0);

      // Tertiary domain.
      const double q_tertiary = soil_water.q_tertiary (edge);
      daisy_assert (std::isfinite (q_tertiary));
      const double tertiary_in = q_tertiary * in_sign; // [cm^3 W/cm^2 S/h]
      if (tertiary_in > 0)
        {
          total_water_in += tertiary_in * area;
          J_tertiary[edge] = q_tertiary; // [cm^3 W/cm^2 S/h]
        }
      else
        J_tertiary[edge] = 0.0;

      // Secondary domain.
      const double q_secondary = soil_water.q_secondary (edge);
      const double secondary_in = q_secondary * in_sign; // [cm^3 W/cm^2 S/h]
      if (secondary_in > 0)
        {
          total_water_in += secondary_in * area;
          J_secondary[edge] = q_secondary; // [cm^3 W/cm^2 S/h]
          
        }
      else
        J_secondary[edge] = 0.0;

      // Primary domain.
      const double q_primary = soil_water.q_primary (edge);
      const double primary_in = q_primary * in_sign; // [cm^3 W/cm^2 S/h]
      if (primary_in > 0)
        {
          total_water_in += primary_in * area;
          J_primary[edge] = q_primary; // [cm^3 W/cm^2 S/h]
        }
      else
        J_primary[edge] = 0.0;
    }
  daisy_approximate (total_area, geo.surface_area ());

  if (total_water_in > 1e-9 * total_area)
    // Scale with incomming solute.
    {
      // [g/cm^3 W] = [g/cm^2 S/h] * [cm^2 S] / [cm^3 W/h] 
      const double C_above = -J_above * total_area / total_water_in;
      daisy_assert (std::isfinite (C_above));
      for (size_t i = 0; i < edge_above_size; i++)
        {
          const size_t edge = edge_above[i];
          // [g/cm^2 S/h] = [cm^3 W/cm^2 S/h] * [g/cm^3 W]
          J_tertiary[edge] *= C_above;
          J_secondary[edge] *= C_above;
          J_primary[edge] *= C_above;
        }
    }
  else
    {
      daisy_assert (total_water_in >= 0.0);
      for (size_t i = 0; i < edge_above_size; i++)
        {
          const size_t edge = edge_above[i];
          const double in_sign 
            = geo.cell_is_internal (geo.edge_to (edge)) ? 1.0 : -1.0;
          J_tertiary[edge] = 0.0;
          J_secondary[edge] = 0.0;
          J_primary[edge] = -J_above * in_sign;
        }
    }
}
Example #13
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);
}
Example #14
0
void
MovementSolute::secondary_transport (const Geometry& geo,
                                     const Soil& soil,
                                     const SoilWater& soil_water,
                                     const std::map<size_t, double>& J_forced,
                                     const std::map<size_t, double>& C_border,
                                     Chemical& solute, 
                                     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, 0.0); // Flux delivered by flow.

    for (size_t e = 0; e < edge_size; e++)
    {
      q[e] = soil_water.q_secondary (e);
      daisy_assert (std::isfinite (q[e]));
    }

  // 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> A (cell_size); // Content ignored by flow.
  std::vector<double> Mf (cell_size); // Content 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_secondary_old (c);
      daisy_assert (Theta_old[c] >= 0.0);
      Theta_new[c] = soil_water.Theta_secondary (c);
      daisy_assert (Theta_new[c] >= 0.0);
      const double source = solute.S_secondary (c);
      daisy_assert (std::isfinite (source));
      Mf[c] = solute.C_secondary (c) * Theta_old[c];
      daisy_assert (Mf[c] >= 0.0);
      A[c] = solute.M_secondary (c) - Mf[c];
      daisy_assert (std::isfinite (A[c]));
      if (Theta_new[c] > 0)
        {
          if (Theta_old[c] > 0)
            // Secondary water fully active.
            S[c] = source;
          else if (source > 0.0)
            // Fresh water and source.
            S[c] = source;
          else
            // Fresh water and sink.
            S[c] = 0.0;
        }
      else
        // No secondary water at end of timestep.
        S[c] = 0.0;
      
      // Put any remaining source in S_extra.
      S_extra[c] += source - S[c];
      daisy_assert (std::isfinite (S_extra[c]));
    }
  
  // Flow.
  secondary_flow (geo, Theta_old, Theta_new, q, solute.objid, 
                  S, J_forced, C_border, Mf, J, dt, msg);

  // Check fluxes.
  for (size_t e = 0; e < edge_size; e++)
    daisy_assert (std::isfinite (J[e]));


  // Negative content should be handled by primary transport.
  std::vector<double> Mn (cell_size); // New content.
  std::vector<double> C (cell_size);
  for (size_t c = 0; c < cell_size; c++)
    {
      Mn[c] = A[c] + Mf[c] + S_extra[c] * dt;
      if (Mn[c] < 0.0 || Theta_new[c] < 1e-6)
        {
          S_extra[c] = Mn[c] / dt;
          Mn[c] = 0.0;
        }
      else
        S_extra[c] = 0.0;

      daisy_assert (std::isfinite (S_extra[c]));
    }
  solute.set_secondary (soil, soil_water, Mn, J);
}
Example #15
0
void 
Ridge::Implementation::tick (const Geometry1D& geo,
                             const Soil& soil, const SoilWater& soil_water,
			     const double external_ponding, const double dt)
{
  // First, we need to find the internal ponding height. 
  // The external ponding assumes a flat surface, we need to find the
  // point (x_pond, z_pond) in the ridge geometry where air, soil and
  // surface water all meet.
  if (external_ponding < 1.0e-5)
    {
      // No ponding.
      x_pond = 0.0;
      z_pond = lowest;
    }
  else if (external_ponding > highest - 1.0e-5)
    {
      // Above top of ridge (ridge geometry doesn't matter then). 
      x_pond = 1.0;
      z_pond = external_ponding;
    }
  else
    {
      // Somewhere in between.  
      //
      // For a given point (x, z), x * z is the amount of space
      // available had all the soil from the ridge been removed.  If
      // you remove the space occupied by the ridge soil, the
      // remaining space is available to free water and air.  So we
      // need to find the point where x * z minus the ridge soil is
      // equal to the amount of water in the pond.  
      //
      // We can't solve this analytically in general, but we can solve
      // it when the ridge geometry is a straight line.  Since the
      // geometry is represented by a PLF, we can find the relevant
      // piece and solve it there.

      // Note that x * z can be negative because our zero point is at
      // the original soil surface, not the bottom of the ridge, this
      // doesn't matter since we are only interested in differences,
      // not absolute number.  

      // Ridge soil until now.
      double integral = 0.0;
      // Last point in the PLF.
      double x0 = 0.0;
      double z0 = lowest;
      
      for (unsigned int i = 1; ; i++)
	{
	  // We already know the answer must lie between two points.
	  daisy_assert (i < z.size ());
	  // New point in the PLF.
	  const double x1 = z.x (i);
	  const double z1 = z.y (i);
	  // Difference.
	  const double delta_z = z1 - z0;
	  const double delta_x = x1 - x0;
	  // Average z height.
	  const double average_z = 0.5 * (z0 + z1);
	  // Ridge soil for this step.
	  const double this_step = delta_x * average_z;
	  // Total space for this step.
	  const double total = x1 * z1;

	  // Check if this interval can contain all the ponded water.
	  if (total - (integral + this_step) >= external_ponding)
	    {
	      // Find slant in this interval.
	      const double slant = delta_z / delta_x;

	      // We now need to find the point (x_p, z_p) where 
	      //     total - (integral + this_step) = external_ponding)
	      // where
	      //     total = x_p * z_p
	      //     this_step = 0.5 * (x_p - x0) * (z0 + z_p)
	      //
	      // Substitute 
	      //     z_p = z0 + slant * (x_p - x0)
	      // and we get
	      //     x_p * (z0 + slant * (x_p - x0))
	      //     - (integral 
	      //        + 0.5 * (x_p - x0) * (z0 + z0 + slant * (x_p - x0))
	      //     = external_ponding
	      // <=>
	      //     x_p * z0 + slant * x_p^2 - slant * x0 * x_p
	      //     - integral
	      //     - (x_p - x0) * ( z0 + 0.5 * slant * (x_p - x0))
	      //     - external_ponding
	      //     = 0
	      // <=>
	      //     x_p * z0 + slant * x_p^2 - slant * x0 * x_p
	      //     - integral 
	      //     - x_p * z0
	      //     - 0.5 * x_p * slant * (x_p - x0)
	      //     + x0 * z0
	      //     + 0.5 * x0 * slant * (x_p - x0)
	      //     - external_ponding
	      //     = 0
	      // <=> 
	      //     x_p * z0 + slant * x_p^2 - slant * x0 * x_p
	      //     - integral 
	      //     - x_p * z0
	      //     - 0.5 * x_p * slant * x_p
	      //     + 0.5 * x_p * slant * x0
	      //     + x0 * z0
	      //     + 0.5 * x0 * slant * x_p
	      //     - 0.5 * x0 * slant * x0
	      //     - external_ponding
	      //     = 0
	      // <=>
	      //     (slant - 0.5 * slant) * x_p^2
	      //     (z0 - slant * x0 - z0 + 0.5 * slant * x0 
	      //      + 0.5 * slant * x0) * x_p
	      //     - integral + x0 * z0 - 0.5 * x0 * slant * x0
	      //     - external_ponding
	      //     = 0
	      // <=>
	      //     0.5 * slant * x_p^2
	      //     = integral - x0 * z0 + 0.5 * x0 * slant * x0
	      //     + external_ponding
	      // <=>
	      //     x_p^2 = 2 * (integral - x0 * z0 + external_ponding)
	      //             / slant + x0 * x0 
	      // <=>
	      //     x_p = sqrt (2.0 * (integral - x0 * z0 
	      //                        + external_ponding) / slant
	      //                 + x0 * x0)
	      x_pond = sqrt (2.0 * (integral - x0 * z0 + external_ponding) 
			     / slant + x0 * x0);
	      z_pond = z (x_pond);

	      // Check that we got the right result.
	      daisy_assert (x_pond > 0.0);
	      daisy_assert (x_pond < 1.0);
	      const double total = x_pond * z_pond;
	      const double this_step = 0.5 * (x_pond - x0) * (z0 + z_pond);
	      daisy_assert (approximate (total - (integral + this_step), 
				   external_ponding));
	      break;
	    }
	  
	  // Prepare next step
	  integral += this_step;
	  x0 = x1;
	  z0 = z1;
	}
    }
  internal_ponding = z_pond - lowest;

  if (external_ponding < 0.0)
    // Exfiltration
    {
      R_bottom = -42.42e42;
      I_bottom = external_ponding;
      // R_wall meaningless.
      I_wall = 0.0;
      R_wall = -42.42e42;
    }
  else
    {
      // Find maximal infiltration.
      const double Theta_sat = soil.Theta (0, 0.0, 0.0);
      const double available_space = (Theta_sat - Theta) * dz;
      daisy_assert (available_space > 0.0);
      const double I_max = std::min (external_ponding, 
                                     available_space - 1.0e-8) / dt;

      // Find resistance and infiltration for bottom regime.
      const double x_bottom = std::min (x_pond, x_switch);
      const double bottom_width = x_bottom - 0.0;
      if (bottom_width > 0.0)
	{
	  const double K_bottom = std::min (K, K_sat_below);
	  const double dz_bottom
	    = z.integrate (0.0, x_bottom) / bottom_width + dz;
#if 0
	  cerr << "dz_bottom = " << dz_bottom << ", x_bottom = " 
	       << x_bottom << ", bottom_width = " << bottom_width
	       << ", dz = " << dz << "\n";
#endif
	  R_bottom
	    = (x_width / bottom_width) * (dz_bottom /  K_bottom + R_crust);
	  I_bottom = std::min (internal_ponding / R_bottom, I_max);
	}
      else
	{
	  R_bottom = -42.42e42;
	  I_bottom = 0.0;
	}

      // Find resistance and infiltration for wall regime.
      if (z_pond > z_switch + 1e-5)
	{
	  const double wall_width = x_pond - x_switch;
	  daisy_assert (wall_width > 0.0);
	  const double dz_wall
	    = z.integrate (x_switch, x_pond) / wall_width + dz;
#if 0 
	  cerr << "dz_wall = " << dz_wall << ", wall_with = " 
	       << wall_width << "\n";
#endif
	  R_wall = (x_width / wall_width) * (dz_wall / K);
#if 0
	  cerr << "R_wall = " << R_wall << ", x_width = " <<x_width
	       << ", dz_wall = " <<dz_wall << ", K = " << K << "\n";
#endif
	  I_wall = std::min ((z_pond - z_switch) / R_wall, I_max - I_bottom);
	}
      else
	{
	  // R_wall meaningless.
	  I_wall = 0.0;
	  R_wall = -42.42e42;
	}
    }
  // Total infiltration.
  I = I_bottom + I_wall;
  daisy_assert (I < external_ponding + 1.0e-8);

#if 0
  cerr << "switch = (" << x_switch << ", " << z_switch << "), pond = ("
       << x_pond << ", " << z_pond << ") I = " << I 
       << " (bottom = " << I_bottom << ", wall = " << I_wall 
       << "), internal ponding = " << internal_ponding 
       << ",external ponding = " << external_ponding << "\n";
#endif

  // Update water.
  Theta = I * dt;
  for (int i = 0; i <= last_cell; i++)
    Theta += soil_water.Theta (i) * geo.dz (i);
  Theta /= dz;
  Theta_pre = Theta;
  daisy_assert (Theta < soil.Theta (0, 0.0, 0.0));
  h = soil.h (0, Theta);
}