Skip to content

D:/projects/scheduling/kalasim/docs/userguide/docs/examples/emergency_room.ipynb

Emergency Room

An emergency room is one of the most complex and time-critical systems in a city. Patients arrive unpredictably, injuries differ in severity, and resources like doctors and operating rooms are limited. Every decision—who to treat first, which room to prepare, how to allocate staff—can directly affect patient outcomes.

Even in a modern hospital, the ER operates under constant pressure. Delays increase risk, overcrowding reduces efficiency, and poor coordination can lead to serious consequences. The challenge is not only medical—it is operational.

Recent portrayals of emergency medicine, such as The Pitt, highlight the intensity of hospital workflows. While dramatized, they remind us how resource-constrained these environments are and how small decisions can cascade into major consequences. This motivated the simplified model developed here.

image.png

© Warner Bros. Discovery Source

In this article, we construct a simulation model of an emergency room to understand its dynamics and identify opportunities for improvement. The goal is to analyze how scheduling rules, resource constraints, and system interactions influence performance. See Günal 2010 for an excellent review of related work.

Specifically, we will:

  1. Model the current ER process as a simulation.
  2. Define key objectives and measurable performance indicators.
  3. Analyze system behavior and identify bottlenecks.
  4. Explore alternative decision policies to improve outcomes.

The conceptual framework for this model was inspired by Kramer et al. (2019):

"Other relevant applications arise in the context of health-care, where... patients have to be assigned to surgery rooms that must be equipped by considering the type... of surgery to be performed. In such cases, the weight usually models a level of urgency for the patient."

Let’s begin by formalizing the process model.

Process Model

Patients are classified in two ways: 1. Severity: The ER uses the Emergency Severity Index to triage patients based on acuity and anticipated resource needs. 2. Type of Injury: Defined based on standard medical classifications.

Resources

  • Surgery Rooms: Must be prepared based on the type (family) of surgery. Setup times vary by injury type and are defined in the model configuration.
  • Doctors: Qualified for specific subsets of injuries.

Process Dynamics

  • PD-A: Patient health deteriorates if not treated; severity increases over time, potentially leading to death.
  • PD-B: Efficiency decreases as the waiting room becomes overcrowded due to staff stress and resource over-allocation.
  • PD-C: High-severity patients face a risk of mortality during surgery.
  • PD-D: Surgery duration correlates with injury severity.
  • PD-E: Patient arrival rates fluctuate, typically decreasing at night.

While many supporting processes exist in a real ER, we focus on these core elements to maintain a useful level of abstraction for process optimization.

Key Objectives & Observations

The simulation balances two competing factors: the natural deterioration of patient health and the treatment process that stops it. Our objective is to minimize loss of life and waiting times while maximizing treatment efficiency.

The head nurse currently schedules patients based on a simple principle:

Most urgent injuries first.

We aim to explore if we can also: * Minimize overall waiting times. * Reduce the frequency of surgery room re-configurations (setups).

Analysis

Due to the variety of injuries, surgery rooms require frequent setup changes. We often observe rooms being re-configured even when other patients with the same injury type are already waiting.

@file:Repository("*mavenLocal")
//@file:DependsOn("com.github.holgerbrandl:kalasim:0.6.97-SNAPSHOT")

//todo use artifact for release
//@file:DependsOn("com.github.holgerbrandl:kalasim:0.6.92")
//@file:DependsOn("com.github.holgerbrandl:kravis:0.8.1")
//@file:DependsOn("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2")
import org.kalasim.examples.er.*
import org.kalasim.*
import org.kalasim.monitors.MetricTimeline
import kotlin.math.pow
import kotlin.math.sqrt
import kotlin.random.Random
import org.kalasim.plot.kravis.display
import org.koin.core.qualifier.named


import org.kalasim.examples.er.*

kravis.SessionPrefs.OUTPUT_DEVICE = kravis.device.JupyterDevice() // bug in library,

Simulation

Let's start by creating the model.

val er = EmergencyRoom(
    numPhysicians = 8,
    patientArrival = exponential(0.25.hours),
)

// enable log to capture events for later analysis
er.enableEventLog()
org.kalasim.EventLog@499619bb

Now run it for some days, and print its state afterwards

er.run(7.days)

// print the object to render short status summary
er
EmergencyRoom Summary Statistics (at t=2022-03-02T06:00:00Z):
  Physicians: 8
  Rooms: 4
  Waiting Area Capacity: 300

  Incoming Patients: 344
  Currently Waiting: 12
  Treated Successfully: 291
  Deceased: 37
  Success Rate: 88,72%

  Nurse Policy: FifoNurse
er.waitingLine.sizeTimeline.display("Waiting Time")

jpeg

er.treatedMonitor.display("Treated Patients")

jpeg

Thats visualization is technically correct, but most likely we rather want to see treated patients per day

import org.kalasim.plot.kravis.displayStateCounts

er.patients.map { it.severity }.displayStateCounts()

jpeg

er.deceasedMonitor.display("Deceased Patients")

jpeg

Analysis

To analyze the model, we first use different visualization functinos defined for collection os states, components and resources.

import org.kalasim.plot.kravis.displayTimelines

er.doctors.displayTimelines()

jpeg

er.doctors.displayTimelines(byRequester = true, colorBy = { it.requester.name })

jpeg

When studying the data from above, we observe that an ER with more staff than available surgery rooms (e.g., numPhysicians = 8 but only a limited number of rooms as defined in the EmergencyRoom example), we observe that the bottleneck shifts from staff availability to room availability.

import org.kalasim.plot.kravis.displayStateCounts

er.patients.map { it.patientStatus }.displayStateCounts()

jpeg

Daily statistics vary slightly, but the overall distribution remains stable. This is expected since the current model does not yet include time-varying factors such as weekend shifts or staff changes.

er.patients.take(20).map { it.patientStatus }.displayTimelines(to = er.startDate + 4.hours)

jpeg

We observe the different arrivals and their stay durations. Most of the sampled patients in the observed time window are going into surgey without a previous waiting time.

import org.kalasim.plot.kravis.displayStayDistributions

er.patients.map { it.patientStatus }.displayStayDistributions()

jpeg

The distribution of stay durations is heavily skewed towards shorter stays, indicating that most patients are discharged quickly. This could be due to the efficient triage and treatment processes in place.

Finally, as a DES model is driven by events, we inspect raw events from the model (using the kotlin-dataframe interation)

import org.kalasim.analysis.InteractionEvent

val interactions = er.eventsInstanceOf<InteractionEvent>()

