Utopia 2
Framework for studying models of complex & adaptive systems.
Loading...
Searching...
No Matches
ForestFire.hh
Go to the documentation of this file.
1#ifndef UTOPIA_MODELS_FORESTFIRE_HH
2#define UTOPIA_MODELS_FORESTFIRE_HH
3
4#include <functional>
5#include <random>
6#include <numeric>
7
11
12#include "../ContDisease/state.hh"
13
14namespace Utopia {
15namespace Models {
16namespace ForestFire {
17
18// ++ Type definitions ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
19
21
25
26
28struct State {
30 Kind kind;
31
33 unsigned short age;
34
36 unsigned int cluster_id;
37
39 State () = delete;
40
42 template<class RNG>
43 State (const DataIO::Config& cfg, const std::shared_ptr<RNG>& rng)
44 :
45 kind(Kind::empty),
46 age(0),
47 cluster_id(0)
48 {
49 // Get the desired probability to be a tree
50 const auto p_tree = get_as<double>("p_tree", cfg);
51
52 // Check obvious cases (no need to draw a random number)
54 throw std::invalid_argument("p_tree needs to be in interval "
55 "[0., 1.], but was not!");
56 }
57 else if (p_tree == 0.) {
58 return;
59 }
60 else if (p_tree == 1.) {
61 kind = Kind::tree;
62 return;
63 }
64
65 // With this probability, the cell state is a tree
66 if (std::uniform_real_distribution<double>(0., 1.)(*rng) < p_tree) {
67 kind = Kind::tree;
68 }
69 // NOTE Although the distribution object is created each time, this
70 // is not a significant slow-down compared to re-using an
71 // existing distribution object (<4%). When compiled with
72 // optimization flags, that slowdown is even smaller...
73 }
74};
75
76
78
86
87
89struct Param {
91 const double p_growth;
92
94 const double p_lightning;
95
97 const double p_immunity;
98
101 :
102 p_growth(get_as<double>("p_growth", cfg)),
103 p_lightning(get_as<double>("p_lightning", cfg)),
104 p_immunity(get_as<double>("p_immunity", cfg))
105 {
106 if ((p_growth > 1.) or (p_growth < 0.)) {
107 throw std::invalid_argument("Invalid p_growth! Need be a value "
108 "in range [0, 1] and specify the probability per time step "
109 "and cell with which an empty cell turns into a tree.");
110 }
111 if ((p_lightning > 1.) or (p_lightning < 0.)) {
112 throw std::invalid_argument("Invalid p_lightning! Need be "
113 "in range [0, 1] and specify the probability per cell and "
114 "time step for lightning to strike.");
115 }
116 if ((p_immunity > 1.) or (p_immunity < 0.)) {
117 throw std::invalid_argument("Invalid p_immunity! "
118 "Need be a value in range [0, 1] and specify the probability "
119 "per neighbor with which that neighbor is immune to fire.");
120 }
121 }
122};
123
124
127
128
129// ++ Model definition ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
130
132
137 public Model<ForestFire, ModelTypes>
138{
139public:
142
144 using DataSet = typename Base::DataSet;
145
148
150 using Cell = typename CellManager::Cell;
151
154
155
156private:
157 // Base members: _time, _name, _cfg, _hdfgrp, _rng, _monitor, _log, _space
158 // ... but you should definitely check out the documentation ;)
159
160 // -- Members -------------------------------------------------------------
163
166
168 std::uniform_real_distribution<double> _prob_distr;
169
171 unsigned int _cluster_id_cnt;
172
174 std::vector<std::shared_ptr<CellManager::Cell>> _cluster_members;
175
176 // .. Output-related ......................................................
179
181 const std::shared_ptr<DataSet> _dset_kind;
182
184 const std::shared_ptr<DataSet> _dset_age;
185
187 const std::shared_ptr<DataSet> _dset_cluster_id;
188
190 const std::shared_ptr<DataSet> _dset_tree_density;
191
192
193public:
194 // -- Model Setup ---------------------------------------------------------
196
204 template<class ParentModel>
206 const std::string& name,
208 const DataIO::Config& custom_cfg = {}
209 )
210 :
211 // Initialize first via base model
213
214 // Initialize the cell manager, binding it to this model
215 _cm(*this),
216
217 // Carry over parameters
218 _param(this->_cfg),
219
220 // Initialize remaining members
221 _prob_distr(0., 1.),
224 _write_only_tree_density(get_as<bool>("write_only_tree_density",
225 this->_cfg)),
226
227 // Create datasets using the helper functions for CellManager-data
230 _dset_cluster_id{this->create_cm_dset("cluster_id", _cm)},
231 _dset_tree_density{this->create_dset("tree_density", {})}
232 {
233 // Cells are already set up in the CellManager.
234 // Take care of the heterogeneities now:
235
236 // Stones
237 if (_cfg["stones"] and get_as<bool>("enabled", _cfg["stones"])) {
238 this->_log->info("Setting cells to be stones ...");
239
240 // Get the container
241 auto to_turn_to_stone = _cm.select_cells(_cfg["stones"]);
242
243 // Apply a rule to all cells of that container: turn to stone
245 [](const auto& cell){
246 auto& state = cell->state;
247 state.kind = Kind::stone;
248 return state;
249 },
251 );
252
253 this->_log->info("Set {} cells to be stones using selection mode "
254 "'{}'.", to_turn_to_stone.size(),
255 get_as<std::string>("mode", _cfg["stones"]));
256 }
257
258 // Ignite some cells permanently: fire sources
259 if ( _cfg["ignite_permanently"]
260 and get_as<bool>("enabled", _cfg["ignite_permanently"]))
261 {
262 this->_log->info("Setting cells to be permanently ignited ...");
263 auto to_be_ignited = _cm.select_cells(_cfg["ignite_permanently"]);
264
266 [](const auto& cell){
267 auto& state = cell->state;
268 state.kind = Kind::source;
269 return state;
270 },
272 );
273
274 this->_log->info("Set {} cells to be permanently ignited using "
275 "selection mode '{}'.", to_be_ignited.size(),
276 get_as<std::string>("mode", _cfg["ignite_permanently"]));
277 }
278
279 this->_log->debug("{} model fully set up.", this->_name);
280 }
281
282
283private:
284 // .. Setup functions .....................................................
285
286 // .. Helper functions ....................................................
288 double calculate_tree_density() const {
289 // NOTE If execution policies are implemented, this could be easily
290 // made parallel by replacing std::accumulate with std::reduce
291 // and adding std::execution::par from the header
292 // <execution> as first argument.
293 return
294 std::accumulate(_cm.cells().begin(), _cm.cells().end(),
295 0,
296 [&](std::size_t sum, const std::shared_ptr<Cell>& cell) {
297 return sum + (cell->state.kind == Kind::tree);
298 }
299 )
300 / static_cast<double>(_cm.cells().size());
301 }
302
304
309 unsigned int identify_clusters() {
310 this->_log->debug("Identifying clusters...");
311
312 // Reset counter for cluster IDs, then call the identification function
313 _cluster_id_cnt = 0;
314
316 _identify_cluster, _cm.cells()
317 );
318
319 this->_log->debug("Identified {} clusters.", _cluster_id_cnt);
320
321 return _cluster_id_cnt;
322 }
323
324 // .. Rule functions ......................................................
326
341 RuleFunc _update = [this](const auto& cell){
342 // Get the current state of the cell and reset the cluster tag
343 auto& state = cell->state;
344 state.cluster_id = 0;
345
346 // Empty cells can grow a tree
347 if (state.kind == Kind::empty) {
348 if (this->_prob_distr(*this->_rng) < _param.p_growth) {
349 state.kind = Kind::tree;
350 }
351 }
352
353 // Trees can be hit by lightning or continue living
354 else if (state.kind == Kind::tree) {
355 // Can be hit by lightning
356 if (this->_prob_distr(*this->_rng) < _param.p_lightning) {
357 state = _burn_cluster(cell);
358 }
359 else {
360 // Lives. Increase its age
361 state.age++;
362 }
363 }
364
365 // Permanently ignited cells always burn the cluster
366 else if (state.kind == Kind::source) {
367 state = _burn_cluster(cell);
368 }
369
370 // Stones don't do anything
371 else if (state.kind == Kind::stone) {
372 // Not doing anything, like the good stone I am ...
373 }
374
375 else {
376 // Should never occur!
377 throw std::invalid_argument("Invalid cell state!");
378 }
379
380 return state;
381 };
382
384
387 RuleFunc _burn_cluster = [this](const auto& cell) {
388 // A tree cell should burn, i.e.: transition to empty.
389 if (cell->state.kind == Kind::tree) {
390 cell->state.kind = Kind::empty;
391 }
392 // The only other possibility would be a fire source: remains alight!
393
394 // Use existing cluster member container, clear it, add current cell
395 auto& cluster = _cluster_members;
396 cluster.clear();
397 cluster.push_back(cell);
398
399 // Recursively go over all cluster members
400 for (unsigned int i = 0; i < cluster.size(); ++i) {
401 const auto& cluster_member = cluster[i];
402
403 // Iterate over all potential cluster members
404 for (const auto& c : this->_cm.neighbors_of(cluster_member)) {
405 // If it is a tree, it will burn ...
406 if (c->state.kind == Kind::tree) {
407 // ... unless there is p_immunity > 0 ...
408 if (this->_param.p_immunity > 0.) {
409 // ... where there is a chance not to burn:
410 if ( this->_prob_distr(*this->_rng)
411 < _param.p_immunity) {
412 continue;
413 }
414 }
415
416 // Bad luck. Burn.
417 c->state.kind = Kind::empty;
418 c->state.age = 0;
419 cluster.push_back(c);
420 // This extends the outer for-loop
421 }
422 }
423 }
424
425 // Return the current cell's adjusted state.
426 return cell->state;
427 };
428
430 /* Runs a percolation on a cell, that has ID 0. Then, give all cells of
431 * that percolation the same ID. The ``_cluster_id_cnt`` member keeps
432 * track of already given IDs.
433 * Both a cell's cluster ID and the cluster ID counter are reset as part of
434 * a regular iteration step.
435 */
436 RuleFunc _identify_cluster = [this](const auto& cell){
437 // Only need to continue if a tree and not already labelled
438 if ( cell->state.cluster_id != 0
439 or cell->state.kind != Kind::tree)
440 {
441 return cell->state;
442 }
443 // else: is an unlabelled tree; need to label it.
444
445 // Increment the cluster ID counter and label the given cell
446 _cluster_id_cnt++;
447 cell->state.cluster_id = _cluster_id_cnt;
448
449 // Use existing cluster member container, clear it, add current cell
450 auto& cluster = _cluster_members;
451 cluster.clear();
452 cluster.push_back(cell);
453
454 // Perform the percolation
455 for (unsigned int i = 0; i < cluster.size(); ++i) {
456 // Iterate over all potential cluster members c, i.e. all
457 // neighbors of cell cluster[i] that is already in the cluster
458 for (const auto& c : this->_cm.neighbors_of(cluster[i])) {
459 // If it is a tree that is not yet in the cluster, add it.
460 if ( c->state.cluster_id == 0
461 and c->state.kind == Kind::tree)
462 {
463 c->state.cluster_id = _cluster_id_cnt;
464 cluster.push_back(c);
465 // This extends the outer for-loop...
466 }
467 }
468 }
469
470 return cell->state;
471 };
472
473
474public:
475 // -- Public Interface ----------------------------------------------------
476 // .. Simulation Control ..................................................
477
479 void perform_step () {
480 // Apply update rule on all cells, asynchronously and shuffled
482 _update, _cm.cells(), *this->_rng
483 );
484 }
485
487 void monitor () {
488 this->_monitor.set_entry("tree_density", calculate_tree_density());
489 }
490
492 void write_data () {
493 // Calculate and write the tree density
494 _dset_tree_density->write(calculate_tree_density());
495
496 if (_write_only_tree_density) {
497 // Done here.
498 return;
499 }
500
501 // Store all cells' kind
502 _dset_kind->write(_cm.cells().begin(), _cm.cells().end(),
503 [](const auto& cell) {
504 return static_cast<char>(cell->state.kind);
505 });
506
507 // ... and age
508 _dset_age->write(_cm.cells().begin(), _cm.cells().end(),
509 [](const auto& cell) {
510 return cell->state.age;
511 });
512
513 // Identify the clusters (only needed when actually writing)
514 identify_clusters();
515 _dset_cluster_id->write(_cm.cells().begin(), _cm.cells().end(),
516 [](const auto& cell) {
517 return cell->state.cluster_id;
518 });
519 }
520};
521
522} // namespace ForestFire
523} // namespace Models
524} // namespace Utopia
525
526#endif // UTOPIA_MODELS_FORESTFIRE_HH
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
Utopia::Cell< CellTraits > Cell
Type of the managed cells.
Definition cell_manager.hh:47
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
std::shared_ptr< DataSet > create_dset(const std::string name, const std::shared_ptr< DataGroup > &hdfgrp, std::vector< hsize_t > add_write_shape, const std::size_t compression_level=1, const std::vector< hsize_t > chunksize={})
Create a new dataset within the given group.
Definition model.hh:752
The ForestFire model.
Definition ForestFire.hh:138
void perform_step()
Perform step.
Definition ForestFire.hh:479
typename Base::DataSet DataSet
Data type for a dataset.
Definition ForestFire.hh:144
CellManager _cm
The cell manager for the forest fire model.
Definition ForestFire.hh:162
unsigned int identify_clusters()
Identifies clusters in the cells and labels them with corresponding IDs.
Definition ForestFire.hh:309
void monitor()
Provide monitoring information to the frontend: tree_density
Definition ForestFire.hh:487
Model< ForestFire, ModelTypes > Base
The base model type.
Definition ForestFire.hh:141
double calculate_tree_density() const
Calculate and return the density of tree cells.
Definition ForestFire.hh:288
std::uniform_real_distribution< double > _prob_distr
A [0,1]-range uniform distribution used for evaluating probabilities.
Definition ForestFire.hh:168
const std::shared_ptr< DataSet > _dset_age
2D dataset (tree age and time) of cells
Definition ForestFire.hh:184
unsigned int _cluster_id_cnt
The incremental cluster tag caching variable.
Definition ForestFire.hh:171
std::vector< std::shared_ptr< CellManager::Cell > > _cluster_members
A temporary container for use in cluster identification.
Definition ForestFire.hh:174
typename CellManager::Cell Cell
The type of a cell.
Definition ForestFire.hh:150
const std::shared_ptr< DataSet > _dset_tree_density
The dataset that stores the mean density.
Definition ForestFire.hh:190
const std::shared_ptr< DataSet > _dset_kind
The dataset that stores the kind for each cell, e.g. Kind::tree.
Definition ForestFire.hh:181
void write_data()
Write data.
Definition ForestFire.hh:492
const bool _write_only_tree_density
Whether to only write the tree density.
Definition ForestFire.hh:178
ForestFire(const std::string &name, ParentModel &parent_model, const DataIO::Config &custom_cfg={})
Construct the ForestFire model.
Definition ForestFire.hh:205
typename CellManager::RuleFunc RuleFunc
Rule function type, extracted from CellManager.
Definition ForestFire.hh:153
const Param _param
Model parameters.
Definition ForestFire.hh:165
const std::shared_ptr< DataSet > _dset_cluster_id
The dataset that stores the cluster id.
Definition ForestFire.hh:187
@ empty
Every entity is utterly alone in the world.
YAML::Node Config
Type of a variadic dictionary-like data structure used throughout Utopia.
Definition types.hh:71
ReturnType get_as(const std::string &key, const DataIO::Config &node)
This function is a wrapper around the yaml-cpp YAML::Node::as function.
Definition cfg_utils.hh:158
Container select_entities(const Manager &mngr, const DataIO::Config &sel_cfg)
Select entities according to parameters specified in a configuration.
Definition select.hh:213
Kind
The kind of the cell: empty, tree, infected, source, stone.
Definition state.hh:9
Definition agent.hh:11
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
ForestFire model parameter struct.
Definition ForestFire.hh:89
const double p_lightning
Frequency of lightning occurring per cell.
Definition ForestFire.hh:94
Param(const DataIO::Config &cfg)
Construct the parameters from the given configuration node.
Definition ForestFire.hh:100
const double p_growth
Rate of growth per cell.
Definition ForestFire.hh:91
const double p_immunity
The probability (per neighbor) to be immune to a spreading fire.
Definition ForestFire.hh:97
The full cell struct for the ForestFire model.
Definition ForestFire.hh:28
State(const DataIO::Config &cfg, const std::shared_ptr< RNG > &rng)
Construct a cell from a configuration node and an RNG.
Definition ForestFire.hh:43
unsigned int cluster_id
An ID denoting to which cluster this cell belongs (if it is a tree)
Definition ForestFire.hh:36
unsigned short age
The age of the tree on this cell.
Definition ForestFire.hh:33
Kind kind
The kind of object that populates this cell, e.g. a tree.
Definition ForestFire.hh:30
State()=delete
Remove default constructor, for safety.