CARA Simulation: Overview of the Model

The formal model underlying the CARA simulation applet assumes a fixed decomposition of the CARA system into a number of communicating modules, which may be regarded as state machines. These modules fall into one of two classes: modules that represent entities (such as the patient or the infusion pump) external to the CARA system, and modules internal to the CARA system.

The modules representing external entities are as follows:

The modules internal to the CARA system are as follows:

Each of the above modules is represented by a Java class of the same name. Besides these classes, there are several auxiliary classes whose purpose is to define data types relevant to the model. These are:

Below we give a brief overview of each of the CARA modules listed above.

External Modules

ArterialSource

The arterial line sensor is a "beat-to-beat" blood pressure sensor that provides a direct measurement of blood pressure via a transducer inserted into an artery.

In our model, a blood pressure reading is obtained from the arterial line when the ArterialSource module invokes the ArterialBP.setBP() method. This currently occurs every 15 seconds, though it might be more realistic if it occurred in synchrony with a simulated patient heartbeat.

The run() method of the ArterialSource module contains a loop that obtains the current blood pressure from the Patient module every 15 seconds. The pressure supplied by the ArterialSource module consists of this "true" blood pressure, plus some Gaussian noise whose standard deviation is specified as a percentage of the true pressure.

Also incorporated into the ArterialSource module is a simple two-state Markov failure model. At any time, the module is either in the "failed" or "not failed" state. When the module is in the "not failed" state, it supplies blood pressure readings periodically as described above. When the module is in the "failed" state, it does not supply any readings. Every 15 seconds, there is a possible transition between states. Transitions from "not failed" to "failed" occur with a probability chosen to produce a specified mean time to failure (MTTF). Transitions from "failed" to "not failed" occur with a probability chosen to produce a specified mean time to repair (MTTR).

CuffSource

The cuff sensor uses a traditional sphygnomanometric method that involves inflating a cuff to a pressure somewhat over the systolic blood pressure of the patient and observing the waveforms associated with heartbeats as the cuff pressure is slowly reduced. In contrast to the "beat-to-beat" methods, the cuff sensor produces a blood pressure reading only when activated, and substantial time (on the order of one minute) may elapse between the time the reading is requested and the time it is obtained.

In our model, when a cuff pressure is required, the CuffBP module invokes the CuffSource.pollBP() method. In contrast to the ArterialSource and PulseWaveSource modules, the CuffSource module does not immediately return a pressure. Rather, there is a random delay which we model using a geometric distribution having a specified mean (the default is 40 seconds). When the delay time expires, the current blood pressure is returned via a callback to the CuffBP.setBP() method.

The run() method of the CuffSource module contains a loop that obtains the current blood pressure from the Patient module every 15 seconds. The pressure returned in response to a pollBP() request consists of this "true" blood pressure, plus some Gaussian noise whose standard deviation is specified as a percentage of the true pressure.

Also incorporated into the CuffSource module is a simple two-state Markov failure model. At any time, the module is either in the "failed" or "not failed" state. When the module is in the "not failed" state, it responds to a pollBP() request by returning a blood pressure as described above. When the module is in the "failed" state, it does not return any blood pressure. Once per second, there is a possible transition between states. Transitions from "not failed" to "failed" occur with a probability chosen to produce a specified mean time to failure (MTTF). Transitions from "failed" to "not failed" occur with a probability chosen to produce a specified mean time to repair (MTTR).

Patient

The Patient module models the response of the human patient to fluid infusion. We use a very simple-minded model in which the blood pressure of the patient is directly proportional to the current volume of blood in the patient's circulatory system. The infusion of fluid is modeled by the Pump module periodically invoking the Patient.addInfusedVolume() to add a small amount of fluid volume. The run() method of the Patient module contains a loop that runs once per second to remove a small amount of fluid volume, to model bleeding and excretion.