interactions.first()
{"receiver":"main","details":"running; Hold +168.00, scheduled for 168.00","time":"2022-02-23T06:00:00Z","state":"SCHEDULED","type":"RescheduledEvent"}
val intDF = interactions.toDataFrame()
intDF.take(10)
        <iframe onload="o_resize_iframe_out_5()" style="width:100%;" class="result_container" id="iframe_out_5" frameBorder="0" srcdoc="        &lt;html&gt;
    &lt;head&gt;
        &lt;style type=&quot;text&sol;css&quot;&gt;
            :root {
--background: #fff;
--background-odd: #f5f5f5;
--background-hover: #d9edfd;
--header-text-color: #474747;
--text-color: #848484;
--text-color-dark: #000;
--text-color-medium: #737373;
--text-color-pale: #b3b3b3;
--inner-border-color: #aaa;
--bold-border-color: #000;
--link-color: #296eaa;
--link-color-pale: #296eaa;
--link-hover: #1a466c;

}

:root[theme="dark"], :root [data-jp-theme-light="false"], .dataframe_dark{ --background: #303030; --background-odd: #3c3c3c; --background-hover: #464646; --header-text-color: #dddddd; --text-color: #b3b3b3; --text-color-dark: #dddddd; --text-color-medium: #b2b2b2; --text-color-pale: #737373; --inner-border-color: #707070; --bold-border-color: #777777; --link-color: #008dc0; --link-color-pale: #97e1fb; --link-hover: #00688e; }

p.dataframe_description { color: var(--text-color-dark); }

table.dataframe { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 12px; background-color: var(--background); color: var(--text-color-dark); border: none; border-collapse: collapse; }

table.dataframe th, td { padding: 6px; border: 1px solid transparent; text-align: left; }

table.dataframe th { background-color: var(--background); color: var(--header-text-color); }

table.dataframe td { vertical-align: top; white-space: nowrap; }

table.dataframe th.bottomBorder { border-bottom-color: var(--bold-border-color); }

table.dataframe tbody > tr:nth-child(odd) { background: var(--background-odd); }

table.dataframe tbody > tr:nth-child(even) { background: var(--background); }

table.dataframe tbody > tr:hover { background: var(--background-hover); }

table.dataframe a { cursor: pointer; color: var(--link-color); text-decoration: none; }

table.dataframe tr:hover > td a { color: var(--link-color-pale); }

table.dataframe a:hover { color: var(--link-hover); text-decoration: underline; }

table.dataframe img { max-width: fit-content; }

table.dataframe th.complex { background-color: var(--background); border: 1px solid var(--background); }

table.dataframe .leftBorder { border-left-color: var(--inner-border-color); }

table.dataframe .rightBorder { border-right-color: var(--inner-border-color); }

table.dataframe .rightAlign { text-align: right; }

table.dataframe .expanderSvg { width: 8px; height: 8px; margin-right: 3px; }

table.dataframe .expander { display: flex; align-items: center; }

/ formatting /

table.dataframe .null { color: var(--text-color-pale); }

table.dataframe .structural { color: var(--text-color-medium); font-weight: bold; }

table.dataframe .dataFrameCaption { font-weight: bold; }

table.dataframe .numbers { color: var(--text-color-dark); }

table.dataframe td:hover .formatted .structural, .null { color: var(--text-color-dark); }

table.dataframe tr:hover .formatted .structural, .null { color: var(--text-color-dark); }

:root { --scroll-bg: #f5f5f5; --scroll-fg: #b3b3b3; } :root[theme="dark"], :root [data-jp-theme-light="false"]{ --scroll-bg: #3c3c3c; --scroll-fg: #97e1fb; } body { scrollbar-color: var(--scroll-fg) var(--scroll-bg); } body::-webkit-scrollbar { width: 10px; / Mostly for vertical scrollbars / height: 10px; / Mostly for horizontal scrollbars / } body::-webkit-scrollbar-thumb { background-color: var(--scroll-fg); } body::-webkit-scrollbar-track { background-color: var(--scroll-bg); } </style> </head> <body> <table class="dataframe" id="df_-2013265912"></table>

<p class="dataframe_description">DataFrame: rowsCount = 10, columnsCount = 7</p>

    &lt;&sol;body&gt;
    &lt;script&gt;
        (function () {
window.DataFrame = window.DataFrame || new (function () {
    this.addTable = function (df) {
        let cols = df.cols;
        for (let i = 0; i &lt; cols.length; i++) {
            for (let c of cols[i].children) {
                cols[c].parent = i;
            }
        }
        df.nrow = 0
        for (let i = 0; i &lt; df.cols.length; i++) {
            if (df.cols[i].values.length &gt; df.nrow) df.nrow = df.cols[i].values.length
        }
        if (df.id === df.rootId) {
            df.expandedFrames = new Set()
            df.childFrames = {}
            const table = this.getTableElement(df.id)
            table.df = df
            for (let i = 0; i &lt; df.cols.length; i++) {
                let col = df.cols[i]
                if (col.parent === undefined &amp;&amp; col.children.length &gt; 0) col.expanded = true
            }
        } else {
            const rootDf = this.getTableData(df.rootId)
            rootDf.childFrames[df.id] = df
        }
    }

    this.computeRenderData = function (df) {
        let result = []
        let pos = 0
        for (let col = 0; col &lt; df.cols.length; col++) {
            if (df.cols[col].parent === undefined)
                pos += this.computeRenderDataRec(df.cols, col, pos, 0, result, false, false)
        }
        for (let i = 0; i &lt; result.length; i++) {
            let row = result[i]
            for (let j = 0; j &lt; row.length; j++) {
                let cell = row[j]
                if (j === 0)
                    cell.leftBd = false
                if (j &lt; row.length - 1) {
                    let nextData = row[j + 1]
                    if (nextData.leftBd) cell.rightBd = true
                    else if (cell.rightBd) nextData.leftBd = true
                } else cell.rightBd = false
            }
        }
        return result
    }

    this.computeRenderDataRec = function (cols, colId, pos, depth, result, leftBorder, rightBorder) {
        if (result.length === depth) {
            const array = [];
            if (pos &gt; 0) {
                let j = 0
                for (let i = 0; j &lt; pos; i++) {
                    let c = result[depth - 1][i]
                    j += c.span
                    let copy = Object.assign({empty: true}, c)
                    array.push(copy)
                }
            }
            result.push(array)
        }
        const col = cols[colId];
        let size = 0;
        if (col.expanded) {
            let childPos = pos
            for (let i = 0; i &lt; col.children.length; i++) {
                let child = col.children[i]
                let childLeft = i === 0 &amp;&amp; (col.children.length &gt; 1 || leftBorder)
                let childRight = i === col.children.length - 1 &amp;&amp; (col.children.length &gt; 1 || rightBorder)
                let childSize = this.computeRenderDataRec(cols, child, childPos, depth + 1, result, childLeft, childRight)
                childPos += childSize
                size += childSize
            }
        } else {
            for (let i = depth + 1; i &lt; result.length; i++)
                result[i].push({id: colId, span: 1, leftBd: leftBorder, rightBd: rightBorder, empty: true})
            size = 1
        }
        let left = leftBorder
        let right = rightBorder
        if (size &gt; 1) {
            left = true
            right = true
        }
        result[depth].push({id: colId, span: size, leftBd: left, rightBd: right})
        return size
    }

    this.getTableElement = function (id) {
        return document.getElementById(&quot;df_&quot; + id)
    }

    this.getTableData = function (id) {
        return this.getTableElement(id).df
    }

    this.createExpander = function (isExpanded) {
        const svgNs = &quot;http:&sol;&sol;www.w3.org&sol;2000&sol;svg&quot;
        let svg = document.createElementNS(svgNs, &quot;svg&quot;)
        svg.classList.add(&quot;expanderSvg&quot;)
        let path = document.createElementNS(svgNs, &quot;path&quot;)
        if (isExpanded) {
            svg.setAttribute(&quot;viewBox&quot;, &quot;0 -2 8 8&quot;)
            path.setAttribute(&quot;d&quot;, &quot;M1 0 l-1 1 4 4 4 -4 -1 -1 -3 3Z&quot;)
        } else {
            svg.setAttribute(&quot;viewBox&quot;, &quot;-2 0 8 8&quot;)
            path.setAttribute(&quot;d&quot;, &quot;M1 0 l-1 1 3 3 -3 3 1 1 4 -4Z&quot;)
        }
        path.setAttribute(&quot;fill&quot;, &quot;currentColor&quot;)
        svg.appendChild(path)
        return svg
    }

    this.renderTable = function (id) {

        let table = this.getTableElement(id)

        if (table === null) return

        table.innerHTML = &quot;&quot;

        let df = table.df
        let rootDf = df.rootId === df.id ? df : this.getTableData(df.rootId)

        &sol;&sol; header
        let header = document.createElement(&quot;thead&quot;)
        table.appendChild(header)

        let renderData = this.computeRenderData(df)
        for (let j = 0; j &lt; renderData.length; j++) {
            let rowData = renderData[j]
            let tr = document.createElement(&quot;tr&quot;);
            let isLastRow = j === renderData.length - 1
            header.appendChild(tr);
            for (let i = 0; i &lt; rowData.length; i++) {
                let cell = rowData[i]
                let th = document.createElement(&quot;th&quot;);
                th.setAttribute(&quot;colspan&quot;, cell.span)
                let colId = cell.id
                let col = df.cols[colId];
                if (!cell.empty) {
                    if (col.children.length === 0) {
                        th.innerHTML = col.name
                    } else {
                        let link = document.createElement(&quot;a&quot;)
                        link.className = &quot;expander&quot;
                        let that = this
                        link.onclick = function () {
                            col.expanded = !col.expanded
                            that.renderTable(id)
                        }
                        link.appendChild(this.createExpander(col.expanded))
                        link.innerHTML += col.name
                        th.appendChild(link)
                    }
                }
                let classes = (cell.leftBd ? &quot; leftBorder&quot; : &quot;&quot;) + (cell.rightBd ? &quot; rightBorder&quot; : &quot;&quot;)
                if (col.rightAlign)
                    classes += &quot; rightAlign&quot;
                if (isLastRow)
                    classes += &quot; bottomBorder&quot;
                if (classes.length &gt; 0)
                    th.setAttribute(&quot;class&quot;, classes)
                tr.appendChild(th)
            }
        }

        &sol;&sol; body
        let body = document.createElement(&quot;tbody&quot;)
        table.appendChild(body)

        let columns = renderData.pop()
        for (let row = 0; row &lt; df.nrow; row++) {
            let tr = document.createElement(&quot;tr&quot;);
            body.appendChild(tr)
            for (let i = 0; i &lt; columns.length; i++) {
                let cell = columns[i]
                let td = document.createElement(&quot;td&quot;);
                let colId = cell.id
                let col = df.cols[colId]
                let classes = (cell.leftBd ? &quot; leftBorder&quot; : &quot;&quot;) + (cell.rightBd ? &quot; rightBorder&quot; : &quot;&quot;)
                if (col.rightAlign)
                    classes += &quot; rightAlign&quot;
                if (classes.length &gt; 0)
                    td.setAttribute(&quot;class&quot;, classes)
                tr.appendChild(td)
                let value = col.values[row]
                if (value.frameId !== undefined) {
                    let frameId = value.frameId
                    let expanded = rootDf.expandedFrames.has(frameId)
                    let link = document.createElement(&quot;a&quot;)
                    link.className = &quot;expander&quot;
                    let that = this
                    link.onclick = function () {
                        if (rootDf.expandedFrames.has(frameId))
                            rootDf.expandedFrames.delete(frameId)
                        else rootDf.expandedFrames.add(frameId)
                        that.renderTable(id)
                    }
                    link.appendChild(this.createExpander(expanded))
                    link.innerHTML += value.value
                    if (expanded) {
                        td.appendChild(link)
                        td.appendChild(document.createElement(&quot;p&quot;))
                        const childTable = document.createElement(&quot;table&quot;)
                        childTable.className = &quot;dataframe&quot;
                        childTable.id = &quot;df_&quot; + frameId
                        let childDf = rootDf.childFrames[frameId]
                        childTable.df = childDf
                        td.appendChild(childTable)
                        this.renderTable(frameId)
                        if (childDf.nrow !== childDf.totalRows) {
                            const footer = document.createElement(&quot;p&quot;)
                            footer.innerText = `... showing only top ${childDf.nrow} of ${childDf.totalRows} rows`
                            td.appendChild(footer)
                        }
                    } else {
                        td.appendChild(link)
                    }
                } else if (value.style !== undefined) {
                    td.innerHTML = value.value
                    td.setAttribute(&quot;style&quot;, value.style)
                } else td.innerHTML = value
                this.nodeScriptReplace(td)
            }
        }
    }

    this.nodeScriptReplace = function (node) {
        if (this.nodeScriptIs(node) === true) {
            node.parentNode.replaceChild(this.nodeScriptClone(node), node);
        } else {
            let i = -1, children = node.childNodes;
            while (++i &lt; children.length) {
                this.nodeScriptReplace(children[i]);
            }
        }

        return node;
    }

    this.nodeScriptClone = function (node) {
        let script = document.createElement(&quot;script&quot;);
        script.text = node.innerHTML;

        let i = -1, attrs = node.attributes, attr;
        while (++i &lt; attrs.length) {
            script.setAttribute((attr = attrs[i]).name, attr.value);
        }
        return script;
    }

    this.nodeScriptIs = function (node) {
        return node.tagName === 'SCRIPT';
    }
})()

window.call_DataFrame = function (f) {
    return f();
};

let funQueue = window[&quot;kotlinQueues&quot;] &amp;&amp; window[&quot;kotlinQueues&quot;][&quot;DataFrame&quot;];
if (funQueue) {
    funQueue.forEach(function (f) {
        f();
    });
    funQueue = [];
}

})()

/<!--/ call_DataFrame(function() { DataFrame.addTable({ cols: [{ name: "<span title=\"time: kotlinx.datetime.Instant\">time</span>", children: [], rightAlign: false, values: ["2022-02-23T06:00:00Z","2022-02-23T06:00:00Z","2022-02-23T06:00:00Z","2022-02-23T06:00:00Z","2022-02-23T06:00:00Z","2022-02-23T06:00:00Z","2022-02-23T06:04:57.988113230Z","2022-02-23T06:04:57.988113230Z","2022-02-23T06:04:57.988113230Z","2022-02-23T06:16:54.513625724Z"] }, { name: "<span title=\"current: org.kalasim.Component?\">current</span>", children: [], rightAlign: false, values: ["<span class=\"formatted\" title=\"\"><span class=\"null\">null</span></span>","room 0","room 1","room 2","room 3","ComponentGenerator.1","ComponentGenerator.1","ComponentGenerator.1","ComponentGenerator.1","ComponentGenerator.1"] }, { name: "<span title=\"component: org.kalasim.Component\">component</span>", children: [], rightAlign: false, values: ["main","room 0","room 1","room 2","room 3","ComponentGenerator.1","<span class=\"formatted\" title=\"Patient(fullName=Armando Kessler, patientId=0, type=Scratches, severity=Armando Kessler[Emergent], patientStatus=Armando Kessler[Waiting])\">Patient(fullName=Armando Kessler, pat<span class=\"structural\">...</span></span>","<span class=\"formatted\" title=\"Patient(fullName=Armando Kessler, patientId=0, type=Scratches, severity=Armando Kessler[Emergent], patientStatus=Armando Kessler[Waiting])\">Patient(fullName=Armando Kessler, pat<span class=\"structural\">...</span></span>","ComponentGenerator.1","<span class=\"formatted\" title=\"Patient(fullName=Laurie Keebler, patientId=1, type=Dislocations, severity=Laurie Keebler[Resuscitation], patientStatus=Laurie Keebler[Waiting])\">Patient(fullName=Laurie Keebler, pati<span class=\"structural\">...</span></span>"] }, { name: "<span title=\"actionFn: Function0<String>?\">actionFn</span>", children: [], rightAlign: false, values: ["<span class=\"formatted\" title=\"\"><span class=\"null\">null</span></span>","<span class=\"formatted\" title=\"org.kalasim.analysis.EventsKt$$Lambda/0x0000000015995aa8@2f19d994\">org.kalasim.analysis.EventsKt$$Lambda<span class=\"structural\">...</span></span>","<span class=\"formatted\" title=\"org.kalasim.analysis.EventsKt$$Lambda/0x0000000015995aa8@2e962c62\">org.kalasim.analysis.EventsKt$$Lambda<span class=\"structural\">...</span></span>","<span class=\"formatted\" title=\"org.kalasim.analysis.EventsKt$$Lambda/0x0000000015995aa8@547debdc\">org.kalasim.analysis.EventsKt$$Lambda<span class=\"structural\">...</span></span>","<span class=\"formatted\" title=\"org.kalasim.analysis.EventsKt$$Lambda/0x0000000015995aa8@a2bb420\">org.kalasim.analysis.EventsKt$$Lambda<span class=\"structural\">...</span></span>","<span class=\"formatted\" title=\"\"><span class=\"null\">null</span></span>","<span class=\"formatted\" title=\"\"><span class=\"null\">null</span></span>","<span class=\"formatted\" title=\"org.kalasim.analysis.EventsKt$$Lambda/0x0000000015995aa8@6b9f0988\">org.kalasim.analysis.EventsKt$$Lambda<span class=\"structural\">...</span></span>","<span class=\"formatted\" title=\"\"><span class=\"null\">null</span></span>","<span class=\"formatted\" title=\"\"><span class=\"null\">null</span></span>"] }, { name: "<span title=\"action: String\">action</span>", children: [], rightAlign: false, values: ["<span class=\"formatted\" title=\"running; Hold +168.00, scheduled for 168.00\">running; Hold +168.00, scheduled for <span class=\"structural\">...</span></span>","canceled","canceled","canceled","canceled","Hold +.08, scheduled for .08","Activated, scheduled for .08","canceled","Hold +.20, scheduled for .28","Activated, scheduled for .28"] }, { name: "<span title=\"eventType: String\">eventType</span>", children: [], rightAlign: false, values: ["RescheduledEvent","ComponentStateChangeEvent","ComponentStateChangeEvent","ComponentStateChangeEvent","ComponentStateChangeEvent","RescheduledEvent","RescheduledEvent","ComponentStateChangeEvent","RescheduledEvent","RescheduledEvent"] }, { name: "<span title=\"tickTime: Long\">tickTime</span>", children: [], rightAlign: true, values: ["<span class=\"formatted\" title=\"\"><span class=\"numbers\">1645596000</span></span>","<span class=\"formatted\" title=\"\"><span class=\"numbers\">1645596000</span></span>","<span class=\"formatted\" title=\"\"><span class=\"numbers\">1645596000</span></span>","<span class=\"formatted\" title=\"\"><span class=\"numbers\">1645596000</span></span>","<span class=\"formatted\" title=\"\"><span class=\"numbers\">1645596000</span></span>","<span class=\"formatted\" title=\"\"><span class=\"numbers\">1645596000</span></span>","<span class=\"formatted\" title=\"\"><span class=\"numbers\">1645596297</span></span>","<span class=\"formatted\" title=\"\"><span class=\"numbers\">1645596297</span></span>","<span class=\"formatted\" title=\"\"><span class=\"numbers\">1645596297</span></span>","<span class=\"formatted\" title=\"\"><span class=\"numbers\">1645597014</span></span>"] }, ], id: -2013265912, rootId: -2013265912, totalRows: 10 } ) }); /-->/

call_DataFrame(function() { DataFrame.renderTable(-2013265912) });

    &lt;&sol;script&gt;
    &lt;&sol;html&gt;"></iframe>
        <script>
            function o_resize_iframe_out_5() {
                let elem = document.getElementById("iframe_out_5");
                resize_iframe_out_5(elem);
                setInterval(resize_iframe_out_5, 5000, elem);
            }
            function resize_iframe_out_5(el) {
                let h = el.contentWindow.document.body.scrollHeight;
                el.height = h === 0 ? 0 : h + 41;
            }
        </script>        <html>
    <head>
        <style type="text/css">
            :root {
--background: #fff;
--background-odd: #f5f5f5;
--background-hover: #d9edfd;
--header-text-color: #474747;
--text-color: #848484;
--text-color-dark: #000;
--text-color-medium: #737373;
--text-color-pale: #b3b3b3;
--inner-border-color: #aaa;
--bold-border-color: #000;
--link-color: #296eaa;
--link-color-pale: #296eaa;
--link-hover: #1a466c;

}

:root[theme="dark"], :root [data-jp-theme-light="false"], .dataframe_dark{ --background: #303030; --background-odd: #3c3c3c; --background-hover: #464646; --header-text-color: #dddddd; --text-color: #b3b3b3; --text-color-dark: #dddddd; --text-color-medium: #b2b2b2; --text-color-pale: #737373; --inner-border-color: #707070; --bold-border-color: #777777; --link-color: #008dc0; --link-color-pale: #97e1fb; --link-hover: #00688e; }

p.dataframe_description { color: var(--text-color-dark); }

table.dataframe { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 12px; background-color: var(--background); color: var(--text-color-dark); border: none; border-collapse: collapse; }

table.dataframe th, td { padding: 6px; border: 1px solid transparent; text-align: left; }

table.dataframe th { background-color: var(--background); color: var(--header-text-color); }

table.dataframe td { vertical-align: top; white-space: nowrap; }

table.dataframe th.bottomBorder { border-bottom-color: var(--bold-border-color); }

table.dataframe tbody > tr:nth-child(odd) { background: var(--background-odd); }

table.dataframe tbody > tr:nth-child(even) { background: var(--background); }

table.dataframe tbody > tr:hover { background: var(--background-hover); }

table.dataframe a { cursor: pointer; color: var(--link-color); text-decoration: none; }

table.dataframe tr:hover > td a { color: var(--link-color-pale); }

table.dataframe a:hover { color: var(--link-hover); text-decoration: underline; }

table.dataframe img { max-width: fit-content; }

table.dataframe th.complex { background-color: var(--background); border: 1px solid var(--background); }

table.dataframe .leftBorder { border-left-color: var(--inner-border-color); }

table.dataframe .rightBorder { border-right-color: var(--inner-border-color); }

table.dataframe .rightAlign { text-align: right; }

table.dataframe .expanderSvg { width: 8px; height: 8px; margin-right: 3px; }

table.dataframe .expander { display: flex; align-items: center; }

/ formatting /

table.dataframe .null { color: var(--text-color-pale); }

table.dataframe .structural { color: var(--text-color-medium); font-weight: bold; }

table.dataframe .dataFrameCaption { font-weight: bold; }

table.dataframe .numbers { color: var(--text-color-dark); }

table.dataframe td:hover .formatted .structural, .null { color: var(--text-color-dark); }

table.dataframe tr:hover .formatted .structural, .null { color: var(--text-color-dark); }

        </style>
    </head>
    <body>
        <table class="dataframe" id="static_df_-2013265911"><thead><tr><th class="bottomBorder" style="text-align:left">time</th><th class="bottomBorder" style="text-align:left">current</th><th class="bottomBorder" style="text-align:left">component</th><th class="bottomBorder" style="text-align:left">actionFn</th><th class="bottomBorder" style="text-align:left">action</th><th class="bottomBorder" style="text-align:left">eventType</th><th class="bottomBorder" style="text-align:left">tickTime</th></tr></thead><tbody><tr><td  style="vertical-align:top">2022-02-23T06:00:00Z</td><td  style="vertical-align:top">null</td><td  style="vertical-align:top">main</td><td  style="vertical-align:top">null</td><td  style="vertical-align:top">running; Hold +168.00, scheduled for <span class="structural">...</span></td><td  style="vertical-align:top">RescheduledEvent</td><td  style="vertical-align:top">1645596000</td></tr><tr><td  style="vertical-align:top">2022-02-23T06:00:00Z</td><td  style="vertical-align:top">room 0</td><td  style="vertical-align:top">room 0</td><td  style="vertical-align:top">org.kalasim.analysis.EventsKt$$Lambda<span class="structural">...</span></td><td  style="vertical-align:top">canceled</td><td  style="vertical-align:top">ComponentStateChangeEvent</td><td  style="vertical-align:top">1645596000</td></tr><tr><td  style="vertical-align:top">2022-02-23T06:00:00Z</td><td  style="vertical-align:top">room 1</td><td  style="vertical-align:top">room 1</td><td  style="vertical-align:top">org.kalasim.analysis.EventsKt$$Lambda<span class="structural">...</span></td><td  style="vertical-align:top">canceled</td><td  style="vertical-align:top">ComponentStateChangeEvent</td><td  style="vertical-align:top">1645596000</td></tr><tr><td  style="vertical-align:top">2022-02-23T06:00:00Z</td><td  style="vertical-align:top">room 2</td><td  style="vertical-align:top">room 2</td><td  style="vertical-align:top">org.kalasim.analysis.EventsKt$$Lambda<span class="structural">...</span></td><td  style="vertical-align:top">canceled</td><td  style="vertical-align:top">ComponentStateChangeEvent</td><td  style="vertical-align:top">1645596000</td></tr><tr><td  style="vertical-align:top">2022-02-23T06:00:00Z</td><td  style="vertical-align:top">room 3</td><td  style="vertical-align:top">room 3</td><td  style="vertical-align:top">org.kalasim.analysis.EventsKt$$Lambda<span class="structural">...</span></td><td  style="vertical-align:top">canceled</td><td  style="vertical-align:top">ComponentStateChangeEvent</td><td  style="vertical-align:top">1645596000</td></tr><tr><td  style="vertical-align:top">2022-02-23T06:00:00Z</td><td  style="vertical-align:top">ComponentGenerator.1</td><td  style="vertical-align:top">ComponentGenerator.1</td><td  style="vertical-align:top">null</td><td  style="vertical-align:top">Hold +.08, scheduled for .08</td><td  style="vertical-align:top">RescheduledEvent</td><td  style="vertical-align:top">1645596000</td></tr><tr><td  style="vertical-align:top">2022-02-23T06:04:57.988113230Z</td><td  style="vertical-align:top">ComponentGenerator.1</td><td  style="vertical-align:top">Patient(fullName=Armando Kessler, pat<span class="structural">...</span></td><td  style="vertical-align:top">null</td><td  style="vertical-align:top">Activated, scheduled for .08</td><td  style="vertical-align:top">RescheduledEvent</td><td  style="vertical-align:top">1645596297</td></tr><tr><td  style="vertical-align:top">2022-02-23T06:04:57.988113230Z</td><td  style="vertical-align:top">ComponentGenerator.1</td><td  style="vertical-align:top">Patient(fullName=Armando Kessler, pat<span class="structural">...</span></td><td  style="vertical-align:top">org.kalasim.analysis.EventsKt$$Lambda<span class="structural">...</span></td><td  style="vertical-align:top">canceled</td><td  style="vertical-align:top">ComponentStateChangeEvent</td><td  style="vertical-align:top">1645596297</td></tr><tr><td  style="vertical-align:top">2022-02-23T06:04:57.988113230Z</td><td  style="vertical-align:top">ComponentGenerator.1</td><td  style="vertical-align:top">ComponentGenerator.1</td><td  style="vertical-align:top">null</td><td  style="vertical-align:top">Hold +.20, scheduled for .28</td><td  style="vertical-align:top">RescheduledEvent</td><td  style="vertical-align:top">1645596297</td></tr><tr><td  style="vertical-align:top">2022-02-23T06:16:54.513625724Z</td><td  style="vertical-align:top">ComponentGenerator.1</td><td  style="vertical-align:top">Patient(fullName=Laurie Keebler, pati<span class="structural">...</span></td><td  style="vertical-align:top">null</td><td  style="vertical-align:top">Activated, scheduled for .28</td><td  style="vertical-align:top">RescheduledEvent</td><td  style="vertical-align:top">1645597014</td></tr></tbody></table>
    </body>
    <script>
        document.getElementById("static_df_-2013265911").style.display = "none";
    </script>
    </html>
intDF.groupBy { expr { eventType } }
    .count()
        <iframe onload="o_resize_iframe_out_7()" style="width:100%;" class="result_container" id="iframe_out_7" frameBorder="0" srcdoc="        &lt;html&gt;
    &lt;head&gt;
        &lt;style type=&quot;text&sol;css&quot;&gt;
            :root {
--background: #fff;
--background-odd: #f5f5f5;
--background-hover: #d9edfd;
--header-text-color: #474747;
--text-color: #848484;
--text-color-dark: #000;
--text-color-medium: #737373;
--text-color-pale: #b3b3b3;
--inner-border-color: #aaa;
--bold-border-color: #000;
--link-color: #296eaa;
--link-color-pale: #296eaa;
--link-hover: #1a466c;

}

:root[theme="dark"], :root [data-jp-theme-light="false"], .dataframe_dark{ --background: #303030; --background-odd: #3c3c3c; --background-hover: #464646; --header-text-color: #dddddd; --text-color: #b3b3b3; --text-color-dark: #dddddd; --text-color-medium: #b2b2b2; --text-color-pale: #737373; --inner-border-color: #707070; --bold-border-color: #777777; --link-color: #008dc0; --link-color-pale: #97e1fb; --link-hover: #00688e; }

p.dataframe_description { color: var(--text-color-dark); }

table.dataframe { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 12px; background-color: var(--background); color: var(--text-color-dark); border: none; border-collapse: collapse; }

table.dataframe th, td { padding: 6px; border: 1px solid transparent; text-align: left; }

table.dataframe th { background-color: var(--background); color: var(--header-text-color); }

table.dataframe td { vertical-align: top; white-space: nowrap; }

table.dataframe th.bottomBorder { border-bottom-color: var(--bold-border-color); }

table.dataframe tbody > tr:nth-child(odd) { background: var(--background-odd); }

table.dataframe tbody > tr:nth-child(even) { background: var(--background); }

table.dataframe tbody > tr:hover { background: var(--background-hover); }

table.dataframe a { cursor: pointer; color: var(--link-color); text-decoration: none; }

table.dataframe tr:hover > td a { color: var(--link-color-pale); }

table.dataframe a:hover { color: var(--link-hover); text-decoration: underline; }

table.dataframe img { max-width: fit-content; }

table.dataframe th.complex { background-color: var(--background); border: 1px solid var(--background); }

table.dataframe .leftBorder { border-left-color: var(--inner-border-color); }

table.dataframe .rightBorder { border-right-color: var(--inner-border-color); }

table.dataframe .rightAlign { text-align: right; }

table.dataframe .expanderSvg { width: 8px; height: 8px; margin-right: 3px; }

table.dataframe .expander { display: flex; align-items: center; }

/ formatting /

table.dataframe .null { color: var(--text-color-pale); }

table.dataframe .structural { color: var(--text-color-medium); font-weight: bold; }

table.dataframe .dataFrameCaption { font-weight: bold; }

table.dataframe .numbers { color: var(--text-color-dark); }

table.dataframe td:hover .formatted .structural, .null { color: var(--text-color-dark); }

table.dataframe tr:hover .formatted .structural, .null { color: var(--text-color-dark); }

:root { --scroll-bg: #f5f5f5; --scroll-fg: #b3b3b3; } :root[theme="dark"], :root [data-jp-theme-light="false"]{ --scroll-bg: #3c3c3c; --scroll-fg: #97e1fb; } body { scrollbar-color: var(--scroll-fg) var(--scroll-bg); } body::-webkit-scrollbar { width: 10px; / Mostly for vertical scrollbars / height: 10px; / Mostly for horizontal scrollbars / } body::-webkit-scrollbar-thumb { background-color: var(--scroll-fg); } body::-webkit-scrollbar-track { background-color: var(--scroll-bg); } </style> </head> <body> <table class="dataframe" id="df_-2013265908"></table>

<p class="dataframe_description">DataFrame: rowsCount = 5, columnsCount = 2</p>

    &lt;&sol;body&gt;
    &lt;script&gt;
        (function () {
window.DataFrame = window.DataFrame || new (function () {
    this.addTable = function (df) {
        let cols = df.cols;
        for (let i = 0; i &lt; cols.length; i++) {
            for (let c of cols[i].children) {
                cols[c].parent = i;
            }
        }
        df.nrow = 0
        for (let i = 0; i &lt; df.cols.length; i++) {
            if (df.cols[i].values.length &gt; df.nrow) df.nrow = df.cols[i].values.length
        }
        if (df.id === df.rootId) {
            df.expandedFrames = new Set()
            df.childFrames = {}
            const table = this.getTableElement(df.id)
            table.df = df
            for (let i = 0; i &lt; df.cols.length; i++) {
                let col = df.cols[i]
                if (col.parent === undefined &amp;&amp; col.children.length &gt; 0) col.expanded = true
            }
        } else {
            const rootDf = this.getTableData(df.rootId)
            rootDf.childFrames[df.id] = df
        }
    }

    this.computeRenderData = function (df) {
        let result = []
        let pos = 0
        for (let col = 0; col &lt; df.cols.length; col++) {
            if (df.cols[col].parent === undefined)
                pos += this.computeRenderDataRec(df.cols, col, pos, 0, result, false, false)
        }
        for (let i = 0; i &lt; result.length; i++) {
            let row = result[i]
            for (let j = 0; j &lt; row.length; j++) {
                let cell = row[j]
                if (j === 0)
                    cell.leftBd = false
                if (j &lt; row.length - 1) {
                    let nextData = row[j + 1]
                    if (nextData.leftBd) cell.rightBd = true
                    else if (cell.rightBd) nextData.leftBd = true
                } else cell.rightBd = false
            }
        }
        return result
    }

    this.computeRenderDataRec = function (cols, colId, pos, depth, result, leftBorder, rightBorder) {
        if (result.length === depth) {
            const array = [];
            if (pos &gt; 0) {
                let j = 0
                for (let i = 0; j &lt; pos; i++) {
                    let c = result[depth - 1][i]
                    j += c.span
                    let copy = Object.assign({empty: true}, c)
                    array.push(copy)
                }
            }
            result.push(array)
        }
        const col = cols[colId];
        let size = 0;
        if (col.expanded) {
            let childPos = pos
            for (let i = 0; i &lt; col.children.length; i++) {
                let child = col.children[i]
                let childLeft = i === 0 &amp;&amp; (col.children.length &gt; 1 || leftBorder)
                let childRight = i === col.children.length - 1 &amp;&amp; (col.children.length &gt; 1 || rightBorder)
                let childSize = this.computeRenderDataRec(cols, child, childPos, depth + 1, result, childLeft, childRight)
                childPos += childSize
                size += childSize
            }
        } else {
            for (let i = depth + 1; i &lt; result.length; i++)
                result[i].push({id: colId, span: 1, leftBd: leftBorder, rightBd: rightBorder, empty: true})
            size = 1
        }
        let left = leftBorder
        let right = rightBorder
        if (size &gt; 1) {
            left = true
            right = true
        }
        result[depth].push({id: colId, span: size, leftBd: left, rightBd: right})
        return size
    }

    this.getTableElement = function (id) {
        return document.getElementById(&quot;df_&quot; + id)
    }

    this.getTableData = function (id) {
        return this.getTableElement(id).df
    }

    this.createExpander = function (isExpanded) {
        const svgNs = &quot;http:&sol;&sol;www.w3.org&sol;2000&sol;svg&quot;
        let svg = document.createElementNS(svgNs, &quot;svg&quot;)
        svg.classList.add(&quot;expanderSvg&quot;)
        let path = document.createElementNS(svgNs, &quot;path&quot;)
        if (isExpanded) {
            svg.setAttribute(&quot;viewBox&quot;, &quot;0 -2 8 8&quot;)
            path.setAttribute(&quot;d&quot;, &quot;M1 0 l-1 1 4 4 4 -4 -1 -1 -3 3Z&quot;)
        } else {
            svg.setAttribute(&quot;viewBox&quot;, &quot;-2 0 8 8&quot;)
            path.setAttribute(&quot;d&quot;, &quot;M1 0 l-1 1 3 3 -3 3 1 1 4 -4Z&quot;)
        }
        path.setAttribute(&quot;fill&quot;, &quot;currentColor&quot;)
        svg.appendChild(path)
        return svg
    }

    this.renderTable = function (id) {

        let table = this.getTableElement(id)

        if (table === null) return

        table.innerHTML = &quot;&quot;

        let df = table.df
        let rootDf = df.rootId === df.id ? df : this.getTableData(df.rootId)

        &sol;&sol; header
        let header = document.createElement(&quot;thead&quot;)
        table.appendChild(header)

        let renderData = this.computeRenderData(df)
        for (let j = 0; j &lt; renderData.length; j++) {
            let rowData = renderData[j]
            let tr = document.createElement(&quot;tr&quot;);
            let isLastRow = j === renderData.length - 1
            header.appendChild(tr);
            for (let i = 0; i &lt; rowData.length; i++) {
                let cell = rowData[i]
                let th = document.createElement(&quot;th&quot;);
                th.setAttribute(&quot;colspan&quot;, cell.span)
                let colId = cell.id
                let col = df.cols[colId];
                if (!cell.empty) {
                    if (col.children.length === 0) {
                        th.innerHTML = col.name
                    } else {
                        let link = document.createElement(&quot;a&quot;)
                        link.className = &quot;expander&quot;
                        let that = this
                        link.onclick = function () {
                            col.expanded = !col.expanded
                            that.renderTable(id)
                        }
                        link.appendChild(this.createExpander(col.expanded))
                        link.innerHTML += col.name
                        th.appendChild(link)
                    }
                }
                let classes = (cell.leftBd ? &quot; leftBorder&quot; : &quot;&quot;) + (cell.rightBd ? &quot; rightBorder&quot; : &quot;&quot;)
                if (col.rightAlign)
                    classes += &quot; rightAlign&quot;
                if (isLastRow)
                    classes += &quot; bottomBorder&quot;
                if (classes.length &gt; 0)
                    th.setAttribute(&quot;class&quot;, classes)
                tr.appendChild(th)
            }
        }

        &sol;&sol; body
        let body = document.createElement(&quot;tbody&quot;)
        table.appendChild(body)

        let columns = renderData.pop()
        for (let row = 0; row &lt; df.nrow; row++) {
            let tr = document.createElement(&quot;tr&quot;);
            body.appendChild(tr)
            for (let i = 0; i &lt; columns.length; i++) {
                let cell = columns[i]
                let td = document.createElement(&quot;td&quot;);
                let colId = cell.id
                let col = df.cols[colId]
                let classes = (cell.leftBd ? &quot; leftBorder&quot; : &quot;&quot;) + (cell.rightBd ? &quot; rightBorder&quot; : &quot;&quot;)
                if (col.rightAlign)
                    classes += &quot; rightAlign&quot;
                if (classes.length &gt; 0)
                    td.setAttribute(&quot;class&quot;, classes)
                tr.appendChild(td)
                let value = col.values[row]
                if (value.frameId !== undefined) {
                    let frameId = value.frameId
                    let expanded = rootDf.expandedFrames.has(frameId)
                    let link = document.createElement(&quot;a&quot;)
                    link.className = &quot;expander&quot;
                    let that = this
                    link.onclick = function () {
                        if (rootDf.expandedFrames.has(frameId))
                            rootDf.expandedFrames.delete(frameId)
                        else rootDf.expandedFrames.add(frameId)
                        that.renderTable(id)
                    }
                    link.appendChild(this.createExpander(expanded))
                    link.innerHTML += value.value
                    if (expanded) {
                        td.appendChild(link)
                        td.appendChild(document.createElement(&quot;p&quot;))
                        const childTable = document.createElement(&quot;table&quot;)
                        childTable.className = &quot;dataframe&quot;
                        childTable.id = &quot;df_&quot; + frameId
                        let childDf = rootDf.childFrames[frameId]
                        childTable.df = childDf
                        td.appendChild(childTable)
                        this.renderTable(frameId)
                        if (childDf.nrow !== childDf.totalRows) {
                            const footer = document.createElement(&quot;p&quot;)
                            footer.innerText = `... showing only top ${childDf.nrow} of ${childDf.totalRows} rows`
                            td.appendChild(footer)
                        }
                    } else {
                        td.appendChild(link)
                    }
                } else if (value.style !== undefined) {
                    td.innerHTML = value.value
                    td.setAttribute(&quot;style&quot;, value.style)
                } else td.innerHTML = value
                this.nodeScriptReplace(td)
            }
        }
    }

    this.nodeScriptReplace = function (node) {
        if (this.nodeScriptIs(node) === true) {
            node.parentNode.replaceChild(this.nodeScriptClone(node), node);
        } else {
            let i = -1, children = node.childNodes;
            while (++i &lt; children.length) {
                this.nodeScriptReplace(children[i]);
            }
        }

        return node;
    }

    this.nodeScriptClone = function (node) {
        let script = document.createElement(&quot;script&quot;);
        script.text = node.innerHTML;

        let i = -1, attrs = node.attributes, attr;
        while (++i &lt; attrs.length) {
            script.setAttribute((attr = attrs[i]).name, attr.value);
        }
        return script;
    }

    this.nodeScriptIs = function (node) {
        return node.tagName === 'SCRIPT';
    }
})()

window.call_DataFrame = function (f) {
    return f();
};

let funQueue = window[&quot;kotlinQueues&quot;] &amp;&amp; window[&quot;kotlinQueues&quot;][&quot;DataFrame&quot;];
if (funQueue) {
    funQueue.forEach(function (f) {
        f();
    });
    funQueue = [];
}

})()

/<!--/ call_DataFrame(function() { DataFrame.addTable({ cols: [{ name: "<span title=\"untitled: String\">untitled</span>", children: [], rightAlign: false, values: ["RescheduledEvent","ComponentStateChangeEvent","StateChangedEvent","ResourceEvent","InteractionEvent"] }, { name: "<span title=\"count: Int\">count</span>", children: [], rightAlign: true, values: ["<span class=\"formatted\" title=\"\"><span class=\"numbers\">2547</span></span>","<span class=\"formatted\" title=\"\"><span class=\"numbers\">860</span></span>","<span class=\"formatted\" title=\"\"><span class=\"numbers\">960</span></span>","<span class=\"formatted\" title=\"\"><span class=\"numbers\">1708</span></span>","<span class=\"formatted\" title=\"\"><span class=\"numbers\">328</span></span>"] }, ], id: -2013265908, rootId: -2013265908, totalRows: 5 } ) }); /-->/

call_DataFrame(function() { DataFrame.renderTable(-2013265908) });

    &lt;&sol;script&gt;
    &lt;&sol;html&gt;"></iframe>
        <script>
            function o_resize_iframe_out_7() {
                let elem = document.getElementById("iframe_out_7");
                resize_iframe_out_7(elem);
                setInterval(resize_iframe_out_7, 5000, elem);
            }
            function resize_iframe_out_7(el) {
                let h = el.contentWindow.document.body.scrollHeight;
                el.height = h === 0 ? 0 : h + 41;
            }
        </script>        <html>
    <head>
        <style type="text/css">
            :root {
--background: #fff;
--background-odd: #f5f5f5;
--background-hover: #d9edfd;
--header-text-color: #474747;
--text-color: #848484;
--text-color-dark: #000;
--text-color-medium: #737373;
--text-color-pale: #b3b3b3;
--inner-border-color: #aaa;
--bold-border-color: #000;
--link-color: #296eaa;
--link-color-pale: #296eaa;
--link-hover: #1a466c;

}

:root[theme="dark"], :root [data-jp-theme-light="false"], .dataframe_dark{ --background: #303030; --background-odd: #3c3c3c; --background-hover: #464646; --header-text-color: #dddddd; --text-color: #b3b3b3; --text-color-dark: #dddddd; --text-color-medium: #b2b2b2; --text-color-pale: #737373; --inner-border-color: #707070; --bold-border-color: #777777; --link-color: #008dc0; --link-color-pale: #97e1fb; --link-hover: #00688e; }

p.dataframe_description { color: var(--text-color-dark); }

table.dataframe { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 12px; background-color: var(--background); color: var(--text-color-dark); border: none; border-collapse: collapse; }

table.dataframe th, td { padding: 6px; border: 1px solid transparent; text-align: left; }

table.dataframe th { background-color: var(--background); color: var(--header-text-color); }

table.dataframe td { vertical-align: top; white-space: nowrap; }

table.dataframe th.bottomBorder { border-bottom-color: var(--bold-border-color); }

table.dataframe tbody > tr:nth-child(odd) { background: var(--background-odd); }

table.dataframe tbody > tr:nth-child(even) { background: var(--background); }

table.dataframe tbody > tr:hover { background: var(--background-hover); }

table.dataframe a { cursor: pointer; color: var(--link-color); text-decoration: none; }

table.dataframe tr:hover > td a { color: var(--link-color-pale); }

table.dataframe a:hover { color: var(--link-hover); text-decoration: underline; }

table.dataframe img { max-width: fit-content; }

table.dataframe th.complex { background-color: var(--background); border: 1px solid var(--background); }

table.dataframe .leftBorder { border-left-color: var(--inner-border-color); }

table.dataframe .rightBorder { border-right-color: var(--inner-border-color); }

table.dataframe .rightAlign { text-align: right; }

table.dataframe .expanderSvg { width: 8px; height: 8px; margin-right: 3px; }

table.dataframe .expander { display: flex; align-items: center; }

/ formatting /

table.dataframe .null { color: var(--text-color-pale); }

table.dataframe .structural { color: var(--text-color-medium); font-weight: bold; }

table.dataframe .dataFrameCaption { font-weight: bold; }

table.dataframe .numbers { color: var(--text-color-dark); }

table.dataframe td:hover .formatted .structural, .null { color: var(--text-color-dark); }

table.dataframe tr:hover .formatted .structural, .null { color: var(--text-color-dark); }

        </style>
    </head>
    <body>
        <table class="dataframe" id="static_df_-2013265907"><thead><tr><th class="bottomBorder" style="text-align:left">untitled</th><th class="bottomBorder" style="text-align:left">count</th></tr></thead><tbody><tr><td  style="vertical-align:top">RescheduledEvent</td><td  style="vertical-align:top">2547</td></tr><tr><td  style="vertical-align:top">ComponentStateChangeEvent</td><td  style="vertical-align:top">860</td></tr><tr><td  style="vertical-align:top">StateChangedEvent</td><td  style="vertical-align:top">960</td></tr><tr><td  style="vertical-align:top">ResourceEvent</td><td  style="vertical-align:top">1708</td></tr><tr><td  style="vertical-align:top">InteractionEvent</td><td  style="vertical-align:top">328</td></tr></tbody></table>
    </body>
    <script>
        document.getElementById("static_df_-2013265907").style.display = "none";
    </script>
    </html>

The event distribution shows that the Emergency Room model is primarily time-driven, with RescheduledEvents (~ 40%) dominating due to frequent hold() operations for arrivals, severity escalation, setup, and surgery durations. ResourceEvents (~ 27%) indicate significant doctor allocation and release activity, reflecting contention and queue dynamics around limited medical staff. StateChangedEvents and ComponentStateChangeEvents (~ 28% combined) stem from patient severity progression and status updates (e.g., Waiting → InSurgery → Released/Deceased). InteractionEvents (~ 5%) are comparatively low, suggesting relatively simple direct component interactions. Overall, the model’s dynamics are mainly governed by time scheduling and resource competition rather than complex inter-component messaging.

Conclusion & Summary

This study demonstrates how a complex and highly dynamic environment such as an Emergency Room can be captured, analyzed, and improved using discrete-event simulation with Kalasim. By explicitly modeling patient deterioration, resource constraints, setup dependencies, and policy-driven decision making, we gain transparency into bottlenecks and systemic trade-offs that are difficult to detect in real operations. The simulation highlights how performance is shaped not only by staffing levels, but by intelligent coordination of rooms, doctors, and triage policies. Such models provide a safe environment to experiment with alternative strategies before applying them in practice. Ultimately, data-driven process modeling enables more informed decisions, more efficient resource utilization, and—most importantly—the potential to save more lives through smarter system design.

Disclaimer: The author is not a medical doctor, so please excuse possible inprecsion in wording and lack of ER process understanding. Feel welcome to suggest corrections or improvements.