Monday, December 3, 2018

Crosswalking structures to and from FHIR

Boston - Crosswalk
In thinking about how to express the mappings, I'm going through a process of identifying assets that can be used. The biggest of these assets is knowledge, that is to say, expressed, declarative statements that provide facts about how the process might be executed.  These need to be stored in some sort of data structure.  I struggle with how StructureMap is put together, and so I'm going to try something different.  ConceptMap is used for mapping concepts, and model elements are nothing more than specializations of concepts, so I'm going to use that I think.  In fact, if you look at it, you can see the developers of it considered that it could be used for just that purpose as well, as you can see certain references in it to StructureDefinition.  So, the next thing I need to figure out is how to use that.  Let's go look at the knowledge I need to be able to capture.

For example:  The V2 MSH structure should map to a FHIR MessageHeader resource, or a CWE should map to a CodeableConcept.  A lot of this information is already present (or largely so) as data in Mapping tables found in the FHIR Resource definitions (with some semi-regular structure).

Another core component is information that can be used to make inferences.  For example, ways to convert Instant to DateTime, or CodeableConcept to Coding, or Family Name (FN in V2) to String (ST).  I might even be able to make an inference based on a rule that PPN = Flatten(XCN)+TS (well, not quite, but close, I think I'll skip that one).

Heuristics are also important in this.  My goal is to make this as simple as possible for the implementer of a conversion.  One such heuristic might be that if source structure A maps to target structure B, and the parts of source structure A produce substructures A1, A2, ... AN, and target structure B has substructures B1, B2, ... BM then it would be reasonable to assume that for some values n and m that An => Bm.  Figuring out how n and m are determined is the tricky bit.  That is to say, the subcomponents of B will be found in the subcomponents of A.

Inferring the values of n and m could be done by matching types.  So, if PV1-7 is an XCN, and XCN produces HumanName and Identifier, and PV1 produces an Encounter resource, the software would have to make some guesses about where these things go.  Knowing the HumanName and Identifier are part of several resources, It might guess (based on following references) that these might be part of Patient, RelatedPerson or Practitioner resources as found in Encounter.participant.individual or Encounter.patient, but would have to have something else to disambiguate where these fields go in more detail.

If I were also to say that PV1-7 also produces a fixed Coding having the value "http://hl7.org/fhir/v3/ParticipationType|ATND", now we might have enough information to tip the balance towards Encounter.participant.individual, because this code properly appears in the Encounter.participant.type, and PV1-7 created both.

Looking at it from the instance level, if Identifier.type was populated with the value http://hl7.org/fhir/v2/0203|PRN, http://hl7.org/fhir/v2/0203|MD, http://hl7.org/fhir/identifier-type|PLAC or http://hl7.org/fhir/identifier-type|FILL, again, we'd have some inference somewhere that says these codes apply to Practitioner.identifier.type, and so we could also just handle it by adding a fixed mapping from XCN to one of these based on the context.

XCN -> HumanName, Identifier expresses a concept mapping from coded concepts in HL7 Version 2 (type names are a concept from a coded vocabulary, we could for example, use the names of things in the V2 XML schemas) into coded concepts in HL7 FHIR (using values in /StructureDefinition//element/path or a combination thereof with type (to get to specific detail such as valueInteger on paths with [x] at the end).

So, a concept map going from V2 to FHIR might have elements looking like this:

"element": [
  ...
  {
    "code": "MSH",
    "target": [
      {
        "code": "MessageHeader",
        "equivalence": "equivalent"
      }
    ]
  },
  ...
  { "code": "XCN",
    "target": [
      {
        "code": "Identifier",
        "equivalence": "narrower"
      },
      { "code": "HumanName",
        "equivalence": "narrower"
      }
    ]
  },
  ...
],

Translating the table above to the reverse mapping, one would do the obvious things:  For each group of targets with the same code create a single element having that code, creating a target for each element to which the members of the group belonged with a target code value equal to the original source code value, and reversing the equivalences as follows:


sourcetarget
narrowerwider
widernarrower
subsumesspecializes
specializessubsumes
The rest are essentially unchanged
equivalentequivalent
equalequal
inexactinexact
unmatchedunmatched
disjointdisjoint

Next up, looking at dependencies (and products).

0 comments:

Post a Comment