The various blood pressure sensors periodically obtain the "true" current blood pressure of the patient by calling the Patient.getBP() method. (NOTE: This has to be changed, since it violates our restriction on "no returned values". An easy fix would be to have the run() loop of the Patient module "push" the current blood pressure to each of the sensors periodically; for example on each simulated heartbeat.)

PulseWaveSource

The pulse wave velocity sensor is a "beat-to-beat" blood pressure sensor that infers blood pressure from propagation characteristics of the pressure wave resulting from each heartbeat.

In our model, a blood pressure reading is obtained from the pulse wave velocity sensor when the PulseWaveBP module invokes the PulseWaveBP.setBP() method. This currently occurs every 15 seconds, though it might be more realistic if it occurred in synchrony with a simulated patient heartbeat.

The run() method of the PulseWaveSource contains a loop that obtains the current blood pressure from the Patient module every 15 seconds. The pressure supplied by the PulseWaveSourcemodule consists of this "true" blood pressure, plus some Gaussian noise whose standard deviation is specified as a percentage of the true pressure.

Also incorporated into the PulseWaveSource module is a simple two-state Markov failure model. At any time, the module is either in the "failed" or "not failed" state. When the module is in the "not failed" state, it supplies blood pressure readings periodically as described above. When the module is in the "failed" state, it does not supply any readings. Every 15 seconds, there is a possible transition between states. Transitions from "not failed" to "failed" occur with a probability chosen to produce a specified mean time to failure (MTTF). Transitions from "failed" to "not failed" occur with a probability chosen to produce a specified mean time to repair (MTTR).

Pump

The Pump module models the M100 infusion pump. The pump provides logic-level outputs CONT (continuity), OCC (occlusion), and AirOK (air in line). When these lines change state, the pump generates interrupts, which are simulated by calls to the PumpMonitor.setCont(), PumpMonitor.setOcc(), and PumpMonitor.setAirOK() methods. There are also analog outputs EMF (back EMF) and IMP (impedance). These lines have to be polled by calls to the pollEMF() and pollIMP() methods, which usually result in immediate callbacks to the PumpMonitor.setEMF() and PumpMonitor.setIMP() methods, respectively. There is a certain probability that the corresponding callback will not occur for a particular polling request; this models a failure of the A/D converter that samples the EMF and IMP lines.

The pump is controlled by an analog control voltage that determines the infusion rate. The setting of this control voltage is simulated by calling the setControlVoltage() method. In addition, there is a logic-level input to the pump that determines whether the pump will pay attention to the analog control voltage. Changes to the state of this line are simulated by calling the setAnalogControl() method.

The run() method of the pump contains a loop that runs once per second and sends a certain amount of infused fluid volume to the Patient module. In addition, the value of the EMF output is updated based on the current infusion rate. This is not currently modeled realistically: in the real M100 pump, the EMF value varies constantly as the rocker arm contacts the pump tubing. This constantly varying signal as to be processed in order to estimate the infusion rate. We simply treat the EMF line as giving a direct indication of the current instantaneous infusion rate.

Also incorporated into the Pump module are simple two-state Markov failure models associated with each of the logic-level status lines CONT, OCC, and AirOK. At any time, each of these lines is either in the "failed" or "not failed" state. In the "not failed" state, the line has the logic value "true", indicating normal operational status. In the "failed" state, the line has the logic value "false", which indicates an error condition. Once per second, there is a possible transition between states. Transitions from "not failed" to "failed" occur with a probability chosen to produce a specified mean time to failure (MTTF). Transitions from "failed" to "not failed" occur with a probability chosen to produce a specified mean time to repair (MTTR).

Internal Modules

AlarmControl

The AlarmControl module handles the raising and clearing of the various alarms. Each alarm is designated either a "level 1" (L1) or "level 2" (L2) alarm. As I understand it, L1 alarms are such that they will reset themselves without user intervention if the alarm condition disappears. On the other hand, L2 alarms require manual resetting by the user after the alarm condition is corrected. Resetting an alarm is achieved by the user pressing an "Reset Alarm" button that becomes enabled when there is an active alarm. A reset alarm will be triggered again immediately or almost immediately if the alarm condition has not been corrected. AlarmControl maintains a notion of the "current" alarm, and displays the name of the current alarm in an alarm message field when any alarm is active. Pressing the alarm reset button resets only the current alarm; if there are other active alarms, another one becomes the current alarm.

