What is this?
common purpose.
What does this cover?
The best reference for cylc is the cylc User Guide - some of the following images are taken from there.
Originally called cylon, cylc was first started by Hilary Oliver at NIWA. It runs their EcoConnect operational suite.
cylc was developed to provide a fast, automated way of scheduling tasks so that a suite could catch up after outages.
cylc is now a collaborative effort, involving the Met Office.
cylc is written in Python and uses PyGTK for its GUIs. It is open source and licensed under GPL v3.
cylc lives on github.
Scheduling - making sure tasks run in the correct order, at the correct time - is the core purpose of cylc. The cylc scheduling algorithm is very simple, and very powerful:
Task dependencies - single cycle
Most meteorological suites repeat over time (cycling):
A cycle isn't a prison - if tasks can run ahead of their cycle time, they should. The image shows how a traditional cycle-fixed scheduler operates (bottom) vs cylc (top).
cylc has a single file to configure the suite, the suite.rc file. It configures:
cylc uses a nested INI-based configuration format that looks like this:
[scheduling] # Scheduling section [[dependencies]] # Dependencies sub-section graph = my_task # Graph Setting, option = value [runtime] # Runtime Section [[my_task]] # User-specified task sub-section [[[environment]]] # Environment sub-sub-section FOO = bar # User-specified, option = value
A simple cylc suite has a single suite.rc file that looks like this:
[scheduling] [[dependencies]] graph = hello_world [runtime] [[hello_world]] script = echo 'Hello World'
This will run a cylc task called hello_world that prints Hello World to standard out.
Demo this by creating and running the simple cylc suite:
mkdir $TMPDIR/simplesuite # Make the suite directory touch $TMPDIR/simplesuite/rose-suite.conf # Rose needs a rose-suite.conf cat >$TMPDIR/simplesuite/suite.rc <<__SUITE_RC__ [scheduling] [[dependencies]] graph = hello_world [runtime] [[hello_world]] script = echo 'Hello World' __SUITE_RC__ rose suite-run -C $TMPDIR/simplesuite # Run the suite!
As we saw before, the suite will run and shutdown when all tasks are successful - in this case, the only task is hello_world.
The suite output will be in our cylc-run directory, which you can also access by running rose suite-log --name=simplesuite.
Tasks such as our hello_world task above are generic - they can be Rose applications, or commands or executables as above.
If we wanted to invoke a Rose application, we'd make an app called hello_world, by making an app/hello_world/ directory and creating a rose-app.conf file in there with this content:
[command] default=echo 'Hello World'
We would then have to change the [runtime] section to:
[runtime] [[hello_world]] script = rose task-run
rose task-run
uses environment
variables passed in by cylc (like
CYLC_TASK_NAME) to figure out which Rose
application to run. In this case, it'll be
app/hello_world
rose task-run
is reasonably
generic, so we can put it as the suite default
([[root]])script by
writing:
[runtime] [[root]] script = rose task-run [[hello_world]]
[[root]] contains default settings
that all cylc tasks inherit from. It is a special
case of a cylc family
. Families
contain shared settings for one or more tasks.
Families define shared configuration.
Families can be used to reduce duplication between tasks.
Overriding family settings is possible.
Multiple inheritance.
Families can also be used to help write the dependencies - e.g. to set up a task so that it runs when all the tasks in a family succeed (more later). They can also be used in queues.
Here is a suite.rc [runtime] section snippet for a suite with two similar applications, hello_eris/ and hello_pluto/
[runtime] [[root]] script = rose task-run [[HELLO_FAMILY]] [[[environment]]] IS_WORLD_A_PLANET = false # Shared env variable [[hello_eris]] inherit = HELLO_FAMILY [[hello_pluto]] inherit = HELLO_FAMILY
Overriding family settings can be done under a task section:
[runtime] [[root]] script = rose task-run [[HELLO_FAMILY]] [[[environment]]] IS_WORLD_A_PLANET = false # Shared env variable [[hello_eris]] inherit = HELLO_FAMILY [[hello_pluto]] inherit = HELLO_FAMILY [[[environment]]] IS_WORLD_A_PLANET = true # Override.
In the last example, if we don't really need two Rose applications, (app/hello_eris/ and app/hello_pluto/) we can create two tasks that share a single application, with some override settings - in this example, a WORLD environment variable.
We can reference our single application,
app/hello_world/ by using
ROSE_TASK_APP. rose
task-run
will read this environment variable
value and use it to run the Rose app. We will put
the override setting (WORLD) in the
suite.rc file.
[runtime] [[root]] script = rose task-run [[HELLO_FAMILY]] [[[environment]]] IS_WORLD_A_PLANET = false # Shared env variable ROSE_TASK_APP = hello_world [[hello_eris]] inherit = HELLO_FAMILY [[[environment]]] WORLD = eris [[hello_pluto]] inherit = HELLO_FAMILY [[[environment]]] WORLD = pluto
cylc supports multiple inheritance, so tasks can combine useful configuration from more than one separate family. If you set up families like this:
[[HELLO_FAMILY]] [[[environment]]] ROSE_TASK_APP = hello_world [[GAS_GIANT_FAMILY]] [[[environment]]] ATMOSPHERE_ONLY = true [[ROCKY_FAMILY]] [[[environment]]] ATMOSPHERE_ONLY = false
You can inherit them like this:
[[hello_neptune]] inherit = HELLO_FAMILY, GAS_GIANT_FAMILY [[[environment]]] WORLD = neptune
The order in which they are combined is essentially last to first - e.g. HELLO_FAMILY will override any shared setting in GAS_GIANT_FAMILY.
So far, the example tasks run on the localhost - it is usually better to farm off tasks to a remote host like a compute server or a cluster/supercomputer.
We could set hello_eris to run on a given host by setting [[[remote]]] section settings:
[[hello_eris]] inherit = HELLO_FAMILY [[[environment]]] WORLD = eris [[[remote]]] host = voyager_1
We haven't looked at the [scheduling] part of the suite.rc yet.
Let's say hello_pluto must run and succeed before hello_eris. We can put this in our suite.rc:
[scheduling] [[dependencies]] graph = hello_pluto => hello_eris
We can make this run as a cycling suite, repeating every 12 hours:
[scheduling] initial cycle point = 20130105T00Z # 00:00, 5/1/2013 final cycle point = 20130106T00Z # 00:00, 6/1/2013 [[dependencies]] [[[T00, T12]]] # run each day at 00:00 and 12:00 graph = hello_pluto => hello_eris
N.B. cylc will not work with the various cycle points in real time unless you ask it to. For details see the clock triggered tasks tutorial.
cylc provides an inbuilt templating language that you can use to generate repeated graphing and runtime entries by working through a list of parameter values.
This allows you to reduce down sections of the suite.rc file into special instruction code that will be expanded by cylc at runtime.
This can be particularly useful when you have large amounts of repetition - for example, in an ensemble context.
Sets of parameters are configured in the [cylc][[parameters]] section. For example:
[cylc] [[parameters]] world = eris, pluto, makemake, haumea
You can then get cylc to work through a set of parameters using <parameter> tags.
[scheduling] [[dependencies]] graph = hello_<world>
Similarly, you can work with parameterized items in the runtime section too, further helping you reduce down your suite.rc:
[[hello_<world>]] inherit = HELLO_FAMILY [[[environment]]] WORLD = $CYLC_TASK_PARAM_world
Notice that the task name uses <world> to access the parameter value, telling cylc that it is a repeated item, while access to its value at runtime is provided by a $CYLC_TASK_PARAM_ variable with a suffix of the parameter name (in this case world).
This gets processed internally by the running suite process, so cylc sees it in the same way as if you had explicitly written:
[[hello_eris]] inherit = HELLO_FAMILY [[[environment]]] WORLD = eris [[hello_pluto]] inherit = HELLO_FAMILY [[[environment]]] WORLD = pluto [[hello_makemake]] inherit = HELLO_FAMILY [[[environment]]] WORLD = makemake [[hello_haumea]] inherit = HELLO_FAMILY [[[environment]]] WORLD = haumea
If, for a particular task that is being parameterized, you want to provide a slight variant then you can do so as:
[[hello_<world>]] # General case inherit = HELLO_FAMILY [[[environment]]] WORLD = $CYLC_TASK_PARAM_world [[hello_<world=pluto>]] # Specific case inherit = HELLO_FAMILY [[[environment]]] WORLD = $CYLC_TASK_PARAM_world MOON = True
Make sure that any specific cases follow the generic one as the last config entry in the suite.rc will override any earlier ones.
Beyond the examples given, Parameterization can be used to automatically generate more complex graphing sections between several sets of parameters. For more details see the Cylc User Guide.
cylc uses a templating language called Jinja2 that you can use to embed special instruction code in the suite.rc file to generate or insert text that will be expanded at runtime.
This can be especially useful when you have a
some task or graphing that you want to be able to easily
swap between two modes - for example, for turning on or off
an archiving task. You can use if
and
for
blocks, amongst other things:
{% set ARCHIVE_RESULTS=true %} [scheduling] [[runtime]] [[[dependencies]]] graph = """ HELLO_FAMILY {% if ARCHIVE_RESULTS %} HELLO_FAMILY:succeed-all => archive_results {% endif %} """
This gets processed at suite install time. So, if ARCHIVE_RESULTS is set then the file as read in by cylc looks like:
[scheduling] [[runtime]] [[[dependencies]]] graph = """ HELLO_FAMILY HELLO_FAMILY:succeed-all => archive_results """
Conversely, if ARCHIVE_RESULTS is not set then the file as read in by cylc will look like:
[scheduling] [[runtime]] [[[dependencies]]] graph = """ HELLO_FAMILY """
You can use Jinja2 to centralise commonly-used settings. Rose supports storing these in the rose-suite.conf file - e.g.
[jinja2:suite.rc] ARCHIVE_RESULTS=true
Rose passes variables in this section into the Jinja2 template at runtime so your user doesn't have to look for the line to change in the suite.rc file.
Furthermore, you can also add proper metadata for them such as help, so they have a nice interface in the config editor.
For example, if we had a suite.rc setting that looked like this:
[[[environment]]] INCLUDE_MOONS = {{ HELLO_TO_MOONS }}
We could have a rose-suite.conf file that looked like this:
[jinja2:suite.rc] HELLO_TO_MOONS=false
We could then write some nice metadata (similar to app metadata) for the rose-suite.conf file, such as:
[jinja2:suite.rc=HELLO_TO_MOONS] help=Decide whether to say hello to the moons of the world =(if any). = =If true, include moons like =http://en.wikipedia.org/wiki/Dysnomia_%28moon%29 =If false, ignore them. = =Moons of moons are not supported. title=Include Moons when saying hello? type=boolean
We recommend running all suites in UTC mode. E.g.:
[cylc] UTC mode = True # Ignore DST
Suites can have event handlers to report events or shutdown on failure.
[cylc] UTC mode = True # Ignore DST # abort if any task fails = True [[events]] # abort on timeout = True # mail events = timeout # timeout = P3D
Normally, when a task fails, the suite will
continue to run as much as possible and wait for
user input to fix (and perhaps retry) the failure
so it can continue. If abort if any task
fails = True
is not commented out, the suite
will abort as soon as a task fails.
Suites can also be configured to email you on specific events. For example:
[runtime] [[root]] [[[events]]] mail events = submission retry, retry, \ submission failed, failed, \ submission timeout, execution timeout
The events should be adjusted if necessary - for example, increasing the timeout lengths or altering the events that require email notification.
We may want to configure a job submission
method, such as using PBS
:
[[hello_eris]] ... [[[job]]] batch system = pbs
The job submission method may need configuration
via directives
:
[[hello_eris]] ... [[[job]]] batch system = pbs execution time limit = PT5M # generates directives walltime value [[[directives]]] -l select = 1 -q = normal
cylc supports the following job submission methods (and more):
Next Steps: