Id: OMTKLogic Url: http://fhir.org/guides/cdc/opioid-cds/Library/OMTKLogic Version: 3.0.0 Name: OMTKLogic Title: Opioid Management Terminology Knowledge Logic Status: active Experimental: false Type: system: http://terminology.hl7.org/CodeSystem/library-type code: logic-library Date: 2022-02-01T20:35:24+00:00 Publisher: Alphora Description: This library provides functionality for calculating Morphine Milligram Equivalents (MME) for opioid medications, as described in the CDC Opioid Prescribing Guideline. Knowledge Capability: shareable computable publishable executable Knowledge Representation Level: structured Use Context: code value display focus 182888003 Medication requested (situation) focus 82423001 Chronic pain (finding) Jurisdiction: 840 Usage: NOTE: Do not use the calculated dose in MMEs to determine dosage for converting one opioid to another—the new opioid should be lower to avoid unintentional overdose caused by incomplete cross-tolerance and individual differences in opioid pharmacokinetics. Consult the medication label. Copyright: Copyright 2019+ Centers for Disease Control and Prevention (CDC) Last Review Date: 2020-09-26 Related Artifacts: Documentation MME Calculator MME Calculator References Nelson SJ, Zeng K, Kilbourne J, Powell T, Moore R. Normalized names for clinical drugs: RxNorm at 6 years. J Am Med Inform Assoc. 2011 Jul-Aug;18(4)441-8. doi: 10.1136/amiajnl-2011-000116. Epub 2011 Apr 21. PubMed PMID: 21515544; PubMed Central PMCID: PMC3128404. [Full Text](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3128404/) https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3128404/ Schadow G, McDonald CJ. The Unified Code for Units of Measure. Regenstrief Institute, Inc. and the UCUM Organization, 2017. Version 2.1, Revision 442. http://unitsofmeasure.org http://unitsofmeasure.org Dowell D, Haegerich TM, Chou R. CDC Guideline for Prescribing Opioids for Subacute or Chronic Pain — United States, 2016. MMWR Recomm Rep 2016;65(No. RR-1):1–49. DOI: http://dx.doi.org/10.15585/mmwr.rr6501e1 http://dx.doi.org/10.15585/mmwr.rr6501e1 National Center for Injury Prevention and Control. Calculating total daily dose of opioids for safer dosage. Atlanta, GA: Centers for Disease Control and Prevention; https://www.cdc.gov/drugoverdose/pdf/calculating_total_daily_dose-a.pdf, accessed November 19th, 2020 https://www.cdc.gov/drugoverdose/pdf/calculating_total_daily_dose-a.pdf Dependencies http://fhir.org/guides/cdc/opioid-mme-r4/Library/OMTKData|3.0.0 http://fhir.org/guides/cdc/opioid-mme-r4/Library/ConversionFactors|3.0.0 http://www.nlm.nih.gov/research/umls/rxnorm http://terminology.hl7.org/CodeSystem/usage-context-type http://fhir.org/guides/cdc/opioid-mme-r4/CodeSystem/CDCMMEUsageContextCodes Parameters: Name Type Min Max In/Out ErrorLevel string 0 1 in ConversionFactorSupplementName string 0 1 in Content: text/cql library OMTKLogic version '3.0.0'#xD; #xD; /*#xD; This library provides functionality for calculating Morphine Milligram#xD; Equivalents (MME) for opioid medications, as described in the CDC Opioid#xD; Prescribing Guideline.#xD; #xD; The functionality in this library was developed based on the Java-based#xD; implementation described [here](http://build.fhir.org/ig/cqframework/opioid-cds-r4/service-documentation.html#solution-component-3-core-logic-processing-java-class),#xD; as well as the MME conversion calculation published as part of the CDC Opioid#xD; Prescribing Guideline.#xD; #xD; Note that the logic in this library (and specifically the conversion factors captured here)#xD; are based on the 2016 version of the publication available from the CDC here:#xD; https://www.cdc.gov/drugoverdose/modules/data-files.html#xD; #xD; National Center for Injury Prevention and Control. CDC compilation of benzodiazepines,#xD; muscle relaxants, stimulants, zolpidem, and opioid analgesics with oral morphine milligram#xD; equivalent conversion factors, 2018 version. Atlanta, GA: Centers for Disease Control#xD; and Prevention; 2018. Available at https://www.cdc.gov/drugoverdose/resources/data.html#xD; #xD; This version of the OMTKLogic library uses the OMTKData library as the#xD; source for drug ingredient and strength information, rather than the#xD; OMTK data source. This library has no external dependencies and so#xD; can run in an environment that supports pure CQL.#xD; #xD; NOTE: For performance, all terminology comparisons in this library use#xD; direct integer comparison of the RxNorm codes.#xD; #xD; This product uses publicly available data courtesy of the U.S. National Library of Medicine (NLM),#xD; National Institutes of Health, Department of Health and Human Services; NLM is not responsible for#xD; the product and does not endorse or recommend this or any other product.#xD; #xD; Nelson SJ, Zeng K, Kilbourne J, Powell T, Moore R. Normalized names for clinical drugs: RxNorm at 6 years.#xD; J Am Med Inform Assoc. 2011 Jul-Aug;18(4)441-8. doi: 10.1136/amiajnl-2011-000116.#xD; Epub 2011 Apr 21. PubMed PMID: 21515544; PubMed Central PMCID: PMC3128404.#xD; [Full text](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3128404/)#xD; */#xD; #xD; include OMTKData version '3.0.0' called OMTKData#xD; include ConversionFactors version '3.0.0' called ConversionFactors#xD; #xD; codesystem "RXNORM": 'http://www.nlm.nih.gov/research/umls/rxnorm'#xD; #xD; parameter ErrorLevel String default 'Warning'#xD; #xD; define function Msg(code String, errorLevel String, message String):#xD; {#xD; code: code,#xD; errorLevel: errorLevel,#xD; message: message#xD; }#xD; #xD; /*#xD; Normalizes the input units to UCUM units#xD; #xD; The values listed here are the only ones currently present in the OMTK data#xD; #xD; Based on the HL7 UCUM subset here:#xD; http://download.hl7.de/documents/ucum/ucumdata.html#xD; */#xD; define function ToUCUM(unit String):#xD; case unit#xD; when 'MG' then 'mg'#xD; when 'MG/ACTUAT' then 'mg/{actuat}'#xD; when 'MG/HR' then 'mg/h'#xD; when 'MG/ML' then 'mg/mL'#xD; else Message(null, true, 'OMTKLogic.ToUCUM.UnknownUnit', ErrorLevel, 'Unknown unit ' & unit)#xD; end#xD; #xD; define function ToUCUM_Msg(unit String):#xD; unit U#xD; let result: ToUCUM(U)#xD; return {#xD; result: result,#xD; message:#xD; if result is null then#xD; Msg('OMTKLogic.ToUCUM.UnknownUnit', ErrorLevel, 'Unknown unit' & unit)#xD; else#xD; null#xD; }#xD; #xD; /*#xD; Calculates daily frequency given frequency within a period#xD; */#xD; define function ToDaily(frequency Integer, period Quantity):#xD; case period.unit#xD; when 'h' then frequency * (24.0 / period.value)#xD; when 'min' then frequency * (24.0 / period.value) * 60#xD; when 's' then frequency * (24.0 / period.value) * 60 * 60#xD; when 'd' then frequency * (24.0 / period.value) / 24#xD; when 'wk' then frequency * (24.0 / period.value) / (24 * 7)#xD; when 'mo' then frequency * (24.0 / period.value) / (24 * 30) /* assuming 30 days in month */#xD; when 'a' then frequency * (24.0 / period.value) / (24 * 365) /* assuming 365 days in year */#xD; when 'hour' then frequency * (24.0 / period.value)#xD; when 'minute' then frequency * (24.0 / period.value) * 60#xD; when 'second' then frequency * (24.0 / period.value) * 60 * 60#xD; when 'day' then frequency * (24.0 / period.value) / 24#xD; when 'week' then frequency * (24.0 / period.value) / (24 * 7)#xD; when 'month' then frequency * (24.0 / period.value) / (24 * 30) /* assuming 30 days in month */#xD; when 'year' then frequency * (24.0 / period.value) / (24 * 365) /* assuming 365 days in year */#xD; when 'hours' then frequency * (24.0 / period.value)#xD; when 'minutes' then frequency * (24.0 / period.value) * 60#xD; when 'seconds' then frequency * (24.0 / period.value) * 60 * 60#xD; when 'days' then frequency * (24.0 / period.value) / 24#xD; when 'weeks' then frequency * (24.0 / period.value) / (24 * 7)#xD; when 'months' then frequency * (24.0 / period.value) / (24 * 30) /* assuming 30 days in month */#xD; when 'years' then frequency * (24.0 / period.value) / (24 * 365) /* assuming 365 days in year */#xD; else Message(null, true, 'OMTKLogic.ToDaily.UnknownUnit', ErrorLevel, 'Unknown unit ' & period.unit)#xD; end#xD; #xD; define function ToDaily_Msg(frequency Integer, period Quantity):#xD; frequency F#xD; let result: ToDaily(frequency, period)#xD; return {#xD; result: result,#xD; message:#xD; if result is null then#xD; Msg('OMTKLogic.ToDaily.UnknownUnit', ErrorLevel, 'Unknown unit ' & period.unit)#xD; else#xD; null#xD; }#xD; #xD; /*#xD; Returns the opioid ingredients and their strengths that#xD; make up the drug identified by the given rxNormCode as a list of tuples:#xD; #xD; List<Tuple {#xD; rxNormCode Code,#xD; doseFormCode Code,#xD; doseFormName String,#xD; ingredientCode Code,#xD; ingredientName String,#xD; strength Quantity#xD; }>#xD; */#xD; #xD; /*#xD; GetIngredients:#xD; List<{#xD; rxNormCode Code,#xD; doseFormCode Code,#xD; ingredientCode code,#xD; strength Quantity#xD; }>#xD; */#xD; define function GetIngredients(rxNormCode Code):#xD; OMTKData.DrugIngredients DI#xD; where DI.drugCode = ToInteger(rxNormCode.code)#xD; return {#xD; rxNormCode: Code { code: ToString(DI.drugCode), system: 'http://www.nlm.nih.gov/research/umls/rxnorm', display: DI.drugName },#xD; doseFormCode: Code { code: ToString(DI.doseFormCode), system: 'http://www.nlm.nih.gov/research/umls/rxnorm', display: DI.doseFormName },#xD; ingredientCode: Code { code: ToString(DI.ingredientCode), system: 'http://www.nlm.nih.gov/research/umls/rxnorm', display: DI.ingredientName },#xD; strength: Quantity {#xD; value: DI.strengthValue,#xD; unit: ToUCUM(DI.strengthUnit)#xD; }#xD; }#xD; #xD; /*#xD; Returns the first RxNorm code in the given concept that matches a drug code#xD; specified in OMTKData.#xD; */#xD; define function GetMedicationCode(concept Concept):#xD; First(#xD; ((concept.codes) C#xD; where C.system = 'http://www.nlm.nih.gov/research/umls/rxnorm'#xD; return singleton from (#xD; OMTKData.DrugIngredients DI#xD; where DI.drugCode = ToInteger(C.code)#xD; return Code {#xD; code: ToString(DI.drugCode),#xD; system: 'http://www.nlm.nih.gov/research/umls/rxnorm',#xD; display: DI.drugName#xD; }#xD; )#xD; ) X#xD; where X is not null#xD; )#xD; #xD; /*#xD; Returns the display of the given concept, if present, otherwise, looks up medication#xD; names for any RxNormCodes within the concept using the GetMedicationName function#xD; */#xD; define function GetMedicationConceptName(concept Concept):#xD; if concept.display is null then#xD; First(#xD; (concept.codes) C#xD; where C.system = 'http://www.nlm.nih.gov/research/umls/rxnorm'#xD; return GetMedicationName(C)#xD; )#xD; else#xD; concept.display#xD; #xD; /*#xD; Returns the display of the given code, if present, otherwise looks it up from the OMTK data#xD; */#xD; define function GetMedicationName(rxNormCode Code):#xD; if rxNormCode.display is null then#xD; singleton from (#xD; OMTKData.DrugIngredients DI#xD; where DI.drugCode = ToInteger(rxNormCode.code)#xD; return DI.drugName#xD; )#xD; else rxNormCode.display#xD; #xD; /*#xD; Returns the display of the given ingredient, if present, otherwise looks it up from the OMTK data#xD; */#xD; define function GetIngredientName(ingredientCode Code):#xD; if ingredientCode.display is null then#xD; singleton from (#xD; OMTKData.DrugIngredients DI#xD; where DI.ingredientCode = ToInteger(ingredientCode.code)#xD; return DI.ingredientName#xD; )#xD; else ingredientCode.display#xD; #xD; /*#xD; Returns the display of the given dose form, if present, otherwise looks it up from the OMTK data#xD; */#xD; define function GetDoseFormName(doseFormCode Code):#xD; if doseFormCode.display is null then#xD; singleton from (#xD; OMTKData.DrugIngredients DI#xD; where DI.doseFormCode = ToInteger(doseFormCode.code)#xD; return DI.doseFormName#xD; )#xD; else doseFormCode.display#xD; #xD; /*#xD; Removes the last per argument from a unit#xD; NOTE: Rewrote to not use LastPositionOf, since that function is not implemented in the JS engine#xD; https://github.com/cqframework/cql-execution/issues/147#xD; */#xD; define function StripPer(unit String):#xD; unit X#xD; let split: Split(unit, '/'),#xD; splitCount: Count(split)#xD; return#xD; if splitCount > 1 then#xD; Substring(unit, 0, Length(unit) - Length(split[splitCount - 1]) - 1)#xD; else#xD; unit#xD; #xD; /*#xD; define function StripPer(unit String):#xD; if LastPositionOf('/', unit) >= 0#xD; then Substring(unit, 0, LastPositionOf('/', unit))#xD; else unit#xD; */#xD; #xD; /*#xD; Calculates daily dose for a specific ingredient based on the ingredient strength,#xD; dose form, dose quantity, and daily frequency.#xD; In addition, returns a textual description of the daily dose.#xD; Tuple { result: Quantity, description: String }#xD; */#xD; define function GetDailyDose(ingredientCode Code, strength Quantity, doseFormCode Code, doseQuantity Quantity, dosesPerDay Decimal):#xD; case#xD; when dosesPerDay is null or doseQuantity is null or strength is null or strength.value is null or strength.unit is null then#xD; {#xD; result: null as Quantity,#xD; description: 'Missing doses per day, dose quantity, and/or strength'#xD; }#xD; /* if patch --> daily dose = dose value (e.g, number patches with doseQuantity unit = "patch") * per-hour strength */#xD; when ToInteger(doseFormCode.code) = 316987 then#xD; /* buprenorphine or fentanyl patch */#xD; if ToInteger(ingredientCode.code) in { 1819, 4337 } then#xD; (Quantity { value: dosesPerDay * doseQuantity.value * strength.value, unit: strength.unit }) dailyDose#xD; return {#xD; result: dailyDose,#xD; description: GetIngredientName(ingredientCode) & ' patch: ' & ToString(doseQuantity.value) & ' * ' & ToString(dosesPerDay) & '/d * ' & ToString(strength) & ' = ' + ToString(dailyDose)#xD; }#xD; else#xD; {#xD; result: null as Quantity,#xD; description: 'Unknown patch ingredient: ' & ingredientCode.code & ':' & ingredientCode.display#xD; }#xD; #xD; /* if dose unit in actual mass units (mg or ug -- when it's a single med) --> daily dose = numTimesPerDay * dose */#xD; when doseQuantity.unit in { 'mg', 'ug' } then#xD; (Quantity { value: dosesPerDay * doseQuantity.value, unit: doseQuantity.unit }) dailyDose#xD; return {#xD; result: dailyDose,#xD; description: GetIngredientName(ingredientCode) + ' ' + GetDoseFormName(doseFormCode) + ': ' + ToString(dosesPerDay) + '/d * ' + ToString(doseQuantity) + ' = ' + ToString(dailyDose)#xD; }#xD; #xD; /* if doseQuantity is in actual volume units (mL) --> daily dose = numTimesPerDay * dose * strength */#xD; when doseQuantity.unit = 'mL' and (PositionOf('/mL', strength.unit) = Length(strength.unit) - 3) then#xD; (Quantity { value: dosesPerDay * doseQuantity.value * strength.value, unit: StripPer(strength.unit) }) dailyDose#xD; return {#xD; result: dailyDose,#xD; description: GetIngredientName(ingredientCode) + ' ' + GetDoseFormName(doseFormCode) + ': ' + ToString(dosesPerDay) + '/d * ' + ToString(doseQuantity) + ' * ' & ToString(strength) & ' = ' + ToString(dailyDose)#xD; }#xD; #xD; /* if doseQuantity is not in actual units (e.g., 1 tab, 1 spray -- when it's a combo med with a unit of tablet, or it's mg/actuat) --> daily dose = numTimesPerDay * dose value * strength value */#xD; else#xD; (Quantity { value: dosesPerDay * doseQuantity.value * strength.value, unit: StripPer(strength.unit) }) dailyDose#xD; return {#xD; result: dailyDose,#xD; description: GetIngredientName(ingredientCode) + ' ' + GetDoseFormName(doseFormCode) + ': ' + ToString(dosesPerDay) + '/d * ' + ToString(doseQuantity) + ' * ' + ToString(strength) + ' = ' + ToString(dailyDose)#xD; }#xD; end#xD; #xD; /*#xD; Calculates MMEs for the given input prescription information and returns it#xD; as a list of tuples:#xD; #xD; List<Tuple {#xD; rxNormCode Code,#xD; doseFormCode Code,#xD; doseQuantity Quantity,#xD; dosesPerDay Decimal,#xD; ingredientCode Code,#xD; strength Quantity,#xD; dailyDose Quantity,#xD; dailyDoseDescription String,#xD; conversionFactor Decimal,#xD; mme Quantity#xD; }>#xD; */#xD; define function CalculateMMEs(medications List<Tuple { rxNormCode Code, doseQuantity Quantity, dosesPerDay Decimal }>):#xD; Flatten(#xD; medications M#xD; let Ingredients: GetIngredients(M.rxNormCode)#xD; return#xD; Ingredients I#xD; let#xD; adjustedDoseQuantity: M.doseQuantity,#xD; dailyDose: GetDailyDose(I.ingredientCode, I.strength, I.doseFormCode, adjustedDoseQuantity, M.dosesPerDay),#xD; factor: ConversionFactors.GetConversionFactor(I.ingredientCode, dailyDose.result, I.doseFormCode, M.dosesPerDay)#xD; return {#xD; rxNormCode: M.rxNormCode,#xD; doseFormCode: I.doseFormCode,#xD; doseQuantity: adjustedDoseQuantity,#xD; dosesPerDay: M.dosesPerDay,#xD; ingredientCode: I.ingredientCode,#xD; strength: I.strength,#xD; dailyDose: dailyDose.result,#xD; dailyDoseDescription: dailyDose.description & (' * factor: ' + Coalesce(ToString(factor), 'No conversion factor available')),#xD; conversionFactor: factor,#xD; mme: Quantity(#xD; Round(dailyDose.result.value * factor, 1),#xD; '{MME}/d'#xD; )#xD; }#xD; )#xD; #xD; define function Quantity(value Decimal, unit String):#xD; if value is not null then#xD; Quantity { value: value, unit: unit }#xD; else#xD; null#xD; Content: application/elm+xml Encoded data (391084 characters) Content: application/elm+json Encoded data (2041164 characters)
Opioid Management Terminology Knowledge Logic
Centers for Disease Control and Prevention (CDC)
This library provides functionality for calculating Morphine Milligram Equivalents (MME) for opioid medications, as described in the CDC Opioid Prescribing Guideline.
NOTE: Do not use the calculated dose in MMEs to determine dosage for converting one opioid to another—the new opioid should be lower to avoid unintentional overdose caused by incomplete cross-tolerance and individual differences in opioid pharmacokinetics. Consult the medication label.
Copyright 2019+ Centers for Disease Control and Prevention (CDC)
This product uses publicly available data courtesy of the U.S. National Library of Medicine (NLM), National Institutes of Health, Department of Health and Human Services; NLM is not responsible for the product and does not endorse or recommend this or any other product.
Nelson SJ, Zeng K, Kilbourne J, Powell T, Moore R. Normalized names for clinical drugs: RxNorm at 6 years. J Am Med Inform Assoc. 2011 Jul-Aug;18(4)441-8. doi: 10.1136/amiajnl-2011-000116. Epub 2011 Apr 21. PubMed PMID: 21515544; PubMed Central PMCID: PMC3128404. [Full Text](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3128404/)
This product includes all or a portion of the UCUM table, UCUM codes, and UCUM definitions or is derived from it, subject to a license from Regenstrief Institute, Inc. and The UCUM Organization. Your use of the UCUM table, UCUM codes, UCUM definitions also is subject to this license, a copy of which is available at http://unitsofmeasure.org The current complete UCUM table, UCUM Specification are available for download at http://unitsofmeasure.org The UCUM table and UCUM codes are copyright © 1995-2009, Regenstrief Institute, Inc. and the Unified Codes for Units of Measures (UCUM) Organization. All rights reserved.
Schadow G, McDonald CJ. The Unified Code for Units of Measure. Regenstrief Institute, Inc. and the UCUM Organization, 2017. Version 2.1, Revision 442. http://unitsofmeasure.org
Centers For Disease Control and Prevention (CDC) Guideline for Prescribing Opioids for Subacute or Chronic Pain — United States, 2016
Dowell D, Haegerich TM, Chou R. CDC Guideline for Prescribing Opioids for Subacute or Chronic Pain — United States, 2016. MMWR Recomm Rep 2016;65(No. RR-1):1–49. DOI: http://dx.doi.org/10.15585/mmwr.rr6501e1
Calculating total daily dose of opioids for safer dosage
National Center for Injury Prevention and Control. Calculating total daily dose of opioids for safer dosage.
National Center for Injury Prevention and Control. Calculating total daily dose of opioids for safer dosage. Atlanta, GA: Centers for Disease Control and Prevention; https://www.cdc.gov/drugoverdose/pdf/calculating_total_daily_dose-a.pdf, accessed November 19th, 2020