units-2.3: Tests/LennardJones.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE ImplicitParams #-}
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
module Tests.LennardJones where
import Data.Metrology.Poly
import Data.Metrology.Z
import Data.Metrology.SI.Mono ()
import Data.Metrology.SI.PolyTypes
import Data.Metrology.SI.Poly (SI)
import Data.Units.SI
import Data.Units.SI.Prefixes
import qualified Data.Dimensions.SI as D
-- import Data.Metrology.Show
import Test.Tasty
import Test.Tasty.HUnit
import Test.HUnit.Approx
type Six = S Five
type Seven = S Six
type Eight = S Seven
type Nine = S Eight
type Ten = S Nine
type Eleven = S Ten
type Twelve = S Eleven
type Thirteen = S Twelve
sSix = SS sFive
sSeven = SS sSix
sEight = SS sSeven
sNine = SS sEight
sTen = SS sNine
sEleven = SS sTen
sTwelve = SS sEleven
sThirteen= SS sTwelve
data EV = EV
instance Show EV where show _ = "eV"
instance Unit EV where
type BaseUnit EV = Joule
conversionRatio _ = 1.60217657e-19
data ProtonMass = ProtonMass
instance Show ProtonMass where show _ = "m_p"
instance Unit ProtonMass where
type BaseUnit ProtonMass = Kilo :@ Gram
conversionRatio _ = 1.67262178e-27
data Å = Å
instance Show Å where show _ = "Å"
instance Unit Å where
type BaseUnit Å = Meter
conversionRatio _ = 1e-8
-- | chemist's unit
type CU = MkLCSU '[ (D.Length, Å)
, (D.Mass, ProtonMass)
, (D.Time, Pico :@ Second)]
protonMass :: Mass SI Float
protonMass = 1 % ProtonMass
sigmaAr :: Length SI Float
sigmaAr = 3.4e-8 % Meter
epsAr :: Energy SI Float
epsAr = 1.68e-21 % Joule
ljForce :: Length SI Float -> Force SI Float
ljForce r = redim $ 24 *| epsAr |*| sigmaAr|^ sSix |/| r |^ sSeven
|-| 48 *| epsAr |*| sigmaAr|^ sTwelve |/| r |^ sThirteen
type AParameterDim = D.Energy :* D.Length :^ Twelve
type BParameterDim = D.Energy :* D.Length :^ Six
type APara = MkQu_DLN AParameterDim
type BPara = MkQu_DLN BParameterDim
ljForceP :: Energy l Float -> Length l Float -> Length l Float -> Force l Float
ljForceP eps sigma r
= redim $ 24 *| eps |*| sigma|^ sSix |/| r |^ sSeven
|-| 48 *| eps |*| sigma|^ sTwelve |/| r |^ sThirteen
aParaAr :: (ConvertibleLCSUs_D D.Length SI l , ConvertibleLCSUs_D D.Energy SI l )=> APara l Float
aParaAr = 48 *| convert epsAr |*| convert sigmaAr|^ sTwelve
bParaAr :: (ConvertibleLCSUs_D D.Length SI l , ConvertibleLCSUs_D D.Energy SI l )=> BPara l Float
bParaAr = 24 *| convert epsAr |*| convert sigmaAr|^ sSix
ljForcePOpt :: APara l Float -> BPara l Float -> Length l Float -> Force l Float
ljForcePOpt a b r
= redim $ b |/| r |^ sSeven
|-| a |/| r |^ sThirteen
sigmaAr' :: (DefaultConvertibleLCSU_D D.Length l) => Length l Float
sigmaAr' = constant $ 3.4e-8 % Meter
epsAr' :: (DefaultConvertibleLCSU_D D.Energy l) => Energy l Float
epsAr' = constant $ 1.68e-21 % Joule
aParaAr' :: (DefaultConvertibleLCSU_D AParameterDim l) => APara l Float
aParaAr' = constant $ 48 *| epsAr' |*| sigmaAr'|^ sTwelve
bParaAr' :: (DefaultConvertibleLCSU_D BParameterDim l) => BPara l Float
bParaAr' = constant $ 24 *| epsAr' |*| sigmaAr'|^ sSix
sigmaAr'' :: (ConvertibleLCSUs_D D.Length CU l) => Length l Float
sigmaAr'' = convert $ (3.4e-8 % Meter :: Length CU Float)
epsAr'' :: (ConvertibleLCSUs_D D.Energy CU l) => Energy l Float
epsAr'' = convert $ (1.68e-21 % Joule :: Energy CU Float)
aParaAr'' :: (ConvertibleLCSUs_D AParameterDim CU l) => APara l Float
aParaAr'' = convert $ ((48 :: Float) *| epsAr' |*| sigmaAr'|^ sTwelve :: APara CU Float)
bParaAr'' :: (ConvertibleLCSUs_D BParameterDim CU l) => BPara l Float
bParaAr'' = convert $ ((24 :: Float) *| epsAr' |*| sigmaAr'|^ sSix :: BPara CU Float)
tests :: TestTree
tests =
let ?epsilon = 0.0001 in -- need this because we need Floats, not Doubles!
let ans :: (ConvertibleLCSUs_D D.Length SI l, ConvertibleLCSUs_D D.Energy SI l) => Force l Float
ans = ljForceP (convert epsAr) (convert sigmaAr) (4 % Å)
val = 9.3407324e-14
in
testGroup "LennardJones"
[ testCase "NaN" (assert (isNaN $ ljForce (4 % Å) # Newton))
, testCase "NaNPoly" (assert (isNaN $ (ljForceP (convert epsAr) (convert sigmaAr) (4 % Å) :: Force SI Float) # Newton))
, testCase "CU" ((ljForceP (convert epsAr) (convert sigmaAr) (4 % Å) :: Force CU Float) # Newton @?~ val)
, testCase "ansNaN" (assert $ isNaN $ (ans :: Force SI Float) # Newton)
, testCase "ansCU" ((ans :: Force CU Float) # Newton @?~ val)
, testCase "optNaN" (assert $ isNaN $ (ljForcePOpt aParaAr bParaAr (4%Å) :: Force SI Float) # Newton)
, testCase "optCU" ((ljForcePOpt aParaAr bParaAr (4%Å) :: Force CU Float) # Newton @?~ val)
, testCase "precompNaN" (assert $ isNaN $ (ljForcePOpt aParaAr' bParaAr' (4%Å) :: Force SI Float) # Newton)
, testCase "precompNaN2" (assert $ isNaN $ (ljForcePOpt aParaAr' bParaAr' (4%Å) :: Force CU Float) # Newton)
, testCase "precompPolyNaN" (assert $ isNaN $ (ljForcePOpt aParaAr'' bParaAr'' (4%Å) :: Force SI Float) # Newton)
, testCase "precompPolyCU" ((ljForcePOpt aParaAr'' bParaAr'' (4%Å) :: Force CU Float) # Newton @?~ val) ]
{-
main :: IO ()
main = do
putStrLn "He insists that it's better to do chemistry in CU than SI."
putStrLn "For example, the attractive force between two Argon atom at the distance of 4Å is:"
putStrLn $ ljForce (4 % Å) `showIn` (Newton)
putStrLn "Oops, let's do it polymorphically:"
putStrLn $ (ljForceP (convert epsAr) (convert sigmaAr) (4 % Å) :: Force SI Float) `showIn` (Newton)
putStrLn "I can't do it in SI! On the other hand in CU we can:"
putStrLn $ (ljForceP (convert epsAr) (convert sigmaAr) (4 % Å) :: Force CU Float) `showIn` (Newton)
-- how would you type it polymorphically (not using the default LCSU)?
let ans :: (ConvertibleLCSUs_D D.Length SI l, ConvertibleLCSUs_D D.Energy SI l)=> Force l Float
ans = ljForceP (convert epsAr) (convert sigmaAr) (4 % Å)
putStrLn "We compare again:"
putStrLn $ (ans :: Force SI Float) `showIn` (Newton)
putStrLn $ (ans :: Force CU Float) `showIn` (Newton)
putStrLn $ "Let's optimize the computation by precomputing all the constants."
putStrLn $ (ljForcePOpt aParaAr bParaAr (4%Å):: Force SI Float) `showIn` Newton
putStrLn $ (ljForcePOpt aParaAr bParaAr (4%Å):: Force CU Float) `showIn` Newton
putStrLn $ "We cannot use the default LCSU for calculating constants in this case."
putStrLn $ (ljForcePOpt aParaAr' bParaAr' (4%Å):: Force SI Float) `showIn` Newton
putStrLn $ (ljForcePOpt aParaAr' bParaAr' (4%Å):: Force CU Float) `showIn` Newton
putStrLn $ "We must pay attention in which LCSU the constants are computed in."
putStrLn $ (ljForcePOpt aParaAr'' bParaAr'' (4%Å):: Force SI Float) `showIn` Newton
putStrLn $ (ljForcePOpt aParaAr'' bParaAr'' (4%Å):: Force CU Float) `showIn` Newton
{--- output ---
A chemist said his favorite system of unit (CU) consists of an Ångstrom, a proton mass, and a picosecond. They are 1.0e-8 m, 1.6726218e-27 kg, and 1.0e-12 s, respectively.
He insists that it's better to do chemistry in CU than SI.
For example, the attractive force between two Argon atom at the distance of 4Å is:
NaN N
Oops, let's do it polymorphically:
NaN N
I can't do it in SI! On the other hand in CU we can:
9.3407324e-14 N
We compare again:
NaN N
9.3407324e-14 N
Let's optimize the computation by precomputing all the constants.
NaN N
9.3407324e-14 N
We cannot use the default LCSU for calculating constants in this case.
NaN N
NaN N
We must pay attention in which LCSU the constants are computed in.
NaN N
9.3407324e-14 N
-}
-}