esrf

Beamline Instrument Software Support
SPEC Macro documentation: [ Macro Index | BCU Home ]

#$Log: take_powders.mac,v $
#Revision 1.25  2009/02/09 16:27:54  beteva
#changed _oscillation_prepare to osc_prepare and _oscillation to osc_scan to be able to use the same macro set with the microdiff
#
#Revision 1.24  2007/09/19 09:16:05  spruce
#archive directory for copying succesful powder calibrations changed from /data/idxxehx/archive to /data/pyarch/idxxehx/POWDERS
#
#Revision 1.23  2007/09/03 15:28:07  guijarro
#waitmove replaced by move_poll,
#RMSD value changed to 0.025
#
#Revision 1.22  2007/04/23 08:03:29  spruce
#add print to show where the powder files are being copied
#
#Revision 1.21  2007/01/26 16:16:05  spruce
#copy the powder.log to the archive (if successful) as well as images and xml file
#
#Revision 1.20  2006/10/06 14:55:32  spruce
#It occurred to me that we only want to copy successful powder runs to the archive, not all of them.
#
#Revision 1.19  2006/09/28 15:33:11  spruce
#from id29, only that the resulting archive images go to a dated directory
#
#Revision 1.17  2006/09/22 10:03:56  spruce
#process images in the inhouse directory, only copy results to the archive
#wait for the last image to appear on disk before processing
#
#Revision 1.16  2006/09/20 13:43:35  spruce
#added newline at end of file to powder.inp to fix bug in xml file generation
#.
#
#Revision 1.15  2006/09/07 10:22:16  guijarro
#compare distances with user values
#
#Revision 1.14  2006/07/11 14:10:51  spruce
#changed flag dark to TakeDarkFlag to be compatible with other macros
#
#Revision 1.13  2006/07/06 15:06:05  spruce
#corrections to process_results
#
#Revision 1.12  2006/07/05 13:08:01  blissadm
#new take powders which processes results and puts them back in the new mxlocal.xml file if they meet the requirements. Depends on new functions in xmlutils and mxutils
#
#Revision 1.11  2006/07/03 12:24:03  spruce
#new function process results is called after running the powder program.
#This generates the mxPowderCalibration results in the HardwareRep dir
#Changes are necessary in the mxutils(3.6) to use this file (or the mxlocal derived from it also in the HardwareRep dir)
#
#Revision 1.10  2006/06/15 12:28:46  guijarro
#remove suffix being added to detector files names (the filename should include the suffix directly)
#
#Revision 1.9  2006/05/11 12:28:00  spruce
#many changes for mxutils compatiblity
#
#Revision 1.8  2006/05/09 12:46:24  spruce
#forgot to change prodc_open/close_safety_shutter to shopen/shclose!
#
#Revision 1.7  2006/05/09 12:42:15  spruce
#one collect_par missed (for setting the darks on)
#
#Revision 1.6  2006/04/10 14:17:32  guijarro
#replaced collect_par with setMxSpecPars
#
#Revision 1.5  2006/03/07 13:30:07  beteva
#bug corrected; test for q4r added;
#
#Revision 1.4  2006/02/24 17:02:42  beteva
#added copying to the archive directory. Changes the setup parameters. Cleanup.
#
#Revision 1.3  2006/02/10 11:24:50  guijarro
#new version
#
#Revision 1.1  2005/04/15 15:23:50  beteva
#Initial revision
#
#%TITLE% TAKE_POWDERS.MAC
#%NAME% %B%take_powders.mac%B% - set of macros to take powders image
#%DESCRIPTION%
#%END%

