Utopia  2
Framework for studying models of complex & adaptive systems.
Geomorphology.hh
Go to the documentation of this file.
1 #ifndef GEOMORPHOLOGY_HH
2 #define GEOMORPHOLOGY_HH
3 
4 #include <cmath>
5 #include <functional>
6 #include <random>
7 
8 #include <utopia/core/model.hh>
9 #include <utopia/core/apply.hh>
11 
12 namespace Utopia {
13 namespace Models {
14 namespace Geomorphology {
15 
19  double rock;
20 
22  double watercolumn;
23 
25  double waterline() const { return rock + watercolumn; };
26 
28  double drainage_area;
29 
32 
34  bool is_outflow;
35 
37 
41  template<class RNG>
43  const std::shared_ptr<RNG>& rng)
44  :
45  rock(0.), watercolumn(0.), drainage_area(1.), was_drained(false),
46  is_outflow(false)
47  {
48  std::normal_distribution<> init_height{
49  get_as<double>("initial_height_mean", cfg),
50  get_as<double>("initial_height_var", cfg)
51  };
52  rock = init_height(*rng);
53  }
54 };
55 
56 
58 
66 
69 
72  public Model<Geomorphology, GeomorphologyTypes>
73 {
74 public:
77 
79  using DataSet = typename Base::DataSet;
80 
83 
86 
89 
92 
95 
96 private:
97  // Base members: _time, _name, _cfg, _hdfgrp, _rng, _monitor
98 
99  // -- Members -------------------------------------------------------------
100 
103 
104 
105  // -- The boundary conditions (aka parameters) of the model
106 
108  std::normal_distribution<> _uplift;
109 
112 
115 
117 
122 
124 
132 
133  // A map of lowest neighbors
134  std::unordered_map<GeomorphologyCellIndexType,
135  std::shared_ptr<GeomorphologyCellType>> _lowest_neighbors;
136 
137 
138  const double _float_precision;
139 
141  std::uniform_real_distribution<> _prob_dist;
142 
143  // -- Datasets ------------------------------------------------------------
144  std::shared_ptr<DataSet> _dset_height;
145  std::shared_ptr<DataSet> _dset_drainage_area;
146  std::shared_ptr<DataSet> _dset_watercolumn;
147 
148 
149 public:
150  // -- Model Setup ---------------------------------------------------------
152 
160  template<class ParentModel>
162  const std::string& name,
163  ParentModel& parent_model,
164  const DataIO::Config& custom_cfg = {}
165  )
166  :
167  // Construct the base class
168  Base(name, parent_model, custom_cfg),
169 
170  // Initialize the cell manager, binding it to this model
171  _cm(*this),
172 
173  _uplift{get_as<double>("uplift_mean", this->_cfg),
174  get_as<double>("uplift_var", this->_cfg)},
175  _stream_power_coef(get_as<double>("stream_power_coef", this->_cfg)),
176  _toppling_frequency(get_as<double>("toppling_frequency", this->_cfg)),
177  _toppling_critical_height(get_as<double>("toppling_critical_height",
178  this->_cfg)),
180  get_as<double>("toppling_slope_reduction_factor", this->_cfg, 3.)),
181 
182  _float_precision(1e-10),
183  _prob_dist(0., 1.),
184 
185  // Create datasets using the helper functions for CellManager-data
186  _dset_height(this->create_cm_dset("height", _cm)),
187  _dset_drainage_area(this->create_cm_dset("drainage_area", _cm)),
188  _dset_watercolumn(this->create_cm_dset("watercolumn", _cm))
189  {
190  if (get_as<double>("uplift_var", this->_cfg) <= 1e-10) {
191  throw std::invalid_argument("Invalid argument: uplift_var must be "
192  "> _float_precision (1e-10)!");
193  }
194  // Update initial cell states
196 
197 
198  this->_log->debug("{} model fully set up.", this->_name);
199  }
200 
202 
207  void build_network () {
208  // reset network
209  apply_rule<Update::sync>(reset_network, _cm.cells());
210 
211  // connect cells to drainage network
212  apply_rule<Update::async, Shuffle::off>(connect_cells, _cm.cells());
213 
214  // fill sinks with water
215  apply_rule<Update::async, Shuffle::off>(build_lake, _cm.cells());
216 
217  // get drainage area
218  apply_rule<Update::async, Shuffle::off>(pass_drainage_area,
219  _cm.cells());
220  }
221 
223  void perform_step () {
224  // Erode
225  apply_rule<Update::sync>(erode, _cm.cells());
226 
227  // Uplift
228  apply_rule<Update::sync>(uplift_rule, _cm.cells());
229 
230  // Topple
231  apply_rule<Update::async, Shuffle::on>(toppling, _cm.cells(),
232  *this->_rng);
233 
234  // Build drainage network
235  build_network();
236  }
237 
239  void monitor () { return; }
240 
242 
246  void write_data () {
247  _dset_height->write(_cm.cells().begin(), _cm.cells().end(),
248  [](const auto& cell) { return cell->state.rock; }
249  );
250 
251  _dset_drainage_area->write(_cm.cells().begin(), _cm.cells().end(),
252  [](const auto& cell) { return cell->state.drainage_area; }
253  );
254 
255  _dset_watercolumn->write(_cm.cells().begin(), _cm.cells().end(),
256  [](const auto& cell) { return cell->state.watercolumn; }
257  );
258  }
259 
260 private:
261  // -- Helper functions ----------------------------------------------------
263  bool check_eq(const double& a, const double& b) {
264  return (a-b < _float_precision) && (b-a < _float_precision);
265  }
266 
268  template <typename Cell>
269  bool check_eq_waterline(const Cell& a, const Cell& b) {
270  return check_eq(a->state.waterline(), b->state.waterline());
271  }
272 
273  // -- Initialization function ---------------------------------------------
275 
282  this->_log->debug("Initializing cells ..");
283 
284  // Initialize altitude as an inclined plane
285  // (by making use of coordinates)
286  RuleFunc set_inclined_plane = [this](const auto& cell) {
287  auto state = cell->state;
288  auto pos = _cm.barycenter_of(cell);
289  double slope = get_as<double>("initial_slope",
290  this->_cfg["cell_manager"]["cell_params"]);
291  state.rock += slope*pos[1];
292  if (state.rock < _float_precision) {
293  std::uniform_real_distribution<> dist(0.,1e-5);
294  state.rock = dist(*this->_rng);
295  this->_log->warn("Received negative initial height. Was set "
296  "in [0.,1e-5]. Better chose the initial_height "
297  "distribution such that no negative values occur.");
298  }
299  return state;
300  };
301  apply_rule<Update::sync>(set_inclined_plane, _cm.cells());
302 
303  // set bottom cells as outflow
304  apply_rule<Update::sync>(
305  // The rule to apply
306  [](const auto& cell){
307  auto state = cell->state;
308  state.is_outflow = true;
309  return state;
310  },
311  // The containers over which to iterate
312  _cm.boundary_cells("bottom")
313  );
314 
315  // Build drainage network
316  this->_log->debug(" Initializing drainage network ...");
317  build_network();
318 
319  this->_log->debug("Cells fully set up.");
320  }
321 
322 
323  // -- Helper functions ----------------------------------------------------
324 
326  template <typename Cell>
328  GmorphCellContainer lowest_neighbors = {cell};
329  auto lowest_neighbor = cell;
330  for (const auto& n : this->_cm.neighbors_of(cell)) {
331  double height_diff = n->state.waterline() -
332  lowest_neighbor->state.waterline();
333 
334  if (check_eq_waterline(n, lowest_neighbor)) {
335  lowest_neighbors.push_back(n);
336  }
337 
338  // If neighbor is lower, update lowest_neighbor and list
339  else if (height_diff < 0) {
340  lowest_neighbor = n;
341  lowest_neighbors.clear();
342  lowest_neighbors.push_back(lowest_neighbor);
343  }
344  }
345 
346  // If there is more than one lowest neighbor, select one randomly.
347  if (lowest_neighbors.size() > 1) {
348  std::uniform_int_distribution<> dist(0, lowest_neighbors.size() -1);
349  lowest_neighbor = lowest_neighbors[dist(*(this->_rng))];
350  }
351  return lowest_neighbor;
352  }
353 
355 
365  GmorphCellContainer& shore)
366  {
367  auto it = std::begin(shore);
368  while(it != std::end(shore)) {
369  if (check_eq_waterline(*it, lake[0])) {
370  lake.push_back(*it);
371  auto nb = _cm.neighbors_of(*it);
372  for (auto it_lake : lake) {
373  nb.erase(std::remove(nb.begin(), nb.end(), it_lake),
374  nb.end());
375  }
376  for (auto it_shore : shore) {
377  nb.erase(std::remove(nb.begin(), nb.end(), it_shore),
378  nb.end());
379  }
380 
381  auto next_it = it;
382  auto next = *next_it;
383  if (next_it != shore.end())
384  next_it++;
385  if (next_it != shore.end())
386  next = *next_it;
387  it = shore.erase(it);
388  if (!nb.empty()) {
389  if (it == shore.end())
390  next = nb[0];
391  shore.insert(shore.end(), nb.begin(), nb.end());
392  it = std::find(shore.begin(), shore.end(), next);
393  }
394  }
395  else
396  ++it;
397  }
398 
399  return lake;
400  };
401 
402 
403  // -- Rule functions ------------------------------------------------------
405  RuleFunc uplift_rule = [this](const auto& cell) {
406  auto state = cell->state;
407  state.rock += this->_uplift(*(this->_rng));
408  return state;
409  };
410 
412  RuleFunc erode = [this](const auto& cell) {
413  auto state = cell->state;
414 
415  double slope = state.waterline();
416  if (not state.is_outflow) {
417  // slope = state->waterline - lowest_neighbor->waterline
418  slope -= _lowest_neighbors[cell->id()]->state.waterline();
419  }
420  // else: slope = state->waterline - 0.
421 
422  double stream_power = ( _stream_power_coef * slope
423  * std::sqrt(state.drainage_area));
424  state.rock -= std::min(stream_power, state.rock);
425 
426  return state;
427  };
428 
430 
436  RuleFunc toppling = [this](const auto& cell) {
437  auto state = cell->state;
438  if (_toppling_frequency == 0. or
439  _toppling_frequency < this->_prob_dist(*(this->_rng))
440  ) {
441  // Done here.
442  return state;
443  }
444 
445  auto nbrs = this->_cm.neighbors_of(cell);
446  auto heighest_neighbor = cell;
447  for (auto& nb : nbrs) {
448  if (nb->state.waterline() > heighest_neighbor->state.waterline())
449  {
450  heighest_neighbor = nb;
451  }
452  }
453  double relief = ( heighest_neighbor->state.waterline()
454  - state.waterline());
455  double failure_prob = relief / _toppling_critical_height;
456  if (this->_prob_dist(*(this->_rng)) < failure_prob) {
457  double factor = _toppling_slope_reduction_factor;
458  heighest_neighbor->state.rock -= relief / factor;
459  state.rock += relief / factor;
460  }
461 
462  return state;
463  };
464 
466  RuleFunc reset_network = [](const auto& cell) {
467  auto state = cell->state;
468  state.drainage_area = 1.;
469  state.was_drained = false;
470  state.watercolumn = 0.;
471  return state;
472  };
473 
475 
478  RuleFunc connect_cells = [this](const auto& cell) {
479  if (cell->state.is_outflow) {
480  _lowest_neighbors[cell->id()] = cell; // map to itself
481  return cell->state;
482  }
483 
484  // Set lowest neighbor for cell, is itself is sink
485  _lowest_neighbors[cell->id()] = this->_lowest_grid_neighbor(cell);
486 
487  return cell->state;
488  };
489 
491 
499  RuleFunc build_lake = [this](auto& cell) {
500  // return if cell has lower neighbor
501  if (cell->state.is_outflow or _lowest_neighbors[cell->id()] != cell) {
502  return cell->state;
503  }
504 
505  GmorphCellContainer lake = {cell};
506  GmorphCellContainer shore = this->_cm.neighbors_of(cell);
507  lake = update_lakesites(lake, shore);
508 
509  double waterline = lake[0]->state.waterline();
510  bool no_sink = true;
511 
512  // check for sink in new lake sites
513  for (const auto& lc : lake) {
514  if (lc->state.is_outflow) {
515  no_sink=false;
516  break;
517  }
518  }
519 
520  // lowest shore cell is candidate for outflow
521  auto lowest_shore_cell = shore[0];
522  for (const auto& sc : shore) {
523  if (sc->state.waterline() < lowest_shore_cell->state.waterline())
524  {
525  lowest_shore_cell = sc;
526  }
527  }
528 
529  // raise waterline while no outflow to lake
530  while (lowest_shore_cell->state.waterline() > waterline and no_sink)
531  {
532  // raise watercolumn to new waterline
533  waterline = lowest_shore_cell->state.waterline();
534  for (auto& lc : lake) {
535  lc->state.watercolumn = waterline - lc->state.rock;
536  }
537 
538  // update lake and shore
539  lake = update_lakesites(lake, shore);
540 
541  for (const auto& lc : lake) {
542  if (lc->state.is_outflow) {
543  no_sink=false;
544  break;
545  }
546  }
547 
548  // update lowest shore cell
549  lowest_shore_cell = shore[0];
550  for (const auto& sc : shore) {
551  if (sc->state.waterline() <
552  lowest_shore_cell->state.waterline())
553  {
554  lowest_shore_cell = sc;
555  }
556  }
557  }
558 
559  auto outflow_cell = lowest_shore_cell;
560  if (not no_sink) {
561  for (const auto& lc : lake) {
562  if (lc->state.is_outflow) {
563  outflow_cell = lc;
564  break;
565  }
566  }
567  }
568  else {
569  auto nbs = this->_cm.neighbors_of(lowest_shore_cell);
570  auto it_nb = nbs.begin();
571  while(it_nb != nbs.end()) {
572  if (not check_eq((*it_nb)->state.waterline(), waterline)) {
573  it_nb = nbs.erase(it_nb);
574  }
575  else if (lake.end() == std::find(lake.begin(), lake.end(),
576  *it_nb))
577  {
578  it_nb = nbs.erase(it_nb);
579  }
580  else { ++it_nb; }
581  }
582 
583  if (nbs.size() > 1) {
584  std::uniform_int_distribution<> dist(0, nbs.size() - 1);
585  outflow_cell = nbs[dist(*(this->_rng))];
586  }
587  else { outflow_cell = nbs[0]; }
588  }
589 
590  for (const auto& lc : lake) {
591  if (lc->state.is_outflow) {
592  _lowest_neighbors[lc->id()] = lc;
593  }
594  else {
595  _lowest_neighbors[lc->id()] = outflow_cell;
596  }
597  }
598  if (not outflow_cell->state.is_outflow) {
599  _lowest_neighbors[outflow_cell->id()] = lowest_shore_cell;
600  }
601 
602  return cell->state;
603  };
604 
606 
611  RuleFunc pass_drainage_area = [this](auto& cell) {
612  auto state = cell->state;
613  state.was_drained = true;
614 
615  if (not state.is_outflow and _lowest_neighbors[cell->id()] == cell) {
616  throw std::runtime_error("No recipient assigned to a cell!");
617  }
618  // nothing to do here
619  if (state.is_outflow) {
620  return state;
621  }
622 
623  // get downstream neighbor
624  auto downstream_cell = _lowest_neighbors[cell->id()];
625  downstream_cell->state.drainage_area += state.drainage_area;
626 
627  // pass drainage to all already drained cells
628  while(not downstream_cell->state.is_outflow and
629  downstream_cell->state.was_drained)
630  {
631  downstream_cell = _lowest_neighbors[downstream_cell->id()];
632  downstream_cell->state.drainage_area += state.drainage_area;
633  if (downstream_cell->state.drainage_area > size(this->_cm.cells()))
634  {
635  throw std::runtime_error("Drainage network has loop!");
636  }
637  }
638 
639  return state;
640  };
641 };
642 
643 
644 } // namespace Geomorphology
645 } // namespace Models
646 } // namespace Utopia
647 
648 #endif // GEOMORPHOLOGY_HH
A cell is a slightly specialized state container.
Definition: cell.hh:40
const CellContainer< Cell > & cells() const
Return const reference to the managed CA cells.
Definition: cell_manager.hh:219
typename std::function< CellState(const std::shared_ptr< Cell > &)> RuleFunc
The type of a rule function acting on cells of this cell manager.
Definition: cell_manager.hh:84
SpaceVec barycenter_of(const Cell &cell) const
Returns the barycenter of the given cell.
Definition: cell_manager.hh:249
CellContainer< Cell > neighbors_of(const Cell &cell) const
Retrieve the given cell's neighbors.
Definition: cell_manager.hh:458
Utopia::Cell< CellTraits > Cell
Type of the managed cells.
Definition: cell_manager.hh:47
CellContainer< Cell > boundary_cells(const std::string &select="all") const
Retrieve a container of cells that are at a specified boundary.
Definition: cell_manager.hh:323
Base class interface for Models using the CRT Pattern.
Definition: model.hh:112
std::shared_ptr< DataSet > create_cm_dset(const std::string name, const CellManager &cm, const std::size_t compression_level=1, const std::vector< hsize_t > chunksize={})
Create a dataset storing data from a CellManager.
Definition: model.hh:849
typename ModelTypes::DataSet DataSet
Data type that is used for storing data.
Definition: model.hh:125
const Config _cfg
Config node belonging to this model instance.
Definition: model.hh:158
const std::string _name
Name of the model instance.
Definition: model.hh:149
const std::shared_ptr< spdlog::logger > _log
The (model) logger.
Definition: model.hh:164
const std::shared_ptr< RNG > _rng
The RNG shared between models.
Definition: model.hh:161
A very simple geomorphology model.
Definition: Geomorphology.hh:73
RuleFunc toppling
The rule how to topple / landslide.
Definition: Geomorphology.hh:436
double _toppling_critical_height
The critical height difference to trigget a toppling event.
Definition: Geomorphology.hh:121
bool check_eq_waterline(const Cell &a, const Cell &b)
Compare the waterline of two cells.
Definition: Geomorphology.hh:269
double _stream_power_coef
The stream power coefficient.
Definition: Geomorphology.hh:111
void monitor()
Provide monitoring data: tree density and number of clusters.
Definition: Geomorphology.hh:239
RuleFunc uplift_rule
The rule how to uplift.
Definition: Geomorphology.hh:405
void write_data()
Write the cell states (aka water content)
Definition: Geomorphology.hh:246
typename GeomorphologyCellManager::RuleFunc RuleFunc
Rule function type, extracted from CellManager.
Definition: Geomorphology.hh:94
GmorphCellContainer update_lakesites(GmorphCellContainer &lake, GmorphCellContainer &shore)
Update the CellContainer of lake and shore clls.
Definition: Geomorphology.hh:364
void perform_step()
Perform step.
Definition: Geomorphology.hh:223
RuleFunc erode
The rule how to erode with stream power.
Definition: Geomorphology.hh:412
std::normal_distribution _uplift
The random uplift as normal distribution.
Definition: Geomorphology.hh:108
RuleFunc build_lake
Fill a sink with water.
Definition: Geomorphology.hh:499
std::shared_ptr< DataSet > _dset_watercolumn
Dataset of drainage area.
Definition: Geomorphology.hh:146
typename Base::DataSet DataSet
Data type for a dataset.
Definition: Geomorphology.hh:79
std::shared_ptr< DataSet > _dset_drainage_area
Dataset of rock height.
Definition: Geomorphology.hh:145
GeomorphologyCellManager::Cell GeomorphologyCellType
A cell in the geomorphology model.
Definition: Geomorphology.hh:85
RuleFunc reset_network
The rule to reset the drainage network.
Definition: Geomorphology.hh:466
double _toppling_slope_reduction_factor
The factor by which to reduce the slope in a toppling event.
Definition: Geomorphology.hh:131
Utopia::IndexType GeomorphologyCellIndexType
The index type of the Geomorphology cell manager.
Definition: Geomorphology.hh:88
void build_network()
The set of seperately applied rules to build the drainage network.
Definition: Geomorphology.hh:207
double _toppling_frequency
The frequency of possible toppling events per cell.
Definition: Geomorphology.hh:114
std::uniform_real_distribution _prob_dist
precision when comparing floats
Definition: Geomorphology.hh:141
Geomorphology(const std::string &name, ParentModel &parent_model, const DataIO::Config &custom_cfg={})
Dataset of watercolumn.
Definition: Geomorphology.hh:161
void _initialize_cells()
The initialization of the cells.
Definition: Geomorphology.hh:281
std::shared_ptr< DataSet > _dset_height
Definition: Geomorphology.hh:144
RuleFunc pass_drainage_area
Make a drainage process from this cell.
Definition: Geomorphology.hh:611
const double _float_precision
Definition: Geomorphology.hh:138
Cell _lowest_grid_neighbor(const Cell &cell)
Return the lowest cell of the grid-Neighborhood, including cell itself.
Definition: Geomorphology.hh:327
Utopia::CellContainer< GeomorphologyCellType > GmorphCellContainer
The type of a cell container in the Geomorphology model.
Definition: Geomorphology.hh:91
GeomorphologyCellManager _cm
The cell manager for the forest fire model.
Definition: Geomorphology.hh:102
RuleFunc connect_cells
Build a rudimentary network.
Definition: Geomorphology.hh:478
bool check_eq(const double &a, const double &b)
Compare two floats for equality.
Definition: Geomorphology.hh:263
std::unordered_map< GeomorphologyCellIndexType, std::shared_ptr< GeomorphologyCellType > > _lowest_neighbors
Definition: Geomorphology.hh:135
Model< Geomorphology, GeomorphologyTypes > Base
The base model type.
Definition: Geomorphology.hh:76
YAML::Node Config
Type of a variadic dictionary-like data structure used throughout Utopia.
Definition: types.hh:71
auto end(zip< Containers... > &zipper)
end function like std::end
Definition: zip.hh:550
auto begin(zip< Containers... > &zipper)
Begin function like std::begin.
Definition: zip.hh:537
std::mt19937 rng
– Type definitions ----------------------------------------------------—
Definition: test_revision.cc:17
Definition: agent.hh:11
EntityContainer< CellType > CellContainer
Type of the variably sized container for cells.
Definition: types.hh:26
std::size_t IndexType
Type for indices, i.e. values used for container indexing, agent IDs, ...
Definition: types.hh:40
The entity traits struct gathers types to be used for specializing an entity.
Definition: entity.hh:49
Wrapper struct for defining model class data types.
Definition: model.hh:92
The full cell struct for the Geomorphology model.
Definition: Geomorphology.hh:17
bool was_drained
Was called for drainage area calculation.
Definition: Geomorphology.hh:31
double rock
The cells topgraphic height.
Definition: Geomorphology.hh:19
double watercolumn
The cells watercontent.
Definition: Geomorphology.hh:22
bool is_outflow
Whether the cell is an outflow boundary cell.
Definition: Geomorphology.hh:34
double drainage_area
The drainage area.
Definition: Geomorphology.hh:25
GeomorphologyCell(const DataIO::Config &cfg, const std::shared_ptr< RNG > &rng)
Construct a cell from a configuration node and an RNG.
Definition: Geomorphology.hh:42
double waterline() const
The height of the waterline.
Definition: Geomorphology.hh:25