{-# LANGUAGE DataKinds #-} {-# LANGUAGE NumDecimals #-} {-# LANGUAGE RankNTypes #-} {- | Copyright : Copyright (C) 2006-2015 Bjorn Buckwalter License : BSD3 Maintainer : bjorn@buckwalter.se Stability : Stable Portability: GHC only = Summary This module defines the SI prefixes, the SI base units and the SI derived units. It also defines the units outside of the SI that are accepted for use with the SI. Any chapters, sections or tables referenced are from <#note1 [1]> unless otherwise specified. = References 1. #note1# http://physics.nist.gov/Pubs/SP811/ 2. #note2# http://en.wikipedia.org/wiki/Minute_of_arc 3. #note3# http://en.wikipedia.org/wiki/Astronomical_unit -} module Numeric.Units.Dimensional.SIUnits ( -- * SI Base Units -- $base-units metre, meter, gram, second, ampere, kelvin, mole, candela, -- * SI Derived Units -- $derived-units radian, steradian, hertz, newton, pascal, joule, watt, coulomb, volt, farad, ohm, siemens, weber, tesla, henry, lumen, lux, -- ** Celsius Temperature -- $celsius degreeCelsius, fromDegreeCelsiusAbsolute, toDegreeCelsiusAbsolute, -- ** Units Admitted for Reasons of Safeguarding Human Health -- $health becquerel, gray, sievert, katal, -- * Units Accepted for Use with the SI -- $accepted-units minute, hour, day, hectare, litre, liter, tonne, metricTon, -- ** Units of Plane Angle -- $arc-units degree, arcminute, arcsecond, -- $arc-units-alternate degreeOfArc, minuteOfArc, secondOfArc, -- ** Units Formerly Defined By Experiment -- $values-obtained-experimentally astronomicalUnit, -- * SI Prefixes -- $multiples deka, deca, hecto, kilo, mega, giga, tera, peta, exa, zetta, yotta, -- $submultiples deci, centi, milli, micro, nano, pico, femto, atto, zepto, yocto ) where import Numeric.Units.Dimensional import Numeric.Units.Dimensional.Quantities import Numeric.Units.Dimensional.UnitNames (PrefixName, applyPrefix, nMeter, nGram, nSecond, nAmpere, nKelvin, nMole, nCandela) import qualified Numeric.Units.Dimensional.UnitNames as N import Numeric.Units.Dimensional.UnitNames.Internal (ucum, ucumMetric) import Numeric.NumType.DK.Integers ( pos3 ) import Prelude ( ($), Num, Fractional, Floating, Integer, Rational, recip) import qualified Prelude {- $multiples Prefixes are used to form decimal multiples and submultiples of SI Units as described in section 4.4. We will define the SI prefixes in terms of the 'prefix' function which applies a scale factor to a unit. By defining SI prefixes as functions applied to a 'Unit' we satisfy section 6.2.6 "Unacceptability of stand-alone prefixes". We define all SI prefixes from Table 5. Multiples first. -} applyMultiple :: (Num a) => PrefixName -> Integer -> Unit 'Metric d a -> Unit 'NonMetric d a applyMultiple p x u = mkUnitZ (applyPrefix p (name u)) x u deka, deca, hecto, kilo, mega, giga, tera, peta, exa, zetta, yotta :: Num a => Unit 'Metric d a -> Unit 'NonMetric d a deka = applyMultiple N.deka 10 -- International English. deca = deka -- American English. hecto = applyMultiple N.hecto 100 kilo = applyMultiple N.kilo 1e3 mega = applyMultiple N.mega 1e6 giga = applyMultiple N.giga 1e9 tera = applyMultiple N.tera 1e12 peta = applyMultiple N.peta 1e15 exa = applyMultiple N.exa 1e18 zetta = applyMultiple N.zetta 1e21 yotta = applyMultiple N.yotta 1e24 {- $submultiples Then the submultiples. -} applySubmultiple :: (Fractional a) => PrefixName -> Rational -> Unit 'Metric d a -> Unit 'NonMetric d a applySubmultiple p x u = mkUnitQ (applyPrefix p (name u)) x u deci, centi, milli, micro, nano, pico, femto, atto, zepto, yocto :: Fractional a => Unit 'Metric d a -> Unit 'NonMetric d a deci = applySubmultiple N.deci 0.1 centi = applySubmultiple N.centi 0.01 milli = applySubmultiple N.milli 1e-3 micro = applySubmultiple N.micro 1e-6 nano = applySubmultiple N.nano 1e-9 pico = applySubmultiple N.pico 1e-12 femto = applySubmultiple N.femto 1e-15 atto = applySubmultiple N.atto 1e-18 zepto = applySubmultiple N.zepto 1e-21 yocto = applySubmultiple N.yocto 1e-24 {- $base-units These are the base units from section 4.1. To avoid a myriad of one-letter functions that would doubtlessly cause clashes and frustration in users' code we spell out all unit names in full, as we did for prefixes. We also elect to spell the unit names in singular form, as allowed by section 9.7 "Other spelling conventions". We define the SI base units in the order of table 1. -} metre, meter :: Num a => Unit 'Metric DLength a metre = mkUnitZ nMeter 1 siUnit -- International English. meter = metre -- American English. {- For mass the SI base unit is kilogram. For sensible prefixes we define gram here (see section 6.2.7 "Prefixes and the kilogram"). The drawback is that we are forced to use 'Fractional'. -} gram :: Fractional a => Unit 'Metric DMass a gram = mkUnitQ nGram 1e-3 siUnit second :: Num a => Unit 'Metric DTime a second = mkUnitZ nSecond 1 siUnit ampere :: Num a => Unit 'Metric DElectricCurrent a ampere = mkUnitZ nAmpere 1 siUnit kelvin :: Num a => Unit 'Metric DThermodynamicTemperature a kelvin = mkUnitZ nKelvin 1 siUnit mole :: Num a => Unit 'Metric DAmountOfSubstance a mole = mkUnitZ nMole 1 siUnit candela :: Num a => Unit 'Metric DLuminousIntensity a candela = mkUnitZ nCandela 1 siUnit {- $derived-units From Table 3, SI derived units with special names and symbols, including the radian and steradian. -} radian :: Num a => Unit 'Metric DPlaneAngle a radian = mkUnitZ (ucumMetric "rad" "rad" "radian") 1 siUnit -- meter * meter ^ neg1 steradian :: Num a => Unit 'Metric DSolidAngle a steradian = mkUnitZ (ucumMetric "sr" "sr" "steradian") 1 siUnit -- meter ^ pos2 * meter ^ neg2 hertz :: Num a => Unit 'Metric DFrequency a hertz = mkUnitZ (ucumMetric "Hz" "Hz" "Hertz") 1 $ siUnit newton :: Num a => Unit 'Metric DForce a newton = mkUnitZ (ucumMetric "N" "N" "Newton") 1 $ siUnit pascal :: Num a => Unit 'Metric DPressure a pascal = mkUnitZ (ucumMetric "Pa" "Pa" "Pascal") 1 $ siUnit joule :: Num a => Unit 'Metric DEnergy a joule = mkUnitZ (ucumMetric "J" "J" "Joule") 1 $ siUnit watt :: Num a => Unit 'Metric DPower a watt = mkUnitZ (ucumMetric "W" "W" "Watt") 1 $ siUnit coulomb :: Num a => Unit 'Metric DElectricCharge a coulomb = mkUnitZ (ucumMetric "C" "C" "Coulomb") 1 $ siUnit volt :: Num a => Unit 'Metric DElectricPotential a volt = mkUnitZ (ucumMetric "V" "V" "Volt") 1 $ siUnit farad :: Num a => Unit 'Metric DCapacitance a farad = mkUnitZ (ucumMetric "F" "F" "Farad") 1 $ siUnit ohm :: Num a => Unit 'Metric DElectricResistance a ohm = mkUnitZ (ucumMetric "Ohm" "Ω" "Ohm") 1 $ siUnit siemens :: Num a => Unit 'Metric DElectricConductance a siemens = mkUnitZ (ucumMetric "S" "S" "Siemens") 1 $ siUnit weber :: Num a => Unit 'Metric DMagneticFlux a weber = mkUnitZ (ucumMetric "Wb" "Wb" "Weber") 1 $ siUnit tesla :: Num a => Unit 'Metric DMagneticFluxDensity a tesla = mkUnitZ (ucumMetric "T" "T" "Tesla") 1 $ siUnit henry :: Num a => Unit 'Metric DInductance a henry = mkUnitZ (ucumMetric "H" "H" "Henry") 1 $ siUnit {- We defer the definition of Celcius temperature to another section (would appear here if we stricly followed table 3). -} lumen :: Num a => Unit 'Metric DLuminousFlux a lumen = mkUnitZ (ucumMetric "lm" "lm" "lumen") 1 $ siUnit lux :: Num a => Unit 'Metric DIlluminance a lux = mkUnitZ (ucumMetric "lx" "lx" "lux") 1 $ siUnit {- $celsius A problematic area is units which increase proportionally to the base SI units but cross zero at a different point. An example would be degrees Celsius (see section 4.2.1.1). The author feels that it is appropriate to define a unit for use with relative quantities (taking only into account the proportionality) and complement the unit with functions for converting absolute values. The function 'fromDegreeCelsiusAbsolute' should be used in lieu of "*~ degreeCelsius" when working with absolute temperatures. Similarily, 'toDegreeCelsiusAbsolute' should be used in lieu of "/~ degreeCelsius" when working with absolute temperatures. -} degreeCelsius :: Num a => Unit 'Metric DCelsiusTemperature a degreeCelsius = kelvin fromDegreeCelsiusAbsolute :: Floating a => a -> ThermodynamicTemperature a fromDegreeCelsiusAbsolute x = x *~ degreeCelsius + 273.15 *~ degreeCelsius toDegreeCelsiusAbsolute :: Floating a => ThermodynamicTemperature a -> a toDegreeCelsiusAbsolute x = (x - 273.15 *~ degreeCelsius) /~ degreeCelsius {- $health The last units from Table 3 are SI derived units with special names and symbols admitted for reasons of safeguarding human health. -} becquerel :: Num a => Unit 'Metric DActivity a becquerel = mkUnitZ (ucumMetric "Bq" "Bq" "Becquerel") 1 $ siUnit gray :: Num a => Unit 'Metric DAbsorbedDose a gray = mkUnitZ (ucumMetric "Gy" "Gy" "Gray") 1 $ siUnit sievert :: Num a => Unit 'Metric DDoseEquivalent a sievert = mkUnitZ (ucumMetric "Sv" "Sv" "Sievert") 1 $ siUnit katal :: Num a => Unit 'Metric DCatalyticActivity a katal = mkUnitZ (ucumMetric "kat" "kat" "katal") 1 $ siUnit {- $accepted-units There are several units that are not strictly part of the SI but are either permanently or temporarily accepted for use with the SI. We define the permanently accepted ones in this module. From Table 6, Units accepted for use with the SI. We start with time which we grant exclusive rights to 'minute' and 'second'. -} minute, hour, day :: Num a => Unit 'NonMetric DTime a minute = mkUnitZ (ucum "min" "min" "minute") 60 $ second hour = mkUnitZ (ucum "h" "h" "hour") 60 $ minute day = mkUnitZ (ucum "d" "d" "day") 24 $ hour -- Mean solar day. {- $arc-units Since 'minute' and 'second' are already in use for time we use 'arcminute' and 'arcsecond' <#note2 [2]> for plane angle instead. -} degree, arcminute, arcsecond :: Floating a => Unit 'NonMetric DPlaneAngle a degree = mkUnitR (ucum "deg" "°" "degree") (Prelude.pi Prelude./ 180) $ radian arcminute = mkUnitR (ucum "'" "'" "arcminute") (recip 60) $ degreeOfArc arcsecond = mkUnitR (ucum "''" "''" "arcsecond") (recip 60) $ minuteOfArc {- $arc-units-alternate Alternate (longer) forms of the above. In particular 'degreeOfArc' can be used if there is a percieved need to disambiguate from e.g. temperature. -} degreeOfArc, minuteOfArc, secondOfArc :: Floating a => Unit 'NonMetric DPlaneAngle a degreeOfArc = degree secondOfArc = arcsecond minuteOfArc = arcminute hectare :: Fractional a => Unit 'NonMetric DArea a hectare = square (hecto meter) litre, liter :: Fractional a => Unit 'Metric DVolume a litre = mkUnitQ (ucumMetric "L" "L" "litre") 1 $ deci meter ^ pos3 -- International English. liter = litre -- American English. tonne, metricTon :: Num a => Unit 'Metric DMass a tonne = mkUnitZ (ucumMetric "t" "t" "tonne") 1000 $ siUnit -- Name in original SI text. metricTon = tonne -- American name. {- $values-obtained-experimentally We decline to provide here those units - listed in Table 7 - which, while accepted for use with the SI, have values which are determined experimentally. For versioning purposes, those units can be found in "Numeric.Units.Dimensional.NonSI". However, in 2012 the IAU redefined the astronomical unit as a conventional unit of length directly tied to the meter, with a length of exactly 149,597,870,700 m and the official abbreviation of au <#note3 [3]>. We therefore include it here. -} astronomicalUnit :: Num a => Unit 'NonMetric DLength a astronomicalUnit = mkUnitZ (ucum "AU" "AU" "astronomical unit") 149597870700 $ meter