Esempio n. 1
0
  void
  Scheduler::rebuildList()
  {
    sorter->clear();
    sorter->init(Sim->N() + 1);

    for (Particle& part : Sim->particles)
      addEvents(part);
    rebuildSystemEvents();
  }
Esempio n. 2
0
  void
  SSystemOnly::initialise()
  {
    dout << "Reinitialising on collision " << Sim->eventCount << std::endl;

    if (Sim->systems.empty())
      M_throw() << "A SystemOnlyScheduler used when there are no system events?";
  
    sorter->clear();
    sorter->resize(Sim->N+1);
    eventCount.clear();
    eventCount.resize(Sim->N+1, 0);  
    sorter->init();
    rebuildSystemEvents();
  }
Esempio n. 3
0
  void
  Scheduler::rebuildList()
  {
    sorter->clear();
    //The plus one is because system events are stored in the last heap;
    sorter->resize(Sim->N+1);
    eventCount.clear();
    eventCount.resize(Sim->N+1, 0);

    BOOST_FOREACH(const Particle& part, Sim->particleList)
      addEvents(part);
  
    sorter->init();

    rebuildSystemEvents();
  }
Esempio n. 4
0
void
CSNeighbourList::initialise()
{
  try {
    NBListID = Sim->dynamics.getGlobal("SchedulerNBList")->getID();
  }
  catch(std::exception& cxp)
    {
      M_throw() << "Failed while finding the neighbour list global.\n"
		<< "You must have a neighbour list enabled for this\n"
		<< "scheduler called SchedulerNBList.\n"
		<< cxp.what();
    }
  
  if (dynamic_cast<const CGNeighbourList*>
      (Sim->dynamics.getGlobals()[NBListID].get_ptr())
      == NULL)
    M_throw() << "The Global named SchedulerNBList is not a neighbour list!";

  static_cast<CGNeighbourList&>
    (*Sim->dynamics.getGlobals()[NBListID].get_ptr())
    .markAsUsedInScheduler();

  dout << "Building all events on collision " << Sim->eventCount << std::endl;
  std::cout.flush();

  sorter->clear();
  //The plus one is because system events are stored in the last heap;
  sorter->resize(Sim->N+1);
  eventCount.clear();
  eventCount.resize(Sim->N+1, 0);

  //Now initialise the interactions
  {
    boost::progress_display prog(Sim->N);
 
    BOOST_FOREACH(const Particle& part, Sim->particleList)
      {
	addEventsInit(part);
	++prog;
      }
  }
  
  sorter->init();

  rebuildSystemEvents();
}
Esempio n. 5
0
  void
  SSystemOnly::rebuildList()
  {
#ifdef DYNAMO_DEBUG
    initialise();
#else
    if (Sim->systems.empty())
      M_throw() << "A SystemOnlyScheduler used when there are no system events?";
  
    sorter->clear();
    sorter->resize(Sim->N+1);
    eventCount.clear();
    eventCount.resize(Sim->N+1, 0);  
    sorter->rebuild();
    rebuildSystemEvents();
#endif
  }
