Device
→
device_exposure
documented
primary
FHIR Device and DeviceUseStatement together map to one OMOP device_exposure row. Device carries identity (type, UDI, manufacturer, lot/serial); DeviceUseStatement records the clinical event (who, when, why). Alternatively, Procedure.usedCode can be a source. The mapper must resolve the two-resource join: DeviceUseStatement.device references a Device resource from which type, UDI, and production identifiers are extracted.
Conversion profile
omop-device-device-exposure
A FHIR instance converts to device_exposure iff it validates against this profile.
| Path | Card | Type | Binding / Fixed | Comment |
|---|---|---|---|---|
| Device.type | 1..*MS | omop-device-codesrequired | ||
| Device.patient | 1..* | Reference | Required for device_exposure.person_id. May be resolved via DeviceUseStatement.subject when Device.patient is absent. |
ViewDefinition (Stage 1 flattener)
omop-device-device-exposure
11 columns · resource Device
| column name | FHIRPath | type |
|---|---|---|
| id | Device.id | id |
| type_snomed | Device.type.coding.where(system='http://snomed.info/sct').first().code | code |
| type_text | Device.type.text | string |
| subject_id | DeviceUseStatement.subject | Reference(Patient) |
| device_exposure_start_date | DeviceUseStatement.timing[x] (start) | dateTime | Period |
| device_exposure_start_datetime | DeviceUseStatement.timing[x] (start) | dateTime | Period |
| device_exposure_end_date | DeviceUseStatement.timing[x] (end) | Period |
| device_exposure_end_datetime | DeviceUseStatement.timing[x] (end) | Period |
| unique_device_id | Device.udiCarrier[0].deviceIdentifier | string |
| production_id | Device.lotNumber | string |
| performer_id | DeviceUseStatement.source | Reference(Practitioner | PractitionerRole | RelatedPerson) |
Fields (19)
-
device_exposure_id← — integer · idPKrequiredSurrogate key. Autogenerated sequence or hash of FHIR resource ID. -
person_id←DeviceUseStatement.subjectinteger · Reference(Patient)FK→PERSONrequiredResolve Patient reference to integer person_id. DeviceUseStatement.subject is 1..1 and restricted to Patient. -
device_concept_id←DeviceUseStatement.device -> Device.typeinteger · CodeableConceptFK→CONCEPTrequiredmap:device_typeResolve Device.type.coding to a Standard Concept in the Device domain via vocabulary lookup. SNOMED device codes preferred. If no standard concept found, use 0.3 sources ▾
-
Device.type coding vocab lookup from contained Device; getCodingFirstRep() — DeviceUseStatement+Device pattern
-
Procedure.usedCode coding SNOMED lookup — no DeviceUseStatement, no Device resource needed
- ETL-German-FHIR-Core(java) refs/refs/ETL-German-FHIR-Core/src/main/java/org/miracum/etl/fhirtoomop/mapper/ProcedureMapper.java:240-245
-
No concept mapping (field not populated — PySpark from Procedure)
- HealthcareLakeETL(python) refs/refs/HealthcareLakeETL/mappings/device_exposure.py
-
-
device_exposure_start_date←DeviceUseStatement.timing[x] (start)date · dateTime | Periodrequiredtransform:If timingPeriod: use Period.start. If timingDateTime: use that value. If timingTiming: use bounds start or first event. Fallback to recordedOn if timing absent.Required NOT NULL. If no date can be determined, the record cannot be created.3 sources ▾
-
timingPeriod.start from DeviceUseStatement (Period is the primary pattern)
-
Procedure.performed[x] start (ETL-German uses Procedure as source, not DeviceUseStatement)
- ETL-German-FHIR-Core(java) refs/refs/ETL-German-FHIR-Core/src/main/java/org/miracum/etl/fhirtoomop/mapper/ProcedureMapper.java:246-261
-
performedPeriod.start from FHIR Procedure (PySpark)
- HealthcareLakeETL(python) refs/refs/HealthcareLakeETL/mappings/device_exposure.py
-
-
device_exposure_start_datetime←DeviceUseStatement.timing[x] (start)datetime · dateTime | PeriodFull datetime precision of the start. -
device_exposure_end_date←DeviceUseStatement.timing[x] (end)date · PeriodIf timingPeriod: use Period.end. If timingDateTime: null (point-in-time). If timing absent: null. -
device_exposure_end_datetime←DeviceUseStatement.timing[x] (end)datetime · PeriodFull datetime precision of the end. -
device_type_concept_id← constant integer · integerFK→CONCEPTrequiredmap:device_type_concept=32817Provenance concept. Use 32817 (EHR) for EHR-sourced data. omoponfhir uses 44818707 (EHR order list entry).2 sources ▾
-
32817 (EHR) — modern OMOP CDM v6 recommendation
- ETL-German-FHIR-Core(java) refs/refs/ETL-German-FHIR-Core/src/main/java/org/miracum/etl/fhirtoomop/mapper/ProcedureMapper.java:246-261 — CONCEPT_EHR = 32817
-
44818707 (EHR order list entry) — hardcoded in omoponfhir
- omoponfhir-v54-r4(java) refs/refs/omoponfhir-v54-r4/omoponfhir-omopv5-r4-mapping/src/main/java/edu/gatech/chai/omoponfhir/omopv5/r4/mapping/OmopDeviceUseStatement.java:361-363
- omoponfhir-omopv5-r4-mapping(java) refs/refs/omoponfhir-omopv5-r4-mapping/src/main/java/edu/gatech/chai/omoponfhir/omopv5/r4/mapping/OmopDeviceUseStatement.java
-
-
unique_device_id←Device.udiCarrier[0].deviceIdentifiervarchar(255) · stringThe UDI Device Identifier (UDI-DI). Take from the first udiCarrier entry. This is the fixed portion identifying the device model. -
production_id←Device.lotNumber | Device.serialNumbervarchar(255) · stringThe UDI Production Identifier (UDI-PI). OMOP v5.4 field. Combine lot number and/or serial number. If both present, prefer lotNumber. Some implementations concatenate: lot:{lotNumber};serial:{serialNumber}. -
quantity← — integer · integerNot directly available from DeviceUseStatement. May be derived from Procedure context or set to 1 as default. -
provider_id←DeviceUseStatement.sourceinteger · Reference(Practitioner | PractitionerRole | RelatedPerson)FK→PROVIDERResolve Practitioner/PractitionerRole reference to integer provider_id. Only Practitioner maps cleanly to OMOP provider. Patient/RelatedPerson references are skipped. -
visit_occurrence_id← — integer · Reference(Encounter)FK→VISIT_OCCURRENCEDeviceUseStatement has no direct encounter reference. Resolve via derivedFrom (Procedure with encounter) or temporal overlap with visit_occurrence records. ETL-German gets this from parent Procedure.encounter. -
visit_detail_id← — integer · Reference(Encounter)FK→VISIT_DETAILSame resolution strategy as visit_occurrence_id but at the detail level. Rarely populated. -
device_source_value←DeviceUseStatement.device -> Device.type.coding[0]varchar(50) · stringVerbatim source code. omoponfhir encodes as system:code when concept lookup fails. ETL-German uses the raw code value. Truncate if exceeds 50 chars.2 sources ▾
-
system:code composite string when vocab lookup fails (may exceed varchar(50))
-
Raw code value only (no system prefix)
- ETL-German-FHIR-Core(java) refs/refs/ETL-German-FHIR-Core/src/main/java/org/miracum/etl/fhirtoomop/mapper/ProcedureMapper.java:204-216
-
-
device_source_concept_id←DeviceUseStatement.device -> Device.typeinteger · CodeableConceptFK→CONCEPTThe source concept_id before standardization. If a non-standard concept matches the source coding, use it; otherwise 0. ETL-German sets this equal to device_concept_id. -
unit_concept_id← constant integer · integerFK→CONCEPT=0For devices with measurable quantities (e.g., blood transfusion volume). Not typically available from Device/DeviceUseStatement. -
unit_source_value← — varchar(50) · stringVerbatim unit string if applicable. Not typically available. -
unit_source_concept_id← constant integer · integerFK→CONCEPT=0Source concept for the unit. Default 0.
Vocabularies
device_type
| Source | Display | Concept ID | Concept Name |
|---|---|---|---|
| 360046005 | Cardiac pacemaker | 4141267 | Cardiac pacemaker |
| 304120007 | Total hip replacement prosthesis | 4216669 | Total hip replacement prosthesis |
| 303608005 | Ophthalmic intraocular lens prosthesis | 4230167 | Ophthalmic intraocular lens prosthesis |
| 272265001 | Bone screw | 4109487 | Bone screw |
| 14106009 | Cardiac defibrillator | 4324032 | Cardiac defibrillator |
| (unmapped) | No standard concept found | 0 | No matching concept |
device_type_concept
| Source | Display | Concept ID | Concept Name |
|---|---|---|---|
| EHR | EHR record | 32817 | EHR |
| EHR order | EHR order list entry | 44818707 | EHR order list entry |
| Claim | Claim | 32810 | Claim |
| Self-reported | Patient self-report | 32865 | Patient self-report |
device_vocabulary_systems
| Source | Display | Concept ID | Concept Name |
|---|---|---|---|
| http://snomed.info/sct | SNOMED CT | - | SNOMED |
| https://www.gmdnagency.org | GMDN | - | GMDN |
| http://hl7.org/fhir/NamingSystem/fda-udi | GUDID / UDI | - | None standard |
| http://www.cms.gov/Medicare/Coding/ICD10 | ICD-10-PCS | - | ICD10PCS |
Edge Cases
Missing Device.type
device_concept_id = 0, device_source_value = null. Log warning. The record is still valid -- OMOP allows concept 0.
timing[x] is dateTime (not Period)
Use the dateTime for device_exposure_start_date. device_exposure_end_date = null (point-in-time exposure).
timing[x] is Timing (repeating)
No natural OMOP representation. Options: (a) use Timing.repeat.boundsPeriod as start/end, (b) create one row per event occurrence, (c) skip and log warning. No implementation handles this case.
timing[x] absent
Fall back to DeviceUseStatement.recordedOn for start date. If that is also absent, the record cannot be created (device_exposure_start_date is NOT NULL). Skip and log.
Multiple Device.udiCarrier[] entries
Take the first entry's deviceIdentifier for unique_device_id. OMOP has a single field; additional UDI carriers are lost.
DeviceUseStatement.status = entered-in-error
Do not create a device_exposure row. ETL-German filters on acceptable status values (completed, active, on-hold).
Device is external (not contained)
Must resolve the Device reference before mapping. If the Device resource is unavailable, log warning and set device_concept_id = 0, unique_device_id = null.
Procedure.usedCode with multiple codings
Create one device_exposure row per coding entry (ETL-German approach). Deduplicate if needed.
device_source_value exceeds 50 chars
OMOP field is varchar(50). Truncate the source value. system:code format from omoponfhir may exceed this limit for long system URIs.
Device.type has multiple codings (e.g., SNOMED + GMDN)
Use the first coding that maps to a standard OMOP concept. Store all codings in device_source_value or pick the primary one. omoponfhir uses getCodingFirstRep().
Implantable device with no end date
device_exposure_end_date = null. This is correct for permanent implants -- the exposure is ongoing.
production_id composition
OMOP v5.4 added this field. Combine Device.lotNumber and/or Device.serialNumber. No standard format. Suggestion: lot:{value} or serial:{value} or concatenated.
Reference Implementations
- fhir-omop-ig(fsh) refs/refs/fhir-omop-ig/input/fsh/DeviceExposure.fsh — Logical model only (draft). No FML transform map exists for DeviceExposure.
- omoponfhir-v54-r4(java) refs/refs/omoponfhir-v54-r4/omoponfhir-omopv5-r4-mapping/src/main/java/edu/gatech/chai/omoponfhir/omopv5/r4/mapping/OmopDeviceUseStatement.java:224-366 — Bidirectional. constructOmop (F->O) handles contained Device resolution, timingPeriod, Device.type vocab lookup, udiCarrier, device_type_concept_id=44818707.
- omoponfhir-v54-r4(java) refs/refs/omoponfhir-v54-r4/omoponfhir-omopv5-r4-mapping/src/main/java/edu/gatech/chai/omoponfhir/omopv5/r4/mapping/OmopDevice.java:73-163 — O->F read-only (270 lines). constructFHIR builds Device from device_exposure entity.
- ETL-German-FHIR-Core(java) refs/refs/ETL-German-FHIR-Core/src/main/java/org/miracum/etl/fhirtoomop/mapper/ProcedureMapper.java:218-265 — Maps Procedure.usedCode to device_exposure. No DeviceUseStatement mapper. Uses SNOMED concept lookup. device_type_concept_id=32817.
- omoponfhir-omopv5-r4-mapping(java) refs/refs/omoponfhir-omopv5-r4-mapping/src/main/java/edu/gatech/chai/omoponfhir/omopv5/r4/mapping/OmopDeviceUseStatement.java — Earlier version, same logic as omoponfhir-v54-r4.
- GT-FHIR(java) refs/refs/GT-FHIR/gt-fhir-entities/src/main/java/edu/gatech/i3l/fhir/dstu2/entities/DeviceExposure.java:192-221 — Legacy DSTU2. O->F read-only. Maps entity to FHIR Device (not DeviceUseStatement).
- HealthcareLakeETL(python) refs/refs/HealthcareLakeETL/mappings/device_exposure.py — PySpark. Maps FHIR Procedure (not Device/DeviceUseStatement) to device_exposure. Minimal field coverage. Status: abandoned.