-
Notifications
You must be signed in to change notification settings - Fork 663
Generic Module Framework
Synthea contains a framework for defining modules using JSON. A JSON module configuration describes a progression of states and the transitions between them. On each Synthea generation "cycle", the generic framework processes states one at a time to trigger conditions, encounter, medications, and other clinical events.
The generic module framework currently supports the following states:
- Initial
- Terminal
- Guard
- Delay
- Encounter
- ConditionOnset
- MedicationOrder
- Procedure
- Death
The following states are also planned for future implementation:
- Lab
- ConditionEnd
- MedicationEnd
The Initial
state type is the first state that is processed in a generic module. It does not provide any specific function except to indicate the starting point, so it has no properties except its type
. The Initial state is the only state that requires a specific name: "Initial". In addition, it is the only state for which there can only be one in the whole module.
Supported Properties
- type: must be "Initial" (required)
Example
Please note that, for simplicity, state examples in this document will not include any transition properties. See the Transitions section for information about transitions.
{
"type": "Initial"
}
The Terminal
state type indicates the end of the module progression. Once a Terminal state is reached, no further progress will be made. As such, Terminal states cannot have any transition properties. If desired, there may be multiple Terminal states with different names to indicate different ending points -- but this has no actual effect on the records that are produced.
Supported Properties
- type: must be "Terminal" (required)
Example
{
"type": "Terminal"
}
The Guard
state type indicates a point in the module through which a patient can only pass if they meet certain logical conditions. For example, a Guard may block a workflow until the patient reaches a certain age, after which the Guard allows the module to continue to progress. Depending on the condition, a patient may be blocked by a Guard until they die -- in which case they never reach a Terminal
state. The Guard state's allow
property provides the logical condition which must be met to allow the module to continue to the next state. Please see the Logic section for more information about creating logical conditions.
Guard states are similar to conditional transitions in some ways, but also have an important difference. A conditional transition tests conditions once and uses the result to immediately choose the next state. A Guard state will test the same condition on every time cycle until the condition passes, at which point it progresses to the next state.
Supported Properties
- type: must be "Guard" (required)
- allow: the condition under which the Guard allows the module to progress to the next state; otherwise the module remains at the Guard state (required)
Example
The following is an example of a Guard state that only allows the module to continue if the patient is male and at least 40 years old.
{
"type": "Guard",
"allow": {
"condition_type": "And",
"conditions": [
{
"condition_type": "Gender",
"gender": "M"
},
{
"condition_type": "Age",
"operator": ">=",
"quantity": 40,
"unit": "years"
}
]
}
}
The Delay
state type introduces a pre-configured temporal delay in the module's timeline. As a simple example, a Delay state may indicate a one-month gap in time between an initial encounter and a followup encounter. The module will not pass through the Delay state until the proper amount of time has passed. The Delay state may define an exact time to delay (e.g., 4 days) or a range of time to delay (e.g., 5 - 7 days).
Implementation Detail
Synthea generation occurs in time cycles; currently 7-day cycles. This means that if a module is processed on a given date, the next time it is processed will be exactly 7 days later. If a delay expiration falls between cycles (e.g., day 3 of a 7-day cycle), then the first cycle after the delay expiration will effectively rewind the clock to the delay expiration time and process states using that time. Once it reaches a state that it can't pass through, it will process it once more using the original (7-day cycle) time.
Supported Properties
- type: must be "Delay" (required)
-
exact: an exact amount of time to delay (required if
range
is not set)- quantity: the number of units to delay (e.g., 4) (required)
-
unit: the unit of time pertaining to the quantity (e.g., "days"). Valid unit values are:
years
,months
,weeks
,days
,hours
,minutes
, andseconds
. (required)
-
range: a range indicating the allowable amounts of delay. The actual delay time will be chosen randomly from the range. (required if
exact
is not set)- low: the lowest number (inclusive) of units to delay (e.g., 5) (required)
- high: the highest number (inclusive) of units to delay (e.g., 7) (required)
-
unit: the unit of time pertaining to the range (e.g., "days"). Valid unit values are:
years
,months
,weeks
,days
,hours
,minutes
, andseconds
. (required)
Examples
The following is an example of a Delay state that delays exactly 4 days.
{
"type": "Delay",
"exact": {
"quantity": 4,
"unit": "days"
}
}
The following is an example of a Delay state that delays at least 5 days and at most 7 days.
{
"type": "Delay",
"range": {
"low": 5,
"high": 7,
"unit": "days"
}
}
The Encounter
state type indicates a point in the module where an encounter should take place. Encounters are important in Synthea because they are generally the mechanism through which the actual patient record is updated (a disease is diagnosed, a medication is prescribed, etc). The generic module framework supports integration with scheduled wellness encounters from Synthea's Encounters module, as well as creation of new stand-alone encounters.
Scheduled Wellness Encounters vs. Standalone Encounters
An Encounter state with the wellness
property set to true
will block until the next scheduled wellness encounter occurs. Scheduled wellness encounters are managed by the Encounters module in Synthea and, depending on the patient's age, typically occur every 1 - 3 years. When a scheduled wellness encounter finally does occur, Synthea will search the generic modules for currently blocked Encounter states and will immediately process them (and their subsequent states). An example where this might be used is for a condition that onsets between encounters, but isn't found and diagnosed until the next regularly scheduled wellness encounter.
An Encounter state without the wellness
property set will be processed and recorded in the patient record immediately. Since this creates an encounter, the encounter class and at least one code must be specified in the state configuration. This is how generic modules can introduce encounters that are not already scheduled by other modules.
Encounters and Related Events
Encounters are typically the mechanism through which a patient's record will be updated. This makes sense since most recorded events (diagnoses, prescriptions, and procedures) should happen in the context of an encounter. When an Encounter state is successfully processed, it will look through the previously processed states for un-recorded ConditionOnset instances that indicate it (by name) as the target_encounter
. If it finds any, it will record the corresponding diagnosis in the patient's record at the time of the encounter.
As soon as the Encounter state is processed, Synthea will continue to progress through the module. If any subsequent states identify the previous Encounter as their target_encounter
and they occur at the same time as the target encounter, then they will be added to the patient record. This is the preferred mechanism for simulating events that happen at the encounter (e.g., MedicationOrders, Procedures, and Encounter-caused ConditionOnsets).
Future Implementation Considerations
Future implementations should also consider a more robust mechanism for defining the length of an encounter and the activities that happen during it. Currently, encounter activities must start at the same exact time as the encounter start in order to be recorded. This, however, is unrealistic for multi-day inpatient encounters.
Supported Properties
- type: must be "Encounter" (required)
-
wellness: if
true
, indicates that this state should block until a regularly scheduled wellness encounter occurs (required ifclass
andcodes
are not set) -
class: indicates the class of the encounter, as defined in the EncounterClass value set (required if
wellness
is not set) -
codes[]: a list of codes indicating the encounter type (at least one required if
wellness
is not set)-
system: the code system. Currently, only
SNOMED-CT
is allowed. (required) - code: the code (required)
- display: the human-readable code description (required)
-
system: the code system. Currently, only
Examples
The following is an example of an Encounter state that blocks until a regularly scheduled encounter.
{
"type": "Encounter",
"wellness": true
}
The following is an example of an Encounter state indicating an ED visit.
{
"type": "Encounter",
"class": "emergency",
"codes": [{
"system": "SNOMED-CT",
"code": "50849002",
"display": "Emergency Room Admission"
}]
}
The ConditionOnset
state type indicates a point in the module where the patient acquires a condition. This is not necessarily the same as when the condition is diagnosed and recorded in the patient's record. In fact, it is possible for a condition to onset but never be discovered.
If the ConditionOnset state's target_encounter
is set to the name of a future encounter, then the condition will be diagnosed when that future encounter occurs. If the target_encounter
is set to the name of a previous encounter, then the condition will only be diagnosed if the ConditionOnset start time is the same as the encounter's start time. See the Encounter section above for more details.
Future Implementation Considerations
Although the generic module framework supports a distinction between a condition's onset date and diagnosis date, currently only the diagnosis date is recorded. In the future, the Synthea::Output::Record::condition
method should be updated to support an onset date.
Currently, the generic module framework does not provide a way to resolve (or abate) conditions. There are two ways this could potentially be implemented in the future: (1) by introducing a ConditionEnd
state (recommended), or (2) by introducing a property in ConditionOnset
to indicate its intended duration.
Supported Properties
- type: must be "ConditionOnset" (required)
- target_encounter: the name of the Encounter state at which this condition should be diagnosed and recorded (optional)
-
codes[]: a list of codes indicating the condition (at least one required)
-
system: the code system. Currently, only
SNOMED-CT
is allowed. (required) - code: the code (required)
- display: the human-readable code description (required)
-
system: the code system. Currently, only
Example
The following is an example of a ConditionOnset that should be diagnosed at the "ED_Visit" Encounter.
{
"type": "ConditionOnset",
"target_encounter": "ED_Visit",
"codes": [{
"system": "SNOMED-CT",
"code": "47693006",
"display": "Rupture of appendix"
}]
}
The MedicationOrder
state type indicates a point in the module where a medication should be prescribed. The MedicationOrder state must come after the target_encounter
Encounter state in the module, but must have the same start time as that Encounter; otherwise it will not be recorded in the patient's record. See the Encounter section above for more details.
The MedicationOrder
also supports identifying a previous ConditionOnset
as the reason
for the prescription.
Future Implementation Considerations
Currently, the generic module framework does not provide a way to end medications. There are two ways this could potentially be implemented in the future: (1) by introducing a MedicationEnd
state, or (2) by introducing a property in MedicationOrder
to indicate its intended duration.
Supported Properties
- type: must be "MedicationOrder" (required)
- target_encounter: the name of the Encounter state at which this medication should be prescribed. This Encounter must come before MedicationOrder in the module, but must have the same start time as the MedicationOrder. (required)
-
codes[]: a list of codes indicating the medication (at least one required)
-
system: the code system. Currently, only
RxNorm
is allowed. (required) - code: the code (required)
- display: the human-readable code description (required)
-
system: the code system. Currently, only
- reason: the name of the ConditionOnset state which represents the reason for which the medication is prescribed. This ConditionOnset must come before the MedicationOrder in the module. (optional)
Example
The following is an example of a MedicationOrder that should be prescribed at the "Annual_Checkup" Encounter and cite the "Diabetes" ConditionOnset as the reason.
{
"type": "MedicationOrder",
"target_encounter": "Annual_Checkup",
"codes": [{
"system": "RxNorm",
"code": "860975",
"display": "24 HR Metformin hydrochloride 500 MG Extended Release Oral Tablet"
}],
"reason": "Diabetes"
}
The generic module framework currently supports the following transitions:
- Direct
- Distributed
- Conditional
The Guard state and Conditional transition use conditional (boolean) logic. The following condition types are currently supported:
- Gender
- Age
- And
- Or
- Not
- True
- False