A computable phenotype is a portable, executable definition of "which patients have condition X" — concept sets plus inclusion logic that runs against any OMOP CDM-compliant database. In the OHDSI stack, ATLAS authors these visually, CIRCE serializes them to a standardized JSON representation, and that JSON compiles to database-specific SQL. This skill helps you author such definitions and augment them with NLP features that OpenMed extracts from clinical text — exactly the signals that structured codes miss.
OMOP CDM, ATLAS, CIRCE, and the OHDSI Phenotype Library are open source. The vocabulary content you reference (SNOMED CT, CPT4, ICD) is user-supplied — do not bundle restricted terminologies; load them into your own OMOP vocabulary tables with your own licenses.
For terminology grounding of individual entities, see coding-icd10,
normalizing-rxnorm, mapping-loinc; this skill is about composing them into a
cohort.
A CIRCE cohort definition JSON has two parts: ConceptSets (the code lists) and an expression (entry event + inclusion rules). Shape (abridged):
{
"ConceptSets": [{
"id": 0, "name": "Type 2 diabetes",
"expression": { "items": [{
"concept": { "CONCEPT_ID": 201826, // OMOP standard concept
"CONCEPT_CODE": "44054006", // SNOMED (user vocab)
"VOCABULARY_ID": "SNOMED" },
"includeDescendants": true // pull the hierarchy
}] }
}],
"PrimaryCriteria": { // entry event
"CriteriaList": [{ "ConditionOccurrence": { "CodesetId": 0 } }],
"ObservationWindow": { "PriorDays": 0, "PostDays": 0 },
"PrimaryCriteriaLimit": { "Type": "First" }
},
"InclusionRules": [{
"name": "Adult at index",
"expression": { "Type": "ALL", "CriteriaList": [{
"Criteria": { "ConditionEra": { "AgeAtStart": { "Value": 18, "Op": "gte" } } }
}] }
}]
}
You author this in ATLAS (recommended) or by hand. The OHDSI Phenotype Library ships hundreds of vetted definitions as exactly this JSON; reuse before you write.
Code-based phenotypes are blind to facts that only appear in notes. The pattern is materialize an NLP feature as OMOP rows, then reference it like any concept set.
import openmed
# 1) Extract the text feature OpenMed is good at (e.g. tobacco use, symptom)
note = "Patient is a current smoker, ~1 pack/day, with worsening dyspnea."
res = openmed.analyze_text(note, model_name="disease_detection_superclinical",
output_format="dict")
# 2) Write a derived OBSERVATION (or a custom cohort attribute) per patient,
# mapping each extracted entity to a standard concept (grounded out-of-process).
# e.g. Observation: "Current smoker" -> a SNOMED concept in your vocab.
# 3) Reference that concept in a CIRCE ConceptSet, so the phenotype combines
# structured codes AND the NLP-derived flag in one inclusion rule.
This mirrors how eMERGE and PheKB phenotypes mix structured codes with NLP: the NLP step contributes high-recall flags for concepts that ICD/CPT capture poorly, and CIRCE composes them with the rest of the logic.
includeDescendants
to capture hierarchies. Vocabulary content comes from your own licensed tables.openmed.analyze_text and materialize as OMOP rows / cohort attributes.openmed.analyze_text over notes yields
Disease, Pharmaceutical, Genomics, Oncology, and social/behavioral spans. Ground
each to a standard concept (coding-icd10, normalizing-rxnorm, mapping-loinc,
or your SNOMED map) and write it into OMOP so CIRCE can reference it.openmed.deidentify before they enter any shared analytics environment.includeDescendants silently drops the
hierarchy (e.g. all diabetes subtypes). Forgetting nothing can over-capture —
review the resolved concept list.