Generate XDS.INP

Revision as of 13:35, 27 November 2015 by Kay (talk | contribs)

This script generates XDS.INP based on a list of frame names supplied on the commandline. It currently works for MarCCD, ADSC, Pilatus and a few Rigaku detectors; since this is just a bash script, extension to other detectors is very easy.

The Eiger detector is not yet implemented in the script, but this will eventually come.

Usage

Usage is just (don't forget the quotation marks!):

generate_XDS.INP "/home/myname/frms/mydata_1_???.img"

XDS supports bzip2-ed frames. Thus, when specifying the frame name parameter of the script, you should leave out any .bz2 extension.

For improved interaction with XDSGUI, it is advantageous to use an absolute filename - one that starts with a slash ("/").

The script

#!/bin/bash                                                                   
# purpose: generate XDS.INP                                                   
#                                                                             
# tested with some datasets from ALS, SSRL, SLS, ESRF, BESSY, SPring-8 and PF; only MAR, ADSC/SMV, PILATUS, RAXIS (in-house) detectors; 
# for other detectors, values marked with XXX must be manually filled in.                                  
#                                                                                                          
# revision 0.03 . Kay Diederichs 2/2010                                                                    
# revision 0.04 . Kay Diederichs 4/2010 - include alternative ORGX, ORGY calculations for ADSC             
# revision 0.05 . Kay Diederichs 5/2010 - grep for "Corrected" in addition to "marccd"; needed for BESSY   
# revision 0.06 . KD 6/2010 - add UNTRUSTED_RECTANGLE and UNTRUSTED_ELLIPSE; use `whereis catmar` and so on 
# revision 0.07 . KD 6/2010 - decide about ORGX/Y info in MAR header being pixels or mm; other fixes        
# revision 0.08 . KD 6/2010 - fixes for Pilatus 6M                                                          
# revision 0.09 . KD 6/2010 - get rid of requirement for mccd_xdsparams.pl and/or catmar; rather use "od"   
# revision 0.10 . Tim Gruene 7/2010 - set link 'images' to image directory if path exceeds 72 characters    
# revision 0.11 . KD 7/2010 - for MarCCD: look for distance info at different byte position                 
# revision 0.12 . KD 7/2010 - fix for negative PHISTART                                                     
# revision 0.13 . KD 8/2010 - store correct NX NY QX QY in XDS.INP                                          
# revision 0.14 . KD 1/2011 - SENSOR_THICKNESS for Pilatus; MINIMUM_NUMBER_OF_PIXELS_IN_A_SPOT=3            
# revision 0.15 . KD 2/2011 - add comment for -ive sign of APS 19-ID and Australian Synchrotron rotation axis
# revision 0.16 . KD 3/2011 - SENSOR_THICKNESS=0.01 for ADSC and MarCCD. Add comment about SILICON=          
# revision 0.17 . KD 3/2011 - make it work for .bz2 frames; improve screen output                            
# revision 0.18 . KD 4/2011 - faster by doing "strings" only once; revert "images/${1##/*/}" "correction"    
# revision 0.19 . KD 6/2011 - bugfix for 0.18                                                                
# revision 0.20 . KD 7/2011 - redirect stderr of /bin/ls to /dev/null                                        
# revision 0.21 . KD 11/2011 - SEPMIN, CLUSTER_RADIUS hints; read NX NY from header (for Pilatus 2M)
# revision 0.22 . KD 12/2011 - Pilatus 2M UNTRUSTED_RECTANGLE lines, SENSOR_THICKNESS from header
# revision 0.23 . KD 1/2012 - add UNTRUSTED_QUADRILATERAL, remove MINIMUM_ZETA (0.05 is default now)
# revision 0.24 . KD 3/2012 - remove revision 0.10 since XDS now takes much longer paths
# revision 0.25 . KD 3/2012 - remove revision 0.22 for PSI Pilatus 2M; see http://www.globalphasing.com/autoproc/wiki/index.cgi?TroubleShootingKnownIssues
# revision 0.26 . KD 7/2012 - Mac-compatibility: replace od flags --skip-bytes= and --read-bytes= with -j and -N (thanks to Oliver Clarke for working this out!)
# revision 0.27 . KD 11/2012 - EXCLUDE_RESOLUTION_RANGE lines and generic Pilatus Flat_field test
# revision 0.28 . Keitaro 11/2012 - for MarCCD: read oscillation range from the position 1024+736 (fix for omega rotation)
# revision 0.29 . KD 1/2013 - include UNTRUSTED_RECTANGLEs for Pilatus 6M; never hurts but needed if the beamline software does not mark them with -2 or such
# revision 0.30 . Keitaro 3/2013 - for ADSC: write all possible beam center conventions in XDS.INP as comments
# revision 0.31 . Keitaro 3/2013 - add comment for reversed phi for SPring-8
# revision 0.32 . Keitaro 3/2013 - add RAXIS support. only tested with RAXIS IV++ and VII.
# revision 0.33 . Keitaro 5/2013 - automatically set ROTATION_AXIS=-1 0 0 for SPring-8 BL32XU/41XU/44XU beamlines based on detector serial numbers.
# revision 0.34 . Keitaro 5/2013 - recognize ADSC detectors in Photon Factory and choose correct beam center convention based on detector serial numbers.
# revision 0.35 . KD 6/2013 - reduce 7000 to 6000 for shadow detection; insert comment about *_RESOLUTION_RANGE lines
# revision 0.36 . KD 6/2013 - insert NUMBER_OF_PROFILE_GRID* lines for Pilatus (suggested by C.Vonrhein)
# revision 0.37 . Keitaro 10/2013 - fix for MX225HS detector on SPring-8 BL32XU (Ignore case when matching marccd in header); but cannot decide reverse-phi because of missing detector S/N
# revision 0.38 . KD 2/2014 - change defaults for REFINE(IDXREF) and REFINE(INTEGRATE) such that more stable results are obtained for difficult datasets
# revision 0.39 . Keitaro 4/2014 - automatically set ROTATION_AXIS=-1 0 0 for MX225HS at SPring-8 BL32XU.
# revision 0.40 . Jan Gebauer /KD 4/2014 - simple implementation of MAR345 detector
# revision 0.41 . recognize header starting with R-AXIS instead of RAXIS
# revision 0.42 . Keitaro 5/2014 automatically set ROTATION_AXIS=-1 0 0 for Q315 at SPring-8 BL38B1.
# revision 0.43 . Keitaro 5/2014 add experimental support of dTREK format (raxis_smv)
# revision 0.44 . Keitaro 5/2014 automatically set ROTATION_AXIS=-1 0 0 for PILATUS3 at SPring-8 BL41XU.
# revision 0.45 . KD cope with blanks in filenames
# revision 0.46 . Keitaro 6/2014 automatically set ROTATION_AXIS=-1 0 0 for Mar225 at SPring-8 BL26B2.
# revision 0.47 . Keitaro 7/2014 more generic dTREK format support (Saturn and RAXIS)
# revision 0.48 . Kip Guja 11/2014 add detector serial number for ALS 5.0.2 to beam center convention 1 
# revision 0.49 . Nobuhisa 2/2015 add  detector serial number for AichiSR BL2S1 to beam center convention 1
# revision 0.50 . KD 03/2015 workaround for Mar-1 change of parameter name "DISTANCE" to "POSITION" in REFINE(*) keywords
# revision 0.51 . Keitaro 03/2015 add .gz and .xz support and remove limitation - frame numbers can start with any.
# revision 0.52 . Keitaro 05/2015 fix ADSC beam center convention for SPring-8 and DET_SN acquisition for PILATUS (didn't work on Mac)
# revision 0.53 . KD add ADSC beam center convention for APS Argonne but only as commented line in XDS.INP (not detector serial no)
# revision 0.54 . KD add ADSC S/N 911 for APS Argonne
REVISION="0.54 (27-Nov-2015)"
#                                                                                                            
# usage: e.g. generate_XDS.INP "/file/system/frms/mydata_1_???.img"                                                       
# make sure to have the two quotation marks !                                                                
# the ? are wildcards for the frame numbers.                                                                 
#                                                                                                            
# known problems:                                                                                            
# - for ADSC detectors, there are at least three ways to obtain ORGX and ORGY values from the header (see below);
# - the same might be a problem for MAR headers, too (not sure about this) 
# - on Mac OS X, Xcode (from http://developer.apple.com/tools/xcode) might be needed - not sure about this                               
#                                                                                                                
# notes for debugging of the script:                                                                             
# - add the -v option to the first line, to see where an error occurs                                            
# - comment out the removal of tmp1 and tmp2 in the last line                                                    
#                                                                                                                
# ====== Start of script ======                                                                                  
echo generate_XDS.INP version $REVISION . Obtain the latest version from                                         
echo http://strucbio.biologie.uni-konstanz.de/xdswiki/index.php/generate_XDS.INP                                 
if [ "$1" == "help" ] || [ "$1" == "-help" ] || [ "$1" == "-h" ]; then                                           
  echo usage: generate_XDS.INP \"/file/system/frms/mydata_1_???.img\"   \(_with_ the quotation marks!\)                       
  echo if the frames are compressed with bzip2, leave out the .bz2 extension!                                    
  exit                                                                                                           
fi                                                                                                               
#                                                                                                                
# defaults:                                                                                                      
#                                                                                                                
DETECTOR="XXX MINIMUM_VALID_PIXEL_VALUE=XXX OVERLOAD=XXX"                                                        
REVERSE_PHI="no"
ORGX=XXX                                                                                                         
ORGY=XXX                                                                                                         
DETECTOR_DISTANCE=XXX                                                                                            
OSCILLATION_RANGE=XXX                                                                                            
X_RAY_WAVELENGTH=XXX                                                                                             
QX=XXX                                                                                                           
QY=XXX                                                                                                           
NX=XXX                                                                                                           
NY=XXX                                                                                                           
SENSOR_THICKNESS=0   
TRUSTED_REGION="0.0 1.2 ! partially use corners of detector (0 1.4143: use all pixels)"                                                                                           
# see how we are called:                                                                                         
NAME_TEMPLATE_OF_DATA_FRAMES="$1"
# list frames matching the wildcards in NAME_TEMPLATE_OF_DATA_FRAMES
# don't accept the "direct beam" shot at SLS/Pilatus PX-I and PX-II 
# cope with blanks in directory / file name
IFS=$'\n'
/bin/ls -C1 $1 $1.bz2 $1.gz $1.xz 2>/dev/null | egrep -v "_00000.cbf|_000.img" > tmp1 || exit 1
unset IFS
# we can continue - the frames are found

# Find the first '?' position and the number of '?' to determine DATA_RANGE=.
pos1=`echo "$1" | awk '{print index($0, "?")}'`
pos2=`echo "$1" | sed -e "s/[^\?]//g" | awk '{print length+'$pos1' - 1'}`
data_first=`cat tmp1 | cut -b $pos1-$pos2 | head -n1|bc`
data_last=`cat tmp1 | cut -b $pos1-$pos2 | tail -n1|bc`
DATA_RANGE="$data_first $data_last"

# set SPOT_RANGE to first half of DATA_RANGE
data_num=`wc -l tmp1 | awk '{print $1}'`
data_half=`echo "scale=0; $data_num/2" | bc -l`                        
data_half=`echo "if ($data_half<=1) 1;if ($data_half>1) $data_half" | bc -l`
spot_last=`echo "scale=0; $data_first+$data_half-1" | bc -l`
SPOT_RANGE="$data_first $spot_last"

echo DATA_RANGE=$DATA_RANGE

# find out detector type
DET=XXX              
IFS=$'\n'    
FIRSTFRAME=`head -1 tmp1`
echo $FIRSTFRAME | grep -q '\.bz2$' && bzcat $FIRSTFRAME > tmp1 && FIRSTFRAME=tmp1
# for mac/linux compatibility. zcat foo.gz doesn't work on mac.
echo $FIRSTFRAME | grep -q '\.gz$' && zcat < $FIRSTFRAME > tmp1 && FIRSTFRAME=tmp1
echo $FIRSTFRAME | grep -q '\.xz$' && xzcat $FIRSTFRAME > tmp1 && FIRSTFRAME=tmp1

strings $FIRSTFRAME > tmp2   
unset IFS
# TODO: whenever FIRSTFRAME is used below, it should be copied to tmp2 (using IFS as above), and tmp2 should be used instead
# this was done for "mccd", but still has to be done for the "raxis" detector types
# the reason is that FIRSTFRAME may contain a blank, which makes some commands fail                                                
egrep -qi 'marccd|Corrected' tmp2 && DET=mccd                                 
grep -q PILATUS tmp2             && DET=pilatus                              
grep -q BEAM_CENTER_X tmp2       && DET=adsc                                 
head -n1 tmp2 | grep -q "^RAXIS" && DET=raxis
head -n1 tmp2 | grep -q "^R-AXIS" && DET=raxis
grep -q "^SOURCE_WAVELENGTH=1" tmp2 && DET=dtrek
grep -q mar345 tmp2		 && DET=MAR345                         

# identify other detector types in the same way 

# parse ASCII header of first frame

if [ "$DET" == "XXX" ]; then
  echo "this is not a MAR, ADSC/SMV or PILATUS detector - fill in XXX values manually!"
  DETECTOR="XXX MINIMUM_VALID_PIXEL_VALUE=XXX OVERLOAD=XXX"                            

# find parameters of first frame
elif [ "$DET" == "mccd" ]; then 
  echo Data from a MarCCD detector
                                  
  DETECTOR="CCDCHESS MINIMUM_VALID_PIXEL_VALUE= 1 OVERLOAD= 65500"
  SENSOR_THICKNESS=0.01                                           
  # use first frame of dataset to obtain parameters               

  # Check detector serial number and recognize beamline for reversed-phi setting.
  # Known detectors for reversed-phi in SPring-8: 24: BL26B2 Mar225, 31: BL32XU MX225HE, 38: BL44XU MX225HE, 42: BL44XU MX300HE, 40: BL41XU MX225HE, 106: BL32XU MX225HS
  REVERSEPHI_SNs="
24
31
38
40
42
106
"
  # get detector serial number and check if it is included in the list
  DET_SN=`grep "Detector Serial Number =" tmp2 | sed "s/Detector Serial Number = //"`
  if echo "${DET_SN}${REVERSEPHI_SNs}" | sort | uniq -d | grep [0-9] > /dev/null; then
    REVERSE_PHI="yes"
  fi

  # offsets are documented; values can be found in mccd_xdsparams.pl script
  IFS=$'\n'
  cp $FIRSTFRAME tmp2
  unset IFS
  let SKIP=1024+80                                                        
  NX=$(od -t dI -j $SKIP -N 4 tmp2 | head -1 | awk '{print $2}')
  let SKIP=$SKIP+4                                                                         
  NY=$(od -t dI -j $SKIP -N 4 tmp2 | head -1 | awk '{print $2}')

  let SKIP=1720
  DETECTOR_DISTANCE=$(od -t dI -j $SKIP -N 4 tmp2 | head -1 | awk '{print $2}')
  DETECTOR_DISTANCE=`echo "scale=3; $DETECTOR_DISTANCE/1000" | bc -l`                                     
                                                                                                          
  let SKIP=1024+256+128+256+4                                                                             
  ORGX=$(od -t dI -j $SKIP -N 4 tmp2 | head -1 | awk '{print $2}')             
  ORGX=`echo "scale=2; $ORGX/1000" | bc -l `                                                              
  let SKIP=$SKIP+4                                                                                        
  ORGY=$(od -t dI -j $SKIP -N 4 tmp2 | head -1 | awk '{print $2}')             
  ORGY=`echo "scale=2; $ORGY/1000" | bc -l `                                                              
# FIXME 25.5.2015: reverse ORGX and ORGY for BM14 @ ESRF (no serial number in header)

  let SKIP=1024+736
  OSCILLATION_RANGE=$(od -t dI -j $SKIP -N 4 tmp2 | head -1 | awk '{print $2}') 
  OSCILLATION_RANGE=`echo "scale=3; $OSCILLATION_RANGE/1000" | bc -l`   
                                                                                                 
  let SKIP=1024+256+128+256+128+4                                                                
  QX=$(od -t dI -j $SKIP -N 4 tmp2 | head -1 | awk '{print $2}')      
  QX=`echo "scale=10; $QX/1000000" |bc -l `                                                      
  let SKIP=$SKIP+4                                                                               
  QY=$(od -t dI -j $SKIP -N 4 tmp2 | head -1 | awk '{print $2}')      
  QY=`echo "scale=10; $QY/1000000" |bc -l `                                                      

  let SKIP=1024+256+128+256+128+128+12
  X_RAY_WAVELENGTH=$(od -t dI -j $SKIP -N 4 tmp2 | head -1 | awk '{print $2}')
  X_RAY_WAVELENGTH=`echo "scale=5; $X_RAY_WAVELENGTH/100000" | bc -l`                                    

# at most BLs, ORGX and ORGY are in pixels, but sometimes in mm ... guess:
  NXBYFOUR=`echo "scale=0; $NX/4" | bc -l `                               
  ORGXINT=`echo "scale=0; $ORGX/1" | bc -l `                              
  if [ $ORGXINT -lt $NXBYFOUR ]; then                                     
     ORGX=`echo "scale=1; $ORGX/$QX" | bc -l`                             
     ORGY=`echo "scale=1; $ORGY/$QY" | bc -l`                             
     echo MARCCD detector: header ORGX, ORGY seem to be in mm ... converting to pixels
  else                                                                                
     echo MARCCD detector: header ORGX, ORGY seem to be in pixel units                
  fi                                                                                  

elif [ "$DET" == "adsc" ]; then

  DETECTOR="ADSC MINIMUM_VALID_PIXEL_VALUE= 1 OVERLOAD= 65000"
  echo Data from ADSC detector. Obtaining ORGX, ORGY depends on beamline setup:
  SENSOR_THICKNESS=0.01                                                        
  sed s/\;// tmp2 > tmp1                                                       
  mv tmp1 tmp2                                                                 

      # find X_RAY_WAVELENGTH:
      X_RAY_WAVELENGTH=`grep WAVELENGTH tmp2 | head -1 | sed s/WAVELENGTH=//`

      # find NX, QX, ORGX and ORGY:
      NX=`grep SIZE1 tmp2 | tail -1 | sed s/SIZE1=//`
      QX=`grep PIXEL_SIZE tmp2 | sed s/PIXEL_SIZE=//`
# FIXME - next 2 lines should be done properly, from header
      NY=$NX                                               
      QY=$QX                                               
      BEAM_CENTER_X=`grep BEAM_CENTER_X tmp2 | sed s/BEAM_CENTER_X=//`
      BEAM_CENTER_Y=`grep BEAM_CENTER_Y tmp2 | sed s/BEAM_CENTER_Y=//`
# fix 2010-04-26 - tell user about possible ORGX, ORGY alternatives -  
      COMMENT_ORGXY="
! Following are possible beam center interpretations for ADSC detectors"
# at ESRF, PF, ALS 5.0.2 and ... (pls fill in!) the following should be used:         
      ORGX1=`echo "scale=1; $BEAM_CENTER_Y/$QX" | bc -l`
      ORGY1=`echo "scale=1; $BEAM_CENTER_X/$QX" | bc -l`
      echo - at ESRF, PF, APS Argonne BLs use: ORGX=$ORGX1 ORGY=$ORGY1                    
      COMMENT_ORGXY="${COMMENT_ORGXY}
! ORGX= $ORGX1 ORGY= $ORGY1 ! For ESRF, PF, APS Argonne ..."
# this 2nd alternative convention should be used at the following beamlines (pls complete the list): ALS 5.0.3, ...
      ORGX2=`echo "scale=1; $NX-$BEAM_CENTER_X/$QX" | bc -l `
      ORGY2=`echo "scale=1; $BEAM_CENTER_Y/$QX" | bc -l `
      echo - at e.g. ALS 5.0.3 use: ORGX=$ORGX2 ORGY=$ORGY2                                                          
      COMMENT_ORGXY="${COMMENT_ORGXY}
! ORGX= $ORGX2 ORGY= $ORGY2 ! For ALS 5.0.3,.."
# this 3rd alternative convention should be used at the following beamlines (pls complete the list): ALS 8.2.2, ... 
# this alternative is written into the generated XDS.INP ! You have to correct this manually in XDS.INP, or adjust this script.
      ORGX3=`echo "scale=1; $BEAM_CENTER_X/$QX" | bc -l `
      ORGY3=`echo "scale=1; $NX-$BEAM_CENTER_Y/$QX" | bc -l `
      echo - at e.g. ALS 8.2.2 use: ORGX=$ORGX3 ORGY=$ORGY3 - this is written to XDS.INP if beamline is not detected
      COMMENT_ORGXY="${COMMENT_ORGXY}
! ORGX= $ORGX3 ORGY= $ORGY3 ! For ALS 8.2.2,.."
# this 4th alternative convention should be used at the following beamlines (pls complete the list): SPring-8, ...
      ORGX4=`echo "scale=1; $BEAM_CENTER_X/$QX" | bc -l `
      ORGY4=`echo "scale=1; $BEAM_CENTER_Y/$QX" | bc -l `
      echo - at e.g. SPring-8 use: ORGX=$ORGX4 ORGY=$ORGY4 
      COMMENT_ORGXY="${COMMENT_ORGXY}
! ORGX= $ORGX4 ORGY= $ORGY4 ! For SPring-8,.."

      # Decision of beam center convention based on detector serial numbers.
      DET_SN=`grep DETECTOR_SN tmp2 | sed -e "s/DETECTOR_SN=//"`
      # For convention 1; Known PF detectors = 449: NW12A Q210, 472: NE3A Q270, 474: BL17A Q270, 912: BL5A Q315, 923: ALS BL5.0.2 Q315, 933: AichiSR BL2S1 Q315
      ORG1_SNs="
449
472
474
912
923
933
911
"
      ORG4_SNs="
915
"
      if echo "${DET_SN}${ORG1_SNs}" | sort | uniq -d | grep [0-9] > /dev/null; then
       ORGX=$ORGX1
       ORGY=$ORGY1
      elif echo "${DET_SN}${ORG4_SNs}" | sort | uniq -d | grep [0-9] > /dev/null; then
       ORGX=$ORGX4
       ORGY=$ORGY4
      else
       ORGX=$ORGX3
       ORGY=$ORGY3
      fi

      # Check detector serial number and recognize beamline for reversed-phi setting.
      # Known detectors for reversed-phi in SPring-8: 915: BL38B1 Q315
      REVERSEPHI_SNs="
915
"
      if echo "${DET_SN}${REVERSEPHI_SNs}" | sort | uniq -d | grep [0-9] > /dev/null; then
        REVERSE_PHI="yes"
      fi

      # find DETECTOR_DISTANCE and OSCILLATION_RANGE:                                                                                
      DETECTOR_DISTANCE=`grep DISTANCE tmp2 | sed s/DISTANCE=//`                                                                     
      OSCILLATION_RANGE=`grep OSC_RANGE tmp2 | sed s/OSC_RANGE=//`                                                                   

elif [ "$DET" == "pilatus" ]; then
  DETECTOR="PILATUS MINIMUM_VALID_PIXEL_VALUE=0 OVERLOAD= 1048576  !PILATUS"
  QX=0.172 QY=0.172                                                         
  echo Data from a Pilatus detector                                         
  sed s/#// tmp2 > tmp1                                                     
  mv tmp1 tmp2                                                              

      # find SENSOR_THICKNESS:
      SENSOR_THICKNESS=`grep thickness tmp2 | sed -e s/'Silicon sensor, thickness'// | awk '{print $1*1000}'`
      # find X_RAY_WAVELENGTH:                                                                               
      X_RAY_WAVELENGTH=`grep Wavelength tmp2 | sed -e s/Wavelength// -e s/A// | awk '{print $1}'`            

      # find NX and NY; 2463/2527 is 6M, 1475/1679 is 2M
      NX=`grep X-Binary-Size-Fastest-Dimension tmp2 | awk '{print $2}'`
      NY=`grep X-Binary-Size-Second-Dimension tmp2 | awk '{print $2}'` 

      # find ORGX and ORGY:
      ORGX=`grep Beam_xy tmp2 | sed -e s/\(// -e s/\)// -e s/\,// | awk '{print $2}'`
      ORGY=`grep Beam_xy tmp2 | sed -e s/\(// -e s/\)// -e s/\,// | awk '{print $3}'`

      # find DETECTOR_DISTANCE and OSCILLATION_RANGE:
      DETECTOR_DISTANCE=`awk '/distance/{print $2}' tmp2`
      DETECTOR_DISTANCE=`echo "$DETECTOR_DISTANCE*1000" | bc -l`

      OSCILLATION_RANGE=`awk '/Angle/{print $2}' tmp2`

      # get detector serial number and check if it is included in the list
      # Known detectors for reversed-phi in SPring-8: BL41XU PILATUS3 6M 60-0125
      DET_SN=`grep "Detector:" tmp2 | sed "s/^.*Detector: *//"`
      REVERSEPHI_SNs="
PILATUS3 6M, S/N 60-0125
"
      if echo "${DET_SN}${REVERSEPHI_SNs}" | sort | uniq -d | grep [0-9] > /dev/null; then
        REVERSE_PHI="yes"
      fi

elif [ "$DET" == "raxis" ]; then
  echo Data from a RAXIS detector

  DETECTOR="RAXIS MINIMUM_VALID_PIXEL_VALUE=0  OVERLOAD=2000000"
  #let SKIP=768
  #NX=$(od -t x -j $SKIP -N 4 $FIRSTFRAME |awk 'NR==1{print toupper($2)}'|perl -nle '@array= $_ =~/.{2}/g; print "ibase=16;obase=A;".join("",reverse @array)'|bc)
  NX=$(python -c 'import struct; f=open("'$FIRSTFRAME'","rb");f.seek(768);print "%.4d"%struct.unpack(">i",f.read(4))')
  NY=$(python -c 'import struct; f=open("'$FIRSTFRAME'","rb");f.seek(772);print "%.4d"%struct.unpack(">i",f.read(4))')

  DETECTOR_DISTANCE=$(python -c 'import struct; f=open("'$FIRSTFRAME'","rb");f.seek(344);print "-%.4f"%struct.unpack(">f",f.read(4))')

  ORGX=$(python -c 'import struct; f=open("'$FIRSTFRAME'","rb");f.seek(540);print "%.4f"%struct.unpack(">f",f.read(4))')
  ORGY=$(python -c 'import struct; f=open("'$FIRSTFRAME'","rb");f.seek(544);print "%.4f"%struct.unpack(">f",f.read(4))')

  OSCILLATION_RANGE=$(python -c 'import struct; f=open("'$FIRSTFRAME'","rb");f.seek(524);phis,phie=struct.unpack(">ff",f.read(8));print "%.4f"%(phie-phis)')

  QX=$(python -c 'import struct; f=open("'$FIRSTFRAME'","rb");f.seek(776);print "%.6f"%struct.unpack(">f",f.read(4))')
  QY=$(python -c 'import struct; f=open("'$FIRSTFRAME'","rb");f.seek(780);print "%.6f"%struct.unpack(">f",f.read(4))')

  X_RAY_WAVELENGTH=$(python -c 'import struct; f=open("'$FIRSTFRAME'","rb");f.seek(292);print "%.6f"%struct.unpack(">f",f.read(4))')

elif [ "$DET" == "dtrek" ]; then
  echo "Data from a RAXIS or Saturn detector with dTREK format"

  sed s/\;// tmp2 > tmp1
  mv tmp1 tmp2

  dname=`grep "DETECTOR_NAMES=" tmp2 | sed -e "s/.*=//"`

  if [ "$dname" == "CCD_" ]; then
    DETECTOR="SATURN MINIMUM_VALID_PIXEL_VALUE=1"
    dtrek_det="saturn"
  else # should be RX_
    DETECTOR="RAXIS MINIMUM_VALID_PIXEL_VALUE=0"
    dtrek_det="raxis"
  fi

  # Saturn on SPring-8 BL26B1
  if grep "CCD_DETECTOR_IDENTIFICATION=MSC_REIT_A200_SN09250183" tmp2 > /dev/null; then
   REVERSE_PHI="yes"
  fi

  # find OVERLOAD
  SATURATED_VALUE=`grep SATURATED_VALUE tmp2 | head -1 | sed s/SATURATED_VALUE=//`
  DETECTOR="${DETECTOR}  OVERLOAD=${SATURATED_VALUE}"

  # find X_RAY_WAVELENGTH:
  X_RAY_WAVELENGTH=(`grep SOURCE_WAVELENGTH tmp2 | head -1 | sed s/SOURCE_WAVELENGTH=//`)
  X_RAY_WAVELENGTH=${X_RAY_WAVELENGTH[1]}

  # find NX,NY,QX,QY
  # NX,NY should be read from *_DETECTOR_DIMENSIONS?
  NX=`grep SIZE1 tmp2 | tail -1 | sed s/SIZE1=//`
  NY=`grep SIZE2 tmp2 | tail -1 | sed s/SIZE2=//`
  DET_SIZE=(`grep ${dname}DETECTOR_SIZE tmp2 | tail -1 | sed s/.*_DETECTOR_SIZE=//`)
  QX=`echo "scale=6; ${DET_SIZE[0]} / $NX" | bc -l`
  QY=`echo "scale=6; ${DET_SIZE[1]} / $NY" | bc -l`

  # find ORGX, ORGY
  SPATIAL_DISTORTION_INFO=(`grep ${dname}SPATIAL_DISTORTION_INFO tmp2 | tail -1 | sed s/.*_SPATIAL_DISTORTION_INFO=//`)
  ORGX=${SPATIAL_DISTORTION_INFO[0]}
  ORGY=${SPATIAL_DISTORTION_INFO[1]}

  # find DETECTOR_DISTANCE 
  GONIO_NAMES=(`grep ${dname}GONIO_NAMES tmp2 | tail -1 | sed s/.*_GONIO_NAMES=//`)
  GONIO_VALUES=(`grep ${dname}GONIO_VALUES tmp2 | tail -1 | sed s/.*_GONIO_VALUES=//`)
  GONIO_UNITS=(`grep ${dname}GONIO_UNITS tmp2 | tail -1 | sed s/.*_GONIO_UNITS=//`)
  for i in `seq 1 ${#GONIO_NAMES[*]}`
  do
   idx=$((i-1))
   if [ "${GONIO_NAMES[$idx]}" == "Distance" ]; then
    DETECTOR_DISTANCE="-${GONIO_VALUES[$idx]}"
    # TODO: Check unit!! - ${GONIO_UNITS[$idx]}
   fi
  done

  # find OSCILLATION_RANGE
  ROTATION=(`grep "^ROTATION=" tmp2 | tail -1 | sed s/ROTATION=//`)
  OSCILLATION_RANGE=${ROTATION[2]}

  # NOTE: how can we know correct ROTATION_AXIS=?

elif [ "$DET" == "MAR345" ]; then
  
 echo  "Data from a Mar345 image plate detector"
 DETECTOR="MAR345  MINIMUM_VALID_PIXEL_VALUE=0  OVERLOAD=130000"
 NX=`awk '/FORMAT/{print $2}' tmp2`
 NY=$NX
 QX=`awk '/PIXEL/{print $3/1000.}' tmp2`
 QY=`awk '/PIXEL/{print $5/1000.}' tmp2`
 if grep -q 'CENTER' tmp2; then
   echo Beam center found.
   ORGX=`grep 'CENTER' tmp2 | awk '{print $3}'`
   ORGY=`grep 'CENTER' tmp2 | awk '{print $5}'`
 else
   echo No beam center was found. Setting beam center to the middle of the detector.
   ORGX=`echo $NX / 2 | bc`
   ORGY=`echo $NY / 2 | bc`
 fi
 DETECTOR_DISTANCE=`grep 'DISTANCE' tmp2 | awk '{print $2}'`               
 X_RAY_WAVELENGTH=`grep 'WAVELENGTH' tmp2 | awk '{print $2}'` 
 OSCILLATION_RANGE=`grep 'PHI' tmp2 | awk '{print $5-$3}'`    
 TRUSTED_REGION="0 0.99"        
else
  echo should never come here
  exit 1                     
fi                           

echo ORGX= $ORGX ORGY= $ORGY - check these values with adxv !
echo DETECTOR_DISTANCE= $DETECTOR_DISTANCE                   
echo OSCILLATION_RANGE= $OSCILLATION_RANGE                   
echo X-RAY_WAVELENGTH= $X_RAY_WAVELENGTH                     

# now we know everything that is required to generate XDS.INP

cat > XDS.INP << eof
! written by generate_XDS.INP version $REVISION
JOB= XYCORR INIT COLSPOT IDXREF DEFPIX INTEGRATE CORRECT
ORGX= $ORGX ORGY= $ORGY  ! check these values with adxv !\
$COMMENT_ORGXY
DETECTOR_DISTANCE= $DETECTOR_DISTANCE                    
OSCILLATION_RANGE= $OSCILLATION_RANGE                    
X-RAY_WAVELENGTH= $X_RAY_WAVELENGTH                      
NAME_TEMPLATE_OF_DATA_FRAMES=$NAME_TEMPLATE_OF_DATA_FRAMES
! REFERENCE_DATA_SET=xxx/XDS_ASCII.HKL ! e.g. to ensure consistent indexing  
DATA_RANGE=$DATA_RANGE                                                     
SPOT_RANGE=$SPOT_RANGE                                                     
! BACKGROUND_RANGE=1 10 ! rather use defaults (first 5 degree of rotation)   

SPACE_GROUP_NUMBER=0                   ! 0 if unknown
UNIT_CELL_CONSTANTS= 70 80 90 90 90 90 ! put correct values if known
INCLUDE_RESOLUTION_RANGE=50 0  ! after CORRECT, insert high resol limit; re-run CORRECT
! IDXREF now obeys INCLUDE_RESOLUTION_RANGE and EXCLUDE_RESOLUTION_RANGE to exclude ice-rings

FRIEDEL'S_LAW=FALSE     ! This acts only on the CORRECT step
! If the anom signal turns out to be, or is known to be, very low or absent,
! use FRIEDEL'S_LAW=TRUE instead (or comment out the line); re-run CORRECT

! remove the "!" in the following line:
! STRICT_ABSORPTION_CORRECTION=TRUE
! if the anomalous signal is strong: in that case, in CORRECT.LP the three
! "CHI^2-VALUE OF FIT OF CORRECTION FACTORS" values are significantly> 1, e.g. 1.5
!
! exclude (mask) untrusted areas of detector, e.g. beamstop shadow :
! UNTRUSTED_RECTANGLE= 1800 1950 2100 2150 ! x-min x-max y-min y-max ! repeat
! UNTRUSTED_ELLIPSE= 2034 2070 1850 2240 ! x-min x-max y-min y-max ! if needed
! UNTRUSTED_QUADRILATERAL= x1 y1 x2 y2 x3 y3 x4 y4 ! see documentation
!
! parameters with changes wrt default values:
TRUSTED_REGION=$TRUSTED_REGION
VALUE_RANGE_FOR_TRUSTED_DETECTOR_PIXELS=6000. 30000. ! often 7000 or 8000 is ok
STRONG_PIXEL=4           ! COLSPOT: only use strong reflections (default is 3)
MINIMUM_NUMBER_OF_PIXELS_IN_A_SPOT=3 ! default of 6 is sometimes too high
! close spots/long cell axis: reduce SEPMIN and CLUSTER_RADIUS from their defaults of 6 and 3
! SEPMIN=4  CLUSTER_RADIUS=2 ! should be default for Pilatus and other detectors with low PSF
! since XDS 01-MAR-2015, POSITION is used instead of DISTANCE. Older versions do it the other way round.
! nowadays headers are usually correct so refine DISTANCE/POSITION in INTEGRATE but not IDXREF
REFINE(IDXREF)=CELL BEAM ORIENTATION AXIS ! DISTANCE POSITION
REFINE(INTEGRATE)= DISTANCE POSITION BEAM ORIENTATION ! AXIS CELL
! REFINE(CORRECT)=CELL BEAM ORIENTATION AXIS DISTANCE POSITION ! Default is: refine everything

! parameters specifically for this detector and beamline:
DETECTOR= $DETECTOR
SENSOR_THICKNESS= $SENSOR_THICKNESS
! attention CCD detectors: for very high resolution (better than 1A) make sure to specify SILICON
! as about 32* what CORRECT.LP suggests (absorption of phosphor is much higher than that of silicon).
! Better: read the article http://strucbio.biologie.uni-konstanz.de/xdswiki/index.php/SILICON .
NX= $NX NY= $NY  QX= $QX  QY= $QY ! to make CORRECT happy if frames are unavailable

eof
if [ "$DET" == "raxis" -o "$dtrek_det" == "raxis" ]; then
 cat >> XDS.INP << eof
DIRECTION_OF_DETECTOR_X-AXIS=1 0 0
DIRECTION_OF_DETECTOR_Y-AXIS=0 -1 0
INCIDENT_BEAM_DIRECTION=0 0 1
ROTATION_AXIS=0 1 0
!FRACTION_OF_POLARIZATION=0.98   ! uncomment if synchrotron
POLARIZATION_PLANE_NORMAL=1 0 0
eof
else
 if [ "$REVERSE_PHI" == "no" ]; then
  echo 'ROTATION_AXIS=1 0 0  ! at e.g. Australian Synchrotron, SERCAT ID-22 (?), APS 19-ID (?), ESRF BM30A, SPring-8, SSRF BL17U this needs to be -1 0 0' >> XDS.INP
 else
  echo 'ROTATION_AXIS=-1 0 0  ! if this is wrong, please contact author.' >> XDS.INP
 fi
 if [ "$dtrek_det" == "saturn" ]; then
 cat >> XDS.INP << eof
DIRECTION_OF_DETECTOR_X-AXIS=-1 0 0
DIRECTION_OF_DETECTOR_Y-AXIS= 0 1 0
eof
 else
 cat >> XDS.INP << eof
DIRECTION_OF_DETECTOR_X-AXIS=1 0 0
DIRECTION_OF_DETECTOR_Y-AXIS=0 1 0
eof
 fi
 cat >> XDS.INP << eof
INCIDENT_BEAM_DIRECTION=0 0 1
FRACTION_OF_POLARIZATION=0.98   ! better value is provided by beamline staff!
POLARIZATION_PLANE_NORMAL=0 1 0
eof
fi
cat >> XDS.INP << eof
!used by DEFPIX and CORRECT to exclude ice-reflections / ice rings - uncomment if necessary
!EXCLUDE_RESOLUTION_RANGE= 3.93 3.87 !ice-ring at 3.897 Angstrom
!EXCLUDE_RESOLUTION_RANGE= 3.70 3.64 !ice-ring at 3.669 Angstrom
!EXCLUDE_RESOLUTION_RANGE= 3.47 3.41 !ice-ring at 3.441 Angstrom
!EXCLUDE_RESOLUTION_RANGE= 2.70 2.64 !ice-ring at 2.671 Angstrom
!EXCLUDE_RESOLUTION_RANGE= 2.28 2.22 !ice-ring at 2.249 Angstrom
!EXCLUDE_RESOLUTION_RANGE= 2.102 2.042 !ice-ring at 2.072 Angstrom - strong
!EXCLUDE_RESOLUTION_RANGE= 1.978 1.918 !ice-ring at 1.948 Angstrom - weak
!EXCLUDE_RESOLUTION_RANGE= 1.948 1.888 !ice-ring at 1.918 Angstrom - strong
!EXCLUDE_RESOLUTION_RANGE= 1.913 1.853 !ice-ring at 1.883 Angstrom - weak
!EXCLUDE_RESOLUTION_RANGE= 1.751 1.691 !ice-ring at 1.721 Angstrom - weak
eof
if [ "$DET" == "pilatus" ]; then
cat >> XDS.INP << eof
SEPMIN=4 CLUSTER_RADIUS=2
NUMBER_OF_PROFILE_GRID_POINTS_ALONG_ALPHA/BETA=13 ! Default is 9 - Increasing may improve data 
NUMBER_OF_PROFILE_GRID_POINTS_ALONG_GAMMA=13      ! accuracy, particularly if finely-sliced on phi, 
!                                                   and does not seem to have any downsides. 
eof
  if [ $NX == "1475" ]; then
    if ! grep -q Flat_field tmp2 ; then
    cat >> XDS.INP << eof
! the following specifications are for a detector _without_ proper
! flat_field correction; they cut away one additional pixel adjacent 
! to each UNTRUSTED_RECTANGLE
!EXCLUSION OF VERTICAL DEAD AREAS OF THE PILATUS 2M DETECTOR
UNTRUSTED_RECTANGLE= 486  496     0 1680
UNTRUSTED_RECTANGLE= 980  990     0 1680
!EXCLUSION OF HORIZONTAL DEAD AREAS OF THE PILATUS 2M DETECTOR
UNTRUSTED_RECTANGLE=   0 1476   194  214
UNTRUSTED_RECTANGLE=   0 1476   406  426
UNTRUSTED_RECTANGLE=   0 1476   618  638
UNTRUSTED_RECTANGLE=   0 1476   830  850
UNTRUSTED_RECTANGLE=   0 1476  1042 1062
UNTRUSTED_RECTANGLE=   0 1476  1254 1274
UNTRUSTED_RECTANGLE=   0 1476  1466 1486
eof
    else
    cat >> XDS.INP << eof
!EXCLUSION OF VERTICAL DEAD AREAS OF THE PILATUS 2M DETECTOR
UNTRUSTED_RECTANGLE= 487  495     0 1680
UNTRUSTED_RECTANGLE= 981  989     0 1680
!EXCLUSION OF HORIZONTAL DEAD AREAS OF THE PILATUS 2M DETECTOR
UNTRUSTED_RECTANGLE=   0 1476   195  213
UNTRUSTED_RECTANGLE=   0 1476   407  425
UNTRUSTED_RECTANGLE=   0 1476   619  637
UNTRUSTED_RECTANGLE=   0 1476   831  849
UNTRUSTED_RECTANGLE=   0 1476  1043 1061
UNTRUSTED_RECTANGLE=   0 1476  1255 1273
UNTRUSTED_RECTANGLE=   0 1476  1467 1485
eof
    fi
  elif [ $NX == "2463" ]; then
# Pilatus 6M
# FIXME: here we could test if a Flat_field correction was applied like we do for 2M
    cat >> XDS.INP << eof
UNTRUSTED_RECTANGLE= 487  495     0 2528
UNTRUSTED_RECTANGLE= 981  989     0 2528
UNTRUSTED_RECTANGLE=1475 1483     0 2528
UNTRUSTED_RECTANGLE=1969 1977     0 2528
UNTRUSTED_RECTANGLE=   0 2464   195  213
UNTRUSTED_RECTANGLE=   0 2464   407  425
UNTRUSTED_RECTANGLE=   0 2464   619  637
UNTRUSTED_RECTANGLE=   0 2464   831  849
UNTRUSTED_RECTANGLE=   0 2464  1043 1061
UNTRUSTED_RECTANGLE=   0 2464  1255 1273
UNTRUSTED_RECTANGLE=   0 2464  1467 1485
UNTRUSTED_RECTANGLE=   0 2464  1679 1697
UNTRUSTED_RECTANGLE=   0 2464  1891 1909
UNTRUSTED_RECTANGLE=   0 2464  2103 2121
UNTRUSTED_RECTANGLE=   0 2464  2315 2333
eof
  fi
fi
echo XDS.INP is ready for use. The file has only the most important keywords.
echo Full documentation, including complete detector templates, is at
echo http://www.mpimf-heidelberg.mpg.de/~kabsch/xds . More documentation in XDSwiki
echo After running xds, inspect, using XDS-Viewer, at least the beamstop mask in
echo BKGPIX.cbf, and the agreement of predicted and observed spots in FRAME.cbf!
rm -f tmp1 tmp2
# end of generate_XDS.INP

System-wide or personal installation

Ask your system adminstrator to cut-and-paste the script into e.g. /usr/local/bin/generate_XDS.INP, and to make it "executable".

But you may also cut-and-paste the script from this webpage into a file in e.g. your home directory; the filename should be generate_XDS.INP. After creating the file, make it executable - e.g. if it's in your $HOME, use:

chmod +x ~/generate_XDS.INP

After that, you can just run it in a similar way as if it were installed in your $PATH:

~/generate_XDS.INP "frms/mydata_1_???.img"

By using your own file, you can easily update to the latest revision, or even change the script, without having to bother the system administrator.

On Mac OS X, installation of the "Command Line Tools" (from http://developer.apple.com/downloads; requires Apple ID) is required (I think). These are also part of the (larger, but also free) Xcode package. This package comes with a license that has to be accepted by the user when running a Command Line Tool for the first time.

Generating generate_XDS.INP from this webpage

Instead of cutting-and-pasting the lines of the script, you (or the system administrator) could just cut-and-paste the following four lines

 wget http://strucbio.biologie.uni-konstanz.de/xdswiki/index.php/generate_XDS.INP -O - | \
   sed -e s/\&nbsp\;/\ /g -e s/\&gt\;/\>/g -e s/\&lt\;/\</g -e s/amp\;//g -e s/\&quot\;/\"/g -e s/\&\#\1\6\0\;/\ /g | \
   sed '/# end of generate_XDS.INP/,$d' | awk '/^#/,/rm -f tmp1 tmp2/' > generate_XDS.INP
 chmod +x generate_XDS.INP

to copy the script from this website into an executable file generate_XDS.INP in your current directory. On a Mac (which does not seem to have wget), one could try

 curl -L -o - http://strucbio.biologie.uni-konstanz.de/xdswiki/index.php/generate_XDS.INP | \
   sed -e s/\&nbsp\;/\ /g -e s/\&gt\;/\>/g -e s/\&lt\;/\</g -e s/amp\;//g -e s/\&quot\;/\"/g -e s/\&\#\1\6\0\;/\ /g | \
   sed '/# end of generate_XDS.INP/,$d' | awk '/^#/,/rm -f tmp1 tmp2/' > generate_XDS.INP
 chmod +x generate_XDS.INP

If you do use cut-and-paste from the webpage, be aware of the following problem report: On the Mac, after loading frames, by clicking “generate XDS.INP”, the program gives some strange symbol “” in XDS.INP. And the more you click “save” button, the more “” appear. This looks like e.g.
SPACE_GROUP_NUMBER=0  ! 0 if unknown
UNIT_CELL_CONSTANTS= 70 80 90 90 90 90 Â
The problem is due to the “Rich text” format in TextEdit when saving "generate_XDS.INP". It is solved by re-downloading the script, and changing format to Plain - everything should work then.

Calling generate_XDS.INP from a Python script

It is recommended to use the subprocess.Popen() module instead of os.system():

 subprocess.Popen(["generate_XDS.INP",imagepath],stdout=outputfile)

where imagepath is a string containing the path to an image and outputfile is either a chosen variable for an output file or subprocess.PIPE if you're not interested in the output of the script.

The module os.system() internally uses /bin/sh to execute the command and overrides #!/bin/bash at the beginning of the script. While this is not a problem on most operating systems, /bin/sh points to dash instead of bash on Ubuntu, which leads to a program crash with the error message

 sh: 1: Syntax error: Bad fd number

Limitations

  • The script tries to interpret the header of the frames, so is currently limited to Dectris (Pilatus), ADSC (Quantum), Rigaku (several types) and MAR (CCD and image plate) detectors. Other detectors need some values to be manually filled into XDS.INP - the relevant places are marked with XXX. These are detector properties (type, pixel size and number, min and max counts in a pixel), and experimental parameters like oscillation range, wavelength, distance, and direct beam position (or rather: point of detector that is closest to the crystal). For fine-tuning of detector parameters, see the detector-specific templates.
  • The authors have made a "best effort" to provide a XDS.INP that results in the correct sign of the anomalous signal. In the case of one detector type (internally called Rigaku SMV) this requires reversal of one detector axis, and a negative DETECTOR_DISTANCE, as is found in some of the detector-specific templates. For an unusual or unknown detector setup, the correct sign of the anomalous signal needs to be established and verified e.g. with a good dataset from a test crystal that has a anomalous signal. The authors do not take any responsibility for problems arising from incorrect sign of the anomalous signal, nor - obviously! - for any other mischief arising in or from data processing.
  • At some beamlines, the ROTATION_AXIS should be -1 0 0 ("backwards") instead of the usual 1 0 0 ("horizontal"), or even 0 1 0 ("vertical") like at one of the PETRA Hamburg BLs. We have only just started the article Beamline notes, and the frame headers do not have this information, so the default chosen by generate_XDS.INP may be wrong and need manual correction.
  • Sometimes, the x- and y- values of the primary beam position recorded in the header should be used for ORGY and ORGX (i.e reversed) instead of as ORGX and ORGY. For ADSC, this has been implemented in the script for a number of beamlines. A beamline that is also known to have reversed X and Y is BM14 (ESRF; Mar CCD, as of April 2014) but the script does not currently implement the reversal.

See also