currycarbon-0.2.1.2: src/Currycarbon/CLI/RunCalibrate.hs
{-# LANGUAGE BangPatterns #-}
module Currycarbon.CLI.RunCalibrate
(CalibrateOptions (..), runCalibrate) where
import Currycarbon.CalCurves.Intcal20
import Currycarbon.Calibration.Calibration
import Currycarbon.Parsers
import Currycarbon.SumCalibration
import Currycarbon.Types
import Currycarbon.Utils
import Control.Monad (unless, when)
import Data.Maybe (fromJust, fromMaybe,
isJust)
import System.IO (hPutStrLn, stderr, stdout)
-- | A data type to represent the options to the CLI module function runCalibrate
data CalibrateOptions = CalibrateOptions {
_calibrateExprs :: [CalExpr] -- ^ String listing the uncalibrated dates that should be calibrated
, _calibrateExprFiles :: [FilePath] -- ^ List of files with uncalibrated dates to be calibrated
, _calibrateCalCurveFile :: Maybe FilePath -- ^ Path to a .14c file
, _calibrateCalibrationMethod :: CalibrationMethod -- ^ Calibration algorithm that should be used
, _calibrateAllowOutside :: Bool -- ^ Allow calibration to run outside of the range of the calibration curve
, _calibrateDontInterpolateCalCurve :: Bool -- ^ Don't interpolate the calibration curve
, _calibrateQuiet :: Bool -- ^ Suppress the printing of calibration results to the command line
, _calibrateStdOutEncoding :: String -- ^ Encoding of the stdout stream (show TextEncoding)
, _calibrateDensityFile :: Maybe FilePath -- ^ Path to an output file (see CLI documentation)
, _calibrateHDRFile :: Maybe FilePath -- ^ Path to an output file
, _calibrateCalCurveSegmentFile :: Maybe FilePath -- ^ Path to an output file
, _calibrateCalCurveMatrixFile :: Maybe FilePath -- ^ Path to an output file
}
-- | Interface function to trigger calibration from the command line
runCalibrate :: CalibrateOptions -> IO ()
runCalibrate (CalibrateOptions exprs exprFiles calCurveFile method allowOutside noInterpolate quiet encoding densityFile hdrFile calCurveSegmentFile calCurveMatrixFile) = do
let ascii = encoding /= "UTF-8"
-- compile dates
exprsFromFile <- mapM readCalExprFromFile exprFiles
let exprsRenamed = replaceEmptyNames $ exprs ++ concat exprsFromFile
if null exprsRenamed
then hPutStrLn stderr "Nothing to calibrate. See currycarbon -h for help"
else do
-- prep data
hPutStrLn stderr $ "Method: " ++ show method
hPutStrLn stderr $ "Curve: " ++ fromMaybe "IntCal20" calCurveFile
calCurve <- maybe (return intcal20) readCalCurveFromFile calCurveFile
let calConf = defaultCalConf {
_calConfMethod = method
, _calConfAllowOutside = allowOutside
, _calConfInterpolateCalCurve = not noInterpolate
}
-- run calibration
hPutStrLn stderr "Calibrating..."
let errorOrCalPDFs = map (evalCalExpr calConf calCurve) exprsRenamed
handleDates ascii True calCurve $ zip exprsRenamed errorOrCalPDFs
where
-- the bool manages if a date is the first, calibratable date
handleDates :: Bool -> Bool -> CalCurveBP -> [(CalExpr, Either CurrycarbonException CalPDF)] -> IO ()
handleDates _ _ _ [] = hPutStrLn stderr "Done."
handleDates _ascii True calCurve (firstDate:otherDates) = case firstDate of
(_, Left e) -> printE e >> handleDates _ascii True calCurve otherDates
(calExpr, Right cPDF) -> firstOut _ascii calCurve calExpr cPDF >> handleDates _ascii False calCurve otherDates
handleDates _ascii False calCurve (firstDate:otherDates) = case firstDate of
(_, Left e) -> printE e >> handleDates _ascii False calCurve otherDates
(calExpr, Right cPDF) -> otherOut _ascii calExpr cPDF >> handleDates _ascii False calCurve otherDates
firstOut :: Bool -> CalCurveBP -> CalExpr -> CalPDF -> IO ()
firstOut _ascii calCurve calExpr@(UnCalDate uncal) calPDF = do
flexOut _ascii calExpr calPDF writeCalPDF writeCalC14
when (isJust calCurveSegmentFile || isJust calCurveMatrixFile) $ do
hPutStrLn stderr $
"Warning: The calCurveSegment file and the calCurveMatrix file only consider the first date, " ++
renderUncalC14 uncal
let calCurveSegment = prepareCalCurveSegment (not noInterpolate) $ getRelevantCalCurveSegment uncal calCurve
when (isJust calCurveSegmentFile) $
writeCalCurve (fromJust calCurveSegmentFile) calCurveSegment
when (isJust calCurveMatrixFile) $
writeCalCurveMatrix (fromJust calCurveMatrixFile) $
makeCalCurveMatrix (uncalToPDF uncal) calCurveSegment
firstOut _ascii _ calExpr calPDF = do
flexOut _ascii calExpr calPDF writeCalPDF writeCalC14
when (isJust calCurveSegmentFile || isJust calCurveMatrixFile) $ do
hPutStrLn stderr $ "Warning: The calCurveSegment file and the calCurveMatrix file can only be produced for simple dates"
otherOut :: Bool -> CalExpr -> CalPDF -> IO ()
otherOut _ascii calExpr calPDF =
flexOut _ascii calExpr calPDF appendCalPDF appendCalC14
flexOut :: Bool -> CalExpr -> CalPDF -> (FilePath -> CalPDF -> IO ()) -> (FilePath -> CalC14 -> IO ()) -> IO ()
flexOut _ascii calExpr calPDF calPDFToFile calC14ToFile = do
case refineCalDate calPDF of
Nothing -> do
unless quiet $ do
hPutStrLn stdout $ renderCalExpr calExpr
hPutStrLn stderr "Warning: Could not calculate meaningful HDRs for this expression. Check --densityFile."
when (isJust hdrFile) $ unless quiet $ hPutStrLn stderr "Nothing written to the HDR file"
when (isJust densityFile) $ calPDFToFile (fromJust densityFile) calPDF
Just calC14 -> do
unless quiet $ hPutStrLn stdout $ renderCalDatePretty _ascii (calExpr, calPDF, calC14)
when (isJust hdrFile) $ calC14ToFile (fromJust hdrFile) calC14
when (isJust densityFile) $ calPDFToFile (fromJust densityFile) calPDF
printE :: CurrycarbonException -> IO ()
printE e = hPutStrLn stderr $ renderCurrycarbonException e
-- | Helper function to replace empty input names with a sequence of numbers,
-- to get each input date an unique identifier
replaceEmptyNames :: [CalExpr] -> [CalExpr]
replaceEmptyNames = zipWith (replaceName . show) ([1..] :: [Integer])
where
replaceName :: String -> CalExpr -> CalExpr
replaceName i (UnCalDate (UncalC14 name x y)) =
if name == "unknownSampleName"
then UnCalDate $ UncalC14 i x y
else UnCalDate $ UncalC14 name x y
replaceName i (CalDate (CalPDF name x y)) =
if name == "unknownSampleName"
then CalDate $ CalPDF i x y
else CalDate $ CalPDF name x y
replaceName i (SumCal a b) = SumCal (replaceName (i ++ "s") a) (replaceName (i ++ "S") b)
replaceName i (ProductCal a b) = ProductCal (replaceName (i ++ "p") a) (replaceName (i ++ "P") b)