#%UU% ()
#%MDESC% Init generic parameters
def powders_init() '{
global PDR_SUPPORTED_FORMATS[] PDR_POWDERPROG
global PDR_MSG[] PDR_DETMNE PDR_DET PDR_OK
global PDR_PHI_START PDR_PHI_STOP PDR_EXPOSURE
local type format

  PDR_SUPPORTED_FORMATS["q4"]="Q4unbin"
  PDR_SUPPORTED_FORMATS["q210"]="Q210bin"
  PDR_SUPPORTED_FORMATS["q315"]="Q315bin"
  PDR_SUPPORTED_FORMATS[165]="MCCD165"
  PDR_SUPPORTED_FORMATS[133]="MCCD133"
  PDR_SUPPORTED_FORMATS["mar225"]="MAR225"

  PDR_POWDERPROG = "/opt/pxsoft/bin/powder"

  PDR_DET = MXBCM_PARS["detector"]["type"]
  if (PDR_DET == "adsc") {
    type = MXBCM_PARS["detector"]["model"]
    if (index(type,"q4") != 0) {
      type = "q4"
      #force the mode to unbinned
      setMxSpecPars([0:"detector", 1:"binning"], 1)
    } else {
      #force the mode to hwbinned
      setMxSpecPars([0:"detector", 1:"binning"], 2)
    }

  } else if (PDR_DET == "marccd") {
    type = MXBCM_PARS["detector"]["model"]
  } else
    type = "mar225"

  format = PDR_SUPPORTED_FORMATS[type]
  PDR_DETMNE = getMxSpecPars("detdistmot")

  PDR_MSG["FORMAT"] = sprintf ("FORMAT %s\n", format)
  PDR_MSG["DVALUES"] = "DVALUES 3.645 2.672 2.487 2.181 1.819 1.676 1.467 1.433\n"
  PDR_MSG["DSTART"] = "DSTART 0.00001 0.1  0.1  0.0002   0.1    0.1   0.001    0.1  0.001\n"
  PDR_MSG["TOLERANCE"] = "TOLERANCE 0.0001\n"
  # the name of the xml file that will be generated by the powder program
	PDR_MSG["XMLFILE"] = "XMLFILE mxPowderCalibration.xml"

  PDR_PHI_START = 0
  PDR_PHI_STOP = 1
  PDR_EXPOSURE = 1

  PDR_OK = 0
}'

#%UU% [det_mne phi_start phi_end exp_time det_dev powder_prog]
#%MDESC% Define the %B%det_mne%B% - detector motor mnemonic, the spindle
#%B%phi_start%B% and %B%phi_stop%B% angle, the %B%exp_time%B%, the detector
#device server name %B%det_dev%B% and %B%powder_prog%B% - the name of the
#computation program.
def powders_setup '{
global PDR_DETDEV PDR_FIRST PDR_LAST PDR_LOCALDIR PDR_TUNE
global PDR_DETMOT PDR_DISTNB
global PDR_PREFIX PDR_DIR PDR_ADIR
local fn[] i lim lfirst llast res sign ldir

	powders_init()

  if ($# < 4) {
    PDR_DETDEV = getval("Detector device server name (e.g. id29/adsc/1):", PDR_DETDEV)
    PDR_FIRST = getval("Initial detector distance [mm]:", PDR_FIRST)
    PDR_LAST = getval("End detector distance [mm]:", PDR_LAST)
    PDR_LOCALDIR = getval("Data beamline directory, as mounted (e.g. id14eh1):", PDR_LOCALDIR)
    PDR_TUNE = yesno("Further setup", PDR_TUNE)
    if (PDR_TUNE) {
      PDR_PHI_START = getval("Phi start angle:", PDR_PHI_START)
      PDR_PHI_STOP = getval("Phi stop angle:", PDR_PHI_STOP)
      PDR_EXPOSURE = getval("Exposure time [s]:", PDR_EXPOSURE)
    }
  } else if ($# >= 4) {
    PDR_DETDEV = "$1"
    PDR_FIRST = $2
    PDR_LAST = $3
    PDR_LOCALDIR = "$4"
    if ($# > 4) {
      PDR_PHI_START = $5
      PDR_PHI_STOP = $6
      PDR_EXPOSURE = $7
    }
  }

  PDR_DETMOT = motor_num(PDR_DETMNE)
  if (PDR_DETMOT == -1) {
    eprintf ("Detector distance motor %s not configured, please change the name\n", PDR_DETMNE)
    PDR_OK = -1
  }

  if (PDR_FIRST > PDR_LAST) {
    lfirst = PDR_LAST
    llast = PDR_FIRST
    sign = -1
  } else {
    lfirst = PDR_FIRST
    llast = PDR_LAST
    sign = 1
  }

  lim = user(PDR_DETMOT, get_lim(PDR_DETMOT, -1))
  if (lim > lfirst) {
    eprintf ("Distance %g lower than the motor limit %g, please change it\n", lfirst, lim)
    PDR_OK = -1
  }

  lim = user(PDR_DETMOT, get_lim(PDR_DETMOT, 1))
  if (lim < llast) {
    eprintf ("Distance %g higher than the motor limit %g, please change it\n", llast, lim)
    PDR_OK = -1
  }

  unglobal PDR_DETDIST
  global PDR_DETDIST[]

  if (PDR_OK != -1) {
    res = fabs(PDR_LAST - PDR_FIRST)
    PDR_DISTNB = int(res/25)
    PDR_DETDIST[0] = PDR_FIRST
    if ((res%25) != 0) {
      PDR_DISTNB += 1
    }
    PDR_DETDIST[PDR_DISTNB] = PDR_LAST
  } else
    PDR_DISTNB = 0

  if (PDR_DISTNB == 0) {
    eprintf ("powders_setup: Distances not set !\n")
    PDR_OK = -1
  }

  PDR_DIR = sprintf("/data/%s/inhouse", PDR_LOCALDIR)
  if ((file_info(PDR_DIR,"isdir") == 0) || (file_info(PDR_DIR,"-w") == 0)) {
    eprintf ("Directory %s is not writable, setup failed\n", PDR_DIR)
    PDR_OK = -1
    PDR_DIR = ""
  }

  PDR_ADIR = sprintf("/data/pyarch/%s/POWDERS",PDR_LOCALDIR)
  if ((file_info(PDR_ADIR,"isdir") == 0) || (file_info(PDR_ADIR,"-w") == 0)) {
    if (file_info(PDR_ADIR,"-e") == 0) {
      if (unix(sprintf("mkdir %s",PDR_ADIR)) == -1) {
        eprintf ("Cannot create %s directory\n", PDR_ADIR)
        PDR_OK = -1
      }
    } else {
      eprintf ("Directory %s is not writable, setup failed\n", PDR_DIR)
      PDR_OK = -1
      PDR_ADIR = ""
    }
  }

  if (PDR_OK != -1) {
    for (i=1; i<PDR_DISTNB; i++)
      PDR_DETDIST[i] = PDR_DETDIST[i-1] + 25*sign
    PDR_DISTNB += 1

    printf ("%d images will be taken from %d to %d, every 25 mm\n", \
	PDR_DISTNB, PDR_DETDIST[0], PDR_DETDIST[PDR_DISTNB-1])

    PDR_OK = 0
  }
}'


