camfort-0.905: src/Camfort/Specification/Units/ModFile.hs
{- |
Module : Camfort.Specification.Units.ModFile
Description : Helpers for working with units-relevant ModFiles.
Copyright : (c) 2017, Dominic Orchard, Andrew Rice, Mistral Contrastin, Matthew Danish
License : Apache-2.0
Maintainer : dom.orchard@gmail.com
Stability : experimental
-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
module Camfort.Specification.Units.ModFile
(
genUnitsModFile
, initializeModFiles
, runCompileUnits
) where
import Control.Monad.State (get, gets, lift)
import Data.Binary (Binary, decodeOrFail, encode)
import qualified Data.ByteString.Lazy.Char8 as LB
import Data.Data (Data)
import Data.Generics.Uniplate.Operations (universeBi)
import Data.List (partition)
import qualified Data.Map.Strict as M
import Data.Typeable (Typeable)
import GHC.Generics (Generic)
import qualified Language.Fortran.AST as F
import Language.Fortran.Util.ModFile
import Camfort.Analysis (analysisModFiles)
import Camfort.Specification.Units.Annotation (UA)
import Camfort.Specification.Units.Environment (Constraint(..), foldUnits, UnitInfo(..), colSort)
import Camfort.Specification.Units.InferenceBackend (flattenConstraints, flattenUnits, genUnitAssignments, genUnitAssignments')
import Camfort.Specification.Units.Monad
-- | The data-structure stored in 'fortran-src mod files'
data CompiledUnits = CompiledUnits
{ cuTemplateMap :: TemplateMap
, cuNameParamMap :: NameParamMap
} deriving (Ord, Eq, Show, Data, Typeable, Generic)
instance Binary CompiledUnits
emptyCompiledUnits :: CompiledUnits
emptyCompiledUnits = CompiledUnits M.empty M.empty
combinedCompiledUnits :: ModFiles -> CompiledUnits
combinedCompiledUnits mfs = CompiledUnits { cuTemplateMap = M.unions tmaps
, cuNameParamMap = M.unions nmaps }
where
cus = map mfCompiledUnits mfs
tmaps = map cuTemplateMap cus
nmaps = map cuNameParamMap cus
-- | Name of the labeled data within a ModFile containing unit-specific info.
unitsCompiledDataLabel :: String
unitsCompiledDataLabel = "units-compiled-data"
mfCompiledUnits :: ModFile -> CompiledUnits
mfCompiledUnits mf = case lookupModFileData unitsCompiledDataLabel mf of
Nothing -> emptyCompiledUnits
Just bs -> case decodeOrFail (LB.fromStrict bs) of
Left _ -> emptyCompiledUnits
Right (_, _, cu) -> cu
-- | Initialize units-relevant ModFile information.
initializeModFiles :: UnitSolver ()
initializeModFiles = do
mfs <- lift . lift $ analysisModFiles
let compiledUnits = combinedCompiledUnits mfs
modifyTemplateMap . const . cuTemplateMap $ compiledUnits
modifyNameParamMap . const . cuNameParamMap $ compiledUnits
-- | Produce information for a "units-mod" file.
runCompileUnits :: UnitSolver CompiledUnits
runCompileUnits = do
cons <- usConstraints `fmap` get
let unitAssigns = flattenConstraints cons
let epsilon = 0.001 -- arbitrary
let approxEq a b = abs (b - a) < epsilon
let variables = M.fromList [ (NPKVariable var, units) | ([UnitPow (UnitVar var) k], units) <- unitAssigns
, k `approxEq` 1 ]
tmap <- M.map optimiseTemplate `fmap` gets usTemplateMap
let npm = variables
-- D.traceM $ "npm = " ++ unlines (map show (M.toList npm))
-- D.traceM $ "tmap = \n" ++ unlines [ f ++ "\n" ++ unlines (map show cons) | (f, cons) <- M.toList tmap ]
pure CompiledUnits { cuTemplateMap = tmap, cuNameParamMap = variables }
optimiseTemplate cons = map (\ (l, r) -> ConEq (foldUnits l) r) optimised
where
unitAssigns = genUnitAssignments' (compileColSort) cons
unitPows = map (fmap flattenUnits) unitAssigns
optimised = filter cull $ map (fmap foldUnits . shiftTerms) unitPows
cull (lhs, _) = or [ True | (UnitPow (UnitParamPosAbs _) _) <- universeBi lhs ]
isUnitRHS (UnitPow (UnitName _) _) = True
isUnitRHS (UnitPow (UnitParamEAPAbs _) _) = True
isUnitRHS (UnitPow (UnitParamImpAbs _) _) = True
isUnitRHS (UnitPow (UnitParamPosAbs _) _) = False
isUnitRHS _ = False
negateCons = map (\ (UnitPow u k) -> UnitPow u (-k))
shiftTerms :: ([UnitInfo], [UnitInfo]) -> ([UnitInfo], [UnitInfo])
shiftTerms (lhs, rhs) = (lhsOk ++ negateCons rhsShift, rhsOk ++ negateCons lhsShift)
where
(lhsOk, lhsShift) = partition (not . isUnitRHS) lhs
(rhsOk, rhsShift) = partition isUnitRHS rhs
compileColSort = flip colSort
-- | Generate a new ModFile containing Units information.
genUnitsModFile :: F.ProgramFile UA -> CompiledUnits -> ModFile
genUnitsModFile pf cu = alterModFileData f unitsCompiledDataLabel $ genModFile pf
where
f _ = Just . LB.toStrict $ encode cu