Arithmetic with $CYLC_TASK_CYCLE _POINT in standard date/time format

Hey there

I need to be able to do integer arithmetic with one bit of $CYLC_TASK_CYCLE_POINT, specifically the year; i.e. I need 1950 from 19500101T0000Z.

This works…

echo $CYLC_TASK_CYCLE_POINT | cut -c1-4

1950

… but I then want to do this…

1950 * a * b / c

… where a, b and c are constants.

Does anyone have any ideas about how to do this?

Thanks!

Jonny

Hi Jonny,

Firstly regarding the first step, grabbing the year only from the full datetime. If you are using Cylc with Rose, you can do this in a more explicit way than the bash pipe you state (though there is otherwise no problem with that) which was designed for doing such datetime manipulation, the ‘rose date’ command (see also here), which can format datetimes. Use the 'print-format- option, like so:

rose date ${CYLC_TASK_CYCLE_POINT} --print-format='%Y'

To use this in a suites script, wrap it in a $( ... ) construct.

For performing the arithmetic, it should just be a case of applying bash arithmetic, where I’m using some arbitarry integers as an example here:

expr <1950 calcuated via the above> \* 70 \* 80 / 4

Note you need to escape the asterisk so it is not interpreted as a wildcard character by the shell. So, overall this should work (replace with your bash solution for 1950 if you don’t want to use Rose):

expr $(rose date ${CYLC_TASK_CYCLE_POINT} --print-format='%Y') \* 70 \* 80 / 4

You could also use Jinja2 to do the trimming of all but the year (using string indexing [:4] for a sub-string, exactly as for Python) & the arithmetic, but since you have put some bash it seems you want to use this directly in your task script, & with Jinja2 you would have to convert into the equivalent environment variable to use in the script, so it is a more contrived solution. However, if you want to extend your current logic significantly to do further arithmetic, Jinja2 might be the way to go as it is very powerful. [UPDATE: this is incorrect; Jinja2 is processed at start-up, whereas CYLC_TASK_CYCLE_POINT is a job environment variable set at run time - HO]

Hope that helps!

1 Like

Optionally, one can use double parenthesis to perform bash arithmetic - that way escaping the asterisks is not required
$(( $(rose date ${CYLC_TASK_CYCLE_POINT} -f '%Y')*70*80/4 ))

3 Likes

Good point, that’s much nicer!

1 Like

Note Cylc also has a built-in utility for doing date-time arithmetic and extracting date-time components such as the year value. See:

cylc cyclepoint --help
2 Likes

Thanks a lot for your help everyone, all very useful!

Here is my final code…

_1_nn_it000=$((($(cylc cyclepoint --print-year)-1950)*360*86400/900+1))
sed "s/nn_it000=.*/nn_it000=$_1_nn_it000,/g" -i $CYLC_SUITE_RUN_DIR/bin/1_namelist_cfg

Here’s what it does…

line 1 - Define an environment variable which is equal to the number of timesteps of length 900s at the beginning of a cycle point. 360*86400 is the number of seconds per cycle point, i.e. per year in this case.

line 2 - replace nn_it000=foo with nn_it000=$_1_nn_it000 in $CYLC_SUITE_RUN_DIR/bin/1_namelist_cfg.

I can probably make line 1 even more portable eventually since 1950 is the suite start date and the value of 900s could change. Also the cycle point length of 1 year is also arbitrary.

Thanks a lot for your help.

Jonny

1 Like

I’m late to the party, but when we’ve done this before we’ve actually defined environment variables with Jinja2 filters that allow us to convert ISO8601 durations and do math with them. In your example here, you need to figure out (a) the number of seconds per cycle point (which is a function of the cycling interval), and (b) the number of 900s timesteps (which is a function of the cycling interval from (a) as well as timestep length).

So you can define at a top level within the suite the cycling interval (here P1Y) and the timestep length (PT900S) in “standard” notation, then use those filters to put everything together to define the variables that you need.

1 Like

Hey Tim

That sounds interesting and certainly useful.

I’ll look into whether this would be suitable for our purposes. The solution I have at the moment works fine but is a bit clunky.

Cheers

Jonny

Not on MOSRS, but I’ll see if I can dig up an illustrative example.

1 Like