#%IU%
#%MDESC% Cleanup procedure.
def powders_cleanup '{
  local msg

  printf("powder collect cleanup initiated")
  msg = sprintf("---- Collect Powders Aborted on %s ----", date())
  egui_logmsg(msg)

  msclose
  ccdcleanup
  shclose
  exit
}'

def create_dir(ldir)  '{
local fn fulldir ret

  split(date(),fn)
  fulldir = sprintf("%s/%s%s%s",ldir,fn[2],fn[1],fn[4])
  if (file_info(fulldir) == 0) {
    if (unix(sprintf("mkdir %s",fulldir)) == -1) {
      eprintf ("Cannot create %s directory\n", fulldir)
      fulldir = ""
    }
  }
  PDR_PREFIX = sprintf("powder_%s%s", fn[2],fn[1])
  return(fulldir)
}'

#%IU%
#%MDESC% Collect process the images
def collect_powders() '{
  unglobal COLLET_SEQ
	global COLLECT_SEQ
	local cmd ihdir ardir

  if (PDR_OK == 0) {
    if ((ihdir = create_dir(PDR_DIR)) == "") {
      eprintf ("error in configuration, procedure not executed\n")
      return(-1)
    }
    if ((ardir = create_dir(PDR_ADIR)) == "") {
      eprintf ("error in configuration, procedure not executed\n")
      return(-1)
    }
    backin
  	lightout
		COLLECT_SEQ["fileinfo"]["prefix"] = PDR_PREFIX
		COLLECT_SEQ["fileinfo"]["directory"] = ihdir
		COLLECT_SEQ["fileinfo"]["run_number"] = 1
		COLLECT_SEQ["start"] = PDR_PHI_START
		COLLECT_SEQ["range"] = PDR_PHI_STOP - PDR_PHI_START
		COLLECT_SEQ["exposure_time"] = PDR_EXPOSURE
		COLLECT_SEQ["number_of_passes"] = 1
		COLLECT_SEQ["start_image_number"] = 0
    take_images(COLLECT_SEQ, PDR_DETMOT)
    process_images(ihdir, PDR_PREFIX, REAL_DIST)
  }
}'

