Procedures, devices, and the rest¶
The smaller domains, grouped because they share patterns you already know. Once the routing rule and the source-standard column pattern are second nature, these chapters get short.
Procedure to PROCEDURE_OCCURRENCE¶
Usually. The routing rule applies, and procedures cross domains more than people expect.
PROCEDURE_OCCURRENCE columns follow the standard pattern, plus quantity and modifier_concept_id.
Vocabularies. The Procedure domain has several standard vocabularies rather than one: SNOMED CT, CPT4, HCPCS, and ICD-10-PCS all contain standard concepts in this domain. This differs from Condition, where SNOMED is the single standard. It means a CPT4 code may already be standard and need no translation, which is convenient and occasionally confusing when you expect the SNOMED-style mapping path.
CPT4 requires a license, and Athena handles this with a post-download step. Plan for it, because discovering it mid-load is a delay.
Modifiers. CPT modifiers carry real meaning: laterality, whether a procedure was bilateral, whether it was discontinued. modifier_concept_id holds one. Sources frequently carry several, and the OMOP CDM has room for one, so this is a documented loss.
Where procedures route elsewhere. Some SNOMED procedure codes carry the Condition domain when they describe a resulting state, or the Observation domain when they describe an assessment. Run them through the routing procedure like everything else.
Status. Procedure.status includes not-done, which means the procedure was planned and did not happen. Loading it as a performed procedure is a straightforward inversion of meaning.
Device to DEVICE_EXPOSURE¶
DeviceRequest, DeviceUseStatement, and Device resources contribute here, and coverage is uneven across sources. Many EHR FHIR endpoints expose devices thinly or not at all.
DEVICE_EXPOSURE follows the standard pattern with unique_device_id and production_id for UDI content, and quantity.
Vocabularies. SNOMED for device concepts. Some HCPCS codes for durable medical equipment carry the Device domain.
UDI. The unique device identifier is genuinely valuable when present, because it identifies a specific manufactured item, which is what device safety surveillance depends on. It is also frequently absent. Preserve it when you have it.
Where devices arrive from elsewhere. Implant status codes often arrive in Condition or Observation resources and route here by domain.
Immunization¶
Covered in the medications chapter. Immunizations become DRUG_EXPOSURE rows. CVX codes are non-standard and map to RxNorm.
Worth adding: Immunization.status includes not-done, and Immunization.education and Immunization.reaction carry content with no natural OMOP CDM home. Reactions may belong in CONDITION_OCCURRENCE or OBSERVATION depending on their codes.
AllergyIntolerance to OBSERVATION¶
Allergies land in OBSERVATION, not CONDITION_OCCURRENCE. Allergy concepts generally carry the Observation domain.
The structure is awkward, and understanding it goes better than fighting it. An allergy has a substance, a criticality, a set of reactions, and a verification status. OBSERVATION gives you one concept, one value, and one qualifier per row.
A workable approach is one row for the allergy assertion itself, with the substance as value_as_concept_id or the allergy concept as observation_concept_id, and additional rows for reactions where they are clinically coded. FACT_RELATIONSHIP can link them, and in v5.4 observation_event_id with obs_event_field_concept_id can also express the linkage.
No known allergies deserves specific mention. A code asserting the absence of allergies is an assertion about absence. It is genuinely informative, since it distinguishes "asked and none" from "never asked", and it must not be loaded in any way that implies the person has an allergy to something.
AllergyIntolerance.verificationStatus includes refuted and entered-in-error, handled as in the conditions chapter.
Specimen to SPECIMEN¶
Direct and rarely populated. SPECIMEN holds the sample: what it was, when collected, from what anatomic site, in what quantity.
Most EHR FHIR endpoints do not expose specimens usefully. Laboratory information systems do. If your source has them and your research touches biospecimens or laboratory quality, populate it. Otherwise an empty SPECIMEN table is normal and acceptable.
DiagnosticReport¶
DiagnosticReport groups results with an interpretation. It has no single OMOP destination.
Its component Observation resources route independently by domain, as in the observations chapter. Its narrative conclusion, if present, belongs in NOTE. The grouping relationship can be preserved in FACT_RELATIONSHIP or through the v5.4 event linkage columns.
The loss here is real and should be documented: the interpretive act of grouping results and drawing a conclusion is clinical content that the OMOP CDM represents only weakly. For imaging and pathology in particular, the report is often where the meaning lives, and five MEASUREMENT rows do not carry it.
DocumentReference to NOTE¶
NOTE holds clinical text. DocumentReference points at documents, sometimes with the content inline as base64 and sometimes as a URL to be retrieved separately.
NOTE columns include note_type_concept_id, note_class_concept_id, note_title, note_text, encoding_concept_id, and language_concept_id.
Three considerations.
Retrieval. If content is by URL rather than inline, your pipeline needs a second retrieval step with its own authentication and error handling.
Governance. Clinical notes carry direct identifiers throughout and are the highest-risk content in an OMOP CDM instance. Whether to load notes at all is a governance decision, not a technical one. Many instances deliberately leave NOTE empty.
NOTE_NLP. Structured output from natural language processing over notes. Populated by a separate NLP pipeline, not by the FHIR transformation.
Coverage to PAYER_PLAN_PERIOD¶
Coverage gives payer, plan, and period. PAYER_PLAN_PERIOD holds the spans during which a person had a given coverage.
Coverage information is inconsistently exposed in EHR FHIR endpoints and much better in claims-derived sources. An empty PAYER_PLAN_PERIOD is common in EHR-derived instances.
One indirect use to keep in mind: continuous enrollment is the classic way to derive OBSERVATION_PERIOD in claims data. If your source has reliable coverage information, it is a much better basis for observation period than event-based inference. See the derived tables chapter.
Organization, Location, Practitioner, PractitionerRole¶
These populate the health system tables.
| FHIR | OMOP |
|---|---|
Location, Patient.address, Organization.address |
LOCATION |
Organization, Location as a service delivery site |
CARE_SITE |
Practitioner, PractitionerRole |
PROVIDER |
CARE_SITE wants a place_of_service_concept_id, drawn from a designated vocabulary for care settings. Mapping a source department name to a place of service is often a manual exercise.
PROVIDER holds specialty in specialty_concept_id, from PractitionerRole.specialty where present. Identifiers such as NPI go in provider_source_value or the dedicated NPI column, and that is a governance decision since provider identity is identifiable information.
Deduplication needs attention here. The same organization frequently appears many times across resources. Deduplicate on identifier where available, and be careful about deduplicating on name, since departments and facilities often share names across systems.
Where this quietly breaks, across all of these¶
Assuming resource type determines table. Still true here. Procedures route to Condition, conditions route to Device, allergies route to Observation.
Ignoring not-done and refuted statuses. Each of these resources has a status value that means the thing did not happen. Loading them inverts meaning.
Treating empty tables as failures. Empty SPECIMEN, PAYER_PLAN_PERIOD, NOTE, and DEVICE_EXPOSURE are all normal for EHR-derived instances. Note the emptiness in CDM_SOURCE so a consumer knows it is deliberate rather than broken.
Loading notes without a governance decision. The highest re-identification risk in the whole instance, and the easiest to load thoughtlessly because the transformation is simple.
Losing procedure modifiers. One column, several source modifiers, silent truncation.