Skip to content

Commit ef0d625

Browse files
committed
Update documentation
1 parent e9651e7 commit ef0d625

713 files changed

Lines changed: 2997658 additions & 197164 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.buildinfo

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Sphinx build info version 1
22
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
3-
config: 3c4fc6418eec8a888d9bfced2b71f28c
3+
config: 862fd0596be33d5281a36c240023a5e9
44
tags: 645f666f9bcd5a90fca523b33c5a78b7
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import streamlit as st
2+
import numpy as np
3+
from stroke_rehab_model import (
4+
Experiment, multiple_replications, combine_pdelay_results,
5+
combine_occup_results, mean_results, prob_delay_plot,
6+
occupancy_plot, summary_table
7+
)
8+
9+
def main():
10+
st.title("Stroke Rehabilitation Model Simulation")
11+
12+
# Sidebar for Experiment parameters
13+
st.sidebar.header("Simulation Parameters")
14+
15+
# Inter-arrival rates
16+
st.sidebar.subheader("Inter-arrival Rates")
17+
stroke_mean = st.sidebar.number_input("Stroke patients", value=1.2, step=0.1)
18+
tia_mean = st.sidebar.number_input("TIA patients", value=9.3, step=0.1)
19+
neuro_mean = st.sidebar.number_input("Complex Neurological patients", value=3.6, step=0.1)
20+
other_mean = st.sidebar.number_input("Other patients", value=3.2, step=0.1)
21+
22+
# Model control
23+
st.sidebar.subheader("Model Control")
24+
trace = st.sidebar.checkbox("Trace patients in simulation", value=False)
25+
warm_up = st.sidebar.number_input("Warm-up period", value=1095, step=1)
26+
27+
# Number of replications
28+
num_replications = st.number_input("Number of replications", value=100, min_value=1, step=1)
29+
30+
if st.button("Simulate"):
31+
with st.spinner("Please wait for results..."):
32+
# Create an instance of Experiment with user-defined parameters
33+
experiment = Experiment(
34+
stroke_mean=stroke_mean,
35+
tia_mean=tia_mean,
36+
neuro_mean=neuro_mean,
37+
other_mean=other_mean,
38+
trace=trace,
39+
warm_up=warm_up
40+
)
41+
42+
# Run multiple replications
43+
rep_results = multiple_replications(experiment, num_replications)
44+
45+
# Combine results and take the mean
46+
pd_asu, pd_rehab = combine_pdelay_results(rep_results)
47+
rel_asu, rel_rehab = combine_occup_results(rep_results)
48+
mean_pd_asu, mean_pd_rehab = mean_results(pd_asu), mean_results(pd_rehab)
49+
mean_rel_asu, mean_rel_rehab = mean_results(rel_asu), mean_results(rel_rehab)
50+
51+
# Display tables
52+
st.subheader("Acute Care Results")
53+
df_acute = summary_table(mean_pd_asu, 9, 14, "acute")
54+
st.table(df_acute)
55+
56+
st.subheader("Rehabilitation Results")
57+
df_rehab = summary_table(mean_pd_rehab, 10, 16, "rehab")
58+
st.table(df_rehab)
59+
60+
# Display plots
61+
st.subheader("Probability Delay Plots")
62+
fig_pd_asu, ax_pd_asu = prob_delay_plot(mean_pd_asu, np.arange(0, 30))
63+
st.pyplot(fig_pd_asu)
64+
65+
fig_pd_rehab, ax_pd_rehab = prob_delay_plot(mean_pd_rehab, np.arange(0, 30), "No. rehab beds available")
66+
st.pyplot(fig_pd_rehab)
67+
68+
st.subheader("Occupancy Plots")
69+
fig_occ_asu, ax_occ_asu = occupancy_plot(mean_rel_asu, np.arange(0, 30))
70+
st.pyplot(fig_occ_asu)
71+
72+
fig_occ_rehab, ax_occ_rehab = occupancy_plot(mean_rel_rehab, np.arange(0, 30), "No. people in rehab")
73+
st.pyplot(fig_occ_rehab)
74+
75+
if __name__ == "__main__":
76+
main()
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
import streamlit as st
2+
import simpy
3+
import numpy as np
4+
import pandas as pd
5+
6+
class Experiment:
7+
def __init__(self,
8+
interarrival_means=[22.72, 26.0, 37.0, 47.2, 575.0, 17.91],
9+
stay_distributions=[(128.79, 267.51), (177.89, 276.54), (140.15, 218.02), (212.86, 457.67), (87.53, 108.67), 57.34],
10+
elective_treatment_mean=57.34,
11+
num_critical_care_beds=24,
12+
intensive_cleaning_duration=5,
13+
warm_up_period=30*24,
14+
results_collection_period=12*30*24,
15+
trace=False,
16+
random_number_set=0):
17+
self.interarrival_means = interarrival_means
18+
self.stay_distributions = stay_distributions
19+
self.elective_treatment_mean = elective_treatment_mean
20+
self.num_critical_care_beds = num_critical_care_beds
21+
self.intensive_cleaning_duration = intensive_cleaning_duration
22+
self.warm_up_period = warm_up_period
23+
self.results_collection_period = results_collection_period
24+
self.total_treatment_time = 0
25+
self.cancelled_elective_count = 0
26+
self.mean_waiting_time_unplanned = 0
27+
self.total_unplanned_admissions = 0
28+
self.bed_occupancy = 0
29+
self.trace = trace
30+
self.patient_count = 0
31+
self.random_number_set = random_number_set
32+
self.streams = []
33+
34+
self.setup_streams(random_number_set)
35+
36+
def reset_kpi(self):
37+
self.total_treatment_time = 0
38+
self.cancelled_elective_count = 0
39+
self.mean_waiting_time_unplanned = 0
40+
self.total_unplanned_admissions = 0
41+
self.bed_occupancy = 0
42+
self.patient_count = 0
43+
44+
def setup_streams(self, random_number_set):
45+
self.streams = []
46+
rng = np.random.default_rng(random_number_set)
47+
seeds = rng.integers(0, np.iinfo(np.int64).max, size=12)
48+
for seed in seeds:
49+
self.streams.append(np.random.default_rng(seed))
50+
51+
class CCUModel:
52+
def __init__(self, env, experiment):
53+
self.env = env
54+
self.experiment = experiment
55+
self.patient_count = 0
56+
self.critical_care_beds = simpy.Resource(env, capacity=experiment.num_critical_care_beds)
57+
58+
def patient_arrival_AE(self):
59+
while True:
60+
yield self.env.timeout(self.experiment.streams[0].exponential(self.experiment.interarrival_means[0]))
61+
self.patient_count += 1
62+
if self.experiment.trace:
63+
print(f"Patient {self.patient_count} arrived from Accident and Emergency at time {self.env.now}")
64+
self.env.process(self.unplanned_admission(self.experiment.stay_distributions[0]))
65+
66+
def patient_arrival_wards(self):
67+
while True:
68+
yield self.env.timeout(self.experiment.streams[1].exponential(self.experiment.interarrival_means[1]))
69+
self.patient_count += 1
70+
if self.experiment.trace:
71+
print(f"Patient {self.patient_count} arrived from the Wards at time {self.env.now}")
72+
self.env.process(self.unplanned_admission(self.experiment.stay_distributions[1]))
73+
74+
def patient_arrival_surgery(self):
75+
while True:
76+
yield self.env.timeout(self.experiment.streams[2].exponential(self.experiment.interarrival_means[2]))
77+
self.patient_count += 1
78+
if self.experiment.trace:
79+
print(f"Patient {self.patient_count} arrived from Emergency surgery at time {self.env.now}")
80+
self.env.process(self.unplanned_admission(self.experiment.stay_distributions[2]))
81+
82+
def patient_arrival_other_hospitals(self):
83+
while True:
84+
yield self.env.timeout(self.experiment.streams[3].exponential(self.experiment.interarrival_means[3]))
85+
self.patient_count += 1
86+
if self.experiment.trace:
87+
print(f"Patient {self.patient_count} arrived from other hospitals at time {self.env.now}")
88+
self.env.process(self.unplanned_admission(self.experiment.stay_distributions[3]))
89+
90+
def patient_arrival_X_ray(self):
91+
while True:
92+
yield self.env.timeout(self.experiment.streams[4].exponential(self.experiment.interarrival_means[4]))
93+
self.patient_count += 1
94+
if self.experiment.trace:
95+
print(f"Patient {self.patient_count} arrived from the X-Ray department at time {self.env.now}")
96+
self.env.process(self.unplanned_admission(self.experiment.stay_distributions[4]))
97+
98+
def patient_arrival_elective_surgery(self):
99+
while True:
100+
yield self.env.timeout(self.experiment.streams[5].normal(self.experiment.interarrival_means[5], 3.16))
101+
self.patient_count += 1
102+
if self.experiment.trace:
103+
print(f"Elective surgery patient {self.patient_count} arrived at time {self.env.now}")
104+
if len(self.critical_care_beds.users) == self.critical_care_beds.capacity:
105+
if self.experiment.trace:
106+
print(f"Elective surgery for patient {self.patient_count} cancelled due to no available critical care beds at time {self.env.now}")
107+
if self.env.now > self.experiment.warm_up_period:
108+
self.experiment.cancelled_elective_count += 1
109+
else:
110+
self.env.process(self.elective_surgery_process(self.experiment.elective_treatment_mean))
111+
112+
def unplanned_admission(self, stay_distribution):
113+
arrival_time = self.env.now
114+
with self.critical_care_beds.request() as req:
115+
yield req
116+
wait_time = self.env.now - arrival_time
117+
if self.experiment.trace:
118+
print(f"Patient {self.patient_count} admitted to critical care bed at time {self.env.now}")
119+
treatment_time = self.experiment.streams[6].lognormal(np.log(stay_distribution[0]) - 0.5 * np.log(1 + (stay_distribution[1] / stay_distribution[0])**2),
120+
np.sqrt(np.log(1 + (stay_distribution[1] / stay_distribution[0])**2)))
121+
yield self.env.timeout(treatment_time)
122+
if self.experiment.trace:
123+
print(f"Patient {self.patient_count} discharged from critical care bed at time {self.env.now}")
124+
if self.env.now > self.experiment.warm_up_period:
125+
self.experiment.total_treatment_time += treatment_time
126+
self.experiment.mean_waiting_time_unplanned += wait_time
127+
self.experiment.total_unplanned_admissions += 1
128+
yield self.env.timeout(self.experiment.intensive_cleaning_duration)
129+
if self.experiment.trace:
130+
print(f"Critical care bed is available for next patient at time {self.env.now}")
131+
132+
def elective_surgery_process(self, treatment_mean):
133+
with self.critical_care_beds.request() as req:
134+
yield req
135+
if self.experiment.trace:
136+
print(f"Elective surgery patient {self.patient_count} admitted to critical care bed at time {self.env.now}")
137+
treatment_time = self.experiment.streams[7].exponential(treatment_mean)
138+
yield self.env.timeout(treatment_time)
139+
if self.experiment.trace:
140+
print(f"Elective surgery patient {self.patient_count} discharged from critical care bed at time {self.env.now}")
141+
if self.env.now > self.experiment.warm_up_period:
142+
self.experiment.total_treatment_time += treatment_time
143+
yield self.env.timeout(self.experiment.intensive_cleaning_duration)
144+
if self.experiment.trace:
145+
print(f"Critical care bed is available for next patient at time {self.env.now}")
146+
147+
def warmup_complete(self):
148+
yield self.env.timeout(self.experiment.warm_up_period)
149+
self.patient_count = 0
150+
if self.experiment.trace:
151+
print("Warm-up complete")
152+
153+
def run(self):
154+
self.env.process(self.patient_arrival_AE())
155+
self.env.process(self.patient_arrival_wards())
156+
self.env.process(self.patient_arrival_surgery())
157+
self.env.process(self.patient_arrival_other_hospitals())
158+
self.env.process(self.patient_arrival_X_ray())
159+
self.env.process(self.patient_arrival_elective_surgery())
160+
self.env.process(self.warmup_complete())
161+
self.env.run(until=self.experiment.results_collection_period + self.experiment.warm_up_period)
162+
if self.env.now > self.experiment.warm_up_period:
163+
mean_waiting_time_unplanned = self.experiment.mean_waiting_time_unplanned / self.experiment.total_unplanned_admissions
164+
bed_utilization = self.experiment.total_treatment_time / (self.experiment.num_critical_care_beds * self.experiment.results_collection_period)
165+
bed_occupancy = bed_utilization * self.experiment.num_critical_care_beds
166+
performance_measures = pd.DataFrame({'Cancelled Elective Operations': [self.experiment.cancelled_elective_count], 'Bed Utilization': [bed_utilization], 'Mean Waiting Time Unplanned': [mean_waiting_time_unplanned], 'Bed Occupancy': [bed_occupancy], 'Patient Count': [self.patient_count]})
167+
return performance_measures
168+
169+
170+
import pandas as pd
171+
172+
def multiple_replications(experiment, num_replications=5):
173+
all_results = []
174+
175+
for i in range(num_replications):
176+
experiment.setup_streams(i) # Call the setup_streams method and pass in the current replication number
177+
model = CCUModel(simpy.Environment(), experiment)
178+
experiment.reset_kpi()
179+
results = model.run()
180+
results.insert(0, 'Replication', i+1)
181+
all_results.append(results)
182+
183+
return pd.concat(all_results, ignore_index=True)
184+
185+
186+
def results_summary(results):
187+
# Drop the 'Replication' column
188+
results = results.drop(columns='Replication')
189+
190+
# Calculate the mean and standard deviation of each column
191+
mean = results.mean()
192+
std = results.std()
193+
194+
# Create a summary dataframe
195+
summary = pd.DataFrame({'Mean': mean, 'Standard Deviation': std})
196+
197+
return summary
198+
199+
200+
def get_experiments():
201+
experiments = {}
202+
for i in range(23, 29):
203+
exp = Experiment(num_critical_care_beds=i)
204+
experiments[f'Experiment_{i}'] = exp
205+
return experiments
206+
207+
def run_all_experiments(experiments, num_replications):
208+
summaries = {}
209+
for name, exp in experiments.items():
210+
print(f"Running experiment: {name}")
211+
model = CCUModel(simpy.Environment(), exp)
212+
results = multiple_replications(exp, num_replications)
213+
summary = results_summary(results)
214+
summaries[name] = summary
215+
return summaries
216+
217+
def summary_of_experiments(experiment_summaries):
218+
return pd.concat(experiment_summaries, axis=1)
219+
220+
221+
222+
import streamlit as st
223+
import pandas as pd
224+
225+
def main():
226+
st.title('A simulation model of bed-occupancy in a critical care unit')
227+
st.write('This model is a recreation of the model reported in a published academic study:')
228+
st.write('J D Griffiths, M Jones, M S Read & J E Williams (2010) A simulation model of bed-occupancy in a critical care unit, Journal of Simulation, 4:1, 52-59, DOI: 10.1057/jos.2009.22')
229+
st.write('Original Study: [Journal of Simulation](https://www.tandfonline.com/doi/full/10.1057/jos.2009.22)')
230+
231+
with st.sidebar:
232+
st.subheader('Experiment Parameters')
233+
num_beds = st.slider('Number of Critical Care Beds', 23, 28, 23)
234+
cleaning_duration = st.slider('Intensive Cleaning Duration', 1, 10, 5)
235+
trace = st.checkbox('Enable Trace', False)
236+
num_replications = st.number_input('Number of Replications', 1, 10, 5)
237+
238+
experiment = Experiment(num_critical_care_beds=num_beds, intensive_cleaning_duration=cleaning_duration, trace=trace)
239+
simulate_button = st.button('Simulate')
240+
241+
if simulate_button:
242+
with st.spinner('Please wait for results...'):
243+
results = multiple_replications(experiment, num_replications)
244+
summary = results_summary(results)
245+
st.write(summary)
246+
247+
if __name__ == '__main__':
248+
main()
249+
250+
251+
252+
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import streamlit as st
2+
import numpy as np
3+
from stroke_rehab_model import (
4+
Experiment, multiple_replications, combine_pdelay_results,
5+
combine_occup_results, mean_results, prob_delay_plot,
6+
occupancy_plot, summary_table
7+
)
8+
9+
def main():
10+
st.title("Stroke Rehabilitation Model Simulation")
11+
12+
if st.button("Simulate"):
13+
with st.spinner("Please wait for results..."):
14+
# Create an instance of Experiment (default params, default warm-up and rcp)
15+
experiment = Experiment()
16+
17+
# Run multiple replications
18+
rep_results = multiple_replications(experiment, 100)
19+
20+
# Combine results and take the mean
21+
pd_asu, pd_rehab = combine_pdelay_results(rep_results)
22+
rel_asu, rel_rehab = combine_occup_results(rep_results)
23+
mean_pd_asu, mean_pd_rehab = mean_results(pd_asu), mean_results(pd_rehab)
24+
mean_rel_asu, mean_rel_rehab = mean_results(rel_asu), mean_results(rel_rehab)
25+
26+
# Display tables
27+
st.subheader("Acute Care Results")
28+
df_acute = summary_table(mean_pd_asu, 9, 14, "acute")
29+
st.table(df_acute)
30+
31+
st.subheader("Rehabilitation Results")
32+
df_rehab = summary_table(mean_pd_rehab, 10, 16, "rehab")
33+
st.table(df_rehab)
34+
35+
# Display plots
36+
st.subheader("Probability Delay Plots")
37+
fig_pd_asu, ax_pd_asu = prob_delay_plot(mean_pd_asu, np.arange(0, 30))
38+
st.pyplot(fig_pd_asu)
39+
40+
fig_pd_rehab, ax_pd_rehab = prob_delay_plot(mean_pd_rehab, np.arange(0, 30), "No. rehab beds available")
41+
st.pyplot(fig_pd_rehab)
42+
43+
st.subheader("Occupancy Plots")
44+
fig_occ_asu, ax_occ_asu = occupancy_plot(mean_rel_asu, np.arange(0, 30))
45+
st.pyplot(fig_occ_asu)
46+
47+
fig_occ_rehab, ax_occ_rehab = occupancy_plot(mean_rel_rehab, np.arange(0, 30), "No. people in rehab")
48+
st.pyplot(fig_occ_rehab)
49+
50+
if __name__ == "__main__":
51+
main()

0 commit comments

Comments
 (0)