Esempio n. 6
0
void 
CSNeighbourList::rebuildList()
{ 
#ifdef DYNAMO_DEBUG
  initialise();
#else
  sorter->clear();
  //The plus one is because system events are stored in the last heap;
  sorter->resize(Sim->N+1);
  eventCount.clear();
  eventCount.resize(Sim->N+1, 0);

  BOOST_FOREACH(const Particle& part, Sim->particleList)
    addEventsInit(part);
  
  sorter->rebuild();
  
  rebuildSystemEvents();
#endif
}
Esempio n. 7
0
  void
  Scheduler::runNextEvent()
  {
    sorter->sort();

#ifdef DYNAMO_DEBUG
    if (sorter->nextPELEmpty())
      M_throw() << "Next particle list is empty but top of list!";
#endif

    lazyDeletionCleanup();

    if (boost::math::isnan(sorter->next_dt()))
      M_throw() << "Next event time is NaN"
		<< "\nTime to event "
		<< sorter->next_dt()
		<< "\nEvent Type = " 
		<< sorter->next_type()
		<< "\nOwner Particle = " << sorter->next_ID()
		<< "\nID2 = " << sorter->next_p2();
    
    if (sorter->next_dt() == HUGE_VAL)
      {
	derr << "Next event time is Inf! (Queue has run out of events!)\n"
	     << "Shutting simulation down..."
	     << "\nEvent details, Type = " 
	     << sorter->next_type()
	     << "\nOwner Particle = " << sorter->next_ID()
	     << "\nID2 = " << sorter->next_p2()
	     << std::endl;
	Sim->endEventCount = Sim->eventCount;
	return;
      }

    ////////////////////////////////////////////////////////////////////
    // We can't perform such strict testing as commented out
    // below. Sometimes negative event times occur, usually at the start
    // of a simulation when particles are initialized just on the edge
    // of a cell, or if we have a system event which is "triggered" and
    // sets its own event time to 0. These must be tolerated and we must
    // trust in the determinism of the dynamics and the precision of the
    // calculations to minimise any effects. Generally, systems
    // shouldn't crash because of negative event times that were not
    // caused by a physically incorrect initial configuration
    ////////////////////////////////////////////////////////////////////
    //  if (sorter->next_dt() < 0)
    //    M_throw() << "Next event time is less than 0"
    //	      << "\nTime to event "
    //	      << sorter->next_dt()
    //	      << "\nEvent Type = " 
    //	      << sorter->next_type()
    //      	      << "\nOwner Particle = " << sorter->next_ID()
    //	      << "\nID2 = " << sorter->next_p2();
  
    /*! This is our dimensionless parameter which we need to correct a
      edge case for the collision testing. If an event is scheduled to
      occur its collision time is always double checked before it is
      executed. If two events are close together in time, the earliest
      might be popped off the queue, retested and then appear to occur
      later than the next event. In this case the original event is
      discarded and the new version is reinserted into the event
      queue. However, a rounding error might then cause the new event to
      appear earlier than the second event and we're back where we
      started. Basically, if "rejectionLimit" rejections occur in a row we
      just accept the next event in the queue. This breaks these loops and
      allows the simulation to continue. 

      With this method the system is guarranteed to maintain the correct
      event sequence to within machine precision. The queue can even
      handle negative time events provided the dynamics allow it.
    */
    const size_t rejectionLimit = 10;

    switch (sorter->next_type())
      {
      case INTERACTION:
	{
	  const Particle& p1(Sim->particleList[sorter->next_ID()]);
	  const Particle& p2(Sim->particleList[sorter->next_p2()]);

	  //Ready the next event in the FEL
	  sorter->popNextEvent();
	  sorter->update(sorter->next_ID());
	  sorter->sort();	
	  lazyDeletionCleanup();

	  //Now recalculate the FEL event
	  Sim->dynamics.getLiouvillean().updateParticlePair(p1, p2);       
	  IntEvent Event(Sim->dynamics.getEvent(p1, p2));
	
#ifdef DYNAMO_DEBUG
	  if (sorter->nextPELEmpty())
	    M_throw() << "The next PEL is empty, cannot perform the comparison to see if this event is out of sequence";
#endif

	  if ((Event.getType() == NONE)
	      || ((Event.getdt() > sorter->next_dt()) 
		  && (++_interactionRejectionCounter < rejectionLimit)))
	    {
#ifdef DYNAMO_DEBUG
	      derr << "Event " << Sim->eventCount << ":" << Event.getType()
		   << ",dt=" << Event.getdt() << ">nextdt=" << sorter->next_dt()
		   << ",p1=" << p1.getID() << ",p2=" << p2.getID() << std::endl;
#endif		
	      this->fullUpdate(p1, p2);
	      return;
	    }
	
#ifdef DYNAMO_DEBUG
	  if (Event.getdt() < 0)
	    derr << "Negative time " << Event.getdt() << std::endl;
#endif

	  //Reset the rejection watchdog, we will run an interaction event now
	  _interactionRejectionCounter = 0;
		
#ifdef DYNAMO_DEBUG

	  if (boost::math::isnan(Event.getdt()))
	    M_throw() << "A NAN Interaction collision time has been found"
		      << Event.stringData(Sim);
	
	  if (Event.getdt() == HUGE_VAL)
	    M_throw() << "An infinite Interaction (not marked as NONE) collision time has been found\n"
		      << Event.stringData(Sim);
#endif
	
	  //Debug section
#ifdef DYNAMO_CollDebug
	  std::cerr << "\nsysdt " << (Event.getdt() + Sim->dSysTime) / Sim->dynamics.units().unitTime()
		    << "  ID1 " << ((p2.getID() < p2.getID()) ? p1.getID() : p2.getID())
		    << "  ID2 " << ((p2.getID() < p2.getID()) ? p2.getID() : p1.getID())
		    << "  dt " << Event.getdt()
		    << "  Type " << Event.getType();
#endif

	  Sim->dSysTime += Event.getdt();
	
	  stream(Event.getdt());
	
	  //dynamics must be updated first
	  Sim->dynamics.stream(Event.getdt());
	
	  Event.addTime(Sim->freestreamAcc);

	  Sim->freestreamAcc = 0;

	  Sim->dynamics.getInteractions()[Event.getInteractionID()]
	    ->runEvent(p1,p2,Event);

	  break;
	}
      case GLOBAL:
	{
	  //We don't stream the system for globals as neighbour lists
	  //optimise this (they dont need it).

	  //We also don't recheck Global events! (Check, some events might rely on this behavior)
	  Sim->dynamics.getGlobals()[sorter->next_p2()]
	    ->runEvent(Sim->particleList[sorter->next_ID()], sorter->next_dt());       	
	  break;	           
	}
      case LOCAL:
	{
	  const Particle& part(Sim->particleList[sorter->next_ID()]);

	  //Copy the FEL event
	  size_t localID = sorter->next_p2();

	  //Ready the next event in the FEL
	  sorter->popNextEvent();
	  sorter->update(sorter->next_ID());
	  sorter->sort();
	  lazyDeletionCleanup();

	  Sim->dynamics.getLiouvillean().updateParticle(part);
	  LocalEvent iEvent(Sim->dynamics.getLocals()[localID]->getEvent(part));

	  if (iEvent.getType() == NONE)
	    {
#ifdef DYNAMO_DEBUG
	      derr << "Local event found not to occur [" << part.getID()
		   << "] (possible glancing/tenuous event canceled due to numerical error)" << std::endl;
#endif		
	      this->fullUpdate(part);
	      return;
	    }
	
	  double next_dt = sorter->next_dt();

	  if ((iEvent.getdt() > next_dt) && (++_localRejectionCounter < rejectionLimit))
	    {
#ifdef DYNAMO_DEBUG 
	      derr << "Recalculated LOCAL event time is greater than the next event time, recalculating" << std::endl;
#endif
	      this->fullUpdate(part);
	      return;
	    }

	  _localRejectionCounter = 0;

#ifdef DYNAMO_DEBUG 
	  if (boost::math::isnan(iEvent.getdt()))
	    M_throw() << "A NAN Global collision time has been found\n"
		      << iEvent.stringData(Sim);
	
	  if (iEvent.getdt() == HUGE_VAL)
	    M_throw() << "An infinite (not marked as NONE) Global collision time has been found\n"
		      << iEvent.stringData(Sim);
#endif
	
	  Sim->dSysTime += iEvent.getdt();
	
	  stream(iEvent.getdt());
	
	  //dynamics must be updated first
	  Sim->dynamics.stream(iEvent.getdt());
	
	  iEvent.addTime(Sim->freestreamAcc);
	  Sim->freestreamAcc = 0;

	  Sim->dynamics.getLocals()[localID]->runEvent(part, iEvent);	  
	  break;
	}
      case SYSTEM:
	{
	  Sim->dynamics.getSystemEvents()[sorter->next_p2()]
	    ->runEvent();
	  //This saves the system events rebuilding themselves
	  rebuildSystemEvents();
	  break;
	}
      case VIRTUAL:
	{
	  //This is a special type which requires that the system is
	  //moved forward to the current time and the events for this
	  //particle recalculated.

	  double dt = sorter->next_dt();
	  size_t ID = sorter->next_ID();
	  Sim->dSysTime += dt;
	  Sim->ptrScheduler->stream(dt);
	  Sim->dynamics.stream(dt);
	  Sim->freestreamAcc += dt;
	  this->fullUpdate(Sim->particleList[ID]);
	  break;
	}
      case NONE:
	{
	  M_throw() << "A NONE event has reached the top of the queue."
	    "\nThe simulation has run out of events! Aborting!";
	}
      default:
	M_throw() << "Unhandled event type requested to be run\n"
		  << "Type is " << sorter->next_type();
      }
  }
