Stream: cimpl
Topic: Fixed Value Output
Kurt Allen (Aug 09 2019 at 15:46):
Hey @Chris Moesel
I took a look at the output for fixed values, specifically fixing Observation.Code.
Entry: BreastAbnormality
Parent: Observation
Parent: Observation
Code = BREASTABNORMALITYCODESCS#BreastAbnormalityObservationCode
It appears that to fix the value, the cli creates a slice on Observation.Code and then fixes the value in that slice. If I understand fhir, this means that there is a slice of Observation.Code that has a fixed value, but there can be other slices as well with different values.
I hand modified the above example, and put in what my (limited) understanding of the fixed value is. It passed validation. This one limits Code to a single value and fixes that value.
Should this be what the cli outputs?
The reason this came up is that I am writing tooling that will output c# classes for a profile, constraining the properties to be only what is allowed in the constrained profile. Reading the cli output, unless I misunderstand, would cause me to output two properties for Code, the slice that has a fixed value, and another slice that can contain any value (the original slice).
I attached the two files
cli output -> Fixed-cli.json
hand modified -> Fixed-modified.json output)
Please let me know your thoughts.
Best,
Kurt A.
Kurt Allen (Aug 14 2019 at 23:35):
Hey @Chris Moesel and @Mark Kramer
I am seeing some issues that don't look quite perfect in the way slicing is done.
a) I am seeing a 'slicing' statement on the slice (I think you only need one on the base definition).
I think this element can be removed (or at least the slicing section can be).
From the fhir documentation, it appears that adding the slicing item will de facto define a new slice, which we don't want to do.
{
"id": "Observation.component:breastrad-AbnormalityDensity.code.coding",
"path": "Observation.component.code.coding",
"slicing": {
"id": "6",
"discriminator": [
{
"type": "value",
"path": "code"
}
],
...
},
b) The fixed statements of the component slices have the same problem as what I mentioned in my previous post. i.e. the fixed value is set on a slice of the slice, not on the slice itself.
Kurt Allen (Aug 15 2019 at 12:53):
@Chris Moesel and @Mark Kramer I am not sure if this is a bug or just messy, but when a CodeableConcept is set to a fixed value, the cli generates a separate fixed StructuredDefinition.ElementDefinition for each sub part of the CodeableConcept (system, code, text, etc). When I populate a structured definition c# item manually and write it out with a fixed value, I get something much cleaner - one single element definition for all the parts.
"fixedCodeableConcept": {
"coding": [
{
"system": "codeSystem",
"code": "codeCode",
"display": "codeDisplay"
}
],
"text": "codeText"
},
Kurt Allen (Aug 15 2019 at 13:34):
@Chris Moesel and @Mark Kramer
When slicing Observation.Component, cli setd the discriminator path to code.coding.code. This should probably be just 'code'.
Chris Moesel (Aug 19 2019 at 14:37):
Hey @Kurt Allen -- sorry for the late responses; both @Mark Kramer and I were on vacation.
It appears that to fix the value, the cli creates a slice on Observation.Code and then fixes the value in that slice. If I understand fhir, this means that there is a slice of Observation.Code that has a fixed value, but there can be other slices as well with different values.
Observation.code
is 1..1
, so we don't slice that -- we slice Observation.code.coding
. You are correct in your interpretation -- we are basically saying that the CodeableConcept must have one coding with the fixed value, but other codings are allowed as well. This allows the data to contain other synonyms, proprietary codes, or perhaps the "user-selected" code, etc -- while still requiring the one standards-based code.
Your modified version is also a valid approach, but would not allow the data to contain any additional codes; so in cases where a code was translated to the standards-based code, the "original" code would have to be dropped from the data entirely in order to be compliant with the profile.
Chris Moesel (Aug 19 2019 at 14:43):
I am seeing a 'slicing' statement on the slice (I think you only need one on the base definition)...
...
"id": "Observation.component:breastrad-AbnormalityDensity.code.coding",
...
There are indeed two slices here and I believe that both are necessary if you want to follow the current approach of fixing codes on CodeableConcepts as I described above. The first slice (on Observation.component
) identifies the slice for the breastrad-AbnormalityDensity
component. The entire structure of an Observaton.component
backbone element is part of this top-level slice.
Within that Observation.component
slice, we want to slice the Observation.component.code.coding
in order to indicate that _one_ of the codings must be the fixed code, while still allowing other codings (for synonyms, original code, etc). If you're fine with not allowing any additional codes over the required standardized code, then that slicing could be removed. But for CIMPL, we felt it was important to allow the data to still contain those alternate/original codes as well.
Chris Moesel (Aug 19 2019 at 14:45):
I should note, however, that based on recent discussions, we plan to change the CLI to use pattern[x]
for fixing codings on CodeableConcept instead. This approach is semantically the same (require a specific coding but also allow other codings to co-exist), but doesn't need a slice to represent it.
Chris Moesel (Aug 19 2019 at 14:48):
when a CodeableConcept is set to a fixed value, the cli generates a separate fixed StructuredDefinition.ElementDefinition for each sub part of the CodeableConcept (system, code, text, etc).
I believe that we're required to redefine all of the sub-elements in the snapshot when we create a slice. If you look at other profiles (generated with other tools), I believe you'll find the same.
Chris Moesel (Aug 19 2019 at 14:51):
When slicing Observation.Component, cli setd the discriminator path to code.coding.code. This should probably be just 'code'.
IIRC, the FHIR spec requires the discriminator path to terminate at the element that contains the fixed[x]
or pattern[x]
declaration -- which is why we need to do code.coding.code
rather than just code
. Technically, we should probaly be using a dual discriminator that looks at code.coding.code
and code.coding.system
, but there were some technical limitations that made this difficult.
When we move to using pattern[x]
instead of fixed[x]
, then the pattern will be at the code
level, and the discriminator can point to code
instead of code.coding.code
-- but we're not there yet.
Kurt Allen (Aug 19 2019 at 14:59):
Do you think 'pattern' slice will be implemented before the end of the year? I have concerns about releasing the next round of the ballot (which is not for comment only...) with the current setup.
I am writing a profile c# class generator from the output profiles generated, and the current output is really hard to parse properly...
Chris Moesel (Aug 19 2019 at 15:39):
Priority is @Mark Kramer's call. Mark, what say you? By end of the year?
Mark Kramer (Aug 19 2019 at 15:44):
It is a priority. Lloyd may even start enforcing this in the validation
Chris Moesel (Aug 19 2019 at 16:06):
(deleted)
Chris Moesel (Aug 19 2019 at 16:16):
Lloyd may even start enforcing this in the validation
That's interesting considering the FHIR Vital Signs profiles and the US Core profiles (even the R4 ones) use the slicing approach (not the pattern approach). If we profile a vital signs profile or US Core profile, we can't change those definitions. Would we fail validation?
For a little history: one of the reasons we avoided pattern was because the DSTU2 spec strongly recommends against using a pattern within a slice. This meant we shouldn't use a pattern to differentiate slices of Observation.component -- leaving us with slicing + fixed[x] as the only viable option. From the DSTU2 spec:
Note: At present, only a fixed value or a required value set should be used for slicing; using ElementDefinition.pattern[x]) is not recommended as a basis for slicing while issues related to this are investigated during the DSTU period.
All that said, now that Grahame has updated how he renders patterns in the IG publisher, I'm fine with making the switch and agree that it should take priority.
Kurt Allen (Aug 19 2019 at 16:31):
@Chris Moesel
Welcome back!
Some thoughts: sorry if this is irritating anal :-)
1) so when I have the following CIMPL code,
* Entry: BreastAbnormality
Parent: Observation
Code = BREASTABNORMALITYCODESCS#BreastAbnormalityObservationCode
*
am I saying the Code is exactly and only
*BREASTABNORMALITYCODESCS#BreastAbnormalityObservationCode
*or am I saying that Code contains
BREASTABNORMALITYCODESCS#BreastAbnormalityObservationCode
and may or may not contain other codes.
I wonder if the 'fixed' statement needs to be a bit more nuanced allowing either to be specified. I can see cases for both.
2) from https://www.hl7.org/fhir/profiling.html * Each slice must use the element definition for the element(s) in the discriminator(s) to ensure that the slices are clearly differentiated by assigning an appropriate value domain, depending on the discriminator type. If the type is value, or pattern, then the element definition must use either:
ElementDefinition.fixed[x], or ElementDefinition.pattern[x], or
*
To me that suggests that if the discriminator is
"discriminator": [
{
"type": "value",
"path": "code"
}
then code must be fixed; Is this the same as fixing code.code and code.system separately?
3) I think you are using the "id" ':sliceName' format to define if an element is in a slice. The Fhir spec says that this format is expected, but some articles from Firely have said that you can not count on it for parsing; i.e. is is a really good idea but not required.
If that is the case, then do we need to add sliceName entries to all child entries?
i.e.
- {
"id": "Observation.component:breastrad-AbnormalityType",
"path": "Observation.component",
"sliceName": "breastrad-AbnormalityType",
...
},
{
"id": "Observation.component:breastrad-AbnormalityType.code",
"path": "Observation.component.code"
added> "sliceName": "breastrad-AbnormalityType",
}, *
Also, is the following a reslice
-
{
"id": "Observation.component:breastrad-AbnormalityType.code.coding:Fixed_AbnormalityType",
"path": "Observation.component.code.coding",
"sliceName": "Fixed_AbnormalityType",
}, * and if so should sliceName be modified: -
{
"id": "Observation.component:breastrad-AbnormalityType.code.coding:Fixed_AbnormalityType",
"path": "Observation.component.code.coding",
"sliceName": "breastrad-AbnormalityType/Fixed_AbnormalityType",
}, *
Chris Moesel (Aug 19 2019 at 17:13):
1) so when I have the following CIMPL code,
Entry: BreastAbnormality
Parent: Observation
Code = BREASTABNORMALITYCODESCS#BreastAbnormalityObservationCode
am I saying the Code is exactly and only BREASTABNORMALITYCODESCS#BreastAbnormalityObservationCode
or am I saying that Code contains BREASTABNORMALITYCODESCS#BreastAbnormalityObservationCode
and may or may not contain other codes.
It's kind of nuanced, but you're saying that the Code
element value is the BreastAbnormalityObservationCode concept. At this point, we're in the conceptual space, not the physical representation. When we map to FHIR, we need to indicate the physical representation of that idea, and it will depend on what you're binding to:
- If the target FHIR type is a primitive
code
(which is essentially just a string), then there is no other choice but to say that it must be exactlyBreastAbnormalityObservationCode
(without a code system) - If the target FHIR type is a
Coding
(which has structure forsystem
but is still essentially singular), then there is no other choice but to say it must have only the exact codeBreastAbnormalityObservationCode
and exact systemBREASTABNORMALITYCODESCS
- If the target FHIR type is a
CodeableConcept
, however, then then itscoding
is a multiple (0..*
). While we could choose to constrain it to1..1
and say it must have a single coding that has exact codeBreastAbnormalityObservationCode
and exact systemBREASTABNORMALITYCODESCS
, we think there is little value in this rigidity. The FHIR team chose to use aCodeableConcept
instead of aCoding
because they felt that it was useful to allow secondary codes, so we choose to keep it that way. At the end of the day it should still be a single concept; it just might have multiple codes representing that concept-- and as long as one is what we want, then we're good.
Perhaps we could allow authors to specify which flavor they want, but I'm not sure exactly where/how that should be done since it is really only a question when you get down to FHIR (not at the conceptual model level). What is the use case where you wouldn't want to allow extraneous codings within a single CodeableConcept?
I should also mention, once you get down to Coding
and CodeableConcept
you're not fixing the Coding or CodeableConcept as a whole -- because you shouldn't fix the display
or text
(it's not considered good practice to do so); rather you're fixing just parts of the Coding
and CodeableConcept
as appropriate.
Chris Moesel (Aug 19 2019 at 17:23):
2) ...
To me that suggests that if the discriminator is
"discriminator": [
{
"type": "value",
"path": "code"
}
then code must be fixed; Is this the same as fixing code.code and code.system separately?
If the discriminator has path "code"
then the code
element must have a fixed[x]
or pattern[x]
. Let's say that the path "code"
has type CodeableConcept
. In this case, it must have a fixedCodeableConcept
declaration or patternCodeableConcept
declaration. In 99.99% of cases you would never want fixedCodeableConcept
because it is too rigid -- it is saying that everything must be exactly as declared (each coding
and its code
, system
, version
, and display
, as well as the text
). Any variation in something even as trivial as text
or display
would fail validation. For this reason, only patternCodeableConcept
would be recommended for a discriminator with path "code"
.
If, however, you are fixing values at the code.coding.code
and code.coding.system
paths, however, then those are the paths you must use in the discriminator. Even though they are using fixed[x], they are not too rigid because they are only fixing code and system (not display, text, etc).
Chris Moesel (Aug 19 2019 at 17:41):
3) 3) I think you are using the "id" ':sliceName' format to define if an element is in a slice. The Fhir spec says that this format is expected, but some articles from Firely have said that you can not count on it for parsing; i.e. is is a really good idea but not required.
FHIR R4 actually does require this format usingSHALL
: http://hl7.org/fhir/elementdefinition.html#id
That said, versions prior to R4 did not have this language (and, in fact, DSTU2 prohibits some of those characters from being in the id!). So the Firely guidance is wise for versions prior to R4. But... It should still be possible to parse the definitions even without it; once a slice is started (with sliceName
), you know you're out of the slice when you (a) encounter an element path that is the same as the root slice path, or (b) encounter an element path that does not start with the root slice path.
I don't believe you are allowed to repeat the sliceName
on each component of the slice.
Chris Moesel (Aug 19 2019 at 18:17):
Also, is the following a reslice
{
"id": "Observation.component:breastrad-AbnormalityType.code.coding:Fixed_AbnormalityType",
"path": "Observation.component.code.coding",
"sliceName": "Fixed_AbnormalityType",
}
No, I don't think so. Based on my understanding, it is a nested slice, but not a re-slice. I believe a re-slice is when a profile is based on another profile, and the child profile wants to divide a parent profile's slices into more specific slices. In this case, the re-slicing happens at the same path as the original slice (versus a subpath, which would be a nested slice). The profiling doc uses an example where a parent profile might define an "example" slice that can contain examples, but the child profile wants to define a more specific "example1" slice from the original "example" slice.
Kurt Allen (Aug 21 2019 at 14:10):
@Chris Moesel
Thanks for your info - your comments have helped me to understand slicing. :-)
Last updated: Apr 12 2022 at 19:14 UTC