source: trunk/Mars/mfileio/MWriteFitsFile.cc@ 14857

Last change on this file since 14857 was 14792, checked in by lyard, 12 years ago
Added fits output for MC data
File size: 41.6 KB
Line 
1#include <TDataMember.h>
2#include <TClonesArray.h>
3
4#include "MArrayS.h"
5#include "MArrayB.h"
6
7#include "MRead.h"
8#include "MParList.h"
9#include "MStatusDisplay.h"
10
11#include "MLog.h"
12#include "MLogManip.h"
13#include "MWriteRootFile.h"
14
15#include "MWriteFitsFile.h"
16#include "MFitsArray.h"
17
18//for find
19#include <algorithm>
20
21ClassImp(MWriteFitsFile);
22//ClassImp(MArrayHelper);
23//ClassImp(MTopFitsGroup);
24
25using namespace std;
26//ClassImp(std::ofits);
27
28
29map<MUniqueFileId, MTopFitsGroup> MWriteFitsFile::fTopFitsGroups;
30
31
32
33// ----------------------------------------------------------------------------
34//
35// if fileMode == kSingleFile
36// Opens the top level group file, if it was not already opened by an other
37// instance of MWriteFitsFile.
38// if fileMode == kMultiFiles
39// fname defines the rules to build the output filenames from the current
40// input filename. The files are opened in the ReInit() method.
41//
42// opt = RECREATE : an existing file will be deleted
43// = NEW : an already existing file will not be deleted and the new
44// file is not created. Check with IsFileOpen() if the new
45// top level group could be opened.
46// name will be the name of the group
47// title will be written as keyword TITLE into the header
48//
49MWriteFitsFile::MWriteFitsFile(const char *fname,
50 FILE_MODE fileMode,
51 const Option_t *opt,
52 const char *name,
53 const char *title)
54{
55 fOpenOption = opt;
56 if (name && name[0] != 0)
57 fGroupName = name;
58 else
59 fGroupName = "MARS";
60
61 if (title)
62 fTitle = title;
63
64 iTopFitsGroup = fTopFitsGroups.end();
65
66 if (fileMode == kMultiFiles)
67 fRule = fname;
68 else
69 *fLog << err << "The new version of MWriteFitsFile does not support multiple tables in one file. Please use kMultiFiles instead" << endl;
70 //ETIENNE DEPREC
71 // iTopFitsGroup will be set by OpenTopLevelGroup, if a top level
72 // group can be assigned to this MWriteFitsFile.
73 //OpenTopLevelGroup(fname);
74}
75
76MWriteFitsFile::MWriteFitsFile(const Int_t comp,
77 const char* rule,
78 const Option_t* option,
79 const char* ftitle,
80 const char* name,
81 const char* title)
82{
83 fOpenOption = option;
84 if (name && name[0] != 0)
85 fGroupName = name;
86 else
87 fGroupName = "MARS";
88 if (title) fTitle = ftitle;
89 iTopFitsGroup = fTopFitsGroups.end();
90 //always kMultiFiles
91 fRule = rule;
92}
93// ----------------------------------------------------------------------------
94//
95// Closes the top level group, it is is not used by an other instance of
96// MWriteFitsFile.
97// std::map<TString, std::ofits*>
98MWriteFitsFile::~MWriteFitsFile()
99{
100 for (std::map<TString, std::ofits*>::iterator it=fFitsTables.begin(); it!= fFitsTables.end();it++)
101 {
102 if (it->second != NULL)
103 {
104 it->second->close();
105 delete it->second;
106 }
107 }
108// CloseTopLevelGroup();
109 DeleteArrayHelper();
110}
111
112// ----------------------------------------------------------------------------
113//
114// If the same file is already used by an other instance of MWriteFitsFile,
115// the user counter is increasesd.
116// Else: If the file exist and option is "RECREATE", the file is deleted and
117// recreated.
118// Finally the file with an empty group file is created.
119//
120void MWriteFitsFile::OpenTopLevelGroup(const char * fname)
121{
122 // get rid of environment variables, ~, ... in filename
123 char * expandedFileName = gSystem->ExpandPathName(fname);
124
125 // get unique identifier of the new file
126 struct stat fileId;
127 if (stat(expandedFileName, &fileId) == 0)
128 {
129 // file exist already
130 iTopFitsGroup = fTopFitsGroups.find(MUniqueFileId(fileId.st_dev, fileId.st_ino));
131 if (iTopFitsGroup != fTopFitsGroups.end())
132 {
133 *fLog << err << "You are trying to use an already existing file. Please choose a different file name (" << fname << ")" << endl;
134 exit(0);
135 // this group is already open. we use it as well
136 *fLog << inf2 << "Found open file '" << GetFileName() << "'... re-using." << endl;
137 iTopFitsGroup->second.IncreaseUsage();
138 delete [] expandedFileName;
139 return;
140 }
141
142 // file exist, but we cannot use
143 if (fOpenOption == "RECREATE")
144 // remove the file
145 unlink(expandedFileName);
146
147 else if (fOpenOption == "NEW")
148 {
149 *fLog << err << "File '" << expandedFileName << "' exist already." << endl;
150 delete [] expandedFileName;
151 return;
152 }
153 else
154 {
155 *fLog << err << "Unsupported option (" << fOpenOption.Data() << ") to open file " <<
156 expandedFileName << endl;
157 delete [] expandedFileName;
158 return;
159 }
160 }
161
162 // file does not yet exist or was deleted
163
164// try {
165// AstroRootGroup topGroup;
166// char wrTitle[fTitle.Length() + 1];
167// if (fTitle.Length() > 0)
168// {
169// strcpy(wrTitle, fTitle.Data());
170// topGroup.InitAttr("TITLE", "A", wrTitle);
171// }
172
173 //ETIENNE do not open top level group as using one file per table
174 //I kept this code active anyway, because it is checked elsewhere in the code that iTopFitsGroup is set.
175 ofits* topGroup = NULL;
176// ofits* topGroup = new ofits();
177// TString dol = expandedFileName;
178// topGroup->open(dol.Data());
179
180
181 // store this new file in fTopFitsGroups and get the iterator to the
182 // new group
183 stat(expandedFileName, &fileId);
184 iTopFitsGroup = fTopFitsGroups.insert(pair<MUniqueFileId, MTopFitsGroup>
185 (MUniqueFileId(fileId.st_dev, fileId.st_ino),
186 MTopFitsGroup(expandedFileName, topGroup) )).first;
187
188 delete [] expandedFileName;
189
190}
191
192// --------------------------------------------------------------------------
193//
194// Reduces the use - counter of the top level group. Closes the group
195// table, if the counter is 0, i.e. no other MWriteFitsFile instance uses
196// the same top level group.
197//
198
199void MWriteFitsFile::CloseTopLevelGroup()
200{
201 if (iTopFitsGroup != fTopFitsGroups.end())
202 {
203 iTopFitsGroup->second.DecreaseUsage();
204 if (iTopFitsGroup->second.GetNumUsage() <= 0)
205 fTopFitsGroups.erase(iTopFitsGroup);
206
207 iTopFitsGroup = fTopFitsGroups.end();
208 }
209}
210
211// --------------------------------------------------------------------------
212//
213// returns the name of the top level group file. It may be expanded if the
214// name, given by the user, contained environment variables of the ~ sign.
215// If no top level group is open, "no_open_file" is returned.
216//
217const char * MWriteFitsFile::GetFileName() const
218{
219 if (IsFileOpen())
220 return iTopFitsGroup->second.GetFileName().Data();
221 else
222 return "no_open_file";
223}
224
225// --------------------------------------------------------------------------
226//
227// Add a new Container to list of containers which should be written to the
228// file. Give the name of the container which will identify the container
229// in the parameterlist. tname is the name of the FITS table to which the
230// container should be written (Remark: one table can hold more than one
231// container). The default is the same name as the container name.
232// If "mus"t is set to kTRUE (the default), then the container must exist
233// in the parameterlist. The container will be ignored, if "must" is set to
234// kFALSE and the container does not exist in the parameterlist.
235//
236void MWriteFitsFile::AddContainer(const char *cName,
237 const char *tName,
238 Bool_t must)
239{
240 if (cName == NULL || strlen(cName) == 0)
241 {
242 *fLog << warn << "Warning - container name in method MWriteFitsFile::"
243 "AddContainer not defined... ignored" << endl;
244 return;
245 }
246
247 // if tName is not defined, we use cName as the table name
248 TString tableName;
249 if (tName == NULL || strlen(tName) == 0)
250 tableName = cName;
251 else
252 tableName = tName;
253
254 // try to insert this container to the list of sub-tables. Write a
255 // warning, if the same container name was already defined and ignore
256 // this one.
257 if (fSubTables[tableName].insert( pair<TString, MFitsSubTable>
258 (cName, MFitsSubTable(must))).second == false)
259 {
260 *fLog << warn << "Warning - Container '"<< cName <<"' in table '" <<
261 tableName.Data() << "' already scheduled... ignored." << endl;
262 }
263}
264
265// --------------------------------------------------------------------------
266//
267// Add a new Container to list of containers which should be written to the
268// file. tname is the name of the FITS table to which the
269// container should be written (Remark: one table can hold more than one
270// container). The default is the same name as the container name.
271// "must" is ignored. It is just for compatibility with MWriteRootFiles.
272//
273void MWriteFitsFile::AddContainer(MParContainer *cont, const char *tName,
274 Bool_t must)
275{
276 if (cont == NULL)
277 {
278 *fLog << warn << "Warning - container in method MWriteFitsFile::"
279 "AddContainer not defined... ignored" << endl;
280 return;
281 }
282
283 TString tableName;
284 if (tName == NULL || strlen(tName) == 0)
285 tableName = cont->IsA()->GetName();
286 else
287 tableName = tName;
288
289 TString cName = cont->IsA()->GetName();
290 if (fSubTables[tableName].insert( pair<TString, MFitsSubTable>
291 (cName, MFitsSubTable(cont, must))).second == false)
292 {
293 *fLog << warn << "Warning - Container '"<< cName.Data() <<
294 "' in table '" << tableName.Data() <<
295 "' already scheduled... ignored." << endl;
296 }
297}
298
299// --------------------------------------------------------------------------
300//
301// Tries to open the given output file.
302//
303Int_t MWriteFitsFile::PreProcess(MParList *pList)
304{
305 if (fRule.Length() != 0)
306 // the file is not yet open
307 return kTRUE;
308 //
309 // test whether file is now open or not
310 //
311 if (!IsFileOpen())
312 {
313 *fLog << err << dbginf << "Cannot open file '" << GetFileName() << "'" << endl;
314 return kFALSE;
315 }
316
317 *fLog << inf << "File '" << GetFileName() << "' open for writing." << endl;
318
319 //
320 // Get the containers (pointers) from the parameter list you want to write
321 //
322 if (!GetContainer(pList))
323 return kFALSE;
324
325 //
326 // write the container if it is already in changed state
327 //
328 return CheckAndWrite();
329
330}
331
332Int_t MWriteFitsFile::PostProcess()
333{
334 for (std::map<TString, std::ofits*>::iterator it=fFitsTables.begin(); it!= fFitsTables.end();it++)
335 {
336 it->second->close();
337 delete it->second;
338 it->second = NULL;
339 }
340
341 return kTRUE;
342}
343
344
345// --------------------------------------------------------------------------
346//
347// Opens all FITS files for all container, which were registered with the
348// AddContainer() - methods. The container is taken from the pList, if it
349// was registered just by its name.
350// Calling the AddContainer() after calling this method will not add new
351// containers to the list.
352//
353Bool_t MWriteFitsFile::GetContainer(MParList *pList)
354{
355 if (iTopFitsGroup == fTopFitsGroups.end())
356 // something went wrong while the top level group was created
357 return kFALSE;
358
359
360 // remove the extension from the filename
361 char fileNameNoExt[strlen(GetFileName()) + 1];
362 strcpy(fileNameNoExt, GetFileName());
363 char * pos = strrchr(fileNameNoExt, '.');
364 if (pos) *pos = 0;
365
366 // loop over all FITS tables which have to be created.
367 map<TString, map<TString, MFitsSubTable> >::iterator i_table =
368 fSubTables.begin();
369 while (i_table != fSubTables.end())
370 {
371
372 ofits* fitsTable = new ofits();
373 TString dol = fileNameNoExt;
374 //get rid of the ".root" extension in the file name (if any)
375 if (dol(dol.Length()-5, dol.Length()) == ".root")
376 {
377 dol = dol(0, dol.Length()-5);
378// *fLog << err << "New name: " << dol.Data() << endl;
379 }
380// else
381// *fLog << err << dol(dol.Length()-5, dol.Length()).Data() << endl;
382 dol += "_";
383 dol += i_table->first;
384 dol += ".fits";
385 fitsTable->open(dol.Data());
386 *fLog << inf << "Opening FITS file: " << dol.Data() << endl;
387 fFitsTables[i_table->first] = fitsTable;
388 fTableObjectCreated[i_table->first] = true;
389 fTableHeaderWritten[i_table->first] = false;
390
391 // loop over all containers, which define a sub-table of the current table
392 Int_t num = 0;
393 map<TString, MFitsSubTable>::iterator i_subTable = i_table->second.begin();
394 while (i_subTable != i_table->second.end())
395 {
396
397 MFitsSubTable & subTable = i_subTable->second;
398 if (subTable.GetContainer() == NULL)
399 {
400 // container address is not yet known
401 const char * cname = i_subTable->first.Data();
402 MParContainer *cont = (MParContainer*)pList->FindObject(cname);
403 if (!cont)
404 {
405 // no corresponding container is available in pList
406 if (subTable.MustHave())
407 {
408 *fLog << err << "Cannot find parameter container '" << cname << "'." << endl;
409 return kFALSE;
410 }
411
412 // we can ignore this container, delete it from the map
413 *fLog << inf2 << "Unnecessary parameter container '" << cname << "' not found..." << endl;
414 map<TString, MFitsSubTable>::iterator i_tmp = i_subTable;
415 i_subTable++;
416 i_table->second.erase(i_tmp);
417 continue;
418 }
419
420 // we have our container address and can use it
421 subTable.SetContainer(cont);
422 }
423
424 // initialize all columns of the sub-table, defined by the current container
425 TClass * cl = i_subTable->second.GetContainer()->IsA();
426 if (!InitColumns(i_table->first, TString(cl->GetName()) + ".", fitsTable,
427 i_subTable->second.GetContainer(), cl) )
428 return kFALSE;
429
430 char attrName[10];
431 sprintf(attrName, "CLNAME%d", num);
432 num++;
433
434 InitAttr(attrName,
435 "A",
436 (void*)i_subTable->first.Data(),
437 NULL,
438 "MARS container name",
439 fitsTable);
440
441 i_subTable++;
442 }
443
444 // in case not all sub-tables were removed, we can now create the FITS table
445 if (i_table->second.size() > 0)
446 {
447 // create the DOL of the table. It will be something like:
448 // fileNameNoExt_dataName.fits[dataName]
449 TString dol = fileNameNoExt;
450 dol += "_";
451 dol += i_table->first;
452 dol += ".fits";
453
454 if (fOpenOption == "RECREATE")
455 // remove the file
456 unlink(dol.Data());
457
458 dol += "[";
459 dol += i_table->first;
460 dol += "]";
461// *fLog << err << "Reiner would have opened fits file: " << dol.Data() << endl;
462 //exit(0);
463 //fitsTable->open(dol.Data());
464
465 // attach the new table to the top level group
466 //TODO make sure that this attach below is not needed...
467 //iTopFitsGroup->second.Attach(fitsTable);
468 }
469
470 i_table++;
471 }
472
473 return kTRUE;
474}
475void MWriteFitsFile::InitAttr(const char* attrName,
476 const char* dataType,
477 void* var,
478 const char* unit,
479 const char* comment,
480 std::ofits* outFile)
481{
482 if (outFile == NULL)
483 return;
484 string dts(dataType);
485 string ans(attrName);
486 string cs;
487 if (comment != NULL)
488 cs = string(comment);
489 else
490 cs = "";
491 ostringstream val;
492 if ((dts == "bool") || (dts == "Bool_t") || (dts == "L"))
493 {
494 outFile->SetBool(ans, ((bool*)(var))[0], cs);
495 return;
496 }
497 if ((dts == "char") || (dts == "Char_t") || (dts == "S"))
498 {
499 val << ((char*)(var))[0];
500 outFile->SetStr(ans, val.str(), cs);
501 return;
502 }
503 if ((dts == "unsigned char") || (dts == "UChar_t") || (dts == "B"))
504 {
505 val << ((unsigned char*)(var))[0];
506 outFile->SetStr(ans, val.str(), cs);
507 return;
508 }
509 if ((dts == "short") || (dts == "Short_t") || (dts == "I"))
510 {
511 val << ((short*)(var))[0];
512 outFile->SetStr(ans, val.str(), cs);
513 return;
514 }
515 if ((dts == "unsigned short") || (dts == "UShort_t") || (dts == "U"))
516 {
517 val << ((unsigned short*)(var))[0];
518 outFile->SetStr(ans, val.str(), cs);
519 return;
520 }
521 if ((dts == "int") || (dts == "Int_t") || (dts == "V"))
522 {
523 outFile->SetInt(ans, ((int*)(var))[0], cs);
524 return;
525 }
526 if ((dts == "unsigned int") || (dts == "UInt_t") || (dts == "V"))
527 {
528 outFile->SetInt(ans, ((unsigned int*)(var))[0], cs);
529 return;
530 }
531 if ((dts == "long long") || (dts == "Long64_t") || (dts == "K"))
532 {
533 outFile->SetInt(ans, ((long long*)(var))[0], cs);
534 return;
535 }
536 if ((dts == "unsigned long long") || (dts == "ULong64_t") || (dts == "W"))
537 {
538 val << ((unsigned long long*)(var))[0];
539 outFile->SetStr(ans, val.str(), cs);
540 return;
541 }
542 if ((dts == "float") || (dts == "TFloat_t") || (dts == "E"))
543 {
544 outFile->SetFloat(ans, ((float*)(var))[0], cs);
545 return;
546 }
547 if ((dts == "double") || (dts == "TDouble_t") || (dts == "D"))
548 {
549 outFile->SetFloat(ans, ((double*)(var))[0], cs);
550 return;
551 }
552 if ((dts == "char*") || (dts == "A"))
553 {
554 outFile->SetStr(ans, string((char*)(var)), cs);
555 return;
556 }
557 //trigger error
558 *fLog << err << "Format string not recognized while adding header entry " << ans << " with type " << dts << endl;
559}
560// --------------------------------------------------------------------------
561//
562// This method is called when new data should be written to the FITS table.
563// In general only one row is written. (There are exceptions for Arrays).
564// A new row to a FITS table is written if at least for one container
565// (one sub-table) the ReadyToSave - flag is set.
566//
567Bool_t MWriteFitsFile::CheckAndWrite()
568{
569// try {
570
571 // loop over all tables
572 map<TString, map<TString, MFitsSubTable> >::iterator i_table =
573 fSubTables.begin();
574 while (i_table != fSubTables.end())
575 {
576 // is this table open?
577 if (fFitsTables.find(i_table->first) != fFitsTables.end())
578 {
579
580 // loop over all sub-tables
581 map<TString, MFitsSubTable>::iterator i_subTable = i_table->second.begin();
582 while (i_subTable != i_table->second.end())
583 {
584
585 if (i_subTable->second.GetContainer()->IsReadyToSave())
586 {
587 // first write the TClonesArray and set the size of the arrays
588 list<MArrayHelperBase*> & clHelper = fClHelper[i_table->first];
589 list<MArrayHelperBase*>::iterator i_clHelper = clHelper.begin();
590 while (i_clHelper != clHelper.end())
591 {
592 // write all entries in the TClonesArray in its FITS table
593 //removed this write because I do it elsewhere. is that alright ?
594 // (*i_clHelper)->Write();
595 i_clHelper++;
596 }
597
598 // write one line to this table
599 writeOneRow(i_table->first);
600// fFitsTables[i_table->first].Write();
601 break;
602 }
603 i_subTable++;
604 }
605 }
606
607 i_table++;
608 }
609// }
610// catch (exception &e)
611 // {
612 // *fLog << err << e.what() << endl;
613 // return kFALSE;
614 // }
615
616 return kTRUE;
617}
618
619string MWriteFitsFile::Trim(const string &str)
620{
621 // Trim Both leading and trailing spaces
622 const size_t first = str.find_first_not_of(' '); // Find the first character position after excluding leading blank spaces
623 const size_t last = str.find_last_not_of(' '); // Find the first character position from reverse af
624
625 // if all spaces or empty return an empty string
626 if (string::npos==first || string::npos==last)
627 return string();
628
629 return str.substr(first, last-first+1);
630}
631
632Bool_t MWriteFitsFile::VetoColumn(const std::string& colName)
633{
634 for (std::vector<string>::iterator it=fVetoedColumns.begin(); it != fVetoedColumns.end(); it++)
635 if (*it == colName)
636 {
637 *fLog << warn << "Warning: column " << colName << " is being vetoed twice" << endl;
638 return kFALSE;
639 }
640 fVetoedColumns.push_back(colName);
641 return kTRUE;
642}
643
644Bool_t MWriteFitsFile::SetBytesPerSample(const std::string& colName, uint32_t numBytes)
645{
646 for (map<string, uint32_t>::iterator it=fBytesPerSamples.begin(); it!=fBytesPerSamples.end(); it++)
647 if (it->first == colName)
648 {
649 *fLog << warn << "Warning: column " << colName << " bytes per sample is being redefined twice" << endl;
650 return kFALSE;
651 }
652 if (numBytes != 1 && numBytes != 2 && numBytes != 4 && numBytes != 8)
653 {
654 *fLog << warn << "Only powers of two are allowed for types re-mapping." << endl;
655 return kFALSE;
656 }
657 fBytesPerSamples[colName] = numBytes;
658 return kTRUE;
659}
660
661// --------------------------------------------------------------------------
662//
663// Initialize all columns in "fitsTable" of the class "classDef". The data
664// of this class are stored in a buffer, beginning at "baseAdr".
665//
666Bool_t MWriteFitsFile::InitColumns(const TString & tableName,
667 const TString & parentVarName,
668 ofits* fitsTable,
669 void * baseAdr,
670 TClass * classDef)
671{
672 // get all data members of the class
673 TList * dataMembers = classDef->GetListOfDataMembers();
674 TIter next(dataMembers);
675 TDataMember * dataMember;
676
677 // loop over all data members
678 while ((dataMember = (TDataMember*)next()) != NULL)
679 {
680 if (!dataMember->IsPersistent())
681 // don't store this variable
682 continue;
683
684 if (dataMember->Property() & ( G__BIT_ISENUM | G__BIT_ISSTATIC))
685 // we cannot store this
686 continue;
687
688 if (strcmp(dataMember->GetTrueTypeName(), "TClass*") == 0)
689 // we don't want to store this.
690 continue;
691
692 // is it an array of more than 1 dimension?
693 if (dataMember->GetArrayDim() > 1)
694 {
695 *fLog << err << "Two and more dimensional arrays of member variables"
696 " are not supported." << endl;
697 *fLog << "See variable " << dataMember->GetName() <<
698 " in container " << classDef->GetName() << endl;
699 return kFALSE;
700 }
701
702
703 // replace � by **2 in the comment field
704 string comment(dataMember->GetTitle());
705 string::size_type pos1, pos2;
706 if ((pos1 = comment.find(178)) != string::npos)
707 comment.replace(pos1, 1, "**2");
708
709 // get the optional mapping to the fits column names
710 string fitsOptions="";
711 string unit="unit";
712
713 pos1 = comment.find("[fits: ");
714 pos2 = comment.find(']');
715 std::map<string, string> fitsTokens;
716 if (pos1 != string::npos && pos2 != string::npos)
717 {
718 fitsOptions=comment.substr(pos1+7, (pos2-pos1)-7);
719 //do we have more than one option ?
720 string::size_type pos3= fitsOptions.find_first_of(';');
721 string::size_type pos4 = string::npos;
722 string key="";
723 string value="";
724 string keyValue="";
725 while (pos3 != string::npos)
726 {//we have at least 2 options left
727 // *fLog << err << "fitsOptions: " << fitsOptions << endl;
728 keyValue = fitsOptions.substr(0,pos3);
729 // *fLog << err << "keyValue: " << keyValue << endl;
730 pos4 = keyValue.find('=');
731 if (pos4 == string::npos)
732 {
733 *fLog << err << "Error while parsing comment \"" << comment << "\" from variable " << parentVarName + dataMember->GetName() << endl;
734 return kFALSE;
735 }
736 key = Trim(keyValue.substr(0, pos4));
737 value = Trim(keyValue.substr(pos4+1, pos3));
738 fitsTokens[key] = value;
739 // *fLog << err << "key: " << key << " value: " << value << endl;
740 fitsOptions = fitsOptions.substr(pos3+1, fitsOptions.size());
741 pos3 = fitsOptions.find_first_of(';');
742 }
743// *fLog << err << "fitsOptions: " << fitsOptions << endl;
744 keyValue = fitsOptions;
745 pos4 = keyValue.find('=');
746 if (pos4 == string::npos)
747 {
748 *fLog << err << "Error while parsing comment \"" << comment << "\" from variable " << parentVarName + dataMember->GetName() << endl;
749 return kFALSE;
750 }
751 key = Trim(keyValue.substr(0, pos4));
752 value = Trim(keyValue.substr(pos4+1, pos3));
753 fitsTokens[key] = value;
754// *fLog << err << "key: " << key << " value: " << value << endl;
755 }
756
757 TString colName = parentVarName + dataMember->GetName();
758
759 if (fitsTokens.find("unit") != fitsTokens.end())
760 unit = fitsTokens["unit"];
761 if (fitsTokens.find("name") != fitsTokens.end())
762 colName = fitsTokens["name"];
763
764 //check for column veto
765 if (std::find(fVetoedColumns.begin(), fVetoedColumns.end(), colName.Data())!=fVetoedColumns.end())
766 {
767 *fLog << inf << "Vetoing column " << colName.Data() << endl;
768 continue;
769 }
770
771 // set the array size
772 TString dataType = dataMember->GetTrueTypeName();
773
774 uint32_t dataLength = 0;
775 switch (dataMember->GetArrayDim())
776 {
777 case 0:
778 dataLength = 1;
779 break;
780 case 1:
781 dataLength = dataMember->GetMaxIndex(0);
782 break;
783 default:
784 *fLog << err << "n-dimensional array should have been discarded already " << colName.Data();
785 break;
786 };
787// if (dataLength == 0)
788// continue;
789
790 if (dataMember->Property() & G__BIT_ISCLASS)
791 {
792 // special treatment for classes
793 uint32_t arraySize = 0;
794 TString typeName;
795
796 if (strncmp(dataMember->GetTrueTypeName(), "MArray", 6) == 0)
797 {
798// MArrayHelperBase * clHelper;
799 if (strcmp(dataMember->GetTrueTypeName(), "MArrayS*") == 0)
800 {
801 typeName = "UShort_t";
802 arraySize = (*((MArrayS**)((char*)baseAdr + dataMember->GetOffset())))->GetSize();
803 InitSingleColumn(tableName,
804 arraySize,
805 typeName.Data(),
806 (char*)(*((MArrayS**)((char*)baseAdr + dataMember->GetOffset())))->GetArray(),
807 colName.Data(),
808 unit,
809 comment);
810 }
811 else
812 {
813 if (strcmp(dataMember->GetTrueTypeName(), "MArrayB*") == 0)
814 {
815 typeName = "UChar_t";
816 arraySize = (*((MArrayB**)((char*)baseAdr + dataMember->GetOffset())))->GetSize();
817 InitSingleColumn(tableName,
818 arraySize,
819 typeName.Data(),
820 (char*)(*((MArrayB**)((char*)baseAdr + dataMember->GetOffset())))->GetArray(),
821 colName.Data(),
822 unit,
823 comment);
824 }
825 else
826 {
827 *fLog << err << dataMember->GetTrueTypeName() << " not yet implemented." << endl;
828 return kFALSE;
829 }
830 }
831
832 continue;
833 }
834 else if (strcmp(dataMember->GetTrueTypeName(), "TClonesArray") == 0)
835 {
836 *fLog << warn << "I'm skipping the TClonesArray for now" << endl;
837 continue;
838 // each TClonesArray requires a FITS table by itself.
839 MClonesArrayHelper * clHelper;
840
841 TClonesArray * cloneArray = (TClonesArray*)((char*)baseAdr + dataMember->GetOffset());
842 Bool_t status;
843 clHelper = new MClonesArrayHelper(cloneArray, fLog, status);
844 if (!status) return status;
845
846 fClHelper[tableName].push_back(clHelper);
847
848 // add one column in the parent table of the TClonesArray to store the
849 // number of entries in the TClonesArray.
850 InitSingleColumn(tableName,
851 1,
852 "UInt_t",
853 clHelper->GetArraySizePtr(),
854 colName.Data(),
855 unit,
856 comment);
857
858 // initialize the columns of the new FITS table, which will store the
859 // data entries of the TClonesArray
860 if (InitColumns(TString("noName"),
861 colName + "_",
862 fitsTable,
863 clHelper->GetDataBuffer(),
864 cloneArray->GetClass())
865 == kFALSE)
866 return kFALSE;
867
868 // the columns are initialized. We can create the FITS table
869 if (clHelper->OpenFitsTable(GetFileName(), dataMember->GetName(),
870 fOpenOption, fLog) == kFALSE)
871 return kFALSE;
872 }
873
874 else
875 {
876 // the current container has a variable of an other class. We create
877 // also columns of this other class in the same table
878 TClass * newClassDef = TClass::GetClass(dataMember->GetTrueTypeName(), kFALSE, kTRUE);
879 if (newClassDef)
880 {
881 if (InitColumns(tableName, colName + ".", fitsTable, (char*)baseAdr + dataMember->GetOffset(),
882 newClassDef) == kFALSE)
883 return kFALSE;
884 }
885 else
886 *fLog << warn << "Cannot write data of class " << colName + "." + dataMember->GetTrueTypeName() << endl;
887 }
888 continue;
889 }
890
891 InitSingleColumn(tableName,
892 dataLength,
893 dataType.Data(),
894 (char*)baseAdr + dataMember->GetOffset(),
895 colName.Data(),
896 unit,
897 comment);
898
899 }
900 return kTRUE;
901}
902void MWriteFitsFile::InitSingleColumn(const TString& tableName,
903 uint32_t count,
904 const string& typeName,
905 void* dataPointer,
906 const string& columnName,
907 const string& unit,
908 const string& comment)
909{
910 if (!fTableObjectCreated[tableName])
911 {
912 *fLog << err << "ERROR: Cannot init column " << columnName << " before assigning object: " << tableName << endl;
913 return;
914 }
915 int typeFound = 0;
916 char typeChar = '0';
917 if ((typeName == "bool") || (typeName == "Bool_t") || (typeName == "L"))
918 {
919 typeChar = 'L';
920 typeFound++;
921 }
922 if ((typeName == "char") || (typeName == "Char_t") || (typeName == "S"))
923 {
924 typeChar = 'A';
925 typeFound++;
926 }
927 if ((typeName == "unsigned char") || (typeName == "UChar_t") || (typeName == "B"))
928 {
929 typeChar = 'A';
930// *fLog << warn << "Converting unsigned char to char in fits file" << endl;
931 typeFound++;
932 }
933 if ((typeName == "short") || (typeName == "Short_t") || (typeName == "I"))
934 {
935 typeChar = 'I';
936 typeFound++;
937 }
938 if ((typeName == "unsigned short") || (typeName == "UShort_t") || (typeName == "U"))
939 {
940 typeChar = 'I';
941// *fLog << warn << "Converting unsigned short to short in fits file" << endl;
942 typeFound++;
943 }
944 if ((typeName == "int") || (typeName == "Int_t") || (typeName == "V"))
945 {
946 typeChar = 'J';
947 typeFound++;
948 }
949 if ((typeName == "unsigned int") || (typeName == "UInt_t") || (typeName == "V"))
950 {
951 typeChar = 'J';
952// *fLog << warn << "Converting unsigned int to int in fits file" << endl;
953 typeFound++;
954 }
955 if ((typeName == "long long") || (typeName == "Long64_t") || (typeName == "K"))
956 {
957 typeChar = 'K';
958 typeFound++;
959 }
960 if ((typeName == "unsigned long long") || (typeName == "ULong64_t") || (typeName == "W"))
961 {
962 typeChar = 'K';
963// *fLog << warn << "Converting unsigned long to long in fits file" << endl;
964 typeFound++;
965 }
966 if ((typeName == "float") || (typeName=="TFloat_t") || (typeName == "E"))
967 {
968 typeChar = 'E';
969 typeFound++;
970 }
971 if ((typeName == "double") || (typeName == "TDouble_t") || (typeName == "D"))
972 {
973 typeChar = 'D';
974 typeFound++;
975 }
976// if ((typeName == "char*") || (typeName == "A"))
977// {
978// typeFound++;
979// }
980 if (typeFound != 1)
981 {
982 *fLog << err << "We have a problem with the data type: " << typeName << endl;
983 return;
984 }
985 uint32_t colWidth = 0;
986 switch (typeChar)
987 {
988 case 'L': colWidth = 1*count; break;
989 case 'A': colWidth = 1*count; break;
990 case 'B': colWidth = 1*count; break;
991 case 'I': colWidth = 2*count; break;
992 case 'J': colWidth = 4*count; break;
993 case 'K': colWidth = 8*count; break;
994 case 'E': colWidth = 4*count; break;
995 case 'D': colWidth = 8*count; break;
996 default:
997 *fLog << err << "ERROR: typeChar could not be resolved to an actual type" << endl;
998 };
999 //check for type remapping here
1000 if (fBytesPerSamples.find(columnName) != fBytesPerSamples.end())
1001 {
1002 if (typeChar != 'A')
1003 {
1004 *fLog << err << "Attempt to remap type " << typeChar << " to " << fBytesPerSamples[columnName] << " is only allowed on bytes (variable name: " << columnName << "). Ignoring column" << endl;
1005 return;
1006 }
1007 uint32_t bytesPerSample = fBytesPerSamples.find(columnName)->second;
1008 if (colWidth%bytesPerSample != 0)
1009 {
1010 *fLog << err << "Type remapping cannot be done using " << bytesPerSample << " bytes per sample on an array of char of size " << colWidth << ". Ignoring column " << columnName << endl;
1011 return;
1012 }
1013 switch (bytesPerSample)
1014 {
1015 case 1: count = count/1; typeChar = 'A'; break;
1016 case 2: count = count/2; typeChar = 'I'; break;
1017 case 4: count = count/4; typeChar = 'J'; break;
1018 case 8: count = count/8; typeChar = 'K'; break;
1019 default:
1020 *fLog << err << "ERROR: num bytes per sample = " << bytesPerSample << " should have been forbidden already" << endl;
1021 }
1022
1023 }
1024
1025 fDataPointers[tableName].push_back(dataPointer);
1026 fTypeChars[tableName].push_back(typeChar);
1027 fColSizes[tableName].push_back(count);
1028 fColWidth[tableName].push_back(colWidth);
1029
1030 //FIXME ofits does not allow for much liberty regarding the size of the column names.
1031 //Truncating them badly here, will probably cause other problems -> Modify ofits.h instead
1032 string truncatedName=columnName.substr((columnName.size()>40)?columnName.size()-40:0,columnName.size());
1033 string truncatedComment = comment.substr((comment.size()>10)?comment.size()-10:0,comment.size());
1034// *fLog << warn << "In table " << tableName << " Adding column |" << truncatedName << "| |" << truncatedComment << "| |" << count << "| |" << typeChar;
1035// *fLog << warn << "| Real: "<< columnName << " comment: " << comment << endl;
1036 fFitsTables[tableName]->AddColumn(count, typeChar, truncatedName, unit, truncatedComment);
1037}
1038
1039void MWriteFitsFile::writeOneRow(const TString& tableName)
1040{
1041 if (!fTableHeaderWritten[tableName])
1042 {
1043 fFitsTables[tableName]->WriteTableHeader(tableName.Data());
1044 fTableHeaderWritten[tableName] = true;
1045 }
1046 if (!fTableObjectCreated[tableName])
1047 {
1048 *fLog << err << "This is not good. Please initialize the fits table before writing to it: " << tableName << endl;
1049 return;
1050 }
1051 //first calculate the size of one row
1052 uint32_t rowWidth = 0;
1053 for (uint32_t i=0;i<fTypeChars[tableName].size();i++)
1054 rowWidth += fColWidth[tableName][i];
1055 unsigned char* tempBuffer = new unsigned char[rowWidth];
1056 //then copy the data to be written contiguously
1057 uint32_t bytesCounter = 0;
1058 for (uint32_t i=0;i<fDataPointers[tableName].size();i++)
1059 {
1060 memcpy(&tempBuffer[bytesCounter], fDataPointers[tableName][i], fColWidth[tableName][i]);
1061 bytesCounter+=fColWidth[tableName][i];
1062 }
1063 if (fFitsTables[tableName]->WriteRow(tempBuffer, bytesCounter) == false)
1064 *fLog << err << "Error while writing to FITS table " << tableName << endl;
1065 else
1066 fFitsTables[tableName]->FlushNumRows();
1067
1068 delete[] tempBuffer;
1069}
1070Bool_t MWriteFitsFile::ReInit(MParList *pList)
1071{
1072 if (fRule.Length() == 0)
1073 // there is not rule defined. We keep the old file
1074 return MWriteFile::ReInit(pList);
1075
1076 MRead *read = (MRead*)pList->FindTask("MRead");
1077 if (!read)
1078 {
1079 *fLog << err;
1080 *fLog << "ERROR: No Task 'MRead' found in the tasklist. This task is" << endl;
1081 *fLog << " necessary to get the filename. Without a read-filename" << endl;
1082 *fLog << " no output-filename can be created... abort." << endl;
1083 *fLog << endl;
1084 return kFALSE;
1085 }
1086
1087
1088 // close the current files
1089 CloseTopLevelGroup();
1090 for (std::map<TString,std::ofits*>::iterator it=fFitsTables.begin(); it!=fFitsTables.end(); it++)
1091 {
1092 (it->second)->close();
1093 delete it->second;
1094 }
1095 fFitsTables.clear();
1096 fDataPointers.clear();
1097 fTypeChars.clear();
1098 fColSizes.clear();
1099 fColWidth.clear();
1100 fTableObjectCreated.clear();
1101 fTableHeaderWritten.clear();
1102 DeleteArrayHelper();
1103 fClHelper.clear();
1104
1105 // get new filename
1106 const TString readFileName = read->GetFullFileName();
1107 const TString newname = MWriteRootFile::SubstituteName(fRule, readFileName)+".fits";
1108
1109 // create new files
1110 OpenTopLevelGroup(newname.Data());
1111 if (!IsFileOpen())
1112 return kFALSE;
1113
1114 if (GetContainer(pList) == kFALSE)
1115 return kFALSE;
1116
1117 // do, what has to be done in ReInit.
1118 return MWriteFile::ReInit(pList);
1119
1120}
1121
1122void MWriteFitsFile::DeleteArrayHelper()
1123{
1124 map<TString, list<MArrayHelperBase *> >::iterator i_helper1 = fClHelper.begin();
1125 while (i_helper1 != fClHelper.end())
1126 {
1127 list<MArrayHelperBase *>::iterator i_helper2 = i_helper1->second.begin();
1128 while(i_helper2 != i_helper1->second.end())
1129 {
1130 delete *i_helper2;
1131
1132 i_helper2++;
1133 }
1134
1135 i_helper1++;
1136 }
1137}
Note: See TracBrowser for help on using the repository browser.