Resetting time to zero after cloning a climlab process

Brian Rose, 2/15/2022

Here are some notes on how to reset a model’s internal clock to zero after cloning a process with climlab.process_like()

These notes may become out of date after the next revision of climlab, because the calendar object that climlab uses will likely get replaced with something more robust in the future.

For posterity, this is the version of climlab we’re using in this example:

[1]:
import numpy as np
import climlab
climlab.__version__
[1]:
'0.7.13'

The climlab time dictionary

Every process object contains a time attribute, which is just a dictionary with various counters and information about timesteps.

Here we create a single-column radiation model m1 with a timestep of 1 day, and inspect its time dictionary:

[2]:
mystate = climlab.column_state()
m1 = climlab.radiation.RRTMG(state=mystate, timestep=climlab.utils.constants.seconds_per_day)
m1.time
[2]:
{'timestep': 86400.0,
 'num_steps_per_year': 365.2422,
 'day_of_year_index': 0,
 'steps': 0,
 'days_elapsed': 0,
 'years_elapsed': 0,
 'days_of_year': array([  0.,   1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.,  10.,
         11.,  12.,  13.,  14.,  15.,  16.,  17.,  18.,  19.,  20.,  21.,
         22.,  23.,  24.,  25.,  26.,  27.,  28.,  29.,  30.,  31.,  32.,
         33.,  34.,  35.,  36.,  37.,  38.,  39.,  40.,  41.,  42.,  43.,
         44.,  45.,  46.,  47.,  48.,  49.,  50.,  51.,  52.,  53.,  54.,
         55.,  56.,  57.,  58.,  59.,  60.,  61.,  62.,  63.,  64.,  65.,
         66.,  67.,  68.,  69.,  70.,  71.,  72.,  73.,  74.,  75.,  76.,
         77.,  78.,  79.,  80.,  81.,  82.,  83.,  84.,  85.,  86.,  87.,
         88.,  89.,  90.,  91.,  92.,  93.,  94.,  95.,  96.,  97.,  98.,
         99., 100., 101., 102., 103., 104., 105., 106., 107., 108., 109.,
        110., 111., 112., 113., 114., 115., 116., 117., 118., 119., 120.,
        121., 122., 123., 124., 125., 126., 127., 128., 129., 130., 131.,
        132., 133., 134., 135., 136., 137., 138., 139., 140., 141., 142.,
        143., 144., 145., 146., 147., 148., 149., 150., 151., 152., 153.,
        154., 155., 156., 157., 158., 159., 160., 161., 162., 163., 164.,
        165., 166., 167., 168., 169., 170., 171., 172., 173., 174., 175.,
        176., 177., 178., 179., 180., 181., 182., 183., 184., 185., 186.,
        187., 188., 189., 190., 191., 192., 193., 194., 195., 196., 197.,
        198., 199., 200., 201., 202., 203., 204., 205., 206., 207., 208.,
        209., 210., 211., 212., 213., 214., 215., 216., 217., 218., 219.,
        220., 221., 222., 223., 224., 225., 226., 227., 228., 229., 230.,
        231., 232., 233., 234., 235., 236., 237., 238., 239., 240., 241.,
        242., 243., 244., 245., 246., 247., 248., 249., 250., 251., 252.,
        253., 254., 255., 256., 257., 258., 259., 260., 261., 262., 263.,
        264., 265., 266., 267., 268., 269., 270., 271., 272., 273., 274.,
        275., 276., 277., 278., 279., 280., 281., 282., 283., 284., 285.,
        286., 287., 288., 289., 290., 291., 292., 293., 294., 295., 296.,
        297., 298., 299., 300., 301., 302., 303., 304., 305., 306., 307.,
        308., 309., 310., 311., 312., 313., 314., 315., 316., 317., 318.,
        319., 320., 321., 322., 323., 324., 325., 326., 327., 328., 329.,
        330., 331., 332., 333., 334., 335., 336., 337., 338., 339., 340.,
        341., 342., 343., 344., 345., 346., 347., 348., 349., 350., 351.,
        352., 353., 354., 355., 356., 357., 358., 359., 360., 361., 362.,
        363., 364., 365.]),
 'active_now': True}

If we take a single time step forward, some elements in this dictionary get updated:

