varadpuntambekar commited on
Commit
fd5261f
1 Parent(s): af73b50

Initial commit with right files

Browse files
Files changed (4) hide show
  1. .gitignore +160 -0
  2. LICENSE +121 -0
  3. OOP_ED_v2.0_gr.py +603 -0
  4. packages.txt +0 -0
.gitignore ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # poetry
98
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
+ #poetry.lock
103
+
104
+ # pdm
105
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106
+ #pdm.lock
107
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108
+ # in version control.
109
+ # https://pdm.fming.dev/#use-with-ide
110
+ .pdm.toml
111
+
112
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113
+ __pypackages__/
114
+
115
+ # Celery stuff
116
+ celerybeat-schedule
117
+ celerybeat.pid
118
+
119
+ # SageMath parsed files
120
+ *.sage.py
121
+
122
+ # Environments
123
+ .env
124
+ .venv
125
+ env/
126
+ venv/
127
+ ENV/
128
+ env.bak/
129
+ venv.bak/
130
+
131
+ # Spyder project settings
132
+ .spyderproject
133
+ .spyproject
134
+
135
+ # Rope project settings
136
+ .ropeproject
137
+
138
+ # mkdocs documentation
139
+ /site
140
+
141
+ # mypy
142
+ .mypy_cache/
143
+ .dmypy.json
144
+ dmypy.json
145
+
146
+ # Pyre type checker
147
+ .pyre/
148
+
149
+ # pytype static type analyzer
150
+ .pytype/
151
+
152
+ # Cython debug symbols
153
+ cython_debug/
154
+
155
+ # PyCharm
156
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
159
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160
+ #.idea/
LICENSE ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Creative Commons Legal Code
2
+
3
+ CC0 1.0 Universal
4
+
5
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
7
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
10
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
11
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
12
+ HEREUNDER.
13
+
14
+ Statement of Purpose
15
+
16
+ The laws of most jurisdictions throughout the world automatically confer
17
+ exclusive Copyright and Related Rights (defined below) upon the creator
18
+ and subsequent owner(s) (each and all, an "owner") of an original work of
19
+ authorship and/or a database (each, a "Work").
20
+
21
+ Certain owners wish to permanently relinquish those rights to a Work for
22
+ the purpose of contributing to a commons of creative, cultural and
23
+ scientific works ("Commons") that the public can reliably and without fear
24
+ of later claims of infringement build upon, modify, incorporate in other
25
+ works, reuse and redistribute as freely as possible in any form whatsoever
26
+ and for any purposes, including without limitation commercial purposes.
27
+ These owners may contribute to the Commons to promote the ideal of a free
28
+ culture and the further production of creative, cultural and scientific
29
+ works, or to gain reputation or greater distribution for their Work in
30
+ part through the use and efforts of others.
31
+
32
+ For these and/or other purposes and motivations, and without any
33
+ expectation of additional consideration or compensation, the person
34
+ associating CC0 with a Work (the "Affirmer"), to the extent that he or she
35
+ is an owner of Copyright and Related Rights in the Work, voluntarily
36
+ elects to apply CC0 to the Work and publicly distribute the Work under its
37
+ terms, with knowledge of his or her Copyright and Related Rights in the
38
+ Work and the meaning and intended legal effect of CC0 on those rights.
39
+
40
+ 1. Copyright and Related Rights. A Work made available under CC0 may be
41
+ protected by copyright and related or neighboring rights ("Copyright and
42
+ Related Rights"). Copyright and Related Rights include, but are not
43
+ limited to, the following:
44
+
45
+ i. the right to reproduce, adapt, distribute, perform, display,
46
+ communicate, and translate a Work;
47
+ ii. moral rights retained by the original author(s) and/or performer(s);
48
+ iii. publicity and privacy rights pertaining to a person's image or
49
+ likeness depicted in a Work;
50
+ iv. rights protecting against unfair competition in regards to a Work,
51
+ subject to the limitations in paragraph 4(a), below;
52
+ v. rights protecting the extraction, dissemination, use and reuse of data
53
+ in a Work;
54
+ vi. database rights (such as those arising under Directive 96/9/EC of the
55
+ European Parliament and of the Council of 11 March 1996 on the legal
56
+ protection of databases, and under any national implementation
57
+ thereof, including any amended or successor version of such
58
+ directive); and
59
+ vii. other similar, equivalent or corresponding rights throughout the
60
+ world based on applicable law or treaty, and any national
61
+ implementations thereof.
62
+
63
+ 2. Waiver. To the greatest extent permitted by, but not in contravention
64
+ of, applicable law, Affirmer hereby overtly, fully, permanently,
65
+ irrevocably and unconditionally waives, abandons, and surrenders all of
66
+ Affirmer's Copyright and Related Rights and associated claims and causes
67
+ of action, whether now known or unknown (including existing as well as
68
+ future claims and causes of action), in the Work (i) in all territories
69
+ worldwide, (ii) for the maximum duration provided by applicable law or
70
+ treaty (including future time extensions), (iii) in any current or future
71
+ medium and for any number of copies, and (iv) for any purpose whatsoever,
72
+ including without limitation commercial, advertising or promotional
73
+ purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
74
+ member of the public at large and to the detriment of Affirmer's heirs and
75
+ successors, fully intending that such Waiver shall not be subject to
76
+ revocation, rescission, cancellation, termination, or any other legal or
77
+ equitable action to disrupt the quiet enjoyment of the Work by the public
78
+ as contemplated by Affirmer's express Statement of Purpose.
79
+
80
+ 3. Public License Fallback. Should any part of the Waiver for any reason
81
+ be judged legally invalid or ineffective under applicable law, then the
82
+ Waiver shall be preserved to the maximum extent permitted taking into
83
+ account Affirmer's express Statement of Purpose. In addition, to the
84
+ extent the Waiver is so judged Affirmer hereby grants to each affected
85
+ person a royalty-free, non transferable, non sublicensable, non exclusive,
86
+ irrevocable and unconditional license to exercise Affirmer's Copyright and
87
+ Related Rights in the Work (i) in all territories worldwide, (ii) for the
88
+ maximum duration provided by applicable law or treaty (including future
89
+ time extensions), (iii) in any current or future medium and for any number
90
+ of copies, and (iv) for any purpose whatsoever, including without
91
+ limitation commercial, advertising or promotional purposes (the
92
+ "License"). The License shall be deemed effective as of the date CC0 was
93
+ applied by Affirmer to the Work. Should any part of the License for any
94
+ reason be judged legally invalid or ineffective under applicable law, such
95
+ partial invalidity or ineffectiveness shall not invalidate the remainder
96
+ of the License, and in such case Affirmer hereby affirms that he or she
97
+ will not (i) exercise any of his or her remaining Copyright and Related
98
+ Rights in the Work or (ii) assert any associated claims and causes of
99
+ action with respect to the Work, in either case contrary to Affirmer's
100
+ express Statement of Purpose.
101
+
102
+ 4. Limitations and Disclaimers.
103
+
104
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
105
+ surrendered, licensed or otherwise affected by this document.
106
+ b. Affirmer offers the Work as-is and makes no representations or
107
+ warranties of any kind concerning the Work, express, implied,
108
+ statutory or otherwise, including without limitation warranties of
109
+ title, merchantability, fitness for a particular purpose, non
110
+ infringement, or the absence of latent or other defects, accuracy, or
111
+ the present or absence of errors, whether or not discoverable, all to
112
+ the greatest extent permissible under applicable law.
113
+ c. Affirmer disclaims responsibility for clearing rights of other persons
114
+ that may apply to the Work or any use thereof, including without
115
+ limitation any person's Copyright and Related Rights in the Work.
116
+ Further, Affirmer disclaims responsibility for obtaining any necessary
117
+ consents, permissions or other rights required for any use of the
118
+ Work.
119
+ d. Affirmer understands and acknowledges that Creative Commons is not a
120
+ party to this document and has no duty or obligation with respect to
121
+ this CC0 or use of the Work.
OOP_ED_v2.0_gr.py ADDED
@@ -0,0 +1,603 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Mon Apr 22 10:55:25 2024
4
+
5
+ @author: varad
6
+ """
7
+ #OOP model for a real ED with 3 processes and 3 resources
8
+ #import libraries
9
+ import simpy
10
+ import matplotlib
11
+ import matplotlib.pyplot as plt
12
+ import math
13
+ import numpy as np
14
+ import pandas as pd
15
+ import random
16
+ import csv
17
+ import gradio as gr
18
+ import plotly.graph_objects as go
19
+
20
+ class g (object):
21
+ '''
22
+ A class that holds all the global variables that will be passed into the different processes.
23
+ It's easier to change the global variables here rather than change them in the process code as it could lead to errors
24
+ '''
25
+ #service times (these are more or less constant and hence won't be changed through user input, although it is possible to change them too,
26
+ #but for the sake of this program, they won't be changed)
27
+ ed_inter_arrival = 6 #mins
28
+ mean_registeration = 2 #mins
29
+ mean_triage = 6 #mins
30
+ mean_acu_ass = 60 #mins
31
+ mean_ed_ass = 30 #mins
32
+
33
+ #resources (this will need to me moved from a static variable to a variable within the ED class as it will then be fed into the gradio app
34
+ #as variables to be modified through user input)
35
+ receptionist = 1
36
+ nurse = 2
37
+ ed_doc = 2
38
+ acu_doc = 1
39
+
40
+ #simulation variables
41
+ number_of_runs = 100
42
+ warmup_time = 1440 #24 hours and 60 minutes per hour
43
+ run_time = 100 # mins
44
+
45
+ class ed_patient (object):
46
+ '''
47
+ Defines the patient characteristics. Could be interesting if we have different types of patients
48
+ But for this exercise we have only one type of patient that is a regular patient that has a 20% chance
49
+ of going to the ACU. Also important to store patient level variables to then summarize and plot
50
+ '''
51
+ def __init__(self, uhid):
52
+ self.id = uhid
53
+
54
+ #declaring these variables as they will be recorded and manipulated with later
55
+ self.time_entered_in_system = 0
56
+ self.q_reception = 0
57
+ self.service_reception = 0
58
+ self.q_nurse = 0
59
+ self.service_nurse = 0
60
+ self.q_edd_ass = 0
61
+ self.ed_ass_time = 0
62
+ self.acu_ass_time = 0
63
+ self.q_acu_ass = 0
64
+ self.time_exit_system = 0
65
+ self.tot_system_time = 0
66
+
67
+ class ED_sim (object):
68
+ '''
69
+ This is the actual clinic where everything is simulated.
70
+ '''
71
+ def __init__(self, run_number,receptionist = 3, nurse = 2, ed_doc = 2, acu_doc = 1 ):
72
+ #declaring the environment
73
+ self.env = simpy.Environment()
74
+
75
+
76
+
77
+ #declaring resource capacity as variables to later be changed through gradio inputs
78
+ self.rec_no = receptionist
79
+ self.nurse_no = nurse
80
+ self.ed_doc_no = ed_doc
81
+ self.acu_doc_no = acu_doc
82
+
83
+
84
+ self.patient_counter = 0
85
+ #declaring resources
86
+ self.receptionist = simpy.Resource(self.env, capacity = self.rec_no)
87
+ self.nurse = simpy.Resource(self.env, capacity = self.nurse_no)
88
+ self.ed_doc = simpy.Resource(self.env, capacity = self.ed_doc_no)
89
+ self.acu_doc = simpy.Resource(self.env, capacity = self.acu_doc_no)
90
+ self.run_number = run_number + 1
91
+
92
+ #initiating a dataframe with required columns
93
+ self.individual_level_results = pd.DataFrame({
94
+ "UHID" :[],
95
+ "Time_entered_in_system" : [],
96
+ "Q_time_receptionist":[],
97
+ "Q_time_nurse":[],
98
+ "Q_time_acu_doc":[],
99
+ "Q_time_ed_doc":[],
100
+ "Service_time_receptionist":[],
101
+ "Service_time_nurse":[],
102
+ "Service_time_acu_doc":[],
103
+ "Service_time_ed_doc":[],
104
+ "Time_exit_system":[],
105
+ "Total time in System":[]
106
+ })
107
+ self.individual_level_results.set_index('UHID', inplace= True) #sets the index to UHID which is just 1,2,3,4 etc
108
+
109
+ #declaring mean variables for or KPIs to be calculated at the end of one run
110
+ self.Mean_Q_Rec_time = 0
111
+ self.Mean_Q_Nurse_time = 0
112
+ self.Mean_Q_ED_time = 0
113
+ self.Mean_Q_ACU_time = 0
114
+ self.Rec_utilize = 0
115
+ self.Nurse_utilize = 0
116
+ self.ED_doc_utilize = 0
117
+ self.ACU_doc_utilize = 0
118
+
119
+
120
+
121
+ def generate_ed_arrivals(self):
122
+ while True:
123
+ self.patient_counter += 1 #this is also the UHID of the patient
124
+
125
+ ed_pt = ed_patient(self.patient_counter)
126
+
127
+ ed_pt.time_entered_in_system = self.env.now #Used to calculate total time spent in the system
128
+
129
+ #Patient goes to registeration
130
+ self.env.process(self.registration(ed_pt))
131
+
132
+ #print(self.individual_level_results)
133
+ #draws a random value from an exponential distribution with lambda = interarrival time
134
+ ed_arrival_time = random.expovariate(1/g.ed_inter_arrival)
135
+
136
+ yield self.env.timeout(ed_arrival_time)
137
+
138
+
139
+ def registration(self, patient):
140
+
141
+ start_q_rec = self.env.now
142
+
143
+ with self.receptionist.request() as req:
144
+ yield req
145
+
146
+ end_q_rec = self.env.now
147
+
148
+ #storing patient level values in patient level variables
149
+ patient.q_reception = end_q_rec - start_q_rec
150
+
151
+ #patient goes to triage
152
+ self.env.process(self.triage(patient))
153
+
154
+ #register_time = random.triangular(0,g.mean_registeration, 2*g.mean_registeration)
155
+ register_time = random.expovariate(1/g.mean_registeration)
156
+
157
+ patient.service_reception = register_time
158
+
159
+
160
+
161
+ yield self.env.timeout(register_time)
162
+
163
+ def triage (self, patient):
164
+ start_q_nurse = self.env.now
165
+ with self.nurse.request() as req:
166
+ yield req
167
+
168
+ end_q_nurse = self.env.now
169
+
170
+ patient.q_nurse = end_q_nurse - start_q_nurse
171
+
172
+ #patient goes either to ACU or to ED based on probability
173
+ if random.random() > 0.2: #80% chance that the patient goes to ED
174
+ self.env.process(self.ed_ass(patient))
175
+ else:
176
+ self.env.process(self.acu_ass(patient))
177
+
178
+
179
+ triage_time = random.triangular(g.mean_triage/2, g.mean_triage, g.mean_triage*2 )
180
+
181
+ patient.service_nurse = triage_time
182
+
183
+
184
+
185
+ yield self.env.timeout(triage_time)
186
+
187
+ def ed_ass (self, patient):
188
+ start_ed_q = self.env.now
189
+ with self.ed_doc.request() as req:
190
+ yield req
191
+ end_ed_q = self.env.now
192
+ patient.q_edd_ass = end_ed_q - start_ed_q
193
+
194
+
195
+ ed_ass_time = random.triangular(g.mean_ed_ass/2, g.mean_ed_ass, g.mean_ed_ass*2)
196
+ patient.ed_ass_time = ed_ass_time
197
+
198
+
199
+
200
+ yield self.env.timeout(ed_ass_time)
201
+
202
+ patient.time_exit_system = self.env.now
203
+
204
+ patient.tot_system_time = patient.time_exit_system - patient.time_entered_in_system
205
+
206
+ ED_sim.add_to_df(self, patient)
207
+
208
+
209
+ def acu_ass (self, patient):
210
+ start_acu_q = self.env.now
211
+
212
+ with self.acu_doc.request() as req:
213
+ yield req
214
+
215
+ end_acu_q = self.env.now
216
+ patient.q_acu_ass = end_acu_q - start_acu_q
217
+
218
+ acu_ass_time = random.triangular(g.mean_acu_ass/2, g.mean_acu_ass, g.mean_acu_ass*2)
219
+
220
+ patient.acu_ass_time = acu_ass_time
221
+ yield self.env.timeout(acu_ass_time)
222
+
223
+ patient.time_exit_system = self.env.now
224
+
225
+ patient.tot_system_time = patient.time_exit_system - patient.time_entered_in_system
226
+ ED_sim.add_to_df(self, patient)
227
+
228
+
229
+ def add_to_df(self, patient):
230
+ '''
231
+ Basically takes all the variables and adds them to the dataframe without having to enter them manually with
232
+ 12 line codes in every function
233
+ '''
234
+ df_to_add = pd.DataFrame({
235
+ "UHID" :[patient.id],
236
+ "Time_entered_in_system" : [patient.time_entered_in_system],
237
+ "Q_time_receptionist":[patient.q_reception],
238
+
239
+ #using zeros as placeholders
240
+ "Q_time_nurse":[patient.q_nurse],
241
+ "Q_time_acu_doc":[patient.q_acu_ass],
242
+ "Q_time_ed_doc":[patient.q_edd_ass],
243
+ "Service_time_receptionist":[patient.service_reception],
244
+ "Service_time_nurse":[patient.service_nurse],
245
+ "Service_time_acu_doc":[patient.acu_ass_time],
246
+ "Service_time_ed_doc":[patient.ed_ass_time],
247
+ "Time_exit_system" :[patient.time_exit_system],
248
+ "Total time in System":[patient.tot_system_time]
249
+ })
250
+
251
+ df_to_add.set_index('UHID', inplace=True)
252
+ self.individual_level_results = self.individual_level_results._append(df_to_add)
253
+ #print(self.individual_level_results)
254
+
255
+
256
+ def mean_calculator (self):
257
+ '''
258
+ calculates the average statistic for each individual run for all the different KPIs and maybe stores it in a global database
259
+ '''
260
+ #mean queuing times
261
+ self.Mean_Q_Rec_time = self.individual_level_results['Q_time_receptionist'].mean()
262
+ self.Mean_Q_Nurse_time = self.individual_level_results['Q_time_nurse'].mean()
263
+ self.Mean_Q_ED_time = self.individual_level_results['Q_time_ed_doc'].mean()
264
+ self.Mean_Q_ACU_time = self.individual_level_results['Q_time_acu_doc'].mean()
265
+
266
+ #%resource utilisation
267
+ self.Rec_utilize = self.individual_level_results[
268
+ 'Service_time_receptionist'].sum()/(g.run_time*self.rec_no)
269
+ self.Nurse_utilize = self.individual_level_results['Service_time_nurse'].sum()/(g.run_time*self.nurse_no)
270
+ self.ED_doc_utilize = self.individual_level_results['Service_time_ed_doc'].sum()/(g.run_time*self.ed_doc_no)
271
+ self.ACU_doc_utilize = self.individual_level_results['Service_time_acu_doc'].sum()/(g.run_time*self.acu_doc_no)
272
+
273
+
274
+
275
+ def export_row_to_csv(self):
276
+ '''
277
+ Writes the results of an individual run as a row in a csv file in the desired folder
278
+ '''
279
+
280
+ with open (r"C:\Users\varad\Desktop\Education Material\Mathematical Modelling\HSMA\HSMA_modelling_ED\trial_results.csv",'a') as f:
281
+ writer = csv.writer(f, delimiter = ',')
282
+ row_to_add = [self.run_number,
283
+ self.Mean_Q_Rec_time,
284
+ self.Mean_Q_Nurse_time,
285
+ self.Mean_Q_ED_time,
286
+ self.Mean_Q_ACU_time,
287
+ self.Rec_utilize,
288
+ self.Nurse_utilize,
289
+ self.ED_doc_utilize,
290
+ self.ACU_doc_utilize]
291
+ writer.writerow(row_to_add)
292
+
293
+
294
+ def run (self):
295
+ '''
296
+ suns the simulation program
297
+ '''
298
+ self.env.process(self.generate_ed_arrivals())
299
+ self.env.run(until = g.run_time)
300
+ #print(self.individual_level_results)
301
+ self.individual_level_results.to_csv(r"C:\Users\varad\Desktop\Education Material\Mathematical Modelling\HSMA\HSMA_modelling_ED\individual_results.csv")
302
+ self.mean_calculator()
303
+ self.export_row_to_csv()
304
+
305
+ class summary_statistics(object):
306
+ '''
307
+ This object calculates the mean, median or other summary statistics from a dataframe that has means of individual runs
308
+ So this object calculates the means of means
309
+ '''
310
+ def __init__(self):
311
+ self.total_mean_df = pd.DataFrame({
312
+ "Median_q_rec_time":[],
313
+ "25_q_rec_time":[],
314
+ "75_q_rec_time":[],
315
+
316
+ "Median_q_nurse_time":[],
317
+ "25_q_nurse_time":[],
318
+ "75_q_nurse_time":[],
319
+
320
+ "Median_q_ed_doc_time":[],
321
+ "25_q_ed_doc_time":[],
322
+ "75_q_ed_doc_time":[],
323
+
324
+ "Median_q_acu_doc_time":[],
325
+ "25_q_acu_doc_time":[],
326
+ "75_q_acu_doc_time":[],
327
+
328
+ "Median_%_utilize_rec":[],
329
+
330
+ "Median_%_utilize_nurse":[],
331
+
332
+
333
+ "Median_%_utilize_ed_doc":[],
334
+
335
+
336
+ "Median_%_utilize_acu_doc":[],
337
+
338
+ })
339
+ filepath = r"C:\Users\varad\Desktop\Education Material\Mathematical Modelling\HSMA\HSMA_modelling_ED\trial_results.csv"
340
+ self.dataframe = pd.read_csv(filepath)
341
+
342
+ def mean_of_means(self):
343
+
344
+ median_q_rec = self.dataframe["Mean_Q_Rec_time"].median()
345
+ twofive_q_rec = self.dataframe["Mean_Q_Rec_time"].quantile(0.25)
346
+ sevfive_q_rec = self.dataframe["Mean_Q_Rec_time"].quantile(0.75)
347
+
348
+ median_q_nurse = self.dataframe["Mean_Q_Nurse_time"].median()
349
+ twofive_q_nurse = self.dataframe["Mean_Q_Nurse_time"].quantile(0.25)
350
+ sevfive_q_nurse = self.dataframe["Mean_Q_Nurse_time"].quantile(0.75)
351
+
352
+ median_q_ed_doc = self.dataframe["Mean_Q_ED_time"].median()
353
+ twofive_q_ed_doc = self.dataframe["Mean_Q_ED_time"].quantile(0.25)
354
+ sevfive_q_ed_doc = self.dataframe["Mean_Q_ED_time"].quantile(0.75)
355
+
356
+ median_q_acu_doc = self.dataframe["Mean_Q_ACU_time"].median()
357
+ twofive_q_acu_doc = self.dataframe["Mean_Q_ACU_time"].quantile(0.25)
358
+ sevfive_q_acu_doc = self.dataframe["Mean_Q_ACU_time"].quantile(0.75)
359
+
360
+ median_rec_uti = self.dataframe["Rec_%_utilize"].median()
361
+ median_nurse_uti = self.dataframe["Nurse_%_utilize"].median()
362
+ median_ed_doc_uti = self.dataframe["ED_doc_%_utilize"].median()
363
+ median_acu_doc_uti = self.dataframe["ACU_doc_%_utilize"].median()
364
+
365
+
366
+ print("Results of " + str(g.number_of_runs) + " runs")
367
+ print("-------------")
368
+
369
+ self.total_mean_df = pd.DataFrame({
370
+ "Median_q_rec_time":[median_q_rec],
371
+ "25_q_rec_time":[twofive_q_rec],
372
+ "75_q_rec_time":[sevfive_q_rec],
373
+
374
+ "Median_q_nurse_time":[median_q_nurse],
375
+ "25_q_nurse_time":[twofive_q_nurse],
376
+ "75_q_nurse_time":[sevfive_q_nurse],
377
+
378
+ "Median_q_ed_doc_time":[median_q_ed_doc],
379
+ "25_q_ed_doc_time":[twofive_q_ed_doc],
380
+ "75_q_ed_doc_time":[sevfive_q_ed_doc],
381
+
382
+ "Median_q_acu_doc_time":[median_q_acu_doc],
383
+ "25_q_acu_doc_time":[twofive_q_acu_doc],
384
+ "75_q_acu_doc_time":[sevfive_q_acu_doc],
385
+
386
+ "Median_%_utilize_rec":[median_rec_uti],
387
+
388
+
389
+ "Median_%_utilize_nurse":[median_nurse_uti],
390
+
391
+
392
+ "Median_%_utilize_ed_doc":[median_ed_doc_uti],
393
+
394
+
395
+ "Median_%_utilize_acu_doc":[median_acu_doc_uti],
396
+
397
+ })
398
+
399
+ print(self.total_mean_df)
400
+
401
+ with open (r"C:\Users\varad\Desktop\Education Material\Mathematical Modelling\HSMA\HSMA_modelling_ED\mean_per_lambda.csv",'a') as f:
402
+ writer = csv.writer(f, delimiter = ',')
403
+ row_to_add = [g.ed_inter_arrival,
404
+ median_q_rec,
405
+ twofive_q_rec,
406
+ sevfive_q_rec,
407
+
408
+ median_q_nurse,
409
+ twofive_q_nurse,
410
+ sevfive_q_nurse,
411
+
412
+ median_q_ed_doc,
413
+ twofive_q_ed_doc,
414
+ sevfive_q_ed_doc,
415
+
416
+ median_q_acu_doc,
417
+ twofive_q_acu_doc,
418
+ sevfive_q_acu_doc,
419
+
420
+ median_rec_uti,
421
+ median_nurse_uti,
422
+ median_ed_doc_uti,
423
+ median_acu_doc_uti
424
+ ]
425
+ writer.writerow(row_to_add)
426
+
427
+ def file_opener():
428
+ '''
429
+ Adds one row to the filename that is passed to it
430
+ '''
431
+ #This is not the most compulsory function here as the above function will also create a file if it does not exist already
432
+
433
+ with open (r"C:\Users\varad\Desktop\Education Material\Mathematical Modelling\HSMA\HSMA_modelling_ED\trial_results.csv",'w') as f:
434
+ writer = csv.writer(f, delimiter = ',')
435
+ columns_headers = ["Run_Number",
436
+ "Mean_Q_Rec_time",
437
+ "Mean_Q_Nurse_time",
438
+ "Mean_Q_ED_time",
439
+ "Mean_Q_ACU_time",
440
+ "Rec_%_utilize",
441
+ "Nurse_%_utilize",
442
+ "ED_doc_%_utilize",
443
+ "ACU_doc_%_utilize"]
444
+ writer.writerow(columns_headers)
445
+
446
+ with open (r"C:\Users\varad\Desktop\Education Material\Mathematical Modelling\HSMA\HSMA_modelling_ED\mean_per_lambda.csv",'w') as f:
447
+ writer = csv.writer(f, delimiter = ',')
448
+ columns_headers = ["Pt Interarrival Time (lambda)",
449
+ "Median_Q_Rec_time",
450
+ "25_Q_rec_time",
451
+ "75_Q_rec_time",
452
+
453
+ "Median_Q_Nurse_time",
454
+ "25_Q_Nurse_time",
455
+ "75_Q_Nurse_time",
456
+
457
+ "Median_Q_ED_time",
458
+ "25_Q_ED_time",
459
+ "75_Q_ED_time",
460
+
461
+ "Median_Q_ACU_time",
462
+ "25_Q_ACU_time",
463
+ "75_Q_ACU_time",
464
+
465
+ "Median_Rec_%_utilize",
466
+ "Median_Nurse_%_utilize",
467
+ "Median_ED_doc_%_utilize",
468
+ "Median_ACU_doc_%_utilize"]
469
+
470
+ writer.writerow(columns_headers)
471
+
472
+ def Plotter():
473
+ filepath = r"C:\Users\varad\Desktop\Education Material\Mathematical Modelling\HSMA\HSMA_modelling_ED\mean_per_lambda.csv"
474
+ df_to_plot = pd.read_csv(filepath)
475
+
476
+ figure = plt.subplots()
477
+ plt.plot(df_to_plot["Pt Interarrival Time (lambda)"], df_to_plot['Median_Q_Rec_time'], color = 'green', linestyle = '-', label = 'Queue for reception')
478
+ plt.plot(df_to_plot["Pt Interarrival Time (lambda)"], df_to_plot['Median_Q_Nurse_time'], color = 'blue', linestyle = ':', label = 'Queue for nurses')
479
+ plt.plot(df_to_plot["Pt Interarrival Time (lambda)"], df_to_plot['Median_Q_ED_time'], color = 'red', linestyle = '--', label = 'Queue for ED_doc')
480
+ plt.plot(df_to_plot["Pt Interarrival Time (lambda)"], df_to_plot['Median_Q_ACU_time'], color = 'black', linestyle = '-.', label = 'Queue for ACU_doc')
481
+
482
+ plt.xlabel("Pt interarrival time (min)")
483
+ plt.ylabel("Time in minutes")
484
+ plt.title("Queuing times for the Emergency room")
485
+
486
+ plt.text(3,10,"Rec = 1, Nur = 2, ED_doc = 2, ACU_doc = 1")
487
+ plt.legend()
488
+
489
+
490
+
491
+ figure = plt.subplots()
492
+ plt.plot(df_to_plot["Pt Interarrival Time (lambda)"], df_to_plot['Median_Rec_%_utilize'], color = 'green', linestyle = '-', label = '% utilise of reception')
493
+ plt.plot(df_to_plot["Pt Interarrival Time (lambda)"], df_to_plot['Median_Nurse_%_utilize'], color = 'blue', linestyle = ':', label = '% utilise of nurses')
494
+ plt.plot(df_to_plot["Pt Interarrival Time (lambda)"], df_to_plot['Median_ED_doc_%_utilize'], color = 'red', linestyle = '--', label = '%utilise of ED_doc')
495
+ plt.plot(df_to_plot["Pt Interarrival Time (lambda)"], df_to_plot['Median_ACU_doc_%_utilize'], color = 'black', linestyle = '-.', label = '%utilise of ACU_doc')
496
+
497
+ plt.xlabel("Pt interarrival time (min)")
498
+ plt.ylabel("Time in minutes")
499
+ plt.title("Percentage utlisation for the Emergency room different HR")
500
+
501
+ plt.text(3,10,"Rec = 1, Nur = 2, ED_doc = 2, ACU_doc = 1")
502
+ plt.legend()
503
+
504
+
505
+ return figure
506
+
507
+ def plotly_plotter():
508
+ '''
509
+ Uses the Plotly library to plot the graphs as the plotly library is web application friendly
510
+ '''
511
+ filepath = r"C:\Users\varad\Desktop\Education Material\Mathematical Modelling\HSMA\HSMA_modelling_ED\mean_per_lambda.csv"
512
+ df_to_plot = pd.read_csv(filepath)
513
+
514
+ fig = go.Figure()
515
+ fig.add_trace(go.Scatter(x = df_to_plot["Pt Interarrival Time (lambda)"], y = df_to_plot['Median_Rec_%_utilize'], name='Receptionist utilization%'))
516
+ fig.add_trace(go.Scatter(x = df_to_plot["Pt Interarrival Time (lambda)"], y = df_to_plot['Median_Nurse_%_utilize'], name='Nurse utilization%'))
517
+ fig.add_trace(go.Scatter(x = df_to_plot["Pt Interarrival Time (lambda)"], y = df_to_plot['Median_ED_doc_%_utilize'], name='ED Doc utilization%'))
518
+ fig.add_trace(go.Scatter(x = df_to_plot["Pt Interarrival Time (lambda)"], y = df_to_plot['Median_ACU_doc_%_utilize'], name='ACU Doc utilization%'))
519
+ fig.update_layout(title = "% utilization of different HR")
520
+ #fig.show()
521
+
522
+ return fig
523
+
524
+
525
+
526
+ def main(receptionist, nurse, ed_doc, acu_doc):
527
+ '''
528
+ this is going to to be the main function that gradio will operate on
529
+ It will take input parameters as model parameters which will be modified by the users
530
+ It will output a plot or a set of plots which will then be plotted in a given block in gradio
531
+ '''
532
+ file_opener()
533
+ for l in range(1,11):
534
+ print("Pt interarrival time = ", l)
535
+ for run in range (g.number_of_runs):
536
+ print(f"Run {run + 1} of {g.number_of_runs}")
537
+ my_ED_model = ED_sim(run, receptionist, nurse, ed_doc, acu_doc)
538
+ my_ED_model.run()
539
+ g.ed_inter_arrival = l
540
+ my_sum_stats = summary_statistics()
541
+ my_sum_stats.mean_of_means()
542
+ return plotly_plotter()
543
+
544
+ def get_data_gradio():
545
+ filepath = r"C:\Users\varad\Desktop\Education Material\Mathematical Modelling\HSMA\HSMA_modelling_ED\mean_per_lambda.csv"
546
+ return pd.read_csv(filepath)
547
+
548
+
549
+
550
+ #main()
551
+
552
+
553
+
554
+ with gr.Blocks() as demo:
555
+ gr.Markdown(r"A Discrete Event Simulation run of an imaginary Emergency Room")
556
+ gr.Image(r"C:\Users\varad\Desktop\Education Material\Mathematical Modelling\HSMA\HSMA_modelling_ED\Ed_process_map.png")
557
+ with gr.Row():
558
+ gr.HighlightedText(label = "Modify these parameters (number of different human resources) using the sliders below")
559
+ with gr.Row():
560
+ receptionist = gr.Slider(minimum=1, maximum=10,label = "No of Receptionists")
561
+ nurse = gr.Slider(minimum=1, maximum=10, label = "No of Nurses")
562
+ with gr.Row():
563
+ ed_doc = gr.Slider(minimum=1, maximum=10, label = "No of ED doctors")
564
+ acu_doc = gr.Slider(minimum=1, maximum=10,label = "No of ACU Doctors")
565
+
566
+
567
+ with gr.Row():
568
+ btn = gr.Button(value = "Run the Simulation")
569
+
570
+
571
+ with gr.Row():
572
+ output = gr.Plot(label = "Simulation Run")
573
+ btn.click(main,[receptionist,nurse,ed_doc,acu_doc], output)
574
+ demo.launch(share = True)
575
+
576
+
577
+
578
+
579
+
580
+
581
+
582
+
583
+
584
+
585
+
586
+
587
+
588
+
589
+
590
+
591
+
592
+
593
+
594
+
595
+
596
+
597
+
598
+
599
+
600
+
601
+
602
+
603
+
packages.txt ADDED
Binary file (2.14 kB). View file