From a6cd0f92882854742e213d9c4dd6f292b2b404df Mon Sep 17 00:00:00 2001 From: Dylan Hall Date: Fri, 31 May 2024 12:19:29 -0400 Subject: [PATCH] enhancements --- keep_diabetes_no_dr.json | 54 +++++++++++++++++++ keep_npdr.json | 36 +++++++++++++ keep_npdr_no_pdr.json | 54 +++++++++++++++++++ keep_pdr.json | 36 +++++++++++++ run_coherent_eyes.sh | 11 ++++ .../synthea/modules/OphthalmicNoteModule.java | 9 +++- .../modules/OphthalmicProgressionModule.java | 45 ++++++++++------ src/main/python/coherent-data | 1 + 8 files changed, 229 insertions(+), 17 deletions(-) create mode 100644 keep_diabetes_no_dr.json create mode 100644 keep_npdr.json create mode 100644 keep_npdr_no_pdr.json create mode 100644 keep_pdr.json create mode 100755 run_coherent_eyes.sh create mode 160000 src/main/python/coherent-data diff --git a/keep_diabetes_no_dr.json b/keep_diabetes_no_dr.json new file mode 100644 index 0000000000..19e9391b71 --- /dev/null +++ b/keep_diabetes_no_dr.json @@ -0,0 +1,54 @@ +{ + "name": "keep_diabetes_no_dr", + "remarks": [ + "A blank module" + ], + "states": { + "Initial": { + "type": "Initial", + "conditional_transition": [ + { + "transition": "Keep", + "condition": { + "condition_type": "And", + "conditions": [ + { + "condition_type": "Active Condition", + "codes": [ + { + "system": "SNOMED-CT", + "code": 44054006, + "display": "Diabetes mellitus type 2 (disorder)" + } + ] + }, + { + "condition_type": "Not", + "condition": { + "condition_type": "Active Condition", + "codes": [ + { + "system": "SNOMED-CT", + "code": "1551000119108", + "display": "Nonproliferative diabetic retinopathy due to type 2 diabetes mellitus (disorder)" + } + ] + } + } + ] + } + }, + { + "transition": "Terminal" + } + ] + }, + "Terminal": { + "type": "Terminal" + }, + "Keep": { + "type": "Terminal" + } + }, + "gmf_version": 2 +} \ No newline at end of file diff --git a/keep_npdr.json b/keep_npdr.json new file mode 100644 index 0000000000..f23728fa49 --- /dev/null +++ b/keep_npdr.json @@ -0,0 +1,36 @@ +{ + "name": "Generated Keep Module", + "states": { + "Initial": { + "type": "Initial", + "name": "Initial", + "conditional_transition": [ + { + "transition": "Keep", + "condition": { + "condition_type": "Active Condition", + "codes": [ + { + "system": "SNOMED-CT", + "code": "1551000119108", + "display": "Nonproliferative diabetic retinopathy due to type 2 diabetes mellitus (disorder)" + } + ] + } + }, + { + "transition": "Terminal" + } + ] + }, + "Terminal": { + "type": "Terminal", + "name": "Terminal" + }, + "Keep": { + "type": "Terminal", + "name": "Keep" + } + }, + "gmf_version": 2 +} \ No newline at end of file diff --git a/keep_npdr_no_pdr.json b/keep_npdr_no_pdr.json new file mode 100644 index 0000000000..ae94d51578 --- /dev/null +++ b/keep_npdr_no_pdr.json @@ -0,0 +1,54 @@ +{ + "name": "keep_npdr_no_pdr", + "remarks": [ + "A blank module" + ], + "states": { + "Initial": { + "type": "Initial", + "conditional_transition": [ + { + "transition": "Keep", + "condition": { + "condition_type": "And", + "conditions": [ + { + "condition_type": "Active Condition", + "codes": [ + { + "system": "SNOMED-CT", + "code": "1551000119108", + "display": "Nonproliferative diabetic retinopathy due to type 2 diabetes mellitus (disorder)" + } + ] + }, + { + "condition_type": "Not", + "condition": { + "condition_type": "Active Condition", + "codes": [ + { + "system": "SNOMED-CT", + "code": "1501000119109", + "display": "Proliferative diabetic retinopathy due to type II diabetes mellitus (disorder)" + } + ] + } + } + ] + } + }, + { + "transition": "Terminal" + } + ] + }, + "Terminal": { + "type": "Terminal" + }, + "Keep": { + "type": "Terminal" + } + }, + "gmf_version": 2 +} \ No newline at end of file diff --git a/keep_pdr.json b/keep_pdr.json new file mode 100644 index 0000000000..78c086e091 --- /dev/null +++ b/keep_pdr.json @@ -0,0 +1,36 @@ +{ + "name": "Generated Keep Module", + "states": { + "Initial": { + "type": "Initial", + "name": "Initial", + "conditional_transition": [ + { + "transition": "Keep", + "condition": { + "condition_type": "Active Condition", + "codes": [ + { + "system": "SNOMED-CT", + "code": "1501000119109", + "display": "Proliferative diabetic retinopathy due to type II diabetes mellitus (disorder)" + } + ] + } + }, + { + "transition": "Terminal" + } + ] + }, + "Terminal": { + "type": "Terminal", + "name": "Terminal" + }, + "Keep": { + "type": "Terminal", + "name": "Keep" + } + }, + "gmf_version": 2 +} \ No newline at end of file diff --git a/run_coherent_eyes.sh b/run_coherent_eyes.sh new file mode 100755 index 0000000000..b4ac2854e8 --- /dev/null +++ b/run_coherent_eyes.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +basedir=`pwd` + +#./run_synthea -p 10 -a 55-70 -k keep_diabetes.json -fm src/test/resources/flexporter/eyes_on_fhir.yaml +# pre-processed files are now in ./output + +cd src/main/python/coherent-data/ +source ./venv/bin/activate + +./venv/bin/python associate_images.py ${basedir}/images/fundus_index.csv ${basedir}/images/oct_index.csv ${basedir}/output/fhir --clean --output ${basedir}/coherent_eyes \ No newline at end of file diff --git a/src/main/java/org/mitre/synthea/modules/OphthalmicNoteModule.java b/src/main/java/org/mitre/synthea/modules/OphthalmicNoteModule.java index dc1bc2ca29..2b4d0dfa59 100644 --- a/src/main/java/org/mitre/synthea/modules/OphthalmicNoteModule.java +++ b/src/main/java/org/mitre/synthea/modules/OphthalmicNoteModule.java @@ -126,12 +126,17 @@ public boolean process(Person person, long time) { boolean edema = (boolean) person.attributes.getOrDefault("macular_edema", false); if (edema) { - // TODO encounterNote.append("Macula: Flat, no edema or exudates OU\n"); + encounterNote.append("Macula: edema present OU\n"); } else { encounterNote.append("Macula: Flat, no edema or exudates OU\n"); } - encounterNote.append("Vessels: Attenuated arterioles with some copper wiring changes OU. No neovascularization noted.\n"); + if (drStage >= 3) { + encounterNote.append("Vessels: Attenuated arterioles with some copper wiring changes OU. Neovascularization present.\n"); + } else { + encounterNote.append("Vessels: Attenuated arterioles with some copper wiring changes OU. No neovascularization noted.\n"); + } + encounterNote.append("Periphery: No tears, holes, or detachments OU"); Procedure firstAntiVEGF = (Procedure)person.attributes.get("first_anti_vegf"); diff --git a/src/main/java/org/mitre/synthea/modules/OphthalmicProgressionModule.java b/src/main/java/org/mitre/synthea/modules/OphthalmicProgressionModule.java index 8c4d530bc9..f915d80b9a 100644 --- a/src/main/java/org/mitre/synthea/modules/OphthalmicProgressionModule.java +++ b/src/main/java/org/mitre/synthea/modules/OphthalmicProgressionModule.java @@ -15,6 +15,12 @@ public OphthalmicProgressionModule() { public Module clone() { return this; } + + /** + * Constant factor to represent impact of Diabetic Retinopathy stage on Macular thickness. + * Based on https://pubmed.ncbi.nlm.nih.gov/12510717/ + */ + public static final double DR_THICKNESS_FACTOR = 1; @Override public boolean process(Person person, long time) { @@ -23,7 +29,7 @@ public boolean process(Person person, long time) { boolean edema = (boolean) person.attributes.getOrDefault("macular_edema", false); // set visual acuity in LogMAR - + // some examples (ref: https://en.wikipedia.org/wiki/LogMAR_chart) // Foot LogMAR // 20/200 1.00 @@ -45,6 +51,8 @@ public boolean process(Person person, long time) { // "Pressures of between 11 and 21 mmHg are considered normal" // https://www.ncbi.nlm.nih.gov/books/NBK532237/ + // + // https://www.mdpi.com/2077-0383/13/3/676 if (highIop || (drStage == 4 && person.rand() < 0.001)) { // very small chance of this happening person.attributes.getOrDefault("high_iop", true); @@ -83,7 +91,8 @@ public boolean process(Person person, long time) { */ // https://iovs.arvojournals.org/article.aspx?articleid=2165526 - Integer octOffset = (Integer) person.attributes.get("oct_offset"); + // another source (not used) https://www.ncbi.nlm.nih.gov/pmc/articles/PMC1941772/ + Integer octOffset = (Integer) person.attributes.get("base_oct_offset"); if (octOffset == null) { octOffset = (int) person.rand(-10, 10); if (person.attributes.get(Person.GENDER).equals("M")) { @@ -92,22 +101,28 @@ public boolean process(Person person, long time) { // with a mean of 278 ± 23 μm in males and 263 ± 22 μm in females. octOffset += 10; } - - person.attributes.put("oct_center_point_thickness", 227 + octOffset); - person.attributes.put("oct_center_subfield_thickness", 270 + octOffset); - person.attributes.put("oct_inner_superior_subfield_thickness", 335 + octOffset); - person.attributes.put("oct_inner_nasal_subfield_thickness", 338 + octOffset); - person.attributes.put("oct_inner_inferior_subfield_thickness", 332 + octOffset); - person.attributes.put("oct_inner_temporal_subfield_thickness", 324 + octOffset); - person.attributes.put("oct_outer_superior_subfield_thickness", 290 + octOffset); - person.attributes.put("oct_outer_nasal_subfield_thickness", 305 + octOffset); - person.attributes.put("oct_outer_inferior_subfield_thickness", 280 + octOffset); - person.attributes.put("oct_outer_temporal_subfield_thickness", 279 + octOffset); - person.attributes.put("oct_total_volume", 8.4); + + person.attributes.put("base_oct_offset", octOffset); } + + // + octOffset += (int)(drStage * DR_THICKNESS_FACTOR); + + person.attributes.put("oct_center_point_thickness", 227 + octOffset); + person.attributes.put("oct_center_subfield_thickness", 270 + octOffset); + person.attributes.put("oct_inner_superior_subfield_thickness", 335 + octOffset); + person.attributes.put("oct_inner_nasal_subfield_thickness", 338 + octOffset); + person.attributes.put("oct_inner_inferior_subfield_thickness", 332 + octOffset); + person.attributes.put("oct_inner_temporal_subfield_thickness", 324 + octOffset); + person.attributes.put("oct_outer_superior_subfield_thickness", 290 + octOffset); + person.attributes.put("oct_outer_nasal_subfield_thickness", 305 + octOffset); + person.attributes.put("oct_outer_inferior_subfield_thickness", 280 + octOffset); + person.attributes.put("oct_outer_temporal_subfield_thickness", 279 + octOffset); + person.attributes.put("oct_total_volume", 8.4); // note return options here, see State$CallSubmodule - // if we return true, the submodule completed and processing continues to the next state + // if we return true, the submodule completed and processing continues to the + // next state // if we return false, the submodule did not complete (like with a Delay) // and will re-process the next timestep. return true; diff --git a/src/main/python/coherent-data b/src/main/python/coherent-data new file mode 160000 index 0000000000..9dac1f0747 --- /dev/null +++ b/src/main/python/coherent-data @@ -0,0 +1 @@ +Subproject commit 9dac1f0747ceee83c6b300ccea2ced4360708087