[3]:
m1.step_forward()
m1.time
[3]:
{'timestep': 86400.0,
 'num_steps_per_year': 365.2422,
 'day_of_year_index': 1,
 'steps': 1,
 'days_elapsed': 1.0,
 'years_elapsed': 0,
 'days_of_year': array([  0.,   1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.,  10.,
         11.,  12.,  13.,  14.,  15.,  16.,  17.,  18.,  19.,  20.,  21.,
         22.,  23.,  24.,  25.,  26.,  27.,  28.,  29.,  30.,  31.,  32.,
         33.,  34.,  35.,  36.,  37.,  38.,  39.,  40.,  41.,  42.,  43.,
         44.,  45.,  46.,  47.,  48.,  49.,  50.,  51.,  52.,  53.,  54.,
         55.,  56.,  57.,  58.,  59.,  60.,  61.,  62.,  63.,  64.,  65.,
         66.,  67.,  68.,  69.,  70.,  71.,  72.,  73.,  74.,  75.,  76.,
         77.,  78.,  79.,  80.,  81.,  82.,  83.,  84.,  85.,  86.,  87.,
         88.,  89.,  90.,  91.,  92.,  93.,  94.,  95.,  96.,  97.,  98.,
         99., 100., 101., 102., 103., 104., 105., 106., 107., 108., 109.,
        110., 111., 112., 113., 114., 115., 116., 117., 118., 119., 120.,
        121., 122., 123., 124., 125., 126., 127., 128., 129., 130., 131.,
        132., 133., 134., 135., 136., 137., 138., 139., 140., 141., 142.,
        143., 144., 145., 146., 147., 148., 149., 150., 151., 152., 153.,
        154., 155., 156., 157., 158., 159., 160., 161., 162., 163., 164.,
        165., 166., 167., 168., 169., 170., 171., 172., 173., 174., 175.,
        176., 177., 178., 179., 180., 181., 182., 183., 184., 185., 186.,
        187., 188., 189., 190., 191., 192., 193., 194., 195., 196., 197.,
        198., 199., 200., 201., 202., 203., 204., 205., 206., 207., 208.,
        209., 210., 211., 212., 213., 214., 215., 216., 217., 218., 219.,
        220., 221., 222., 223., 224., 225., 226., 227., 228., 229., 230.,
        231., 232., 233., 234., 235., 236., 237., 238., 239., 240., 241.,
        242., 243., 244., 245., 246., 247., 248., 249., 250., 251., 252.,
        253., 254., 255., 256., 257., 258., 259., 260., 261., 262., 263.,
        264., 265., 266., 267., 268., 269., 270., 271., 272., 273., 274.,
        275., 276., 277., 278., 279., 280., 281., 282., 283., 284., 285.,
        286., 287., 288., 289., 290., 291., 292., 293., 294., 295., 296.,
        297., 298., 299., 300., 301., 302., 303., 304., 305., 306., 307.,
        308., 309., 310., 311., 312., 313., 314., 315., 316., 317., 318.,
        319., 320., 321., 322., 323., 324., 325., 326., 327., 328., 329.,
        330., 331., 332., 333., 334., 335., 336., 337., 338., 339., 340.,
        341., 342., 343., 344., 345., 346., 347., 348., 349., 350., 351.,
        352., 353., 354., 355., 356., 357., 358., 359., 360., 361., 362.,
        363., 364., 365.]),
 'active_now': True}

In particular, steps has increased by 1, and days_elapsed is now 1.0 (using a timestep of 1 day).

Let’s now clone this model. Both the state and the calendar are cloned, so our new model has the same date as m1:

