1 {-# LANGUAGE DataKinds #-}
2 {-# LANGUAGE DeriveAnyClass #-}
3 {-# LANGUAGE DeriveGeneric #-}
4 {-# LANGUAGE FlexibleContexts #-}
5 {-# LANGUAGE FlexibleInstances #-}
6 {-# LANGUAGE MultiParamTypeClasses #-}
7 {-# LANGUAGE OverloadedStrings #-}
8 {-# LANGUAGE ScopedTypeVariables #-}
9 {-# LANGUAGE StandaloneDeriving #-}
10 {-# LANGUAGE TypeFamilies #-}
12 {-# OPTIONS_GHC -fno-warn-orphans #-}
15 Copyright : Copyright (C) 2014-2024 Synchrotron SOLEIL
16 L'Orme des Merisiers Saint-Aubin
17 BP 48 91192 GIF-sur-YVETTE CEDEX
20 Maintainer : Picca Frédéric-Emmanuel <picca@synchrotron-soleil.fr>
21 Stability : Experimental
22 Portability: GHC only (not tested)
25 module Hkl
.Binoculars
.Projections
.Hkl
34 import Control
.Concurrent
.Async
(mapConcurrently
)
35 import Control
.Monad
.Catch
(MonadThrow
)
36 import Control
.Monad
.IO.Class
(MonadIO
(liftIO
))
37 import Control
.Monad
.Logger
(MonadLogger
, logDebugN
,
39 import Control
.Monad
.Reader
(MonadReader
, ask
, forM_
,
41 import Data
.Aeson
(FromJSON
, ToJSON
,
42 eitherDecode
', encode
)
43 import Data
.ByteString
.Lazy
(fromStrict
, toStrict
)
44 import Data
.Functor
.Identity
(Identity
)
45 import Data
.HashMap
.Strict
(fromList
)
46 import Data
.Ini
(Ini
(..))
47 import Data
.Ini
.Config
.Bidir
(FieldValue
(..))
48 import Data
.Text
(pack
, unpack
)
49 import Data
.Text
.Encoding
(decodeUtf8
, encodeUtf8
)
50 import Data
.Text
.IO (putStr)
51 import Data
.Vector
.Storable
.Mutable
(unsafeWith
)
52 import Foreign
.C
.Types
(CDouble
(..))
53 import Foreign
.ForeignPtr
(withForeignPtr
)
54 import GHC
.Generics
(Generic
)
55 import Path
(Abs
, Dir
, Path
)
56 import Pipes
(await
, each
, runEffect
,
58 import Pipes
.Prelude
(filter, map, tee
, toListM
)
59 import Pipes
.Safe
(runSafeP
, runSafeT
)
60 import Text
.Printf
(printf
)
62 import Hkl
.Binoculars
.Common
63 import Hkl
.Binoculars
.Config
64 import Hkl
.Binoculars
.Config
.Common
65 import Hkl
.Binoculars
.Config
.Sample
66 import Hkl
.Binoculars
.Pipes
67 import Hkl
.Binoculars
.Projections
68 import Hkl
.Binoculars
.Projections
.QCustom
69 import Hkl
.C
.Binoculars
87 { dataFrameHkl
'DataFrameQCustom
:: HKD f DataFrameQCustom
88 , dataFrameHkl
'Sample
:: HKD f Sample
92 deriving instance Show (DataFrameHkl
' DataSourcePath
)
93 instance FromJSON
(DataFrameHkl
' DataSourcePath
)
94 instance ToJSON
(DataFrameHkl
' DataSourcePath
)
96 defaultDataSourcePath
'DataFrameHkl
:: DataFrameHkl
' DataSourcePath
97 defaultDataSourcePath
'DataFrameHkl
= DataFrameHkl
98 default'DataSourcePath
'DataFrameQCustom
99 default'DataSourcePath
'Sample
101 instance HasFieldComment
(DataFrameHkl
' DataSourcePath
) where
102 fieldComment _
= [ "`datapath` internal value used to find the data in the data file."
104 , "This value is for expert only."
106 , "default value: <not set>"
109 instance HasFieldValue
(DataFrameHkl
' DataSourcePath
) where
110 fieldvalue
= FieldValue
111 { fvParse
= eitherDecode
' . fromStrict
. encodeUtf8
112 , fvEmit
= decodeUtf8
. toStrict
. encode
119 data instance Config
'HklProjection
120 = BinocularsConfig
'Hkl
121 { binocularsConfig
'Hkl
'Common
:: BinocularsConfig
'Common
122 , binocularsConfig
'Hkl
'Sample
:: BinocularsConfig
'Sample
123 , binocularsConfig
'Hkl
'ProjectionType
:: ProjectionType
124 , binocularsConfig
'Hkl
'ProjectionResolution
:: Resolutions DIM3
125 , binocularsConfig
'Hkl
'ProjectionLimits
:: Maybe (RLimits DIM3
)
126 , binocularsConfig
'Hkl
'DataPath
:: DataFrameHkl
' DataSourcePath
129 newtype instance Args
'HklProjection
= Args
'HklProjection
(Maybe ConfigRange
)
131 default'BinocularsConfig
'Hkl
:: Config
'HklProjection
132 default'BinocularsConfig
'Hkl
133 = BinocularsConfig
'Hkl
134 { binocularsConfig
'Hkl
'Common
= default'BinocularsConfig
'Common
135 , binocularsConfig
'Hkl
'Sample
= default'BinocularsConfig
'Sample
136 , binocularsConfig
'Hkl
'ProjectionType
= HklProjection
137 , binocularsConfig
'Hkl
'ProjectionResolution
= Resolutions3
0.01 0.01 0.01
138 , binocularsConfig
'Hkl
'ProjectionLimits
= Nothing
139 , binocularsConfig
'Hkl
'DataPath
= defaultDataSourcePath
'DataFrameHkl
142 overload
'DataSourcePath
'DataFrameHkl
:: BinocularsConfig
'Common
143 -> BinocularsConfig
'Sample
144 -> DataFrameHkl
' DataSourcePath
145 -> DataFrameHkl
' DataSourcePath
146 overload
'DataSourcePath
'DataFrameHkl common sample
(DataFrameHkl qCustomPath samplePath
)
147 = DataFrameHkl newQCustomPath newSamplePath
149 newQCustomPath
= overload
'DataSourcePath
'DataFrameQCustom common Nothing qCustomPath
150 newSamplePath
= overload
'DataSourcePath
'Sample sample samplePath
152 instance HasIniConfig
'HklProjection
where
154 getConfig content
@(ConfigContent cfg
) (Args
'HklProjection mr
) capabilities
= do
156 common
<- parse
'BinocularsConfig
'Common cfg mr capabilities
157 sample
<- parse
'BinocularsConfig
'Sample cfg
162 <*> parseFDef cfg
"projection" "type" (binocularsConfig
'Hkl
'ProjectionType
default'BinocularsConfig
'Hkl
)
163 <*> parseFDef cfg
"projection" "resolution" (binocularsConfig
'Hkl
'ProjectionResolution
default'BinocularsConfig
'Hkl
)
164 <*> parseMb cfg
"projection" "limits"
165 <*> (pure
$ eitherF
(const $ guess
'DataSourcePath
'DataFrameHkl common sample content
) (parse
' cfg
"input" "datapath")
167 Nothing
-> guess
'DataSourcePath
'DataFrameHkl common sample content
168 Just d
-> overload
'DataSourcePath
'DataFrameHkl common sample d
))
171 instance ToIni
(Config
'HklProjection
) where
172 toIni c
= toIni
(binocularsConfig
'Hkl
'Common c
)
174 toIni
(binocularsConfig
'Hkl
'Sample c
)
176 Ini
{ iniSections
= fromList
[ ("input", elemFDef
' "datapath" binocularsConfig
'Hkl
'DataPath c
default'BinocularsConfig
'Hkl
)
177 , ("projection", elemFDef
' "type" binocularsConfig
'Hkl
'ProjectionType c
default'BinocularsConfig
'Hkl
178 <> elemFDef
' "resolution" binocularsConfig
'Hkl
'ProjectionResolution c
default'BinocularsConfig
'Hkl
179 <> elemFMbDef
' "limits" binocularsConfig
'Hkl
'ProjectionLimits c
default'BinocularsConfig
'Hkl
190 {-# INLINE spaceHkl #-}
191 spaceHkl
:: Detector b DIM2
-> Array F DIM3
Double -> Resolutions DIM3
-> Maybe Mask
-> Maybe (RLimits DIM3
) -> Bool -> Space DIM3
-> DataFrameHkl
' Identity
-> IO (DataFrameSpace DIM3
)
192 spaceHkl det pixels rs mmask
' mlimits doPolarizationCorrection space
@(Space fSpace
) (DataFrameHkl
(DataFrameQCustom att g img _
) samplePath
) = do
193 withNPixels det
$ \nPixels
->
194 withForeignPtr g
$ \geometry
->
195 withSample samplePath
$ \sample
->
196 withForeignPtr
(toForeignPtr pixels
) $ \pix
->
197 withResolutions rs
$ \nr r
->
198 withMaybeMask mmask
' $ \ mask
'' ->
199 withPixelsDims pixels
$ \ndim dims
->
200 withMaybeLimits mlimits rs
$ \nlimits limits
->
201 withForeignPtr fSpace
$ \pSpace
-> do
203 (ImageInt32 arr
) -> unsafeWith arr
$ \i
-> do
204 {-# SCC "hkl_binoculars_space_hkl_int32_t" #-} c
'hkl_binoculars_space_hkl_int32_t pSpace geometry sample i nPixels
(CDouble
. unAttenuation
$ att
) pix
(toEnum ndim
) dims r
(toEnum nr
) mask
'' limits
(toEnum nlimits
) (toEnum . fromEnum $ doPolarizationCorrection
)
205 (ImageWord16 arr
) -> unsafeWith arr
$ \i
-> do
206 {-# SCC "hkl_binoculars_space_hkl_uint16_t" #-} c
'hkl_binoculars_space_hkl_uint16_t pSpace geometry sample i nPixels
(CDouble
. unAttenuation
$ att
) pix
(toEnum ndim
) dims r
(toEnum nr
) mask
'' limits
(toEnum nlimits
) (toEnum . fromEnum $ doPolarizationCorrection
)
207 (ImageWord32 arr
) -> unsafeWith arr
$ \i
-> do
208 {-# SCC "hkl_binoculars_space_hkl_uint32_t" #-} c
'hkl_binoculars_space_hkl_uint32_t pSpace geometry sample i nPixels
(CDouble
. unAttenuation
$ att
) pix
(toEnum ndim
) dims r
(toEnum nr
) mask
'' limits
(toEnum nlimits
) (toEnum . fromEnum $ doPolarizationCorrection
)
209 return (DataFrameSpace img space att
)
215 processHklP
:: (MonadIO m
, MonadLogger m
, MonadReader
(Config
'HklProjection
) m
, MonadThrow m
)
218 conf
:: Config
'HklProjection
<- ask
220 -- directly from the common config
221 let common
= binocularsConfig
'Hkl
'Common conf
223 let overwrite
= binocularsConfig
'Common
'Overwrite common
224 let det
= binocularsConfig
'Common
'Detector common
225 let (NCores cap
) = binocularsConfig
'Common
'NCores common
226 let destination
= binocularsConfig
'Common
'Destination common
227 let centralPixel
' = binocularsConfig
'Common
'Centralpixel common
228 let (Meter sampleDetectorDistance
) = binocularsConfig
'Common
'Sdd common
229 let (Degree detrot
) = binocularsConfig
'Common
'Detrot common
230 let mImageSumMax
= binocularsConfig
'Common
'ImageSumMax common
231 let inputRange
= binocularsConfig
'Common
'InputRange common
232 let nexusDir
= binocularsConfig
'Common
'Nexusdir common
233 let tmpl
= binocularsConfig
'Common
'Tmpl common
234 let maskMatrix
= binocularsConfig
'Common
'Maskmatrix common
235 let mSkipFirstPoints
= binocularsConfig
'Common
'SkipFirstPoints common
236 let mSkipLastPoints
= binocularsConfig
'Common
'SkipLastPoints common
237 let doPolarizationCorrection
= binocularsConfig
'Common
'PolarizationCorrection common
239 -- directly from the specific config
240 let mlimits
= binocularsConfig
'Hkl
'ProjectionLimits conf
241 let res
= binocularsConfig
'Hkl
'ProjectionResolution conf
242 let datapaths
= binocularsConfig
'Hkl
'DataPath conf
243 let projectionType
= binocularsConfig
'Hkl
'ProjectionType conf
246 -- built from the config
247 output
' <- liftIO
$ destination
' projectionType Nothing inputRange mlimits destination overwrite
248 filenames
<- InputFn
'List
<$> files nexusDir
(Just inputRange
) tmpl
249 mask
' <- getMask maskMatrix det
250 pixels
<- liftIO
$ getPixelsCoordinates det centralPixel
' sampleDetectorDistance detrot NoNormalisation
252 let fns
= concatMap (replicate 1) (toList filenames
)
253 chunks
<- liftIO
$ runSafeT
$ toListM
$ each fns
>-> chunkP mSkipFirstPoints mSkipLastPoints datapaths
254 let ntot
= sum (Prelude
.map clength chunks
)
255 let jobs
= chunk
(quot ntot cap
) chunks
259 logDebugNSH filenames
260 logDebugNSH datapaths
262 logDebugN
"start gessing final cube size"
264 -- guess the final cube dimensions (To optimize, do not create the cube, just extract the shape)
266 guessed
<- liftIO
$ withCubeAccumulator EmptyCube
$ \c
->
267 runSafeT
$ runEffect
$
269 >-> Pipes
.Prelude
.map (\(Chunk fn f t
) -> (fn
, [f
, quot (f
+ t
) 4, quot (f
+ t
) 4 * 2, quot (f
+ t
) 4 * 3, t
]))
270 >-> framesP datapaths
271 >-> project det
3 (spaceHkl det pixels res mask
' mlimits doPolarizationCorrection
)
274 logDebugN
"stop gessing final cube size"
276 logInfoN
(pack
$ printf
"let's do an Hkl projection of %d %s image(s) on %d core(s)" ntot
(show det
) cap
)
278 liftIO
$ withProgressBar ntot
$ \pb
-> do
279 r
' <- mapConcurrently
(\job
-> withCubeAccumulator guessed
$ \c
->
280 runEffect
$ runSafeP
$
282 >-> Pipes
.Prelude
.map (\(Chunk fn f t
) -> (fn
, [f
..t
]))
283 -- >-> tee Pipes.Prelude.print
284 >-> framesP datapaths
285 >-> Pipes
.Prelude
.filter (\(DataFrameHkl
(DataFrameQCustom _ _ img _
) _
) -> filterSumImage mImageSumMax img
)
286 >-> project det
3 (spaceHkl det pixels res mask
' mlimits doPolarizationCorrection
)
287 >-> tee
(accumulateP c
)
290 saveCube output
' (unpack
. serializeConfig
$ conf
) r
'
294 instance ChunkP
(DataFrameHkl
' DataSourcePath
) where
295 chunkP sf sl
(DataFrameHkl p _
) = chunkP sf sl p
297 instance FramesP
(DataFrameHkl
' DataSourcePath
) (DataFrameHkl
' Identity
) where
298 framesP
(DataFrameHkl qcustom sample
) = skipMalformed
$ forever
$ do
300 withFileP
(openFile' fp
) $ \f ->
301 withDataSourceP f qcustom
$ \qcustomAcq
->
302 withDataSourceP f sample
$ \sampleAcq
->
303 forM_ js
(\j
-> tryYield
( DataFrameHkl
304 <$> extract1DStreamValue qcustomAcq j
305 <*> extract0DStreamValue sampleAcq
312 guess
'DataSourcePath
'DataFrameHkl
:: BinocularsConfig
'Common
313 -> BinocularsConfig
'Sample
315 -> DataFrameHkl
' DataSourcePath
316 guess
'DataSourcePath
'DataFrameHkl common sample content
318 (guess
'DataSourcePath
'DataFrameQCustom common Nothing content
)
319 (guess
'DataSourcePath
'Sample common sample
)
325 processHkl
:: (MonadLogger m
, MonadThrow m
, MonadIO m
) => Maybe FilePath -> Maybe ConfigRange
-> m
()
326 processHkl mf mr
= cmd processHklP mf
(Args
'HklProjection mr
)
328 newHkl
:: (MonadIO m
, MonadLogger m
, MonadThrow m
)
329 => Path Abs Dir
-> m
()
331 let conf
= default'BinocularsConfig
'Hkl
332 { binocularsConfig
'Hkl
'Common
= default'BinocularsConfig
'Common
333 { binocularsConfig
'Common
'Nexusdir
= Just cwd
}
335 liftIO
$ Data
.Text
.IO.putStr $ serializeConfig conf
338 updateHkl
:: (MonadIO m
, MonadLogger m
, MonadThrow m
)
339 => Maybe FilePath -> Maybe ConfigRange
-> m
()
340 updateHkl mf mr
= cmd
(pure
()) mf
(Args
'HklProjection mr
)