Ah, OK, got it.
Yeah, clock-triggers are only intended for cycling real-time workflows.
And xtriggers wait for an external condition to become satisfied, at which time dependent tasks can trigger.
I think the proper solution to this problem is a batch queue (PBS, Slurm, …) that only releases jobs after hours. However, if your site does not provide such a queue (and aren’t willing to do so) we may be able to work around it in Cylc.
simulated & @allow_transfer => transfer
So in what order are the things called ? Is allow_transfer
checked before simulated
succeeds ?
xtriggers start getting checked as soon as the dependent task enters the active window of the workflow, which is when its first task prerequisite gets satisfied. If transfer
s only parent task in the graph is simulated
, then transfer
will be spawned as soon as simulated
succeeds, and Cylc will start checking @allow_transfer
then. (There’s really no point in checking earlier than that, because transfer
could not run anyway due to its dependence on simulated
).
If many months are all at the transfer step, is allow_transfer called each time a spot is freed in the queue ?
It will be called as soon as the task enters the active window, and queued tasks are considered “active” (they are being “actively managed” by the scheduler).
A subtlety of xtriggers is that their uniqueness is determined by the uniqueness of their function argument values. I.e. Cylc assumes that if you call a function with the same arguments you should get the same result.
So if you define an xtrigger that simply checks “is it after-hours yet?” and then make every cycle depend on it, it will be considered satisfied for all time once it returns True, so it won’t constrain upcoming cycles at all.
So (if using xtriggers) you should add the dependent task’s cycle point as an xtrigger argument (the user guide shows how to do this - something like “%(point)s” as a string template for the cycle point). The xtrigger function might not use the argument at all, but it effectively makes a new xtrigger that will start checking anew every time a new task instance comes into the active window, instead of one that is shared by all dependent tasks.
This would fix your case (1.) IF transfer
has no other parents to spawn it into the active window - because the xtrigger won’t start checking until simulated
succeeds.
However, if transfer
gets spawned by another parent before simulated
succeeds, you will run into problem (1.).
For case (2.) problem is that all 12 months got queued (in your internal Cylc queue) - which means they are ready to run as soon as the queue releases them, but your queue only releases one task at a time, and before it empties external circumstances change such that you want the already-queued tasks be treated as “not ready” any more!
I think a robust solution might be (a) use a task instead of an xtrigger so that time-of-day checking does not start until simulated
succeeds, regardless of what parent tasks there are; and (b) set runahead limit = P0
to force a single cycle at a time (which your are effectively doing with the limit = 1
queue anyway). BTW simulated could potentially be an xtrigger.
# with runahead limit = P0
simulated => allow_transfer => transfer
or to still use the xtrigger:
@allow_transfer => dummy
simulated => dummy => tranfer
Note that with runahead limit P0 (one cycle at a time) the next simulated
won’t run until transfer
is finished. I’m guessing simulated it quick so that probably doesn’t matter, but to get more concurrency you could leave runahead limit alone and put in an intercycle dependency:
@allow_transfer => dummy
simulated => dummy => transfer
simulated[-P1M] => simulated