= 0.1 # hours
dt = 20
days = np.arange(0, 24 * days, dt)
time = 500
regular_lux = LightSchedule.Regular(regular_lux, lights_on=8, lights_off=24)
schedule = schedule(time)
light_input = [Forger99(), Jewett99(), Hannay19(), Hannay19TP()]
model_list = []
equilibrium_states
for model in model_list:
= np.arange(0, 24 * days, dt)
time_eq = model.equilibrate(time_eq, light_input, num_loops=2)
final_state equilibrium_states.append(final_state)
Circadian rhythm disruptions
Light exposure is one of the main factors affecting circadian rhythms. Here we use the circadian
package to explore the effects of light pulses at different times of the day. We will explore this effect using four different circadian models Forger99
, Jewett99
, Hannay19
, and Hannay19TP
and compare the results among them.
Entraining models to a regular light schedule
First, we entrain each model to a regular light schedule to have a baseline to compare to. In circadian
we do this by
Pulse during the day
Next, we can explore how models respond to lights pulse during the bright hours of the day. We can use the LightSchedule.from_pulse
function to add pulses at different times
= 3
days = np.arange(0, 24 * days, dt)
time = 6
pulse_num = 1e4
pulse_lux = 1 # hour
pulse_duration = np.linspace(32, 47, pulse_num)
start_values
= {}
simulation_result
for idx,model in enumerate(model_list):
str(model)] = {}
simulation_result[for pulse_start in start_values:
= LightSchedule.Regular(regular_lux, lights_on=8, lights_off=24)
schedule += LightSchedule.from_pulse(pulse_lux, pulse_start, pulse_duration)
schedule = schedule(time)
light_input = model(time, equilibrium_states[idx], light_input)
trajectory str(model)][str(pulse_start)] = {
simulation_result['light': light_input,
'trajectory': trajectory
}
We see that light pulses during bright hours don’t have a major effect on circadian rhythms. However, this is not the case for light pulses during darkness.
Pulse at night
Using the same code as above but with different pulse start values
= np.linspace(24, 31, pulse_num) start_values
gives us the following
Models are more sensitive to pulses during the dark hours of the day, the sinusoidal signal changes abruptly when the pulse is applied. This reflects an important property of circadian rhythms: their sensitivity to light is dependent on the current phase of the clock. We can calculate how much the phase of the clock changes after a pulse by constructing a phase response curve (PRC).
Building phase response curves
To build a phase response curve we need to calculate how much the phase of the clock changed after the pulse ended with respect to an unperturbed clock. We can do this the following way
from circadian.utils import phase_difference
= Forger99()
model
= 3
days = np.arange(0, 24 * days, dt)
time = 1e4
pulse_lux = 25 # hours
pulse_start = 1 # hour
pulse_duration
= LightSchedule.Regular(regular_lux, lights_on=8, lights_off=24)
regular_schedule = LightSchedule.from_pulse(pulse_lux, pulse_start, pulse_duration)
pulse = regular_schedule + pulse
pulse_schedule
= regular_schedule(time)
regular_light = pulse_schedule(time)
pulse_light
= model(time, equilibrium_states[0], regular_light)
regular_trajectory = model(time, equilibrium_states[0], pulse_light)
pulse_trajectory
= pulse_start + pulse_duration
pulse_end
= model.phase(regular_trajectory, pulse_end)
regular_phase = model.phase(pulse_trajectory, pulse_end)
pulse_phase
= phase_difference(regular_phase, pulse_phase) phase_diff
Phase difference: -0.16 radians
This negative value means that the pulse delays the clock. We can visualize this by comparing the state of the clock before and after the pulse
Here we can also observe that the amplitude of the clock is reduced after the pulse. To calculate this we do the following
from circadian.utils import amplitude_percent_change
= model.amplitude(regular_trajectory, pulse_end)
regular_amplitude = model.amplitude(pulse_trajectory, pulse_end)
pulse_amplitude
= amplitude_percent_change(regular_amplitude, pulse_amplitude) amplitude_change
Amplitude change: -12.40%
So the pulse both delays the clock and reduces its amplitude. We can now calculate the phase response curve by repeating this process for different pulse times. We will also store the amplitude change information for the following section.
= 2.5
days = 0.02 # hours. We need a smaller dt to get a smooth phase response curve
dt = np.arange(0, 24 * days, dt)
time = 150
pulse_num = 1e4
pulse_lux = 1 # hour
pulse_duration = np.linspace(24, 48, pulse_num)
start_values
for idx,model in enumerate(model_list):
str(model)] = {
simulation_result['cbtmin': np.NaN,
'phase_response': [],
'amplitude_response': [],
}# create the reference trajectory
= LightSchedule.Regular(regular_lux, lights_on=8, lights_off=24)
regular_schedule = regular_schedule(time)
regular_light = model(time, equilibrium_states[idx], regular_light)
regular_trajectory
# calculate cbt to use as pulse start time reference
= model.cbt(regular_trajectory)[1]
cbtmin str(model)]['cbtmin'] = cbtmin
simulation_result[
for pulse_start in start_values:
= LightSchedule.Regular(regular_lux, lights_on=8, lights_off=24)
schedule += LightSchedule.from_pulse(pulse_lux, pulse_start, pulse_duration)
schedule = schedule(time)
light_input = model(time, equilibrium_states[idx], light_input)
pulse_trajectory
= pulse_start + pulse_duration
pulse_end
= model.phase(regular_trajectory, pulse_end)
regular_phase = model.phase(pulse_trajectory, pulse_end)
pulse_phase = phase_difference(regular_phase, pulse_phase)
phase_diff str(model)]['phase_response'].append(phase_diff)
simulation_result[
= model.amplitude(regular_trajectory, pulse_end)
regular_amplitude = model.amplitude(pulse_trajectory, pulse_end)
pulse_amplitude = amplitude_percent_change(regular_amplitude, pulse_amplitude)
amplitude_change str(model)]['amplitude_response'].append(amplitude_change)
simulation_result[
# convert phase differences from radians to hours
= 24.2 # hours
period for model in model_list:
= simulation_result[str(model)]['phase_response']
phase_response = np.array(phase_response) * period / (2 * np.pi)
phase_response str(model)]['phase_response'] = phase_response simulation_result[
This is the phase response curve for four different models. On the y-axis we have how much the phase of the circadian clock changes when the pulse ends. Positive values mean the clock is advanced with respect to an unperturbed case. On the x-axis we have the pulse end time relative to the core body temperature minimum (CBTmin). When the pulse ends close to CBTmin (x-axis value of 0) the phase of the clock is maximally advanced. On the contrary, when the pulse ends five hours before CBTmin, the clock is delayed the most. The overall shape of the response is similar between models.
Building amplitude response curves
Our previous simulation shows that not only the phase of the clock changes after a pulse, but also its amplitude. We already calculated the amplitude change in the previous section so we can plot the result
The models still have a similar response: all of them show a decrease in amplitude four hours before CBTmin. However, the magnitude of change in amplitude varies between them. For a more detailed discussion we refer the reader to Hannay et al. (2019).