Пример #1
0
void
Ridge::Implementation::update_water (const Geometry1D& geo,
                                     const Soil& soil,
				     const std::vector<double>& S_,
				     std::vector<double>& h_,
				     std::vector<double>& Theta_,
				     std::vector<double>& q,
				     const std::vector<double>& q_p,
                                     const double dt)
{
  const double E = -(q[last_cell + 1] + q_p[last_cell + 1]);
  Theta = (I - E) * dt;
  for (int i = 0; i <= last_cell; i++)
    Theta += (Theta_[i] - S_[i] * dt) * geo.dz (i);
  Theta /= dz;
  const double Theta_sat = soil.Theta (0, 0.0, 0.0);
  daisy_assert (Theta < Theta_sat);
  h = soil.h (0, Theta);

  q[0] = -I;
  for (int i = 0; i <= last_cell; i++)
    {
      q[i+1] = q[i] + geo.dz (i) * (S_[i] + (Theta - Theta_[i]) / dt ) 
	- q_p[i+1];
      Theta_[i] = Theta;
      h_[i] = h;
      if (!approximate (soil.h (i, Theta), h))
	{
	  daisy_assert (i > 0);
	  throw ("Soil hydraulic paramteres change in ridge area");
	}
    }
  daisy_assert (approximate (E, -(q[last_cell + 1] + q_p[last_cell + 1])));
}
Пример #2
0
double
SoilWater::overflow (const Geometry& geo, 
                     const Soil& soil, const SoilHeat& soil_heat, 
                     Treelog& msg)
{
  const size_t cell_size = geo.cell_size ();
  double extra = 0.0;           // [cm^3]
  for (size_t c = 0; c < cell_size; c++)
    {
      const double h_sat = 0.0;
      const double Theta_sat = soil.Theta (c, h_sat, h_ice (c));
      const double Theta_extra = std::max (Theta_[c] - Theta_sat, 0.0);
      Theta_[c] -= Theta_extra;
      tillage_[c] -= Theta_extra;
      extra += Theta_extra * geo.cell_volume (c);
      const double new_h = soil.h (c, Theta_[c]);
      if (h_[c] < 0.0 || new_h < 0)
        h_[c] = new_h;
    }
  tick_after (geo, soil, soil_heat, false, msg);

  extra /= geo.surface_area (); // [cm]
  extra *= 10.0;                // [mm]
  return extra;
}
Пример #3
0
void 
SoilWater::tick_source (const Geometry& geo, const Soil& soil, Treelog& msg)
{
  const size_t cell_size = geo.cell_size ();

  sink_dt = 0.0;
  sink_cell = Geometry::cell_error;
  for (size_t c = 0; c < cell_size; c++)
    {
      const double S = S_forward_total (c);
      if (!std::isnormal (S))
        continue;
      static const double h_wp = -15000.0;
      static const double h_sat = 0.0;
      const double h_ice = this->h_ice (c);
      const double Theta_wp = soil.Theta (c, h_wp, h_ice);
      const double Theta_sat = soil.Theta (c, h_sat, h_ice);
      const double Theta_max = Theta_sat - Theta_wp;
      if (Theta_max < 0.01)
        // Mostly ice?
        continue;
      const double dt = Theta_max * max_sink_change / std::fabs (S);
      if (std::isnormal (dt)
          && (!std::isnormal (sink_dt) || std::fabs (sink_dt) > std::fabs (dt)))
        {
          sink_dt = dt;
          sink_cell = c;
        }
    }
}
Пример #4
0
std::vector<double> 
Movement1D::default_heat (const Soil& soil, 
                          const Time& time, const Weather& weather)
{
  // Fetch average temperatur.
  const double rad_per_day = 2.0 * M_PI / 365.0;

  // Calculate delay.
  const double pF_2_0 = -100.0;
  double k = 0;
  double C = 0;

  std::vector<double> T;
  const size_t cell_size = geo->cell_size ();

  for (unsigned int i = 0; i < cell_size; i++)
    {
      const double Theta_pF_2_0 = soil.Theta (i, pF_2_0, 0.0);
      k += geo->dz (i) * soil.heat_conductivity (i, Theta_pF_2_0, 0.0);
      C += geo->dz (i) * soil.heat_capacity (i, Theta_pF_2_0, 0.0);
      const double a = k / C;
      delay = geo->zplus (i) / sqrt (24.0 * 2.0 * a / rad_per_day);

      T.push_back (bottom_heat (time, weather));
    }
  daisy_assert (T.size () == cell_size);
  return T;
}
Пример #5
0
double 
UZRectMollerup::find_K_edge (const Soil& soil, const Geometry& geo, 
                             const size_t e,
                             const ublas::vector<double>& h, 
                             const ublas::vector<double>& h_ice, 
                             const ublas::vector<double>& h_old, 
                             const ublas::vector<double>& T) const
{
  const double anisotropy = soil.anisotropy_edge (e);
  const int from = geo.edge_from (e);
  const int to = geo.edge_to (e);

  // External edges.
  if (!geo.cell_is_internal (from))
    return soil.K (to, h (to), h_ice (to), T (to)) * anisotropy;

  if (!geo.cell_is_internal (to))
    return soil.K (from, h (from), h_ice (from), T (from)) * anisotropy;
  
  // Internal edges.
  const double K_from = soil.K (from, h (from), h_ice (from), T (from));
  const double K_to = soil.K (to, h (to), h_ice (to), T (to));
  return  K_average->average (soil, geo, e, 
                              K_from, h (from), h_ice (from), h_old (from), T (from),
                              K_to, h (to), h_ice (to), h_old (from), T (to)) * anisotropy;
}
Пример #6
0
double
DrainLateral::K_to_pipes (const unsigned int i, 
                            const Soil& soil, 
                            const SoilHeat& soil_heat) const
{
  if (K_to_pipes_ < 0)
    return soil.K (i, 0.0, 0.0, soil_heat.T (i))
      * soil.anisotropy_cell (i);
  return K_to_pipes_;
}
Пример #7
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);
}
Пример #8
0
double
SoilWater::MaxExfiltration (const Geometry& geo, const size_t edge,
                            const Soil& soil, const double T) const
{
  const size_t n = geo.edge_other (edge, Geometry::cell_above);
  const double h0 = h (n);
  const double K0 = soil.K (n, h0, h_ice (n), T);
  if (max_exfiltration_gradient > 0.0)
    return K0 * max_exfiltration_gradient;
  const double Cw2 = soil.Cw2 (n, h0);
  const double Theta0 = Theta (n);
  const double Theta_surf = soil.Theta_res (n);
  const double delta_Theta = Theta0 - Theta_surf;
  const double z0 = geo.cell_z (n);
  // Darcy formulated for Theta between middle of node and soil surface.
  return - (K0 / Cw2) * (delta_Theta / z0);
}
Пример #9
0
void
SoilWater::tick_ice (const Geometry& geo, const Soil& soil, 
                     const double dt, Treelog& msg)
{
  TREELOG_SUBMODEL (msg, "SoilWater");

  const size_t cell_size = geo.cell_size ();

  // Ice first.
  for (size_t i = 0; i < cell_size; i++)
    {
      const double porosity = soil.Theta (i, 0.0, 0.0);
      const double Theta_res = soil.Theta_res (i);

      X_ice_[i] -= S_ice_ice[i] * dt;

      // Move extra ice to buffer.
      const double total_ice = X_ice_[i] + X_ice_buffer_[i];
      if (total_ice > 0.0)
        {
          if (Theta_[i] < Theta_res)
            {
              std::ostringstream tmp;
              tmp << "Theta[" << i << "] = " << Theta_[i]
                  << ", less than Theta_res = " << Theta_res;
              daisy_warning (tmp.str ());
            }
          const double Theta_lim = std::max (Theta_res, 
                                             Theta_[i] - S_ice_water_[i] * dt);

          const double available_space = porosity - Theta_lim - 1e-9;
          X_ice_[i] = std::min (available_space, total_ice);
          X_ice_buffer_[i] = total_ice - X_ice_[i];
        }
      else
        {
          X_ice_[i] = 0.0;
          X_ice_buffer_[i] = total_ice;
        }
      daisy_approximate (X_ice_[i] + X_ice_buffer_[i], total_ice);

      // Update ice pressure.
      h_ice_[i] = soil.h (i, porosity - X_ice_[i]);
    }
}
Пример #10
0
void
ReactionNitrification::initialize (const Units&, const Geometry&,
                                   const Soil& soil, 
                                   const SoilWater&, const SoilHeat&,
                                   const Surface&, Treelog&)
{
  const size_t cell_size = soil.size ();

  NH4 = std::vector<double> (cell_size, 0.0);
  NO3 = std::vector<double> (cell_size, 0.0);
  N2O = std::vector<double> (cell_size, 0.0);
}
Пример #11
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);
}
Пример #12
0
void
ReactionDenit::initialize (const Units&, const Geometry&,
                           const Soil& soil, const SoilWater&,
                           const SoilHeat&, const Surface&, Treelog&)
{
  const size_t cell_size = soil.size ();

  converted.insert (converted.begin (), cell_size, 0.0);
  converted_fast.insert (converted_fast.begin (), cell_size, 0.0);
  converted_redox.insert (converted_redox.begin (), cell_size, 0.0);
  potential.insert (potential.begin (), cell_size, 0.0);
  potential_fast.insert (potential_fast.begin (), cell_size, 0.0);
}
Пример #13
0
void
ClayOMBiomod::set_rates (const Soil& soil, const std::vector<SMB*>& smb) const
{ 
  // We always have two SMB pools in BIOMOD.
  daisy_assert (smb.size () == 2);

  // Total death and maintenance.
  const double t_SMB1 = smb[0]->turnover_rate + smb[0]->maintenance;
  const double t_SMB2 = smb[1]->turnover_rate + smb[1]->maintenance;

  // The ratios we want to change.
  const double r_SMB1 = smb[0]->turnover_rate / t_SMB1;
  const double r_SMB2 = smb[1]->turnover_rate / t_SMB2;

  for (unsigned int i  = 0; i < soil.size (); i++)
    {
      // Find modifier.
      const double f = find_f (r_SMB1, r_SMB2, soil.clay (i));
      daisy_assert (f > 0.0);

      // Update turnover rate and maintence.
      const double clay_rate1 = smb[0]->turnover_rate * f;
      daisy_assert (clay_rate1 < 1.0);
      smb[0]->clay_turnover.push_back (clay_rate1);
      daisy_assert (t_SMB1 >= clay_rate1);
      smb[0]->clay_maintenance.push_back (t_SMB1 - clay_rate1);
      const double clay_rate2 = smb[1]->turnover_rate * f;
      daisy_assert (clay_rate2 < 1.0);
      smb[1]->clay_turnover.push_back (clay_rate2);
      daisy_assert (t_SMB2 >= clay_rate2);
      smb[1]->clay_maintenance.push_back (t_SMB2 - clay_rate2);
    }

  for (unsigned int i = 0; i < smb.size (); i++)
    {
      daisy_assert (smb[i]->clay_turnover.size () == soil.size ());
      daisy_assert (smb[i]->clay_maintenance.size () == soil.size ());
    }
}
Пример #14
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;
}
Пример #15
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.
}
Пример #16
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_;
}
Пример #17
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);
}
Пример #18
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];
        }
    }
}
Пример #19
0
void* Creator::create(const string& value, int cidx, int ridx) const
{
	if (!has(value)) return NULL;
	// Shell retorna tudo com lowercase
	if (isEqual(value,"soilsoft"))
	{
		Soil* b = new Soil(cidx,ridx);
		b->setSoilType(stSoft);
		return b;
	}
	else if (isEqual(value,"soilhard"))
	{
		Soil* b = new Soil(cidx,ridx);
		b->setSoilType(stHard);
		return b;
	}
	else if (isEqual(value,"rock"))
	{
		return new Rock(cidx,ridx);
	}
	else if (isEqual(value,"aluminum"))
	{
		return new Aluminum(cidx,ridx);
	}
	else if (isEqual(value,"coal"))
	{
		return new Coal(cidx,ridx);
	}
	else if (isEqual(value,"diamond"))
	{
		return new Diamond(cidx,ridx);
	}
	else if (isEqual(value,"gold"))
	{
		return new Gold(cidx,ridx);
	}
	else if (isEqual(value,"iron"))
	{
		return new Iron(cidx,ridx);
	}
	// Ferramentas
	// Picaretas
	else if (isEqual(value,"pickernormal"))
	{
		return new Picker(PICKERNORMAL);
	}
	else if (isEqual(value,"pickerpro"))
	{
		return new Picker(PICKERPRO);
	}
	else if (isEqual(value,"pickermaster"))
	{
		return new Picker(PICKERMASTER);
	}
	// Mochilas
	else if (isEqual(value,"bagnormal"))
	{
		return new Bag(BAGNORMAL);
	}
	else if (isEqual(value,"bagpro"))
	{
		return new Bag(BAGPRO);
	}
	else if (isEqual(value,"bagmaster"))
	{
		return new Bag(BAGMASTER);
	}
	// Iluminacao
	else if (isEqual(value,"lighter"))
	{
		return new Light(LIGHTNORMAL);
	}
	else if (isEqual(value,"flashlight"))
	{
		return new Light(LIGHTPRO);
	}
	else if (isEqual(value,"spotlight"))
	{
		return new Light(LIGHTMASTER);
	}
	// Outros
	else if (isEqual(value,"ladder"))
	{
		return new Ladder(cidx,ridx);
	}
	else if (isEqual(value,"beam"))
	{
		return new Beam(cidx,ridx);
	}
	else if (isEqual(value,"food"))
	{
		return new Food(cidx,ridx);
	}
	else if (isEqual(value,"extralife"))
	{
		return new Extralife(cidx,ridx);
	}
	else if (isEqual(value,"dinamite"))
	{
		return new Dinamite(cidx,ridx);
	}
	else
		return NULL;
}
Пример #20
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"; 
}
Пример #21
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");
	}
    }
}
Пример #22
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);
}
Пример #23
0
double
SoilWater::Theta_ice (const Soil& soil, const size_t i, const double h) const
{ return soil.Theta (i, h, h_ice (i)); }
Пример #24
0
static DeclareSubmodel 
soil_layer_submodel (Soil::Implementation::Layer::load_syntax, "SoilLayer", "\
A location and content of a soil layer.\n\
The layers apply to the soil section not covered by the 'zones' parameter.");

static DeclareSubmodel 
soil_zone_submodel (Soil::Implementation::Region::load_syntax, "SoilRegion", "\
A location and content of a soil zone.\n\
If several zones cover the same soil, the first one listed is used.\n\
If no zones cover the soil, the 'horizons' parameter is used.\n\
\n\
With regard to the numeric discretization, the whole cell is assumed to\n\
be of the soil found in the cell center.");

size_t 
Soil::size () const
{ return impl->horizon_.size (); }

const Horizon& 
Soil::horizon (size_t i) const
{ return *impl->horizon_[i]; }

double 
Soil::K (size_t i, double h, double h_ice, double T) const
{ 
  static struct ViscosityFactor : public PLF
  {
    ViscosityFactor ()
    {
      const double v20 = Water::viscosity (20.0);
      add ( 0.0, v20 / Water::viscosity (0.0));