#%IU% (collect_seq, det_mot)
#%MDESC% Collect PDR_DISTNB number of images. Write them in the %B%dir%B%
#directory.
def take_images(tp_pars, det_mot)  '{
  local i msg end_ang
  unglobal REAL_DIST
  global REAL_DIST[]

  cdef("cleanup_once","powders_cleanup;","_powders")
  # force darks
  setMxCollectPars("TakeDarkFlag",1)
  msg = sprintf ("---- Collect Powders Beam Calibration procedure started on %s ----", date())
  egui_logmsg(msg)
  egui_logmsg("Current configuration: (to change type powders_setup in spec)")
  msg = sprintf ("          File: %s/%s\n", dir, prefix)
  msg = sprintf ("%sPhi Start/Stop: %g - %g degrees\n", msg, tp_pars["start"], tp_pars["start"]+tp_pars["range"])
  msg = sprintf ("%s Exposure time: %g seconds, 1 pass\n", msg, tp_pars["exposure_time"])
  egui_logmsg(msg)
  ccdprep(getMxCollectPars("dark"),tp_pars["start"],tp_pars["range"],tp_pars["exposure_time"],tp_pars["number_of_passes"],tp_pars["comment"])
  shopen
  for (i=0;i<PDR_DISTNB;i++) {
    egui_logmsg(sprintf("Collecting powder at %6.3f mm...",PDR_DETDIST[i]))
    printf("Moving detector to %g\n", PDR_DETDIST[i])
    A[det_mot] = PDR_DETDIST[i]
    move_em; move_poll; get_angles
    beam_centre_update()
    printf("Image %d\n", i+1)
    REAL_DIST[i] = A[det_mot]
    ccdprep(getMxCollectPars("TakeDarkFlag"),tp_pars["start"],tp_pars["range"],tp_pars["exposure_time"],tp_pars["number_of_passes"],tp_pars["comment"])
		ccdfile(tp_pars,PDR_DETDIST[i])

    ccdstart
    end_ang = tp_pars["start"] + tp_pars["range"]
    osc_prepare tp_pars["start"] end_ang tp_pars["exposure_time"] 1
    osc_scan tp_pars["start"] end_ang tp_pars["exposure_time"] 1

    ccdread
    if (i == (PDR_DISTNB-1)) {
      ccdflush
    } else {
      ccdsave 0
    }
  }
  shclose
  # wait for the last image to be written an error will be generated later on if it times out
  tout = 10
  printf("Waiting for %s to appear on disk",ccdfile(tp_pars,PDR_DETDIST[i-i]))
  while (file_info(ccdfile(tp_pars,PDR_DETDIST[i-i]) != 1) && tout>0) {
    sleep(1); tout -=1
  }
}'