[4]:
m2 = climlab.process_like(m1)
m2.time
[4]:
{'timestep': 86400.0,
 'num_steps_per_year': 365.2422,
 'day_of_year_index': 1,
 'steps': 1,
 'days_elapsed': 1.0,
 'years_elapsed': 0,
 'days_of_year': array([  0.,   1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.,  10.,
         11.,  12.,  13.,  14.,  15.,  16.,  17.,  18.,  19.,  20.,  21.,
         22.,  23.,  24.,  25.,  26.,  27.,  28.,  29.,  30.,  31.,  32.,
         33.,  34.,  35.,  36.,  37.,  38.,  39.,  40.,  41.,  42.,  43.,
         44.,  45.,  46.,  47.,  48.,  49.,  50.,  51.,  52.,  53.,  54.,
         55.,  56.,  57.,  58.,  59.,  60.,  61.,  62.,  63.,  64.,  65.,
         66.,  67.,  68.,  69.,  70.,  71.,  72.,  73.,  74.,  75.,  76.,
         77.,  78.,  79.,  80.,  81.,  82.,  83.,  84.,  85.,  86.,  87.,
         88.,  89.,  90.,  91.,  92.,  93.,  94.,  95.,  96.,  97.,  98.,
         99., 100., 101., 102., 103., 104., 105., 106., 107., 108., 109.,
        110., 111., 112., 113., 114., 115., 116., 117., 118., 119., 120.,
        121., 122., 123., 124., 125., 126., 127., 128., 129., 130., 131.,
        132., 133., 134., 135., 136., 137., 138., 139., 140., 141., 142.,
        143., 144., 145., 146., 147., 148., 149., 150., 151., 152., 153.,
        154., 155., 156., 157., 158., 159., 160., 161., 162., 163., 164.,
        165., 166., 167., 168., 169., 170., 171., 172., 173., 174., 175.,
        176., 177., 178., 179., 180., 181., 182., 183., 184., 185., 186.,
        187., 188., 189., 190., 191., 192., 193., 194., 195., 196., 197.,
        198., 199., 200., 201., 202., 203., 204., 205., 206., 207., 208.,
        209., 210., 211., 212., 213., 214., 215., 216., 217., 218., 219.,
        220., 221., 222., 223., 224., 225., 226., 227., 228., 229., 230.,
        231., 232., 233., 234., 235., 236., 237., 238., 239., 240., 241.,
        242., 243., 244., 245., 246., 247., 248., 249., 250., 251., 252.,
        253., 254., 255., 256., 257., 258., 259., 260., 261., 262., 263.,
        264., 265., 266., 267., 268., 269., 270., 271., 272., 273., 274.,
        275., 276., 277., 278., 279., 280., 281., 282., 283., 284., 285.,
        286., 287., 288., 289., 290., 291., 292., 293., 294., 295., 296.,
        297., 298., 299., 300., 301., 302., 303., 304., 305., 306., 307.,
        308., 309., 310., 311., 312., 313., 314., 315., 316., 317., 318.,
        319., 320., 321., 322., 323., 324., 325., 326., 327., 328., 329.,
        330., 331., 332., 333., 334., 335., 336., 337., 338., 339., 340.,
        341., 342., 343., 344., 345., 346., 347., 348., 349., 350., 351.,
        352., 353., 354., 355., 356., 357., 358., 359., 360., 361., 362.,
        363., 364., 365.]),
 'active_now': True}

What if we want to clone the state, but reset the calendar to zero?

One simple hack is just to keep a copy of the initial time dictionary prior to taking any steps forward.

We can do this with the dict’s built-in copy() method.

[5]:
mystate2 = climlab.column_state()
m3 = climlab.radiation.RRTMG(state=mystate2, timestep=climlab.utils.constants.seconds_per_day)
zero_time = m3.time.copy()
m3.step_forward()
m4 = climlab.process_like(m3)
# Now both m3 and m4 have the same state:
assert m3.Ts == m4.Ts
assert np.all(m3.Tatm == m4.Tatm)
# And they also have the same calendar:
print('After cloning, m3 has taken {} steps, and m4 has taken {} steps.'.format(m3.time['steps'], m4.time['steps']))
# But we can reset the calendar for m4 as if it had never taken a step forward:
m4.time = zero_time
print('After replacing the time dict, m3 has taken {} steps, and m4 has taken {} steps.'.format(m3.time['steps'], m4.time['steps']))
After cloning, m3 has taken 1 steps, and m4 has taken 1 steps.
After replacing the time dict, m3 has taken 1 steps, and m4 has taken 0 steps.

Since we haven’t changed any model parameters, they should both evolve exactly the same way on their next timestep so the states remain the same:

[6]:
for model in [m3, m4]:
    model.step_forward()
assert m3.Ts == m4.Ts
assert np.all(m3.Tatm == m4.Tatm)
print('After one more step, m3 has taken {} steps, and m4 has taken {} steps.'.format(m3.time['steps'], m4.time['steps']))
After one more step, m3 has taken 2 steps, and m4 has taken 1 steps.

But if I now change a parameter in m4, their states will begin to differ:

[7]:
m4.subprocess['SW'].S0 += 10.
for model in [m3, m4]:
    model.step_forward()
print('One step after changing S0 in m4, m3 has taken {} steps, and m4 has taken {} steps.'.format(m3.time['steps'], m4.time['steps']))
print('')
print('Now checking to see if the states are still the same:')
assert m3.Ts == m4.Ts
One step after changing S0 in m4, m3 has taken 3 steps, and m4 has taken 2 steps.

Now checking to see if the states are still the same:
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
Input In [7], in <module>
      5 print('')
      6 print('Now checking to see if the states are still the same:')
----> 7 assert m3.Ts == m4.Ts

AssertionError:

The assertion fails because the surface temperatures of the two states have diverged, as expected.

[ ]: