301 lines
No EOL
12 KiB
Text
301 lines
No EOL
12 KiB
Text
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"<p style=\"color: red; font-weight: bold; font-size: xx-large; text-align: center;\" align=\"center\">\n",
|
|
" 'CARA' (COVID Airborne Risk Assessment)\n",
|
|
"</p>\n",
|
|
"\n",
|
|
"<p style=\"color: blue; font-weight: bold; font-size: x-large; text-align: center;\" align=\"center\">\n",
|
|
"Airborne Transmission of SARS-CoV-2\n",
|
|
"</p>"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"%matplotlib widget\n",
|
|
"import ipywidgets as widgets\n",
|
|
"import matplotlib.pyplot as plt\n",
|
|
"import numpy as np\n",
|
|
"import typing"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"ename": "ModuleNotFoundError",
|
|
"evalue": "No module named 'cara'",
|
|
"output_type": "error",
|
|
"traceback": [
|
|
"\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
|
|
"\u001B[0;31mModuleNotFoundError\u001B[0m Traceback (most recent call last)",
|
|
"\u001B[0;32m<ipython-input-9-4b5d4c4413b1>\u001B[0m in \u001B[0;36m<module>\u001B[0;34m\u001B[0m\n\u001B[0;32m----> 1\u001B[0;31m \u001B[0;32mimport\u001B[0m \u001B[0mcara\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mmodels\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 2\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 3\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 4\u001B[0m \u001B[0;32mdef\u001B[0m \u001B[0mprepare_model\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mvolume\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mn_infected\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0;36m1\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mn_exposed\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0;36m10\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mmask\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0;34m'Type I'\u001B[0m\u001B[0;34m)\u001B[0m \u001B[0;34m->\u001B[0m \u001B[0mcara\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mmodels\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mModel\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 5\u001B[0m \"\"\"\n",
|
|
"\u001B[0;31mModuleNotFoundError\u001B[0m: No module named 'cara'"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"import cara.models\n",
|
|
"\n",
|
|
"\n",
|
|
"def prepare_model(volume, n_infected=1, n_exposed=10, mask='Type I') -> cara.models.Model:\n",
|
|
" \"\"\"\n",
|
|
" Transform configurable values into a cara model instance.\n",
|
|
" \n",
|
|
" \"\"\"\n",
|
|
" model = cara.models.Model(\n",
|
|
" room=cara.models.Room(volume=volume),\n",
|
|
" ventilation=cara.models.Ventilation(),\n",
|
|
" infected=cara.models.InfectedPerson(\n",
|
|
" virus=cara.models.Virus.types['SARS_CoV_2'],\n",
|
|
" present_times=((0, 4), (5, 8)),\n",
|
|
" mask=cara.models.Mask.types[mask],\n",
|
|
" activity=cara.models.Activity.types['Light exercise'],\n",
|
|
" expiration=cara.models.Expiration.types['Unmodulated Vocalization'],\n",
|
|
" ),\n",
|
|
" infected_occupants=n_infected,\n",
|
|
" exposed_occupants=n_exposed,\n",
|
|
" exposed_activity=cara.models.Activity.types['Light exercise'],\n",
|
|
" )\n",
|
|
" return model"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 10,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Setup our plotting environment.\n",
|
|
"plt.interactive(False)\n",
|
|
"fig_concentration_over_time = plt.figure()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 11,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Define some useful widget machinery.\n",
|
|
"\n",
|
|
"def collapsible(widgets_to_collapse: typing.List, title: str, start_collapsed=True):\n",
|
|
" collapsed = widgets.Accordion([widgets.VBox(widgets_to_collapse)])\n",
|
|
" collapsed.set_title(0, title)\n",
|
|
" if start_collapsed:\n",
|
|
" collapsed.selected_index = None\n",
|
|
" return collapsed\n",
|
|
"\n",
|
|
"\n",
|
|
"def widget_group(label_widget_pairs):\n",
|
|
" labels, widgets_ = zip(*label_widget_pairs) \n",
|
|
" labels_w = widgets.VBox(labels)\n",
|
|
" widgets_w = widgets.VBox(widgets_)\n",
|
|
" return widgets.HBox([labels_w, widgets_w])"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 12,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"application/vnd.jupyter.widget-view+json": {
|
|
"model_id": "f18dd83234d345e39f3e841dec017ff8",
|
|
"version_major": 2,
|
|
"version_minor": 0
|
|
},
|
|
"text/plain": [
|
|
"Accordion(children=(VBox(children=(HBox(children=(VBox(children=(Label(value='Room volume'),)), VBox(children=…"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"room_volume = widgets.IntSlider(value=75, min=10, max=150)\n",
|
|
"mask_used = widgets.Checkbox(value=True, description='Mask worn')\n",
|
|
"\n",
|
|
"collapsible(\n",
|
|
" [widget_group(\n",
|
|
" [[widgets.Label('Room volume'), room_volume]]\n",
|
|
" )],\n",
|
|
" title='Specification of workplace', start_collapsed=False,\n",
|
|
")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 13,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"application/vnd.jupyter.widget-view+json": {
|
|
"model_id": "45619847b1a9413f8c864775e597d515",
|
|
"version_major": 2,
|
|
"version_minor": 0
|
|
},
|
|
"text/plain": [
|
|
"Accordion(children=(VBox(children=(HBox(children=(VBox(children=(Label(value='Ventilation type'),)), VBox(chil…"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"ventilation_widgets = {\n",
|
|
" 'Natural': widgets.Label('Currently hard-coded to 514.74 / volume'),\n",
|
|
" 'other': widgets.Label('Not yet implemented.')\n",
|
|
"}\n",
|
|
"for name, widget in ventilation_widgets.items():\n",
|
|
" widget.layout.visible = False\n",
|
|
"\n",
|
|
"ventilation_w = widgets.ToggleButtons(\n",
|
|
" options=['Natural'], # cara.models.Ventilation.types.keys(),\n",
|
|
")\n",
|
|
"def toggle_ventilation(value):\n",
|
|
" for name, widget in ventilation_widgets.items():\n",
|
|
" widget.layout.display = 'none'\n",
|
|
" other = ventilation_widgets['other']\n",
|
|
" widget = ventilation_widgets.get(value, other)\n",
|
|
" widget.layout.visible = True\n",
|
|
" widget.layout.display = 'block'\n",
|
|
"\n",
|
|
"ventilation_w.observe(lambda event: toggle_ventilation(event['new']), 'value')\n",
|
|
"toggle_ventilation(ventilation_w.value)\n",
|
|
"\n",
|
|
"\n",
|
|
"collapsible(\n",
|
|
" [widget_group([[widgets.Label('Ventilation type'), ventilation_w]])]\n",
|
|
" + list(ventilation_widgets.values()),\n",
|
|
" title='Ventilation scheme'\n",
|
|
")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 14,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"ename": "NameError",
|
|
"evalue": "name 'prepare_model' is not defined",
|
|
"output_type": "error",
|
|
"traceback": [
|
|
"\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
|
|
"\u001B[0;31mNameError\u001B[0m Traceback (most recent call last)",
|
|
"\u001B[0;32m<ipython-input-14-30d890ac3193>\u001B[0m in \u001B[0;36m<module>\u001B[0;34m\u001B[0m\n\u001B[1;32m 48\u001B[0m \u001B[0mobservable\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mobserve\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mplot_concentrations\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 49\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m---> 50\u001B[0;31m \u001B[0mplot_concentrations\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;36m1\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 51\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 52\u001B[0m \u001B[0mfig\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mcanvas\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mtoolbar_visible\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0;32mTrue\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
|
|
"\u001B[0;32m<ipython-input-14-30d890ac3193>\u001B[0m in \u001B[0;36mplot_concentrations\u001B[0;34m(_)\u001B[0m\n\u001B[1;32m 8\u001B[0m \u001B[0;32mdef\u001B[0m \u001B[0mplot_concentrations\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0m_\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 9\u001B[0m \u001B[0;32mglobal\u001B[0m \u001B[0mline\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m---> 10\u001B[0;31m \u001B[0mmodel\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mprepare_model\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mroom_volume\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mvalue\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 11\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 12\u001B[0m \u001B[0mts\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mnp\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0marange\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;36m0\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;36m10.\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;36m0.01\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
|
|
"\u001B[0;31mNameError\u001B[0m: name 'prepare_model' is not defined"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"import matplotlib.pyplot as plt\n",
|
|
"\n",
|
|
"line = None\n",
|
|
"# plt.ioff()\n",
|
|
"# fig = plt.figure()\n",
|
|
"fig = fig_concentration_over_time\n",
|
|
"\n",
|
|
"def plot_concentrations(_):\n",
|
|
" global line\n",
|
|
" model = prepare_model(room_volume.value)\n",
|
|
"\n",
|
|
" ts = np.arange(0, 10., 0.01)\n",
|
|
" concentration = [model.concentration(t) for t in ts]\n",
|
|
"\n",
|
|
" ax = fig.gca()\n",
|
|
" \n",
|
|
" plt.text(0.5, 0.9, 'Without masks & window open', transform=ax.transAxes, ha='center')\n",
|
|
" if line is None:\n",
|
|
" ax.spines['right'].set_visible(False)\n",
|
|
" ax.spines['top'].set_visible(False)\n",
|
|
" [line] = plt.plot(ts, concentration)\n",
|
|
" ax.set_xlabel('Time (hours)')\n",
|
|
" ax.set_ylabel('Concentration ($q/m^3$)')\n",
|
|
" plt.title('Concentration of infectious quanta aerosols')\n",
|
|
" \n",
|
|
" ax.set_ymargin(0.2)\n",
|
|
" ax.set_ylim(bottom=0)\n",
|
|
" else:\n",
|
|
" line.set_data(ts, concentration)\n",
|
|
" ax.relim()\n",
|
|
" ax.autoscale_view()\n",
|
|
" \n",
|
|
" plt.draw()\n",
|
|
"\n",
|
|
"# print(f'Probability of infection: {np.round(model.[\"P\"], 1)}')\n",
|
|
"# print(f'Expected number of new cases: {prepared[\"R0\"]}')\n",
|
|
" \n",
|
|
"\n",
|
|
"# widgets.interact(\n",
|
|
"# plot_concentrations,\n",
|
|
"# volume=room_volume,\n",
|
|
"# n_exposed=widgets.IntSlider(value=10, min=0, max=25),\n",
|
|
"# n_infected=widgets.IntSlider(value=1, min=1, max=5),\n",
|
|
"# );\n",
|
|
"\n",
|
|
"\n",
|
|
"for observable in [room_volume]:\n",
|
|
" observable.observe(plot_concentrations)\n",
|
|
"\n",
|
|
"plot_concentrations(1)\n",
|
|
"\n",
|
|
"fig.canvas.toolbar_visible = True\n",
|
|
"fig.canvas.toolbar.collapsed = True\n",
|
|
"fig.canvas.footer_visible = False\n",
|
|
"fig.canvas.header_visible = False\n",
|
|
"\n",
|
|
"\n",
|
|
"collapsible([\n",
|
|
" widgets.HBox([\n",
|
|
" fig.canvas,\n",
|
|
" # text_report,\n",
|
|
" ])\n",
|
|
"], 'Report', start_collapsed=False)\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "Python 3",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.8.5"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 4
|
|
} |