BFF to OMOP
This table reflects the current bff2omop implementation in convert-pheno.
The conversion maps one Beacon individual into OMOP PERSON and expands
repeated Beacon fields into OMOP row arrays such as CONDITION_OCCURRENCE,
OBSERVATION, PROCEDURE_OCCURRENCE, MEASUREMENT, and DRUG_EXPOSURE.
Version 0.34β
Output model: OMOP CDM
Entity: PERSON, CONDITION_OCCURRENCE, OBSERVATION, PROCEDURE_OCCURRENCE, MEASUREMENT, DRUG_EXPOSURE
OMOP Concept Resolutionβ
For each mapped BFF ontology term, Convert-Pheno checks the identifier before
the label. An active standard concept in the expected OMOP domain is used
directly; a non-standard source concept is resolved through its active Maps to relationship (Maps to value for categorical measurement values). Only
then can the configured exact, mixed, or fuzzy search use the label, and that
search remains restricted to active standard concepts in the expected domain.
If no unique eligible target exists, the target concept is 0 rather than an
unrelated same-label concept. Source values are preserved, and source concept
ids are populated when the BFF identifier exists in Athena. This behavior
requires the current enriched ohdsi.db; see
Terminology Search for the label-search modes and audit output.
Termsβ
PERSONβ
| Source field | Target field | Notes |
|---|---|---|
id | PERSON.person_id | Direct when numeric; otherwise convert-pheno allocates a surrogate integer and preserves the original value in person_source_value |
id | PERSON.person_source_value | Direct |
info.dateOfBirth | PERSON.birth_datetime | Direct |
info.dateOfBirth | PERSON.year_of_birth, month_of_birth, day_of_birth | Extracted from the ISO8601 timestamp |
missing info.dateOfBirth | PERSON.birth_datetime | Defaults to 1900-01-01T00:00:00Z |
missing info.dateOfBirth | PERSON.year_of_birth | Defaults to 1900 |
sex | PERSON.gender_concept_id, gender_source_value, gender_source_concept_id | Resolved as an OMOP Gender term |
ethnicity | PERSON.race_* or ethnicity_* | Resolved against the OMOP Race and Ethnicity domains; the target domain determines the destination fields |
geographicOrigin | PERSON.ethnicity_source_value | Established fallback when no true Ethnicity term resolved; geographic origin is not treated as an ethnicity concept |
missing ethnicity / geographicOrigin | PERSON.race_concept_id, ethnicity_concept_id | Default concept id 0 with empty source values |
CONDITION_OCCURRENCEβ
| Source field | Target field | Notes |
|---|---|---|
diseases[].diseaseCode | CONDITION_OCCURRENCE.condition_concept_id, condition_source_value, condition_source_concept_id | Resolved as an OMOP Condition term |
diseases[].ageOfOnset.age.iso8601duration + PERSON.year_of_birth | CONDITION_OCCURRENCE.condition_start_date | Converted with get_date_at_age(...) |
missing diseases[].ageOfOnset | CONDITION_OCCURRENCE.condition_start_date | Defaults to 1900-01-01 |
generated by convert-pheno | CONDITION_OCCURRENCE.condition_occurrence_id | Monotonic counter |
| default | CONDITION_OCCURRENCE.condition_type_concept_id | Currently forced to concept id 0 |
diseases[]._visit.occurrence_id | CONDITION_OCCURRENCE.visit_occurrence_id | Direct when numeric; otherwise a surrogate integer is allocated |
diseases[]._visit.detail_id | CONDITION_OCCURRENCE.visit_detail_id | Direct |
| current individual id | CONDITION_OCCURRENCE.person_id | Reuses PERSON.person_id |
OBSERVATION from exposuresβ
| Source field | Target field | Notes |
|---|---|---|
exposures[].exposureCode | OBSERVATION.observation_concept_id, observation_source_value, observation_source_concept_id | Resolved as an OMOP Observation term |
exposures[].date | OBSERVATION.observation_date | Direct |
exposures[].value | OBSERVATION.value_as_number | Direct |
missing exposures[].value | OBSERVATION.value_as_number | Defaults to -1 |
exposures[].unit | OBSERVATION.unit_concept_id, unit_source_value | Resolved as an OMOP Unit term |
generated by convert-pheno | OBSERVATION.observation_id | Monotonic counter |
| default | OBSERVATION.observation_type_concept_id | Currently forced to concept id 0 |
| default | OBSERVATION.value_as_concept_id, value_as_string | Currently emitted as empty strings |
exposures[]._visit.occurrence_id | OBSERVATION.visit_occurrence_id | Direct when numeric; otherwise a surrogate integer is allocated |
exposures[]._visit.detail_id | OBSERVATION.visit_detail_id | Direct |
| current individual id | OBSERVATION.person_id | Reuses PERSON.person_id |
OBSERVATION from phenotypicFeaturesβ
| Source field | Target field | Notes |
|---|---|---|
phenotypicFeatures[].featureType | OBSERVATION.observation_concept_id, observation_source_value, observation_source_concept_id | Resolved as an OMOP Observation term |
phenotypicFeatures[].onset.iso8601duration + PERSON.year_of_birth | OBSERVATION.observation_date | Converted with get_date_at_age(...) |
missing phenotypicFeatures[].onset | OBSERVATION.observation_date | Defaults to 1900-01-01 |
phenotypicFeatures[].value | OBSERVATION.value_as_number | Direct |
missing phenotypicFeatures[].value | OBSERVATION.value_as_number | Defaults to -1 |
phenotypicFeatures[].unit | OBSERVATION.unit_concept_id, unit_source_value | Resolved as an OMOP Unit term |
phenotypicFeatures[].excluded == true | none | Skipped entirely |
generated by convert-pheno | OBSERVATION.observation_id | Monotonic counter, appended to any existing exposure-derived observations |
| default | OBSERVATION.observation_type_concept_id | Currently forced to concept id 0 |
| default | OBSERVATION.value_as_concept_id, value_as_string | Currently emitted as empty strings |
phenotypicFeatures[]._visit.occurrence_id | OBSERVATION.visit_occurrence_id | Direct when numeric; otherwise a surrogate integer is allocated |
phenotypicFeatures[]._visit.detail_id | OBSERVATION.visit_detail_id | Direct |
| current individual id | OBSERVATION.person_id | Reuses PERSON.person_id |
PROCEDURE_OCCURRENCEβ
| Source field | Target field | Notes |
|---|---|---|
interventionsOrProcedures[].procedureCode | PROCEDURE_OCCURRENCE.procedure_concept_id, procedure_source_value, procedure_source_concept_id | Resolved as an OMOP Procedure term |
interventionsOrProcedures[].dateOfProcedure | PROCEDURE_OCCURRENCE.procedure_date | Direct |
interventionsOrProcedures[].dateOfProcedure | PROCEDURE_OCCURRENCE.procedure_datetime | Converted to YYYY-MM-DDTHH:MM:SSZ |
missing interventionsOrProcedures[].dateOfProcedure | PROCEDURE_OCCURRENCE.procedure_date, procedure_datetime | Defaults to 1900-01-01 and 1900-01-01T00:00:00Z |
generated by convert-pheno | PROCEDURE_OCCURRENCE.procedure_occurrence_id | Monotonic counter |
| default | PROCEDURE_OCCURRENCE.procedure_type_concept_id | Currently forced to concept id 0 |
interventionsOrProcedures[]._visit.occurrence_id | PROCEDURE_OCCURRENCE.visit_occurrence_id | Direct when numeric; otherwise a surrogate integer is allocated |
interventionsOrProcedures[]._visit.detail_id | PROCEDURE_OCCURRENCE.visit_detail_id | Direct |
| current individual id | PROCEDURE_OCCURRENCE.person_id | Reuses PERSON.person_id |
MEASUREMENTβ
| Source field | Target field | Notes |
|---|---|---|
measures[].assayCode | MEASUREMENT.measurement_concept_id, measurement_source_value, measurement_source_concept_id | Resolved as an OMOP Measurement term |
measures[].date | MEASUREMENT.measurement_date | Direct |
measures[].measurementValue (scalar) | MEASUREMENT.value_as_number | Direct |
measures[].measurementValue.quantity.value | MEASUREMENT.value_as_number | Used when the measurement value is a quantity object |
measures[].measurementValue.quantity.referenceRange.low | MEASUREMENT.range_low | Direct |
measures[].measurementValue.quantity.referenceRange.high | MEASUREMENT.range_high | Direct |
measures[].measurementValue (ontology term) | MEASUREMENT.value_as_concept_id, value_source_value | Resolved in the OMOP Meas Value domain, including Maps to value relationships |
measures[].measurementValue.quantity.unit | MEASUREMENT.unit_concept_id, unit_source_value, unit_source_concept_id | Resolved as an OMOP Unit term |
missing measures[].measurementValue | MEASUREMENT.value_as_number | Defaults to -1 |
MEASUREMENT.value_as_number | MEASUREMENT.value_source_value | Copied back as provenance/source payload |
measures[].procedure | none | Describes the assay method, not OMOP record provenance; measurement_type_concept_id remains 0 |
generated by convert-pheno | MEASUREMENT.measurement_id | Monotonic counter |
measures[]._visit.occurrence_id | MEASUREMENT.visit_occurrence_id | Direct when numeric; otherwise a surrogate integer is allocated |
measures[]._visit.detail_id | MEASUREMENT.visit_detail_id | Direct |
| current individual id | MEASUREMENT.person_id | Reuses PERSON.person_id |
DRUG_EXPOSUREβ
| Source field | Target field | Notes |
|---|---|---|
treatments[].treatmentCode | DRUG_EXPOSURE.drug_concept_id, drug_source_value, drug_source_concept_id | Resolved as an OMOP Drug term |
treatments[].doseIntervals[0].quantity.value | DRUG_EXPOSURE.quantity | Uses only the first dose interval |
missing treatments[].doseIntervals[].quantity.value | DRUG_EXPOSURE.quantity | Defaults to -1 |
treatments[].doseIntervals[0].quantity.unit.label | DRUG_EXPOSURE.dose_unit_source_value | Uses only the first dose interval |
treatments[].routeOfAdministration | DRUG_EXPOSURE.route_concept_id, route_source_value | Resolved as an OMOP Route term |
treatments[].doseIntervals[0].interval.start/end | DRUG_EXPOSURE.drug_exposure_start_date, drug_exposure_end_date | Preferred source; timestamps are truncated to dates and a single bound is reused for both fields when the other is missing |
treatments[].ageOfOnset.age.iso8601duration + PERSON.year_of_birth | DRUG_EXPOSURE.drug_exposure_start_date | Used when no interval dates are present |
missing interval dates and missing ageOfOnset | DRUG_EXPOSURE.drug_exposure_start_date | Defaults to 1900-01-01 |
| missing explicit end date | DRUG_EXPOSURE.drug_exposure_end_date | Reuses the resolved start date |
treatments[].cumulativeDose.unit.label + cumulativeDose.value | DRUG_EXPOSURE.days_supply | Converted with convert_label_to_days(...) |
generated by convert-pheno | DRUG_EXPOSURE.drug_exposure_id | Monotonic counter |
| default | DRUG_EXPOSURE.drug_type_concept_id | Currently forced to 0 |
treatments[]._visit.occurrence_id | DRUG_EXPOSURE.visit_occurrence_id | Direct when numeric; otherwise a surrogate integer is allocated |
treatments[]._visit.detail_id | DRUG_EXPOSURE.visit_detail_id | Direct |
| current individual id | DRUG_EXPOSURE.person_id | Reuses PERSON.person_id |