Utopia  2
Framework for studying models of complex & adaptive systems.
SimpleFlocking.hh
Go to the documentation of this file.
1 #ifndef UTOPIA_MODELS_SIMPLEFLOCKING_HH
2 #define UTOPIA_MODELS_SIMPLEFLOCKING_HH
3 
4 #include <random>
5 #include <functional>
6 
7 #include <utopia/core/model.hh>
8 #include <utopia/core/types.hh>
10 #include <utopia/core/apply.hh>
11 
12 #include "state.hh"
13 
14 
16 
17 // ++ Type definitions ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
18 
21 
23 
27 
28 
29 
30 
31 // ++ Model definition ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
33 
40  public Model<SimpleFlocking, ModelTypes>
41 {
42 public:
45 
48 
51 
53  using AgentPtr = std::shared_ptr<Agent>;
54 
56  using Rule = typename AgentManager::RuleFunc;
57 
60 
62  using SpaceVec = typename AgentManager::SpaceVec;
63 
65  using DataSet = typename Base::DataSet;
66 
67 
68 private:
69  // -- Members -------------------------------------------------------------
70 
73 
74  // .. Global parameters ...................................................
75 
77  double _speed;
78 
81 
83  double _noise_level;
84 
85 
86  // .. Temporary and helper objects ........................................
87 
89  std::uniform_real_distribution<double> _noise_distr;
90 
91 
92  // .. Output-related ......................................................
95 
96  // Agent-specific datasets
97  std::shared_ptr<DataSet> _dset_agent_x;
98  std::shared_ptr<DataSet> _dset_agent_y;
99  std::shared_ptr<DataSet> _dset_agent_orientation;
100 
101  // Global observables
102  std::shared_ptr<DataSet> _dset_orientation_circmean;
103  std::shared_ptr<DataSet> _dset_orientation_circstd;
104  std::shared_ptr<DataSet> _dset_norm_group_velocity;
105 
106 
107 public:
108  // -- Model Setup ---------------------------------------------------------
109 
111 
119  template<class ParentModel>
121  const std::string& name,
122  ParentModel& parent_model,
123  const DataIO::Config& custom_cfg = {}
124  )
125  :
126  Base(name, parent_model, custom_cfg)
127 
128  , _am(*this)
129 
130  , _speed(get_as<double>("speed", this->_cfg))
131  , _interaction_radius(get_as<double>("interaction_radius", this->_cfg))
132  , _noise_level(get_as<double>("noise_level", this->_cfg))
133 
134  , _noise_distr(-_noise_level/2., +_noise_level/2.)
135 
136  // .. Output-related ......................................................
137  , _store_agent_data(get_as<bool>("store_agent_data", this->_cfg))
138 
139  , _dset_agent_x(this->create_am_dset("agent/x", _am))
140  , _dset_agent_y(this->create_am_dset("agent/y", _am))
141  , _dset_agent_orientation(this->create_am_dset("agent/orientation", _am))
142 
144  this->create_dset("orientation_circmean", {})
145  )
147  this->create_dset("orientation_circstd", {})
148  )
150  this->create_dset("norm_group_velocity", {})
151  )
152  {
153  set_agent_speed(_speed);
154 
155  this->_log->info("{} all set up.", this->_name);
156  this->_log->info(" Store agent data? {}", _store_agent_data);
157  }
158 
159 
160 private:
161  // .. Setup functions .....................................................
162 
163  // .. Helper functions ....................................................
164 
165 public:
166  // -- Public Interface ----------------------------------------------------
167  // .. Simulation Control ..................................................
168 
170 
172  void perform_step () {
175  }
176 
177 
179 
184  void monitor () {
185  const auto orientations = get_from_agents([](const auto& agent){
186  return agent->state().get_orientation();
187  });
188 
189  const auto [circ_mean, circ_std] = circular_mean_and_std(orientations);
190  this->_monitor.set_entry("orientation_mean", circ_mean);
191  this->_monitor.set_entry("orientation_std", circ_std);
192  this->_monitor.set_entry("norm_group_velocity", norm_group_velocity());
193  }
194 
195 
197  void write_data () {
198  using WriteT = float;
199  const auto& agents = _am.agents();
200 
201  // -- Global observables
202  const auto orientations = get_from_agents([](const auto& agent){
203  return agent->state().get_orientation();
204  });
205 
206  const auto [circ_mean, circ_std] = circular_mean_and_std(orientations);
207  _dset_orientation_circmean->write(circ_mean);
208  _dset_orientation_circstd->write(circ_std);
209 
211 
212  // -- Agent-specific data
213  // ... only stored optionally
214  if (not _store_agent_data) return;
215 
216  _dset_agent_x->write(
217  agents.begin(), agents.end(),
218  [](const auto& agent) {
219  return static_cast<WriteT>(agent->position()[0]);
220  });
221 
222  _dset_agent_y->write(
223  agents.begin(), agents.end(),
224  [](const auto& agent) {
225  return static_cast<WriteT>(agent->position()[1]);
226  });
227 
229  orientations.begin(), orientations.end(),
230  [](const auto& orientation) {
231  return static_cast<WriteT>(orientation);
232  });
233  }
234 
235 
236  // Getters and setters ....................................................
237 
239  std::size_t num_agents () const {
240  return _am.agents().size();
241  }
242 
244  void set_agent_speed (const double new_speed) {
245  this->_log->info("Setting all agent's speed to {} ...", new_speed);
246  apply_rule(
247  [speed=new_speed](const auto& agent){
248  auto state = agent->state();
249  state.set_speed(speed);
250  return state;
251  },
252  this->_am.agents()
253  );
254  }
255 
257 
266  double norm_group_velocity () const {
267  const auto velocities = get_from_agents([](const auto& agent){
268  return agent->state().get_displacement();
269  });
270  return absolute_group_velocity(velocities) / std::fabs(_speed);
271  }
272 
274  template<
275  class Adapter,
276  class ValueType = std::invoke_result_t<Adapter, const AgentPtr&>
277  >
278  std::vector<ValueType> get_from_agents (const Adapter& adapter) const {
279  std::vector<ValueType> cont;
280  cont.reserve(num_agents());
281 
283  this->_am.agents().begin(), this->_am.agents().end(),
284  std::back_inserter(cont), adapter
285  );
286  return cont;
287  }
288 
289 
290  // Rules ..................................................................
291 
293 
298  const Rule _adjust_orientation = [this](const auto& agent){
299  auto state = agent->state();
300 
301  // Find all agents within the interaction radius and compute their
302  // average orientation, including (!) the current agent.
303  // To find an average orientation, we need to separate the x- and y-
304  // components and later combine them back into an angle.
305  auto agg_sin = std::sin(agent->state().get_orientation());
306  auto agg_cos = std::cos(agent->state().get_orientation());
307 
308  // NOTE The AgentManager::neighbors_of method finds neighbors with
309  // linear complexity in agent number, leading to an overall
310  // quadratic complexity in agent number for this search.
311  // This can be mitigated (on the level of the AgentManager!) by
312  // using a spatially restricted search or a lookup grid.
313  for (const auto& nb :
314  this->_am.neighbors_of(agent, this->_interaction_radius))
315  {
316  agg_sin += std::sin(nb->state().get_orientation());
317  agg_cos += std::cos(nb->state().get_orientation());
318  }
319 
320  // Can now set the orientation, including noise
321  // NOTE Could divide by number of involved agents here, but that is
322  // unnecessary because it cancels out through division anyway.
323  state.set_orientation(
324  std::atan2(agg_sin, agg_cos) +
325  (this->_noise_level > 0. ? _noise_distr(*this->_rng) : 0.)
326  );
327 
328  return state;
329  };
330 
332  const VoidRule _move = [this](const auto& agent){
333  this->_am.move_by(agent, agent->state().get_displacement());
334  };
335 
336 };
337 
338 
339 } // namespace Utopia::Models::SimpleFlocking
340 
341 #endif // UTOPIA_MODELS_SIMPLEFLOCKING_HH
An agent is a slightly specialized state container.
Definition: agent.hh:47
void move_by(const std::shared_ptr< Agent > &agent, const SpaceVec &move_vec) const
Move an agent relative to its current position.
Definition: agent_manager.hh:214
AgentContainer< Agent > neighbors_of(const std::shared_ptr< Agent > &agent, const double radius) const
Returns a container of all agents within a certain radius.
Definition: agent_manager.hh:434
SpaceVecType< dim > SpaceVec
The type of the vectors that represent physical quantities.
Definition: agent_manager.hh:52
std::function< void(const std::shared_ptr< Agent > &)> VoidRuleFunc
The type of a void rule function acting on agents of this agent manager.
Definition: agent_manager.hh:70
const AgentContainer< Agent > & agents() const
Return const reference to the managed agents.
Definition: agent_manager.hh:184
std::function< AgentState(const std::shared_ptr< Agent > &)> RuleFunc
The type of a rule function acting on agents of this agent manager.
Definition: agent_manager.hh:64
Utopia::Agent< AgentTraits, Space > Agent
The type of the managed agents.
Definition: agent_manager.hh:40
Base class interface for Models using the CRT Pattern.
Definition: model.hh:112
std::shared_ptr< DataSet > create_am_dset(const std::string name, const AgentManager &am, const std::size_t compression_level=1, const std::vector< hsize_t > chunksize={})
Create a dataset storing data from a AgentManager.
Definition: model.hh:939
Monitor _monitor
The monitor.
Definition: model.hh:188
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
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
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
The SimpleFlocking Model.
Definition: SimpleFlocking.hh:41
double _interaction_radius
The radius within which agents interact with each other.
Definition: SimpleFlocking.hh:80
std::shared_ptr< DataSet > _dset_agent_orientation
Definition: SimpleFlocking.hh:99
void perform_step()
Iterate a single step: adjust agent orientation, then move all agents.
Definition: SimpleFlocking.hh:172
AgentManager _am
The agent manager.
Definition: SimpleFlocking.hh:72
std::shared_ptr< DataSet > _dset_agent_x
Definition: SimpleFlocking.hh:97
void write_data()
Write data.
Definition: SimpleFlocking.hh:197
const VoidRule _move
Rule that applies the current displacement vector to the agent position.
Definition: SimpleFlocking.hh:332
Model< SimpleFlocking, ModelTypes > Base
The type of the Model base class of this derived class.
Definition: SimpleFlocking.hh:44
std::shared_ptr< DataSet > _dset_norm_group_velocity
Definition: SimpleFlocking.hh:104
std::shared_ptr< Agent > AgentPtr
Pointer to agent.
Definition: SimpleFlocking.hh:53
typename AgentManager::VoidRuleFunc VoidRule
Type of the update rules for agents where no state is to be returned.
Definition: SimpleFlocking.hh:59
std::size_t num_agents() const
The number of agents in the system (typically constant)
Definition: SimpleFlocking.hh:239
double _noise_level
The amplitude of the noise applied to the orientation update.
Definition: SimpleFlocking.hh:83
typename Base::DataSet DataSet
Data type for a dataset.
Definition: SimpleFlocking.hh:65
SimpleFlocking(const std::string &name, ParentModel &parent_model, const DataIO::Config &custom_cfg={})
Construct the SimpleFlocking model instance.
Definition: SimpleFlocking.hh:120
std::uniform_real_distribution< double > _noise_distr
The distribution used for determining the orientation noise.
Definition: SimpleFlocking.hh:89
double _speed
The global speed value, used as the uniform speed of all agents.
Definition: SimpleFlocking.hh:77
const Rule _adjust_orientation
Rule that sets agent orientation to the mean orientation (in a radius)
Definition: SimpleFlocking.hh:298
void monitor()
Monitor the model state.
Definition: SimpleFlocking.hh:184
std::shared_ptr< DataSet > _dset_orientation_circmean
Definition: SimpleFlocking.hh:102
double norm_group_velocity() const
The normalized absolute group velocity.
Definition: SimpleFlocking.hh:266
std::shared_ptr< DataSet > _dset_agent_y
Definition: SimpleFlocking.hh:98
typename AgentManager::RuleFunc Rule
Type of the update rules for agents.
Definition: SimpleFlocking.hh:56
void set_agent_speed(const double new_speed)
Sets the speed value of all agents.
Definition: SimpleFlocking.hh:244
std::vector< ValueType > get_from_agents(const Adapter &adapter) const
Aggregate agent properties into a container.
Definition: SimpleFlocking.hh:278
bool _store_agent_data
Whether to store agent-specific data.
Definition: SimpleFlocking.hh:94
typename AgentManager::SpaceVec SpaceVec
Type of spatial vectors within the domain.
Definition: SimpleFlocking.hh:62
std::shared_ptr< DataSet > _dset_orientation_circstd
Definition: SimpleFlocking.hh:103
OutputIt transform(const Utopia::ExecPolicy policy, InputIt first1, InputIt last1, OutputIt d_first, UnaryOperation unary_op)
Apply a unary operator to a range and store the result in a new range.
Definition: parallel.hh:368
YAML::Node Config
Type of a variadic dictionary-like data structure used throughout Utopia.
Definition: types.hh:71
void apply_rule(Rule &&rule, const ContTarget &cont_target, ContArgs &&... cont_args)
Sequential overload.
Definition: apply.hh:133
Definition: SimpleFlocking.hh:15
double absolute_group_velocity(const Container &velocities)
Computes the absolute group velocity from a container of velocity vectors.
Definition: utils.hh:58
auto circular_mean_and_std(const Container &angles)
Computes the circular mean and std from a sample of (constrained) angles.
Definition: utils.hh:129
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