When there are active alarms, a "Silence Alarm" button is also available. Pressing this button silences the audible alarm signal for all alarms for a period of time that is different for each alarm. When the silence time for an alarm has expired, the audible alarm signal again becomes active for that alarm. Pressing the silence button again will again silence that alarm, but will not affect the remaining silence times associated with other already-silenced alarms.

Separate methods are provided by AlarmControl for the activating and deactivating of each different alarm. Most of these methods take a single boolean parameter that is true if the alarm is to be activated, and false if the alarm is to be deactivated. There is also a cancelAllAlarms() method which is used by Mode to deactivate any active alarms when leaving auto-control mode.

ArterialBP:

The ArterialBP module handles the obtaining of blood pressure readings from ArterialSource, checking them for validity, and delivering them on demand to Corroborate. Each time ArterialBP receives a blood pressure reading from ArterialSource, it checks this reading for validity and saves it. When Corroborate requires a blood pressure reading, it calls ArterialBP.pollBP(), which results in an immediate callback to Corroborate.setArterialBP().

The ArterialBP.intrT15() method is called by Timer every 15 seconds, to enable ArterialBP to detect the lost of BP data from ArterialSource. If 15 seconds go by without a new reading, then the current reading is flagged as "stale" and invalidated.

Corroborate:

The Corroborate module is responsible for combining information from the various blood pressure sources to determine the control BP supplied to PumpControl. This function involves the manipulation of the cuff blood pressure sensor to obtain readings at appropriate times to corroborate the readings provided by the sources used for control, to monitor for lost blood pressure sources and issue alarms, and to cause auto-control mode to be terminated in case no suitable control source is available. Corroborate is provided with blood pressure data via calls to the setArterialBP(), setPulseWaveBP(), and setCuffBP() methods. Calls to these occur in response to calls made by Corroborate to the pollBP() methods supplied by the ArterialBP, PulseWaveBP, and CuffBP modules.

Corroborate supplies the current control BP to PumpControl every 15 seconds. The control BP is only updated upon receipt of new valid BP data from the current control source, or upon the occurrence of a timeout indicating that the current source has been lost. If no new data arrives from the current BP source, then the most recent control BP is what is supplied to PumpControl.

If no blood pressure source other than the cuff is available, Corroborate exhibits some special behavior. It calls CuffBP.setBPInterval() to initiate periodic automatic readings of the cuff. The period of cuff readings ranges from 1 minute apart to 10 minutes apart, depending on the current blood pressure. In addition, a failure of the cuff BP source to provide data in a timely fashion will cause more serious alarms when the cuff is the only source available than it would otherwise.

The core function of the Corroborate module is "corroboration", which involves comparing readings obtained from the so-called "beat-to-beat" BP sensors, the arterial line sensor and the pulse wave velocity sensor, to the readings obtained from the cuff sensor. This corroboration function is modeled as a state machine, which becomes active every 30 minutes during auto-control or whenever a change in the available BP sources makes re-corroboration necessary. The transitions of this state machine are driven by the responses from the cuff BP source and from caregiver interaction via "override dialogs".

A list of the possible corroboration states and their meanings is as follows:

disabled
Corroboration is disabled due to CARA not being in auto-control mode.
idle
No corroboration is currently in progress.
start
Waiting for the first cuff reading after entering auto-control mode.
cuff0
Waiting for the first cuff reading in a normal corroboration cycle.
cuff1
Waiting for the first attempt to re-read the cuff after the initial reading has failed.
cuff2
Waiting for the second attempt to re-read the cuff after the initial reading has failed.
dialog0
Waiting for a response to an override dialog during the initial corroboration after entering auto-control mode.
dialog1
Waiting for a response to an override dialog during a periodic corroboration cycle.

