Ticket #9212: test9212.py

File test9212.py, 20.4 KB (added by Wenduo Zhou, 6 years ago)
Line 
1################################################################################
2#
3# Auto reduction script for VULCAN
4# Input
5# - Event file name with path
6# - Output directory
7#
8# Test example:
9# 1. reduce_VULCAN.py /SNS/VULCAN/IPTS-11090/0/41703/NeXus/VULCAN_41703_event.nxs
10#                     /SNS/users/wzz/Projects/VULCAN/AutoReduction/autoreduce/Temp
11#
12# 2. reduce_VULCAN.py /SNS/VULCAN/IPTS-11090/0/41739/NeXus/VULCAN_41739_event.nxs
13#                     /SNS/users/wzz/Projects/VULCAN/AutoReduction/autoreduce/Temp
14#
15################################################################################
16
17import os
18import sys
19import shutil 
20import xml.etree.ElementTree as ET
21
22sys.path.append("/opt/mantidnightly/bin")
23
24mantidpathlocal = "/home/wzz/Projects/MantidProjects/Mantid2/Code/debug/bin"
25if os.path.exists(mantidpathlocal) is True: 
26    USELOCAL = True
27    sys.path.append(mantidpathlocal)
28
29from mantid.simpleapi import *
30import mantid
31
32
33def changeOutputDir(outputdir):
34    """ Change the output direction from ..../autoreduce/ to ..../logs/
35    If the new directory does not exist, make it
36    """
37    # Change path from ..../autoreduce/ ... to .../logs/
38    if outputdir.endswith("/"):
39        outputdir = os.path.split(outputdir)[0]
40    lastdir = os.path.split(outputdir)[-1]
41    # print "Furance: last dir of output dir = %s. " % (lastdir)
42
43    if lastdir == "autoreduce":
44        modoutputdir = os.path.join(os.path.split(outputdir)[0], "logs")
45        print "Log file will be written to directory %s. " % (modoutputdir)
46    else:
47        modoutputdir = outputdir
48        print "Log file will be written to directory %s as auto reduction service specified. " % (modoutputdir)
49   
50    # Create path
51    if os.path.exists(modoutputdir) is False:
52        # create
53        os.mkdir(modoutputdir)
54
55    return modoutputdir
56
57
58def exportFurnaceLog(logwsname, outputDir, runNumber):
59    """ Export the furnace log
60    """
61    logfilename = os.path.join(outputDir, "furnace%d.txt" % (runNumber))
62   
63    ExportSampleLogsToCSVFile(InputWorkspace = logwsname, 
64            OutputFilename = logfilename, 
65            SampleLogNames = ["furnace.temp1", "furnace.temp2", "furnace.power"])
66           
67    return
68
69
70def exportMTSLog(logwsname, outputDir, ipts, runnumber):
71    """ Export MTS log
72    List of MTS Log:
73        X       Y       Z       O       HROT   
74        MTSDisplacement MTSForce        MTSStrain       MTSStress      MTSAngle     
75        MTSTorque       MTSLaser        MTSlaserstrain  MTSDisplaceoffset       MTSAngleceoffset       
76        MTST1   MTST2   MTST3   MTST4   FurnaceT       
77        FurnaceOT       FurnacePower    VacT    VacOT
78    """
79    # Organzied by dictionary
80    vulcanheaderlist = []
81    vulcanheaderlist.append( ("TimeStamp"           , "")       )
82    vulcanheaderlist.append( ("Time [sec]"          , "")       )
83    vulcanheaderlist.append( ("MPTIndex"            , "loadframe.MPTIndex")     )
84    vulcanheaderlist.append( ("X"                   , "X")      )
85    vulcanheaderlist.append( ("Y"                   , "Y")      )
86    vulcanheaderlist.append( ("Z"                   , "Z")      )
87    vulcanheaderlist.append( ("O"                   , "OMEGA")  )
88    vulcanheaderlist.append( ("HROT"                , "HROT")   )
89    vulcanheaderlist.append( ("MTSDisplacement"     , "loadframe.displacement") )
90    vulcanheaderlist.append( ("MTSForce"            , "loadframe.force")        )
91    vulcanheaderlist.append( ("MTSStrain"           , "loadframe.strain")       )
92    vulcanheaderlist.append( ("MTSStress"           , "loadframe.stress")       )
93    vulcanheaderlist.append( ("MTSAngle"            , "loadframe.rot_angle")    )
94    vulcanheaderlist.append( ("MTSTorque"           , "loadframe.torque")       )
95    vulcanheaderlist.append( ("MTSLaser"            , "loadframe.laser")        )
96    vulcanheaderlist.append( ("MTSlaserstrain"      , "loadframe.laserstrain")  )
97    vulcanheaderlist.append( ("MTSDisplaceoffset"   , "loadframe.x_offset")     )
98    vulcanheaderlist.append( ("MTSAngleceoffset"    , "loadframe.rot_offset")   )
99    vulcanheaderlist.append( ("MTS1"                , "loadframe.furnace1") )
100    vulcanheaderlist.append( ("MTS2"                , "loadframe.furnace2") )
101    vulcanheaderlist.append( ("MTS3"                , "loadframe.extTC3") )
102    vulcanheaderlist.append( ("MTS4"                , "loadframe.extTC4") )
103    vulcanheaderlist.append( ("FurnaceT"            , "furnace.temp1") )
104    vulcanheaderlist.append( ("FurnaceOT"           , "furnace.temp2") )
105    vulcanheaderlist.append( ("FurnacePower"        , "furnace.power") )
106    vulcanheaderlist.append( ("VacT"                , "partlow1.temp") )
107    vulcanheaderlist.append( ("VacOT"               , "partlow2.temp") )
108
109    # Format to lists for input
110    samplelognames = []
111    header = []
112    for i in xrange(len(vulcanheaderlist)):
113        title = vulcanheaderlist[i][0]
114        logname = vulcanheaderlist[i][1]
115       
116        header.append(title)
117        if len(logname) > 0:
118            samplelognames.append(logname)
119   
120    headstr = ""
121    for title in header: 
122        headstr += "%s\t" % (title)
123   
124    """
125    print header
126    print samplelognames
127    print headstr
128    """
129   
130    outputfilename = "IPTS-%d-MTSLoadFrame-%d.txt" % (ipts, runnumber)
131    outputfilename = os.path.join(outputDir, outputfilename)
132    # print "Loadframe output filename: %s" % (outputfilename)
133 
134    ExportSampleLogsToCSVFile(
135        InputWorkspace = logwsname,
136        OutputFilename = outputfilename,
137        SampleLogNames = samplelognames,
138        WriteHeaderFile = True,
139        Header = headstr)
140
141
142    return
143   
144RecordBase = [ 
145        ("RUN",             "run_number", None),
146        ("IPTS",            "IPTS", None),
147        ("Title",           "run_title", None),
148        ("Notes",           "Notes", None),
149        ("Sample",          "Sample", None),
150        ("StartTime",       "run_start", "localtime"),
151        ("Duration",        "duration", None),
152        ("ProtonCharge",    "protoncharge", "sum"),
153        ("TotalCounts",     "das.counts", "sum"),
154        ("Monitor1",        "das.monitor2counts", "sum"),
155        ("Monitor2",        "das.monitor3counts", "sum"),
156        ("X",               "X", "0"),
157        ("Y",               "Y", "0"),
158        ("Z",               "Z", "0"),
159        ("O",               "Omega", "0"),       
160        ("HROT",            "HROT", "0"),
161        ("BandCentre",      "lambda", "0"),
162        ("BandWidth",       "bandwidth", "0"),
163        ("Frequency",       "skf1.speed", "0"),
164        ("Guide",           "Guide", "0"),
165        ("IX",              "IX", "0"),
166        ("IY",              "IY", "0"),
167        ("IZ",              "IZ", "0"),
168        ("IHA",             "IHA", "0"),
169        ("IVA",             "IVA", "0"),
170        ("Collimator",      "Vcollimator", None),
171        ("MTSDisplacement", "loadframe.displacement", "0"),
172        ("MTSForce",        "loadframe.force", "0"),
173        ("MTSStrain",       "loadframe.strain", "0"),
174        ("MTSStress",       "loadframe.stress", "0"),
175        ("MTSAngle",        "loadframe.rot_angle", "0"),
176        ("MTSTorque",       "loadframe.torque", "0"),
177        ("MTSLaser",        "loadframe.laser", "0"),
178        ("MTSlaserstrain",  "loadframe.laserstrain", "0"),
179        ("MTSDisplaceoffset","loadframe.x_offset", "0"),
180        ("MTSAngleceoffset", "loadframe.rot_offset", "0"),
181        ("MTST1",           "loadframe.furnace1", "0"),
182        ("MTST2",           "loadframe.furnace2", "0"),
183        ("MTST3",           "loadframe.extTC3", "0"),
184        ("MTST4",           "loadframe.extTC4", "0"),
185        ("FurnaceT",        "furnace.temp1", "0"),
186        ("FurnaceOT",       "furnace.temp2", "0"),
187        ("FurnacePower",    "furnace.power", "0"),
188        ("VacT",            "partlow1.temp", "0"),
189        ("VacOT",           "partlow2.temp", "0") 
190        ]
191       
192   
193class PatchRecord:
194    """ A class whose task is to make patch to Record.txt generated from
195    Mantid.simpleapi.ExportExperimentLog(), which may not be able to retrieve
196    all information from NeXus file. 
197   
198    This class will not be used after all the required information/logs are
199    added to NeXus file or exported to Mantid workspace
200    """
201    def __init__(self, instrument, ipts, run):
202        """ Init
203        """
204        # Generate run_info and cv_info files
205        self._cvinfofname = "/SNS/%s/IPTS-%d/0/%d/preNeXus/%s_%d_cvinfo.xml" % (
206            instrument, ipts, run, instrument, run)
207           
208        self._runinfofname = "/SNS/%s/IPTS-%d/0/%d/preNeXus/%s_%d_runinfo.xml" % (
209            instrument, ipts, run, instrument, run)
210           
211        self._beaminfofname = "/SNS/%s/IPTS-%d/0/%d/preNeXus/%s_beamtimeinfo.xml" % (
212            instrument, ipts, run, instrument)
213           
214        # Verify whether these 2 files are accessible
215        if os.path.exists(self._cvinfofname) is False or os.path.exists(self._runinfofname) is False or os.path.exists(self._beaminfofname) is False:
216            raise NotImplementedError("PreNexus log file %s and/or %s cannot be accessed. " % (
217                self._cvinfofname, self._runinfofname))
218
219        return
220       
221    def patchRecord(self, recordfilename):
222        """ Patch record
223        """
224        # Get last line
225        titleline, lastline = self._getLastLine(recordfilename)
226
227        # print "First line: ", titleline
228        # print "Last line: ", lastline
229       
230        # Parse last line and first line
231        rtitles = titleline.split("\t")
232        titles = []
233        for title in rtitles:
234            title = title.strip()
235            titles.append(title)
236       
237        values = lastline.split("\t")
238       
239        valuedict = {}
240        if len(titles) != len(values):
241            raise NotImplementedError("Number of tiles are different than number of values.")
242        for itit in xrange(len(titles)):
243            valuedict[titles[itit]] = values[itit]
244           
245        # Substitute
246        ipts = self._getIPTS()
247        cvdict = self._readCvInfoFile()
248        rundict = self._readRunInfoFile()
249       
250        valuedict["IPTS"] = "%s" % (str(ipts))
251        for title in cvdict.keys():
252            valuedict[title] = cvdict[title]
253       
254        # print valuedict.keys()
255       
256        for title in rundict.keys():
257            valuedict[title] = rundict[title]
258       
259        # Form the line again: with 7 spaces in front
260        newline = "       "
261        for i in xrange(len(titles)):
262            title = titles[i]
263            if i > 0:
264                newline += "\t"
265            newline += "%s" % (str(valuedict[title]))
266       
267        # Remove last line and append the patched line
268        self._removeLastLine(recordfilename)
269       
270        with open(recordfilename, "a") as myfile:
271            myfile.write("\n"+newline)
272       
273        return
274       
275   
276    def _getLastLine(self, filename):
277        """ Get the last line of a (possibly long) file
278        """
279        with open(filename, 'rb') as fh:
280            # Determine a rougly size of a line
281            firstline = next(fh).decode().strip()
282            secondline = next(fh).decode().strip()
283            linesize = len(secondline)
284           
285            # print "Title line:  ", firstline
286            # print "Second line: ", secondline
287           
288            try: 
289                fh.seek(-2*linesize, 2)
290                lastline = fh.readlines()[-1].decode().strip()
291                fh.close()
292            except IOError as err:
293                # File is short
294                fh.close()
295                fh = open(filename, 'rb')
296                lines = fh.readlines()
297                lastline = lines[-1] 
298
299        #print lastline
300        return (firstline, lastline)
301   
302    def _removeLastLine(self, filename):
303        """ Remove last line
304        """
305        import sys
306        import os
307
308        #ifile = open(sys.argv[1], "r+", encoding = "utf-8")
309        ifile = open(filename, "r+")
310
311        ifile.seek(0, os.SEEK_END)
312        pos = ifile.tell() - 1
313        while pos > 0 and ifile.read(1) != "\n":
314            pos -= 1
315            ifile.seek(pos, os.SEEK_SET)
316
317        if pos > 0:
318            ifile.seek(pos, os.SEEK_SET)
319            ifile.truncate()
320
321        ifile.close()
322   
323        return
324       
325    def _getIPTS(self):
326        """ Get IPTS
327        """
328        tree = ET.parse(self._beaminfofname)
329
330        root = tree.getroot()
331        if root.tag != 'Instrument':
332            raise NotImplementedError("Not an instrument")
333
334        proposal = None
335        for child in root:
336            if child.tag == "Proposal":
337                proposal = child
338                break
339        if proposal is None:
340            raise NotImplementedError("Not have proposal")
341
342        id = None
343        for child in proposal:
344            if child.tag == "ID":
345                id = child
346                break
347        if id is None:
348            raise NotImplementedError("No ID")
349           
350        ipts = id.text
351           
352        return ipts
353       
354    def _readCvInfoFile(self):
355        """ read CV info
356        """
357        cvinfodict = {}
358       
359        # Parse the XML file to tree   
360        tree = ET.parse(self._cvinfofname)
361        root = tree.getroot()
362
363        # Find "DAS_process"
364        das_process = None
365        for child in root:
366            if child.tag == "DAS_process":
367                das_process = child
368        if das_process is None:
369            raise NotImplementedError("DAS_process is not in cv_info.")   
370
371        # Parse all the entries to a dictionary
372        attribdict = {}
373        for child in das_process:
374            attrib = child.attrib
375            name = attrib['name']
376            value = attrib['value']
377            attribdict[name] = value
378
379        name = "das.neutrons"
380        if attribdict.has_key(name): 
381            cvinfodict["TotalCounts"] = attribdict[name]
382           
383        name = "das.protoncharge"
384        if attribdict.has_key(name): 
385            cvinfodict["ProtonCharge"] = attribdict[name]
386           
387        name = "das.runtime"
388        if attribdict.has_key(name): 
389            cvinfodict["Duration(sec)"] = attribdict[name]
390           
391        name = "das.monitor2counts"
392        if attribdict.has_key(name): 
393            cvinfodict["Monitor1"] = attribdict[name]
394           
395        name = "das.monitor3counts"
396        if attribdict.has_key(name): 
397            cvinfodict["Monitor2"] = attribdict[name]
398       
399        return cvinfodict
400       
401    def _readRunInfoFile(self):
402        """ Read Run info file
403        """
404        runinfodict = {}
405       
406        tree = ET.parse(self._runinfofname)
407        root = tree.getroot()
408
409        # Get SampleInfo and GenerateInfo node
410        sampleinfo = None
411        generalinfo = None
412        for child in root:
413            if child.tag == "SampleInfo":
414                sampleinfo = child
415            elif child.tag == "GeneralInfo":
416                generalinfo = child
417       
418        if sampleinfo is None:
419            raise NotImplementedError("SampleInfo is missing.")
420        if generalinfo is None:
421            raise NotImplementedError("GeneralInfo is missing.")
422
423        for child in sampleinfo:
424            if child.tag == "SampleDescription":
425                sampledes = child
426                runinfodict["Sample"] = sampledes.text.replace("\n", " ")
427                break
428
429        for child in generalinfo:
430            if child.tag == "Notes":
431                origtext = child.text
432                if origtext is None:
433                    runinfodict["Notes"] = "(No Notes)"
434                else: 
435                    runinfodict["Notes"] = child.text.replace("\n", " ")
436                break
437               
438        return runinfodict
439       
440# ENDCLASS
441
442
443def generateRecordFormat():
444    """
445    """
446    sampletitles = []
447    samplenames = []
448    sampleoperations = []
449    for ib in xrange(len(RecordBase)):
450        sampletitles.append(RecordBase[ib][0])
451        samplenames.append(RecordBase[ib][1])
452        sampleoperations.append(RecordBase[ib][2])
453   
454    return (sampletitles, samplenames, sampleoperations)
455   
456
457def write_record(wsname, instrument, ipts, run, rfilename):
458    """ Write the run info to a record file
459    """
460    # Convert the record base to input arrays
461    sampletitles, samplenames, sampleoperations = generateRecordFormat()
462   
463    # Load NeXus file
464    # eventnexus = "/SNS/%s/IPTS-%d/0/%d/NeXus/%s_%d_event.nxs" % (instrument, ipts, run, instrument, run)
465
466
467    """
468    try:
469        Load(Filename = eventnexus, OutputWorkspace = wsname,
470                LoadLogs = True, MetaDataOnly = True)
471    except RuntimeError as err:
472        print "Unable to load NeXus file %s. Error message: %s. " % (eventnexus, str(err))
473        return False
474    """
475
476    # Determine mode
477    if os.path.exists(rfilename) is True:
478        filemode = "fastappend"
479    else:
480        filemode = "new"
481
482    print "Output record file will be written to %s. " % (rfilename)
483
484    # Write log
485    ExportExperimentLog(InputWorkspace = wsname, 
486            OutputFilename     = rfilename, 
487            FileMode           = filemode, 
488            SampleLogNames     = samplenames, 
489            SampleLogTitles    = sampletitles, 
490            SampleLogOperation = sampleoperations,
491            TimeZone           = "America/New_York")
492   
493    # Patch for logs that do not exist in event NeXus yet
494    testclass = PatchRecord(instrument, ipts, run)
495    testclass.patchRecord(rfilename)
496   
497    return True
498
499
500def mainProcess(eventFileAbs, outputDir):
501    """ Main
502    eventFileAbs=sys.argv[1]
503    """
504   
505    # Obtain information from input file name and path
506    eventFile = os.path.split(eventFileAbs)[-1]
507    nexusDir = eventFileAbs.replace(eventFile, '')
508    runNumber = int(eventFile.split('_')[1])
509    configService = mantid.config
510    dataSearchPath = configService.getDataSearchDirs()
511    dataSearchPath.append(nexusDir)
512    configService.setDataSearchDirs(";".join(dataSearchPath))
513   
514    # Check file's existence
515    if os.path.exists(eventFileAbs) is False:
516        print "NeXus file %s is not accessible or does not exist. " % (eventFileAbs)
517        return 
518   
519    # Find out IPTS
520    if eventFileAbs.count("IPTS") == 1:
521        terms = eventFileAbs.split("/")
522        for t in terms:
523            if t.count("IPTS") == 1:
524                iptsstr = t
525                break
526        ipts = int(iptsstr.split("-")[1])
527    else:
528        ipts = 0
529
530    # Change the input 'OutputDir' to .../logs/ as instrument scientist requests
531    outputDir = changeOutputDir(outputDir)
532   
533    # Load file to generate the matrix workspace with some logs
534    logwsname = "VULCAN_%d_MetaDataOnly" % (runNumber)
535
536    try:
537        Load(Filename=eventFileAbs, OutputWorkspace=logwsname, MetaDataOnly = True, LoadLogs = True)
538    except RuntimeError as err:
539        print "Unable to load NeXus file %s. Error message: %s. " % (eventFileAbs, str(err))
540        return 
541           
542    # Convert Furnace"/tmp/furnace41703.txt"
543    exportFurnaceLog(logwsname, outputDir, runNumber)
544
545    # Write out loadframe /MTS log
546    exportMTSLog(logwsname, outputDir, ipts, runNumber)
547   
548    # Write experiment log (Record.txt)
549    rfilename = "/SNS/VULCAN/IPTS-%d/shared/AutoRecord_Manual.txt" % (ipts)
550    if USELOCAL is True:
551        rfilename = "%s/AutoRecord_Manual.txt" % (outputDir)
552    instrument="VULCAN"
553    exportgood = write_record(logwsname, instrument, ipts, runNumber, rfilename)
554   
555    # SNSPowderReduction(Instrument="PG3", RunNumber=runNumber, Extension="_event.nxs",
556    #                    PreserveEvents=True,PushDataPositive="AddMinimum",
557    #                    CalibrationFile=cal_file, CharacterizationRunsFile=char_file,
558    #                    LowResRef=0, RemovePromptPulseWidth=50,
559    #                    Binning=-0.0008, BinInDspace=True, FilterBadPulses=True,
560    #                    ScaleData =100,
561    #                    SaveAs="gsas topas and fullprof", OutputDirectory=outputDir,
562    #                    FinalDataUnits="dSpacing")
563   
564    return
565
566
567def main():
568    """ Main
569    """
570    if len(sys.argv) < 3:
571        print "Inputs: [1. IPTS] [2. File containing run number] [3. Output directory]"
572        return
573   
574    ipts = int(sys.argv[1])
575    runfilename = sys.argv[2]
576    outputDir=sys.argv[3]
577
578    # parse run number file
579    rfile = open(runfilename)
580    lines = rfile.readlines()
581    rfile.close()
582
583    runs = []
584    for line in lines:
585        line = line.strip()
586        if len(line) > 0:
587            terms = line.split()
588            for t in terms:
589                run = int(t)
590                runs.append(run)
591
592    # sort runs
593    runs = sorted(runs)
594
595    # generate log
596    for run in runs:
597        eventFileAbs = "/SNS/VULCAN/IPTS-%d/0/%d/NeXus/VULCAN_%d_event.nxs" % (ipts, run, run)
598        mainProcess(eventFileAbs, outputDir)
599
600    return
601
602
603if __name__ == "__main__":
604    main()