Skip to content

Comments

CEP 31 - Agent Registered Discrete Event Timing #447

Draft
meg-krieg wants to merge 8 commits intocyclus:sourcefrom
meg-krieg:discrete-event
Draft

CEP 31 - Agent Registered Discrete Event Timing #447
meg-krieg wants to merge 8 commits intocyclus:sourcefrom
meg-krieg:discrete-event

Conversation

@meg-krieg
Copy link
Contributor

@meg-krieg meg-krieg commented Feb 20, 2026

Summary of Changes

This PR adds a CEP 31 for discrete event timing proposal in Cyclus (and by extent cycamore). There are many areas where a more robust implementation can be proposed including

  1. treatment of Tick/Tock/Decision functions
  2. required cycle_length for each archetype to schedule new events under a capacity-met condition
  3. maintaining the same Timer::RunSim() phase suite ordering within each event even if timesteps are skipped

The CEP goes through a broad restructuring ideas and more detailed implementation could be outlined in the future. Any suggestions to the implementations, clarifications to the CEP, or other comments from the cyclus community would be great!

Related CEPs and Issues

Creates new CEP 31.

@meg-krieg meg-krieg assigned gonuke and munkm and unassigned gonuke and munkm Feb 20, 2026
Copy link
Member

@gonuke gonuke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for kicking this off @meg-krieg!

I'm not sure I understand the motivation for not allowing Tick/Tock/Decision to be registered as events that come at discrete time queued by the context/timer?

Comment on lines +74 to +82
These actions map to the current *kernel* phases of cyclus's cardinal function. Thus, by extent,

- Tick
- Tock
- Decision

may not be considered events because they do *not* change the simulation state. They change
the agent's internal state (agent phase). As these actions are not events, they may never be independently registered by agents.
(There are scenarios in which build/requets/decomission may trigger them, however.)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a key point of discussion/contention. Can you say more about why you want this strict interpretation? If the context will be skipping time steps based on the registered events, how will agents know when it is time to tick or tock? How will they be triggered? What if a tick is necessary to determine whether or not to participate in a trade?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are scenarios in which i can see the Tock and Decision being registrable events based on the outcome of the DRE.

It is mainly the tick that cannot be registrable (in my opinion)! If all traders objects are available bidders every trade event and all bidders need accurate bid information and we don't know who will bid, then all bidders must all tick. In other words, all traders must tick either every scheduled event or every trade event.

I could be really off the mark though.

===============================

Material requests, Build, and Decomissioin event registration will be handled by individual agents. Cyclus already creates a preconditioned timeline
for discrete-build and decomission events. An additional ``EventRequest()`` member funcition for all cycamore archetypes will check a facility's inventory.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If such a function is necessary, it should perhaps be added to the agent class from which these all derive.

for discrete-build and decomission events. An additional ``EventRequest()`` member funcition for all cycamore archetypes will check a facility's inventory.

1. If inventory not at capacity, agent will register for the (+1) next immediate time step to attempt another request.
2. If inventory at capacity, agent will register its next request event for a fixed ``+ cycle_length`` time from the current event.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is too rooted in the current set of archetypes. We need to think beyond the concept of cycle length and reactors.

2. If inventory at capacity, agent will register its next request event for a fixed ``+ cycle_length`` time from the current event.

These material request events will be registered within Context in a dynamic dictionary that contains the event's timestamp and a list of ``Trader`` objects. To ensure that all
facility agents' ``EventRequest()`` functions are checked regularly, A look-ahead function will be added to cyclus's cardinal phase suite after the decomissioning phase.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this degenerate to time steps again? I don't know what the purpose of a look-ahead function is and when/how it gets triggered?

Copy link
Contributor Author

@meg-krieg meg-krieg Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pasted some general code below in case my words are really vague! Maybe this makes what I am thinking a little clearer? It is pretty bare-bones but just for a general idea -- in timer.cc

void Timer::RunSim() {
...
ctx_->Populate(0); // at time 0 everyone must register for a trade event to get started
 while ( (time_ < si_.duration) && (prev_time_ != time_)) {

   CLOG(LEV_INFO1) << "Current time: " << time_;
   if (want_snapshot_) {
     want_snapshot_ = false;
     SimInit::Snapshot(ctx_);
   }
   // run through phases
   DoBuild();
   CLOG(LEV_INFO2) << "Beginning Tick for time: " << time_;
   DoTick();
   CLOG(LEV_INFO2) << "Beginning DRE for time: " << time_;
   DoResEx(&matl_manager, &genrsrc_manager);
   CLOG(LEV_INFO2) << "Beginning Tock for time: " << time_;
   DoTock();
   CLOG(LEV_INFO2) << "Beginning Decision for time: " << time_;
   DoDecision();
   DoDecom();
   DoLookAhead();

#ifdef CYCLUS_WITH_PYTHON
   EventLoop();
#endif
   prev_time_ = time_;
   time_ = NextEvent();
...
}

Then, later in timer.cc, something similar to this will happen where timer checks for the next closest event registered in the timeline. (this is not meant to be accurate code).

void Timer::DoLookAhead() {
  std::set<Trader*> all_traders = ctx_->traders();
  for(Trader* m : all_traders){
    m->EventRequest(); 
  };
// another check will probably go here
}

int Timer::NextEvent(){
  auto reg_traders = ctx_->EventRequesters(); //the list of traders who have registered for events in the timeline
  int t_p = time_ +1; // time plus +1 
  std::vector<int> event_lists = {decom_queue_.upper_bound(t_p)->first,build_queue_.upper_bound(t_p)->first}, reg_traders.upper_bound(t_p)->first};
  return *std::min_element(event_lists.begin(), event_lists.end());
}

So, the simulation finishes some event and it looks for the next closest timestamp that has an event. That timestamp could have events registered under any 3, 2, or 1 of build/trade/decom actions scheduled. The simulation knows it has the event at the timestamp but does not know which type (this could change maybe), so it checks each phase for that event. If the phase is filled with participants, that phase is one of the events and is triggered.

This means that the phases will have some conditional check... for example

void Timer::DoBuild() {
  if(build_queue_.count(time_)==0){
         continue;}
else{
  std::vector<std::pair<std::string, Agent*>> build_list = build_queue_[time_];
  for (int i = 0; i < build_list.size(); ++i) {
    Agent* m = ctx_->CreateAgent<Agent>(build_list[i].first);
    Agent* parent = build_list[i].second;
...
   }
...
  }

Overall, this treatment should skip the timeline from event to event instead of timestep to next timestep when fully fleshed out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants