diff --git a/demo/flocking/CMakeLists.txt b/demo/flocking/CMakeLists.txt new file mode 100644 index 000000000..0bed7c07f --- /dev/null +++ b/demo/flocking/CMakeLists.txt @@ -0,0 +1,29 @@ +# ----------------------------------------------------------------------------- +# +# Copyright (C) 2021 CERN & University of Surrey for the benefit of the +# BioDynaMo collaboration. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# See the LICENSE file distributed with this work for details. +# See the NOTICE file distributed with this work for additional information +# regarding copyright ownership. +# +# ----------------------------------------------------------------------------- + +cmake_minimum_required(VERSION 3.2.0) + +project(flocking_simulation) + +find_package(BioDynaMo REQUIRED) +include("${BDM_USE_FILE}") +include_directories("src") + +file(GLOB_RECURSE HEADERS src/*.h) +file(GLOB_RECURSE SOURCES src/*.cc) + +bdm_add_executable(flocking_simulation + HEADERS "${HEADERS}" + SOURCES "${SOURCES}" + LIBRARIES "${BDM_REQUIRED_LIBRARIES}") \ No newline at end of file diff --git a/demo/flocking/README.md b/demo/flocking/README.md new file mode 100755 index 000000000..3bf5b1c73 --- /dev/null +++ b/demo/flocking/README.md @@ -0,0 +1,98 @@ + +## Flocking Algorithm +This [Flocking](https://en.wikipedia.org/wiki/Flocking_(behavior)) algorithm builds on the framework established by R. Olfati-Saber in his paper ["Flocking for multi-agent dynamic systems: algorithms and theory"](https://ieeexplore.ieee.org/document/1605401?arnumber=1605401). +Boids try to match their velocity with neighboring boids and keep a desired distance to them. A common group objective can be set towards which the boid should migrate. +At each computational step an internal acceleration for each boid is computed and it's velocity and position updated accordingly. The acceleration consists of two terms that get added in a priority based scheme: the flocking force and a steering force towards the group objective. The flocking force is the (weighted) sum of three input terms: + - a term that tries to match the boid's velocity with neighboring boids + - a term that tries to keep a desired distance between a boid and its neighbors + - a term that improves the overall flock cohesion + +## Simulation +At the start of the simulation, all boids spawn uniformly at random inside a starting sphere centered at the domain's origin. All boids have no initial velocity and a random heading direction. By default the domain is an open torus. +To visualize the simulation, import the output in paraview and set the boids' scale array to ```actual_diameter```. + +## Running the Simulation + +Before running the simulation, increase the number of simulation steps in +`bdm.json` to a larger number, we suggest +```json +{ + ... , + "bdm::SimParam": { + ... + "computational_steps": 8000, + ... + } +} +``` +Instead of the default value `200`. To start the simulation, simply execute +``` +bdm run +``` +in the shell in your project folder. Note that you can change the parameters to +explore the parameter space in `bdm.json` without the need to recompile the +simulation. To visualize the results, type +``` +paraview +``` +in your terminal (again while being in the project folder). One may then load +the results via +``` +File > Load State > "output/flocking_simulation/flocking_simulation.pvsm" +> "Use File Names form States" +``` +By default, the boid's vision radius (`diameter`) is shown. The overlapping +illustrates that the boids can sense each other. For a more classical +visualization of the flocking behavior, click `Boids` in the `pipeline browser` +and select `actual_diameter` (scale array) in the tab `properties (Boids)` +(left). The `Play` button on the top right shows the results over time. + +## Simulation Specific Parameters +```n_boids:``` + number of boids that are spawned in the simulation + +```starting_sphere_radius:``` +a boid's starting location is chosen uniformly at random inside a sphere with this radius + +```boid_perception_radius:``` +determining radius for the extended cohesion term + +```boid_interaction_radius:``` +boids try to match their velocity and keep a desired distance to boids within this radius + +```perception_angle_deg:``` +a boid's field of view in degree (max. of 360°) + +```neighbor_distance:``` +a boid's desired distance to neighboring boids + +```max_accel:``` +maximal value for the total internal acceleration of a boid + +```max_speed:``` +maximal value for the speed of a boid + +```c_a_1:``` +weight for the term that controls the velocity matching with neighboring boids + +```c_a_2:``` +weight for the term that controls the desired distance to neighboring boids + +```c_a_3:``` +weight for the extended cohesion term + +```c_y:``` +weight for the navigational feedback term + +```d_t:``` +time between two computational steps. A smaller d_t corresponds to a higher update frequency + +```pos_gamma:``` +location of the common group objective + +## Extensions to this project + +The original implementation of the demo can be found in this +(GitHub repository)[https://github.com/mhoghrab/biodynamo-flocking-simulation]. +It contains further extensions to the project such as obstacle avoidance and +perturbations via a random field. \ No newline at end of file diff --git a/demo/flocking/bdm.json b/demo/flocking/bdm.json new file mode 100755 index 000000000..5fb806f92 --- /dev/null +++ b/demo/flocking/bdm.json @@ -0,0 +1,47 @@ +{ + "bdm::Param": { + "remove_output_dir_contents": true, + "compute_target": "cuda", + "statistics": false, + "bound_space": 2, + "max_bound": 1000, + "min_bound": -1000, + "visualization_interval": 20, + "export_visualization": true, + "unschedule_default_operations": [ + "mechanical forces" + ], + "use_progress_bar": true, + "visualize_agents": { + "Boid": [ + "actual_diameter_" + ] + } + }, + "bdm::SimParam": { + "_typename": "bdm::SimParam", + "computational_steps": 200, + "n_boids": 250, + "starting_sphere_radius": 200, + "double actual_diameter": 10, + "boid_perception_radius": 250, + "boid_interaction_radius": 70, + "perception_angle_deg": 300, + "neighbor_distance": 50, + "max_accel": 0.4, + "max_speed": 5, + "c_a_1": 0.37, + "c_a_2": 0.05, + "c_a_3": 0.05, + "c_y": 0.03, + "d_t": 0.05, + "pos_gamma": { + "_typename": "bdm::MathArray", + "data_": [ + 10000, + 0, + 0 + ] + } + } +} \ No newline at end of file diff --git a/demo/flocking/src/boid.cc b/demo/flocking/src/boid.cc new file mode 100755 index 000000000..0c6619f51 --- /dev/null +++ b/demo/flocking/src/boid.cc @@ -0,0 +1,353 @@ +// ----------------------------------------------------------------------------- +// +// Copyright (C) 2022 CERN & University of Surrey for the benefit of the +// BioDynaMo collaboration. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// +// See the LICENSE file distributed with this work for details. +// See the NOTICE file distributed with this work for additional information +// regarding copyright ownership. +// +// Author: Moritz Grabmann (2022) +// +// ----------------------------------------------------------------------------- + +#include "boid.h" +#include +#include "sim_param.h" + +namespace bdm { + +// --------------------------------------------------------------------------- +// Double3 Methods +double NormSq(Double3 vector) { + double res = 0; + for (size_t i = 0; i < 3; i++) + res += vector[i] * vector[i]; + return res; +}; + +Double3 UpperLimit(Double3 vector, double upper_limit) { + double length = vector.Norm(); + if (length == 0) { + return {0, 0, 0}; + } + if (length > upper_limit) { + vector = (vector / length) * upper_limit; + } + return vector; +}; + +Double3 GetNormalizedArray(Double3 vector) { + if (vector.Norm() == 0) + return {0, 0, 0}; + else + return vector.GetNormalizedArray(); +}; + +Double3 GetRandomVectorInUnitSphere() { + auto* random = Simulation::GetActive()->GetRandom(); + + double phi = random->Uniform(0, 2 * M_PI); + double costheta = random->Uniform(-1, 1); + double u = random->Uniform(0, 1); + + double theta = acos(costheta); + double r = sqrt(u); + + double x_coord = r * sin(theta) * cos(phi); + double y_coord = r * sin(theta) * sin(phi); + double z_coord = r * cos(theta); + + Double3 vec = {x_coord, y_coord, z_coord}; + return vec; +}; + +//////////////////////////////////////////////////////////////////////////////// +//----------------------------------------------------------------------------// +// Boid Class // +//----------------------------------------------------------------------------// +//////////////////////////////////////////////////////////////////////////////// + +void Boid::InitializeMembers() { + const auto* param = Simulation::GetActive()->GetParam(); + const auto* sparam = param->Get(); + + actual_diameter_ = sparam->actual_diameter; + SetBoidPerceptionRadius(sparam->boid_perception_radius); + boid_interaction_radius_ = sparam->boid_interaction_radius; + SetPerceptionAngle((sparam->perception_angle_deg / 360) * M_PI); + neighbor_distance_ = sparam->neighbor_distance; + max_acceleration_ = sparam->max_accel; + max_speed_ = sparam->max_speed; + limit_speed_ = sparam->limit_speed; + + // Flocking constants + c_a_1_ = sparam->c_a_1; + c_a_2_ = sparam->c_a_2; + c_a_3_ = sparam->c_a_3; + c_y_ = sparam->c_y; + h_a_ = sparam->h_a; + eps_ = sparam->eps; + d_t_ = sparam->d_t; + pos_gamma_ = sparam->pos_gamma; +}; + +// --------------------------------------------------------------------------- +// Define necessary virtual functions of Base class + +Shape Boid::GetShape() const { return Shape::kSphere; }; + +Double3 Boid::CalculateDisplacement(const InteractionForce* force, + double squared_radius, double dt) { + Double3 zero = {0, 0, 0}; + return zero; +}; + +void Boid::ApplyDisplacement(const Double3& displacement) { ; }; + +const Double3& Boid::GetPosition() const { return position_; }; + +void Boid::SetPosition(const Double3& pos) { position_ = pos; }; + +double Boid::GetDiameter() const { return diameter_; }; + +void Boid::SetDiameter(double diameter) { diameter_ = diameter; }; + +// --------------------------------------------------------------------------- +// Important getter and setter + +Double3 Boid::GetVelocity() const { return velocity_; }; + +void Boid::SetVelocity(Double3 velocity) { + velocity_ = velocity; + SetHeadingDirection(velocity_); +}; + +void Boid::SetBoidPerceptionRadius(double perception_radius) { + boid_perception_radius_ = perception_radius; + SetDiameter(boid_perception_radius_ * 2); +}; + +void Boid::SetPerceptionAngle(double angle) { + cos_perception_angle_ = std::cos(angle); +}; + +void Boid::SetHeadingDirection(Double3 dir) { + if (dir.Norm() != 0) { + heading_direction_ = GetNormalizedArray(dir); + } +}; + +double Boid::GetBoidInteractionRadius() { return boid_interaction_radius_; }; + +double Boid::GetBoidPerceptionRadius() { return boid_perception_radius_; }; + +// --------------------------------------------------------------------------- + +bool Boid::CheckIfVisible(Double3 point) { + if ((point - GetPosition()).Norm() == 0) { + // identical points + return true; + } + + Double3 cone_normal = heading_direction_; + Double3 direction_normal = (point - GetPosition()).GetNormalizedArray(); + double cos_angle = cone_normal * direction_normal; + + if (cos_angle >= cos_perception_angle_) { + return true; + } else { + return false; + } +}; + +Double3 Boid::SteerTowards(Double3 vector) { + if (vector.Norm() == 0) { + return {0, 0, 0}; + } + Double3 steer = vector.GetNormalizedArray() * max_speed_ - velocity_; + return steer; + return UpperLimit(steer, max_acceleration_); +}; + +// --------------------------------------------------------------------------- +// Data Updates + +void Boid::UpdateData() { + // update velocity with current acceleration + Double3 new_velocity_ = velocity_ + acceleration_ * d_t_; + if (limit_speed_) { + new_velocity_ = UpperLimit(new_velocity_, max_speed_); + } + SetVelocity(new_velocity_); + + // update position with new velocity + Double3 new_position_ = GetPosition() + new_velocity_ * d_t_; + SetPosition(new_position_); + + // reset acceleration acucumulator + acceleration_ = {0, 0, 0}; + acceleration_accum_scalar = 0; +}; + +void Boid::AccelerationAccumulator(Double3 acc) { + if (acceleration_accum_scalar + acc.Norm() <= max_acceleration_) { + acceleration_accum_scalar += acc.Norm(); + acceleration_ += acc; + } else { + double s = max_acceleration_ - acceleration_accum_scalar; + acceleration_accum_scalar = max_acceleration_; + acceleration_ += acc * s; + } +}; + +// --------------------------------------------------------------------------- +// Flocking Algorithm + +Double3 Boid::GetFlockingForce() { + auto* ctxt = Simulation::GetActive()->GetExecutionContext(); + CalculateNeighborData NeighborData(this); + ctxt->ForEachNeighbor(NeighborData, *this, pow(boid_perception_radius_, 2)); + + Double3 force = {0, 0, 0}; + force += NeighborData.GetU_a(); + force += GetExtendedCohesionTerm(NeighborData.GetCentreOfMass()); + + return force; +}; + +Double3 Boid::GetNavigationalFeedbackForce() { + return SteerTowards(pos_gamma_ - GetPosition()) * c_y_; +}; + +Double3 Boid::GetExtendedCohesionTerm(Double3 centre_of_mass) { + double ratio = + (centre_of_mass - GetPosition()).Norm() / boid_perception_radius_; + double h_1 = boid_interaction_radius_ / boid_perception_radius_; + double h_2 = h_1 * 1.2; + + double scale = Zeta(ratio, h_1, h_2); + + Double3 result = + GetNormalizedArray(centre_of_mass - GetPosition()) * scale * c_a_3_; + return result; +}; + +Double3 Boid::GetBoidInteractionTerm(const Boid* boid) { + Double3 u_a = {0, 0, 0}; + + // add gradient-based term to u_a + Double3 n_ij = GetNormalizedArray(boid->GetPosition() - GetPosition()); + + u_a += n_ij * Phi_a(Norm_sig(boid->GetPosition() - GetPosition())) * c_a_1_; + + // add consensus term + double r_a = Norm_sig(boid_interaction_radius_); + double a_ij = + Rho_h(Norm_sig(boid->GetPosition() - GetPosition()) / r_a, h_a_); + + u_a += (boid->GetVelocity() - GetVelocity()) * a_ij * c_a_2_; + + return u_a; +}; + +double Boid::Norm_sig(Double3 z) { + return (std::sqrt(1 + eps_ * NormSq(z)) - 1) / eps_; +}; + +double Boid::Norm_sig(double z) { + return (std::sqrt(1 + eps_ * z * z) - 1) / eps_; +}; + +double Boid::Phi(double z) { + // 0 < a <= b + // "a" controls a max for attaction scaling, "b" min for repelling + double a = 1; + double b = 2.5; + double c = std::abs(a - b) / std::sqrt(4 * a * b); + return ((a + b) * Sigmoid(z + c) + (a - b)) / 2; +}; + +double Boid::Rho_h(double z, double h) { + if (z >= 0 && z < h) { + return 1; + } + if (z >= h && z <= 1) { + return (1 + cos(M_PI * (z - h) / (1 - h))) / 2; + } + return 0; +}; + +double Boid::Rho_h_a(double z, double h) { + if (z >= 0 && z < h) { + return 1; + } + if (z >= h && z <= 1) { + double scale = exp(-5 * (z - h) * (z - h)); + return scale * (1 + cos(M_PI * (z - h) / (1 - h))) / 2; + } + return 0; +}; + +double Boid::Zeta(double z, double h_1, double h_2) { + if (z < h_1) { + return 0; + } else if (z >= h_1 && z <= h_2) { + return (1 + cos(M_PI * (h_2 - z) / (h_2 - h_1))) / 2; + } else { + return 1; + } +}; + +double Boid::Sigmoid(double z) { return z / (1 + std::abs(z)); }; + +double Boid::Phi_a(double z) { + double r_a = Norm_sig(boid_interaction_radius_); + double d_a = Norm_sig(neighbor_distance_); + + return Rho_h_a(z / r_a, h_a_) * Phi(z - d_a); +}; + +//////////////////////////////////////////////////////////////////////////////// +//----------------------------------------------------------------------------// +// Flocking Behaviour // +//----------------------------------------------------------------------------// +//////////////////////////////////////////////////////////////////////////////// + +void Flocking::Run(Agent* agent) { + auto* boid = dynamic_cast(agent); + + boid->AccelerationAccumulator(boid->GetFlockingForce()); + boid->AccelerationAccumulator(boid->GetNavigationalFeedbackForce()); +}; + +void CalculateNeighborData::operator()(Agent* neighbor, + double squared_distance) { + auto* neighbor_boid = bdm_static_cast(neighbor); + + double distance = std::sqrt(squared_distance); + bool is_visible = boid_->CheckIfVisible(neighbor_boid->GetPosition()); + + if (is_visible && distance <= boid_->GetBoidInteractionRadius()) { + u_a += boid_->GetBoidInteractionTerm(neighbor_boid); + } + + if (is_visible && distance <= boid_->GetBoidPerceptionRadius()) { + sum_pos += neighbor_boid->GetPosition(); + n++; + } +}; + +Double3 CalculateNeighborData::GetCentreOfMass() { + if (n != 0) + return (sum_pos / n); + else + return boid_->GetPosition(); +}; + +Double3 CalculateNeighborData::GetU_a() { return u_a; }; + +} // namespace bdm diff --git a/demo/flocking/src/boid.h b/demo/flocking/src/boid.h new file mode 100755 index 000000000..e780570f1 --- /dev/null +++ b/demo/flocking/src/boid.h @@ -0,0 +1,191 @@ +// ----------------------------------------------------------------------------- +// +// Copyright (C) 2022 CERN & University of Surrey for the benefit of the +// BioDynaMo collaboration. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// +// See the LICENSE file distributed with this work for details. +// See the NOTICE file distributed with this work for additional information +// regarding copyright ownership. +// +// Author: Moritz Grabmann (2022) +// +// ----------------------------------------------------------------------------- + +#ifndef BOID_H_ +#define BOID_H_ + +#include "core/behavior/behavior.h" +#include "core/container/math_array.h" +#include "core/functor.h" + +namespace bdm { + +// --------------------------------------------------------------------------- +// Double3 Methods + +double NormSq(Double3 vector); + +Double3 UpperLimit(Double3 vector, double upper_limit); + +Double3 GetNormalizedArray(Double3 vector); + +Double3 GetRandomVectorInUnitSphere(); + +//////////////////////////////////////////////////////////////////////////////// +//----------------------------------------------------------------------------// +// Boid Class // +//----------------------------------------------------------------------------// +//////////////////////////////////////////////////////////////////////////////// + +class Boid : public Agent { + BDM_AGENT_HEADER(Boid, Agent, 1); + + public: + Boid() {} + explicit Boid(const Double3& position) + : position_(position), diameter_(1.0) {} + virtual ~Boid() {} + + // Initializes Boid parameters with given SimParam + void InitializeMembers(); + + // --------------------------------------------------------------------------- + // Define necessary virtual functions of Base class. Those functions are + // called from BioDynaMo's main engine but we don't need that here. Thus, + // the function return zero or are defined as an empty call. + + Shape GetShape() const override; + + Double3 CalculateDisplacement(const InteractionForce* force, + double squared_radius, double dt) override; + + void ApplyDisplacement(const Double3& displacement) override; + + const Double3& GetPosition() const override; + + void SetPosition(const Double3& pos) override; + + double GetDiameter() const override; + + void SetDiameter(double diameter) override; + + // --------------------------------------------------------------------------- + // Important getter and setter + + Double3 GetVelocity() const; + + void SetVelocity(Double3 velocity); + + void SetBoidPerceptionRadius(double perception_radius); + + void SetPerceptionAngle(double angle); + + void SetHeadingDirection(Double3 dir); + + double GetBoidInteractionRadius(); + + double GetBoidPerceptionRadius(); + + // --------------------------------------------------------------------------- + // Checks if the point is inside the viewing cone defined by + // heading_direction_ and perception_angle_ + bool CheckIfVisible(Double3 point); + + // Returns a Steering-Force towards the argument vector + Double3 SteerTowards(Double3 vector); + + // --------------------------------------------------------------------------- + // Data Updates + + // Update position and velocity after a computational step + void UpdateData(); + + // Accumulates acceleration terms in a priority based scheme + void AccelerationAccumulator(Double3 acceleration_to_add); + + // --------------------------------------------------------------------------- + // Flocking Algorithm + + // iterates over all neighbor boids and adds the interaction terms; + // returns a flocking force that produces an a-latice structure + Double3 GetFlockingForce(); + + // Returns an acceleration term towards pos_gamma_ + Double3 GetNavigationalFeedbackForce(); + + // Returns an acceleration term that improves the overall flock cohesion + Double3 GetExtendedCohesionTerm(Double3 centre_of_mass); + + // Returns the interaction term for a given boid + Double3 GetBoidInteractionTerm(const Boid* boid); + + // Functions needed to calculate the interaction terms + double Norm_sig(Double3 z); + + double Norm_sig(double z); + + double Phi(double z); + + double Rho_h(double z, double h); + + double Rho_h_a(double z, double h); + + double Zeta(double z, double h_onset, double h_maxeff); + + double Sigmoid(double z); + + double Phi_a(double z); + + // --------------------------------------------------------------------------- + private: + Double3 position_, velocity_, heading_direction_, acceleration_; + double acceleration_accum_scalar; + double diameter_, actual_diameter_; + double boid_perception_radius_, boid_interaction_radius_; + double cos_perception_angle_; + double neighbor_distance_; + double max_acceleration_, max_speed_; + bool limit_speed_; + + // Flocking constants + double c_a_1_, c_a_2_, c_a_3_, c_y_; + double h_a_, eps_, d_t_; + Double3 pos_gamma_; // gamma agent location (common group objective) +}; + +//////////////////////////////////////////////////////////////////////////////// +//----------------------------------------------------------------------------// +// Flocking Behaviour // +//----------------------------------------------------------------------------// +//////////////////////////////////////////////////////////////////////////////// + +struct Flocking : public Behavior { + BDM_BEHAVIOR_HEADER(Flocking, Behavior, 1); + + void Run(Agent* agent) override; +}; + +// Functor class needed to calculate neighbor data in Flocking +// ForEachNeighbor call +class CalculateNeighborData : public Functor { + public: + CalculateNeighborData(Boid* boid) : boid_(boid) {} + virtual ~CalculateNeighborData() {} + + void operator()(Agent* neighbor, double squared_distance) override; + + Double3 GetU_a(); + + Double3 GetCentreOfMass(); + + Boid* boid_; + Double3 u_a = {0, 0, 0}, sum_pos = {0, 0, 0}; + int n = 0; +}; + +} // namespace bdm + +#endif // BOID_H_ diff --git a/demo/flocking/src/flocking.cc b/demo/flocking/src/flocking.cc new file mode 100755 index 000000000..c355d760f --- /dev/null +++ b/demo/flocking/src/flocking.cc @@ -0,0 +1,75 @@ +// ----------------------------------------------------------------------------- +// +// Copyright (C) 2022 CERN & University of Surrey for the benefit of the +// BioDynaMo collaboration. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// +// See the LICENSE file distributed with this work for details. +// See the NOTICE file distributed with this work for additional information +// regarding copyright ownership. +// +// Author: Moritz Grabmann (2022) +// +// ----------------------------------------------------------------------------- + +#include "flocking.h" +#include "biodynamo.h" +#include "boid.h" +#include "sim_param.h" +#include "update_operation.h" + +namespace bdm { + +const ParamGroupUid SimParam::kUid = ParamGroupUidGenerator::Get()->NewUid(); + +int Simulate(int argc, const char** argv) { + // --------------------------------------------------------------------------- + // Set references + Param::RegisterParamGroup(new SimParam()); + Simulation simulation(argc, argv); + auto* rm = simulation.GetResourceManager(); + auto* param = simulation.GetParam(); + auto* sparam = param->Get(); + auto* scheduler = simulation.GetScheduler(); + + // --------------------------------------------------------------------------- + // Spawn and initialize boids + double centre = (param->max_bound + param->min_bound) / 2; + double radius = sparam->starting_sphere_radius; + + Double3 transl = {centre, centre, centre}; + + for (size_t i = 0; i < sparam->n_boids; ++i) { + auto* boid = new Boid(); + Double3 coord = GetRandomVectorInUnitSphere() * radius + transl; + + boid->SetPosition(coord); + boid->SetVelocity({0, 0, 0}); + boid->SetHeadingDirection(GetRandomVectorInUnitSphere()); + boid->AddBehavior(new Flocking()); + boid->InitializeMembers(); + + rm->AddAgent(boid); + } + + // --------------------------------------------------------------------------- + // Add a PostScheduledOp to update the boids position and velocity after each + // computational step + OperationRegistry::GetInstance()->AddOperationImpl( + "UpdateOp", OpComputeTarget::kCpu, new UpdateOp()); + auto* update_op = NewOperation("UpdateOp"); + scheduler->ScheduleOp(update_op, OpType::kPostSchedule); + + // --------------------------------------------------------------------------- + // Simulate + scheduler->Simulate(sparam->computational_steps); + + // --------------------------------------------------------------------------- + // End of simulation + std::cout << "Simulation completed successfully!" << std::endl; + return 0; +}; + +} // namespace bdm diff --git a/demo/flocking/src/flocking.h b/demo/flocking/src/flocking.h new file mode 100755 index 000000000..4a10b0240 --- /dev/null +++ b/demo/flocking/src/flocking.h @@ -0,0 +1,26 @@ +// ----------------------------------------------------------------------------- +// +// Copyright (C) 2022 CERN & University of Surrey for the benefit of the +// BioDynaMo collaboration. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// +// See the LICENSE file distributed with this work for details. +// See the NOTICE file distributed with this work for additional information +// regarding copyright ownership. +// +// Author: Moritz Grabmann (2022) +// +// ----------------------------------------------------------------------------- + +#ifndef FLOCKING_H_ +#define FLOCKING_H_ + +namespace bdm { + +int Simulate(int argc, const char** argv); + +} // namespace bdm + +#endif // FLOCKING_H_ diff --git a/demo/flocking/src/main.cc b/demo/flocking/src/main.cc new file mode 100755 index 000000000..5632a5111 --- /dev/null +++ b/demo/flocking/src/main.cc @@ -0,0 +1,19 @@ +// ----------------------------------------------------------------------------- +// +// Copyright (C) 2022 CERN & University of Surrey for the benefit of the +// BioDynaMo collaboration. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// +// See the LICENSE file distributed with this work for details. +// See the NOTICE file distributed with this work for additional information +// regarding copyright ownership. +// +// Author: Moritz Grabmann (2022) +// +// ----------------------------------------------------------------------------- + +#include "flocking.h" + +int main(int argc, const char** argv) { return bdm::Simulate(argc, argv); } diff --git a/demo/flocking/src/sim_param.h b/demo/flocking/src/sim_param.h new file mode 100644 index 000000000..93110f0d4 --- /dev/null +++ b/demo/flocking/src/sim_param.h @@ -0,0 +1,57 @@ +// ----------------------------------------------------------------------------- +// +// Copyright (C) 2022 CERN & University of Surrey for the benefit of the +// BioDynaMo collaboration. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// +// See the LICENSE file distributed with this work for details. +// See the NOTICE file distributed with this work for additional information +// regarding copyright ownership. +// +// Author: Moritz Grabmann (2022) +// +// ----------------------------------------------------------------------------- + +#ifndef SIM_PARAM_H_ +#define SIM_PARAM_H_ + +#include "biodynamo.h" +#include "core/container/math_array.h" + +namespace bdm { + +// ----------------------------------------------------------------------------- +// Parameters specific for this simulation +// ----------------------------------------------------------------------------- +struct SimParam : public ParamGroup { + BDM_PARAM_GROUP_HEADER(SimParam, 1); + + uint64_t computational_steps = 8000; + size_t n_boids = 250; + double starting_sphere_radius = 200; + + double actual_diameter = 10; + double boid_perception_radius = 250; + double boid_interaction_radius = 70; + double perception_angle_deg = 300; + double neighbor_distance = 50; + double max_accel = 0.4; + double max_speed = 5; + bool limit_speed = true; + + // Flocking 2 Algorithm + double c_a_1 = 0.37; + double c_a_2 = 0.05; + double c_a_3 = 0.05; + double c_y = 0.05; + double h_a = 0.25; + double eps = 0.1; + double d_t = 0.05; + Double3 pos_gamma = {1000, 0, 0}; +}; + +} // namespace bdm + +#endif // SIM_PARAM_H_ diff --git a/demo/flocking/src/update_operation.h b/demo/flocking/src/update_operation.h new file mode 100755 index 000000000..da9870d26 --- /dev/null +++ b/demo/flocking/src/update_operation.h @@ -0,0 +1,38 @@ +// ----------------------------------------------------------------------------- +// +// Copyright (C) 2022 CERN & University of Surrey for the benefit of the +// BioDynaMo collaboration. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// +// See the LICENSE file distributed with this work for details. +// See the NOTICE file distributed with this work for additional information +// regarding copyright ownership. +// +// Author: Moritz Grabmann (2022) +// +// ----------------------------------------------------------------------------- + +#ifndef UPDATE_OP_H_ +#define UPDATE_OP_H_ + +#include "boid.h" +#include "core/operation/operation.h" +#include "core/resource_manager.h" + +using namespace bdm; + +struct UpdateOp : public StandaloneOperationImpl { + BDM_OP_HEADER(UpdateOp); + void operator()() override { + auto* sim = Simulation::GetActive(); + auto* rm = sim->GetResourceManager(); + + rm->ForEachAgent([](Agent* agent) { + auto* boid = dynamic_cast(agent); + boid->UpdateData(); + }); + } +}; +#endif // UPDATE_OP_H_ diff --git a/util/housekeeping/check-copyright.sh b/util/housekeeping/check-copyright.sh index 6d4ac185e..9ae61780c 100755 --- a/util/housekeeping/check-copyright.sh +++ b/util/housekeeping/check-copyright.sh @@ -29,10 +29,13 @@ shift set -e for file in $@; do - # Ingore copyrights of demo/epidemiology because of Lukas private copyright + # Ingore some files because of private copyright in demos if [[ $file == *"demo/epidemiology/"* ]];then continue fi + if [[ $file == *"demo/flocking/"* ]];then + continue + fi if [ "$(uname)" = 'Darwin' ]; then COMM_OPTIONS="-23" else diff --git a/util/housekeeping/run-clang-format.sh b/util/housekeeping/run-clang-format.sh index 8962fd5a4..ce0bd06ad 100755 --- a/util/housekeeping/run-clang-format.sh +++ b/util/housekeeping/run-clang-format.sh @@ -75,7 +75,9 @@ else NUM_CORRECTIONS=`$CLANG_FORMAT -output-replacements-xml $@ | grep offset | wc -l` if [ "$NUM_CORRECTIONS" -gt "0" ]; then - echo "Error: clang-format suggested changes, please fix them!" + echo "Error: clang-format suggested $NUM_CORRECTIONS changes." + echo "Please fix them, i.e. run `make format` in the build directory." + popd >/dev/null exit 1 fi