1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 """Implementation of SrRietveld refinement class.
16 """
17
18 __id__ = "$Id: refinement.py 6724 2011-08-25 09:31:57Z yshang $"
19
20 import os, shutil, copy
21 from diffpy.refinementdata.refinement import Refinement as DataRefinement
22 from diffpy.srrietveld.utility import printWarning
23 from diffpy.srrietveld.convert.datafile import DataFile
24 from diffpy.srrietveld.convert.strategyfile import StrategyFile
25 from diffpy.srrietveld.phase import Phase
26 from diffpy.srrietveld.pattern import Pattern
27 from diffpy.srrietveld.profile import Profile
28 import diffpy.srrietveld.utility as UTILS
29 from diffpy.srrietveld.paramnames import PARAMNAMES
32 '''The refinement (fit) class for SrRietveld. A refinement object contains
33 information for a series of single refinement. It contains the subobjects
34 such as the Pattern, Profile, and Contribution. '''
35
36 - def __init__(self, owner, name=None, handle=None, shape=None):
37 '''constructor'''
38 DataRefinement.__init__(self, owner, name, handle, shape)
39
40 return
41
43 '''Add a parameter to strategy. The included parameters will be turned on
44 in the refinement.
45
46 @type datapath: string
47 @param datapath: the dataset signature path to be added to the strategy
48 @type step: integer
49 @param step: the step of the strategy to which the parameter will be added
50 - None, the parameter will be added to all steps
51 - step >= total number of existing steps, a new step will be
52 added to the strategy list. The new step includes the
53 new parameter, and all previously included parameters
54 - 0 <= step < total number of existing steps, the new parameter
55 will be added to the step
56 @return: no return value
57 '''
58
59
60 paramPaths = self.getParamListForStrategy(datapath)
61
62 for paramPath in paramPaths:
63 strategy = StrategyFile(self, strategy = self.strategy)
64 stepid = strategy.getNumStep()
65
66 value = ""
67 formula = "1.0*Var_" + str(strategy.getNewVariableId())
68 if step >= self.numStrategySteps:
69 lastparamlist = strategy.getStepParamList(stepid)
70 currparamlist = strategy.mergeParamList(
71 [paramPath, value, formula], lastparamlist)
72 method = strategy.getStepMethod(stepid)
73 strategy.addNewStep(stepid, method, currparamlist)
74 elif step is None:
75 for step in range(stepid):
76 if not self.isParamTurnedOn(paramPath, step):
77 strategy.addParam(step + 1, 0, paramPath, value, formula)
78
79 else:
80 assert(step < stepid and step >= 0)
81 if not self.isParamTurnedOn(paramPath, step):
82 strategy.addParam(step + 1, 0, paramPath, value, formula)
83
84 self.setStrategy(strategy.strategy)
85
86 return
87
89 '''Dump the datafile for refinement in index
90
91 @type directory: directory path string
92 @param directory: the directory where the data files will be saved
93 @type index: integer or tuple
94 @param index: the index of the single refinement
95 @type bankid: integer
96 @param bankid: the bank id of the pattern to look for
97 @return: the list of data file path saved, listed same as the bank number
98 if the file is not found or not set, the item is None
99 '''
100 rv = None
101
102 pt = self.getObject('Pattern')[bankid]
103 ds = pt.get('Datafile')
104 rv = self.dumpFileFromDataset(directory, ds, index)
105
106
107
108 if not rv:
109 absds = pt.get('Datafile_abspath')
110 abspath = str(absds[index])
111 basename = os.path.basename(abspath)
112 rootname, ext = os.path.splitext(basename)
113
114 df = self.getDataFileFromPattern(index, bankid, rootname = rootname)
115
116 if df:
117
118
119 basename = rootname + '.dat'
120 newpath = os.path.join(directory, basename)
121 df.write(newpath)
122 rv = newpath
123
124 return rv
125
127 '''dump the engine file to the directory
128
129 @type directory: directory path string
130 @param directory: the directory where the file will be saved
131 @type index: integer or tuple
132 @param index: the data index of the engine file
133 @type basename: string
134 @param basename: the file name of the engine file
135 @return: the path of the engine file'''
136
137 ds = self.get('enginefile')
138 if not basename:
139 basename = self.name + '.' +self.getEngineFileExt()
140
141 newpath = os.path.join(directory, basename)
142
143 with open(newpath, 'w') as f:
144 f.write( str(ds[index]) )
145
146 return
147
149 '''Dump the files for refinement, including all the files needed to
150 run the refinement (datafile, instrument file, incident spectrum file, engine file)
151
152 @type directory: directory path string
153 @param directory: the directory where the files will be saved
154 @type index: integer or tuple
155 @param index: the index of the refinement to dump
156 @return: no return value
157 '''
158 assert(os.path.isdir(directory))
159
160
161 patterns = self.getObject("Pattern")
162 for pt in patterns:
163 for param in ['Datafile', 'MFIL', 'Instrumentfile']:
164 ds = pt.get(param)
165
166
167 if ds:
168
169
170 relpath = str(ds[index])
171 abspath = os.path.join(self.owner.basepath, relpath)
172 basename = os.path.basename(relpath)
173 newFilePath = os.path.join(directory, basename)
174
175 if os.path.isfile(abspath):
176 shutil.copy(abspath, newFilePath)
177 __msg = 'The relative path of file (%s) is not valid, %s \n.' + \
178 'Trying to use the absolute path saved in the project...' % (param, abspath)
179
180 absds = pt.get(param + '_abspath')
181 if absds:
182 abspath = str(absds[index])
183
184 else:
185 __msg = 'The file (%s) in %s does not exist.' % (param, abspath)
186 printWarning(__msg)
187
188
189 ds = self.get('enginefile')
190 if not ds:
191 __msg = 'The engine file is not saved in the project'
192 printWarning(__msg)
193 else:
194 engText = str(ds[index])
195 basename = self.name + '.' + self.getEngineFileExt()
196 filename = os.path.join(directory, basename)
197 with open(filename, 'w') as f:
198 f.write(filename)
199
200 return
201
203 '''dump the files in pattern objects, such as Datafile, MFIL, and
204 Instrumentfile
205
206 @type directory: directory path string
207 @param directory - the directory where the file will be saved
208 @type ds: a Dataset object
209 @param ds: the dataset stores the file path information
210 @type index: integer or tuple
211 @param index - the index in the dataset
212 @type basename: string
213 @param basename: if defined, will be used as the file name
214 @return - the paths of the dumped new file
215 '''
216
217 filepath = self.findFileFromDataset(ds, index)
218
219 rv = None
220
221 if filepath:
222 basename = os.path.basename(filepath) if basename is None else basename
223 newpath = os.path.join(directory, basename)
224 shutil.copy(filepath, newpath)
225 rv = newpath
226 else:
227 dname = ds.name
228 __msg = 'The file (%s) is not found.' %(dname)
229 printWarning(__msg)
230
231 return rv
232
234 '''Dump the incident spectrum file (MFIL, only in EXP files for GSAS)
235
236 @type directory: directory path string
237 @param directory: the directory where the file will be saved
238 @type index: integer or tuple
239 @param index: the index of the dataset
240 @type bankid: integer
241 @param bankid: the bank id of the pattern to look for
242 @return: the new path of the saved file'''
243 rv = None
244
245 pt = self.getObject('Pattern')[bankid]
246 ds = pt.get('MFIL')
247 rv = self.dumpFileFromDataset(directory, ds, index)
248
249 return rv
250
252 '''Dump the instrument file into a folder. Usually the instrument files
253 are the same for all data set.
254
255 @type directory: directory path string
256 @param directory: the directory where the file will be moved to
257 @type index: integer or tuple
258 @param index: the index of the dataset, default to be the first in the list
259 @type bankid: integer
260 @param bankid: the bank id of the pattern to look for
261 @return: the new path of the instrument file'''
262
263 rv = None
264
265 pt = self.getObject('Pattern')[bankid]
266 ds = pt.get('Instrumentfile')
267 rv = self.dumpFileFromDataset(directory, ds, index)
268
269 return rv
270
272 '''Try to find the file from the information in the dataset
273
274 @type ds: a Dataset object
275 @param ds: the dataset which stores the file path information
276 @type index: integer or tuple
277 @param index: the index in the dataset
278 @return: the file absolute path if the file is found, None otherwise'''
279
280 dname = ds.name
281
282 relpath = str(ds[index])
283 abspath = os.path.abspath(os.path.join(self.owner.basepath, relpath))
284 filepath = None
285 if os.path.isfile(abspath):
286 filepath = abspath
287 else:
288
289 absds = ds.owner.get(dname + '_abspath')
290 if absds:
291 abspath = str(absds[index])
292 if os.path.isfile(abspath):
293 filepath = abspath
294
295
296
297 if filepath:
298 self.saveFilePathToDataset(filepath, ds, index)
299
300 return filepath
301
303 '''
304 Get the chi square values of the refinement
305
306 @return: the chi square values of the refinement
307 '''
308 return self.getByPath(PARAMNAMES[self.getEngineType()]['chi2'])
309
311 '''Get a data file object from the numpy array saved in Pattern
312
313 @type index: integer or tuple
314 @param index: the index of the single refinement
315 @type bankid: integer
316 @param bankid: the bank id of the pattern to look for
317 @type rootname: string
318 @param rootname: the rootname of the data file, which is the file name
319 without the extension
320 @return: a Datafile object
321 '''
322 df = None
323 pt = self.getObject("Pattern")[bankid]
324 dsx = pt.get('xobs')
325 dsy = pt.get('yobs')
326 if dsx is not None and dsy is not None:
327 xobs = dsx[index]
328 yobs = dsy[index]
329
330 if not rootname:
331 rootname = "%s_idx%s_bid%s" %(self.name, index, bankid)
332
333 df = DataFile(xobs = xobs,
334 yobs = yobs,
335 rootname = rootname)
336
337 return df
338
340 '''get the extension name of the engine file for this fit
341
342 @return: string of file extension, for example "EXP" or "pcr"'''
343 engType = self.getEngineType()
344
345 rv = None
346 if engType.lower() == 'gsas':
347 rv = 'EXP'
348 elif engType.lower() == 'fullprof':
349 rv = 'pcr'
350
351 return rv
352
354 '''Get the engine type for this refinement
355
356 @return: the string of the engine type, for example I{"gsas"} or I{"fullprof"}'''
357
358 if self.getAttr('rietveldcls').count('gsas') > 0:
359 engineName = 'gsas'
360 elif self.getAttr('rietveldcls').count('fullprof') > 0:
361 engineName = 'fullprof'
362
363 return engineName
364
366 '''Get the refined parameters. The datasets ever included in the strategy list
367 will be listed. The order of the datasets when they turned on is ignored
368
369 @type step: integer
370 @param step: the strategy step, if step is a valid integer, then get
371 the datasets in that step; if none, get all the datasets
372 in all steps
373 @return: the a list of Dataset objects
374 '''
375 datapaths = []
376
377 if step is None:
378 strategyStep = self.getOneStepStrategy()[0]
379 else:
380 if step < 0 or step >= len(self.strategy):
381 return []
382 strategyStep = self.strategy[step]
383
384 type = strategyStep.keys()[0]
385 for param in strategyStep[type]:
386 datapaths.append(param[0])
387
388 return datapaths
389
391 '''
392 Get the name of the refinement
393
394 @return: the refinement name
395 '''
396 return self.name
397
399 '''
400 Get the number of cycles of the refinement
401
402 @return: the chi square values of the refinement
403 '''
404 return self.getByPath(PARAMNAMES[self.getEngineType()]['ncycle'])
405
407 '''Get number of phases in the refinement
408
409 @return: number of phases
410 '''
411 return len(self.listPhases())
412
414 '''
415 Get the number of patterns in the refinement
416
417 @return: number of patterns
418 '''
419 return len(self.listPatterns())
420
422 '''Combine the strategy steps into one step, so the refinement can run
423 this strategy step and skep running through the steps
424
425 @type type: string
426 @param type: the type of the refinement, default to be Rietveld.
427 Possible value can be "Rietveld" or "LeBail"
428 @return: the one step strategy list
429 '''
430
431 allInOneStep = []
432 strategy = copy.deepcopy(self.strategy)
433
434 for step in strategy:
435 allInOneStep.extend(step.values()[0])
436
437 if allInOneStep:
438 allInOneStep.sort()
439 last = allInOneStep[-1]
440 for i in range(len(allInOneStep)-2, -1, -1):
441 if last == allInOneStep[i]:
442 del allInOneStep[i]
443 else:
444 last = allInOneStep[i]
445 step = {}
446
447 step[type] = allInOneStep
448 return [step]
449
451 '''Handling different mechanisms for different engines. For example,
452 for gsas, some parameters such as all background parameters have to be
453 added or removed all together. Also the lattice parameters have to be
454 combined in GSAS as well.
455
456 @type datapath: string
457 @param datapath: the data path, which denotes the ownership tree of a
458 dataset. It reads as Pattern[0].Back[0], Phase[0].Atom[1].x,
459 or Phase[0].a
460 @return: a list of paths of the parameter data object that should be
461 combined with the passed in datapath'''
462 rv = []
463 if self.getEngineType() == 'gsas':
464
465
466
467 latticeParams = ['a', 'b', 'c', 'alpha', 'beta', 'gamma']
468 suffix = datapath.split('.')[-1]
469 if suffix in latticeParams:
470 __msg = "In GSAS engine, the lattice parameters have to be flagged/" + \
471 "unflagged together. This operation will be applied to other lattice" + \
472 "parameters as well. "
473 print __msg
474 for param in latticeParams:
475 rv.append('.'.join([datapath.rsplit('.', 1)[0], param]))
476
477
478 if datapath.endswith(']'):
479 __msg = "In GSAS engine, the background parameters have to be " + \
480 "flagged/unflagged together. This operation will be applied to " + \
481 "other background parameters as well. "
482 print __msg
483 datapath = datapath.rsplit('[', 1)[0]
484 paramPath = datapath.split('.', 1)[-1]
485 ds = self.getByPath(paramPath)
486
487
488 extrasize = ds.shape[-1]
489 for extraidx in range(extrasize):
490 rv.append(datapath + "[%i]" % extraidx)
491
492 else:
493 rv = [datapath]
494
495 return rv
496
498 '''
499 Get the phase object by its name
500
501 @type name: string
502 @param name: the phase name to obtain
503
504 @return: the phase object with the name if exists. None if no such
505 phase object. The program will emit a warning if duplicate
506 phase names exist, and the first phase obj with the same name
507 will be returned
508 '''
509 rv = [None]
510 rv = [obj for obj in self.listPhases() if obj.getName() == name]
511 if len(rv) > 1:
512 __msg = 'Duplicate phase names, the first refinement is returned'
513 UTILS.printWarning(__msg)
514
515 return rv[0]
516
518 '''check if the dataset is included in the strategy
519
520 @type datapath: string
521 @param datapath: the signature path string of the data. For ordinary
522 data, just dataset.path for data with extra index,
523 its dataset.path + '[%i]' % extraindex
524 @type step: integer
525 @param step: the strategy step to be investigate. If None, return True if the
526 dataset is included in any step
527 @return: True if the dataset is included, False otherwise'''
528 rv = False
529 if step is not None:
530 sf = StrategyFile(self, strategy = self.strategy)
531 paramList = sf.getStepParamList(step + 1)
532 for param in paramList:
533 if datapath == param[0]:
534 rv = True
535 break
536
537 else:
538 for step in range(len(self.strategy)):
539 if self.isParamTurnedOn(datapath, step):
540 rv = True
541 break
542
543 return rv
544
546 '''
547 List the environment parameter objects on which the refinements are organized.
548 For example, the temperature or pressure series
549
550 @return: a list of environment parameter objects
551 '''
552 rv = []
553 for name in self.labels:
554 rv.append(self.get(name))
555 return rv
556
558 '''
559 List the environment parameter names on which the refinements are organized.
560 For example, the temperature or pressure series
561
562 @return: the environment parameter names
563 '''
564 return self.labels
565
567 '''
568 List the Pattern objects in the refinement
569
570 @return: a list of patterns
571 '''
572 return self.listObjects(Pattern, recursively = True)
573
575 '''
576 List the Phase objects in the refinement
577
578 @return: a list of phases
579 '''
580 return self.listObjects(Phase, recursively = True)
581
583 '''
584 Return a list of phase names in the refinement
585
586 @return: a list of phase names
587 '''
588 return [obj.get('Name').first() for obj in self.listPhases()]
589
591 '''
592 Get a list of Profile objects in the refinement
593
594 @return: a list of Profile objects
595 '''
596 return self.listObjects(Profile, recursively = True)
597
599 '''load the data file to the project
600
601 @type datafile: file path string
602 @param datafile: the data file absolute path
603 @type index: integer or tuple
604 @param index: the index of the single refinement
605 @type bankid: integer
606 @param bankid: the bank id of the pattern, default to be 0
607 @return: no return value
608 '''
609
610 patterns = self.getObject("Pattern")
611 pattern = patterns[bankid]
612 ds = pattern.get('Datafile')
613
614 self.saveFilePathToDataset(datafile, ds, index)
615
616
617 return
618
620 '''Load the incident spectrum file path into the object
621
622 @type mfil: file path string
623 @param mfil: the abs path of the incident spectrum file MFIL
624 @param index: integer or tuple
625 @type index: the index of the single refinement
626 @type bankid: integers
627 @param bankid: the bank id of the pattern
628 @return: no return value
629 '''
630 patterns = self.getObject("Pattern")
631 pattern = patterns[bankid]
632 ds = pattern.get('MFIL')
633
634 self.saveFilePathToDataset(mfil, ds, index)
635 return
636
638 '''load the instrument file path to the pattern
639 instrumentfile - the abs path to the instrument file
640
641 @type index: integer or tuple
642 @param index: the index of the single refinement
643 @type bankid: integer or tuple
644 @param bankid: the bankid of the pattern
645 @return: no return value
646 '''
647 patterns = self.getObject("Pattern")
648 pattern = patterns[bankid]
649 ds = pattern.get('Instrumentfile')
650
651 self.saveFilePathToDataset(instrumentfile, ds, index)
652
653 return
654
656 '''load the strategy from a text file
657
658 @type fullpath: file path string
659 @param fullpath: the stratey file path
660 @return: no return value
661 '''
662 st = StrategyFile(self, fullpath)
663 self.setStrategy(st.strategy)
664 return
665
666 @property
668 '''Get the number of strategy steps
669
670 @return: the total number of strategy'''
671 return len(self.strategy)
672
674 '''remove parameter from the strategy
675
676 @type datapath: string
677 @param datapath: the data signature path to be removed from the strategy
678 @type step: integer
679 @param step: the strategy step.
680 - None, will remove the strategy from all steps
681 - 0 <= step < total number of existing steps, the new
682 parameter will be removed from the step
683 @return: no return value
684 '''
685 datapaths = self.getParamListForStrategy(datapath)
686
687 for datapath in datapaths:
688 strategyList = self.strategy
689 if step is not None:
690 if step > len(strategyList) and step < 0:
691 print "Step number (%s) is out of strategy list boundary (0 - %s)" % (step, len(strategyList))
692 return
693 else:
694 sf = StrategyFile(self,strategy = self.strategy)
695 paramList = sf.getStepParamList(step + 1)
696 for idx, param in enumerate(paramList):
697 if datapath == param[0]:
698 paramList.pop(idx)
699
700 type = sf.getStepMethod(step)
701
702 strategyList[step][type] = paramList
703 self.setStrategy(strategyList)
704
705 else:
706 for ss in range(len(strategyList)):
707 self.removeParamFromStrategy(datapath, ss)
708
709 return
710
712 '''Save the engine file content to the datasets
713 @type enginefile: filepath string
714 @param enginefile: the engine file path
715 @type index: integer or tuple
716 @param index: the index of the single refinement
717 @return: no return value
718 '''
719 ds = self.get('enginefile')
720 if ds is None:
721 self.set('enginefile', open(enginefile, 'rb').read(),
722 repeat = True, grow = True)
723 else:
724 ds[index] = open(enginefile, 'rb').read()
725
726 return
727
729 '''Save the absolute file path to the dataset
730
731 @type ds: Dataset object
732 @param ds: the dataset to store the file path information
733 @type index: integer or tuple
734 @param index: the index of the dataset
735 @type absPath: file path string
736 @param absPath: the absolute path of the data file
737 @type basepath: directory path string
738 @param basepath: the basepath used to determine the relative path,
739 the project.basepath will be used if basepath None
740 @return: no return value
741 '''
742 dname = ds.name
743 parent = ds.owner
744 if absPath:
745 if basepath is None: basepath = self.owner.basepath
746 relpath = os.path.relpath(absPath, basepath)
747 else:
748 relpath = ""
749
750 ds[index] = relpath
751
752 ds = parent.get(dname + '_abspath')
753 if ds is None:
754 parent.set(dname + '_abspath', absPath, repeat = True, grow = True)
755 else:
756 ds[index] = absPath
757
758 return
759
761 '''
762 Set the strategy to refinement object
763
764 @type strategylist: list
765 @param strategylist: a list with strategy steps
766 @return: no return value
767 '''
768 self.setPyObj('Strategy', strategylist)
769 return
770
771 @property
773 '''Get the strategy list from the fit object
774
775 @return: the strategy list
776 '''
777 strategylist = self.getPyObj("Strategy")
778 return strategylist
779
781 '''write the strategy to a local file
782
783 @type fullpath: file path string
784 @param fullpath: the file path to write to
785 @return: no return value
786 '''
787 st = StrategyFile(self, strategy = self.strategy)
788 st.writeToFile(fullpath)
789 return
790
791
792
793
794