├── .gitattributes ├── .gitignore ├── README.md ├── graph.py ├── hospital.py ├── images └── desc.jpeg ├── main.py └── peoplepool.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 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 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 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 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # celery beat schedule file 95 | celerybeat-schedule 96 | 97 | # SageMath parsed files 98 | *.sage.py 99 | 100 | # Environments 101 | .env 102 | .venv 103 | env/ 104 | venv/ 105 | ENV/ 106 | env.bak/ 107 | venv.bak/ 108 | 109 | # Spyder project settings 110 | .spyderproject 111 | .spyproject 112 | 113 | # Rope project settings 114 | .ropeproject 115 | 116 | # mkdocs documentation 117 | /site 118 | 119 | # mypy 120 | .mypy_cache/ 121 | .dmypy.json 122 | dmypy.json 123 | 124 | # Pyre type checker 125 | .pyre/ 126 | 127 | .DS_Store 128 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # epidemic-simulation 2 | A python epidemic simultation programme 3 | 4 | #### 图表信息: 5 | Total 总存活人数 6 | Susceptible 未感染 7 | Contagious 传染源 8 | Exposed 潜伏期(黄色) 9 | Illed 总体得病(红色,仅包含确诊和住院,不包含潜伏期) 10 | Infective 确诊(左图中红色) 11 | Hospitalized 住院隔离(医院中红色) 12 | Recovered 免疫期(绿色) 13 | Te 实际平均潜伏时间 14 | Ti 实际平均传染时间 15 | Tg 实际平均入院时间(约等于Ti+Te,由于存在死亡,因此有些许不同) 16 | R0 基本传染数(Basic reproduction number) 定义为一个感染到某种传染病的人,会把疾病传染给其他多少个人的平均数 R0 = k*b*D --Lipsitch M,Cohen T, Cooper B, et al. TransmissionDynamics and Control of Severe Acute Respiratory Syndrome. Science, 2003,300(5627): 1966-1970. 17 | 18 | #### 可用参数: 19 | ORIGINAL_COUNT 初始感染数量 20 | BROAD_RATE 传播率 21 | PROTECTION_RATE 保护措施增长率(连续)(影响BROAD_RATE, DEATH_RATE, u) 22 | SHADOW_TIME 平均潜伏时间 23 | HOSPITAL_RECEIVE_TIME 平均医院收治时间 24 | CURE_TIME 平均治疗时间 25 | IMMUNED_TIME 平均免疫期时间 26 | DEATH_RATE 每日死亡概率 27 | BED_COUNT 医院床位 28 | SAFETY_DIST 安全距离 29 | FLUCTUATION 各参数与平均值之间的波动 30 | u 流动意向 31 | PERSON_COUNT 城市内人数 32 | can_exposed_infect 潜伏期能否感染他人 33 | recovered_included 是否有免疫期 34 | mode ‘l'或者'line'运行时右侧为折线图,否则为柱状图 35 | 36 | 图示: 37 | ![image](https://github.com/y1han/virus-simulation/raw/master/images/desc.jpeg) 38 | 39 | 由 https://github.com/KikiLetGo/VirusBroadcast 启发进行的python复刻,并进行了一些改动 40 | 运行main.py即可 41 | -------------------------------------------------------------------------------- /graph.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from matplotlib import animation 4 | from hospital import Hospital 5 | from peoplepool import PeoplePool 6 | from threading import Thread, Condition 7 | 8 | line = ['l', 'line'] 9 | 10 | def graph(pool, hos, mode=line[0]): 11 | #0未感染, 1潜伏期, 2确诊, 3住院(在地图上无), 4免疫期, 5病死, 6总体得病(仅包含确诊), 7总存活人数, 8传染源, 12 | colors_people = ['white', 'yellow', 'red', 'black', 'green', 'black', 'purple', 'grey', 'blue'] 13 | colors_bed = ['black', 'red'] #0无人,1有人 14 | colors_statics = ['white'] #1 R0 15 | PERSON_COUNT = pool.num 16 | BED_COUNT = hos.bed_counts 17 | 18 | fig = plt.figure(figsize=(20, 10)) 19 | plt.style.use('dark_background') 20 | fig.patch.set_facecolor('black') 21 | grid = plt.GridSpec(3, 5, wspace=0.5, hspace=0.3) 22 | ax1 = plt.subplot(grid[0:3, 0:3]) 23 | ax2 = plt.subplot(grid[0:2, 3]) 24 | 25 | ax3 = plt.subplot(grid[0, 4]) 26 | ax4 = plt.subplot(grid[1, 4]) 27 | ax5 = plt.subplot(grid[2, 4]) 28 | ax6 = plt.subplot(grid[2, 3]) 29 | 30 | if mode in line: 31 | ax3_susceptible_data = [0, 0] 32 | ax3_total_data = [0, 0] 33 | ax3_recover_data = [0, 0] 34 | ax4_exposed_data = [0, 0] 35 | ax4_contagious_data = [0, 0] 36 | ax5_infective_data = [0, 0] 37 | ax5_diagnosed_data = [0, 0] 38 | ax6_r0_data = [0, 0] 39 | 40 | hosX = hos.getX() 41 | hosY = hos.getY() 42 | 43 | def init(): 44 | pass 45 | 46 | anim_running = True 47 | def onClick(event): 48 | nonlocal anim_running 49 | if anim_running: 50 | anim.event_source.stop() 51 | anim_running = False 52 | else: 53 | anim.event_source.start() 54 | anim_running = True 55 | fig.canvas.mpl_connect('button_press_event', onClick) 56 | 57 | def multi_process(time): 58 | cond = Condition() 59 | animate_thread = Thread(target=animate, args=(time, cond), name='animate') 60 | update_thread = Thread(target=pool.update, args=(time, hos, cond), name='update') 61 | update_thread.start() 62 | animate_thread.start() 63 | update_thread.join() 64 | return ax1, ax2, ax3, ax4, ax5, ax6 65 | 66 | def animate(time, cond): 67 | cond.acquire() 68 | cond.notify() 69 | boundry = 5.5 * pool.SCALE 70 | status = pool.getStatus() 71 | status_hos = hos.getStatus() 72 | susceptible = np.sum(status == 0) 73 | exposed = np.sum(status == 1) 74 | infective = np.sum(status == 2) 75 | recovered = np.sum(status == 4) 76 | hospitalized = np.sum(status_hos == 1) 77 | diagnosed = infective + hospitalized # 仅包含确诊 78 | contagious = exposed + infective # 能传染给他人的 79 | total = susceptible + exposed + diagnosed + recovered # SEIR 80 | 81 | if time > 0 and sum(pool.Ti) != 0: 82 | Ti = sum(pool.Ti)/len(pool.Ti) 83 | Tg = sum(pool.Tg)/len(pool.Tg) 84 | Te = sum(pool.Te)/len(pool.Te) 85 | if pool.can_exposed_infect: 86 | k = pool.in_touch / contagious if contagious != 0 else 0 87 | R0 = k * pool.BROAD_RATE * (Ti + Te) 88 | # R0 = k*b*D (Lipsitch M,Cohen T, Cooper B, et al. TransmissionDynamics and Control of Severe Acute Respiratory Syndrome. Science, 2003,300(5627): 1966-1970.) 89 | else: 90 | k = pool.in_touch / infective if infective != 0 else 0 91 | R0 = k * pool.BROAD_RATE * Ti 92 | else: 93 | R0 = np.nan 94 | Ti = np.nan 95 | Tg = np.nan 96 | Te = np.nan 97 | 98 | if mode in line: 99 | ax3_susceptible_data[1] = susceptible 100 | ax3_total_data[1] = total 101 | ax3_recover_data[1] = recovered 102 | ax4_exposed_data[1] = exposed 103 | ax4_contagious_data[1] = contagious 104 | ax5_infective_data[1] = infective 105 | ax5_diagnosed_data[1] = diagnosed 106 | ax6_r0_data[1] = R0 107 | 108 | ax1.clear() 109 | ax1.scatter(pool.getX(), pool.getY(), c = [colors_people[j] for j in status], marker = '.', \ 110 | alpha = 0.5, s = 10) 111 | ax1.set_title(f'Te:{Te:<10.2f}Ti:{Ti:<10.2f}Tg:{Tg:<10.2f}R0:{R0:.2f}\nTime:{time:<10}Susceptible:{susceptible:<10}Exposed:{exposed:<10}Infective:{infective:<10}Recovered:{recovered}') 112 | ax1.set_xticks([]) 113 | ax1.set_yticks([]) 114 | ax1.set_xlim(-boundry, boundry) 115 | ax1.set_ylim(-boundry, boundry) 116 | 117 | ax2.clear() 118 | ax2.scatter(hosX, hosY, c = [colors_bed[j] for j in status_hos], marker = '.', \ 119 | alpha = 1, s = 10) 120 | ax2.set_title(f'death:{PERSON_COUNT-total}\nhospitalized:{hospitalized}/{BED_COUNT}') 121 | ax2.set_xticks([]) 122 | ax2.set_yticks([]) 123 | 124 | cond.wait() 125 | cond.release() 126 | 127 | color_total = colors_people[7] 128 | color_contagious = colors_people[8] 129 | color_diagnosed = colors_people[6] 130 | color_susceptible = colors_people[0] 131 | color_exposed = colors_people[1] 132 | color_infective = colors_people[2] 133 | color_recovered = colors_people[4] 134 | color_R0 = colors_statics[0] 135 | 136 | if mode in line: 137 | if (time >= 1): 138 | ax3.plot([time-1, time], ax3_susceptible_data, color = color_susceptible) 139 | ax3.plot([time-1, time], ax3_total_data, color = color_total) 140 | ax3.plot([time-1, time], ax5_infective_data, color = color_infective) 141 | ax3.plot([time-1, time], ax4_exposed_data, color = color_exposed) 142 | ax3.plot([time-1, time], ax3_recover_data, color = color_recovered) if pool.recovered_included == True else None 143 | ax4.plot([time-1, time], ax4_exposed_data, color = color_exposed) 144 | ax4.plot([time-1, time], ax4_contagious_data, color = color_contagious) 145 | ax5.plot([time-1, time], ax5_infective_data, color = color_infective) 146 | ax5.plot([time-1, time], ax5_diagnosed_data, color = color_diagnosed) 147 | ax6.plot([time-1, time], ax6_r0_data, color = color_R0) 148 | else: 149 | ax3.bar(time, total, color = color_total, width=1) 150 | ax3.bar(time, susceptible, color = color_susceptible, width=1) 151 | ax3.bar(time, exposed, color = color_exposed, width=1) 152 | ax3.bar(time, infective, color = color_infective, width=1) 153 | ax3.bar(time, recovered, color = color_recovered, width=1) if pool.recovered_included == True else None 154 | ax4.bar(time, contagious, color = color_contagious, width=1) 155 | ax4.bar(time, exposed, color = color_exposed, width=1) 156 | ax5.bar(time, diagnosed, color = color_diagnosed, width=1) 157 | ax5.bar(time, infective, color = color_infective, width=1) 158 | ax6.bar(time, R0, color = color_R0, width=1) 159 | 160 | ax3.set_title(f'total({color_total}):{total}\nsusceptible({color_susceptible}):{susceptible}\nrecovered({color_recovered}):{recovered}\nexposed({color_exposed}):{exposed}\ninfective({color_infective}):{infective}') 161 | ax4.set_title(f'contagious({color_contagious}):{contagious}\nexposed({color_exposed}):{exposed}') 162 | ax5.set_title(f'diagnosed({color_diagnosed}):{diagnosed}\ninfective({color_infective}):{infective}') 163 | ax6.set_title(f'R0({color_R0}):{R0:.2f}\nBROAD_RATE:{pool.BROAD_RATE:.2f}\nu:{pool.u:.2f}') 164 | 165 | if mode in line: 166 | ax3_susceptible_data[0] = susceptible 167 | ax3_total_data[0] = total 168 | ax3_recover_data[0] = recovered 169 | ax4_exposed_data[0] = exposed 170 | ax4_contagious_data[0] = contagious 171 | ax5_infective_data[0] = infective 172 | ax5_diagnosed_data[0] = diagnosed 173 | ax6_r0_data[0] = R0 174 | 175 | anim = animation.FuncAnimation(fig=fig, init_func=init, func=multi_process, repeat=False) 176 | plt.show() 177 | -------------------------------------------------------------------------------- /hospital.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | class Hospital: 4 | def __init__(self, bed_counts): 5 | self.bed_counts = bed_counts 6 | 7 | self.width = int(bed_counts / 20) 8 | self.column = int(bed_counts / self.width) 9 | if not bed_counts % self.width == 0: 10 | self.column += 1 11 | 12 | self.beds = np.empty(shape=(0, 3), dtype=int) 13 | for i in range(self.column): 14 | for j in range(self.width): 15 | bed = [[i, j, 0]] 16 | self.beds = np.r_[self.beds, bed] 17 | 18 | def pickBed(self): 19 | try: 20 | tmp = np.where(self.beds[:, 2] == False)[0][0] 21 | self.beds[tmp, 2] = 1 22 | return self.beds[tmp] 23 | except: 24 | return np.array([]) 25 | 26 | def getX(self): 27 | return self.beds[:, 0] 28 | 29 | def getY(self): 30 | return self.beds[:, 1] 31 | 32 | def getStatus(self): 33 | return self.beds[:, 2] -------------------------------------------------------------------------------- /images/desc.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y1han/epidemic-simulation/1ab80f58bddb654b6265c3f03b94335e46edaf33/images/desc.jpeg -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from hospital import Hospital 3 | from peoplepool import PeoplePool 4 | from graph import graph 5 | 6 | def init(): 7 | ORIGINAL_COUNT = 50 #初始感染数量 8 | BROAD_RATE = 0.5 #被传染概率 9 | PROTECTION_RATE = 0.001 # 保护措施增长率(连续)(影响BROAD_RATE, DEATH_RATE, u) 10 | EXPOSED_TIME = 5 #平均潜伏时间 11 | HOSPITAL_RECEIVE_TIME = 3 #平均医院收治时间 12 | CURE_TIME = 10 #平均治疗时间 13 | IMMUNED_TIME = 30 #平均免疫期时间 14 | DEATH_RATE = 0.01/(CURE_TIME+10*HOSPITAL_RECEIVE_TIME) #每日死亡概率 15 | BED_COUNT = 2000 #医院床位 16 | SAFETY_DIST = 50 #安全距离 17 | SCALE = SAFETY_DIST * 20 #大小 18 | FLUCTUATION = 4 #各参数与平均值之间的波动 19 | u = 1 #流动意向 20 | PERSON_COUNT = 10000 #城市内人数 21 | can_exposed_infect = False # 潜伏期能否感染他人 22 | recovered_included = True # 是否有免疫期 23 | 24 | city = (0, 0) 25 | pool = PeoplePool(PERSON_COUNT, city, SCALE, BROAD_RATE, PROTECTION_RATE, DEATH_RATE, EXPOSED_TIME, IMMUNED_TIME, \ 26 | HOSPITAL_RECEIVE_TIME, CURE_TIME, SAFETY_DIST, u, FLUCTUATION, \ 27 | can_exposed_infect, recovered_included) 28 | hos = Hospital(BED_COUNT) 29 | 30 | for i in range(ORIGINAL_COUNT): 31 | idx = np.random.randint(0, PERSON_COUNT) 32 | pool.peoples[idx][2] = 1 # 设定status为1,即潜伏 33 | pool.peoples[idx][4] = 0 # 设定infected_time = 0 34 | 35 | return pool, hos 36 | 37 | def choose(pool, hos, mode='bar'): # mode='l'或者'line'时为折线图(line),否则为柱状图(bar) 38 | graph(pool, hos, mode) 39 | 40 | if '__main__' == __name__: 41 | pool, hos = init() 42 | choose(pool, hos, 'l') 43 | -------------------------------------------------------------------------------- /peoplepool.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.spatial import cKDTree 3 | 4 | dic = {'x':0, 'y':1, 'status':2, 'bed':3, 'infected_time':4, 'confirmed_time':5, 'hospitalized_time':6, 'immuned_time':7} 5 | x = dic['x'] 6 | y = dic['y'] 7 | status = dic['status'] 8 | bed = dic['bed'] 9 | infected_time = dic['infected_time'] 10 | confirmed_time = dic['confirmed_time'] 11 | hospitalized_time = dic['hospitalized_time'] 12 | immuned_time = dic['immuned_time'] 13 | 14 | class PeoplePool: 15 | def __init__(self, num, city, SCALE, BROAD_RATE, PROTECTION_RATE, DEATH_RATE, EXPOSED_TIME, IMMUNED_TIME, \ 16 | HOSPITAL_RECEIVE_TIME, CURE_TIME, SAFETY_DIST, u, FLUCTUATION, can_exposed_infect, recovered_included): 17 | self.num = num 18 | self.peoples = np.empty(shape=(0, len(dic)), dtype=int) 19 | self.BROAD_RATE = BROAD_RATE 20 | self.PROTECTION_RATE = PROTECTION_RATE 21 | self.DEATH_RATE = DEATH_RATE 22 | self.EXPOSED_TIME = EXPOSED_TIME 23 | self.HOSPITAL_RECEIVE_TIME = HOSPITAL_RECEIVE_TIME 24 | self.CURE_TIME = CURE_TIME 25 | self.IMMUNED_TIME = IMMUNED_TIME 26 | self.SAFETY_DIST = SAFETY_DIST 27 | self.FLUCTUATION = FLUCTUATION 28 | self.SCALE = SCALE 29 | self.u = np.exp(u) 30 | self.can_exposed_infect = can_exposed_infect 31 | self.recovered_included = recovered_included 32 | self.Tg = [] 33 | self.Te = [] 34 | self.Ti = [] 35 | self.in_touch = 0 36 | self.can_infect_status = [1, 2] if can_exposed_infect == True else [2] 37 | for i in range(num): 38 | x_init = self.SCALE*np.random.normal(0, 1) + city[0] 39 | y_init = self.SCALE*np.random.normal(0, 1) + city[1] 40 | # People Status: [0]Susceptible, [1]Exposed, [2]Infective, [3]Hospitalized, [4]Recovered, [5]Dead 41 | #[0]x, [1]y, [2]status, [3]bed, [4]infected_time, [5]confirmed_time, [6]hospitalized_time, [7]immuned_time 42 | people = [[x_init, y_init, 0, None, None, None, None, None]] 43 | self.peoples = np.r_[self.peoples, people] 44 | 45 | def getX(self, included=False): 46 | if not included: 47 | return self.peoples[(self.peoples[:,2] != 3) & (self.peoples[:,2] != 5)][:, x] 48 | else: 49 | return self.peoples[:, x] 50 | 51 | def getY(self, included=False): 52 | if not included: 53 | return self.peoples[(self.peoples[:,2] != 3) & (self.peoples[:,2] != 5)][:, y] 54 | else: 55 | return self.peoples[:, y] 56 | 57 | def getStatus(self, included=False): 58 | if not included: 59 | return self.peoples[(self.peoples[:,2] != 3) & (self.peoples[:,2] != 5)][:, status] 60 | else: 61 | return self.peoples[:, status] 62 | 63 | def getCoordinates(self, only=None): 64 | if only == 23: 65 | if self.can_infect_status == [1, 2]: 66 | return self.peoples[(self.peoples[:,2] == 1) | (self.peoples[:,2] == 2)][:, [x, y]] 67 | else: 68 | return self.peoples[self.peoples[:,2] == 2][:, [x, y]] 69 | elif only == 1: 70 | return self.peoples[self.peoples[:,2] == 0][:, [x, y]] 71 | else: 72 | return self.peoples[:, [x, y]] 73 | 74 | 75 | def update(self, time, hospital, cond): 76 | cond.acquire() 77 | cond.wait() 78 | protection_factor = np.exp(-self.PROTECTION_RATE) 79 | self.BROAD_RATE *= protection_factor 80 | self.u *= protection_factor 81 | self.DEATH_RATE *= protection_factor 82 | exposed_time = np.random.normal(self.EXPOSED_TIME, self.FLUCTUATION, size=(self.num, 1)) 83 | hospital_receive_time = np.random.normal(self.HOSPITAL_RECEIVE_TIME, self.FLUCTUATION, size=(self.num, 1)) 84 | cure_time = np.random.normal(self.CURE_TIME, self.FLUCTUATION, size=(self.num, 1)) 85 | immune_time = np.random.normal(self.IMMUNED_TIME, self.FLUCTUATION, size=(self.num, 1)) 86 | peoples = self.peoples 87 | coord = self.getCoordinates() 88 | tree = cKDTree(coord) 89 | coord_susceptible = self.getCoordinates(1) 90 | coord_contagious = self.getCoordinates(23) 91 | if len(coord_contagious) != 0: 92 | tree_susceptible = cKDTree(coord_susceptible) 93 | tree_contagious = cKDTree(coord_contagious) 94 | self.in_touch = tree_susceptible.count_neighbors(tree_contagious, r=self.SAFETY_DIST) 95 | else: 96 | self.in_touch = 0 97 | for idx, people in enumerate(peoples): 98 | if people[status] == 0: # Susceptible 99 | for index in tree.query_ball_point(people[0:2], self.SAFETY_DIST): 100 | if peoples[index][status] in self.can_infect_status: 101 | if np.random.rand() < self.BROAD_RATE: 102 | people[infected_time] = time 103 | people[status] = 1 104 | break 105 | elif people[status] == 1: # Exposed 106 | if (time - people[infected_time]) > exposed_time[idx][0]: 107 | people[confirmed_time] = time 108 | people[status] = 2 109 | self.Te.append(time-people[infected_time]) 110 | elif people[status] == 2: # Infective 111 | if np.random.rand() < self.DEATH_RATE: 112 | people[status] = 5 # Dead 113 | elif (time - people[confirmed_time]) > hospital_receive_time[idx][0]: 114 | tmp = hospital.pickBed() 115 | if len(tmp) == 0: 116 | print(f"Time={time:<6}无隔离病房床位") 117 | else: 118 | people[bed] = tmp 119 | people[status] = 3 # Hospitalized 120 | people[hospitalized_time] = time 121 | self.Ti.append(time-people[confirmed_time]) 122 | self.Tg.append(time-people[infected_time]) 123 | elif people[status] == 3: # Hospitalized 124 | if np.random.rand() < self.DEATH_RATE / 10: 125 | people[status] = 5 # Dead 126 | people[bed][2] = False 127 | people[bed] = None 128 | elif (time - people[hospitalized_time]) > cure_time[idx][0]: 129 | if self.recovered_included: 130 | people[status] = 4 # Recovered 131 | people[immuned_time] = time 132 | else: 133 | people[status] = 0 # Susceptible 134 | people[bed][2] = False 135 | people[bed] = None 136 | people[hospitalized_time] = None 137 | people[confirmed_time] = None 138 | people[infected_time] = None 139 | elif people[status] == 4: # Recovered 140 | if (time - people[immuned_time]) > immune_time[idx][0]: 141 | people[immuned_time] = None 142 | people[status] = 0 143 | elif people[status] == 5: # Dead 144 | continue 145 | peoples[:, [0, 1]] += self.u*self.SCALE/50*np.random.randn(self.num, 2) 146 | cond.notify() 147 | cond.release() --------------------------------------------------------------------------------