/**
* \file AgentsSourcesManager.cpp
* \date Apr 14, 2015
* \version v0.7
* \copyright <2009-2015> Forschungszentrum Jülich GmbH. All rights reserved.
*
* \section License
* This file is part of JuPedSim.
*
* JuPedSim is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* JuPedSim is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with JuPedSim. If not, see .
*
* \section Description
* This class is responsible for materialising agent in a given location at a given frequency up to a maximum number.
* The optimal position where to put the agents is given by various algorithms, for instance
* the Voronoi algorithm or the Mitchell Best candidate algorithm.
*
**/
#include "AgentsSourcesManager.h"
#include "Pedestrian.h"
#include "../mpi/LCGrid.h"
#include
#include "AgentsQueue.h"
#include "../voronoi-boost/VoronoiPositionGenerator.h"
#define UNUSED(x) [&x]{}() // c++11 silence warnings
using namespace std;
bool AgentsSourcesManager::_isCompleted=true;
AgentsSourcesManager::AgentsSourcesManager()
{
}
AgentsSourcesManager::~AgentsSourcesManager()
{
}
void AgentsSourcesManager::operator()()
{
Run();
}
void AgentsSourcesManager::Run()
{
SetRunning(true);
Log->Write("INFO:\tStarting agent manager thread");
/* std::cout<< KGRN << "\n Starting agent manager thread\n" << "\n>> time: " << Pedestrian::GetGlobalTime() << RESET << "\n"; */
//Generate all agents required for the complete simulation
//It might be more efficient to generate at each frequency step
//TODO this loop is exactly GenerateAgents( --> REFACTOR)
for (const auto& src : _sources)
{
/* std::cout << "Generate AgentsAndAddToPool src: " << src->GetId() << "\n" ; */
src->GenerateAgentsAndAddToPool(src->GetMaxAgents(), _building);
}
//first call ignoring the return value
ProcessAllSources();
//the loop is updated each x second.
//it might be better to use a timer
_isCompleted = false;
bool finished = false;
SetBuildingUpdated(false);
long updateFrequency = 1; //TODO parse this from inifile
/* std::cout << KMAG << "RUN Starting thread manager with _lastUpdateTime " << _lastUpdateTime<< std::endl; */
do
{
int current_time = (int)Pedestrian::GetGlobalTime();
/* std::cout << KBLU << ">> RUN: current_time " << current_time << " last update " << _lastUpdateTime << "\n" << RESET; */
if ((current_time != _lastUpdateTime)
&& ((current_time % updateFrequency) == 0))
{
/* std::cout << " --- Enter IF --- \n" << KRED << "QUEUE isempty: " << AgentsQueueIn::IsEmpty() << "\n" << RESET; */
if(AgentsQueueIn::IsEmpty())
//if queue is empty. Otherwise, wait for main thread to empty it and update _building
{
/* std::cout << " --- Enter QUEUE EMPTY --- \n"; */
finished=ProcessAllSources();
_lastUpdateTime = current_time;
//SetBuildingUpdated(false);
}
}
// wait for main thread to update building
if(current_time >= GetMaxSimTime())
break; // break if max simulation time is reached.
} while (!finished);
Log->Write("INFO:\tTerminating agent manager thread");
_isCompleted = true;
}
bool AgentsSourcesManager::ProcessAllSources() const
{
/* std::cout << "\nSTART AgentsSourcesManager::ProcessAllSources()\n"; */
bool empty=true;
double current_time = Pedestrian::GetGlobalTime();
vector source_peds; // we have to collect peds from all sources, so that we can consider them while computing new positions
for (const auto& src : _sources)
{
/* std::cout << KRED << "\nprocessing src: " << src->GetId() << " -- current time: " << current_time << " schedule time: " << src->GetPlanTime() <<". number of peds in building " << _building->GetAllPedestrians().size() << "\n" << RESET; */
if (src->GetPoolSize() && (src->GetPlanTime() <= current_time) )// maybe diff peds;
src->RemoveAgentsFromPool(peds, src->GetFrequency());
source_peds.reserve(source_peds.size() + peds.size());
Log->Write("\nINFO:\tSource %d generating %d agents (%d remaining)\n",src->GetId(),peds.size(),src->GetPoolSize());
printf("\nINFO:\tSource %d generating %lu agents (%d remaining)\n",src->GetId(), peds.size(),src->GetPoolSize());
//ComputeBestPositionRandom(src.get(), peds);
//todo: here every pedestrian needs an exitline
if(!std::isnan(src->GetStartX()) && !std::isnan(src->GetStartY()))
{
printf("INFO:\tSet source agent on fixed position (%.2f, %.2f)", src->GetStartX(), src->GetStartY());
InitFixedPosition(src.get(), peds);
}
else
if( !ComputeBestPositionVoronoiBoost(src.get(), peds, _building, source_peds) )
Log->Write("WARNING:\tThere was no place for some pedestrians");
source_peds.insert(source_peds.end(), peds.begin(), peds.end());
/* std::cout << KRED << ">> Add to queue " << peds.size() << "\n" << RESET; */
/* for( auto pp: peds) */
/* std::cout << "id: "<< pp->GetID() << " pos " << pp->GetPos()._x << ", " << pp->GetPos()._y << "\n"; */
/* std::cout << "------\n"; */
/* for( auto pp: source_peds) */
/* std::cout << "id: "<< pp->GetID() << " POS " << pp->GetPos()._x << ", " << pp->GetPos()._y << "\n"; */
AgentsQueueIn::Add(peds);
empty = false;
//src->Dump();
}
if (src->GetPlanTime() > current_time) // for the case we still expect
// agents coming
empty = false;
//src->Dump();//exit(0);
}
/* std::cout << "LEAVE AgentsSourcesManager::ProcessAllSources()\n"; */
// std::cout << " Source building: "<< _building << " size " << _building->GetAllPedestrians().size()<< std::endl;
// for(auto pp: _building->GetAllPedestrians())
// std::cout<< KBLU << "BUL: agentssourcesManager: " << pp->GetPos()._x << ", " << pp->GetPos()._y << RESET << std::endl;
//
/* std::cout << "========================\n"; */
return empty;
}
void AgentsSourcesManager::InitFixedPosition(AgentsSource* src,
vector& peds)const
{
for(auto&& ped : peds)
{
Point v;
if (ped->GetExitLine()) {
v = (ped->GetExitLine()->ShortestPoint(ped->GetPos())- ped->GetPos()).Normalized();
} else {
auto transitions = ped->GetBuilding()->GetAllTransitions();
auto transition = transitions[0];
int trans_ID = transition->GetID();
ped->SetExitLine(transition); // set dummy line
ped->SetExitIndex(trans_ID);
//ped->SetFinalDestination(trans_ID);
v = Point(0., 0.);
}
double speed=ped->GetEllipse().GetV0();
v=v*speed;
ped->SetV(v);
ped->SetPos( Point(src->GetStartX(), src->GetStartY()) );
}
}
void AgentsSourcesManager::ComputeBestPositionCompleteRandom(AgentsSource* src,
vector& peds)const
{
auto dist = src->GetStartDistribution();
auto subroom = _building->GetRoom(dist->GetRoomId())->GetSubRoom(dist->GetSubroomID());
vector positions = PedDistributor::PossiblePositions(*subroom);
double seed = time(0);
//TODO: get the seed from the simulation
std:: cout << "seed: "<< seed << std::endl;
srand (seed);
for (auto& ped : peds)
{
if( positions.size() )
{
int index = rand()%positions.size();
Point new_pos = positions[index];
positions.erase(positions.begin() + index);
ped->SetPos(new_pos, true);
std:: cout << "pos: " << new_pos._x << new_pos._y << std::endl;
AdjustVelocityByNeighbour(ped);
}
else
{
Log->Write("\t No place for a pedestrian");
break;
}
}
}
/*
void AgentsSourcesManager::ComputeBestPositionVoronoi(AgentsSource* src,
Pedestrian* agent) const
{
auto dist = src->GetStartDistribution();
double bounds[4];
dist->Getbounds(bounds);
int roomID = dist->GetRoomId();
int subroomID = dist->GetSubroomID();
//Get all pedestrians in that location
vector peds;
_building->GetPedestrians(roomID, subroomID, peds);
//filter the points that are not within the boundaries
for (auto&& iter = peds.begin(); iter != peds.end();)
{
const Point& pos = (*iter)->GetPos();
if ((bounds[0] <= pos._x && pos._x <= bounds[1])
&& (bounds[1] <= pos._y && pos._y <= bounds[2]))
{
iter = peds.erase(iter);
cout << "removing (testing only)..." << endl;
exit(0);
} else
{
++iter;
}
}
//special case with 1, 2 or only three pedestrians in the area
if (peds.size() < 3)
{
//TODO/random position in the area
return;
}
// compute the cells and cut with the bounds
const int count = peds.size();
float* xValues = new float[count];
float* yValues = new float[count];
//float xValues[count];
//float yValues[count];
for (int i = 0; i < count; i++)
{
xValues[i] = peds[i]->GetPos()._x;
yValues[i] = peds[i]->GetPos()._y;
}
VoronoiDiagramGenerator vdg;
vdg.generateVoronoi(xValues, yValues, count, bounds[0], bounds[1],
bounds[2], bounds[3], 3);
vdg.resetIterator();
vdg.resetVerticesIterator();
printf("\n------vertices---------\n");
//collect the positions
vector positions;
float x1, y1;
while (vdg.getNextVertex(x1, y1))
{
printf("GOT Point (%f,%f)\n", x1, y1);
positions.push_back(Point(x1, y1));
}
//look for the biggest spot
map map_dist_to_position;
for (auto&& pos : positions)
{
double min_dist = FLT_MAX;
for (auto&& ped : peds)
{
double dist = (pos - ped->GetPos()).NormSquare();
if (dist < min_dist)
{
min_dist = dist;
}
}
map_dist_to_position[min_dist] = pos;
}
//list the result
for (auto&& mp : map_dist_to_position)
{
cout << "dist: " << mp.first << " pos: " << mp.second.toString()
<< endl;
//agent->SetPos(mp.second, true);
}
//the elements are ordered.
// so the last one has the largest distance
if (!map_dist_to_position.empty())
{
agent->SetPos(map_dist_to_position.rbegin()->second, true);
cout << "position:" << agent->GetPos().toString() << endl;
//exit(0);
} else
{
cout << "position not set:" << endl;
cout << "size: " << map_dist_to_position.size() << endl;
cout << " for " << peds.size() << " pedestrians" << endl;
exit(0);
}
//exit(0);
// float x1,y1,x2,y2;
//while(vdg.getNext(x1,y1,x2,y2))
//{
// printf("GOT Line (%f,%f)->(%f,%f)\n",x1,y1,x2, y2);
//
//}
//compute the best position
//exit(0);
}
*/
void AgentsSourcesManager::ComputeBestPositionRandom(AgentsSource* src,
std::vector& peds) const
{
//generate the agents with default positions
auto dist = src->GetStartDistribution();
auto subroom = _building->GetRoom(dist->GetRoomId())->GetSubRoom(
dist->GetSubroomID());
vector positions = PedDistributor::PossiblePositions(*subroom);
double bounds[4] = { 0, 0, 0, 0 };
dist->Getbounds(bounds);
std::vector peds_without_place;
vector extra_positions;
std::vector::iterator iter_ped;
for (iter_ped = peds.begin(); iter_ped != peds.end(); )
{
//need to be called at each iteration
SortPositionByDensity(positions, extra_positions);
int index = -1;
double radius = ( (*iter_ped)->GetEllipse() ).GetBmax() ;
//in the case a range was specified
//just take the first element
for (unsigned int a = 0; a < positions.size(); a++)
{
Point pos = positions[a];
//cout<<"checking: "< neighbours;
_building->GetGrid()->GetNeighbourhood(pos,neighbours);
for (const auto& ngh: neighbours)
if( (ngh->GetPos() - pos).NormSquare() < 4*radius*radius )
{
enough_space = false;
break;
}
if( enough_space )
{
index = a;
break;
}
}
}
if (index == -1)
{
if (positions.size())
{
Log->Write(
"ERROR:\t AgentSourceManager Cannot distribute pedestrians in the mentioned area [%0.2f,%0.2f,%0.2f,%0.2f]",
bounds[0], bounds[1], bounds[2], bounds[3]);
Log->Write(" \t Specifying a subroom_id might help");
Log->Write(" \t %d positions were available",positions.size());
//exit(EXIT_FAILURE);
}
//dump the pedestrian, move iterator
peds_without_place.push_back(*iter_ped);
iter_ped=peds.erase(iter_ped);
}
else //we found a position with enough space
{
const Point& pos = positions[index];
extra_positions.push_back(pos);
(*iter_ped)->SetPos(pos, true); //true for the initial position
positions.erase(positions.begin() + index);
//at this point we have a position
//so we can adjust the velocity
//AdjustVelocityUsingWeidmann(ped);
AdjustVelocityByNeighbour( (*iter_ped) );
//move iterator
iter_ped++;
}
//return the pedestrians without place
}
if(peds_without_place.size()>0)
src->AddAgentsToPool(peds_without_place);
}
void AgentsSourcesManager::AdjustVelocityByNeighbour(Pedestrian* ped) const
{
//get the density
vector neighbours;
_building->GetGrid()->GetNeighbourhood(ped,neighbours);
double speed=0.0;
double radius_square=0.56*0.56;//corresponding to an area of 1m3
int count=0;
for(const auto& p: neighbours)
{
//only pedes in a sepcific rance
if( (ped->GetPos()-p->GetPos()).NormSquare()<=radius_square)
{
//only peds with the same destination
if(ped->GetExitIndex()==p->GetExitIndex())
{
double dist1=ped->GetDistanceToNextTarget();
double dist2=p->GetDistanceToNextTarget();
//only peds in front of me
if(dist2GetV().Norm();
count++;
}
}
}
}
//mean speed
if(count==0)
{
speed=ped->GetEllipse().GetV0(); // FIXME: bad fix for: peds without navline (ar.graf)
//speed=ped->GetV0Norm();
}
else
{
speed=speed/count;
}
if(ped->FindRoute()!=-1)
{
//get the next destination point
Point v =(ped->GetExitLine()->ShortestPoint(ped->GetPos())- ped->GetPos()).Normalized();
v=v*speed;
ped->SetV(v);
}
else
{
Log->Write(">> ERROR:\t no route could be found for agent [%d] going to [%d]",ped->GetID(),ped->GetFinalDestination());
//that will be most probably be fixed in the next computation step.
// so do not abort
}
}
void AgentsSourcesManager::AdjustVelocityUsingWeidmann(Pedestrian* ped) const
{
//get the density
vector neighbours;
_building->GetGrid()->GetNeighbourhood(ped,neighbours);
//density in pers per m2
double density = 1.0;
//radius corresponding to a surface of 1m2
//double radius_square=0.564*0.564;
double radius_square=1.0;
for(const auto& p: neighbours)
{
if( (ped->GetPos()-p->GetPos()).NormSquare()<=radius_square)
density+=1.0;
}
density=density/(radius_square*M_PI);
//get the velocity
double density_max=5.4;
//speed from taken from weidmann FD
double speed=1.34*(1-exp(-1.913*(1.0/density-1.0/density_max)));
if(speed>=ped->GetV0Norm())
{
speed=ped->GetV0Norm();
}
//set the velocity vector
if(ped->FindRoute()!=-1)
{
//get the next destination point
Point v =(ped->GetExitLine()->ShortestPoint(ped->GetPos())- ped->GetPos()).Normalized();
v=v*speed;
ped->SetV(v);
//cout<<"density: "<Write(">>> SOURCE ERROR:\t no route could be found for agent [%d] going to [%d]",ped->GetID(),ped->GetFinalDestination());
//that will be most probably be fixed in the next computation step.
// so do not abort
}
}
void AgentsSourcesManager::SortPositionByDensity(std::vector& positions, std::vector& extra_positions) const
{
std::multimap density2pt;
//std::map density2pt;
for(auto&& pt:positions)
{
vector neighbours;
_building->GetGrid()->GetNeighbourhood(pt,neighbours);
//density in pers per m2
double density = 0.0;
double radius_square=0.40*0.40;
for(const auto& p: neighbours)
{
//FIXME: p can be null, if deleted in the main simulation thread.
if( p && (pt-p->GetPos()).NormSquare()<=radius_square)
density+=1.0;
}
//consider the extra positions
for(const auto& ptx: extra_positions)
{
if( (ptx-pt).NormSquare()<=radius_square)
density+=1.0;
}
density=density/(radius_square*M_PI);
density2pt.insert(std::pair(density,pt));
}
//cout<<"------------------"<GenerateAgentsAndAddToPool(src->GetMaxAgents(), _building);
}
}
void AgentsSourcesManager::AddSource(std::shared_ptr src)
{
_sources.push_back(src);
_isCompleted=false;//at least one source was provided
}
const std::vector >& AgentsSourcesManager::GetSources() const
{
return _sources;
}
void AgentsSourcesManager::SetBuilding(Building* building)
{
_building = building;
}
bool AgentsSourcesManager::IsCompleted() const
{
return _isCompleted;
}
bool AgentsSourcesManager::IsRunning() const
{
return _isRunning;
}
bool AgentsSourcesManager::IsBuildingUpdated() const
{
return _buildingUpdated;
}
void AgentsSourcesManager::SetBuildingUpdated(bool update)
{
_buildingUpdated = update;
}
void AgentsSourcesManager::SetRunning(bool running)
{
_isRunning = running;
}
Building* AgentsSourcesManager::GetBuilding() const
{
return _building;
}
long AgentsSourcesManager::GetMaxAgentNumber() const
{
long pop=0;
for (const auto& src : _sources)
{
pop+=src->GetMaxAgents();
}
return pop;
}
int AgentsSourcesManager::GetMaxSimTime() const{
return maxSimTime;
}
void AgentsSourcesManager::SetMaxSimTime(int t){
maxSimTime = t;
}