synthetichealth/synthea

RuntimeException: unexpected value -1 within Framingham.stroke10Year (VitalSign.SYSTOLIC_BLOOD_PRESSURE is -1)

arvindshmicrosoft opened this issue · 2 comments

What happened?

When running with the following parameters, I'm getting a RuntimeException as shown in the log output section.

--exporter.fhir.export false --exporter.csv.export true --exporter.clinical_note.export false -p 5618 Kentucky Campbellsville -s 1705316793119 -cs 1705316793119

Note: in order to reproduce the issue locally, you also need to change the code here to options.referenceTime = 1705316793119L; to ensure that the reference time is also set to exactly the same as in the original run. I found that merely passing in yyyyMMdd for -rf is not sufficient to reproduce this issue locally as the scenario seems to be very sensitive to the exact value of referenceTime.

From what I could find debugging locally, it looks like in this code for BloodPressureValueGenerator, the following values are seen:

  • baseline is ~ 148
  • getMedicationImpacts(person) returns -110
  • getLifestyleImpacts(person, baseline, time) returns -15
  • getVariation(person, time) returns -34 or so

Therefore the final value computed for the BP ends up being negative. Eventually that translates to a -1 for bp in the context of Framingham.stroke10Year, which in turn finally causes the RuntimeException: unexpected value -1 exception.

Worth noting (perhaps) is that when I tried to re-run this with -ps specified, and the exact same person seed as I had observed in the debugging session, it does not result in the exception. I was only able to reproduce it with the exact "full" steps I mentioned above.

Environment

- OS: Rocky Linux 8.8
Linux version 4.18.0-477.27.1.el8_8.x86_64 (mockbuild@iad1-prod-build001.bld.equ.rockylinux.org) (gcc version 8.5.0 20210514 (Red Hat 8.5.0-18) (GCC)) #1 SMP Wed Sep 20 15:55:39 UTC 2023

- Java: openjdk 11.0.20 2023-07-18 LTS
OpenJDK Runtime Environment (Red_Hat-11.0.20.0.8-1) (build 11.0.20+8-LTS)
OpenJDK 64-Bit Server VM (Red_Hat-11.0.20.0.8-1) (build 11.0.20+8-LTS, mixed mode, sharing)

Relevant log output

Population: 5618
Seed: 1705316793119
Provider Seed:1705316793119
Reference Time: 1705316793119
Location: Campbellsville, Kentucky
Min Age: 0
Max Age: 140
java.lang.RuntimeException: unexpected value -1 for data [0, 106, 113, 118, 124, 130, 136, 143, 151, 162, 177]
  at org.mitre.synthea.modules.calculators.Framingham.getIndexForValueInRangelist(Framingham.java:468)
  at org.mitre.synthea.modules.calculators.Framingham.stroke10Year(Framingham.java:515)
  at org.mitre.synthea.modules.CardiovascularDiseaseModule.calculateStrokeRisk(CardiovascularDiseaseModule.java:113)
  at org.mitre.synthea.modules.CardiovascularDiseaseModule.process(CardiovascularDiseaseModule.java:40)
  at org.mitre.synthea.engine.Generator.updatePerson(Generator.java:712)
  at org.mitre.synthea.engine.Generator.createPerson(Generator.java:668)
  at org.mitre.synthea.engine.Generator.generatePerson(Generator.java:478)
  at org.mitre.synthea.engine.Generator.lambda$run$3(Generator.java:383)
  at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
  at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
  at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
  at java.base/java.lang.Thread.run(Thread.java:829)
...
Records: total=6688, alive=5617, dead=1071
RNG=5618
Clinician RNG=6846

For the above example, I have a complete JSON serialized person in case it's helpful. After compression the file is 260Kb and I can share it via. email or any other suggested mechanism. My email ID can be derived easily from my GitHub handle.

Here's another example (with a bigger population) where I saw the same pattern:

Population: 89603
Seed: 1705568678422
Provider Seed:1705568678422
Reference Time: 1705568678422
Location: Providence, Rhode Island
Min Age: 0
Max Age: 140
...
java.lang.RuntimeException: unexpected value -1 for data [0, 106, 116, 126, 136, 146, 156, 166, 176, 185, 196]
  at org.mitre.synthea.modules.calculators.Framingham.getIndexForValueInRangelist(Framingham.java:468)
  at org.mitre.synthea.modules.calculators.Framingham.stroke10Year(Framingham.java:517)
  at org.mitre.synthea.modules.CardiovascularDiseaseModule.calculateStrokeRisk(CardiovascularDiseaseModule.java:113)
  at org.mitre.synthea.modules.CardiovascularDiseaseModule.process(CardiovascularDiseaseModule.java:40)
  at org.mitre.synthea.engine.Generator.updatePerson(Generator.java:712)
  at org.mitre.synthea.engine.Generator.createPerson(Generator.java:668)
  at org.mitre.synthea.engine.Generator.generatePerson(Generator.java:478)
  at org.mitre.synthea.engine.Generator.lambda$run$3(Generator.java:383)
  at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
  at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
  at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
  at java.base/java.lang.Thread.run(Thread.java:829)
...
Records: total=104727, alive=89602, dead=15125
RNG=89603
Clinician RNG=1206

Thanks for reporting this issue and providing all the details. I'm able to reproduce everything you've mentioned here, including the fact that the -ps flag doesn't produce the same error which is frustrating. I may split that out into a separate issue.

At a glance, the biggest problem on this patient is with the medications they is on, leading to impacts that decrease their baseline blood pressure by 100. They appear to be on 7 different medications, some from the same drug class, which doesn't sound right (though I'm a software engineer, not a doctor). It looks like those 7 medications are being prescribed for 3 different reasons - hypertension, stable ischemic heart disease, and congestive heart failure. Unfortunately there's a lot of complexity in patients that have multiple chronic conditions, and the logistics of managing those prescriptions isn't being modeled well here.

A quick fix might be to just restrict the "getMedicationImpacts" total to only a certain amount, or apply other bounding limits to different calculations, but I'll keep looking

Also, looks like the reason the -ps flag didn't work is because of how synthea rotates seeds to ensure the correct number of alive patient records are generated and they match the target demographics. If the patient for seed 1 dies before reaching the target age, it has to retry with a new seed to fill that slot. If the same seed were re-used the same thing would happen all over again. For uniqueness and consistency the next seed is a new number based on the first seed.

In this case the seed to use to reproduce the issue without generating the entire population is 4417061940514179057 which is the original, non-rotated seed. (If you debug at the time of the exception and look at person.seed it's 3-something) That's neither obvious nor intended so I'll open a new issue to track that.