bjornbm / dimensional

Dimensional library variant built on Data Kinds, Closed Type Families, TypeNats (GHC 7.8+).
BSD 3-Clause "New" or "Revised" License
102 stars 15 forks source link

International Units #182

Closed idontgetoutmuch closed 7 years ago

idontgetoutmuch commented 7 years ago

I am not a biologist say I can't say I fully understand this but substances like erythropoietin (EPO) are not measured in e.g. moles but "amounts are expressed in units (U) rather than in grams or moles, because native EPO and rhEPOs are mixtures of isoforms with differing bioactivities" (https://academic.oup.com/ndt/article/24/5/1366/1887384/Efficacy-of-recombinant-erythropoietins-is-there). So I need a new unit and I can't see a way of producing one without making a PR. If I say

iUepo :: Num a => Unit NonMetric DAmountOfSubstance a
iUEpo = mkUnitZ (atom "IUEPO" "IUEPO" "International Unit EPO") 1 siUnit

then I think I can't use e.g. milli-iUs. Also I get this

*Main> 1 *~ iUepo
1.0 mol

when I would expect 1.0 IUEPO.

I think I need something like becquerel but this is defined as

becquerel :: Num a => Unit 'Metric DActivity a
becquerel = mkUnitZ (ucumMetric "Bq" "Bq" "Becquerel") 1 $ siUnit

and I don't think ucumMetric is exposed.

What's the best way of doing what I need?

bjornbm commented 7 years ago

I think you are defining your unit the way you are supposed to define units, generally speaking, although @dmcclean would have to confirm that. What you are doing is defining a new unit in terms of another unit, in this case, 1 siUnit, which happens to be 1 mol with your chosen dimension.

The behaviour

*Main> 1 *~ iUepo
1.0 mol

is correct and expected given your definitions. The Show instance for quantities always shows them with their SI unit. For example, you will see that:

*Numeric.Units.Dimensional.Prelude> 1 *~ becquerel 
1 s^-1

If you want something else than the SI Unit you can use showIn:

*Numeric.Units.Dimensional.Prelude> showIn becquerel $ 1 *~ becquerel 
"1.0 Bq"

I think that addresses half of your question. The other half is trickier because you want to use a unit does not have a direct correspondence to an SI unit (as far as I understand, but I am no biologist either). I think there are a few ways you could define your IUEPO, but which one is appropriate may depend on what you are trying to do with it.

  1. You could define it in terms of (urinary?) “mg glycoprotein” with

    iUEpo = mkUnitZ (atom "IUEPO" "IUEPO" "International Unit EPO") 0.5 (milli gram)

    but this may get messy if you are using other masses.

  2. You could define it in terms of “micromoles of cobaltous chloride” with

    iUepo :: Fractional a => Unit NonMetric DAmountOfSubstance a
    iUEpo = mkUnitQ (atom "IUEPO" "IUEPO" "International Unit EPO") 5 (micro mole)

    but this may get messy if you are using other “amounts of substance”.

  3. Or, you could just pick an SI dimension that you know you will never use for anything else (in combination with IUEPO) and use that:

    iUEpo'' :: Num a => Unit NonMetric DLuminousIntensity a
    iUEpo'' = mkUnitZ (atom "IUEPO" "IUEPO" "International Unit EPO") 1 siUnit

    This is obviously a pretty ugly hack and will not play well with other/intended uses of dimensional. But it may be a practical solution if you a doing a one-off thing in a silo. (You can of course also fork dimensional and add an eighth base dimension for IU, but don't expect us to merge a pull request.)

I hope that helps somewhat.

idontgetoutmuch commented 7 years ago

Thanks very much for your prompt and comprehensive response :) In the end I have gone for

muPerMl :: (Fractional a, Num a) => Unit 'NonMetric DConcentration a
muPerMl = (milli mole) / (milli litre)

pretending that International Units for EPO are actually moles. A typical amount for a person would be 15.0 *~ muPerMl. I don't have any reactions to worry about (at the moment at any rate) so I don't think I will trip over a problem mixing IUs with moles.

When I have time I will try to understand how biologically active molecules are measured. It seems each one has its own units and they can't be expressed in terms of SI units. But I may have misunderstood.