{-| Module : Isotope.Base Description : Contains the data type and type class declarations used in the Isotope library. Copyright : Michael Thomas License : GPL-3 Maintainer : Michael Thomas Stability : Experimental This module defines the majority of the types used in the Isotope library. A large number of type synonyms are provided to improve readability. Of particular importance are the 'Isotope', 'Element', 'ElementSymbol', `ElementalComposition`, `MolecularFormula`,'CondensedFormula' and `EmpiricalFormula` data types. 'ElementSymbol' is an enumeration type of all the element symbols used in the Isotopes library. -} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-} {-# OPTIONS_HADDOCK hide #-} module Isotope.Base ( -- Infix operators used in `Isotope` Operators(..) -- Types for masses , IntegerMass , MonoisotopicMass(..) , NominalMass(..) , AverageMass(..) , IsotopicMass(..) -- Other types , ElementName , IsotopicAbundance(..) , AtomicNumber , ProtonNumber , NeutronNumber , Nucleons , MassNumber -- 'Isotope' and 'Element' data types , Isotope(..) , Element(..) -- Element symbols , ElementSymbol(..) , elementSymbolList -- Functions taking an 'Element' as input , elementMostAbundantIsotope , elementIsotopicMasses , elementIntegerMasses , elementIsotopicAbundances , elementMonoisotopicMass , elementNominalMass , elementAverageMass , massNumber -- 'elements' - a map containing isotopic data for each element. , elements -- Functions taking an 'elementSymbol' as input , findElement , elementName , atomicNumber , isotopes , mostAbundantIsotope , selectIsotope , isotopicMasses , integerMasses , isotopicAbundances -- Formula type class , Formula(..) -- Elemental composition , ElementalComposition(..) , ToElementalComposition(..) , mkElementalComposition -- Molecular formula , MolecularFormula(..) , ToMolecularFormula(..) , mkMolecularFormula -- Condensed formula , CondensedFormula(..) , ToCondensedFormula(..) -- Empirical formula , EmpiricalFormula(..) , ToEmpiricalFormula(..) , mkEmpiricalFormula ) where import Data.Map (Map) import qualified Data.Map as Map import Data.Foldable import Data.Ord import Data.List import Data.Monoid -------------------------------------------------------------------------------- -- | Infix operators used in `Isotope`. These operators support addition, -- subtraction and multiplication. class Operators a where (|+|) :: a -> a -> a (|-|) :: a -> a -> a (|*|) :: a -> Int -> a infixl 6 |+| infixl 7 |*| infixl 6 |-| -------------------------------------------------------------------------------- -- Types for masses -- | Integer mass for an isotope. type IntegerMass = MassNumber -- | The exact mass of the most abundant isotope for an element or the sum of -- the exact masses of the most abundant isotope of each element for a -- elemental composition. newtype MonoisotopicMass = MonoisotopicMass { getMonoisotopicMass :: Double } deriving (Show, Eq, Ord) instance Monoid MonoisotopicMass where mempty = MonoisotopicMass 0 mappend = (|+|) instance Operators MonoisotopicMass where MonoisotopicMass x |+| MonoisotopicMass y = MonoisotopicMass $ x + y MonoisotopicMass x |-| MonoisotopicMass y = MonoisotopicMass $ x - y MonoisotopicMass x |*| y = MonoisotopicMass $ x * fromIntegral y -- | The integer mass of the most abundant isotope for an element or the sum of -- integer mass of the most abundant isotope of each element for a elemental -- composition. newtype NominalMass = NominalMass { getNominalMass :: Int } deriving (Show, Eq, Ord) instance Monoid NominalMass where mempty = NominalMass 0 mappend = (|+|) instance Operators NominalMass where NominalMass x |+| NominalMass y = NominalMass $ x + y NominalMass x |-| NominalMass y = NominalMass $ x - y NominalMass x |*| y = NominalMass $ x * y -- | The average mass of an element or elemental composition based on -- naturally-occurring abundances. newtype AverageMass = AverageMass { getAverageMass :: Double } deriving (Show, Eq, Ord) instance Monoid AverageMass where mempty = AverageMass 0 mappend = (|+|) instance Operators AverageMass where AverageMass x |+| AverageMass y = AverageMass $ x + y AverageMass x |-| AverageMass y = AverageMass $ x - y AverageMass x |*| y = AverageMass $ x * fromIntegral y -- | The exact mass of an isotope. newtype IsotopicMass = IsotopicMass { getIsotopicMass :: Double } deriving (Show, Eq, Ord) instance Monoid IsotopicMass where mempty = IsotopicMass 0 mappend = (|+|) instance Operators IsotopicMass where IsotopicMass x |+| IsotopicMass y = IsotopicMass $ x + y IsotopicMass x |-| IsotopicMass y = IsotopicMass $ x - y IsotopicMass x |*| y = IsotopicMass $ x * fromIntegral y -------------------------------------------------------------------------------- -- Other types -- | The name of an element. type ElementName = String -- | The natural abundance of an isotope. newtype IsotopicAbundance = IsotopicAbundance { getIsotopicAbundance :: Double } deriving (Show, Eq, Ord) -- | Atomic number of an element. type AtomicNumber = Int -- | Proton number (i.e., the number of protons) for an element/isotope. type ProtonNumber = AtomicNumber -- | Neutron number (i.e., the number of neutrons) for an element. type NeutronNumber = Int -- | Type synonym for a pair containing 'ProtonNumber' and 'NeutronNumber'. type Nucleons = (ProtonNumber, NeutronNumber) -- | The number of protons plus the number of neutrons (i.e., proton number + -- neutron number) for an isotope. type MassNumber = Int type Charge = Int -------------------------------------------------------------------------------- -- 'Isotope' and 'Element' data types -- | An 'Isotope' has three parameters; 'Nucleons', 'IsotopeMass' and -- 'IsotopicAbundance'. data Isotope = Isotope { nucleons :: Nucleons , isotopicMass :: IsotopicMass , isotopicAbundance :: IsotopicAbundance } deriving (Show, Eq, Ord) -- | An 'Element' has three parameters; 'AtomicNumber', 'ElementName' and -- list of 'Isotope'. data Element = Element { atomicNumber' :: AtomicNumber , elementName' :: ElementName , isotopes' :: [Isotope] } deriving (Show, Eq, Ord) -------------------------------------------------------------------------------- -- Element symbols -- | Element symbols as an enumeration type. data ElementSymbol = H | He | Li | Be | B | C | N | O | F | Ne | Na | Mg | Al | Si | P | S | Cl | Ar | K | Ca | Sc | Ti | V | Cr | Mn | Fe | Co | Ni | Cu | Zn | Ga | Ge | As | Se | Br | Kr | Rb | Sr | Y | Zr | Nb | Mo | Ru | Rh | Pd | Ag | Cd | In | Sn | Sb | Te | I | Xe | Cs | Ba | La | Ce | Pr | Nd | Sm | Eu | Gd | Tb | Dy | Ho | Er | Tm | Yb | Lu | Hf | Ta | W | Re | Os | Ir | Pt | Au | Hg | Tl | Pb | Bi | Th | Pa | U deriving (Show, Read, Eq, Ord, Enum, Bounded) -- | List containing all element symbols. elementSymbolList :: [ElementSymbol] elementSymbolList = [H .. U] -------------------------------------------------------------------------------- -- Functions taking an 'Element' as input -- | Returns the most abundant naturally-occurring isotope for an element. elementMostAbundantIsotope :: Element -> Isotope elementMostAbundantIsotope e = maximumBy (comparing isotopicAbundance) $ isotopes' e -- | Exact masses for all naturally-occurring isotopes for an element. elementIsotopicMasses :: Element -> [IsotopicMass] elementIsotopicMasses e = isotopicMass <$> isotopes' e -- | Integer masses for all naturally-occurring isotopes for an element. elementIntegerMasses :: Element -> [IntegerMass] elementIntegerMasses e = massNumber . nucleons <$> isotopes' e -- | Isotope abundances for all naturally-occurring isotopes for an element. elementIsotopicAbundances :: Element -> [IsotopicAbundance] elementIsotopicAbundances e = isotopicAbundance <$> isotopes' e -- | Monoistopic mass for an element. elementMonoisotopicMass :: Element -> MonoisotopicMass elementMonoisotopicMass = MonoisotopicMass . getIsotopicMass . isotopicMass . elementMostAbundantIsotope -- | Nominal mass for an element. elementNominalMass :: Element -> NominalMass elementNominalMass = NominalMass . massNumber . nucleons . elementMostAbundantIsotope -- | Average mass of an element. elementAverageMass :: Element -> AverageMass elementAverageMass e = foldMap (\x -> AverageMass $ getIsotopicMass (isotopicMass x) * getIsotopicAbundance (isotopicAbundance x)) (isotopes' e) -- Mass number for an isotope. Mass number is the number of protons plus the -- number of neutrons. massNumber :: Nucleons -> MassNumber massNumber (protonNum, neutronNum) = protonNum + neutronNum -------------------------------------------------------------------------------- -- 'elements' - a map containing isotopic data for each element. -- Helper function to convert a tuple to an `Isotope`. tupleToIsotope :: (Nucleons, Double, Double) -> Isotope tupleToIsotope (nucl, mass, abun) = Isotope nucl (IsotopicMass mass) (IsotopicAbundance abun) tupleToElement :: (AtomicNumber, ElementName, [(Nucleons, Double, Double)]) -> Element tupleToElement (atomNum, name, isotopes) = Element atomNum name (tupleToIsotope <$> isotopes) -- | Map of the periodic table. All data on isotopic masses and abundances is -- contained within this map. elements :: Map ElementSymbol Element elements = tupleToElement <$> Map.fromList [ (H, (1, "hydrogen", [ ((1, 0), 1.00782503223, 0.999885) , ((1, 1), 2.01410177812, 0.000115) ])) , (He, (2, "helium", [ ((2, 1), 3.0160293201, 0.00000134) , ((2, 2), 4.00260325413, 0.99999866) ])) , (Li, (3, "lithium", [ ((3, 3), 6.0151228874, 0.0759) , ((3, 4), 7.0160034366, 0.9241) ])) , (Be, (4, "beryllium", [ ((4, 5), 9.012183065, 1.0) ])) , (B, (5, "boron", [ ((5, 5), 10.01293695, 0.199) , ((5, 6), 11.00930536, 0.801) ])) , (C, (6, "carbon", [ ((6, 6), 12.0000000, 0.9893) , ((6, 7), 13.00335483507, 0.0107) ])) , (N, (7, "nitrogen", [ ((7, 7), 14.00307400443, 0.99636) , ((7, 8), 15.00010889888, 0.00364) ])) , (O, (8, "oxygen", [ ((8, 8), 15.99491461957, 0.99757) , ((8, 9), 16.99913175650, 0.00038) , ((8, 10), 17.99915961286, 0.00205) ])) , (F, (9, "fluorine", [ ((9, 10), 18.99840316273, 1.0) ])) , (Ne, (10, "neon", [ ((10, 10), 19.9924401762, 0.9048) , ((10, 11), 20.993846685, 0.0027) , ((10, 12), 21.991385114, 0.0925) ])) , (Na, (11, "sodium", [ ((11, 12), 22.9897692820, 1.0) ])) , (Mg, (12, "magnesium", [ ((12, 12), 23.985041697, 0.7899) , ((12, 13), 24.985836976, 0.1000) , ((12, 14), 25.982592968, 0.1101) ])) , (Al, (13, "aluminium", [ ((13, 14), 26.98153853, 1.0) ])) , (Si, (14, "silicon", [ ((14, 14), 27.97692653465, 0.92223) , ((14, 15), 28.97649466490, 0.04685) , ((14, 16), 29.973770136, 0.03092) ])) , (P, (15, "phosphorous", [ ((15, 16), 30.97376199842, 1.0) ])) , (S, (16, "sulfur", [ ((16, 16), 31.9720711744, 0.9499) , ((16, 17), 32.9714589098, 0.0075) , ((16, 18), 33.967867004, 0.0425) , ((16, 20), 35.96708071, 0.0001) ])) , (Cl, (17, "chlorine", [ ((17, 18), 34.968852682, 0.7576) , ((17, 20), 36.965902602, 0.2424) ])) , (Ar, (18, "argon", [ ((18, 18), 35.967545105, 0.003336) , ((18, 20), 37.96273211, 0.000629) , ((18, 22), 39.9623831237, 0.996035) ])) , (K, (19, "potassium", [ ((19, 20), 38.9637064864, 0.932581) , ((19, 21), 39.963998166, 0.000117) , ((19, 22), 40.9618252579, 0.067302) ])) , (Ca, (20, "calcium", [ ((20, 20), 39.962590863, 0.96941) , ((20, 22), 41.95861783, 0.00647) , ((20, 23), 42.95876644, 0.00135) , ((20, 24), 43.95548156, 0.02086) , ((20, 26), 45.9536890, 0.00004) , ((20, 28), 47.95252276, 0.00187) ])) , (Sc, (21, "scandium", [ ((21, 24), 44.95590828, 1.0) ])) , (Ti, (22, "titanium", [ ((22, 24), 45.95262772, 0.0825) , ((22, 25), 46.95175879, 0.0744) , ((22, 26), 47.94794198, 0.7372) , ((22, 27), 48.94786568, 0.0541) , ((22, 28), 49.94478689, 0.0518) ])) , (V, (23, "vanadium", [ ((23, 27), 49.94715601, 0.00250) , ((23, 28), 50.94395704, 0.99750) ])) , (Cr, (24, "chromium", [ ((24, 26), 49.94604183, 0.04345) , ((24, 28), 51.94050623, 0.83789) , ((24, 29), 52.94064815, 0.09501) , ((24, 30), 53.93887916, 0.02365) ])) , (Mn, (25, "manganese", [ ((25, 30), 54.93804391, 1.0) ])) , (Fe, (26, "iron", [ ((26, 28), 53.93960899, 0.05845) , ((26, 30), 55.93493633, 0.91754) , ((26, 31), 56.93539284, 0.02119) , ((26, 32), 57.93327443, 0.00282) ])) , (Co, (27, "cobalt", [ ((27, 32), 58.93319429, 1.0) ])) , (Ni, (28, "nickel", [ ((28, 30), 57.93534241, 0.68077) , ((28, 32), 59.93078588, 0.26223) , ((28, 33), 60.93105557, 0.011399) , ((28, 34), 61.92834537, 0.036346) , ((28, 36), 63.92796682, 0.009255) ])) , (Cu, (29, "copper", [ ((29, 34), 62.92959772, 0.6915) , ((29, 36), 64.92778970, 0.3085) ])) , (Zn, (30, "zinc", [ ((30, 34), 63.92914201, 0.4917) , ((30, 36), 65.92603381, 0.2773) , ((30, 37), 66.92712775, 0.0404) , ((30, 38), 67.92484455, 0.1845) , ((30, 40), 69.9253192, 0.0061) ])) , (Ga, (31, "gallium", [ ((31, 38), 68.9255735, 0.60108) , ((31, 40), 70.92470258, 0.39892) ])) , (Ge, (32, "germanium", [ ((32, 38), 69.92424875, 0.2057) , ((32, 40), 71.922075826, 0.2745) , ((32, 41), 72.923458956, 0.0775) , ((32, 42), 73.921177761, 0.3650) , ((32, 44), 75.921402726, 0.0773) ])) , (As, (33, "arsenic", [ ((33, 42), 74.92159457, 1.0) ])) , (Se, (34, "selenium", [ ((34, 40), 73.922475934, 0.0089) , ((34, 42), 75.919213704, 0.0937) , ((34, 43), 76.919914154, 0.0763) , ((34, 44), 77.91730928, 0.2377) , ((34, 46), 79.9165218, 0.4961) , ((34, 48), 81.9166995, 0.0873) ])) , (Br, (35, "bromine", [ ((35, 44), 78.9183376, 0.5069) , ((35, 46), 80.9162897, 0.4931) ])) , (Kr, (36, "krypton", [ ((36, 42), 77.92036494, 0.00355) , ((36, 44), 79.91637808, 0.02286) , ((36, 46), 81.91348273, 0.11593) , ((36, 47), 82.91412716, 0.11500) , ((36, 48), 83.9114977282, 0.56987) , ((36, 50), 85.9106106269, 0.17279) ])) , (Rb, (37, "rubidium", [ ((37, 48), 84.9117897379, 0.7217) , ((37, 50), 86.9091805310, 0.2783) ])) , (Sr, (38, "strontium", [ ((38, 46), 83.9134191, 0.0056) , ((38, 48), 85.9092606, 0.0986) , ((38, 49), 86.9088775, 0.0700) , ((38, 50), 87.9056125, 0.8258) ])) , (Y, (39, "yttrium", [ ((39, 50), 88.9058403, 1.0) ])) , (Zr, (40, "zirconium", [ ((40, 50), 89.9046977, 0.5145) , ((40, 51), 90.9056396, 0.1122) , ((40, 52), 91.9050347, 0.1715) , ((40, 54), 93.9063108, 0.1738) , ((40, 56), 95.9082714, 0.0280) ])) , (Nb, (41, "niobium", [ ((41, 52), 92.9063730, 1.0) ])) , (Mo, (42, "molybdenum", [ ((42, 50), 91.90680796, 0.1453) , ((42, 52), 93.90508490, 0.0915) , ((42, 53), 94.90583877, 0.1584) , ((42, 54), 95.90467612, 0.1667) , ((42, 55), 96.90601812, 0.0960) , ((42, 56), 97.90540482, 0.2439) , ((42, 58), 99.9074718, 0.0982) ])) , (Ru, (44, "ruthenium", [ ((44, 52), 95.90759025, 0.0554) , ((44, 54), 97.9052868, 0.0187) , ((44, 55), 98.9059341, 0.1276) , ((44, 56), 99.9042143, 0.1260) , ((44, 57), 100.9055769, 0.1706) , ((44, 58), 101.9043441, 0.3155) , ((44, 59), 103.9054275, 0.1862) ])) , (Rh, (45, "rhodium", [ ((45, 58), 102.9054980, 1.0) ])) , (Pd, (46, "palladium", [ ((46, 56), 101.9056022, 0.0102) , ((46, 58), 103.9040305, 0.1114) , ((46, 59), 104.9050796, 0.2233) , ((46, 60), 105.9034804, 0.2733) , ((46, 62), 107.9038916, 0.2646) , ((46, 64), 109.90517220, 0.1172) ])) , (Ag, (47, "silver", [ ((47, 60), 106.9050916, 0.51839) , ((47, 62), 108.9047553, 0.48161) ])) , (Cd, (48, "cadmium", [ ((48, 58), 105.9064599, 0.0125) , ((48, 60), 107.9041834, 0.0089) , ((48, 62), 109.90300661, 0.1249) , ((48, 63), 110.90418287, 0.1280) , ((48, 64), 111.90276287, 0.2413) , ((48, 65), 112.90440813, 0.1222) , ((48, 66), 113.90336509, 0.2873) , ((48, 68), 115.90476315, 0.0749) ])) , (In, (49, "indium", [ ((49, 64), 112.90406184, 0.0429) , ((49, 66), 114.903878776, 0.9571) ])) , (Sn, (50, "tin", [ ((50, 62), 111.90482387, 0.0097) , ((50, 64), 113.9027827, 0.0066) , ((50, 65), 114.903344699, 0.0034) , ((50, 66), 115.90174280, 0.1454) , ((50, 67), 116.90295398, 0.0768) , ((50, 68), 117.90160657, 0.2422) , ((50, 69), 118.90331117, 0.0859) , ((50, 70), 119.90220163, 0.3258) , ((50, 72), 121.9034438, 0.0463) , ((50, 74), 123.9052766, 0.0579) ])) , (Sb, (51, "antimony", [ ((51, 70), 120.9038120, 0.5721) , ((51, 72), 122.9042132, 0.4279) ])) , (Te, (52, "tellurium", [ ((52, 68), 119.9040593, 0.0009) , ((52, 70), 121.9030435, 0.0255) , ((52, 71), 122.9042698, 0.0089) , ((52, 72), 123.9028171, 0.0474) , ((52, 73), 124.9044299, 0.0707) , ((52, 74), 125.9033109, 0.1884) , ((52, 76), 127.90446128, 0.3174) , ((52, 78), 129.906222748, 0.3408) ])) , (I, (53, "iodine", [ ((53, 74), 126.9044719, 1.0) ])) , (Xe, (54, "xenon", [ ((54, 70), 123.9058920, 0.000952) , ((54, 72), 125.9042983, 0.000890) , ((54, 74), 127.9035310, 0.019102) , ((54, 75), 128.9047808611, 0.264006) , ((54, 76), 129.903509349, 0.040710) , ((54, 77), 130.90508406, 0.212324) , ((54, 78), 131.9041550856, 0.269086) , ((54, 80), 133.90539466, 0.104357) , ((54, 82), 135.907214484, 0.088573) ])) , (Cs, (55, "caesium", [ ((55, 78), 132.9054519610, 1.0) ])) , (Ba, (56, "barium", [ ((56, 74), 129.9063207, 0.00106) , ((56, 76), 131.9050611, 0.00101) , ((56, 78), 133.90450818, 0.02417) , ((56, 79), 134.90568838, 0.06592) , ((56, 80), 135.90457573, 0.07854) , ((56, 81), 136.90582714, 0.11232) , ((56, 82), 137.90524700, 0.71698) ])) , (La, (57, "lanthanum", [ ((57, 81), 137.9071149, 0.0008881) , ((57, 83), 138.9063563, 0.9991119) ])) , (Ce, (58, "cerium", [ ((58, 78), 135.90712921, 0.00185) , ((58, 80), 137.905991, 0.00251) , ((58, 82), 139.9054431, 0.88450) , ((58, 84), 141.9092504, 0.11114) ])) , (Pr, (59, "praseodymium", [ ((59, 82), 140.9076576, 1.0) ])) , (Nd, (60, "neodymium", [ ((60, 82), 141.9077290, 0.27152) , ((60, 83), 142.9098200, 0.12174) , ((60, 84), 143.9100930, 0.23798) , ((60, 85), 144.9125793, 0.08293) , ((60, 86), 145.9131226, 0.17189) , ((60, 88), 147.9168993, 0.05756) , ((60, 90), 149.9209022, 0.05638) ])) , (Sm, (62, "samarium", [ ((62, 82), 143.9120065, 0.0307) , ((62, 83), 146.9149044, 0.1499) , ((62, 84), 147.9148292, 0.1124) , ((62, 85), 148.9171921, 0.1382) , ((62, 86), 149.9172829, 0.0738) , ((62, 88), 151.9197397, 0.2675) , ((62, 90), 153.9222169, 0.2275) ])) , (Eu, (63, "europium", [ ((63, 88), 150.9198578, 0.4781) , ((63, 90), 152.9212380, 0.5219) ])) , (Gd, (64, "gadolinium", [ ((64, 88), 151.9197995, 0.0020) , ((64, 90), 153.9208741, 0.0218) , ((64, 91), 154.9226305, 0.1480) , ((64, 92), 155.9221312, 0.2047) , ((64, 93), 156.9239686, 0.1565) , ((64, 94), 157.9241123, 0.2484) , ((64, 96), 159.9270624, 0.2186) ])) , (Tb, (65, "terbium", [ ((65, 94), 158.9253547, 1.0) ])) , (Dy, (66, "dysprosium", [ ((66, 90), 155.9242847, 0.00056) , ((66, 92), 157.9244159, 0.00095) , ((66, 94), 159.9252046, 0.02329) , ((66, 95), 160.9269405, 0.18889) , ((66, 96), 161.9268056, 0.25475) , ((66, 97), 162.9287383, 0.24896) , ((66, 98), 163.9291819, 0.28260) ])) , (Ho, (67, "holmium", [ ((67, 98), 164.9303288, 1.0) ])) , (Er, (68, "erbium", [ ((68, 94), 161.9287884, 0.00139) , ((68, 96), 163.9292088, 0.01601) , ((68, 98), 165.9302995, 0.33503) , ((68, 99), 166.9320546, 0.22869) , ((68, 100), 167.9323767, 0.26978) , ((68, 102), 169.9354702, 0.14910) ])) , (Tm, (69, "thulium", [ ((69, 100), 168.9342179, 1.0) ])) , (Yb, (70, "ytterbium", [ ((70, 98), 167.9338896, 0.00123) , ((70, 100), 169.9347664, 0.02982) , ((70, 101), 170.9363302, 0.1409) , ((70, 102), 171.9363859, 0.2168) , ((70, 103), 172.9382151, 0.16103) , ((70, 104), 173.9388664, 0.32026) , ((70, 106), 175.9425764, 0.12996) ])) , (Lu, (71, "lutetium", [ ((71, 104), 174.9407752, 0.97401) , ((71, 105), 175.9426897, 0.02599) ])) , (Hf, (72, "hafnium", [ ((72, 102), 173.9400461, 0.0016) , ((72, 104), 175.9414076, 0.0526) , ((72, 105), 176.9432277, 0.1860) , ((72, 106), 177.9437058, 0.2728) , ((72, 107), 178.9458232, 0.1362) , ((72, 108), 179.9465570, 0.3508) ])) , (Ta, (73, "tantalum", [ ((73, 107), 179.9474648, 0.0001201) , ((73, 108), 180.9479958, 0.9998799) ])) , (W, (74, "tungsten", [ ((74, 106), 179.9467108, 0.0012) , ((74, 108), 181.94820394, 0.2650) , ((74, 109), 182.95022275, 0.1431) , ((74, 110), 183.95093092, 0.3064) , ((74, 112), 185.9543628, 0.2843) ])) , (Re, (75, "rhenium", [ ((75, 110), 184.9529545, 0.3740) , ((75, 112), 186.9557501, 0.6260) ])) , (Os, (76, "osmium", [ ((76, 108), 183.9524885, 0.0002) , ((76, 110), 185.9538350, 0.0159) , ((76, 111), 186.9557474, 0.0196) , ((76, 112), 187.9558352, 0.1324) , ((76, 113), 188.9581442, 0.1615) , ((76, 114), 189.9584437, 0.2626) , ((76, 116), 191.9614770, 0.4078) ])) , (Ir, (77, "iridium", [ ((77, 114), 190.9605893, 0.373) , ((77, 116), 192.9629216, 0.627) ])) , (Pt, (78, "platinum", [ ((78, 112), 189.9599297, 0.00012) , ((78, 114), 191.9610387, 0.00782) , ((78, 116), 193.9626809, 0.3286) , ((78, 117), 194.9647917, 0.3378) , ((78, 118), 195.96495209, 0.2521) , ((78, 120), 197.9678949, 0.07356) ])) , (Au, (79, "gold", [ ((79, 118), 196.96656879, 1.0) ])) , (Hg, (80, "mercury", [ ((80, 116), 195.9658326, 0.0015) , ((80, 118), 197.96676860, 0.0997) , ((80, 119), 198.96828064, 0.1687) , ((80, 120), 199.96832659, 0.2310) , ((80, 121), 200.97030284, 0.1318) , ((80, 122), 201.97064340, 0.2986) , ((80, 124), 203.97349398, 0.0687) ])) , (Tl, (81, "thallium", [ ((81, 122), 202.9723446, 0.2952) , ((81, 124), 204.9744278, 0.7048) ])) , (Pb, (82, "lead", [ ((82, 122), 203.9730440, 0.014) , ((82, 124), 205.9744657, 0.241) , ((82, 125), 206.9758973, 0.221) , ((82, 126), 207.9766525, 0.524) ])) , (Bi, (83, "bismuth", [ ((83, 126), 208.9803991, 1.0) ])) , (Th, (90, "thorium", [ ((90, 142), 232.0380558, 1.0) ])) , (Pa, (91, "protactinium", [ ((91, 140), 231.0358842, 1.0) ])) , (U, (92, "uranium", [ ((92, 142), 234.0409523, 0.000054) , ((92, 143), 235.0439301, 0.007204) , ((92, 146), 238.0507884, 0.992742) ])) ] -------------------------------------------------------------------------------- -- Functions taking an 'elementSymbol' as input -- | Searches 'elements' (a map) with an 'ElementSymbol' key and returns -- information for the element. findElement :: ElementSymbol -> Element findElement = (Map.!) elements -- | Returns the name for an element symbol. elementName :: ElementSymbol -> ElementName elementName = elementName' . findElement -- | Returns the atomic number for an element. atomicNumber :: ElementSymbol -> AtomicNumber atomicNumber = atomicNumber' . findElement -- | Returns all the naturally-occurring isotopes for an element. isotopes :: ElementSymbol -> [Isotope] isotopes = isotopes' . findElement -- | Returns the most abundant naturally-occurring isotope for an element. mostAbundantIsotope :: ElementSymbol -> Isotope mostAbundantIsotope = elementMostAbundantIsotope . findElement -- | Selects an isotope of element based on the isotope's mass number -- ('IntegerMass'). selectIsotope :: ElementSymbol -> MassNumber -> Maybe Isotope selectIsotope sym massNum = find (\iso -> (massNumber . nucleons) iso == massNum) isotopeList where isotopeList = isotopes sym -- | Exact masses for all naturally-occurring isotopes for an element. isotopicMasses :: ElementSymbol -> [IsotopicMass] isotopicMasses = elementIsotopicMasses . findElement -- | Integer masses for all naturally-occurring isotopes for an element. integerMasses :: ElementSymbol -> [IntegerMass] integerMasses = elementIntegerMasses . findElement -- | Isotope abundances for all naturally-occurring isotopes for an element. isotopicAbundances :: ElementSymbol -> [IsotopicAbundance] isotopicAbundances = elementIsotopicAbundances . findElement -------------------------------------------------------------------------------- -- Formula type class -- | Type class with two methods; 'renderFormula' and 'emptyFormula'. The -- 'renderFormula' method converts a formula to its shorthand notation. class Formula a where renderFormula :: a -> String emptyFormula :: a -------------------------------------------------------------------------------- -- Elemental composition -- | Provided since 'EmpiricalFormula' can be thought of as an elemenal -- composition but is not a molecular formula. newtype ElementalComposition = ElementalComposition { getElementalComposition :: Map ElementSymbol Int } deriving (Show, Read, Eq, Ord) -- | Class containing four methods; 'toElementalComposition', -- 'monoisotopicMass', 'nominalMass' and 'averageMass'. class ToElementalComposition a where toElementalComposition :: a -> ElementalComposition charge :: a -> Maybe Charge monoisotopicMass :: a -> MonoisotopicMass nominalMass :: a -> NominalMass averageMass :: a -> AverageMass monoisotopicMass = getFormulaSum elementMonoisotopicMass nominalMass = getFormulaSum elementNominalMass averageMass = getFormulaSum elementAverageMass {-# MINIMAL (toElementalComposition, charge) #-} -- Helper function for the calculating monoistopic masses, average mass and -- nominal masses for molecular formulae. getFormulaSum :: (Monoid a, Operators a, ToElementalComposition b) => (Element -> a) -> b -> a getFormulaSum f m = fold $ Map.mapWithKey mapFunc (getElementalComposition (toElementalComposition m)) where mapFunc k v = (f . findElement) k |*| v -- | Smart constructor to make values of type 'ElementalComposition'. mkElementalComposition :: [(ElementSymbol, Int)] -> ElementalComposition mkElementalComposition = ElementalComposition . filterZero . Map.fromList instance Monoid ElementalComposition where mempty = emptyFormula mappend = (|+|) instance Operators ElementalComposition where ElementalComposition x |+| ElementalComposition y = ElementalComposition $ combineMaps (+) x y ElementalComposition x |-| ElementalComposition y = ElementalComposition $ combineMaps (-) x y m |*| n = ElementalComposition . filterZero $ (fromIntegral n *) <$> getElementalComposition m instance ToElementalComposition ElementSymbol where toElementalComposition sym = mkElementalComposition [(sym, 1)] charge _ = Nothing instance ToElementalComposition ElementalComposition where toElementalComposition = id charge _ = Nothing instance Formula ElementalComposition where renderFormula f = foldMap renderFoldfunc ((sortElementSymbolMap . getElementalComposition) f) emptyFormula = mkElementalComposition [] -- Helper function for 'renderFormula'. renderFoldfunc :: (ElementSymbol, Int) -> String renderFoldfunc (sym, num) = show sym <> if num == 1 then "" else show num -- Use the Hill system for writing formulas. C then H followed by elements in -- alphabetical order. sortElementSymbolMap :: Map ElementSymbol Int -> [(ElementSymbol, Int)] sortElementSymbolMap m = sortBy (hillSystem fst) elementSymbolIntList where elementSymbolIntList = Map.toList m elementSymbols = fst <$> elementSymbolIntList containsC = C `elem` elementSymbols hillSystem f a b = case (f a, f b) of (C, _) -> LT (_, C) -> GT (H, b') -> if containsC then LT else show H `compare` show b' (a', H) -> if containsC then GT else show a' `compare` show H (a', b') -> show a' `compare` show b' -------------------------------------------------------------------------------- -- Molecular formulae -- | 'MolecularFormula' is a newtype to represent a molecular formula. newtype MolecularFormula = MolecularFormula { getMolecularFormula :: Map ElementSymbol Int } deriving (Show, Read, Eq, Ord) class ToElementalComposition a => ToMolecularFormula a where toMolecularFormula :: a -> MolecularFormula -- The function unionWith adapted to work with 'Map ElementSymbol Int'. combineMaps :: (Int -> Int -> Int) -> Map ElementSymbol Int -> Map ElementSymbol Int -> Map ElementSymbol Int combineMaps f m1 m2 = filterZero $ Map.unionWith f m1 m2 -- | Smart constructor to make values of type 'MolecularFormula'. mkMolecularFormula :: [(ElementSymbol, Int)] -> MolecularFormula mkMolecularFormula = MolecularFormula . filterZero . Map.fromList -- Helper function to remove (k, v) pairs where v == 0. filterZero :: Map k Int -> Map k Int filterZero = Map.filter (/= 0) instance Monoid MolecularFormula where mempty = emptyFormula mappend = (|+|) instance Operators MolecularFormula where MolecularFormula x |+| MolecularFormula y = MolecularFormula $ combineMaps (+) x y MolecularFormula x |-| MolecularFormula y = MolecularFormula $ combineMaps (-) x y m |*| n = MolecularFormula . filterZero $ (fromIntegral n *) <$> getMolecularFormula m instance ToElementalComposition MolecularFormula where toElementalComposition (MolecularFormula m) = ElementalComposition m charge _ = Nothing instance Formula MolecularFormula where renderFormula f = foldMap renderFoldfunc ((sortElementSymbolMap . getMolecularFormula) f) emptyFormula = mkMolecularFormula [] -------------------------------------------------------------------------------- -- Condensed formulae -- | 'CondensedFormula' is a newtype to represent a condensed formula. newtype CondensedFormula = CondensedFormula { getCondensedFormula :: [Either MolecularFormula (CondensedFormula, Int)] } deriving (Show, Read, Eq, Ord) class ToElementalComposition a => ToCondensedFormula a where toCondensedFormula :: a -> CondensedFormula instance Monoid CondensedFormula where mempty = emptyFormula CondensedFormula x `mappend` CondensedFormula y = CondensedFormula (x <> y) instance ToElementalComposition CondensedFormula where toElementalComposition = ElementalComposition . getMolecularFormula . toMolecularFormula charge _ = Nothing instance ToMolecularFormula CondensedFormula where toMolecularFormula c = foldMap foldFunc (getCondensedFormula c) where foldFunc = \case Left chemForm -> chemForm Right (condForm, n) -> toMolecularFormula condForm |*| n instance Formula CondensedFormula where renderFormula c = foldMap foldFunc (getCondensedFormula c) where foldFunc = \case Left chemForm -> renderFormula chemForm Right (chemFormList, n) -> "(" <> renderFormula chemFormList <> ")" <> formatNum n where formatNum n' = if n' == 1 then "" else show n' emptyFormula = CondensedFormula [] -------------------------------------------------------------------------------- -- | 'EmpiricalFormula' is a newtype to represent a empirical formula. newtype EmpiricalFormula = EmpiricalFormula { getEmpiricalFormula :: Map ElementSymbol Int } deriving (Show, Read, Eq, Ord) -- | Type class with a single method; 'toEmpiricalFormula', which converts a -- chemical data type to `EmpiricalFormula`. class ToElementalComposition a => ToEmpiricalFormula a where toEmpiricalFormula :: a -> EmpiricalFormula -- | Smart constructor to make values of type 'EmpiricalFormula'. mkEmpiricalFormula :: [(ElementSymbol, Int)] -> EmpiricalFormula mkEmpiricalFormula l = let m = filterZero (Map.fromList l) in EmpiricalFormula $ (`div` greatestCommonDenom m) <$> m instance ToEmpiricalFormula ElementalComposition where toEmpiricalFormula (ElementalComposition m) = EmpiricalFormula $ (`div` greatestCommonDenom m) <$> m instance ToEmpiricalFormula MolecularFormula where toEmpiricalFormula (MolecularFormula m) = EmpiricalFormula $ (`div` greatestCommonDenom m) <$> m instance ToEmpiricalFormula CondensedFormula where toEmpiricalFormula = toEmpiricalFormula . toMolecularFormula instance ToElementalComposition EmpiricalFormula where toElementalComposition (EmpiricalFormula a) = ElementalComposition a charge _ = Nothing instance Formula EmpiricalFormula where renderFormula f = foldMap renderFoldfunc ((sortElementSymbolMap . getEmpiricalFormula) f) emptyFormula = mkEmpiricalFormula [] -- Helper function to find the greatest common denominator in a map. greatestCommonDenom :: (Integral v) => Map k v -> v greatestCommonDenom = foldr gcd 0