#%IU% (dir, prefix, dist)
#%MDESC% Process the %B%prefix%B%.img images in the %B%dir%B% directory, for
#the %B%dist%B% detector distances.
def process_images(dir,prefix,dist) '{
  global COLLECT_SEQ
	local i, file, tp_pars
  
  tp_pars = COLLECT_SEQ
	file = sprintf("%s/%s",dir,"powder.inp")
  
  # remove previous input file if it exists
  unix(sprintf("rm -f %s",file))

  if (open(file)) return (0)

  fprintf(file,">powder.inp created at beamline %s on %s\n",beamline,date())
  fprintf(file, PDR_MSG[0])
  for (i=0; i<PDR_DISTNB; i++) {
    tp_pars["fileinfo"]["directory"] = dir
    tp_pars["fileinfo"]["prefix"] = prefix
		current_filename = ccdfile(tp_pars,PDR_DETDIST[i])
    fprintf(file,"POWDER %s %6.3f\n",current_filename,dist[i])
  }
  fprintf (file, PDR_MSG["FORMAT"])
  fprintf (file, PDR_MSG["DVALUES"])
	inp[0] = "beam"
	inp[1] = "bx"
	bxbeam = getMxSpecPars(inp)
	inp[1] = "ax"
	axbeam = getMxSpecPars(inp)
	inp[1] = "by"
	bybeam = getMxSpecPars(inp)
	inp[1] = "ay"
	aybeam = getMxSpecPars(inp)
  PDR_MSG["START"] = sprintf("START %5.4f 0.0 0.0 1.000 0.0 %6.2f %7.4f %6.2f %7.4f\n", \
					getMxWavelength(),bxbeam,axbeam,bybeam,aybeam)
  fprintf(file, PDR_MSG["START"])
  fprintf(file, PDR_MSG["DSTART"])
  fprintf(file, PDR_MSG["TOLERANCE"])
	fprintf(file, PDR_MSG["XMLFILE"] )
	fprintf(file, "\n" )
  close(file)
  
  egui_logmsg(sprintf("Processing results (see %s/powder.log)",dir))
  cmd = sprintf("cd %s; %s < %s/powder.inp > %s/powder.log", dir, PDR_POWDERPROG, dir, dir)
  printf ("executing.. %s\n", cmd)
  unix(cmd)
  if (process_results(dir) == -1 ) {
		msg = sprintf("---- Collect Powders Beam Calibration procedure failed, calibration not updated on %s---",date())
		print msg;		egui_logmsg(msg)
	} else {
		msg = sprintf("---- Collect Powders Beam Calibration procedure completed on %s ----",date())
		print msg;		egui_logmsg(msg)
  } 
}'

def process_results(dir) '{
	# read the output xml file
	local xmlfile[]
	xmlfile[0]=""
	split(PDR_MSG["XMLFILE"],xmlfile," ")
	xmlname = substr(xmlfile[1],0,index(xmlfile[1],".")-1)
	if (xml_readProperties(sprintf("../../../..%s/%s",dir,xmlname)) == -1) {
      printf("Error getting results from powder program xml output: (%s)",XML_tmp["__error__"])
      return(-1)
    }
	imagesAsked = XML_tmp["numberOfInputImagesAsked"]["__value__"]
	imagesRead = XML_tmp["numberOfInputImagesRead"]["__value__"]
	if (imagesRead != imagesAsked) {
	  msg = sprintf("Problem with powder analysis, images read by program (%d) did not equal images taken (%d)\n",imagesRead,imagesAsked)
		print msg;		egui_logmsg(msg)
    return(-1)
	}
	if (XML_tmp["percentageOfIndexedSpots"]["__value__"] < 50) {
		msg = sprintf("Error: Number of indexed spots below 50% (%6.3f)",XML_tmp["percentageOfIndexedSpots"]["__value__"])
		print msg;		egui_logmsg(msg)
		return(-1)
	}
	if (XML_tmp["rmsd"]["__value__"] > 0.0025) {
		msg = sprintf("Error: RMSD is greater than 0.0025\n")
		print msg;		egui_logmsg(msg)
		return(-1)
	}
	if(xml_readProperties(sprintf("../../../..%s/%s",dir,xmlname),"/*/refinedBeamCentreCalibration")==-1) {
      printf("Error getting results from powder program xml output: (%s)",XML_tmp["__error__"])
      return(-1)
    }
	
	MXSPEC_PARS["beam"]["ax"] = XML_tmp["xbeam_a"]["__value__"]
	MXSPEC_PARS["beam"]["ay"] = XML_tmp["ybeam_a"]["__value__"]
	MXSPEC_PARS["beam"]["bx"] = XML_tmp["xbeam_b"]["__value__"]
	MXSPEC_PARS["beam"]["by"] = XML_tmp["ybeam_b"]["__value__"]
    print "all ok, updating calibration..."
    local_pars_commit()
  # the result was successful so copy it to the archive
  # copy the results to the archive, leaving the rest behind in the inhouse directory
  adir=create_dir(PDR_ADIR)
  cmd = sprintf("cp  %s/powder_* %s/*.xml *.log %s", dir,dir, adir)
  printf("Copy to archive- %s\n",cmd)
  ret = unix(cmd)
	return(0)	
}'
  
#%IU%
#%MDESC% Macro called by ProDC
def beam_calibration '{
  collect_powders()
}'
#%MACROS%
#%IMACROS%
#%TOC%
#%AUTHOR% BLISS (A.Beteva/D.Spruce/M.Guijarro)
#$Revision: 1.25 $ $Date: 2009/02/09 16:27:54 $