At most one corroboration cycle can be in progress at any given time. A new corroboration cycle becomes active in the following circumstances:

Corroborate also interprets the responses from override dialogs issued for the user. An override dialog is issued when a beat-to-beat source being used for control does not corroborate with, or match, the current cuff reading. The user is asked to respond "YES" or "NO" as to whether the current control source should be used anyway. In case of a "YES" response, Corroborate checks to make sure that the selected source is still valid (there are scenarios under which the source can become invalid while the dialog is pending), then remembers that the current source was selected by override. In case of a "NO" response, Corroborate either terminates auto-control or tries to corroborate a lower-priority source, depending on the circumstances.

CuffBP:

The CuffBP module handles the obtaining of blood pressure readings from CuffSource and delivering them on demand to Corroborate Corroborate requests a reading by calling the pollBP() method provided by CuffBP, which responds by calling the setCuffBP)() method provided by Corroborate. CuffBP in turn requests a reading from CuffSource by calling its pollBP() method, and CuffSource responds by calling the setBP() method of CuffBP. The CuffBP module checks each reading it obtains for validity before passing it along to Corroborate.

The blood pressure cuff differs from the "beat-to-beat" sensors in that it may take much longer (a minute or more) to deliver a reading in response to a request, and in some cases it might not deliver a reading at all. To bound the time that it might take to deliver a response when a cuff reading is requested by Corroborate, the CuffBP module sets a timer when a reading is first requested. If no response is forthcoming by the time the timer expires, an invalid blood pressure is given as a response to Corroborate. In the current version of the model, the cuff timeout is set to 55 seconds.

CuffBP also handles the taking of periodic readings that are required when the cuff is the only BP source available for controlling the infusion pump. Corroborate sets the time interval between periodic readings by calling the setBPInterval() method of CuffBP.

Dialog:

The Dialog module handles user input via buttons and dialog boxes. The following buttons are independent of any dialog boxes:

There are two kinds of dialogs:

The override dialog contains a "YES" and a "NO" button. The change set point dialog contains "OK", "Cancel", "Default", "Up", and "Down" buttons, plus a text field indicating the selected set point.

There are somewhat complicated requirements on which things hide or disable which other things on the display. Each time the state of anything changes, Dialog recomputes the display appropriately.

Display:

The Display module handles the display of various kinds of indicators and data. It provides various methods to the other modules by which the state of the items to be displayed can be changed when necessary.

Logging:

The Logging module handles the writing of messages to the resuscitation log. A number of methods are provided for making log entries in situations. These methods are invoked by other modules when necessary.

Mode:

The Mode module keeps track of the current operating mode and manages transitions between modes. The possible modes are "waiting", which is the mode just after the CARA system has been initialized but before the pump has been detected for the first time, "manual", in which the pump ignores the analog control voltage and pumps at its hardware setting of 0.2 lph, and "auto-control", in which the pump is under active control of the CARA software.

A transition from "waiting" to "manual" mode occurs the first time the pump is detected. Mode is informed about the status of the pump via calls made by PumpMonitor to the setPumpStatus() method. A transition from "manual" to "auto-control" mode occurs when the caregiver presses the "start auto-control" button, resulting in a call by the Dialog module to the startAutoControl() method. One way a transition from "auto-control" to manual mode can occur is when the caregiver issues a YES response to a "terminate auto-control" dialog, which results in a call by the Dialog module to the stopAutoControl() method. Another way auto-control can be terminated is when a call by PumpMonitor to the setPumpStatus() method indicates that the pump is no longer "OK". A third way auto-control can be terminated is when a call by Corroborate to the setBPStatus() method indicates that there is no longer any valid control BP. When auto-control initiates or terminates, Mode calls methods of various other modules, including Corroborate to orchestrate the change.

