CMS 3D CMS Logo

TH2PolyOfflineMaps.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 
3 from __future__ import print_function
4 import sys
5 import os
6 from ROOT import *
7 from copy import deepcopy
8 from array import array
9 
10 gROOT.SetBatch() # don't pop up canvases
11 
12 #Find Data files
13 
14 def getFileInPath(rfile):
15  import os
16  for dir in os.environ['CMSSW_SEARCH_PATH'].split(":"):
17  if os.path.exists(os.path.join(dir,rfile)): return os.path.join(dir,rfile)
18  return None
19 
20 
21 # Default values
22 inputFileName = "DQM_V0013_R000292154__StreamExpressCosmics__Commissioning2017-Express-v1__DQMIO.root"
23 limitsFileName = "limits.dat"
24 outputDirectoryName = "OUT/"
25 minMaxFileName = "minmax.out"
26 #detIDsFileName = "DATA/detids.dat"
27 detIDsFileName = getFileInPath('DQM/SiStripMonitorClient/data/detids.dat')
28 #default one
29 baseRootDirs = ["DQMData/Run 292154/PixelPhase1/Run summary/Phase1_MechanicalView"
30  ,"DQMData/Run 292154/PixelPhase1/Run summary/Tracks"
31  ]
32 
33 
34 maxPxBarrel = 4
35 maxPxForward = 3
36 barrelLadderShift = [0, 14, 44, 90]
37 
38 forwardDiskXShift = [25, 75, 125]
39 forwardDiskYShift = 45; # to make +DISK on top in the 'strip-like' layout
40 
41 plotWidth, plotHeight = 3000, 2000
42 extremeBinsNum = 20
43 
44 limits = ["num_digis 0.01 90 1 0",
45  "num_clusters 0.01 25 1 0",
46  "Trechitsize_y 0.01 10 0 0",
47  "Trechitsize_x 0.01 10 0 0",
48  "Tresidual_y 0.0000001 0.004 0 1",
49  "Tresidual_x 0.0000001 0.004 0 1",
50  "Tcharge 2000 80000 0 0",
51  "Thitefficiency 0.95 1 0 0",
52  #"Tmissing 0.01 500 0 0",
53  "Tnum_clusters_ontrack 0.01 15 1 0",
54  "Tsize 0.01 15 0 0",
55  #"Tvalid 0.01 90 0 0",
56  "adc 0.01 256 0 0",
57  "charge 2000 80000 0 0",
58  "size 0.01 15 0 0",]
59 
61 
62 
65 
66 
67 
68  def __TraverseDirTree(self, dir):
69 
70  try:
71  currPath = (dir.GetPath().split(":/"))[1]
72  except:
73  print("Exception raised: Path not found in the input file")
74  return
75 
76  for obj in dir.GetListOfKeys():
77  if not obj.IsFolder():
78  if obj.ReadObjectAny(TClass.GetClass("TH2")):
79  th2 = deepcopy(obj.ReadObj())
80  name = th2.GetName()
81  if 6 < th2.GetNbinsX() < 10 and name.find("per") != -1 and name.find("Lumisection") == -1: #take only module lvl plots
82  print(''.join([dir.GetPath(), '/', name]))
83 
84  # fix when there are plots starting with the same strings in different directories
85  prefix = ""
86  for i in self.dirs:
87  if currPath.startswith(i):
88  prefix = self.dirsAliases[i]
89  break
90  # print(currPath, prefix)
91  th2.SetName(prefix + th2.GetName())
92  self.listOfNumHistograms.append(th2)
93  else:
94  self.__TraverseDirTree(obj.ReadObj())
95 
96  def __GetPartStr(self, isXlowerThanZero, isYlowerThanZero):
97  if isXlowerThanZero and isYlowerThanZero:
98  return "mO"
99  if isXlowerThanZero and isYlowerThanZero == False:
100  return "mI"
101  if isXlowerThanZero == False and isYlowerThanZero:
102  return "pO"
103  if isXlowerThanZero == False and isYlowerThanZero == False:
104  return "pI"
105 
106  def __GetBarrelSector(self, layer, signedLadder, signedModule): #adapted from PixelBarrelName
107  theLadder = abs(signedLadder)
108  theModule = abs(signedModule)
109 
110  sector = 0
111 
112  if layer == 1:
113 
114  if theLadder == 1:
115  if theModule >= 2:
116  return 1
117  else:
118  return 2
119  if theLadder == 2:
120  if theModule >= 3:
121  return 2
122  else:
123  return 3
124  if theLadder == 3:
125  if theModule >= 4:
126  return 3
127  else:
128  return 4
129  if theLadder == 4:
130  if theModule >= 2:
131  return 5
132  else:
133  return 6
134  if theLadder == 5:
135  if theModule >= 3:
136  return 6
137  else:
138  return 7
139  if theLadder == 6:
140  if theModule >= 4:
141  return 7
142  else:
143  return 8
144  # here is used simplified form of assignment, see source file for reference
145  elif layer == 2:
146  i = theLadder // 5
147  sector = i * 3
148  shortLadder = theLadder - 5 * i
149  for i in range(0, shortLadder, 2):
150  sector = sector + 1
151  return sector
152  elif layer == 3:
153  sector = 1
154  for i in range(2, theLadder, 3):
155  if (i + 1) % 3 == 0:
156  sector = sector + 1
157  return sector
158  elif layer == 4:
159  sector = (theLadder + 3) // 4
160  return sector
161 
162  def __BuildOnlineBarrelName(self, signedModule, signedLadder, layer): #in Phase1 it is assumed that there are only full modules
163  thePart = self.__GetPartStr(signedModule < 0, signedLadder < 0)
164  theSector = str(self.__GetBarrelSector(layer, signedLadder, signedModule))
165  return "BPix_B" + thePart + "_SEC" + theSector + "_LYR" + str(layer) + "_LDR" + str(abs(signedLadder)) + "F_MOD" + str(abs(signedModule))
166 
167  def __BuildOnlineDiskName(self, signedDisk, signedBlade, panel, ring):
168  thePart = self.__GetPartStr(signedDisk < 0, signedBlade < 0)
169  return "FPix_B" + thePart + "_D" + str(abs(signedDisk)) + "_BLD" + str(abs(signedBlade)) + "_PNL" + str(panel) + "_RNG" + str(ring)
170 
171  def __GroupHistograms(self):
172  currentGroupName = ""
173  groupOfHists = []
175 
176 
177  for obj in self.listOfNumHistograms:
178  objName = obj.GetName()
179  objNameSplit = objName.split("_")
180  objNameCollected = ''.join(objNameSplit[0:-1])
181  if objNameCollected != currentGroupName:
182  if len(groupOfHists):
183  self.groupedHistograms.append(groupOfHists)
184  groupOfHists = []
185 
186  currentGroupName = objNameCollected
187  groupOfHists.append(obj)
188  self.groupedHistograms.append(groupOfHists) #the last group
189 
190  def __AddNamedBins(self, geoFile, tX, tY, sX, sY, applyModuleRotation = False):
191 
192  for line in geoFile:
193  lineSpl = line.strip().split("\"")
194  #New TH2Poly bin ID = full_name_(detId)
195  detId = str(lineSpl[0].split(" ")[1])+"_("+str(lineSpl[0].split(" ")[0])+")"
196  vertices = lineSpl[1]
197  xy = vertices.split(" ")
198  x, y = array('d'), array('d')
199  verNum = 1
200  for coord in xy:
201  coordSpl = coord.split(",")
202  if applyModuleRotation:
203  x.append(-(float(coordSpl[0]) * sX + tX))
204  y.append((float(coordSpl[1]) * sY + tY))
205  else:
206  x.append(float(coordSpl[0]) * sX + tX)
207  y.append(float(coordSpl[1]) * sY + tY)
208  verNum = verNum + 1
209  #close polygon
210  x.append(x[0])
211  y.append(y[0])
212 
213  if applyModuleRotation:
214  bin = TGraph(verNum, y, x)
215  else:
216  bin = TGraph(verNum, x, y)
217  # bin = TGraph(verNum, y, x) # rotation by 90 deg (so that it had the same layout as for the strips)
218  bin.SetName(detId)
219 
220  self.__BaseTrackerMap.AddBin(bin)
221 
223 
224  self.__BaseTrackerMap = TH2Poly("Summary", "", -10, 160, -70, 70)
225  # self.__BaseTrackerMap = TH2Poly("Summary", "Tracker Map", 0, 0, 0, 0)
226  self.__BaseTrackerMap.SetFloat(1)
227  self.__BaseTrackerMap.GetXaxis().SetTitle("")
228  self.__BaseTrackerMap.GetYaxis().SetTitle("")
229  self.__BaseTrackerMap.SetOption("COLZ L")
230  self.__BaseTrackerMap.SetStats(0)
231 
232  # BARREL FIRST
233  for i in range(maxPxBarrel):
234  with open(self.geometryFilenames[i], "r") as geoFile:
235  currBarrelTranslateX = 0
236  currBarrelTranslateY = barrelLadderShift[i]
237 
238  self.__AddNamedBins(geoFile, currBarrelTranslateX, currBarrelTranslateY, 1, 1, True)
239 
240  # break # debug only 1st layer
241 
242  # MINUS FORWARD
243  for i in range(-maxPxForward, 0):
244  with open(self.geometryFilenames[maxPxBarrel + maxPxForward + i], "r") as geoFile:
245  currForwardTranslateX = forwardDiskXShift[-i - 1]
246  currForwardTranslateY = -forwardDiskYShift
247 
248  self.__AddNamedBins(geoFile, currForwardTranslateX, currForwardTranslateY, 1, 1)
249 
250  # PLUS FORWARD
251  for i in range(maxPxForward):
252  with open(self.geometryFilenames[maxPxBarrel + maxPxForward + i], "r") as geoFile:
253  currForwardTranslateX = forwardDiskXShift[i]
254  currForwardTranslateY = forwardDiskYShift
255 
256  self.__AddNamedBins(geoFile, currForwardTranslateX, currForwardTranslateY, 1, 1)
257 
258  # self.__BaseTrackerMap.Fill("305139728", 2)
259 
260  print("Base Tracker Map: constructed")
261 
262 
263  def __init__(self, inputDQMName, outputDirName, minMaxFileName, limits, modDicName, runNumber, dirs, dirsAliases):
264 # def __init__(self, inputDQMName, outputDirName, minMaxFileName, limitsFileName, modDicName, runNumber, dirs, dirsAliases):
265  self.inputFileName = inputDQMName
266  self.outputDirName = outputDirName
267  self.minMaxFileName = minMaxFileName
268 # self.limitsFileName = limitsFileName
269  self.detIDsFileName = modDicName
270  self.limits = limits
271 
272  self.runNumber = runNumber
273  self.dirs = dirs
274  self.dirsAliases = dirsAliases
275 
276  self.inputFile = TFile(self.inputFileName)
278  self.availableNames = []
279 
280  self.maxLadderToLayer = {6:1, 14:2, 22:3, 32:4}
281  self.maxBladeToRing = {11:1, 17:2}
282 
284  for i in range(maxPxBarrel):
285  self.geometryFilenames.append(getFileInPath("DQM/SiStripMonitorClient/data/Geometry/vertices_barrel_" + str(i + 1)))
286 # self.geometryFilenames.append("DATA/Geometry/vertices_barrel_" + str(i + 1))
287  for i in range(-maxPxForward, maxPxForward + 1):
288  if i == 0:
289  continue #there is no 0 disk
290  self.geometryFilenames.append(getFileInPath("DQM/SiStripMonitorClient/data/Geometry/vertices_forward_" + str(i)))
291 # self.geometryFilenames.append("DATA/Geometry/vertices_forward_" + str(i))
292 
293  self.internalData = {}
294 
295  if self.inputFile.IsOpen():
296  print("%s opened successfully!" % (self.inputFileName))
297  #Get all neeeded histograms
298  for dir in self.dirs:
299  self.__TraverseDirTree(self.inputFile.Get(dir))
300  # print("Histograms to read %d" % (len(self.listOfNumHistograms)))
301 
302  self.detDict = {}
303 
304  with open(self.detIDsFileName, "r") as detIDs: # create dictionary online -> rawid
305  for entry in detIDs:
306  items = entry.replace("\n", " ").split(" ")
307  self.detDict.update({items[1] : int(items[0])})
308  # init internal data structure
309  self.internalData.update({int(items[0]) : {}})
310 
311  self.rawToOnlineDict = dict((v,k) for k,v in self.detDict.items())
312 
313  self.__GroupHistograms()
314 
316 
317  else:
318  print("Unable to open file %s" % (self.inputFileName))
319 
320 
321 
322  self.limitsDic = {}
323  for y in limits:
324 
325  lineSpl = y.strip().split(" ")
326 
327  if len(lineSpl) < 5:
328  continue
329 
330  currName = lineSpl[0]
331  zMin = float(lineSpl[1])
332  zMax = float(lineSpl[2])
333  isLog = False if lineSpl[3] == "0" else True
334  isAbs = False if lineSpl[4] == "0" else True
335 
336  self.limitsDic.update({currName : {"zMin" : zMin, "zMax" : zMax, "isLog" : isLog, "isAbs" : isAbs}})
337  # print limitsDic
338 
339  def ReadHistograms(self):
340  if self.inputFile.IsOpen():
341  for group in self.groupedHistograms:
342  # name = ''.join(group[0].GetName().split("_")[0:-1])
343  if len(group) == 0:
344  return
345  print(group[0].GetName())
346  name = ''.join(group[0].GetName().split("_per_")[0])
347  self.availableNames.append(name)
348  # print(name)
349  for obj in group:
350  nbinsX = obj.GetNbinsX()
351  nbinsY = obj.GetNbinsY()
352 
353  if nbinsX == 9: # BARREL
354  maxX = nbinsX // 2
355  maxY = nbinsY // 2
356 
357  for x in range(-maxX, maxX + 1):
358  if x == 0:
359  continue
360  for y in range(-maxY, maxY + 1, 1):
361  if y == 0:
362  continue
363  onlineName = self.__BuildOnlineBarrelName(x, y, self.maxLadderToLayer[maxY])
364  self.internalData[self.detDict[onlineName]].update({name : obj.GetBinContent(x + maxX + 1, y + maxY + 1)})
365 
366  elif nbinsX == 7: # FORWARD
367  maxX = nbinsX // 2
368  maxY = nbinsY // 4
369 
370  for x in range(-maxX, maxX + 1):
371  if x == 0:
372  continue
373  for y in range(-maxY, maxY + 1):
374  if int(y) == 0:
375  continue
376  for panel in range(1, 3):
377  onlineName = self.__BuildOnlineDiskName(x, y, panel, self.maxBladeToRing[maxY])
378  self.internalData[self.detDict[onlineName]].update({name : obj.GetBinContent(x + maxX + 1, (y + maxY) * 2 + (3-panel))})
379  else:
380  print("Unrecognized plot")
381  else:
382  print("Histograms saved to internal data structure")
383 
384  def DumpData(self):
385  for key in self.internalData:
386  print("#"*20)
387  print(key)
388  module = self.internalData[key]
389  for d in module:
390  print((d, module[d]))
391 
392  print(len(self.internalData))
393 
394  for i in self.availableNames:
395  print(i)
396  print(len(self.availableNames))
397 
398  def PrintTrackerMaps(self):
399  monitoredValues = []
400  gStyle.SetPalette(1)
401  for key in self.internalData:
402  monitoredValues = self.internalData[key].keys()
403  # print(monitoredValues)
404  break
405 
406  if os.path.exists(self.outputDirName) == False: # check whether directory exists
407  os.system("mkdir " + self.outputDirName)
408 
409  with open(self.outputDirName + self.minMaxFileName, "w") as minMaxFile:
410 
411  for mv in monitoredValues:
412  currentHist = deepcopy(self.__BaseTrackerMap)
413  # currentHist.SetTitle("Run " + self.runNumber + ": Tracker Map for " + mv) // to make it compatible between ROOT v.
414  histoTitle = "Run " + self.runNumber + ": Tracker Map for " + mv
415 
416  applyLogScale = False
417  applyAbsValue = False
418  if mv in self.limitsDic:
419  limitsElem = self.limitsDic[mv]
420 
421  print(mv + " found in limits dictionary - applying custom limits...")
422 
423  currentHist.SetMinimum(limitsElem["zMin"])
424  currentHist.SetMaximum(limitsElem["zMax"])
425  applyLogScale = limitsElem["isLog"]
426  applyAbsValue = limitsElem["isAbs"]
427 
428  listOfVals = []
429  onlineName = ""
430  nameId = ""
431 
432  for detId in self.internalData:
433  val = (self.internalData[detId])[mv]
434  onlineName = self.rawToOnlineDict[detId]
435  listOfVals.append([val, detId, onlineName])
436  #New TH2Poly bin ID = full_name_(detId)
437  nameId = str(onlineName)+"_("+str(detId)+")"
438 
439  if applyAbsValue:
440  currentHist.Fill(str(nameId), abs(val))
441  else:
442  currentHist.Fill(str(nameId), val)
443 
444  listOfVals = sorted(listOfVals, key = lambda item: item[0])
445 
446  minMaxFile.write("\n" + mv + "\n\n")
447 
448  minMaxFile.write("MIN:\n")
449  for i in range(extremeBinsNum):
450  minMaxFile.write("\t" + str(listOfVals[i][1]) + " " + str(listOfVals[i][2]) + " " + str(listOfVals[i][0]) + "\n")
451 
452  minMaxFile.write("MAX:\n")
453  for i in range(extremeBinsNum):
454  minMaxFile.write("\t" + str(listOfVals[-i - 1][1]) + " " + str(listOfVals[-i - 1][2]) + " " + str(listOfVals[-i - 1][0]) + "\n")
455 
456  #Canvas name = MyT for both Pixel and Strip Tracker Maps
457  c1 = TCanvas("MyT", "MyT", plotWidth , plotHeight)
458 
459  if applyLogScale:
460  c1.SetLogz()
461 
462  currentHist.Draw("AC COLZ L")
463 
464  gPad.Update()
465  palette = currentHist.FindObject("palette");
466  palette.SetX1NDC(0.89);
467  palette.SetX2NDC(0.91);
468  palette.SetLabelSize(0.05);
469  gPad.Update()
470 
471 
474 
475  # draw axes (z, phi -> BARREL; x, y -> FORWARD)
476 
477 
478 
479  arrow = TArrow(0.05, 27.0, 0.05, -30.0, 0.02, "|>")
480  arrow.SetLineWidth(4)
481  arrow.Draw()
482 
483  phiArrow = TArrow(0.0, 27.0, 30.0, 27.0, 0.02, "|>")
484  phiArrow.SetLineWidth(4)
485  phiArrow.Draw()
486 
487  xArrow = TArrow(25.0, 44.5, 50.0, 44.5, 0.02, "|>")
488  xArrow.SetLineWidth(4)
489  xArrow.Draw()
490 
491  yArrow = TArrow(25.0, 44.5, 25.0, 69.5, 0.02, "|>")
492  yArrow.SetLineWidth(4)
493  yArrow.Draw()
494 
495 
496 
497  # add some captions
498  txt = TLatex()
499  txt.SetNDC()
500  txt.SetTextFont(1)
501  txt.SetTextColor(1)
502  txt.SetTextAlign(22)
503  txt.SetTextAngle(0)
504 
505  # draw new-style title
506  txt.SetTextSize(0.05)
507  txt.DrawLatex(0.5, 0.95, histoTitle)
508 
509  txt.SetTextSize(0.03)
510 
511  txt.DrawLatex(0.5, 0.125, "-DISK")
512  txt.DrawLatex(0.5, 0.075, "NUMBER ->")
513  txt.DrawLatex(0.5, 0.875, "+DISK")
514 
515  txt.DrawLatex(0.17, 0.35, "+z")
516  txt.DrawLatexNDC(0.36, 0.685, "+phi") # WAY TO FORCE IT TO DRAW LATEX CORRECTLY NOT FOUND ('#' DOESN'T WORK)
517  txt.DrawLatex(0.38, 0.73, "+x")
518  txt.DrawLatex(0.26, 0.875, "+y")
519 
520  txt.SetTextAngle(90)
521  txt.DrawLatex(0.17, 0.5, "BARREL")
522 
523  #save to the png
524  c1.Print(self.outputDirName + mv + ".png")
525 
526  #Clean canvas, change settings, save as root
527  c1.Clear()
528  c1.SetLogz(False)
529  currentHist.GetZaxis().UnZoom()
530  currentHist.SetLineColor(kBlack)
531  currentHist.Draw("AL COLZ")
532  currentHist.GetXaxis().SetRangeUser(-10,155)
533 
534  palette.SetX1NDC(0.92);
535  palette.SetX2NDC(0.94);
536  palette.SetY1NDC(0.02);
537  palette.SetY2NDC(0.91);
538  gPad.SetRightMargin(0.08);
539  gPad.SetLeftMargin(0.01);
540  gPad.SetTopMargin(0.09);
541  gPad.SetBottomMargin(0.02);
542  gPad.Update()
543 
544  zarrow = TArrow(0, 27, 0, -30, 0.02, "|>")
545  zarrow.SetLineWidth(3)
546  zarrow.Draw()
547  phiArrow.SetLineWidth(3)
548  phiArrow.Draw()
549  xArrow.SetLineWidth(3)
550  xArrow.Draw()
551  yArrow.SetLineWidth(3)
552  yArrow.Draw()
553 
554  txt.Clear()
555  txt.SetTextAngle(0)
556  txt.SetTextSize(0.05)
557  PixelTitle = "Run " + self.runNumber + ": Pixel " + mv
558  txt.DrawLatex(0.5, 0.95, PixelTitle)
559 
560  txt.SetTextSize(0.04)
561  txt.SetNDC(False)
562  txt.DrawLatex(75, -65, "-DISK")
563  txt.DrawLatex(75, 65, "+DISK")
564  txt.DrawLatex(50, -60, "NUMBER ->")
565 
566  txt.DrawLatex(-5, -20, "+z")
567  txt.DrawLatex(35, 30, "+phi")
568  txt.DrawLatex(55, 45, "+x")
569  txt.DrawLatex(30, 65, "+y")
570 
571  txt.SetTextAngle(90)
572  txt.DrawLatex(-5, 0, "BARREL")
573 
574  c1.SaveAs(self.outputDirName + mv + ".root")
575  c1.Close()
576 
577  def __del__(self):
578  if self.inputFile :
579  if self.inputFile.IsOpen():
580  self.inputFile.Close()
581 
582 #--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--
583 for i in range(1, len(sys.argv), 1):
584  if i == 1:
585  inputFileName = sys.argv[i]
586  elif i == 2:
587  plotWidth = int(sys.argv[i])
588  elif i == 3:
589  plotHeight = int(sys.argv[i])
590 # elif i == 4:
591 # limitsFileName = sys.argv[i]
592 # elif i == 5:
593  elif i == 4:
594  detIDsFileName = sys.argv[i]
595 
596 deductedRunNumber = inputFileName.split("_R000")[1][0:6]
597 print(deductedRunNumber)
598 
599 baseRootDirs = ["DQMData/Run " + deductedRunNumber + "/PixelPhase1/Run summary/Phase1_MechanicalView" #maybe read it from the input file???
600  ,"DQMData/Run " + deductedRunNumber + "/PixelPhase1/Run summary/Tracks"
601  ]
602 
603 baseRootDirsAliases = {baseRootDirs[0]:""
604  , baseRootDirs[1]:"T"
605  }
606 
607 readerObj = TH2PolyOfflineMaps(inputFileName, outputDirectoryName, minMaxFileName, limits, detIDsFileName, deductedRunNumber, baseRootDirs, baseRootDirsAliases)
608 #readerObj = TH2PolyOfflineMaps(inputFileName, outputDirectoryName, minMaxFileName, limitsFileName, detIDsFileName, deductedRunNumber, baseRootDirs, baseRootDirsAliases)
609 readerObj.ReadHistograms()
610 # readerObj.DumpData()
611 readerObj.PrintTrackerMaps()
def __GetBarrelSector(self, layer, signedLadder, signedModule)
def __BuildOnlineDiskName(self, signedDisk, signedBlade, panel, ring)
def __init__(self, inputDQMName, outputDirName, minMaxFileName, limits, modDicName, runNumber, dirs, dirsAliases)
void print(TMatrixD &m, const char *label=nullptr, bool mathematicaFormat=false)
Definition: Utilities.cc:47
Abs< T >::type abs(const T &t)
Definition: Abs.h:22
static std::string join(char **cmd)
Definition: RemoteFile.cc:19
def __GetPartStr(self, isXlowerThanZero, isYlowerThanZero)
def __BuildOnlineBarrelName(self, signedModule, signedLadder, layer)
#define update(a, b)
def __AddNamedBins(self, geoFile, tX, tY, sX, sY, applyModuleRotation=False)
#define str(s)
T * Get(Args... args)
Definition: Trend.h:122