Skip to content
Abigail Watson edited this page Aug 3, 2023 · 2 revisions

Synthea supports the ability to run physiology models, using granular partial differential equations, in addition to the statistical markov chains used in most modules. These physiology models can be initiated in two ways:

  1. running a one-off simulation on a single patient.
  2. adjusting the synthea.properties file and including the physiology data in the entire patient cohort.

Either approach allows the user to adjust the differential equation solver, step size, sim duration, and configuration for output charts if desired. Output files will be in the output/physiology directory.

Configuration

You will need to set the following two fields to true in the src/main/resources/synthea.properties file.

# Use physiology simulations to generate some VitalSigns
physiology.generators.enabled = true

# Allow physiology module states to be executed
# If false, all Physiology state objects will immediately redirect to the state defined in
# the alt_direct_transition field
physiology.state.enabled = true

One-Off Simulations

Run any single physiology simulation with one of the following commands. This is useful for optimizing and debugging the physiology model to your needs, before adding it into the large population cohort.

  ./gradlew physiology --args="config/simulations/circadian_clock.yml"
  ./gradlew physiology --args="config/simulations/ecg.yml"
  ./gradlew physiology --args="config/simulations/insulin_signalling_diabetic.yml"
  ./gradlew physiology --args="config/simulations/insulin_signalling_normal.yml"
  ./gradlew physiology --args="config/simulations/liver_metabolism.yml"
  ./gradlew physiology --args="config/simulations/mammalian_circadian_rhythm_non_24hr.yml"
  ./gradlew physiology --args="config/simulations/menstrual_cycle.yml"
  ./gradlew physiology --args="config/simulations/o2_transport_metabolism.yml"
  ./gradlew physiology --args="config/simulations/plasma_melatonin.yml"  
  ./gradlew physiology --args="config/simulations/pulmonary_fluid_dynamics.yml"
  ./gradlew physiology --args="config/simulations/pulmonary_oxygen_intake.yml"
  ./gradlew physiology --args="config/simulations/telomere_associated_dna_damage.yml"
  ./gradlew physiology --args="config/simulations/weight_change.yml"

Output

Graphs and raw data in CVS files will be found in output/physiology folder.

You may also wish to create a large population of 10,000 or more individuals, and search for gallblader patients (which are currently the only patients that have ECG physiology data attached to them.)

Population Cohort

Alternatively, you can run physiology models on the entire patient cohort. Be forewarned! This can be computationally expensive, and may slow the run down considerably.

# generate the sample patients
./run_synthea -p 100

Biomodel References

You can find the original documentation for the biomodels at the following links:

Physiology Generator Inputs/Outputs

Each of the new physiology models is managed by a generator function, which handles inputs and outputs into the set of partial differential equations contained within the model. Example input and outputs looks like the following:

inputs:
#  # Effects of age and BMI loosely estimated from a comprehensive study on adult blood pressure
#  # percentiles (https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4553889/)
#  # More research is needed to determine appropriate age / BMI effects on all model parameters.
 - fromExp: "1.1889 + 0.025 * (#{BMI} - 21.7) + (if #b{blood_pressure_controlled} then 0 else (0.0112 * #{age}))"
   to: R_sys
   variance: 0.25 # Allowable variance in input to avoid running simulation
   
outputs:
 - fromExp: "((Max(#{V_lv}) - Min(#{V_lv})) / Max(#{V_lv}))*100"
   to: Left ventricular Ejection fraction

Users of Synthea who wish to customize and optimize these physiology models to their use cases are encouraged to look at the fromExp field, which contains a mapping function that can be optimized for particular use cases. In particular, we note the following functionalities that users should be aware of:

  • It is possible to pull in variables from the patient medical chart, using the #{foo} syntax.
  • Basic if then else functionality is supported within the expressions.
  • Functions are supported within the equations, as demonstrated by the Max() and Min() functions.
  • The functions map between parameters in the partial differential equations and variables in the patient record via the to field.

With this syntax and grammar, motivated users can optimize these physiology functions to produce daily activity patterns that the simulated patients follow, such as taking medications each day, exercising, or other behaviors. Seasonal trends can be developed. Acute response to emergencies and accidents can be modeled. And so forth.

Intersecting the Graph

It's important to understand that the physiology models produce continuous theoretical data for the patient over a discrete time period. This data is not the kind of data that would be generated by any particular medical device or wearable; rather, these models describe a theoretical representation of the biological functions going on within a person; of which a medical device or wearable will take a discrete snapshot at any particular time.

To put it in calculus terms, these models represent continuous functions; and any particular clinical observation will be a derivative function at a specific moment in time.

To visualize this, imaging the circadian cycle; which is roughly timed with the human sleep/wake cycle. Physiologically, this cycle is generally measured by melatonin and cortisol levels, which are increasing and decreasing throughout the day.

circadian_clock_1

At certain thresholds, these melatonin and cortisol levels impact a person's natural sleep cycle, in both the time of day they go to sleep, and the length of sleep. We can represent that with an intersecting function, like so.
circadian_clock_2

And what we're seeking with these optimized functions, is to take our continuous theoretical function, and filter it, such that it produces the same data that we might obtain from an empirical clinical measurement. So, for instance, wearing an iWatch with a sleep tracking app, might generate the following data: circadian_clock_3

Another use case for these intersecting functions, is that of setting sensitivity thresholds. Consider the case of an ECG monitor. In the following 10s example, we have an intersecting function that produces a count of N=7 beats per 10s, which could then be mapped to a FHIR Observation for a single vital signs measurement.
ecg_1

Alternatively, if we adjusting the threshold of our intersecting function, we could create a more permissive filter, with a count of N=11.
ecg_2

Such thresholding allows us to use the physiology models in increasingly complex ways. We may take the same model, and use it to generate physiology data: menstrual_cycle_1

Or, we might focus on a different part of the continuous function, and use the intersecting function to generate behavioral or mood data instead.
menstrual_cycle_2

Similar, depending on thresholding levels, one might use a pulmonary oxygen intake model to model chest breathing vs diaphragmatic breathing.
pulmonary_oxygen_intake_1

Intersection Function

How these functions are used to represent discrete data generated by medical devices and wearables is left to the reader. Suffice it to say that you'll want something like the following:

#  Melatonin levels indicating sleep levels   
outputs:
 - fromExp: "if (#{melatonin} > 1.4) then 50 else 80"
   to: heartrate

Or better yet, perhaps the following

#  Melatonin levels indicating sleep levels   
outputs:
 - fromExp: "if (#{melatonin} > 1.4) then 50 else null"
   to: heartrate

Get creative. Between if/then statements, functions, and input/output mapping, there's enough flexibility to get creative and use these physiology models to create all sorts of example wearables data.

Clone this wiki locally