17
17
import pyFAI
18
18
from dahu .plugin import Plugin
19
19
from dahu .factory import register
20
+
20
21
lock = Semaphore ()
21
22
logger = logging .getLogger ("id27" )
22
23
23
24
RAW = "RAW_DATA"
24
25
PROCESSED = "PROCESSED_DATA"
25
26
WORKDIR = "/scratch/shared"
26
27
PREFIX = os .path .dirname (sys .executable ) #where there scripts are
28
+ NEGGIA_PLUGIN = os .path .join (os .environ ["HOME" ], "lib" , "dectris-neggia.so" )
29
+ XDS_EXE = "xds_par"
27
30
28
31
try :
29
32
from cryio import crysalis
@@ -313,10 +316,21 @@ def fabio_conversion(file_path,
313
316
folder = "xdi" ,
314
317
fabioimage = "tifimage" ,
315
318
extension = "tif" ,
316
- export_icat = False ):
317
- "Convert a set of eiger files to cbf or tiff"
319
+ export_icat = False ,
320
+ detector = "eiger" ):
321
+ """Convert a set of eiger files to cbf or tiff
322
+
323
+ :param file_path: First par of the input filename, typically /
324
+ :param scan_number: last part of the path, typically "scan0001".
325
+ The actual filename is actually guessed since there can be several of them, all will be processed
326
+ :param folder: name of the output folder for writing (in PROCESSED_DATA, not in RAW_DATA folder)
327
+ :param fabioimage: type of file to write, name of the Fabio class name.
328
+ :param export_icat: set to True to export to icat the result.
329
+ :param detector: name of the detector, used to determine the input filename
330
+ :return: the logs of the conversion.
331
+ """
318
332
results = []
319
- filename = os .path .join (file_path , scan_number , 'eiger_ ????.h5' )
333
+ filename = os .path .join (file_path , scan_number , f' { detector } _ ????.h5' )
320
334
files = {f :fabio .open (f ).nframes for f in sorted (glob .glob (filename ))} #since python 3.7 dict are ordered !
321
335
all_single = max (files .values ())== 1
322
336
file_path = file_path .rstrip ("/" )
@@ -452,8 +466,9 @@ class XdiConversion(Plugin):
452
466
This is the plugin to convert an HDF5 to a stack of TIFF files
453
467
454
468
Typical JSON file:
455
- {"file_path": "/data/id27/inhouse/some/file.h5",
456
- "scan_number": "0001"
469
+ {"file_path": "/data/id27/inhouse/some/path", #excluding the scan-number and the filename
470
+ "scan_number": "scan0001"
471
+ "detector": "eiger" #optionnal
457
472
}
458
473
459
474
"""
@@ -465,12 +480,14 @@ def process(self):
465
480
466
481
file_path = self .input ["file_path" ]
467
482
scan_number = self .input ["scan_number" ]
483
+ detector = self .input .get ("detector" , "eiger" )
468
484
results = fabio_conversion (file_path ,
469
485
scan_number ,
470
486
folder = "xdi" ,
471
487
fabioimage = "tifimage" ,
472
488
extension = "tif" ,
473
- export_icat = False )
489
+ export_icat = False ,
490
+ detector = detector )
474
491
self .output ["output" ] = results
475
492
476
493
@@ -553,8 +570,8 @@ class XdsConversion(Plugin):
553
570
This is the plugin to convert an HDF5 to a stack of CBF files for XDS
554
571
555
572
Typical JSON file:
556
- {"file_path": "/data/id27/inhouse/some/file.h5",
557
- "scan_number": "0001 "
573
+ {"file_path": "/data/id27/inhouse/some/path", #excluding the scan-number and the filename
574
+ "scan_number": "scan0001 "
558
575
}
559
576
560
577
"""
@@ -638,7 +655,7 @@ def process(self):
638
655
ai ["error_model" ] = "poisson"
639
656
ai ["application" ] = "pyfai-integrate"
640
657
ai ["version" ] = 3
641
- ai ["method" ] = ["bbox " , "csr" , "opencl" ]
658
+ ai ["method" ] = ["full " , "csr" , "opencl" ]
642
659
ai ["opencl_device" ] = "gpu"
643
660
ai ["nbpt_rad" ] = self .input .get ("npt" , 1 )
644
661
ai ["nbpt_azim" ] = 1
@@ -648,8 +665,8 @@ def process(self):
648
665
param ["experiment_title" ] = os .path .join (os .path .basename (file_path ), scan_number )
649
666
param ["fast_motor_name" ] = "fast"
650
667
param ["slow_motor_name" ] = "slow"
651
- param ["fast_motor_points " ] = fast_scan
652
- param ["slow_motor_points " ] = slow_scan
668
+ param ["nbpt_fast " ] = fast_scan
669
+ param ["nbpt_slow " ] = slow_scan
653
670
param ["offset" ] = 0
654
671
param ["output_file" ] = dest
655
672
param ["input_data" ] = [(i , None , None ) for i in files ]
@@ -672,13 +689,16 @@ def process(self):
672
689
try :
673
690
send_icat (file_path , dest_dir , metadata = metadata )
674
691
except Exception as err :
692
+ self .log_error (f"Error in send_icat { type (err )} : { err } " , do_raise = False )
675
693
import traceback
676
- print (f"Error { type (err )} : { err } " )
677
- traceback .print_exc (err , file = sys .stdout )
694
+ traceback .print_exc (file = sys .stdout )
678
695
679
696
680
697
def send_icat (raw_dir , processed_dir , beamline = "id27" , proposal = "" , dataset = "" , metadata = None ):
681
698
"Function that sends to icat the processed data"
699
+ if IcatClient is None :
700
+ logger .warning ("pyicat_plus is not installed, skipping" )
701
+ return
682
702
icat_client = IcatClient (metadata_urls = ["bcu-mq-01.esrf.fr:61613" , "bcu-mq-02.esrf.fr:61613" ])
683
703
metadata = metadata or {"definition" : "dummy processing" , "Sample_name" : "unknown sample" }
684
704
l = raw_dir .split ("/" )
@@ -700,6 +720,96 @@ def send_icat(raw_dir, processed_dir, beamline="id27", proposal="", dataset="",
700
720
icat_client .store_processed_data (** kwargs )
701
721
return kwargs
702
722
723
+ @register
724
+ class XdsProcessing (Plugin ):
725
+ """
726
+ This is the plugin to convert an HDF5-lima file into a Neggia-compatible and processes it using XDS.
727
+
728
+ Typical JSON file:
729
+ {"plugin_name": "id27.xdsprocessing",
730
+ "file_path": "/data/id27/inhouse/some/path", #excluding the scan-number and the filename
731
+ "scan_number": "scan0001",
732
+ "ponifile": "/tmp/geometry.poni",
733
+ "detector": "eiger" #optionnal
734
+ "xds_extra": ["",
735
+ "!UNIT_CELL_CONSTANTS= 10.317 10.317 7.3378 90 90 120 ! put correct values if known",
736
+ ...]
737
+ MINIMUM_NUMBER_OF_PIXELS_IN_A_SPOT=4 ! default of 6 is sometimes too high
738
+ MAXIMUM_NUMBER_OF_STRONG_PIXELS=18000 ! total number of strong pixels
739
+ used for indexation
740
+ BACKGROUND_PIXEL=2.0 ! used by COLSPOT and INTEGRATE
741
+ SIGNAL_PIXEL=3.0 ! needs to be lager than BACKGROUND_PIXEL, specifies
742
+ standard deviation, used in COLSPOT and INTEGRATE
743
+
744
+ !EXCLUDE_RESOLUTION_RANGE= 3.93 3.87 !ice-ring at 3.897 Angstrom
745
+ !INCLUDE_RESOLUTION_RANGE=40 1.75 ! after CORRECT, insert high resol
746
+ limit; re-run CORRECT
747
+
748
+ }
703
749
704
-
705
-
750
+ """
751
+
752
+ def process (self ):
753
+ Plugin .process (self )
754
+ if not self .input :
755
+ logger .error ("input is empty" )
756
+
757
+ script_name = "hdf2neggia"
758
+ file_path = self .input ["file_path" ]
759
+ scan_number = self .input ["scan_number" ]
760
+ ponifile = self .input ["ponifile" ]
761
+ detector = self .input .get ("detector" , "eiger" )
762
+ xds_extra = self .input .get ("xds_extra" )
763
+ if xds_extra is None :
764
+ xds_extra = ["" ,
765
+ "!UNIT_CELL_CONSTANTS= 10.317 10.317 7.3378 90 90 120 ! put correct values if known" ,
766
+ "MINIMUM_NUMBER_OF_PIXELS_IN_A_SPOT=4 ! default of 6 is sometimes too high" ,
767
+ "MAXIMUM_NUMBER_OF_STRONG_PIXELS=18000 ! total number of strong pixels used for indexation" ,
768
+ "BACKGROUND_PIXEL=2.0 ! used by COLSPOT and INTEGRATE" ,
769
+ "SIGNAL_PIXEL=3.0 ! needs to be lager than BACKGROUND_PIXEL, specifies standard deviation, used in COLSPOT and INTEGRATE" ,
770
+ "!EXCLUDE_RESOLUTION_RANGE= 3.93 3.87 !ice-ring at 3.897 Angstrom" ,
771
+ "!INCLUDE_RESOLUTION_RANGE=40 1.75 ! after CORRECT, insert high resol limit; re-run CORRECT" ,
772
+ "" ]
773
+
774
+ filename = os .path .join (file_path , scan_number , f'{ detector } _????.h5' )
775
+ files = sorted (glob .glob (filename ))
776
+ file_path = file_path .rstrip ("/" )
777
+
778
+ splitted = file_path .split ("/" )
779
+ if RAW in splitted :
780
+ raw_pos = splitted .index (RAW )
781
+ splitted [raw_pos ] = PROCESSED
782
+ splitted .append (scan_number )
783
+ splitted .insert (0 , "/" )
784
+ dest_dir = os .path .join (* splitted )
785
+ # sample_name = splitted[raw_pos + 1]
786
+ else :
787
+ dest_dir = os .path .join (file_path .replace (RAW ,PROCESSED ), scan_number )
788
+ # sample_name = "unknown sample"
789
+ dest_dir = os .path .join (dest_dir , "xsd" )
790
+ if len (files ) == 0 :
791
+ raise RuntimeError (f"No such file { filename } " )
792
+
793
+ if not os .path .exists (dest_dir ):
794
+ os .makedirs (dest_dir , exist_ok = True )
795
+
796
+ script_name = os .path .join (PREFIX , script_name )
797
+ parameters = [script_name ,
798
+ "--geometry" , ponifile ,
799
+ "--output" , dest_dir ,
800
+ "--neggia" , NEGGIA_PLUGIN ,
801
+ "--CdTe" ] + files
802
+ self .log_warning (f'start script with parameters: `{ " " .join (parameters )} `' )
803
+ res = subprocess .run (parameters , capture_output = True , check = False )
804
+ self .output ["convert" ] = unpack_processed (res )
805
+ if res .returncode == 0 :
806
+ # Implement the tuning of the XDS.INP file here...
807
+ xdsinp = os .path .join (dest_dir , "XDS.INP" )
808
+ with open (xdsinp , "a" ) as xdsfile :
809
+ xdsfile .write (os .linesep )
810
+ for line in xds_extra :
811
+ xdsfile .write (line + os .linesep )
812
+
813
+ res = subprocess .run (XDS_EXE , cwd = dest_dir , capture_output = True , check = False )
814
+ self .output ["xds" ] = unpack_processed (res )
815
+
0 commit comments