Encounter
→
visit_occurrence
implemented
primary
One FHIR Encounter maps to one visit_occurrence row. The visit_occurrence table is the OMOP anchor for linking clinical events to a care context -- every event table (condition_occurrence, measurement, drug_exposure, etc.) carries a visit_occurrence_id FK. Encounter.class maps to visit_concept_id via a small lookup table; period maps to start/end dates; participant maps to provider_id; serviceProvider maps to care_site_id. Planned and cancelled encounters are dropped.
Conversion profile
omop-encounter-visit-occurrence
A FHIR instance converts to visit_occurrence iff it validates against this profile.
| Path | Card | Type | Binding / Fixed | Comment |
|---|---|---|---|---|
| Encounter.status | 1..*MS | fhir/encounter-statusrequired | Allowed values restricted to finished, in-progress, onleave. | |
| Encounter.class | 1..1MS | omop-visit-codesrequired | ||
| Encounter.subject | 1..1MS | Reference | Required for visit_occurrence.person_id. | |
| Encounter.period | 1..*MS | |||
| Encounter.period.start | 1..1MS | Required for visit_occurrence.visit_start_date / visit_start_datetime. | ||
| Encounter.period.end | MS | Optional — falls back to visit_start_date when absent. | ||
| Encounter.participant | MS | First Practitioner-typed participant becomes visit_occurrence.provider_id. | ||
| Encounter.serviceProvider | MS | Maps to visit_occurrence.care_site_id. |
ViewDefinition (Stage 1 flattener)
omop-encounter-visit-occurrence
18 columns · resource Encounter
| column name | FHIRPath | type |
|---|---|---|
| id | Encounter.id | id |
| class_actcode | Encounter.class.coding.where(system='http://terminology.hl7.org/CodeSystem/v3-ActCode').first().code | code |
| class_text | Encounter.class.text | string |
| type_snomed | Encounter.type.coding.where(system='http://snomed.info/sct').first().code | code |
| type_cpt | Encounter.type.coding.where(system='http://www.ama-assn.org/go/cpt').first().code | code |
| type_text | Encounter.type.text | string |
| subject_id | Encounter.subject | Reference(Patient) |
| visit_start_date | Encounter.period.start | dateTime |
| visit_start_datetime | Encounter.period.start | dateTime |
| visit_end_date | Encounter.period.end | dateTime |
| visit_end_datetime | Encounter.period.end | dateTime |
| performer_id | Encounter.participant[0].individual | Reference(Practitioner) |
| managing_organization_id | Encounter.serviceProvider | Reference(Organization) |
| admitted_from | Encounter.hospitalization.admitSource | CodeableConcept |
| admitted_from_text | Encounter.hospitalization.admitSource | CodeableConcept |
| discharged_to | Encounter.hospitalization.dischargeDisposition | CodeableConcept |
| discharged_to_text | Encounter.hospitalization.dischargeDisposition | CodeableConcept |
| preceding_visit_occurrence_id | Encounter.partOf | Reference(Encounter) |
Condition: status in ('finished', 'in-progress')
Implementation:
src/mapper/encounter.tsFields (17)
-
visit_occurrence_id←Encounter.idinteger · idPKrequiredSurrogate key generated from Encounter.id. HL7 IG FML has this as TODO. NACHC uses auto-increment; omoponfhir uses IdMapping; our project hashes Encounter.id.3 sources ▾
-
TODO/commented placeholder — no concrete ID strategy defined
- fhir-omop-ig(fml) refs/refs/fhir-omop-ig/input/maps/EncounterVisit.fml
-
IdMapping lookup (FPerson-style): resolve FHIR ID to internal OMOP integer
-
Auto-increment sequence
- NACHC-fhir-to-omop(java) refs/refs/NACHC-fhir-to-omop/src/main/java/org/nachc/tools/fhirtoomop/omop/person/factory/builder/visitoccurrence/OmopVisitOccurrenceBuilder.java:50-88
- ETL-German-FHIR-Core(java) refs/refs/ETL-German-FHIR-Core/src/main/java/org/miracum/etl/fhirtoomop/mapper/EncounterInstitutionContactMapper.java:312-348
-
-
person_id←Encounter.subjectinteger · Reference(Patient)FK→PERSONrequiredResolve Patient reference. omoponfhir throws FHIRException if absent. ETL-German looks up by identifier or logical ID. Our project uses ctx.ids.resolveRef(), defaults to 0 if unresolved.3 sources ▾
-
ctx.ids.resolveRef() — defaults to 0 if Patient not found
- fhir-to-omop-demo(jq) refs/refs/fhir-to-omop-demo/demo/translate/map/Encounter.jq — .subject.id
-
IdMapping.getOMOPfromFHIR() — returns null / skips encounter if subject missing
-
Looks up by both identifier and logical ID; skips encounter if not found
-
-
visit_concept_id←Encounter.class.codeinteger · codeFK→CONCEPTrequiredmap:visit_typeSee visit type vocabulary mapping. Small lookup table; 3-9 entries depending on implementation. Core consensus: IMP->9201, AMB->9202, EMER->9203.4 sources ▾
-
Pass-through: code copied without concept translation
- fhir-omop-ig(fml) refs/refs/fhir-omop-ig/input/maps/EncounterVisit.fml:20-24
-
Hardcoded enum: IMP/AMB/EMER only; unknown codes → 0
-
Hardcoded: IMP→9201, EMER→9203, else 9202 (unknown codes fall to outpatient)
- FhirToCdm(csharp) refs/refs/FhirToCdm/FhirToCdmMappings.cs:183-188
- fhir-x-omop(python) refs/refs/fhir-x-omop/fhir_x_omop/to_omop/visit_occurrence.py:23-29
-
DB-backed concept lookup including German-specific class codes; 0 for no match
- ETL-German-FHIR-Core(java) refs/refs/ETL-German-FHIR-Core/src/main/java/org/miracum/etl/fhirtoomop/mapper/EncounterInstitutionContactMapper.java:312-348
- NACHC-fhir-to-omop(java) refs/refs/NACHC-fhir-to-omop/src/main/java/org/nachc/tools/fhirtoomop/omop/person/factory/builder/visitoccurrence/OmopVisitOccurrenceBuilder.java:66-74
-
-
visit_start_date←Encounter.period.startdate · dateTimerequiredtransform:date(Encounter.period.start)Date portion (YYYY-MM-DD). Skip encounter if absent (our project, ETL-German). omoponfhir writes epoch 0 if absent.2 sources ▾
-
Skip record when period.start is absent
-
Write epoch 0 (new Date(0)) when period.start is absent
-
-
visit_start_datetime←Encounter.period.startdatetime · dateTimeFull ISO datetime. omoponfhir copies the start Date object directly. -
visit_end_date←Encounter.period.enddate · dateTimerequiredtransform:date(Encounter.period.end) ?? visit_start_dateIf absent, use visit_start_date (our project, NACHC, FhirToCdm). ETL-German uses LocalDate.now() for still-admitted patients.3 sources ▾
-
Fallback to visit_start_date when period.end is absent
-
LocalDate.now() for still-admitted patients (CONCEPT_STILL_PATIENT status)
-
Write epoch 0 when period.end is absent
-
-
visit_end_datetime←Encounter.period.enddatetime · dateTimeFull ISO datetime; null if absent. ETL-German uses LocalDateTime.now() for CONCEPT_STILL_PATIENT cases. -
visit_type_concept_id← constant integer · integerFK→CONCEPTrequired=3281732817 (EHR) in our project and FhirToCdm. 44818518 (Visit derived from EHR) in omoponfhir and fhir-x-omop. Both acceptable; 32817 is OMOP-recommended for CDM v5.4.3 sources ▾
-
Hardcoded 32817 (EHR) — OMOP-recommended for CDM v5.4
- FhirToCdm(csharp) refs/refs/FhirToCdm/FhirToCdmMappings.cs:237-237
-
Hardcoded 44818518 (Visit derived from EHR) — older vocabulary, still acceptable
- omoponfhir(java) refs/refs/omoponfhir-v54-r4/omoponfhir-omopv5-r4-mapping/src/main/java/edu/gatech/chai/omoponfhir/omopv5/r4/mapping/OmopEncounter.java:285-424
- fhir-x-omop(python) refs/refs/fhir-x-omop/fhir_x_omop/to_omop/visit_occurrence.py:34-34
- fhir-to-omop-demo(jq) refs/refs/fhir-to-omop-demo/demo/translate/map/Encounter.jq:57-57
-
Derived from Encounter.status via concept lookup (not a constant)
-
-
provider_id←Encounter.participant[0].individualinteger · Reference(Practitioner)FK→PROVIDERFirst Practitioner-typed participant (our project). omoponfhir uses getParticipantFirstRep(). fhir-to-omop-demo filters for PPRF (primary performer). fhir-x-omop filters for ATND (attending). NACHC defaults to 1.4 sources ▾
-
First Practitioner-typed participant (type-filtered iteration)
-
Filter for PPRF (primary performer) participant type
- fhir-to-omop-demo(jq) refs/refs/fhir-to-omop-demo/demo/translate/map/Encounter.jq:13-15
-
Filter for ATND (attending) participant type
- fhir-x-omop(python) refs/refs/fhir-x-omop/fhir_x_omop/to_omop/visit_occurrence.py:7-17
-
Default to constant provider_id = 1 (no resolution)
-
-
care_site_id←Encounter.serviceProviderinteger · Reference(Organization)FK→CARE_SITEOrganization reference. Our project resolves via ctx.ids.resolveRef(). omoponfhir resolves via IdMapping. FhirToCdm creates a Provider from serviceProvider.display. NACHC defaults to 1.4 sources ▾
-
IdMapping.getOMOPfromFHIR() → loads CareSite entity from serviceProvider reference
-
Creates a Provider record from serviceProvider.display instead of linking to care_site
- FhirToCdm(csharp) refs/refs/FhirToCdm/FhirToCdmMappings.cs:215-224
-
Uses location[0].location.id instead of serviceProvider
- fhir-to-omop-demo(jq) refs/refs/fhir-to-omop-demo/demo/translate/map/Encounter.jq:59-59
-
Default to constant care_site_id = 1 (no resolution)
-
-
visit_source_value←Encounter.class.codevarchar(50) · codeVerbatim FHIR class code (our project, FhirToCdm). omoponfhir stores Encounter.id. NACHC stores encounter ID.2 sources ▾
-
Verbatim Encounter.class.code stored as source value
- FhirToCdm(csharp) refs/refs/FhirToCdm/FhirToCdmMappings.cs:173-250
- fhir-x-omop(python) refs/refs/fhir-x-omop/fhir_x_omop/to_omop/visit_occurrence.py — type[0] coding, not class
-
Stores Encounter.id (not the class code) as source value
- omoponfhir(java) refs/refs/omoponfhir-v54-r4/omoponfhir-omopv5-r4-mapping/src/main/java/edu/gatech/chai/omoponfhir/omopv5/r4/mapping/OmopEncounter.java:285-424
- NACHC-fhir-to-omop(java) refs/refs/NACHC-fhir-to-omop/src/main/java/org/nachc/tools/fhirtoomop/omop/person/factory/builder/visitoccurrence/OmopVisitOccurrenceBuilder.java:50-88
-
-
visit_source_concept_id← constant integer · integer=00 in most implementations. NACHC maps via concept lookup when visitConcept has a Visit domain. -
admitted_from_concept_id←Encounter.hospitalization.admitSourceinteger · CodeableConceptFK→CONCEPTRequires vocabulary lookup. 0 if unmapped (our project, FhirToCdm). ETL-German stores in post_process_map for deferred resolution. No implementation in the reference set performs a complete vocabulary lookup. -
admitted_from_source_value←Encounter.hospitalization.admitSourcevarchar(50) · CodeableConcepttransform:getSourceValue(admitSource).truncate(50)Raw admit source code. Our project extracts via getSourceValue() and truncates to 50 chars. NACHC stores 'Not Available'. -
discharged_to_concept_id←Encounter.hospitalization.dischargeDispositioninteger · CodeableConceptFK→CONCEPTmap:discharge_dispositionRequires vocabulary lookup. 0 if unmapped (our project). fhir-x-omop maps home->8536, snf->8676, rehab->8615, exp->4216643. Most implementations default to 0.3 sources ▾
-
Hardcoded 4-entry map: home→8536, snf→8676, rehab→8615, exp→4216643; else 0
- fhir-x-omop(python) refs/refs/fhir-x-omop/fhir_x_omop/to_omop/visit_occurrence.py:41-46
-
Deferred to post_process_map for later vocabulary resolution
-
No vocabulary lookup; defaults to 0
- FhirToCdm(csharp) refs/refs/FhirToCdm/FhirToCdmMappings.cs:173-250
- fhir-omop-ig(fml) refs/refs/fhir-omop-ig/input/maps/EncounterVisit.fml:31-43 — pass-through code only
-
-
discharged_to_source_value←Encounter.hospitalization.dischargeDispositionvarchar(50) · CodeableConcepttransform:getSourceValue(dischargeDisposition).truncate(50)Raw discharge code. Our project extracts via getSourceValue(). fhir-x-omop uses the display. -
preceding_visit_occurrence_id←Encounter.partOfinteger · Reference(Encounter)FK→VISIT_OCCURRENCEPrevious encounter in a chain. Not widely used for this field; most implementations use partOf to link child encounters via visit_detail instead.
Vocabularies
visit_type
| Source | Display | Concept ID | Concept Name |
|---|---|---|---|
| IMP | Inpatient | 9201 | Inpatient Visit |
| ACUTE | Acute | 9201 | Inpatient Visit |
| AMB | Ambulatory | 9202 | Outpatient Visit |
| EMER | Emergency | 9203 | Emergency Room Visit |
| HH | Home Health | 581476 | Home Visit |
| SS | Short Stay | 9202 | Outpatient Visit |
| OBSENC | Observation Encounter | 9201 | Inpatient Visit |
| FLD | Field | 9202 | Outpatient Visit |
| VR | Virtual | 9202 | Outpatient Visit |
| (unknown) | Unknown/unmapped code | 0 | No matching concept |
visit_type_concept
| Source | Display | Concept ID | Concept Name |
|---|---|---|---|
| EHR | EHR | 32817 | EHR |
| EHR-derived | Visit derived from EHR | 44818518 | Visit derived from EHR |
discharge_disposition
| Source | Display | Concept ID | Concept Name |
|---|---|---|---|
| home | Home | 8536 | Home |
| snf | Skilled Nursing Facility | 8676 | Skilled Nursing Facility |
| rehab | Rehabilitation Hospital | 8615 | Rehabilitation Hospital |
| exp | Patient died | 4216643 | Patient died |
| (unmapped) | Unmapped | 0 | No matching concept |
Edge Cases
Missing period.start
Skip -- cannot create visit_occurrence without a start date.
Missing period.end
Use visit_start_date as visit_end_date.
Status = planned / cancelled / entered-in-error
Skip -- only finished and in-progress encounters produce rows.
Status = unknown with no end date
ETL-German treats as CONCEPT_STILL_PATIENT and sets end date to now().
Multiple participants
Take first Practitioner reference for provider_id. Alternatives: ATND type (fhir-x-omop), PPRF primary performer (fhir-to-omop-demo).
partOf (nested encounter)
Ideally creates visit_detail row linked to parent visit_occurrence. Our implementation does not yet support this.
No class code
visit_concept_id = 0.
Class code not in lookup
visit_concept_id = 0 (our project, omoponfhir). FhirToCdm and fhir-x-omop default to 9202.
ER-to-inpatient admission
Two separate encounters in FHIR; could merge into OMOP concept 262 (ER+Inpatient). No implementation does this automatically -- requires post-processing.
Missing subject
Skip -- cannot create visit_occurrence without person_id. All implementations skip or throw.
Encounter with location[] transfers
ETL-German creates one visit_detail row per location with separate start/end dates. Other implementations ignore location transfers.
visit_type_concept_id variation
32817 (EHR) vs 44818518 (Visit derived from EHR). Both acceptable. Choose based on CDM version guidance.
hospitalization absent
All admit/discharge fields default to 0/null. No error.
Encounter without an id
visit_occurrence_id remains undefined. Most implementations require an ID for FK resolution by downstream mappers.
Long source values
Truncate admitted_from_source_value and discharged_to_source_value to 50 chars.
Reference Implementations
- fhir-omop-ig(fml) refs/refs/fhir-omop-ig/input/maps/EncounterVisit.fml — Normative IG; minimal (44 lines). Maps class->visit_concept_id, period->dates. Uses R5 field names.
- omoponfhir(java) refs/refs/omoponfhir-v54-r4/omoponfhir-omopv5-r4-mapping/src/main/java/edu/gatech/chai/omoponfhir/omopv5/r4/mapping/OmopEncounter.java:1-425 — Bidirectional. Supports visit_detail via partOf, hospitalization mapping, multiple class codes.
- FhirToCdm(csharp) refs/refs/FhirToCdm/FhirToCdmMappings.cs — CreateVisitOccurrence() lines 173-250. Minimal F->O.
- ETL-German-FHIR-Core(java) refs/refs/ETL-German-FHIR-Core/src/main/java/org/miracum/etl/fhirtoomop/mapper/EncounterInstitutionContactMapper.java:1-872 — Most complete. German encounter classification, department transfers via visit_detail, incremental updates.
- NACHC-fhir-to-omop(java) refs/refs/NACHC-fhir-to-omop/src/main/java/org/nachc/tools/fhirtoomop/omop/person/factory/builder/visitoccurrence/OmopVisitOccurrenceBuilder.java:1-90 — Visit occurrence building with DB-backed concept mapper.
- fhir-to-omop-demo(jq) refs/refs/fhir-to-omop-demo/demo/translate/map/Encounter.jq — Lightweight jq transform (146 lines). Also produces condition_occurrence, observation, procedure_occurrence from same Encounter.
- fhir-x-omop(python) refs/refs/fhir-x-omop/fhir_x_omop/to_omop/visit_occurrence.py — Bidirectional with lossless round-trip (55 lines). Has discharge concept mapping.
- mends-on-fhir(whistle) refs/refs/mends-on-fhir/whistle-mappings/synthea/whistle-functions/Visit_Encounter.wstl — O->F direction (107 lines). Uses ConceptMap JSON for visit_concept_id -> Encounter.class.