Esempio n. 8
0
  void
  Scheduler::runNextEvent()
  {
#ifdef DYNAMO_DEBUG
    if (sorter->empty())
      M_throw() << "Next particle list is empty but top of list!";
#endif

    Event next_event = sorter->top();

    ////////////////////////////////////////////////////////////////////
    // We can't perform such strict testing as commented out
    // below. Sometimes negative event times occur, usually at the start
    // of a simulation when particles are initialized just on the edge
    // of a cell, or if we have a system event which is "triggered" and
    // sets its own event time to 0. These must be tolerated and we must
    // trust in the determinism of the dynamics and the precision of the
    // calculations to minimise any effects. Generally, systems
    // shouldn't crash because of negative event times that were not
    // caused by a physically incorrect initial configuration
    ////////////////////////////////////////////////////////////////////
    //  if (sorter->next_dt() < 0)
    //    M_throw() << "Next event time is less than 0"
    //	      << "\nTime to event "
    //	      << sorter->next_dt()
    //	      << "\nEvent Type = " 
    //	      << sorter->next_type()
    //      	      << "\nOwner Particle = " << sorter->next_ID()
    //	      << "\nID2 = " << sorter->next_p2();
  
    /*! This is our dimensionless parameter which we need to correct a
      edge case for the collision testing. If an event is scheduled to
      occur its collision time is always double checked before it is
      executed. If two events are close together in time, the earliest
      might be popped off the queue, retested and then appear to occur
      later than the next event. In this case the original event is
      discarded and the new version is reinserted into the event
      queue. However, a rounding error might then cause the new event to
      appear earlier than the second event and we're back where we
      started. Basically, if "rejectionLimit" rejections occur in a row we
      just accept the next event in the queue. This breaks these loops and
      allows the simulation to continue. 

      With this method the system is guarranteed to maintain the correct
      event sequence to within machine precision. The queue can even
      handle negative time events provided the dynamics allow it.
    */
    const size_t rejectionLimit = 10;
    const size_t systemParticleID = Sim->N();

    if (next_event._type == RECALCULATE)
      {
	if (next_event._particle1ID == systemParticleID)
	  rebuildSystemEvents();
	else
	  //This is a special event type which requires that the
	  // events for this particle recalculated.
	  this->fullUpdate(Sim->particles[next_event._particle1ID]);

	return;
      }
    
    if (next_event._type == NONE)
      M_throw() << "A type=NONE event with no source has reached the top of the queue."
	"\nThe simulation has run out of events! Aborting!";

    //-inf values are special values for instant event.
    if (next_event._dt == -std::numeric_limits<float>::infinity())
      next_event._dt = 0;

    switch (next_event._source)
      {
      case INTERACTION:
	{
#ifdef DYNAMO_DEBUG
	  if (next_event._particle1ID >= Sim->particles.size())
	    M_throw() << "Out of range particle access";
	  if (next_event._particle2ID >= Sim->particles.size())
	    M_throw() << "Out of range particle access";
#endif

	  Particle& p1(Sim->particles[next_event._particle1ID]);
	  Particle& p2(Sim->particles[next_event._particle2ID]);

	  if (!std::isfinite(next_event._dt))
	    M_throw() << "Next event time is not finite!"
		      << "\ndt = " << next_event._dt
		      << "\nEvent Type = " << next_event._type
		      << "\nParticle 1 ID = " << next_event._particle1ID
		      << "\nParticle 2 ID = " << next_event._particle2ID
		      << "\nInteraction = " << Sim->getInteraction(p1, p2)->getName()
	      ;

	  //Ready the next event in the FEL
	  sorter->pop();

	  //Now recalculate the current FEL event (to check if
	  //accumilation of numerical errors have caused the order of
	  //events to change). This also gives us more information on
	  //the event.
	  Sim->dynamics->updateParticlePair(p1, p2);
	  const Event Event = Sim->getEvent(p1, p2);
	
	  //Now check if the recalculated event is still the first
	  //event in the FEL. If not, force a recalculation of this
	  //particles events and return (so another event can be run).
#ifdef DYNAMO_DEBUG
	  if (sorter->empty())
	    M_throw() << "The next PEL is empty, cannot perform the comparison to see if this event is out of sequence";
#endif
	  next_event = sorter->top();
	  if (next_event._dt == -std::numeric_limits<float>::infinity())
	    next_event._dt = 0;
	  
	  //Here we see if the next FEL event is earlier than the one
	  //about to be processed, we also count the amount of
	  //rejections we perform (its a watchdog), as (in some minor
	  //edge cases) we can enter loops due to tiny precision
	  //differences in event times.
	  if ((Event._type == NONE) || ((Event._dt > next_event._dt) && (++_interactionRejectionCounter < rejectionLimit)))
	    {
	      this->fullUpdate(p1, p2);
	      return;
	    }

	  //Reset the rejection watchdog counter as we are about to
	  //run an interaction event now
	  _interactionRejectionCounter = 0;
		
	  if (!std::isfinite(next_event._dt))
	    M_throw() << "Next event time is not finite!"
		      << "\ndt = " << next_event._dt
		      << "\nEvent Type = " << next_event._type
		      << "\nParticle 1 ID = " << next_event._particle1ID
		      << "\nParticle 2 ID = " << next_event._particle2ID
		      << "\nInteraction = " << Sim->getInteraction(p1, p2)->getName();

#ifdef DYNAMO_DEBUG
	  if (Event._dt < 0)
	    derr << "Warning! Negative time event " << Event << std::endl;
	  
	  if (p1.getID() == p2.getID())
	    M_throw() << "Somehow processing a self Interaction";
#endif

	  //Move the simulation forward to the time of the event
	  Sim->systemTime += Event._dt;
	  stream(Event._dt);
	  //Allow everything to stream up to the current time before executing the event
	  Sim->stream(Event._dt);
	  
	  PairEventData eventdata = Sim->interactions[Event._sourceID]->runEvent(p1, p2, Event);
	  
	  Sim->_sigParticleUpdate(eventdata);
	  Sim->ptrScheduler->fullUpdate(p1, p2);
	  for (shared_ptr<OutputPlugin> & Ptr : Sim->outputPlugins)
	    Ptr->eventUpdate(Event, eventdata);
	  break;
	}
      case GLOBAL:
	{
	  if (!std::isfinite(next_event._dt))
	    M_throw() << "Next event time is not finite!"
		      << "\ndt = " << next_event._dt
		      << "\nEvent Type = " << next_event._type
		      << "\nParticle ID = " << next_event._particle1ID
		      << "\nGlobal (ID=" << next_event._sourceID << ")= " << Sim->globals[next_event._sourceID]->getName()
	      ;

	  //We don't stream the system for globals as neighbour lists
	  //optimise this (they dont need it).  We also don't recheck
	  //Global events! (Check, some events might rely on this
	  //behavior)
	  Sim->globals[next_event._sourceID]->runEvent(Sim->particles[next_event._particle1ID], next_event._dt);
	  break;
	}
      case LOCAL:
	{
	  Particle& part(Sim->particles[next_event._particle1ID]);
	  size_t localID = next_event._sourceID;

	  if (!std::isfinite(next_event._dt))
	    M_throw() << "Next event time is not finite!"
		      << "\ndt = " << next_event._dt
		      << "\nEvent Type = " << next_event._type
		      << "\nParticle ID = " << next_event._particle1ID
		      << "\nGlobal (ID=" << next_event._sourceID << ")= " 
		      << Sim->locals[next_event._sourceID]->getName()
	      ;

	  //Ready the next event in the FEL
	  sorter->pop();
	  Sim->dynamics->updateParticle(part);
	  Event iEvent(Sim->locals[localID]->getEvent(part));

	  next_event = sorter->top();
	  //Check the recalculated event is valid and not later than
	  //the next event in the queue
	  if ((iEvent._type == NONE) || ((iEvent._dt > next_event._dt) && (++_localRejectionCounter < rejectionLimit)))
	    {
	      this->fullUpdate(part);
	      return;
	    }

	  _localRejectionCounter = 0;

#ifdef DYNAMO_DEBUG 
	  if (!std::isfinite(next_event._dt))
	    M_throw() << "Recalculated event time is not finite!"
		      << next_event._particle1ID
		      << "\nGlobal = " << Sim->locals[next_event._sourceID]->getName();
#endif
	
	  Sim->systemTime += iEvent._dt;
	
	  stream(iEvent._dt);
	
	  //dynamics must be updated first
	  Sim->stream(iEvent._dt);
	
	  const ParticleEventData data = Sim->locals[localID]->runEvent(part, iEvent);
	  Sim->_sigParticleUpdate(data);	  
	  Sim->ptrScheduler->fullUpdate(part);
	  for (shared_ptr<OutputPlugin> & Ptr : Sim->outputPlugins)
	    Ptr->eventUpdate(iEvent, data);
	  break;
	}
      case SYSTEM:
	{
	  sorter->pop();
	  //System events can use the value -std::numeric_limits<float>::infinity() to request
	  //immediate processing, therefore, only NaN and +std::numeric_limits<float>::infinity()
	  //values are invalid
	  if (std::isnan(next_event._dt) || (next_event._dt == std::numeric_limits<float>::infinity()))
	    M_throw() << "Next event time is not finite!"
		      << "\ndt = " << next_event._dt
		      << "\nEvent Type = " << next_event._type
		      << "\nParticle ID = " << next_event._particle1ID
		      << "\nSystem (ID=" << next_event._sourceID << ")= " << Sim->systems[next_event._sourceID]->getName()
	      ;
	  
	  Sim->systemTime += next_event._dt;
	  stream(next_event._dt);
	  Sim->stream(next_event._dt);

	  const NEventData data = Sim->systems[next_event._sourceID]->runEvent();

	  if (!data.L1partChanges.empty() || !data.L2partChanges.empty()) {
	    Sim->_sigParticleUpdate(data);
	    for (const auto& d1 : data.L1partChanges)
	      this->fullUpdate(Sim->particles[d1.getParticleID()]);
	    for (const auto& d2 : data.L2partChanges)
	      this->fullUpdate(Sim->particles[d2.particle1_.getParticleID()], Sim->particles[d2.particle2_.getParticleID()]);
	    
	    for (shared_ptr<OutputPlugin>& Ptr : Sim->outputPlugins)
	      Ptr->eventUpdate(next_event, data);
	  }

	  const size_t systemParticleID = Sim->N();
	  Event event = Sim->systems[next_event._sourceID]->getEvent();
	  event._particle1ID = systemParticleID;
	  sorter->push(event);
	  break;
	}
      default:
	M_throw() << "Unhandled event type requested to be run\n"
		  << "Type is " << next_event._type;
      }
  }