PulseWaveBP:

The PulseWaveBP module handles the obtaining of blood pressure readings from PulseWaveSource, checking them for validity, and delivering them on demand to Corroborate Each time PulseWaveBP receives a blood pressure reading from PulseWaveSource, it checks this reading for validity and saves it. When Corroborate requires a blood pressure reading, it calls PulseWaveBP.pollBP(), which results in an immediate callback to Corroborate.setPulseWaveBP)(). method provided by Corroborate.

The PulseWaveBP.intrT15() method is called by Timer every 15 seconds, to enable PulseWaveBP to detect the lost of BP data from PulseWaveSource. If 15 seconds go by without a new reading, then the current reading is flagged as "stale" and invalidated.

PumpControl:

The PumpControl module uses blood pressure and set point information to calculate the control voltage to be supplied to the pump. It also monitors for falling blood pressure. The PumpControl module is informed by Mode via the startAutoControl() method as to when auto-control begins. The set point is initialized to the default of 70mmHg, and the pump control voltage is set so as to yield the initial flow rate of 4 lph. Information about the controlling blood pressure updated when the Corroborate module calls the setControlBP() method supplied by PumpControl. Each time this occurs, PumpControl calculates a new control voltage and supplies it to Pump by calling the Pump.setControlVoltage() method. Periodically (every 15 seconds) PumpControl checks whether the set point has been reached and whether it appears that the blood pressure is falling. If the caregiver modifies the set point via the change set point dialog, the Dialog module supplies the new set point to PumpControl via the setSetPoint() method. When auto-control terminates, Mode uses the stopAutoControl() method provided by Pump to convey this information.

We currently use a very simplistic control algorithm that applies the maximum control voltage to the pump when the control blood pressure is below 60mmHg, applies the "keep vein open" (KVO) control voltage to the pump when the control blood pressure is at or above the set point, and which varies the control voltage linearly with the control blood pressure when the latter is between 60mmHg and the set point.

PumpMonitor:

The PumpMonitor module tracks the status information supplied by the pump. It also estimates the infusion rate, which it integrates over time to determine the total infused fluid volume. The PumpControl module uses the setTimeAtSetPoint() to keep the PumpMonitor informed about how long it has been since the desired blood pressure set point was attained. After the set point has been held for ten minutes, the PumpMonitor calculates a baseline "steady-state" infusion rate and monitors for increases in the infusion rate that signficantly exceed this level.

The setAirOK(), setCONT(), and setOCC() methods provided by PumpMonitor are called by the Pump module to simulate interrupts that occur when the corresponding logic outputs of the pump change their state. The setEMF() and setIMP() methods are called by the Pump module in response to calls made periodically (once every five seconds) by PumpMonitor to the Pump.pollEMF() and Pump.pollIMP() methods. The information obtained in this way is combined to infer some derived status information, such as whether the pump is plugged in (we currently equate this with the CONT line being at a logic "true" level) whether the pump is "OK", and to estimate the current infusion rate. Each time there is a possible change in pump status, the Mode module is informed by a call to the Mode.setPumpStatus() method.

Timer:

The Timer module provides timing services required by the other modules. We decided not to have each of the individual modules perform their own timing, because the overal model was simpler and easier to understand if we localize all timing into a single module.

There are two types of timing services provided by Timer. One kind of service is settable "countdown" timers which other modules use to implement timeouts. These timers are set by supplying Timer with the amount of time to wait. When this amount of time expires, Timer performs a callback to the appropriate method of the invoking module. The other type of timing service provided by Timer is periodic interrupts at various frequencies. Currently, the other modules require interrupts at one-second, five-second, fifteen-second, and one-minute intervals. Not all modules require all frequencies, and some modules do not require any.

Timer itself contains a run() method with a loop that executes once per second. Counters are used to generate all the other timings from this basic one-second period.

BACK to Cara Infusion Pump Project.


Last modified: Wed Jan 9 11:42:28 EST 2002