From 03b9fe1e711f829c62c4f0bf661122e8a935457d Mon Sep 17 00:00:00 2001 From: nemu Date: Tue, 23 Aug 2011 17:22:29 +0000 Subject: [PATCH] added minimal NeXus IDF 2 support. --- ChangeLog | 1 + doc/examples/data/emu00005989_v2.nxs | Bin 0 -> 675651 bytes doc/examples/test-histo-NeXus2.msr | 74 + src/any2many.cpp | 21 +- src/classes/PRunDataHandler.cpp | 573 ++- src/external/nexus/Makefile.am | 2 +- src/external/nexus/PNeXus.cpp | 5708 +++++++++++++++++++------- src/external/nexus/PNeXus.h | 773 ++-- src/include/PMusr.h | 1 + src/include/PRunDataHandler.h | 1 + src/tests/nexus/Makefile | 2 +- src/tests/nexus/nexus_read_test.cpp | 44 +- 12 files changed, 5355 insertions(+), 1845 deletions(-) create mode 100644 doc/examples/data/emu00005989_v2.nxs create mode 100644 doc/examples/test-histo-NeXus2.msr diff --git a/ChangeLog b/ChangeLog index 5490fea9..57a00291 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,6 +6,7 @@ changes since 0.9.0 =================================== +NEW added minimal NeXus IDF 2 support. NEW Added the online documentation to the repository. It can be found under "doc/html". NEW added chisq per run block information (all fit types). The same output rules as for the expected chisq are in place. NEW calculate expected chisq (Pearson's chisq) for single histogram fits. It always will send this information to the stdout diff --git a/doc/examples/data/emu00005989_v2.nxs b/doc/examples/data/emu00005989_v2.nxs new file mode 100644 index 0000000000000000000000000000000000000000..91f812aaf179d9f5850d523147b96c1268aee186 GIT binary patch literal 675651 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

dMcGtMk0H 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, e.g. 111-222"; cout << endl << " -c : "; cout << endl << " : input data file format. Supported formats are:"; - cout << endl << " PSI-BIN, ROOT (LEM), MUD, NeXus, PSI-MDU, WKM"; + cout << endl << " PSI-BIN, ROOT (LEM), MUD, NeXus1, NeXus2, PSI-MDU, WKM"; cout << endl << " : ouput data file format. Supported formats are:"; - cout << endl << " PSI-BIN, ROOT (LEM), MUD, NeXus-HDF4, NeXus-HDF5, NeXus-XML, WKM, ASCII"; + cout << endl << " PSI-BIN, ROOT (LEM), MUD, NeXus1-HDF4, NeXus1-HDF5, NeXus1-XML,"; + cout << endl << " NeXus2-HDF4, NeXus2-HDF5, NeXus2-XML, WKM, ASCII"; cout << endl << " -p : where is the output path for the"; cout << endl << " converted files. If nothing is given, the current directory"; cout << endl << " will be used, unless the option '-s' is used."; @@ -149,9 +150,12 @@ int main(int argc, char *argv[]) outputFormat.push_back("psi-bin"); outputFormat.push_back("root"); outputFormat.push_back("mud"); - outputFormat.push_back("nexus-hdf4"); - outputFormat.push_back("nexus-hdf5"); - outputFormat.push_back("nexus-xml"); + outputFormat.push_back("nexus1-hdf4"); + outputFormat.push_back("nexus1-hdf5"); + outputFormat.push_back("nexus1-xml"); + outputFormat.push_back("nexus2-hdf4"); + outputFormat.push_back("nexus2-hdf5"); + outputFormat.push_back("nexus2-xml"); outputFormat.push_back("wkm"); outputFormat.push_back("ascii"); @@ -159,6 +163,7 @@ int main(int argc, char *argv[]) info.useStandardOutput = false; info.rebin = 1; info.compressionTag = 0; // no compression as default + info.idf = 0; // undefined // call any2many without arguments if (argc == 1) { @@ -404,6 +409,12 @@ int main(int argc, char *argv[]) show_syntax = true; } + // check if the output format is nexus + if (info.outFormat.Contains("nexus1", TString::kIgnoreCase)) + info.idf = 1; + if (info.outFormat.Contains("nexus2", TString::kIgnoreCase)) + info.idf = 2; + if (show_syntax) { info.runList.clear(); any2many_syntax(); diff --git a/src/classes/PRunDataHandler.cpp b/src/classes/PRunDataHandler.cpp index 79420d26..66fbb7a1 100644 --- a/src/classes/PRunDataHandler.cpp +++ b/src/classes/PRunDataHandler.cpp @@ -1346,7 +1346,9 @@ Bool_t PRunDataHandler::ReadNexusFile() PDoubleVector histoData; PRawRunData runData; TString str; + string sstr; Double_t dval; + bool ok; PNeXus *nxs_file = new PNeXus(fRunPathName.Data()); if (!nxs_file->IsValid()) { @@ -1355,81 +1357,265 @@ Bool_t PRunDataHandler::ReadNexusFile() return false; } - // get header information - - // get/set run title - str = TString(nxs_file->GetRunTitle()); - runData.SetRunTitle(str); - - // get/set run number - runData.SetRunNumber(nxs_file->GetRunNumber()); - - // get/set temperature - runData.SetTemperature(0, nxs_file->GetSampleTemperature(), 0.0); - - // get/set field - dval = nxs_file->GetMagneticField(); - str = TString(nxs_file->GetMagneticFieldUnits()); - // since field has to be given in Gauss, check the units - Double_t factor=1.0; - if (!str.CompareTo("gauss", TString::kIgnoreCase)) - factor=1.0; - else if (!str.CompareTo("tesla", TString::kIgnoreCase)) - factor=1.0e4; - else - factor=1.0; - runData.SetField(factor*dval); - - // get/set implantation energy - runData.SetEnergy(PMUSR_UNDEFINED); - - // get/set moderator HV - runData.SetTransport(PMUSR_UNDEFINED); - - // get/set RA HV's (LEM specific) - for (UInt_t i=0; i<4; i++) - runData.SetRingAnode(i, PMUSR_UNDEFINED); - - // get/set setup - runData.SetSetup(nxs_file->GetRunNotes()); - - // get/set sample - runData.SetSample(nxs_file->GetSampleName()); - - // get/set orientation - runData.SetOrientation("??"); - - // get/set time resolution (ns) - runData.SetTimeResolution(nxs_file->GetTimeResolution()); - - // get/set start/stop time - runData.SetStartTime(nxs_file->GetStartTime()); - runData.SetStartDate(nxs_file->GetStartDate()); - runData.SetStopTime(nxs_file->GetStopTime()); - runData.SetStopDate(nxs_file->GetStopDate()); - - // get t0, firstGoodBin, lastGoodBin, data - UInt_t t0=nxs_file->GetT0(); - PIntPair goodDataBin; - goodDataBin.first = nxs_file->GetFirstGoodBin(); - goodDataBin.second = nxs_file->GetLastGoodBin(); - PDoubleVector data; - for (Int_t i=0; iGetNoHistos(); i++) { - runData.AppendT0(t0); - runData.AppendGoodDataBin(goodDataBin); - data.clear(); - for (UInt_t j=0; jGetHisto(i)->size(); j++) { - data.push_back((Double_t)nxs_file->GetHisto(i)->at(j)); + if (nxs_file->GetIdfVersion() == 1) { + if (!nxs_file->IsValid()) { + cout << endl << "**ERROR** invalid NeXus IDF 2 version file found." << endl; + return false; } - runData.AppendDataBin(data); + + // get header information + + // get/set run title + str = TString(nxs_file->GetEntryIdf1()->GetTitle()); + runData.SetRunTitle(str); + + // get/set run number + runData.SetRunNumber(nxs_file->GetEntryIdf1()->GetRunNumber()); + + // get/set temperature + dval = nxs_file->GetEntryIdf1()->GetSample()->GetPhysPropValue("temperature", ok); + if (ok) + runData.SetTemperature(0, dval, 0.0); + + // get/set field + dval = nxs_file->GetEntryIdf1()->GetSample()->GetPhysPropValue("magnetic_field", ok); + nxs_file->GetEntryIdf1()->GetSample()->GetPhysPropUnit("magnetic_field", sstr, ok); + str = sstr; + // since field has to be given in Gauss, check the units + Double_t factor=1.0; + if (!str.CompareTo("gauss", TString::kIgnoreCase)) + factor=1.0; + else if (!str.CompareTo("tesla", TString::kIgnoreCase)) + factor=1.0e4; + else + factor=1.0; + runData.SetField(factor*dval); + + // get/set implantation energy + runData.SetEnergy(PMUSR_UNDEFINED); + + // get/set moderator HV + runData.SetTransport(PMUSR_UNDEFINED); + + // get/set RA HV's (LEM specific) + for (UInt_t i=0; i<4; i++) + runData.SetRingAnode(i, PMUSR_UNDEFINED); + + // get/set setup + runData.SetSetup(nxs_file->GetEntryIdf1()->GetNotes()); + + // get/set sample + runData.SetSample(nxs_file->GetEntryIdf1()->GetSample()->GetName()); + + // get/set orientation + runData.SetOrientation("??"); + + // get/set time resolution (ns) + runData.SetTimeResolution(nxs_file->GetEntryIdf1()->GetData()->GetTimeResolution("ns")); + + // get/set start/stop time + sstr = nxs_file->GetEntryIdf1()->GetStartTime(); + str = sstr; + TString date, time; + SplitTimeDate(str, time, date, ok); + if (ok) { + runData.SetStartTime(time); + runData.SetStartDate(date); + } + + sstr = nxs_file->GetEntryIdf1()->GetStopTime(); + str = sstr; + SplitTimeDate(str, time, date, ok); + if (ok) { + runData.SetStopTime(time); + runData.SetStopDate(date); + } + + // get/set t0, firstGoodBin, lastGoodBin + vector *t0 = nxs_file->GetEntryIdf1()->GetData()->GetT0s(); + vector *fgb = nxs_file->GetEntryIdf1()->GetData()->GetFirstGoodBins(); + vector *lgb = nxs_file->GetEntryIdf1()->GetData()->GetLastGoodBins(); + PIntPair goodDataBin; + for (UInt_t i=0; iGetEntryIdf1()->GetData()->GetNoOfHistos(); i++) { + if (isize()) { + runData.AppendT0(t0->at(i)); + } else { + runData.AppendT0(t0->at(0)); + } + if (isize()) { + goodDataBin.first = fgb->at(i); + goodDataBin.second = lgb->at(i); + } else { + goodDataBin.first = fgb->at(0); + goodDataBin.second = lgb->at(0); + } + runData.AppendGoodDataBin(goodDataBin); + } + + // get/set data + vector *pdata; + PDoubleVector data; + for (UInt_t i=0; iGetEntryIdf1()->GetData()->GetNoOfHistos(); i++) { + pdata = nxs_file->GetEntryIdf1()->GetData()->GetHisto(i); + for (UInt_t j=0; jsize(); j++) + data.push_back(pdata->at(j)); + runData.AppendDataBin(data); + data.clear(); + } + + // keep run name from the msr-file + runData.SetRunName(fRunName); + + // keep the information + fData.push_back(runData); + } else if (nxs_file->GetIdfVersion() == 2) { + if (!nxs_file->IsValid()) { + cout << endl << "**ERROR** invalid NeXus IDF 2 version file found." << endl; + return false; + } + // get header information + + // get/set run title + str = TString(nxs_file->GetEntryIdf2()->GetTitle()); + runData.SetRunTitle(str); + + // get/set run number + runData.SetRunNumber(nxs_file->GetEntryIdf2()->GetRunNumber()); + + // get/set temperature + dval = nxs_file->GetEntryIdf2()->GetSample()->GetPhysPropValue("temperature", ok); + if (ok) + runData.SetTemperature(0, dval, 0.0); + + // get/set field + dval = nxs_file->GetEntryIdf2()->GetSample()->GetPhysPropValue("magnetic_field", ok); + nxs_file->GetEntryIdf2()->GetSample()->GetPhysPropUnit("magnetic_field", sstr, ok); + str = sstr; + // since field has to be given in Gauss, check the units + Double_t factor=1.0; + if (!str.CompareTo("gauss", TString::kIgnoreCase)) + factor=1.0; + else if (!str.CompareTo("tesla", TString::kIgnoreCase)) + factor=1.0e4; + else + factor=1.0; + runData.SetField(factor*dval); + + // get/set implantation energy + runData.SetEnergy(PMUSR_UNDEFINED); + + // get/set moderator HV + runData.SetTransport(PMUSR_UNDEFINED); + + // get/set RA HV's (LEM specific) + for (UInt_t i=0; i<4; i++) + runData.SetRingAnode(i, PMUSR_UNDEFINED); + + // get/set setup take NXsample/temperature_1_env and NXsample/magnetic_field_1_env + sstr = nxs_file->GetEntryIdf2()->GetSample()->GetEnvironmentTemp() + string("/"); + sstr += nxs_file->GetEntryIdf2()->GetSample()->GetEnvironmentField(); + str = sstr; + runData.SetSetup(str); + + // get/set sample + runData.SetSample(nxs_file->GetEntryIdf2()->GetSample()->GetName()); + + // get/set orientation + runData.SetOrientation("??"); + + // get/set time resolution (ns) + runData.SetTimeResolution(nxs_file->GetEntryIdf2()->GetInstrument()->GetDetector()->GetTimeResolution("ns")); + + // get/set start/stop time + sstr = nxs_file->GetEntryIdf2()->GetStartTime(); + str = sstr; + TString date, time; + SplitTimeDate(str, time, date, ok); + if (ok) { + runData.SetStartTime(time); + runData.SetStartDate(date); + } + + sstr = nxs_file->GetEntryIdf2()->GetStopTime(); + str = sstr; + SplitTimeDate(str, time, date, ok); + if (ok) { + runData.SetStopTime(time); + runData.SetStopDate(date); + } + + // get/set t0, firstGoodBin, lastGoodBin + int *t0 = nxs_file->GetEntryIdf2()->GetInstrument()->GetDetector()->GetT0s(); + int *fgb = nxs_file->GetEntryIdf2()->GetInstrument()->GetDetector()->GetFirstGoodBins(); + int *lgb = nxs_file->GetEntryIdf2()->GetInstrument()->GetDetector()->GetLastGoodBins(); + PIntPair goodDataBin; + + if (nxs_file->GetEntryIdf2()->GetInstrument()->GetDetector()->GetT0Tag() == 2) { // t0, fgb, lgb: [][] + for (int i=0; iGetEntryIdf2()->GetInstrument()->GetDetector()->GetNoOfPeriods(); i++) { + for (int j=0; jGetEntryIdf2()->GetInstrument()->GetDetector()->GetNoOfSpectra(); j++) { + if (fgb && lgb) { + goodDataBin.first = *(fgb+i*nxs_file->GetEntryIdf2()->GetInstrument()->GetDetector()->GetNoOfSpectra()+j); + goodDataBin.second = *(lgb+i*nxs_file->GetEntryIdf2()->GetInstrument()->GetDetector()->GetNoOfSpectra()+j); + } + runData.AppendT0(*(t0+i*nxs_file->GetEntryIdf2()->GetInstrument()->GetDetector()->GetNoOfSpectra()+j)); + if (fgb && lgb) { + runData.AppendGoodDataBin(goodDataBin); + } + } + } + } else { // t0, fgb, lgb: single numbers + if (fgb && lgb) { + goodDataBin.first = *fgb; + goodDataBin.second = *lgb; + } + for (int i=0; iGetEntryIdf2()->GetInstrument()->GetDetector()->GetNoOfPeriods(); i++) { + for (int j=0; jGetEntryIdf2()->GetInstrument()->GetDetector()->GetNoOfSpectra(); j++) { + runData.AppendT0(*t0); + if (fgb && lgb) + runData.AppendGoodDataBin(goodDataBin); + } + } + } + + // get/set data + PDoubleVector data; + int *histos = nxs_file->GetEntryIdf2()->GetInstrument()->GetDetector()->GetHistos(); + if (nxs_file->GetEntryIdf2()->GetInstrument()->GetDetector()->GetNoOfPeriods() > 0) { // counts[][][] + for (int i=0; iGetEntryIdf2()->GetInstrument()->GetDetector()->GetNoOfPeriods(); i++) { + for (int j=0; jGetEntryIdf2()->GetInstrument()->GetDetector()->GetNoOfSpectra(); j++) { + for (int k=0; kGetEntryIdf2()->GetInstrument()->GetDetector()->GetNoOfBins(); k++) { + data.push_back(*(histos+i*nxs_file->GetEntryIdf2()->GetInstrument()->GetDetector()->GetNoOfSpectra()+j*nxs_file->GetEntryIdf2()->GetInstrument()->GetDetector()->GetNoOfBins()+k)); + } + runData.AppendDataBin(data); + data.clear(); + } + } + } else { + if (nxs_file->GetEntryIdf2()->GetInstrument()->GetDetector()->GetNoOfSpectra() > 0) { // counts[][] + for (int i=0; iGetEntryIdf2()->GetInstrument()->GetDetector()->GetNoOfSpectra(); i++) { + for (int j=0; jGetEntryIdf2()->GetInstrument()->GetDetector()->GetNoOfBins(); j++) { + data.push_back(*(histos+i*nxs_file->GetEntryIdf2()->GetInstrument()->GetDetector()->GetNoOfBins()+j)); + } + runData.AppendDataBin(data); + data.clear(); + } + } else { // counts[] + for (int i=0; iGetEntryIdf2()->GetInstrument()->GetDetector()->GetNoOfBins(); i++) { + data.push_back(*(histos+i)); + } + runData.AppendDataBin(data); + data.clear(); + } + } + + // keep run name from the msr-file + runData.SetRunName(fRunName); + + // keep the information + fData.push_back(runData); + } else { + cout << endl << ">> PRunDataHandler::ReadNexusFile(): IDF version " << nxs_file->GetIdfVersion() << ", not implemented." << endl; } - data.clear(); - - // keep run name from the msr-file - runData.SetRunName(fRunName); - - // keep the information - fData.push_back(runData); // clean up if (nxs_file) { @@ -3405,8 +3591,11 @@ Bool_t PRunDataHandler::WriteRootFile(TString fln) * \param fln file name. If empty, the routine will try to construct one */ Bool_t PRunDataHandler::WriteNexusFile(TString fln) -{ +{ + #ifdef PNEXUS_ENABLED + int *t0=0, *fgb=0, *lgb=0, *histo=0; + // generate output file name if (fln.Length() == 0) { Bool_t ok = false; @@ -3429,65 +3618,151 @@ Bool_t PRunDataHandler::WriteNexusFile(TString fln) return false; } - // fill necessary data structures - nxs->SetFileName(fln.Data()); - nxs->SetIDFVersion(1); - nxs->SetProgramName("any2many"); - nxs->SetProgramVersion("$Id$"); - nxs->SetRunNumber(fData[0].GetRunNumber()); - nxs->SetRunTitle(fData[0].GetRunTitle()->Data()); - nxs->SetRunNotes("n/a"); - nxs->SetAnalysisTag("n/a"); - nxs->SetLab("PSI"); - nxs->SetBeamline("n/a"); - nxs->SetStartDate(fData[0].GetStartDate()->Data()); - nxs->SetStartTime(fData[0].GetStartTime()->Data()); - nxs->SetStopDate(fData[0].GetStopDate()->Data()); - nxs->SetStopTime(fData[0].GetStopTime()->Data()); - nxs->SetSwitchingState(0); - nxs->SetUser("n/a"); - nxs->SetExperimentNumber("n/a"); - nxs->SetSampleName(fData[0].GetSample()->Data()); - nxs->SetSampleTemperature(fData[0].GetTemperature(0)); - nxs->SetSampleTemperatureUints("Kelvin"); - nxs->SetMagneticField(fData[0].GetField()); - nxs->SetMagneticFieldUnits("Gauss"); - nxs->SetSampleEnvironment("n/a"); - nxs->SetSampleShape("n/a"); - nxs->SetMagneticFieldVectorAvailable(0); - nxs->SetExperimentName("n/a"); - nxs->SetNoDetectors(fData[0].GetNoOfHistos()); - nxs->SetCollimatorType("n/a"); - nxs->SetTimeResolution(fData[0].GetTimeResolution()); - if (fData[0].GetT0(0) == -1) - nxs->SetT0(0); - else - nxs->SetT0(fData[0].GetT0(0)); // this needs to be changed in the long term, since for continous sources each detector has its one t0!! - if (fData[0].GetGoodDataBin(0).first == -1) { - nxs->SetFirstGoodBin(0); - nxs->SetLastGoodBin(0); - } else { - nxs->SetFirstGoodBin(fData[0].GetGoodDataBin(0).first); - nxs->SetLastGoodBin(fData[0].GetGoodDataBin(0).second); - } - // feed real histogram data - PUIntVector data; - for (UInt_t i=0; isize(); j++) { - data.push_back((UInt_t)fData[0].GetDataBin(i)->at(j)); + // set IDF version + nxs->SetIdfVersion(fAny2ManyInfo->idf); + + if (fAny2ManyInfo->idf == 1) { + // fill necessary data structures + nxs->SetFileName(fln.Data()); + + // set file creating time + time_t now; + struct tm *tm; + time(&now); + tm = localtime(&now); + string str(""); + char cstr[128]; + sprintf(cstr, "%04d-%02d-%02d %02d:%02d:%02d", 1900+tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_hour, tm->tm_sec); + str = string(cstr); + nxs->SetFileTime(str); + + nxs->GetEntryIdf1()->SetProgramName("any2many"); + nxs->GetEntryIdf1()->SetProgramVersion("$Id$"); + nxs->GetEntryIdf1()->SetRunNumber(fData[0].GetRunNumber()); + nxs->GetEntryIdf1()->SetTitle(fData[0].GetRunTitle()->Data()); + nxs->GetEntryIdf1()->SetNotes("n/a"); + nxs->GetEntryIdf1()->SetAnalysis("n/a"); + nxs->GetEntryIdf1()->SetLaboratory("PSI"); + nxs->GetEntryIdf1()->SetBeamline("n/a"); + str = string(fData[0].GetStartDate()->Data()) + string(" ") + string(fData[0].GetStartTime()->Data()); + nxs->GetEntryIdf1()->SetStartTime(str); + str = string(fData[0].GetStopDate()->Data()) + string(" ") + string(fData[0].GetStopTime()->Data()); + nxs->GetEntryIdf1()->SetStopTime(str); + nxs->GetEntryIdf1()->SetSwitchingState(0); + nxs->GetEntryIdf1()->GetUser()->SetName("n/a"); + nxs->GetEntryIdf1()->GetUser()->SetExperimentNumber("n/a"); + nxs->GetEntryIdf1()->GetSample()->SetName(fData[0].GetSample()->Data()); + nxs->GetEntryIdf1()->GetSample()->SetPhysProp("temperature", fData[0].GetTemperature(0), "Kelvin"); + nxs->GetEntryIdf1()->GetSample()->SetPhysProp("magnetic_field", fData[0].GetField(), "Gauss"); + nxs->GetEntryIdf1()->GetSample()->SetEnvironment("n/a"); + nxs->GetEntryIdf1()->GetSample()->SetShape("n/a"); + nxs->GetEntryIdf1()->GetSample()->SetMagneticFieldVectorAvailable(0); + nxs->GetEntryIdf1()->GetInstrument()->SetName("n/a"); + nxs->GetEntryIdf1()->GetInstrument()->GetDetector()->SetNumber(fData[0].GetNoOfHistos()); + nxs->GetEntryIdf1()->GetInstrument()->GetCollimator()->SetType("n/a"); + nxs->GetEntryIdf1()->GetData()->SetTimeResolution(fData[0].GetTimeResolution(), "ns"); + if (fData[0].GetT0(0) == -1) + nxs->GetEntryIdf1()->GetData()->SetT0(0); + else + nxs->GetEntryIdf1()->GetData()->SetT0(fData[0].GetT0(0)); // this needs to be changed in the long term, since for continous sources each detector has its one t0!! + if (fData[0].GetGoodDataBin(0).first == -1) { + nxs->GetEntryIdf1()->GetData()->SetFirstGoodBin(0); + nxs->GetEntryIdf1()->GetData()->SetLastGoodBin(0); + } else { + nxs->GetEntryIdf1()->GetData()->SetFirstGoodBin(fData[0].GetGoodDataBin(0).first); + nxs->GetEntryIdf1()->GetData()->SetLastGoodBin(fData[0].GetGoodDataBin(0).second); } - nxs->SetHisto(i, data); - data.clear(); + // feed real histogram data + PUIntVector data; + for (UInt_t i=0; isize(); j++) { + data.push_back((UInt_t)fData[0].GetDataBin(i)->at(j)); + } + nxs->GetEntryIdf1()->GetData()->SetHisto(data, i); + data.clear(); + } + } else if (fAny2ManyInfo->idf == 2) { + // fill necessary data structures + nxs->SetFileName(fln.Data()); + + // set file creating time + time_t now; + struct tm *tm; + time(&now); + tm = localtime(&now); + string str(""); + char cstr[128]; + sprintf(cstr, "%04d-%02d-%02d %02d:%02d:%02d", 1900+tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_hour, tm->tm_sec); + str = string(cstr); + nxs->SetFileTime(str); + + nxs->SetCreator("PSI - any2many"); + + nxs->GetEntryIdf2()->SetDefinition("pulsedTD"); + nxs->GetEntryIdf2()->SetRunNumber(fData[0].GetRunNumber()); + nxs->GetEntryIdf2()->SetTitle(fData[0].GetRunTitle()->Data()); + str = string(fData[0].GetStartDate()->Data()) + string(" ") + string(fData[0].GetStartTime()->Data()); + nxs->GetEntryIdf2()->SetStartTime(str); + str = string(fData[0].GetStopDate()->Data()) + string(" ") + string(fData[0].GetStopTime()->Data()); + nxs->GetEntryIdf2()->SetStopTime(str); + + nxs->GetEntryIdf2()->SetExperimentIdentifier("n/a"); + + nxs->GetEntryIdf2()->GetUser()->SetName("n/a"); + + nxs->GetEntryIdf2()->GetSample()->SetName(fData[0].GetSample()->Data()); + nxs->GetEntryIdf2()->GetSample()->SetDescription("n/a"); + nxs->GetEntryIdf2()->GetSample()->SetPhysProp("temperature_1", fData[0].GetTemperature(0), "Kelvin"); + nxs->GetEntryIdf2()->GetSample()->SetPhysProp("magnetic_field_1", fData[0].GetField(), "Gauss"); + nxs->GetEntryIdf2()->GetSample()->SetEnvironmentTemp("n/a"); + nxs->GetEntryIdf2()->GetSample()->SetEnvironmentField("n/a"); + + // here would be the information for NXinstrument. Currently there are not enough information to feed this + + nxs->GetEntryIdf2()->GetInstrument()->GetDetector()->SetDescription("n/a"); + nxs->GetEntryIdf2()->GetInstrument()->GetDetector()->SetNoOfPeriods(1); // currently red/green is not distinguished + nxs->GetEntryIdf2()->GetInstrument()->GetDetector()->SetNoOfSpectra(fData[0].GetNoOfHistos()); + nxs->GetEntryIdf2()->GetInstrument()->GetDetector()->SetNoOfBins(fData[0].GetDataBin(0)->size()); + nxs->GetEntryIdf2()->GetInstrument()->GetDetector()->SetTimeResolution(fData[0].GetTimeResolution(), "ns"); + histo = new int[fData[0].GetNoOfHistos()*fData[0].GetDataBin(0)->size()]; + for (int i=0; iGetEntryIdf2()->GetInstrument()->GetDetector()->GetNoOfSpectra(); i++) { + for (unsigned int j=0; jsize(); j++) { + *(histo+i*fData[0].GetDataBin(0)->size()+j) = (int) fData[0].GetDataBin(i)->at(j); + } + } + nxs->GetEntryIdf2()->GetInstrument()->GetDetector()->SetHisto(histo); + for (int i=0; iGetEntryIdf2()->GetInstrument()->GetDetector()->GetNoOfSpectra(); i++) + nxs->GetEntryIdf2()->GetInstrument()->GetDetector()->SetSpectrumIndex(i+1); + + nxs->GetEntryIdf2()->GetInstrument()->GetDetector()->SetT0Tag(2); + int *t0 = new int[fData[0].GetT0Size()]; + for (unsigned int i=0; iGetEntryIdf2()->GetInstrument()->GetDetector()->SetT0(t0); + + // first_good_bin - still missing + // last_good_bin - still missing + } else { + // clean up + if (nxs != 0) { + delete nxs; + nxs = 0; + } + return false; } // filter out the proper file type, i.e. HDF4, HDF5, or XML char fileType[32]; memset(fileType, '\0', 32); - if (!fAny2ManyInfo->outFormat.CompareTo("nexus-hdf4", TString::kIgnoreCase)) + if (!fAny2ManyInfo->outFormat.CompareTo("nexus1-hdf4", TString::kIgnoreCase) || !fAny2ManyInfo->outFormat.CompareTo("nexus2-hdf4", TString::kIgnoreCase)) strncpy(fileType, "hdf4", sizeof(fileType)); - else if (!fAny2ManyInfo->outFormat.CompareTo("nexus-hdf5", TString::kIgnoreCase)) + else if (!fAny2ManyInfo->outFormat.CompareTo("nexus1-hdf5", TString::kIgnoreCase) || !fAny2ManyInfo->outFormat.CompareTo("nexus2-hdf5", TString::kIgnoreCase)) strncpy(fileType, "hdf5", sizeof(fileType)); - else if (!fAny2ManyInfo->outFormat.CompareTo("nexus-xml", TString::kIgnoreCase)) + else if (!fAny2ManyInfo->outFormat.CompareTo("nexus1-xml", TString::kIgnoreCase) || !fAny2ManyInfo->outFormat.CompareTo("nexus2-xml", TString::kIgnoreCase)) strncpy(fileType, "xml", sizeof(fileType)); else { cerr << endl << ">> PRunDataHandler::WriteNexusFile(): **ERROR** undefined output NeXus format " << fAny2ManyInfo->outFormat.Data() << " found."; @@ -3500,9 +3775,25 @@ Bool_t PRunDataHandler::WriteNexusFile(TString fln) } // write file - nxs->WriteFile(fln, fileType); + nxs->WriteFile(fln, fileType, fAny2ManyInfo->idf); // clean up + if (t0) { + delete [] t0; + t0 = 0; + } + if (fgb) { + delete [] fgb; + fgb = 0; + } + if (lgb) { + delete [] lgb; + lgb = 0; + } + if (histo) { + delete [] histo; + histo = 0; + } if (nxs != 0) { delete nxs; nxs = 0; @@ -3510,6 +3801,7 @@ Bool_t PRunDataHandler::WriteNexusFile(TString fln) #else cout << endl << ">> PRunDataHandler::WriteNexusFile(): Sorry, not enabled at configuration level, i.e. --enable-NeXus when executing configure" << endl << endl; #endif + return true; } @@ -4567,3 +4859,32 @@ TString PRunDataHandler::GetMonth(Int_t month) return mm; } + +//-------------------------------------------------------------------------- +// SplitTimeData (private) +//-------------------------------------------------------------------------- +/** + *

splits an ISO 8601 timeDate into seperate time and data. The flag ok + * shows if it was successfull. + * + * \param timeData ISO 8601 timeData + * \param time part of timeData + * \param date part of timeData + * \param ok flag showing if the splitting was successfull + */ +void PRunDataHandler::SplitTimeDate(TString timeData, TString &time, TString &date, Bool_t &ok) +{ + struct tm tm; + memset(&tm, 0, sizeof(tm)); + strptime(timeData.Data(), "%Y-%m-%d %H:%M:S", &tm); + if (tm.tm_year == 0) + strptime(timeData.Data(), "%Y-%m-%dT%H:%M:S", &tm); + + if (tm.tm_year == 0) { + ok = false; + return; + } + + time = TString::Format("%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec); + date = TString::Format("%04d-%02d-%02d", tm.tm_year+1900, tm.tm_mon, tm.tm_mday); +} diff --git a/src/external/nexus/Makefile.am b/src/external/nexus/Makefile.am index 23be2465..8206d53d 100644 --- a/src/external/nexus/Makefile.am +++ b/src/external/nexus/Makefile.am @@ -5,7 +5,7 @@ h_sources = \ PNeXus.h cpp_sources = \ - PNeXus.cpp + PNeXus.cpp include_HEADERS = $(h_sources) diff --git a/src/external/nexus/PNeXus.cpp b/src/external/nexus/PNeXus.cpp index 469a9b2d..ca33e904 100644 --- a/src/external/nexus/PNeXus.cpp +++ b/src/external/nexus/PNeXus.cpp @@ -11,7 +11,7 @@ /*************************************************************************** * Copyright (C) 2007-2011 by Andreas Suter * - * andreas.suter@psi.ch * + * andreas.suter@psi.ch * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -29,8 +29,13 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#include -#include +#include +#include +#include + +#include +#include +using namespace std; #include "PNeXus.h" @@ -53,66 +58,1603 @@ # define SIZE_UCHAR16 2 #endif /* SIZE_FLOAT32 */ -//----------------------------------------------------------------------------------------------------- -// PNeXus constructor -//----------------------------------------------------------------------------------------------------- +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//------------------------------------------------------------------------------------------ +// PNeXusProp Constructor +//------------------------------------------------------------------------------------------ /** + *

+ */ +PNeXusProp::PNeXusProp() +{ + fName = "n/a"; + fUnit = "n/a"; + fValue = 1.0e99; +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//------------------------------------------------------------------------------------------ +// IsValid (public) +//------------------------------------------------------------------------------------------ +/** + *

Validates the NXbeam data. * + * \param strict flag if true a strict NeXus validation is performed. + */ +bool PNeXusBeam1::IsValid(bool strict) +{ + bool valid = true; + + if (fTotalCounts == 0) { + cerr << ">> **WARNING** NXbeam total_counts not given" << endl; + } else if (!fUnits.compare("n/a")) { + cerr << ">> **WARNING** NXbeam total_counts units not given" << endl; + } + + return valid; +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//------------------------------------------------------------------------------------------ +// IsValid (public) +//------------------------------------------------------------------------------------------ +/** + *

Validates the NXdetector data. + * + * \param strict flag if true a strict NeXus validation is performed. + */ +bool PNeXusDetector1::IsValid(bool strict) +{ + bool valid = true; + + if (fNumber == 0) { + cerr << ">> **WARNING** NXdetector number of detectors not given" << endl; + } + + return valid; +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//------------------------------------------------------------------------------------------ +// IsValid (public) +//------------------------------------------------------------------------------------------ +/** + *

Validates the NXinstrument data. + * + * \param strict flag if true a strict NeXus validation is performed. + */ +bool PNeXusInstrument1::IsValid(bool strict) +{ + bool valid = true; + + if (!fName.compare("n/a")) { + cerr << ">> **ERROR** NXinstrument name not given" << endl; + valid = false; + } else if (!fDetector.IsValid(strict)) { + valid = false; + } else if (!fCollimator.IsValid(strict)) { + valid = false; + } else if (!fBeam.IsValid(strict)) { + valid = false; + } + + return valid; +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//------------------------------------------------------------------------------------------ +// PNeXusSample1 Constructor +//------------------------------------------------------------------------------------------ +/** + *

+ */ +PNeXusSample1::PNeXusSample1() +{ + fName = "n/a"; + fShape = "n/a"; + fMagneticFieldState = "n/a"; + fEnvironment = "n/a"; + fMagneticFieldVectorAvailable = -1; + fMagneticFieldVectorUnits = "n/a"; + fMagneticFieldVectorCoordinateSystem = "n/a"; +} + +//------------------------------------------------------------------------------------------ +// PNeXusSample1 Destructor +//------------------------------------------------------------------------------------------ +/** + *

+ */ +PNeXusSample1::~PNeXusSample1() +{ + fPhysProp.clear(); + fMagneticFieldVector.clear(); +} + +//------------------------------------------------------------------------------------------ +// IsValid (public) +//------------------------------------------------------------------------------------------ +/** + *

Validates the NXsample data. + * + * \param strict flag if true a strict NeXus validation is performed. + */ +bool PNeXusSample1::IsValid(bool strict) +{ + bool valid = true; + + if (!fName.compare("n/a")) { + cerr << ">> **ERROR** NXsample name not given." << endl; + valid = false; + } else if (!fShape.compare("n/a")) { + cerr << ">> **WARNING** NXsample shape not given." << endl; + } + + int count=0; // at the end count holds the number of required physiscal properties. Currently these are: temperature, magnetic_field + for (unsigned int i=0; i> **ERROR** NXsample temperature unit not given." << endl; + valid = false; + } + if (fPhysProp[i].GetValue() == 1.0e99) { + cerr << ">> **ERROR** NXsample temperature value not given." << endl; + valid = false; + } + } + if (!fPhysProp[i].GetName().compare("magnetic_field")) { + count++; + if (!fPhysProp[i].GetUnit().compare("n/a")) { + cerr << ">> **ERROR** NXsample magnetic_field unit not given." << endl; + valid = false; + } + if (fPhysProp[i].GetValue() == 1.0e99) { + cerr << ">> **ERROR** NXsample magnetic_field value not given." << endl; + valid = false; + } + } + } + if (count < 2) { + cerr << ">> **ERROR** not all required physical properties (e.g. temperature, magnetic_field) are given." << endl; + valid = false; + } + + return valid; +} + +//------------------------------------------------------------------------------------------ +// GetPhysPropValue (public) +//------------------------------------------------------------------------------------------ +/** + *

+ * + * \param name + * \param ok + */ +double PNeXusSample1::GetPhysPropValue(string name, bool &ok) +{ + double dval=0.0; + ok = false; + + for (unsigned int i=0; i + * + * \param name + * \param ok + */ +void PNeXusSample1::GetPhysPropUnit(string name, string &unit, bool &ok) +{ + unit = "n/a"; + ok = false; + + for (unsigned int i=0; iSet the physical property with 'name' and 'value' at index 'idx'. If idx==-1 add it at the + * end, otherwise set it at index idx. + * + * \param name of the physical property + * \param value of the physical property + * \param idx index where to set the physical property + */ +void PNeXusSample1::SetPhysProp(string name, double value, string unit, int idx) +{ + PNeXusProp prop; + + prop.SetName(name); + prop.SetValue(value); + prop.SetUnit(unit); + + if (idx == -1) { + fPhysProp.push_back(prop); + } else if (idx >= (int)fPhysProp.size()) { + fPhysProp.resize(idx+1); + fPhysProp[idx] = prop; + } else { + fPhysProp[idx] = prop; + } +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//------------------------------------------------------------------------------------------ +// PNeXusUser1 Constructor +//------------------------------------------------------------------------------------------ +/** + *

+ */ +PNeXusUser1::PNeXusUser1() +{ + fName = "n/a"; + fExperimentNumber = "n/a"; +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//------------------------------------------------------------------------------------------ +// PNeXusEntry1 Constructor +//------------------------------------------------------------------------------------------ +/** + *

+ */ +PNeXusEntry1::PNeXusEntry1() +{ + fProgramName = "n/a"; + fProgramVersion = "n/a"; + fRunNumber = -1; + fTitle = "n/a"; + fNotes = "n/a"; + fAnalysis = "n/a"; + fLaboratory = "n/a"; + fBeamline = "n/a"; + fStartTime = "n/a"; + fStopTime = "n/a"; + fSwitchingState = -1; +} + +//------------------------------------------------------------------------------------------ +// IsValid (public) +//------------------------------------------------------------------------------------------ +/** + *

Validates the NXentry data. + * + * \param strict flag if true a strict NeXus validation is performed. + */ +bool PNeXusEntry1::IsValid(bool strict) +{ + if (!fProgramName.compare("n/a")) { + cerr << ">> **WARNING** NXentry: program_name not given." << endl; + } else if (!fProgramVersion.compare("n/a")) { + cerr << ">> **WARNING** NXentry: program_name/version not given." << endl; + } else if (fRunNumber == -1) { + cerr << ">> **ERROR** NXentry: number (i.e. run number) not given." << endl; + return false; + } else if (!fTitle.compare("n/a")) { + cerr << ">> **ERROR** NXentry: title not given." << endl; + return false; + } else if (!fNotes.compare("n/a")) { + cerr << ">> **WARNING** NXentry: notes not given." << endl; + } else if (!fAnalysis.compare("n/a")) { + cerr << ">> **ERROR** NXentry: analysis (i.e. type of muon experiment like 'TF', 'ALC', ...) not given." << endl; + return false; + } else if (!fLaboratory.compare("n/a")) { + cerr << ">> **ERROR** NXentry: lab (e.g. 'PSI') not given." << endl; + return false; + } else if (!fBeamline.compare("n/a")) { + cerr << ">> **ERROR** NXentry: beamline (e.g. 'piE3') not given." << endl; + return false; + } else if (!fStartTime.compare("n/a")) { + cerr << ">> **ERROR** NXentry: start_time not given." << endl; + return false; + } else if (!fStopTime.compare("n/a")) { + cerr << ">> **ERROR** NXentry: stop_time not given." << endl; + return false; + } else if (fSwitchingState == -1) { + cerr << ">> **ERROR** NXentry: switching_state (i.e. '1' normal data taking, '2' red/green mode) not given." << endl; + return false; + } else if (!fUser.IsValid(strict)) { + return false; + } else if (!fSample.IsValid(strict)) { + return false; + } else if (!fInstrument.IsValid(strict)) { + return false; + } else if (!fData.IsValid(strict)) { + return false; + } + + return true; +} + +//------------------------------------------------------------------------------------------ +// SetStartTime (public) +//------------------------------------------------------------------------------------------ +/** + *

Set the start time string and check that it is ISO 8601 conform. + * + * \param time start time string + */ +int PNeXusEntry1::SetStartTime(string time) +{ + struct tm tm; + memset(&tm, 0, sizeof(tm)); + strptime(time.c_str(), "%Y-%m-%d %H:%M:S", &tm); + if (tm.tm_year == 0) + strptime(time.c_str(), "%Y-%m-%dT%H:%M:S", &tm); + if (tm.tm_year == 0) + return NX_ERROR; + + fStartTime = time; + + return NX_OK; +} + +//------------------------------------------------------------------------------------------ +// SetStopTime (public) +//------------------------------------------------------------------------------------------ +/** + *

Set the stop time string and check that it is ISO 8601 conform. + * + * \param time stop time string + */ +int PNeXusEntry1::SetStopTime(string time) +{ + struct tm tm; + memset(&tm, 0, sizeof(tm)); + strptime(time.c_str(), "%Y-%m-%d %H:%M:S", &tm); + if (tm.tm_year == 0) + strptime(time.c_str(), "%Y-%m-%dT%H:%M:S", &tm); + if (tm.tm_year == 0) + return NX_ERROR; + + fStopTime = time; + + return NX_OK; +} + +//------------------------------------------------------------------------------------------ +// SetSwitchingState (public) +//------------------------------------------------------------------------------------------ +/** + *

Set the switching state tag. '1' normal data collection, '2' Red/Green mode + * + * \param state switching state tag + */ +int PNeXusEntry1::SetSwitchingState(int state) +{ + if ((state != 1) && (state != 2)) + return NX_ERROR; + + fSwitchingState = state; + + return NX_OK; +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//------------------------------------------------------------------------------------------ +// PNeXusAlpha1 Constructor +//------------------------------------------------------------------------------------------ +/** + *

+ */ +PNeXusAlpha1::PNeXusAlpha1() +{ + fGroupFirst=0; + fGroupSecond=0; + fAlphaVal=0.0; +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//------------------------------------------------------------------------------------------ +// PNeXusData1 Destructor +//------------------------------------------------------------------------------------------ +/** + *

+ */ +PNeXusData1::~PNeXusData1() +{ + fT0.clear(); + fFirstGoodBin.clear(); + fLastGoodBin.clear(); + fHistoName.clear(); + for (unsigned int i=0; iValidates the NXbeam data. + * + * \param strict flag if true a strict NeXus validation is performed. + */ +bool PNeXusData1::IsValid(bool strict) +{ + if (GetNoOfHistos() == 0) { + cerr << ">> **ERROR** NXdata no histograms given." << endl; + return false; + } else if (fTimeResolution == 0.0) { + cerr << ">> **ERROR** NXdata time resolution not given." << endl; + return false; + } else if (fT0.size() == 0) { + cerr << ">> **WARNING** NXdata t0 not given." << endl; + } else if (fFirstGoodBin.size() == 0) { + cerr << ">> **WARNING** NXdata first good bin not given." << endl; + } else if (fLastGoodBin.size() == 0) { + cerr << ">> **WARNING** NXdata last good bin not given." << endl; + } + + return true; +} + + +//------------------------------------------------------------------------------------------ +// GetTimeResolution (public) +//------------------------------------------------------------------------------------------ +/** + *

Get time resolution in the requested units. Allowed units are 'fs', 'ps', 'ns', 'us'. + * If unsupported time units are given, a time resolution == 0.0 is returned. Internally the + * time resolution is stored in 'ps'. + * + * \param units requested units for the time resolution. + */ +double PNeXusData1::GetTimeResolution(string units) +{ + double result=0.0; + + if (!units.compare("fs")) + result = fTimeResolution * 1.0e3; + else if (!units.compare("ps")) + result = fTimeResolution; + else if (!units.compare("ns")) + result = fTimeResolution * 1.0e-3; + else if (!units.compare("us")) + result = fTimeResolution * 1.0e-6; + else + result = 0.0; + + return result; +} + +//------------------------------------------------------------------------------------------ +// GetT0 (public) +//------------------------------------------------------------------------------------------ +/** + *

Returns the t0 at index 'idx' or -1 if the index is out of range. + * + * \param idx index of the requested t0 + */ +int PNeXusData1::GetT0(unsigned int idx) +{ + if (idx >= fT0.size()) + return -1; + + return fT0[idx]; +} + +//------------------------------------------------------------------------------------------ +// GetFirstGoodBin (public) +//------------------------------------------------------------------------------------------ +/** + *

Returns the first good bin at index 'idx' or -1 if the index is out of range. + * + * \param idx index of the requested first good bin + */ +int PNeXusData1::GetFirstGoodBin(unsigned int idx) +{ + if (idx >= fFirstGoodBin.size()) + return -1; + + return fFirstGoodBin[idx]; +} + +//------------------------------------------------------------------------------------------ +// GetLastGoodBin (public) +//------------------------------------------------------------------------------------------ +/** + *

Returns the last good bin at index 'idx' or -1 if the index is out of range. + * + * \param idx index of the requested last good bin + */ +int PNeXusData1::GetLastGoodBin(unsigned int idx) +{ + if (idx >= fLastGoodBin.size()) + return -1; + + return fLastGoodBin[idx]; +} + + +//------------------------------------------------------------------------------------------ +// GetHistoName (public) +//------------------------------------------------------------------------------------------ +/** + *

Extract the histogram name at position 'idx'. If 'idx' is out of range, the 'ok' flag + * is set to 'false' in which case the 'name' is undefined. + * + * \param idx index of the requested histogram name. + * \param name histogram name if idx is within range + * \param ok =true if idx was within range, otherwise false + */ +void PNeXusData1::GetHistoName(unsigned int idx, string &name, bool &ok) +{ + if (idx >= fHistoName.size()) { + ok = false; + } else { + ok = true; + name = fHistoName[idx]; + } +} + +//------------------------------------------------------------------------------------------ +// GetHisto (public) +//------------------------------------------------------------------------------------------ +/** + *

Returns the histogram with index 'histoNo' or 0 if 'histoNo' is out of range. + * + * \param histoNo index of the requested histogram + */ +vector *PNeXusData1::GetHisto(unsigned int histoNo) +{ + if (histoNo >= fHisto.size()) + return 0; + + return &fHisto[histoNo]; +} + + +//------------------------------------------------------------------------------------------ +// SetTimeResolution (public) +//------------------------------------------------------------------------------------------ +/** + *

Set the time resolution. 'units' tells in which units 'val' is provided. Acteptable units + * are 'fs', 'ps', 'ns', 'us'. + * + * \param val time resolution value + * \param units time resolution units + */ +void PNeXusData1::SetTimeResolution(double val, string units) +{ + if (!units.compare("fs")) + fTimeResolution = val * 1.0e-3; + else if (!units.compare("ps")) + fTimeResolution = val; + else if (!units.compare("ns")) + fTimeResolution = val * 1.0e3; + else if (!units.compare("us")) + fTimeResolution = val * 1.0e6; +} + +//------------------------------------------------------------------------------------------ +// SetT0 (public) +//------------------------------------------------------------------------------------------ +/** + *

Set the t0 value at index 'idx'. If 'idx'==-1, the t0 value will be appended + * + * \param t0 bin value + * \param idx index where to set t0 bin value + */ +void PNeXusData1::SetT0(unsigned int t0, int idx) +{ + if (idx == -1) { + fT0.push_back(t0); + } else if (idx >= (int)fT0.size()) { + fT0.resize(idx+1); + fT0[idx] = t0; + } else { + fT0[idx] = t0; + } +} + +//------------------------------------------------------------------------------------------ +// SetFirstGoodBin (public) +//------------------------------------------------------------------------------------------ +/** + *

Set the first good bin value at index 'idx'. If 'idx'==-1, the first good bin value will be appended + * + * \param fgb first good bin value + * \param idx index where to set first good bin value + */ +void PNeXusData1::SetFirstGoodBin(unsigned int fgb, int idx) +{ + if (idx == -1) { + fFirstGoodBin.push_back(fgb); + } else if (idx >= (int)fFirstGoodBin.size()) { + fFirstGoodBin.resize(idx+1); + fFirstGoodBin[idx] = fgb; + } else { + fFirstGoodBin[idx] = fgb; + } +} + +//------------------------------------------------------------------------------------------ +// SetLastGoodBin (public) +//------------------------------------------------------------------------------------------ +/** + *

Set the last good bin value at index 'idx'. If 'idx'==-1, the last good bin value will be appended + * + * \param lgb last good bin value + * \param idx index where to set last good bin value + */ +void PNeXusData1::SetLastGoodBin(unsigned int lgb, int idx) +{ + if (idx == -1) { + fLastGoodBin.push_back(lgb); + } else if (idx >= (int)fLastGoodBin.size()) { + fLastGoodBin.resize(idx+1); + fLastGoodBin[idx] = lgb; + } else { + fLastGoodBin[idx] = lgb; + } +} + +//------------------------------------------------------------------------------------------ +// FlushHistos (public) +//------------------------------------------------------------------------------------------ +/** + *

Flushes all previously allocated histograms. + * + */ +void PNeXusData1::FlushHistos() +{ + for (unsigned int i=0; iSet a histogram at index 'histoNo'. If 'histoNo'==-1, the histogram will be appended + * + * \param data histogram vector + * \param histoNo index where to set the histogram + */ +void PNeXusData1::SetHisto(vector &data, int histoNo) +{ + if (histoNo == -1) { + fHisto.push_back(data); + } else if (histoNo >= (int)fHisto.size()) { + fHisto.resize(histoNo+1); + fHisto[histoNo] = data; + } else { + fHisto[histoNo] = data; + } +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//------------------------------------------------------------------------------------------ +// IsValid (public) +//------------------------------------------------------------------------------------------ +/** + *

Validates the NXbeamline data. + * + * \param strict flag if true a strict NeXus validation is performed. + */ +bool PNeXusBeamline2::IsValid(bool strict) +{ + string msg(""); + + if (!fName.compare("n/a")) { + msg = "IDF2 NXbeamline 'name' not set."; + if (strict) { + cerr << ">> **ERROR** " << msg << endl; + return false; + } else { + cerr << ">> **WARNING** " << msg << endl; + } + } + + return true; +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//------------------------------------------------------------------------------------------ +// PNeXusDetector2 Constructor +//------------------------------------------------------------------------------------------ +/** + *

+ */ +PNeXusDetector2::PNeXusDetector2() +{ + fDescription = "n/a"; + fTimeResolution = 0; + fNoOfPeriods = -1; + fNoOfSpectra = -1; + fNoOfBins = -1; + + fT0Tag = -1; + fT0 = 0; + fFirstGoodBin = 0; + fLastGoodBin = 0; + fHisto = 0; +} + +//------------------------------------------------------------------------------------------ +// PNeXusDetector2 Destructor +//------------------------------------------------------------------------------------------ +/** + *

+ */ +PNeXusDetector2::~PNeXusDetector2() +{ + fSpectrumIndex.clear(); + + if (fT0) { + delete [] fT0; + fT0 = 0; + } + if (fFirstGoodBin) { + delete [] fFirstGoodBin; + fFirstGoodBin = 0; + } + if (fLastGoodBin) { + delete [] fLastGoodBin; + fLastGoodBin = 0; + } + if (fHisto) { + delete [] fHisto; + fHisto = 0; + } +} + +//------------------------------------------------------------------------------------------ +// IsValid (public) +//------------------------------------------------------------------------------------------ +/** + *

Validates the NXdetector data. + * + * \param strict flag if true a strict NeXus validation is performed. + */ +bool PNeXusDetector2::IsValid(bool strict) +{ + string msg(""); + + if (!fDescription.compare("n/a")) { + msg = "IDF2 NXdetector 'description' not set."; + if (strict) { + cerr << ">> **ERROR** " << msg << endl; + return false; + } else { + cerr << ">> **WARNING** " << msg << endl; + } + } + + if (fTimeResolution == 0.0) { + cerr << ">> **ERROR** IDF2 NXdetector 'histogram_resolution' is not set." << endl; + return false; + } + + if (fSpectrumIndex.size() == 0) { + cerr << ">> **ERROR** IDF2 NXdetector 'spectrum_index' is not set." << endl; + return false; + } + + if ((fT0Tag != 1) && (fT0Tag !=2)) { + cerr << ">> **ERROR** IDF2 NXdetector problem with t0/first_good_bin/last_good_bin/counts settings (tagging)." << endl; + return false; + } + + if ((fT0 == 0) || (fHisto == 0)) { + cerr << ">> **ERROR** IDF2 NXdetector problem with t0/counts settings (data)." << endl; + return false; + } + + if (fNoOfBins == -1) { + cerr << ">> **ERROR** IDF2 NXdetector fNoOfBins==-1." << endl; + return false; + } + + return true; +} + +//------------------------------------------------------------------------------------------ +// GetT0 (public) +//------------------------------------------------------------------------------------------ +/** + *

Get T0 bin if present, otherwise return -1. + * + * \param idxp period index + * \param idxs spectrum index + */ +int PNeXusDetector2::GetT0(int idxp, int idxs) +{ + int result = -1; + + if ((idxp < 0) || (idxs < 0)) { // assumption: there is only ONE t0 for all spectra + if (fT0 != 0) { + result = *fT0; + } + } else { // assumption: t0's are represented as t0[np][ns] + if ((idxp < fNoOfPeriods) || (idxs < fNoOfSpectra)) { + result = *(fT0+idxp*fNoOfSpectra+idxs); + } + } + + return result; +} + +//------------------------------------------------------------------------------------------ +// GetFirstGoodBin (public) +//------------------------------------------------------------------------------------------ +/** + *

Get first good bin if present, otherwise return -1. + * + * \param idxp period index + * \param idxs spectrum index + */ +int PNeXusDetector2::GetFirstGoodBin(int idxp, int idxs) +{ + int result = -1; + + if ((idxp < 0) || (idxs < 0)) { // assumption: there is only ONE t0 for all spectra + if (fFirstGoodBin != 0) { + result = *fFirstGoodBin; + } + } else { // assumption: fgb's are represented as fgb[np][ns] + if ((idxp < fNoOfPeriods) || (idxs < fNoOfSpectra)) { + result = *(fFirstGoodBin+idxp*fNoOfSpectra+idxs); + } + } + + return result; +} + +//------------------------------------------------------------------------------------------ +// GetLastGoodBin (public) +//------------------------------------------------------------------------------------------ +/** + *

Get last good bin if present, otherwise return -1. + * + * \param idxp period index + * \param idxs spectrum index + */ +int PNeXusDetector2::GetLastGoodBin(int idxp, int idxs) +{ + int result = -1; + + if ((idxp < 0) || (idxs < 0)) { // assumption: there is only ONE t0 for all spectra + if (fLastGoodBin != 0) { + result = *fLastGoodBin; + } + } else { // assumption: lgb's are represented as lgb[np][ns] + if ((idxp < fNoOfPeriods) || (idxs < fNoOfSpectra)) { + result = *(fLastGoodBin+idxp*fNoOfSpectra+idxs); + } + } + + return result; +} + +//------------------------------------------------------------------------------------------ +// GetHistoValue (public) +//------------------------------------------------------------------------------------------ +/** + *

Get histogram value. If any of the indices is out of range, -1 is returned. Since the + * histogram can have the structures: histo[][][], histo[][], or histo[] + * + * \param idx_p period index + * \param idx_s spectrum index + * \param idx_b histogram bin index + */ +int PNeXusDetector2::GetHistoValue(int idx_p, int idx_s, int idx_b) +{ + int value = -1; + + if ((idx_b < 0) || (idx_b >= fNoOfBins)) { // make sure that idx_b is within proper bounds + return -1; + } + + if (fNoOfSpectra > 0) { // make sure that idx_s is within proper bounds if there are different spectra + if ((idx_s < 0) || (idx_s >= fNoOfSpectra)) { + return -1; + } + } + + if (fNoOfPeriods > 0) { // make sure that idx_p is within proper bounds if there are different periods + if ((idx_p < 0) || (idx_p >= fNoOfPeriods)) { + return -1; + } + } + + if (fNoOfPeriods > 0) { + value = *(fHisto+idx_p*fNoOfSpectra*fNoOfBins+idx_s*fNoOfBins+idx_b); + } else { + if (fNoOfSpectra > 0) { + value = *(fHisto+idx_s*fNoOfBins+idx_b); + } else { + value = *(fHisto+idx_b); + } + } + + return value; +} + +//------------------------------------------------------------------------------------------ +// GetTimeResolution (public) +//------------------------------------------------------------------------------------------ +/** + *

Get time resolution in the requested units. Allowed units are 'fs', 'ps', 'ns', 'us'. + * If unsupported time units are given, a time resolution == 0.0 is returned. Internally the + * time resolution is stored in 'ps'. + * + * \param units requested units for the time resolution. + */ +double PNeXusDetector2::GetTimeResolution(string units) +{ + double result=0.0; + + if (!units.compare("fs") || !units.compare("femto.second") || !units.compare("femto second") || !units.compare("femtoseconds")) + result = fTimeResolution * 1.0e3; + else if (!units.compare("ps") || !units.compare("pico.second") || !units.compare("pico second") || !units.compare("picoseconds")) + result = fTimeResolution; + else if (!units.compare("ns") || !units.compare("nano.second") || !units.compare("nano second") || !units.compare("nanoseconds")) + result = fTimeResolution * 1.0e-3; + else if (!units.compare("us") || !units.compare("micro.second") || !units.compare("micro second") || !units.compare("microseconds")) + result = fTimeResolution * 1.0e-6; + else + result = 0.0; + + return result; +} + +//------------------------------------------------------------------------------------------ +// SetTimeResolution (public) +//------------------------------------------------------------------------------------------ +/** + *

Set the time resolution. 'units' tells in which units 'val' is provided. Acteptable units + * are 'fs', 'ps', 'ns', 'us'. + * + * \param val time resolution value + * \param units time resolution units + */ +void PNeXusDetector2::SetTimeResolution(double val, string units) +{ + if (!units.compare("fs") || !units.compare("femto.second") || !units.compare("femto second") || !units.compare("femtoseconds")) + fTimeResolution = val * 1.0e-3; + else if (!units.compare("ps") || !units.compare("pico.second") || !units.compare("pico second") || !units.compare("picoseconds")) + fTimeResolution = val; + else if (!units.compare("ns") || !units.compare("nano.second") || !units.compare("nano second") || !units.compare("nanoseconds")) + fTimeResolution = val * 1.0e3; + else if (!units.compare("us") || !units.compare("micro.second") || !units.compare("micro second") || !units.compare("microseconds")) + fTimeResolution = val * 1.0e6; +} + +//------------------------------------------------------------------------------------------ +// GetSpectrumIndex (public) +//------------------------------------------------------------------------------------------ +/** + *

returns the global spectrum index of index idx. + * + * \param idx spectrum index idx + */ +int PNeXusDetector2::GetSpectrumIndex(unsigned int idx) +{ + if (idx >= fSpectrumIndex.size()) + return -1; + + return fSpectrumIndex[idx]; +} + +//------------------------------------------------------------------------------------------ +// SetSpectrumIndex (public) +//------------------------------------------------------------------------------------------ +/** + *

set the global spectrum index of index idx. + * + * \param spectIdx spectrum index value + * \param idx spectrum index idx + */ +void PNeXusDetector2::SetSpectrumIndex(int spectIdx, int idx) +{ + if (idx < 0) { + fSpectrumIndex.push_back(spectIdx); + } else if (idx >= (int)fSpectrumIndex.size()) { + fSpectrumIndex.resize(idx+1); + fSpectrumIndex[idx] = spectIdx; + } else { + fSpectrumIndex[idx] = spectIdx; + } +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//------------------------------------------------------------------------------------------ +// IsValid (public) +//------------------------------------------------------------------------------------------ +/** + *

Validates the NXinstrument data. + * + * \param strict flag if true a strict NeXus validation is performed. + */ +bool PNeXusInstrument2::IsValid(bool strict) +{ + string msg(""); + + if (!fName.compare("n/a")) { + msg = "IDF2 NXinstrument 'name' not set."; + if (strict) { + cerr << ">> **ERROR** " << msg << endl; + return false; + } else { + cerr << ">> **WARNING** " << msg << endl; + } + } + + if (!fSource.IsValid(strict)) + return false; + + if (!fBeamline.IsValid(strict)) + return false; + + if (!fDetector.IsValid(strict)) + return false; + + return true; +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//------------------------------------------------------------------------------------------ +// PNeXusSource2 Constructor +//------------------------------------------------------------------------------------------ +/** + *

+ */ +PNeXusSource2::PNeXusSource2() +{ + fName = "n/a"; + fType = "n/a"; + fProbe = "n/a"; +} + +//------------------------------------------------------------------------------------------ +// IsValid (public) +//------------------------------------------------------------------------------------------ +/** + *

Validates the NXsource data. + * + * \param strict flag if true a strict NeXus validation is performed. + */ +bool PNeXusSource2::IsValid(bool strict) +{ + string msg(""); + + if (!fName.compare("n/a")) { + msg = "IDF2 NXsample 'name' not set."; + if (strict) { + cerr << ">> **ERROR** " << msg << endl; + return false; + } else { + cerr << ">> **WARNING** " << msg << endl; + } + } + + if (!fType.compare("n/a")) { + msg = "IDF2 NXsample 'type' not set."; + if (strict) { + cerr << ">> **ERROR** " << msg << endl; + return false; + } else { + cerr << ">> **WARNING** " << msg << endl; + } + } + + if (!fProbe.compare("n/a")) { + msg = "IDF2 NXsample 'probe' not set."; + if (strict) { + cerr << ">> **ERROR** " << msg << endl; + return false; + } else { + cerr << ">> **WARNING** " << msg << endl; + } + } + + return true; +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//------------------------------------------------------------------------------------------ +// PNeXusSample2 Constructor +//------------------------------------------------------------------------------------------ +/** + *

+ */ +PNeXusSample2::PNeXusSample2() +{ + fName = "n/a"; + fDescription = "n/a"; + fMagneticFieldState = "n/a"; + fEnvironmentTemp = "n/a"; + fEnvironmentField = "n/a"; +} + +//------------------------------------------------------------------------------------------ +// PNeXusSample2 Destructor +//------------------------------------------------------------------------------------------ +/** + *

+ */ +PNeXusSample2::~PNeXusSample2() +{ + fPhysProp.clear(); +} + +//------------------------------------------------------------------------------------------ +// IsValid (public) +//------------------------------------------------------------------------------------------ +/** + *

Validates the NXsample data. + * + * \param strict flag if true a strict NeXus validation is performed. + */ +bool PNeXusSample2::IsValid(bool strict) +{ + string msg(""); + + if (!fName.compare("n/a")) { + msg = "IDF2 NXsample 'name' not set."; + if (strict) { + cerr << ">> **ERROR** " << msg << endl; + return false; + } else { + cerr << ">> **WARNING** " << msg << endl; + } + } + + if (!fDescription.compare("n/a")) { + cerr << endl << ">> **WARNING** IDF2 NXsample 'description' not set." << endl; + } + + if (!fMagneticFieldState.compare("n/a")) { + cerr << endl << ">> **WARNING** IDF2 NXsample 'magnetic_field_state' not set." << endl; + } + + if (!fEnvironmentTemp.compare("n/a")) { + cerr << endl << ">> **WARNING** IDF2 NXsample 'temperature_1_env' not set." << endl; + } + + if (!fEnvironmentField.compare("n/a")) { + cerr << endl << ">> **WARNING** IDF2 NXsample 'magnetic_field_1_env' not set." << endl; + } + + bool ok; + double dummy; + + dummy = GetPhysPropValue("temperature_1", ok); + if (!ok) { + cerr << endl << ">> **WARNING** IDF2 NXsample 'temperature_1' not set." << endl; + } + + dummy = GetPhysPropValue("magnetic_field_1", ok); + if (!ok) { + cerr << endl << ">> **WARNING** IDF2 NXsample 'magnetic_field_1' not set." << endl; + } + + return true; +} + +//------------------------------------------------------------------------------------------ +// GetPhysPropValue (public) +//------------------------------------------------------------------------------------------ +/** + *

+ * + * \param name + * \param ok + */ +double PNeXusSample2::GetPhysPropValue(string name, bool &ok) +{ + double dval=0.0; + ok = false; + + for (unsigned int i=0; i + * + * \param name + * \param ok + */ +void PNeXusSample2::GetPhysPropUnit(string name, string &unit, bool &ok) +{ + unit = "n/a"; + ok = false; + + for (unsigned int i=0; iSet the physical property with 'name' and 'value' at index 'idx'. If idx==-1 add it at the + * end, otherwise set it at index idx. + * + * \param name of the physical property + * \param value of the physical property + * \param idx index where to set the physical property + */ +void PNeXusSample2::SetPhysProp(string name, double value, string unit, int idx) +{ + PNeXusProp prop; + + prop.SetName(name); + prop.SetValue(value); + prop.SetUnit(unit); + + if (idx == -1) { + fPhysProp.push_back(prop); + } else if (idx >= (int)fPhysProp.size()) { + fPhysProp.resize(idx+1); + fPhysProp[idx] = prop; + } else { + fPhysProp[idx] = prop; + } +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//------------------------------------------------------------------------------------------ +// PNeXusEntry2 Constructor +//------------------------------------------------------------------------------------------ +/** + *

+ */ +PNeXusEntry2::PNeXusEntry2() +{ + fDefinition = "n/a"; + fProgramName = "n/a"; + fProgramVersion = "n/a"; + fRunNumber = -1; + fTitle = "n/a"; + fStartTime = "n/a"; + fStopTime = "n/a"; + fExperimentIdentifier = "n/a"; +} + +//------------------------------------------------------------------------------------------ +// IsValid (public) +//------------------------------------------------------------------------------------------ +/** + *

Validates the NXentry data. + * + * \param strict flag if true a strict NeXus validation is performed. + */ +bool PNeXusEntry2::IsValid(bool strict) +{ + string msg(""); + + if (!fDefinition.compare("n/a")) { + msg = "IDF2 NXentry definition not set."; + if (strict) { + cerr << endl << ">> **ERROR** " << msg << endl; + return false; + } else { + cerr << endl << ">> **WARNING** " << msg << endl; + } + } + + if (fRunNumber == -1) { + msg = "run number not set."; + if (strict) { + cerr << endl << ">> **ERROR** " << msg << endl; + return false; + } else { + cerr << endl << ">> **WARNING** " << msg << endl; + } + } + + if (!fTitle.compare("n/a")) { + msg = "run title not set."; + if (strict) { + cerr << endl << ">> **ERROR** " << msg << endl; + return false; + } else { + cerr << endl << ">> **WARNING** " << msg << endl; + } + } + + if (!fStartTime.compare("n/a")) { + msg = "start time not set."; + if (strict) { + cerr << endl << ">> **ERROR** " << msg << endl; + return false; + } else { + cerr << endl << ">> **WARNING** " << msg << endl; + } + } + + if (!fStopTime.compare("n/a")) { + msg = "end time not set."; + if (strict) { + cerr << endl << ">> **ERROR** " << msg << endl; + return false; + } else { + cerr << endl << ">> **WARNING** " << msg << endl; + } + } + + if (!fExperimentIdentifier.compare("n/a")) { + msg = "experiment identifier not set."; + if (strict) { + cerr << endl << ">> **ERROR** " << msg << endl; + return false; + } else { + cerr << endl << ">> **WARNING** " << msg << endl; + } + } + + if (!fUser.IsValid(strict)) + return false; + + if (!fSample.IsValid(strict)) + return false; + + if (!fInstrument.IsValid(strict)) + return false; + + return true; +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//------------------------------------------------------------------------------------------ +// SetStartTime (public) +//------------------------------------------------------------------------------------------ +/** + *

Set the start time string and check that it is ISO 8601 conform. + * + * \param time start time string + */ +int PNeXusEntry2::SetStartTime(string time) +{ + struct tm tm; + memset(&tm, 0, sizeof(tm)); + strptime(time.c_str(), "%Y-%m-%d %H:%M:S", &tm); + if (tm.tm_year == 0) + strptime(time.c_str(), "%Y-%m-%dT%H:%M:S", &tm); + if (tm.tm_year == 0) + return NX_ERROR; + + fStartTime = time; + + return NX_OK; +} + +//------------------------------------------------------------------------------------------ +// SetStopTime (public) +//------------------------------------------------------------------------------------------ +/** + *

Set the stop time string and check that it is ISO 8601 conform. + * + * \param time stop time string + */ +int PNeXusEntry2::SetStopTime(string time) +{ + struct tm tm; + memset(&tm, 0, sizeof(tm)); + strptime(time.c_str(), "%Y-%m-%d %H:%M:S", &tm); + if (tm.tm_year == 0) + strptime(time.c_str(), "%Y-%m-%dT%H:%M:S", &tm); + if (tm.tm_year == 0) + return NX_ERROR; + + fStopTime = time; + + return NX_OK; +} + +//------------------------------------------------------------------------------------------ +// PNeXus Constructor +//------------------------------------------------------------------------------------------ +/** + *

*/ PNeXus::PNeXus() { Init(); } -//----------------------------------------------------------------------------------------------------- -// PNeXus constructor -//----------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------ +// PNeXus Constructor +//------------------------------------------------------------------------------------------ /** - * + *

* * \param fileName */ PNeXus::PNeXus(const char* fileName) { Init(); + fFileName = fileName; + if (ReadFile(fileName) != NX_OK) { cerr << endl << fErrorMsg << " (error code=" << fErrorCode << ")" << endl << endl; } else { - fIsValid = true; + fValid = true; } } -//----------------------------------------------------------------------------------------------------- -// PNeXus destructor -//----------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------ +// PNeXus Destructor +//------------------------------------------------------------------------------------------ /** - * + *

*/ PNeXus::~PNeXus() { - CleanUp(); + if (fNxEntry1) { + delete fNxEntry1; + fNxEntry1 = 0; + } + + for (unsigned int i=0; i + *

Define which IDF version shall be written/created. + * + * \param idf version number + */ +void PNeXus::SetIdfVersion(unsigned int idf) +{ + if ((idf != 1) && (idf != 2)) { + cerr << endl << ">> **ERROR** Only IDF versions 1 and 2 are supported." << endl; + return; + } + + fIdfVersion = idf; + + if (idf == 1) { // IDF 1 + if (fNxEntry2) { + delete fNxEntry2; + fNxEntry2 = 0; + } + + fNxEntry1 = new PNeXusEntry1(); + if (fNxEntry1 == 0) { + cerr << endl << ">> **ERROR** couldn't invoke IDF 1 object PNeXusEntry1." << endl; + return; + } + } else { // IDF 2 + if (fNxEntry1) { + delete fNxEntry1; + fNxEntry1 = 0; + } + + fNxEntry2 = new PNeXusEntry2(); + if (fNxEntry2 == 0) { + cerr << endl << ">> **ERROR** couldn't invoke IDF 2 object PNeXusEntry1." << endl; + return; + } + } +} + +//------------------------------------------------------------------------------------------ +// Init (public) +//------------------------------------------------------------------------------------------ +/** + *

Validates the NeXus data. A flag 'strict' controls the degree of validation. If 'strict' == true + * a full NeXus validation is done, otherwise a much sloppier validation is performed. This sloppier + * validation is needed when converting data. + * + * \param strict flag if true a strict NeXus validation is performed. + */ +bool PNeXus::IsValid(bool strict) +{ + bool valid = true; + + if (fIdfVersion == 1) // IDF Version 1 + valid = IsValidIdf1(strict); + else if (fIdfVersion == 2) // IDF Version 2 + valid = IsValidIdf2(strict); + else { + cerr << endl << ">> **ERROR** found IDF Version " << fIdfVersion << ", which is not implemented yet." << endl << endl; + valid = false; + } + + return valid; +} + +//------------------------------------------------------------------------------------------ +// GetGroupedHisto (public) +//------------------------------------------------------------------------------------------ +/** + *

Returns the grouped histogram if idx is within range, otherwise 0. + * + * \param idx index of the grouped histogram to be returned. + */ +vector* PNeXus::GetGroupedHisto(unsigned int idx) +{ + if (idx >= fGroupedHisto.size()) + return 0; + + return &fGroupedHisto[idx]; +} + +//------------------------------------------------------------------------------------------ +// ReadFile (public) +//------------------------------------------------------------------------------------------ +/** + *

Read the NeXus file 'fileName'. * * return: * - NX_OK on successfull reading * - NX_ERROR on error. The error code/message will give the details. * - * \param fileName + * \param fileName file name of the nexus file to be read */ int PNeXus::ReadFile(const char *fileName) { - int status, ival; - float fval; - string str(""); + fFileName = fileName; // open file + NXstatus status; status = NXopen(fileName, NXACC_READ, &fFileHandle); if (status != NX_OK) { fErrorCode = PNEXUS_FILE_OPEN_ERROR; @@ -120,443 +1662,90 @@ int PNeXus::ReadFile(const char *fileName) return NX_ERROR; } - // open group - status = NXopengroup(fFileHandle, "run", "NXentry"); + // get idf in order to decide which read routine needs to be used. + bool found = false; + NXname nxname, nxclass; + int dataType; + // 1) get the first NXentry + if (!SearchInGroup("NXentry", "class", nxname, nxclass, dataType)) { + fErrorCode = PNEXUS_NXENTRY_NOT_FOUND; + fErrorMsg = "PNeXus::ReadFile() **ERROR** Couldn't find any NXentry!"; + return NX_ERROR; + } + // 2) open the NXentry group to obtain the IDF + status = NXopengroup(fFileHandle, nxname, "NXentry"); if (status != NX_OK) { fErrorCode = PNEXUS_GROUP_OPEN_ERROR; - fErrorMsg = "PNeXus::ReadFile() **ERROR** Couldn't open NeXus group run!"; + fErrorMsg = "PNeXus::ReadFile() **ERROR** Couldn't open the NeXus group '" + string(nxname) + "'!"; return NX_ERROR; } - + // 3) get the IDF // IDF - if (NXopendata(fFileHandle, "idf_version") != NX_OK) { - if (NXopendata(fFileHandle, "IDF_version") != NX_OK) { - fErrorCode = PNEXUS_OPEN_DATA_ERROR; - fErrorMsg = "PNeXus::ReadFile() **ERROR** couldn't open 'IDF_version' nor 'idf_version' data!!"; - return NX_ERROR; - } - } - if (!ErrorHandler(NXgetdata(fFileHandle, &fIDFVersion), PNEXUS_GET_DATA_ERROR, "couldn't read 'IDF_version' data!")) return NX_ERROR; + found = false; + do { + status = NXgetnextentry(fFileHandle, nxname, nxclass, &dataType); + if ((strstr(nxname, "IDF_version") != NULL) || (strstr(nxname, "idf_version") != NULL)) + found = true; + } while (!found || (status == NX_EOD)); + + if (!ErrorHandler(NXopendata(fFileHandle, nxname), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'IDF_version' nor 'idf_version' data!!")) return NX_ERROR; + if (!ErrorHandler(NXgetdata(fFileHandle, &fIdfVersion), PNEXUS_GET_DATA_ERROR, "couldn't read 'idf_version' data!")) return NX_ERROR; if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'IDF_version' data")) return NX_ERROR; - - // program_name - if (!ErrorHandler(NXopendata(fFileHandle, "program_name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'program_name' data!")) return NX_ERROR; - if (!ErrorHandler(GetStringData(fProgramName), PNEXUS_GET_DATA_ERROR, "couldn't read 'program_name' data!")) return NX_ERROR; - if (!ErrorHandler(GetStringAttr("version", fProgramVersion), PNEXUS_GET_ATTR_ERROR, "couldn't read program_name attribute!")) return NX_ERROR; - if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'program_name' data")) return NX_ERROR; - - // run number - if (!ErrorHandler(NXopendata(fFileHandle, "number"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'number' data!")) return NX_ERROR; - if (!ErrorHandler(NXgetdata(fFileHandle, &fRunNumber), PNEXUS_GET_DATA_ERROR, "couldn't read 'number' data!")) return NX_ERROR; - if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'number' data")) return NX_ERROR; - - // title - if (!ErrorHandler(NXopendata(fFileHandle, "title"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'title' data!")) return NX_ERROR; - if (!ErrorHandler(GetStringData(fRunTitle), PNEXUS_GET_DATA_ERROR, "couldn't read 'title' data!")) return NX_ERROR; - if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'title' data")) return NX_ERROR; - - // notes - if (!ErrorHandler(NXopendata(fFileHandle, "notes"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'notes' data!")) return NX_ERROR; - if (!ErrorHandler(GetStringData(fNotes), PNEXUS_GET_DATA_ERROR, "couldn't read 'notes' data!")) return NX_ERROR; - if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'notes' data")) return NX_ERROR; - - // analysis tag - if (!ErrorHandler(NXopendata(fFileHandle, "analysis"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'analysis' data!")) return NX_ERROR; - if (!ErrorHandler(GetStringData(fAnalysisTag), PNEXUS_GET_DATA_ERROR, "couldn't read 'analysis' data!")) return NX_ERROR; - if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'analysis' data")) return NX_ERROR; - - // lab - if (!ErrorHandler(NXopendata(fFileHandle, "lab"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'lab' data!")) return NX_ERROR; - if (!ErrorHandler(GetStringData(fLab), PNEXUS_GET_DATA_ERROR, "couldn't read 'lab' data!")) return NX_ERROR; - if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'lab' data")) return NX_ERROR; - - // beamline - if (!ErrorHandler(NXopendata(fFileHandle, "beamline"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'beamline' data!")) return NX_ERROR; - if (!ErrorHandler(GetStringData(fBeamLine), PNEXUS_GET_DATA_ERROR, "couldn't read 'beamline' data!")) return NX_ERROR; - if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'beamline' data")) return NX_ERROR; - - // start time - if (!ErrorHandler(NXopendata(fFileHandle, "start_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'start_time' data!")) return NX_ERROR; - if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'start_time' data!")) return NX_ERROR; - if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'start_time' data")) return NX_ERROR; - size_t pos, pos1, pos2; - pos1 = str.find("T"); - pos2 = str.find(" "); - if ((pos1 == string::npos) && (pos2 == string::npos)) { - fErrorCode = PNEXUS_GET_DATA_ERROR; - fErrorMsg = "PNeXus::ReadFile() **ERROR** Wrong start date-time found (" + str + ")!"; - return NX_ERROR; - } - if (pos1 != string::npos) - pos = pos1; - else - pos = pos2; - fStartDate = str.substr(0, pos); - fStartTime = str.substr(pos+1, str.length()); - - // stop time - if (!ErrorHandler(NXopendata(fFileHandle, "stop_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'start_time' data!")) return NX_ERROR; - if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'start_time' data!")) return NX_ERROR; - if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'start_time' data")) return NX_ERROR; - pos1 = str.find("T"); - pos2 = str.find(" "); - if ((pos1 == string::npos) && (pos2 == string::npos)) { - fErrorCode = PNEXUS_GET_DATA_ERROR; - fErrorMsg = "PNeXus::ReadFile() **ERROR** Wrong stop date-time found (" + str + ")!"; - return NX_ERROR; - } - if (pos1 != string::npos) - pos = pos1; - else - pos = pos2; - fStopDate = str.substr(0, pos); - fStopTime = str.substr(pos+1, str.length()); - - // switching state (red/green mode) - if (!ErrorHandler(NXopendata(fFileHandle, "switching_states"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'switching_states' data!")) return NX_ERROR; - if (!ErrorHandler(NXgetdata(fFileHandle, &fSwitchingState), PNEXUS_GET_DATA_ERROR, "couldn't read 'switching_states' data!")) return NX_ERROR; - if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'switching_states' data")) return NX_ERROR; - - // open subgroup NXuser - if (!ErrorHandler(NXopengroup(fFileHandle, "user", "NXuser"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup user!")) return NX_ERROR; - - // change to subgroup - if (!ErrorHandler(NXinitgroupdir(fFileHandle), PNEXUS_INIT_GROUPDIR_ERROR, "couldn't init group directory")) return NX_ERROR; - int numItems = 0; - NXname groupName, className; - if (!ErrorHandler(NXgetgroupinfo(fFileHandle, &numItems, groupName, className), PNEXUS_GET_GROUP_INFO_ERROR, "couldn't get user group info")) return NX_ERROR; - - // get all the user info - // initialize the user data set - fUser.fUserName = ""; - fUser.fExperimentNumber = ""; - // go through the user list and filter out the required items - NXname nx_label; - int nx_dataType; - for (int i=0; i("available"), &fSample.fMagneticFieldVectorAvailable, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'magnetic_field_vector available' data in sample group!")) return NX_ERROR; - if (fSample.fMagneticFieldVectorAvailable) { - if (!ErrorHandler(GetDoubleVectorData(fSample.fMagneticFieldVector), PNEXUS_GET_DATA_ERROR, "couldn't get 'magnetic_field_vector' data!")) return NX_ERROR; - } - if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'magnetic_field_vector' data in sample group")) return NX_ERROR; - - // read sample environment, e.g. CCR, LowTemp-2, etc. - if (!ErrorHandler(NXopendata(fFileHandle, "environment"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'environment' data in sample group!")) return NX_ERROR; - if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'environment' data in sample group!")) return NX_ERROR; - if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'environment' data in sample group")) return NX_ERROR; - fSample.fEnvironment = str; - - // close subgroup NXsample - NXclosegroup(fFileHandle); - - // get required instrument information - // open subgroup NXinstrument with subgroups detector, collimator, beam - if (!ErrorHandler(NXopengroup(fFileHandle, "instrument", "NXinstrument"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup instrument!")) return NX_ERROR; - - // get instrument name - if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'name' data in instrument group!")) return NX_ERROR; - if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'name' data in instrument group!")) return NX_ERROR; - if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'name' data in instrument group")) return NX_ERROR; - fInstrument.name = str; - - // open subgroup NXdetector - if (!ErrorHandler(NXopengroup(fFileHandle, "detector", "NXdetector"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup detector!")) return NX_ERROR; - - // get number of detectors - if (!ErrorHandler(NXopendata(fFileHandle, "number"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'number' data in detector group!")) return NX_ERROR; - if (!ErrorHandler(NXgetdata(fFileHandle, &ival), PNEXUS_GET_DATA_ERROR, "couldn't read 'number' data in detector group!")) return NX_ERROR; - if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'number' data in detector group")) return NX_ERROR; - fInstrument.detector.number = ival; - - // close subgroup NXdetector - NXclosegroup(fFileHandle); - - // open subgroup NXcollimator - if (!ErrorHandler(NXopengroup(fFileHandle, "collimator", "NXcollimator"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup collimator!")) return NX_ERROR; - - // get collimator type - if (!ErrorHandler(NXopendata(fFileHandle, "type"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'type' data in collimator group!")) return NX_ERROR; - if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'type' data in collimator group!")) return NX_ERROR; - if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'type' data in collimator group")) return NX_ERROR; - fInstrument.collimator.type = str; - - // close subgroup NXcollimator - NXclosegroup(fFileHandle); - - // open subgroup NXbeam - if (!ErrorHandler(NXopengroup(fFileHandle, "beam", "NXbeam"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup beam!")) return NX_ERROR; - - // get the total counts - if (!ErrorHandler(NXopendata(fFileHandle, "total_counts"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'total_counts' data in beam group!")) return NX_ERROR; - if (!ErrorHandler(NXgetdata(fFileHandle, &fval), PNEXUS_GET_DATA_ERROR, "couldn't read 'total_counts' data in beam group!")) return NX_ERROR; - fInstrument.beam.total_counts = (double)fval; - if (!ErrorHandler(GetStringAttr("units", str), PNEXUS_GET_ATTR_ERROR, "couldn't read total_counts units!")) return NX_ERROR; - fInstrument.beam.total_counts_units = str; - if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'total_counts' data in beam group")) return NX_ERROR; - - // close subgroup NXbeam - NXclosegroup(fFileHandle); - - // close subgroup NXinstrument - NXclosegroup(fFileHandle); - - // open subgroup NXdata (this is for Version 1, only and is subject to change in the near future!) - if (!ErrorHandler(NXopengroup(fFileHandle, "histogram_data_1", "NXdata"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup histogram_data_1!")) return NX_ERROR; - - // get time resolution - if (!ErrorHandler(NXopendata(fFileHandle, "resolution"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'resolution' data in histogram_data_1 group!")) return NX_ERROR; - if (!ErrorHandler(NXgetdata(fFileHandle, &ival), PNEXUS_GET_DATA_ERROR, "couldn't read 'resolution' data in histogram_data_1 group!")) return NX_ERROR; - if (!ErrorHandler(GetStringAttr("units", str), PNEXUS_GET_ATTR_ERROR, "couldn't read 'resolution'' units!")) return NX_ERROR; - if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'resolution' data in histogram_data_1 group")) return NX_ERROR; - if (!strcmp(str.data(), "picoseconds")) { - fData.fTimeResolution = (double)ival/1000.0; // time resolution in (ns) - } - - // get data, t0, first good bin, last good bin, data - if (!ErrorHandler(NXopendata(fFileHandle, "counts"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'counts' data in histogram_data_1 group!")) return NX_ERROR; - int histoLength=0; - - // get number of histos - attLen = SIZE_INT32; - attType = NX_INT32; - if (!ErrorHandler(NXgetattr(fFileHandle, const_cast("number"), &fData.fNumberOfHistos, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'number' data in histogram_data_1 group!")) return NX_ERROR; - - // get histo length - attLen = SIZE_INT32; - attType = NX_INT32; - if (!ErrorHandler(NXgetattr(fFileHandle, const_cast("length"), &histoLength, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'number' data in histogram_data_1 group!")) return NX_ERROR; - - // get t0 - attLen = SIZE_INT32; - attType = NX_INT32; - if (!ErrorHandler(NXgetattr(fFileHandle, const_cast("t0_bin"), &ival, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 't0_bin' data in histogram_data_1 group!")) return NX_ERROR; - fData.fT0.push_back(ival); - - // get first good bin - attLen = SIZE_INT32; - attType = NX_INT32; - if (!ErrorHandler(NXgetattr(fFileHandle, const_cast("first_good_bin"), &ival, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'first_good_bin' data in histogram_data_1 group!")) return NX_ERROR; - fData.fFirstGoodBin.push_back(ival); - - // get last good bin - attLen = SIZE_INT32; - attType = NX_INT32; - if (!ErrorHandler(NXgetattr(fFileHandle, const_cast("last_good_bin"), &ival, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'last_good_bin' data in histogram_data_1 group!")) return NX_ERROR; - fData.fLastGoodBin.push_back(ival); - - // get data - - // get information of the current nexus entity - int rank, type, dims[32], size, noOfElements; - if (!ErrorHandler(NXgetinfo(fFileHandle, &rank, dims, &type), PNEXUS_GET_META_INFO_ERROR, "couldn't get data info!")) return NX_ERROR; - - // calculate the needed size - size = dims[0]; - for (int i=1; i data; - for (int i=0; i0)) { - fData.fHisto.push_back(data); - data.clear(); - data.push_back(*(i_data_ptr+i)); - } else { - data.push_back(*(i_data_ptr+i)); - } - } - fData.fHisto.push_back(data); - data.clear(); - - // clean up - if (data_ptr) { - delete [] data_ptr; - } - - if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'counts' data in histogram_data_1 group")) return NX_ERROR; - - // get grouping - if (!ErrorHandler(NXopendata(fFileHandle, "grouping"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'grouping' data in histogram_data_1 group!")) return NX_ERROR; - attLen = SIZE_INT32; - attType = NX_INT32; - if (!ErrorHandler(NXgetattr(fFileHandle, const_cast("available"), &ival, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'grouping available' data in histogram_data_1 group!")) return NX_ERROR; - if (ival) { - vector grouping; - if (!ErrorHandler(GetIntVectorData(grouping), PNEXUS_GET_DATA_ERROR, "couldn't read 'grouping' data in histogram_data_1 group!")) return NX_ERROR; - for (unsigned int i=0; i("available"), &ival, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'alpha available' data in histogram_data_1 group!")) return NX_ERROR; - if (ival) { - if (!ErrorHandler(NXgetinfo(fFileHandle, &rank, dims, &type), PNEXUS_GET_META_INFO_ERROR, "couldn't get alpha info!")) return NX_ERROR; - // calculate the needed size - size = dims[0]; - for (int i=1; i> **ERROR** idf_version=" << fIdfVersion << " not yet implemented."; + fErrorMsg = ss.str(); + status = NX_ERROR; + break; + } - fIsValid = true; - - return true; + return status; } -//----------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------ // WriteFile (public) -//----------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------ /** - *

+ *

Write the NeXus file 'fileName', with the 'fileType' (HDF4 | HDF5 | XML) and the instrument + * definition (1 | 2). * * return: - * - NX_OK on successfull writing + * - NX_OK on successfull reading * - NX_ERROR on error. The error code/message will give the details. * - * \param fileName - * \param fileType, allowed types are: hdf4, hdf5, xml + * \param fileName file name of the nexus file to be written + * \param fileType = HDF4 | HDF5 | XML + * \param idf = 1 or 2 */ -int PNeXus::WriteFile(const char *fileName, const char *fileType) +int PNeXus::WriteFile(const char *fileName, const char *fileType, const unsigned int idf) { - char str[256]; - int size, idata; - float fdata; + if (!IsValid()) { + return NX_ERROR; + } + + int status = NX_OK; NXaccess access=NXACC_CREATE4; if (!strcmp(fileType, "hdf4")) @@ -568,810 +1757,312 @@ int PNeXus::WriteFile(const char *fileName, const char *fileType) else access=NXACC_CREATE4; - sprintf(str, "couldn't open file '%s' for writing", fileName); - if (!ErrorHandler(NXopen(fileName, access, &fFileHandle), PNEXUS_FILE_OPEN_ERROR, str)) return NX_ERROR; - - // write NXfile attributes (NeXus_version, user) - strncpy(str, fUser.fUserName.data(), sizeof(str)); - if (!ErrorHandler(NXputattr(fFileHandle, "user", str, strlen(str), NX_CHAR), PNEXUS_SET_ATTR_ERROR, "couldn't set NXfile attributes")) return NX_ERROR; - - // make group 'run' - if (!ErrorHandler(NXmakegroup(fFileHandle, "run", "NXentry"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'run'.")) return NX_ERROR; - // open group 'run' - if (!ErrorHandler(NXopengroup(fFileHandle, "run", "NXentry"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'run' for writting.")) return NX_ERROR; - - // write IDF_version - size = 1; - if (!ErrorHandler(NXmakedata(fFileHandle, "IDF_version", NX_INT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'IDF_version'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "IDF_version"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'IDF_version' for writting.")) return NX_ERROR; - idata = fIDFVersion; - if (!ErrorHandler(NXputdata(fFileHandle, &idata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'IDF_version'.")) return NX_ERROR; - NXclosedata(fFileHandle); - - // write program_name, and attribute version - size = fProgramName.length(); - if (!ErrorHandler(NXmakedata(fFileHandle, "program_name", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'program_name'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "program_name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'program_name' for writting.")) return NX_ERROR; - strncpy(str, fProgramName.data(), sizeof(str)); - if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'program_name'.")) return NX_ERROR; - strncpy(str, fProgramVersion.data(), sizeof(str)); - if (!ErrorHandler(NXputattr(fFileHandle, "version", str, strlen(str), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'version' for 'program_name'")) return NX_ERROR; - NXclosedata(fFileHandle); - - // write run 'number' - size = 1; - if (!ErrorHandler(NXmakedata(fFileHandle, "number", NX_INT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'number'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "number"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'number' for writting.")) return NX_ERROR; - idata = fRunNumber; - if (!ErrorHandler(NXputdata(fFileHandle, &idata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'number'.")) return NX_ERROR; - NXclosedata(fFileHandle); - - // write run 'title' - size = fRunTitle.length(); - if (!ErrorHandler(NXmakedata(fFileHandle, "title", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'title'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "title"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'title' for writting.")) return NX_ERROR; - strncpy(str, fRunTitle.data(), sizeof(str)); - if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'title'.")) return NX_ERROR; - NXclosedata(fFileHandle); - - // write run 'notes' - size = fNotes.length(); - if (!ErrorHandler(NXmakedata(fFileHandle, "notes", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'notes'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "notes"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'notes' for writting.")) return NX_ERROR; - strncpy(str, fNotes.data(), sizeof(str)); - if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'notes'.")) return NX_ERROR; - NXclosedata(fFileHandle); - - // write run 'analysis' - size = fAnalysisTag.length(); - if (!ErrorHandler(NXmakedata(fFileHandle, "analysis", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'analysis'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "analysis"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'analysis' for writting.")) return NX_ERROR; - strncpy(str, fAnalysisTag.data(), sizeof(str)); - if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'analysis'.")) return NX_ERROR; - NXclosedata(fFileHandle); - - // write run 'lab' - size = fLab.length(); - if (!ErrorHandler(NXmakedata(fFileHandle, "lab", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'lab'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "lab"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'lab' for writting.")) return NX_ERROR; - strncpy(str, fLab.data(), sizeof(str)); - if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'lab'.")) return NX_ERROR; - NXclosedata(fFileHandle); - - // write run 'beamline' - size = fBeamLine.length(); - if (!ErrorHandler(NXmakedata(fFileHandle, "beamline", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'beamline'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "beamline"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'beamline' for writting.")) return NX_ERROR; - strncpy(str, fBeamLine.data(), sizeof(str)); - if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'beamline'.")) return NX_ERROR; - NXclosedata(fFileHandle); - - // write run 'start_time' - snprintf(str, sizeof(str), "%s %s", fStartDate.data(), fStartTime.data()); - size = strlen(str); - if (!ErrorHandler(NXmakedata(fFileHandle, "start_time", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'start_time'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "start_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'start_time' for writting.")) return NX_ERROR; - if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'start_time'.")) return NX_ERROR; - NXclosedata(fFileHandle); - - // write run 'end_time' - snprintf(str, sizeof(str), "%s %s", fStopDate.data(), fStopTime.data()); - size = strlen(str); - if (!ErrorHandler(NXmakedata(fFileHandle, "end_time", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'end_time'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "end_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'end_time' for writting.")) return NX_ERROR; - if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'end_time'.")) return NX_ERROR; - NXclosedata(fFileHandle); - - // write run 'switching_states' - size = 1; - if (!ErrorHandler(NXmakedata(fFileHandle, "switching_states", NX_INT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'switching_states'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "switching_states"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'switching_states' for writting.")) return NX_ERROR; - idata = fSwitchingState; - if (!ErrorHandler(NXputdata(fFileHandle, &idata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'switching_states'.")) return NX_ERROR; - NXclosedata(fFileHandle); - - - // make group 'user' - if (!ErrorHandler(NXmakegroup(fFileHandle, "user", "NXuser"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'user'.")) return NX_ERROR; - // open group 'user' - if (!ErrorHandler(NXopengroup(fFileHandle, "user", "NXuser"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'user' for writting.")) return NX_ERROR; - - // write user 'name' - size = fUser.fUserName.length(); - if (!ErrorHandler(NXmakedata(fFileHandle, "name", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'name'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'name' for writting.")) return NX_ERROR; - strncpy(str, fUser.fUserName.data(), sizeof(str)); - if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'name'.")) return NX_ERROR; - NXclosedata(fFileHandle); - - // write user 'experiment_number' - size = fUser.fExperimentNumber.length(); - if (!ErrorHandler(NXmakedata(fFileHandle, "experiment_number", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'experiment_number'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "experiment_number"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'experiment_number' for writting.")) return NX_ERROR; - strncpy(str, fUser.fExperimentNumber.data(), sizeof(str)); - if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'experiment_number'.")) return NX_ERROR; - NXclosedata(fFileHandle); - - // close group 'user' - NXclosegroup(fFileHandle); - - // make group 'sample' - if (!ErrorHandler(NXmakegroup(fFileHandle, "sample", "NXuser"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'sample'.")) return NX_ERROR; - // open group 'sample' - if (!ErrorHandler(NXopengroup(fFileHandle, "sample", "NXuser"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'sample' for writting.")) return NX_ERROR; - - // write sample 'name' - size = fSample.fName.length(); - if (!ErrorHandler(NXmakedata(fFileHandle, "name", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'name'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'name' for writting.")) return NX_ERROR; - strncpy(str, fSample.fName.data(), sizeof(str)); - if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'name'.")) return NX_ERROR; - NXclosedata(fFileHandle); - - // write sample 'temperature' - size = 1; - if (!ErrorHandler(NXmakedata(fFileHandle, "temperature", NX_FLOAT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'temperature'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "temperature"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'temperature' for writting.")) return NX_ERROR; - fdata = fSample.fTemperature; - if (!ErrorHandler(NXputdata(fFileHandle, &fdata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'temperature'.")) return NX_ERROR; - strncpy(str, fSample.fTemperatureUnit.data(), sizeof(str)); - if (!ErrorHandler(NXputattr(fFileHandle, "units", str, strlen(str), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'temperature'")) return NX_ERROR; - NXclosedata(fFileHandle); - - // write sample 'magnetic_field' - size = 1; - if (!ErrorHandler(NXmakedata(fFileHandle, "magnetic_field", NX_FLOAT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'magnetic_field'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "magnetic_field"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'magnetic_field' for writting.")) return NX_ERROR; - fdata = fSample.fMagneticField; - if (!ErrorHandler(NXputdata(fFileHandle, &fdata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'magnetic_field'.")) return NX_ERROR; - strncpy(str, fSample.fMagneticFieldUnit.data(), sizeof(str)); - if (!ErrorHandler(NXputattr(fFileHandle, "units", str, strlen(str), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'magnetic_field'")) return NX_ERROR; - NXclosedata(fFileHandle); - - // write sample 'shape' - size = fSample.fShape.length(); - if (!ErrorHandler(NXmakedata(fFileHandle, "shape", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'shape'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "shape"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'shape' for writting.")) return NX_ERROR; - strncpy(str, fSample.fShape.data(), sizeof(str)); - if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'shape'.")) return NX_ERROR; - NXclosedata(fFileHandle); - - // write sample 'magnetic_field_state' - size = fSample.fMagneticFieldState.length(); - if (!ErrorHandler(NXmakedata(fFileHandle, "magnetic_field_state", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'magnetic_field_state'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "magnetic_field_state"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'magnetic_field_state' for writting.")) return NX_ERROR; - strncpy(str, fSample.fMagneticFieldState.data(), sizeof(str)); - if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'magnetic_field_state'.")) return NX_ERROR; - NXclosedata(fFileHandle); - - // write sample 'magnetic_field_vector' - // !! TO BE DONE !! - - // write sample 'environment' - size = fSample.fEnvironment.length(); - if (!ErrorHandler(NXmakedata(fFileHandle, "environment", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'environment'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "environment"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'environment' for writting.")) return NX_ERROR; - strncpy(str, fSample.fEnvironment.data(), sizeof(str)); - if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'environment'.")) return NX_ERROR; - NXclosedata(fFileHandle); - - // close group 'sample' - NXclosegroup(fFileHandle); - - // make group 'instrument' - if (!ErrorHandler(NXmakegroup(fFileHandle, "instrument", "NXinstrument"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'instrument'.")) return NX_ERROR; - // open group 'instrument' - if (!ErrorHandler(NXopengroup(fFileHandle, "instrument", "NXinstrument"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'instrument' for writting.")) return NX_ERROR; - - // write instrument 'name' - size = fInstrument.name.length(); - if (!ErrorHandler(NXmakedata(fFileHandle, "name", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'name'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'name' for writting.")) return NX_ERROR; - strncpy(str, fInstrument.name.data(), sizeof(str)); - if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'name'.")) return NX_ERROR; - NXclosedata(fFileHandle); - - // make group 'detector' - if (!ErrorHandler(NXmakegroup(fFileHandle, "detector", "NXdetector"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'detector'.")) return NX_ERROR; - // open group 'detector' - if (!ErrorHandler(NXopengroup(fFileHandle, "detector", "NXdetector"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'detector' for writting.")) return NX_ERROR; - - // write detector 'number' - size = 1; - if (!ErrorHandler(NXmakedata(fFileHandle, "number", NX_INT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'number'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "number"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'number' for writting.")) return NX_ERROR; - idata = fInstrument.detector.number; - if (!ErrorHandler(NXputdata(fFileHandle, &idata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'number'.")) return NX_ERROR; - NXclosedata(fFileHandle); - - // close group 'detector' - NXclosegroup(fFileHandle); - - // make group 'collimator' - if (!ErrorHandler(NXmakegroup(fFileHandle, "collimator", "NXcollimator"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'collimator'.")) return NX_ERROR; - // open group 'collimator' - if (!ErrorHandler(NXopengroup(fFileHandle, "collimator", "NXcollimator"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'collimator' for writting.")) return NX_ERROR; - - // write collimator 'type' - size = fInstrument.collimator.type.length(); - if (!ErrorHandler(NXmakedata(fFileHandle, "type", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'type'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "type"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'type' for writting.")) return NX_ERROR; - strncpy(str, fInstrument.collimator.type.data(), sizeof(str)); - if (!ErrorHandler(NXputdata(fFileHandle, str), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'type'.")) return NX_ERROR; - NXclosedata(fFileHandle); - - // close group 'collimator' - NXclosegroup(fFileHandle); - - // make group 'beam' - if (!ErrorHandler(NXmakegroup(fFileHandle, "beam", "NXbeam"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'beam'.")) return NX_ERROR; - // open group 'beam' - if (!ErrorHandler(NXopengroup(fFileHandle, "beam", "NXbeam"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'beam' for writting.")) return NX_ERROR; - - size = 1; - if (!ErrorHandler(NXmakedata(fFileHandle, "total_counts", NX_FLOAT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'total_counts'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "total_counts"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'total_counts' for writting.")) return NX_ERROR; - fdata = fInstrument.beam.total_counts; - if (!ErrorHandler(NXputdata(fFileHandle, &fdata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'total_counts'.")) return NX_ERROR; - strncpy(str, fInstrument.beam.total_counts_units.data(), sizeof(str)); - if (!ErrorHandler(NXputattr(fFileHandle, "units", str, strlen(str), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'total_counts'")) return NX_ERROR; - NXclosedata(fFileHandle); - - // close group 'beam' - NXclosegroup(fFileHandle); - - // close group 'instrument' - NXclosegroup(fFileHandle); - - // make group 'histogram_data_1' - if (!ErrorHandler(NXmakegroup(fFileHandle, "histogram_data_1", "NXdata"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'histogram_data_1'.")) return NX_ERROR; - // open group 'histogram_data_1' - if (!ErrorHandler(NXopengroup(fFileHandle, "histogram_data_1", "NXdata"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'histogram_data_1' for writting.")) return NX_ERROR; - - // write data 'counts' - int *histo_data=0; - int histo_size[2]; - if (fData.fHisto.size() == 0) { - histo_data = new int[1]; - histo_data[0] = -1; - histo_size[0] = 1; - histo_size[1] = 1; - } else { - histo_data = new int[fData.fHisto.size()*fData.fHisto[0].size()]; - for (unsigned int i=0; i ps - if (!ErrorHandler(NXputdata(fFileHandle, &fdata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'histogram_resolution'.")) return NX_ERROR; - strncpy(str, "picoseconds", sizeof(str)); - if (!ErrorHandler(NXputattr(fFileHandle, "units", str, strlen(str), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'histogram_resolution'")) return NX_ERROR; - NXclosedata(fFileHandle); - - // write data 'time_zero' always set to 0 since the T0_bin attribute of counts is relevant only - if (!ErrorHandler(NXmakedata(fFileHandle, "time_zero", NX_FLOAT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'time_zero'.")) return NX_ERROR; - if (!ErrorHandler(NXopendata(fFileHandle, "time_zero"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'time_zero' for writting.")) return NX_ERROR; - fdata = 0.0; - if (!ErrorHandler(NXputdata(fFileHandle, &fdata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'time_zero'.")) return NX_ERROR; - strncpy(str, "microseconds", sizeof(str)); - if (!ErrorHandler(NXputattr(fFileHandle, "units", str, strlen(str), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'time_zero'")) return NX_ERROR; - idata = 0; - if (!ErrorHandler(NXputattr(fFileHandle, "available", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'available' for 'time_zero'")) return NX_ERROR; - NXclosedata(fFileHandle); - - // write data 'raw_time' - if (fData.fHisto.size() == 0) { - fErrorCode = PNEXUS_HISTO_ERROR; - fErrorMsg = "no data for writing present."; - return NX_ERROR; - } - float *raw_time = new float[fData.fHisto[0].size()-1]; - for (unsigned int i=0; i groupNo; // keep the number of different groupings - if (fData.fHisto.size() == fData.fGrouping.size()) { // grouping vector seems to be properly defined - bool found; - groupNo.push_back(fData.fGrouping[0]); - for (unsigned int i=0; iDump read data to the standard output. + *

Write the content of the NeXus file to stdout. Used for debugging purposes. + * */ void PNeXus::Dump() { - cout << endl << "-----------------------------"; - cout << endl << " NeXus Dump"; - cout << endl << "-----------------------------"; - cout << endl; - cout << endl << "file name : " << fFileName; - cout << endl << "IDF version : " << fIDFVersion; - cout << endl << "program name : " << fProgramName << ", version : " << fProgramVersion; - cout << endl << "run number : " << fRunNumber; - cout << endl << "run title : " << fRunTitle; - cout << endl << "run notes : " << fNotes; - cout << endl << "analysis : " << fAnalysisTag; - cout << endl << "lab : " << fLab; - cout << endl << "beamline : " << fBeamLine; - cout << endl << "start date/time : " << fStartDate << "/" << fStartTime; - cout << endl << "stop date/time : " << fStopDate << "/" << fStopTime; - cout << endl << "switching state : " << fSwitchingState << " (red/green mode)"; - cout << endl << "-----------------------------"; - cout << endl << "user info"; - cout << endl << "user : " << fUser.fUserName; - cout << endl << "exp. number : " << fUser.fExperimentNumber; - cout << endl << "-----------------------------"; - cout << endl << "sample info"; - cout << endl << "sample name : " << fSample.fName; - cout << endl << "temperature : " << fSample.fTemperature << " (" << fSample.fTemperatureUnit << ")"; - cout << endl << "magnetic state : " << fSample.fMagneticFieldState; - cout << endl << "magnetic field : " << fSample.fMagneticField << " (" << fSample.fMagneticFieldUnit << ")"; - cout << endl << "mag. vector : "; - if (fSample.fMagneticFieldVectorAvailable) { - for (unsigned int i=0; iGetProgramName() << ", version: " << fNxEntry1->GetProgramVersion(); + cout << endl << " run number : " << fNxEntry1->GetRunNumber(); + cout << endl << " title : " << fNxEntry1->GetTitle(); + cout << endl << " notes : " << fNxEntry1->GetNotes(); + cout << endl << " analysis : " << fNxEntry1->GetAnalysis(); + cout << endl << " laboratory : " << fNxEntry1->GetLaboratory(); + cout << endl << " beamline : " << fNxEntry1->GetBeamline(); + cout << endl << " start time : " << fNxEntry1->GetStartTime(); + cout << endl << " stop time : " << fNxEntry1->GetStopTime(); + cout << endl << " switching state: " << fNxEntry1->GetSwitchingState() << ", '1' normal data collection, '2' Red/Green mode"; + cout << endl << "NXuser:"; + cout << endl << " name : " << fNxEntry1->GetUser()->GetName(); + cout << endl << " exp.number : " << fNxEntry1->GetUser()->GetExperimentNumber(); + cout << endl << "NXsample:"; + cout << endl << " name : " << fNxEntry1->GetSample()->GetName(); + dval = fNxEntry1->GetSample()->GetPhysPropValue("temperature", ok); + if (ok) + cout << endl << " temperature : " << dval; + fNxEntry1->GetSample()->GetPhysPropUnit("temperature", str, ok); + if (ok) + cout << " (" << str << ")"; + dval = fNxEntry1->GetSample()->GetPhysPropValue("magnetic_field", ok); + if (ok) + cout << endl << " magnetic_field : " << dval; + fNxEntry1->GetSample()->GetPhysPropUnit("magnetic_field", str, ok); + if (ok) + cout << " (" << str << ")"; + cout << endl << " shape : " << fNxEntry1->GetSample()->GetShape(); + cout << endl << " mag.field state: " << fNxEntry1->GetSample()->GetMagneticFieldState(); + cout << endl << " environment : " << fNxEntry1->GetSample()->GetEnvironment(); + if (fNxEntry1->GetSample()->IsMagneticFieldVectorAvailable()) { + cout << endl << " magnetic field vector: "; + for (unsigned int i=0; iGetSample()->GetMagneticFieldVector().size(); i++) { + cout << fNxEntry1->GetSample()->GetMagneticFieldVector().at(i) << ", "; + } + cout << "(" << fNxEntry1->GetSample()->GetMagneticFieldVectorUnits() << "), "; + cout << fNxEntry1->GetSample()->GetMagneticFieldVectorCoordinateSystem(); } - cout << "(" << fSample.fMagneticFieldVectorUnits << "), coord.system: " << fSample.fMagneticFieldVectorCoordinateSystem; - } else { - cout << "not available."; - } - cout << endl << "shape : " << fSample.fShape; - cout << endl << "environment : " << fSample.fEnvironment; - cout << endl << "-----------------------------"; - cout << endl << "instrument info"; - cout << endl << "instrument name : " << fInstrument.name; - cout << endl << " detector info"; - cout << endl << " detector number : " << fInstrument.detector.number; - cout << endl << " -----------------------------"; - cout << endl << " collimator info"; - cout << endl << " collimator type : " << fInstrument.collimator.type; - cout << endl << " -----------------------------"; - cout << endl << " beam info"; - cout << endl << " beam total number of counts : " << fInstrument.beam.total_counts << " " << fInstrument.beam.total_counts_units; - cout << endl << "-----------------------------"; - cout << endl << "run data"; - cout << endl << "time resolution : " << fData.fTimeResolution << " (ns)"; - cout << endl << "number of histos: " << fData.fNumberOfHistos; - if (fData.fAlpha.size() != 0) { - cout << endl << "alpha : "; - for (unsigned int i=0; iGetInstrument()->GetName(); + cout << endl << " NXdetector:"; + cout << endl << " number of detectors: " << fNxEntry1->GetInstrument()->GetDetector()->GetNumber(); + cout << endl << " NXcollimator:"; + cout << endl << " type : " << fNxEntry1->GetInstrument()->GetCollimator()->GetType(); + cout << endl << " Nxbeam:"; + cout << endl << " total_counts : " << fNxEntry1->GetInstrument()->GetBeam()->GetTotalCounts() << " (" << fNxEntry1->GetInstrument()->GetBeam()->GetUnits() << ")"; + cout << endl << "NXdata:"; + cout << endl << " number of histos : " << fNxEntry1->GetData()->GetNoOfHistos(); + cout << endl << " time resolution : " << fNxEntry1->GetData()->GetTimeResolution("ns") << " (ns)"; + if (fNxEntry1->GetData()->GetGrouping()->size() != 0) { + cout << endl << " grouping : "; + for (unsigned int i=0; iGetData()->GetGrouping()->size(); i++) { + cout << "(" << i << "/" << fNxEntry1->GetData()->GetGrouping()->at(i) << "), "; + } + } else { + cout << endl << " grouping : not available"; } - } else { - cout << endl << "alpha : not available"; - } - if (fData.fGrouping.size() != 0) { - cout << endl << "grouping : "; - for (unsigned int i=0; iGetData()->GetT0s()->size() == 0) { + cout << endl << " t0's : not available"; + } else { + cout << endl << " t0's : "; + for (unsigned int i=0; iGetData()->GetT0s()->size(); i++) { + cout << fNxEntry1->GetData()->GetT0(i) << ", "; + } } - } else { - cout << endl << "grouping : not available"; - } - cout << endl << "t0 : " << fData.fT0[0]; - cout << endl << "first good bin : " << fData.fFirstGoodBin[0]; - cout << endl << "last good bin : " << fData.fLastGoodBin[0]; - cout << endl << "histograms : +++++++++++"; - for (unsigned int i=0; iGetData()->GetFirstGoodBins()->size() == 0) { + cout << endl << " first good bins : not available"; + } else { + cout << endl << " first good bins : "; + for (unsigned int i=0; iGetData()->GetFirstGoodBins()->size(); i++) { + cout << fNxEntry1->GetData()->GetFirstGoodBin(i) << ", "; + } } - cout << "..."; - } - cout << endl << "-----------------------------"; - cout << endl << "grouped histograms : ++++++++"; - for (unsigned int i=0; iGetData()->GetLastGoodBins()->size() == 0) { + cout << endl << " last good bins : not available"; + } else { + cout << endl << " last good bins : "; + for (unsigned int i=0; iGetData()->GetLastGoodBins()->size(); i++) { + cout << fNxEntry1->GetData()->GetLastGoodBin(i) << ", "; + } } - cout << "..."; + if (fNxEntry1->GetData()->GetNoOfHistos() == 0) { + cout << endl << " historgrams : not available"; + } else { + cout << endl << " historgrams : +++++++++++++"; + for (unsigned int i=0; iGetData()->GetNoOfHistos(); i++) { + cout << endl << " histo " << i+1 << ": "; + for (unsigned int j=0; j<15; j++) { + cout << fNxEntry1->GetData()->GetHisto(i)->at(j) << ", "; + } + cout << "..."; + } + } + if (fNxEntry1->GetData()->GetAlpha()->size() == 0) { + cout << endl << " alpha : not available"; + } else { + cout << endl << " alpha : "; + for (unsigned int i=0; iGetData()->GetAlpha()->size(); i++) + cout << "(" << fNxEntry1->GetData()->GetAlpha()->at(i).GetGroupFirst() << "/" << fNxEntry1->GetData()->GetAlpha()->at(i).GetGroupSecond() << "/" << fNxEntry1->GetData()->GetAlpha()->at(i).GetAlpha() << "), "; + } + if (fGroupedHisto.size() == 0) { + cout << endl << " grouped historgrams : not available"; + } else { + cout << endl << " grouped historgrams : +++++++++++++"; + for (unsigned int i=0; iGetDefinition(); + cout << endl << " program name : " << fNxEntry2->GetProgramName() << ", version: " << fNxEntry2->GetProgramVersion(); + cout << endl << " run_number : " << fNxEntry2->GetRunNumber(); + cout << endl << " run_title : " << fNxEntry2->GetTitle(); + cout << endl << " start_time : " << fNxEntry2->GetStartTime(); + cout << endl << " end_time : " << fNxEntry2->GetStopTime(); + cout << endl << " exp.identifier : " << fNxEntry2->GetExperimentIdentifier(); + cout << endl << " NXuser:"; + cout << endl << " name : " << fNxEntry2->GetUser()->GetName(); + cout << endl << " NXsample:"; + cout << endl << " name : " << fNxEntry2->GetSample()->GetName(); + cout << endl << " description : " << fNxEntry2->GetSample()->GetDescription(); + cout << endl << " mag.field state : " << fNxEntry2->GetSample()->GetMagneticFieldState(); + dval = fNxEntry2->GetSample()->GetPhysPropValue("temperature", ok); + if (ok) + cout << endl << " temperature : " << dval; + fNxEntry2->GetSample()->GetPhysPropUnit("temperature", str, ok); + if (ok) + cout << " (" << str << ")"; + cout << endl << " temp.environment : " << fNxEntry2->GetSample()->GetEnvironmentTemp(); + dval = fNxEntry2->GetSample()->GetPhysPropValue("magnetic_field", ok); + if (ok) + cout << endl << " magnetic_field : " << dval; + fNxEntry2->GetSample()->GetPhysPropUnit("magnetic_field", str, ok); + if (ok) + cout << " (" << str << ")"; + cout << endl << " mag. field env. : " << fNxEntry2->GetSample()->GetEnvironmentField(); + cout << endl << " NXinstrument:"; + cout << endl << " name : " << fNxEntry2->GetInstrument()->GetName(); + cout << endl << " NXsource:"; + cout << endl << " name : " << fNxEntry2->GetInstrument()->GetSource()->GetName(); + cout << endl << " type : " << fNxEntry2->GetInstrument()->GetSource()->GetType(); + cout << endl << " probe : " << fNxEntry2->GetInstrument()->GetSource()->GetProbe(); + cout << endl << " NXbeamline:"; + cout << endl << " name : " << fNxEntry2->GetInstrument()->GetBeamline()->GetName(); + cout << endl << " NXdetector:"; + cout << endl << " description : " << fNxEntry2->GetInstrument()->GetDetector()->GetDescription(); + cout << endl << " time resolution : " << fNxEntry2->GetInstrument()->GetDetector()->GetTimeResolution("ns") << " (ns)"; + if (fNxEntry2->GetInstrument()->GetDetector()->IsT0Present()) { + if (fNxEntry2->GetInstrument()->GetDetector()->GetT0Tag() == 1) { // only one t0 for all histograms + cout << endl << " t0 : " << fNxEntry2->GetInstrument()->GetDetector()->GetT0(); + } else { // t0 vector of the form t0[np][ns] + cout << endl << " t0 (idx_p/idx_s/t0): "; + for (int i=0; iGetInstrument()->GetDetector()->GetNoOfPeriods(); i++) { + for (int j=0; jGetInstrument()->GetDetector()->GetNoOfSpectra(); j++) { + cout << "(" << i << "/" << j << "/" << fNxEntry2->GetInstrument()->GetDetector()->GetT0(i,j); + } + } + } + } else { + cout << endl << " t0 : n/a"; + } + if (fNxEntry2->GetInstrument()->GetDetector()->IsFirstGoodBinPresent()) { + if (fNxEntry2->GetInstrument()->GetDetector()->GetT0Tag() == 1) { // only one t0/fgb/lgb for all histograms + cout << endl << " first good bin : " << fNxEntry2->GetInstrument()->GetDetector()->GetFirstGoodBin(); + } else { // fgb vector of the form fgb[np][ns] + cout << endl << " fgb (idx_p/idx_s/fgb): "; + for (int i=0; iGetInstrument()->GetDetector()->GetNoOfPeriods(); i++) { + for (int j=0; jGetInstrument()->GetDetector()->GetNoOfSpectra(); j++) { + cout << "(" << i << "/" << j << "/" << fNxEntry2->GetInstrument()->GetDetector()->GetFirstGoodBin(i,j); + } + } + } + } else { + cout << endl << " first good bin : n/a"; + } + if (fNxEntry2->GetInstrument()->GetDetector()->IsFirstGoodBinPresent()) { + if (fNxEntry2->GetInstrument()->GetDetector()->GetT0Tag() == 1) { // only one t0/fgb/lgb for all histograms + cout << endl << " last good bin : " << fNxEntry2->GetInstrument()->GetDetector()->GetLastGoodBin(); + } else { // lgb vector of the form lgb[np][ns] + cout << endl << " lgb (idx_p/idx_s/lgb): "; + for (int i=0; iGetInstrument()->GetDetector()->GetNoOfPeriods(); i++) { + for (int j=0; jGetInstrument()->GetDetector()->GetNoOfSpectra(); j++) { + cout << "(" << i << "/" << j << "/" << fNxEntry2->GetInstrument()->GetDetector()->GetLastGoodBin(i,j); + } + } + } + } else { + cout << endl << " last good bin : n/a"; + } + cout << endl << " spectrum_index : "; + for (unsigned int i=0; iGetInstrument()->GetDetector()->GetSpectrumIndexSize(); i++) + cout << fNxEntry2->GetInstrument()->GetDetector()->GetSpectrumIndex(i) << ", "; + + // dump data + int maxDump = 15; + cout << endl << " counts : "; + if (fNxEntry2->GetInstrument()->GetDetector()->GetNoOfPeriods() > 0) { // counts[np][ns][ntc] + for (int i=0; iGetInstrument()->GetDetector()->GetNoOfPeriods(); i++) { + cout << endl << " period : " << i+1; + for (int j=0; jGetInstrument()->GetDetector()->GetNoOfSpectra(); j++) { + cout << endl << " spectrum : " << j+1 << ", (#bins=" << fNxEntry2->GetInstrument()->GetDetector()->GetNoOfBins() << ")"; + if (maxDump > fNxEntry2->GetInstrument()->GetDetector()->GetNoOfBins()) + maxDump = fNxEntry2->GetInstrument()->GetDetector()->GetNoOfBins(); + cout << endl << " "; + for (int k=0; kGetInstrument()->GetDetector()->GetHistoValue(i,j,k) << ", "; + } + cout << "..."; + } + } + } else { + if (fNxEntry2->GetInstrument()->GetDetector()->GetNoOfSpectra() > 0) { // counts[ns][ntc] + for (int j=0; jGetInstrument()->GetDetector()->GetNoOfSpectra(); j++) { + cout << endl << " spectrum : " << j+1 << ", (#bins=" << fNxEntry2->GetInstrument()->GetDetector()->GetNoOfBins() << ")"; + if (maxDump > fNxEntry2->GetInstrument()->GetDetector()->GetNoOfBins()) + maxDump = fNxEntry2->GetInstrument()->GetDetector()->GetNoOfBins(); + cout << endl << " "; + for (int k=0; kGetInstrument()->GetDetector()->GetHistoValue(0,j,k) << ", "; + } + cout << "..."; + } + } else { // counts[ntc] + cout << endl << " (#bins=" << fNxEntry2->GetInstrument()->GetDetector()->GetNoOfBins() << ")"; + if (maxDump > fNxEntry2->GetInstrument()->GetDetector()->GetNoOfBins()) + maxDump = fNxEntry2->GetInstrument()->GetDetector()->GetNoOfBins(); + cout << endl << " "; + for (int k=0; kGetInstrument()->GetDetector()->GetHistoValue(0,0,k) << ", "; + } + cout << "..."; + } + } + cout << endl << "that's all!"; + cout << endl << endl; } - cout << endl << "-----------------------------"; - cout << endl << "done."; - cout << endl << endl; - } -//----------------------------------------------------------------------------------------------------- -// SetStartDate (public) -//----------------------------------------------------------------------------------------------------- -/** - *

The date should look like yyyy-mm-dd. - * - * return: - * - true on date string has proper format - * - false on error. The error code/message will give the details. - * - * \param date - */ -bool PNeXus::SetStartDate(const char *date) -{ - string strDate = date; - return SetStartDate(strDate); -} - -//----------------------------------------------------------------------------------------------------- -// SetStartDate (public) -//----------------------------------------------------------------------------------------------------- -/** - *

The date should look like yyyy-mm-dd. - * - * return: - * - true on date string has proper format - * - false on error. The error code/message will give the details. - * - * \param date - */ -bool PNeXus::SetStartDate(string date) -{ - bool ok=false; - string str = TransformDate(date, ok); - - if (!ok) - return false; - - fStartDate = str; - - return true; -} - -//----------------------------------------------------------------------------------------------------- -// SetStartTime (public) -//----------------------------------------------------------------------------------------------------- -/** - *

The time should look like hh:mm:ss. - * - * return: - * - true on time string has proper format - * - false on error. The error code/message will give the details. - * - * \param time - */ -bool PNeXus::SetStartTime(const char *time) -{ - string strTime = time; - return SetStartTime(strTime); -} - -//----------------------------------------------------------------------------------------------------- -// SetStartTime (public) -//----------------------------------------------------------------------------------------------------- -/** - *

The time should look like hh:mm:ss. - * - * return: - * - true on time string has proper format - * - false on error. The error code/message will give the details. - * - * \param time - */ -bool PNeXus::SetStartTime(string time) -{ - if (time.length() != 8) { - fErrorCode = PNEXUS_WRONG_TIME_FORMAT; - fErrorMsg = "PNeXus::SetStartTime **ERROR** given time="+time+", is not of the required form hh:mm:ss!"; - return false; - } - - if ((time[2] != ':') || (time[5] != ':')) { - fErrorCode = PNEXUS_WRONG_TIME_FORMAT; - fErrorMsg = "PNeXus::SetStartTime **ERROR** given time="+time+", is not of the required form hh:mm:ss!"; - return false; - } - - fStartTime = time; - - return true; -} - -//----------------------------------------------------------------------------------------------------- -// SetStopDate (public) -//----------------------------------------------------------------------------------------------------- -/** - *

- * - * return: - * - true on date string has proper format - * - false on error. The error code/message will give the details. - * - * \param date - */ -bool PNeXus::SetStopDate(const char *date) -{ - string strDate = date; - return SetStopDate(strDate); -} - -//----------------------------------------------------------------------------------------------------- -// SetStopDate (public) -//----------------------------------------------------------------------------------------------------- -/** - *

- * - * return: - * - true on date string has proper format - * - false on error. The error code/message will give the details. - * - * \param date - */ -bool PNeXus::SetStopDate(string date) -{ - bool ok=false; - string str = TransformDate(date, ok); - - if (!ok) - return false; - - fStopDate = str; - - return true; -} - -//----------------------------------------------------------------------------------------------------- -// SetStopTime (public) -//----------------------------------------------------------------------------------------------------- -/** - *

- * - * return: - * - true on time string has proper format - * - false on error. The error code/message will give the details. - * - * \param time - */ -bool PNeXus::SetStopTime(const char *time) -{ - string strTime = time; - return SetStopTime(strTime); -} - -//----------------------------------------------------------------------------------------------------- -// SetStopTime (public) -//----------------------------------------------------------------------------------------------------- -/** - *

- * - * return: - * - true on time string has proper format - * - false on error. The error code/message will give the details. - * - * \param time - */ -bool PNeXus::SetStopTime(string time) -{ - if (time.length() != 8) { - fErrorCode = PNEXUS_WRONG_TIME_FORMAT; - fErrorMsg = "PNeXus::SetStopTime **ERROR** given time="+time+", is not of the required form hh:mm:ss!"; - return false; - } - - if ((time[2] != ':') || (time[5] != ':')) { - fErrorCode = PNEXUS_WRONG_TIME_FORMAT; - fErrorMsg = "PNeXus::SetStopTime **ERROR** given time="+time+", is not of the required form hh:mm:ss!"; - return false; - } - - fStopTime = time; - - return true; -} - -//----------------------------------------------------------------------------------------------------- -// CleanUp (private) -//----------------------------------------------------------------------------------------------------- -/** - * - */ -void PNeXus::CleanUp() -{ - fData.fTimeResolution = 0.0; - fData.fNumberOfHistos = 0; - fData.fAlpha.clear(); - fData.fGrouping.clear(); - fData.fFirstGoodBin.clear(); - fData.fLastGoodBin.clear(); - fData.fT0.clear(); - fData.fHistoName.clear(); - for (unsigned int i=0; i */ void PNeXus::Init() { - fIsValid = false; + fValid = false; + fErrorCode = PNEXUS_NO_ERROR; + fErrorMsg = "n/a"; + fNeXusVersion = NEXUS_VERSION; + fFileFormatVersion = "n/a"; + fIdfVersion = 0; + fFileName = "n/a"; + fFileTime = "n/a"; + fCreator = "n/a"; - fFileName = "n/a"; - fFileHandle = 0; - - fErrorMsg = "No Data available!"; - fErrorCode = -1; - - fIDFVersion = -1; - fProgramName = "n/a"; - fProgramVersion = "n/a"; - - fRunNumber = -1; - fRunTitle = "n/a"; - fNotes = "n/a"; - fAnalysisTag = "n/a"; - fLab = "n/a"; - fBeamLine = "n/a"; - fStartDate = "n/a"; - fStartTime = "n/a"; - fStopDate = "n/a"; - fStopTime = "n/a"; - - fSwitchingState = -1; - - fSample.fEnvironment = "n/a"; - fSample.fMagneticField = 9.9e17; - fSample.fMagneticFieldUnit = "n/a"; - fSample.fMagneticFieldState = "n/a"; - fSample.fName = "n/a"; - fSample.fShape = "n/a"; - fSample.fTemperature = 9.9e17; - fSample.fTemperatureUnit = "n/a"; - - fInstrument.name = "n/a"; - fInstrument.detector.number = 0; - fInstrument.collimator.type = "n/a"; - fInstrument.beam.total_counts = 0; - fInstrument.beam.total_counts_units = "n/a"; - - fData.fNumberOfHistos = 0; - fData.fTimeResolution = 0.0; - fData.fAlpha.clear(); - fData.fFirstGoodBin.clear(); - fData.fLastGoodBin.clear(); - fData.fHistoName.clear(); - fData.fGrouping.clear(); - fData.fT0.clear(); + fNxEntry1 = 0; + fNxEntry2 = 0; } //----------------------------------------------------------------------------------------------------- @@ -1393,8 +2084,8 @@ bool PNeXus::ErrorHandler(NXstatus status, int errCode, const string &errMsg) if (status != NX_OK) { fErrorCode = errCode; fErrorMsg = errMsg; + cerr << endl << ">> **ERROR** " << fErrorMsg << endl; if (fFileHandle != 0) { - CleanUp(); NXclose(&fFileHandle); } return false; @@ -1434,8 +2125,9 @@ NXstatus PNeXus::GetStringData(string &str) status = NXgetdata(fFileHandle, data_value); if (status != NX_OK) { + cerr << endl << ">> **ERROR** in routine NXgetdata: couldn't get data for '" << str << "'!" << endl; fErrorCode = PNEXUS_GET_DATA_ERROR; - fErrorMsg = "PNeXus::GetStringData() **ERROR** couldn't get data!"; + fErrorMsg = "couldn't get data!"; return NX_ERROR; } @@ -1471,9 +2163,9 @@ NXstatus PNeXus::GetStringAttr(string attr, string &str) atttype = NX_CHAR; status = NXgetattr(fFileHandle, (char *)attr.c_str(), data_value, &attlen, &atttype); if (status != NX_OK) { - cerr << endl << "**ERROR** in routine NXMgetstringattr: couldn't get attribute!" << endl << endl; + cerr << endl << ">> **ERROR** in routine NXgetattr: couldn't get attribute '" << attr << "'! status=" << status << endl << endl; fErrorCode = PNEXUS_GET_ATTR_ERROR; - fErrorMsg = "PNeXus::GetStringAttr() **ERROR** couldn't get string attribute data!"; + fErrorMsg = "couldn't get string attribute data!"; return NX_ERROR; } @@ -1644,296 +2336,2386 @@ NXstatus PNeXus::GetIntVectorData(vector &data) return NX_OK; } -//----------------------------------------------------------------------------------------------------- -// SetT0 (public) -//----------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------ +// ReadFileIdf1 (private) +//------------------------------------------------------------------------------------------ /** - *

Sets t0. The current muSR NeXus implementation has only a single t0 for all detectors since it was - * tailored for pulsed muon sources (ISIS). This eventually needs to be changed. + *

Read the NeXus file of type IDF Version 1. * - * \param t0 to be set - * \param idx index of t0 + * return: + * - NX_OK on successfull reading + * - NX_ERROR on error. The error code/message will give the details. */ -void PNeXus::SetT0(unsigned int t0, unsigned idx) +int PNeXus::ReadFileIdf1() { - if (idx >= fData.fT0.size()) - fData.fT0.resize(idx+1); + // create first the necessary NXentry object for IDF Version 1 + fNxEntry1 = new PNeXusEntry1(); + if (fNxEntry1 == 0) { + fErrorCode = PNEXUS_OBJECT_INVOK_ERROR; + fErrorMsg = ">> **ERROR** couldn't invoke PNeXusEntry1 object."; + return NX_ERROR; + } - fData.fT0[idx] = t0; + string str; + char cstr[128]; + int ival; + float fval; + + // open file + NXstatus status; + status = NXopen(fFileName.c_str(), NXACC_READ, &fFileHandle); + if (status != NX_OK) { + fErrorCode = PNEXUS_FILE_OPEN_ERROR; + fErrorMsg = "PNeXus::ReadFile() **ERROR** Couldn't open file '"+fFileName+"' !"; + return NX_ERROR; + } + + // look for the first occurring NXentry + bool found = false; + NXname nxname, nxclass; + int dataType; + do { + status = NXgetnextentry(fFileHandle, nxname, nxclass, &dataType); + if (strstr(nxclass, "NXentry") != NULL) + found = true; + } while (!found || (status == NX_EOD)); + // make sure any NXentry has been found + if (!found) { + fErrorCode = PNEXUS_NXENTRY_NOT_FOUND; + fErrorMsg = ">> **ERROR** Couldn't find any NXentry!"; + return NX_ERROR; + } + // open the NXentry group to obtain the necessary stuff + status = NXopengroup(fFileHandle, nxname, "NXentry"); + if (status != NX_OK) { + fErrorCode = PNEXUS_GROUP_OPEN_ERROR; + fErrorMsg = "PNeXus::ReadFile() **ERROR** Couldn't open the NeXus group '" + string(nxname) + "'!"; + return NX_ERROR; + } + + // program_name + if (!ErrorHandler(NXopendata(fFileHandle, "program_name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'program_name' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'program_name' data in NXentry!")) return NX_ERROR; + fNxEntry1->SetProgramName(str); + if (!ErrorHandler(GetStringAttr("version", str), PNEXUS_GET_ATTR_ERROR, "couldn't read 'program_name' attribute in NXentry!")) return NX_ERROR; + fNxEntry1->SetProgramVersion(str); + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'program_name' data in NXentry!")) return NX_ERROR; + + // run number + if (!ErrorHandler(NXopendata(fFileHandle, "number"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'number' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(NXgetdata(fFileHandle, &ival), PNEXUS_GET_DATA_ERROR, "couldn't read 'number' data in NXentry!")) return NX_ERROR; + fNxEntry1->SetRunNumber(ival); + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'number' data in NXentry!")) return NX_ERROR; + + // title + if (!ErrorHandler(NXopendata(fFileHandle, "title"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'title' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'title' data in NXentry!")) return NX_ERROR; + fNxEntry1->SetTitle(str); + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'title' data in NXentry!")) return NX_ERROR; + + // notes + if (!ErrorHandler(NXopendata(fFileHandle, "notes"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'notes' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'notes' data in NXentry!")) return NX_ERROR; + fNxEntry1->SetNotes(str); + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'notes' data in NXentry!")) return NX_ERROR; + + // analysis tag + if (!ErrorHandler(NXopendata(fFileHandle, "analysis"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'analysis' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'analysis' data in NXentry!")) return NX_ERROR; + fNxEntry1->SetAnalysis(str); + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'analysis' data in NXentry!")) return NX_ERROR; + + // lab + if (!ErrorHandler(NXopendata(fFileHandle, "lab"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'lab' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'lab' data in NXentry!")) return NX_ERROR; + fNxEntry1->SetLaboratory(str); + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'lab' data in NXentry!")) return NX_ERROR; + + // beamline + if (!ErrorHandler(NXopendata(fFileHandle, "beamline"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'beamline' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'beamline' data in NXentry!")) return NX_ERROR; + fNxEntry1->SetBeamline(str); + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'beamline' data in NXentry!")) return NX_ERROR; + + // start time + if (!ErrorHandler(NXopendata(fFileHandle, "start_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'start_time' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'start_time' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'start_time' data in NXentry!")) return NX_ERROR; + if (fNxEntry1->SetStartTime(str) != NX_OK) { + fErrorCode = PNEXUS_TIME_FORMAT_ERROR; + fErrorMsg = "start time format is not ISO 8601 conform."; + } + + // stop time + if (!ErrorHandler(NXopendata(fFileHandle, "stop_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'start_time' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'start_time' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'start_time' data in NXentry!")) return NX_ERROR; + if (fNxEntry1->SetStopTime(str) != NX_OK) { + fErrorCode = PNEXUS_TIME_FORMAT_ERROR; + fErrorMsg = "stop time format is not ISO 8601 conform.."; + } + + // switching state (red/green mode) + if (!ErrorHandler(NXopendata(fFileHandle, "switching_states"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'switching_states' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(NXgetdata(fFileHandle, &ival), PNEXUS_GET_DATA_ERROR, "couldn't read 'switching_states' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'switching_states' data in NXentry!")) return NX_ERROR; + if (fNxEntry1->SetSwitchingState(ival)) { + fErrorCode = PNEXUS_DATA_ERROR; + fErrorMsg = "unkown switching state found."; + } + + // open group NXuser + if (!ErrorHandler(NXopengroup(fFileHandle, "user", "NXuser"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup 'user'!")) return NX_ERROR; + + // change to subgroup + if (!ErrorHandler(NXinitgroupdir(fFileHandle), PNEXUS_INIT_GROUPDIR_ERROR, "couldn't init group directory")) return NX_ERROR; + int numItems = 0; + NXname groupName, className; + if (!ErrorHandler(NXgetgroupinfo(fFileHandle, &numItems, groupName, className), PNEXUS_GET_GROUP_INFO_ERROR, "couldn't get user group info")) return NX_ERROR; + + // get all the user info + // go through the user list and filter out the required items + NXname nx_label; + int nx_dataType; + for (int i=0; iGetUser()->SetName(str); + } + if (!strcmp(nx_label, "experiment_number")) { + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't get user name")) return NX_ERROR; + fNxEntry1->GetUser()->SetExperimentNumber(str); + } + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close data")) return NX_ERROR; + } + + // close group NXuser + NXclosegroup(fFileHandle); + + // open group NXsample + if (!ErrorHandler(NXopengroup(fFileHandle, "sample", "NXSample"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup sample!")) return NX_ERROR; + + // read sample name + if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'name' data in sample group!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'name' data in sample group!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'name' data in sample group")) return NX_ERROR; + fNxEntry1->GetSample()->SetName(str); + + // read sample temperature + if (!ErrorHandler(NXopendata(fFileHandle, "temperature"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'temperature' data in sample group!")) return NX_ERROR; + if (!ErrorHandler(NXgetdata(fFileHandle, &fval), PNEXUS_GET_DATA_ERROR, "couldn't read 'temperature' data in sample group!")) return NX_ERROR; + if (!ErrorHandler(GetStringAttr("units", str), PNEXUS_GET_ATTR_ERROR, "couldn't read temperature units!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'temperature' data in sample group")) return NX_ERROR; + fNxEntry1->GetSample()->SetPhysProp("temperature", (double)fval, str); + + // read sample magnetic field + if (!ErrorHandler(NXopendata(fFileHandle, "magnetic_field"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'magnetic_field' data in sample group!")) return NX_ERROR; + if (!ErrorHandler(NXgetdata(fFileHandle, &fval), PNEXUS_GET_DATA_ERROR, "couldn't read 'magnetic_field' data in sample group!")) return NX_ERROR; + if (!ErrorHandler(GetStringAttr("units", str), PNEXUS_GET_ATTR_ERROR, "couldn't read magnetic_field units!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'magnetic_field' data in sample group")) return NX_ERROR; + fNxEntry1->GetSample()->SetPhysProp("magnetic_field", (double)fval, str); + + // read sample shape, e.g. powder, single crystal, etc. + if (!ErrorHandler(NXopendata(fFileHandle, "shape"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'shape' data in sample group!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'shape' data in sample group!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'shape' data in sample group")) return NX_ERROR; + fNxEntry1->GetSample()->SetShape(str); + + // read magnetic field state, e.g. TF, LF, ZF + if (!ErrorHandler(NXopendata(fFileHandle, "magnetic_field_state"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'magnetic_field_state' data in sample group!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'magnetic_field_state' data in sample group!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'magnetic_field_state' data in sample group")) return NX_ERROR; + fNxEntry1->GetSample()->SetMagneticFieldState(str); + + // read 'magnetic_field_vector' and 'coordinate_system' attribute + if (!ErrorHandler(NXopendata(fFileHandle, "magnetic_field_vector"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'magnetic_field_vector' data in sample group!")) return NX_ERROR; + int attLen = 1, attType = NX_INT32; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, "available", sizeof(cstr)); + if (!ErrorHandler(NXgetattr(fFileHandle, cstr, &ival, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'magnetic_field_vector available' data in sample group!")) return NX_ERROR; + fNxEntry1->GetSample()->SetMagneticFieldVectorAvailable(ival); + if (fNxEntry1->GetSample()->IsMagneticFieldVectorAvailable()) { + vector magVec; + if (!ErrorHandler(GetDoubleVectorData(magVec), PNEXUS_GET_DATA_ERROR, "couldn't get 'magnetic_field_vector' data!")) return NX_ERROR; + fNxEntry1->GetSample()->SetMagneticFieldVector(magVec); + magVec.clear(); + if (!ErrorHandler(GetStringAttr("coordinate_system", str), PNEXUS_GET_ATTR_ERROR, "couldn't read magnetic field coordinate_system!")) return NX_ERROR; + fNxEntry1->GetSample()->SetMagneticFieldVectorCoordinateSystem(str); + + // workaround since not all ISIS IDF 1 files have the 'units' entry!! + int i, status, attlen, atttype; + char cstr[VGNAMELENMAX]; + NXname data_value; + + attlen = VGNAMELENMAX - 1; + atttype = NX_CHAR; + status = NXgetattr(fFileHandle, "units", data_value, &attlen, &atttype); + if (status == NX_OK) { + for (i = 0; i < attlen; i++) + cstr[i] = *(data_value + i); + cstr[i] = '\0'; + + str = cstr; + } else { + str = string("Gauss"); + } + fNxEntry1->GetSample()->SetMagneticFieldUnits(str); + } + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'magnetic_field_vector' data in sample group")) return NX_ERROR; + + // read sample environment, e.g. CCR, LowTemp-2, etc. + if (!ErrorHandler(NXopendata(fFileHandle, "environment"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'environment' data in sample group!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'environment' data in sample group!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'environment' data in sample group")) return NX_ERROR; + fNxEntry1->GetSample()->SetEnvironment(str); + + // close group NXsample + NXclosegroup(fFileHandle); + + // get required instrument information + // open subgroup NXinstrument with subgroups detector, collimator, beam + if (!ErrorHandler(NXopengroup(fFileHandle, "instrument", "NXinstrument"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup instrument!")) return NX_ERROR; + + // get instrument name + if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'name' data in instrument group!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'name' data in instrument group!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'name' data in instrument group")) return NX_ERROR; + fNxEntry1->GetInstrument()->SetName(str); + + // open subgroup NXdetector + if (!ErrorHandler(NXopengroup(fFileHandle, "detector", "NXdetector"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup detector!")) return NX_ERROR; + + // get number of detectors + if (!ErrorHandler(NXopendata(fFileHandle, "number"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'number' data in detector group!")) return NX_ERROR; + if (!ErrorHandler(NXgetdata(fFileHandle, &ival), PNEXUS_GET_DATA_ERROR, "couldn't read 'number' data in detector group!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'number' data in detector group")) return NX_ERROR; + fNxEntry1->GetInstrument()->GetDetector()->SetNumber(ival); + + // close subgroup NXdetector + NXclosegroup(fFileHandle); + + // open subgroup NXcollimator + if (!ErrorHandler(NXopengroup(fFileHandle, "collimator", "NXcollimator"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup collimator!")) return NX_ERROR; + + // get collimator type + if (!ErrorHandler(NXopendata(fFileHandle, "type"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'type' data in collimator group!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'type' data in collimator group!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'type' data in collimator group")) return NX_ERROR; + fNxEntry1->GetInstrument()->GetCollimator()->SetType(str); + + // close subgroup NXcollimator + NXclosegroup(fFileHandle); + + // open subgroup NXbeam + if (!ErrorHandler(NXopengroup(fFileHandle, "beam", "NXbeam"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup beam!")) return NX_ERROR; + + // get the total counts + if (!ErrorHandler(NXopendata(fFileHandle, "total_counts"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'total_counts' data in beam group!")) return NX_ERROR; + if (!ErrorHandler(NXgetdata(fFileHandle, &fval), PNEXUS_GET_DATA_ERROR, "couldn't read 'total_counts' data in beam group!")) return NX_ERROR; + fNxEntry1->GetInstrument()->GetBeam()->SetTotalCounts((double)fval); + if (!ErrorHandler(GetStringAttr("units", str), PNEXUS_GET_ATTR_ERROR, "couldn't read total_counts units!")) return NX_ERROR; + fNxEntry1->GetInstrument()->GetBeam()->SetUnits(str); + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'total_counts' data in beam group")) return NX_ERROR; + + // close subgroup NXbeam + NXclosegroup(fFileHandle); + + // close subgroup NXinstrument + NXclosegroup(fFileHandle); + + // open subgroup NXdata (this is for Version 1, only and is subject to change in the near future!) + if (!ErrorHandler(NXopengroup(fFileHandle, "histogram_data_1", "NXdata"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup histogram_data_1!")) return NX_ERROR; + + // get time resolution + if (!ErrorHandler(NXopendata(fFileHandle, "resolution"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'resolution' data in histogram_data_1 group!")) return NX_ERROR; + if (!ErrorHandler(NXgetdata(fFileHandle, &ival), PNEXUS_GET_DATA_ERROR, "couldn't read 'resolution' data in histogram_data_1 group!")) return NX_ERROR; + if (!ErrorHandler(GetStringAttr("units", str), PNEXUS_GET_ATTR_ERROR, "couldn't read 'resolution'' units!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'resolution' data in histogram_data_1 group")) return NX_ERROR; + if (!strcmp(str.data(), "picoseconds")) { + fNxEntry1->GetData()->SetTimeResolution((double)ival, "ps"); + } + + // get data, t0, first good bin, last good bin, data + if (!ErrorHandler(NXopendata(fFileHandle, "counts"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'counts' data in histogram_data_1 group!")) return NX_ERROR; + int histoLength=0; + int noOfHistos=0; + + // get number of histos + attLen = 1; + attType = NX_INT32; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, "number", sizeof(cstr)); + if (!ErrorHandler(NXgetattr(fFileHandle, cstr, &noOfHistos, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'number' data in histogram_data_1 group!")) return NX_ERROR; + + // get histo length + attLen = 1; + attType = NX_INT32; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, "length", sizeof(cstr)); + if (!ErrorHandler(NXgetattr(fFileHandle, cstr, &histoLength, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'number' data in histogram_data_1 group!")) return NX_ERROR; + + // get t0 + attLen = 1; + attType = NX_INT32; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, "t0_bin", sizeof(cstr)); + if (!ErrorHandler(NXgetattr(fFileHandle, cstr, &ival, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 't0_bin' data in histogram_data_1 group!")) return NX_ERROR; + fNxEntry1->GetData()->SetT0(ival); + + // get first good bin + attLen = 1; + attType = NX_INT32; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, "first_good_bin", sizeof(cstr)); + if (!ErrorHandler(NXgetattr(fFileHandle, cstr, &ival, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'first_good_bin' data in histogram_data_1 group!")) return NX_ERROR; + fNxEntry1->GetData()->SetFirstGoodBin(ival); + + // get last good bin + attLen = 1; + attType = NX_INT32; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, "last_good_bin", sizeof(cstr)); + if (!ErrorHandler(NXgetattr(fFileHandle, cstr, &ival, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'last_good_bin' data in histogram_data_1 group!")) return NX_ERROR; + fNxEntry1->GetData()->SetLastGoodBin(ival); + + // get data + + // get information of the current nexus entity + int rank, type, dims[32], size, noOfElements; + if (!ErrorHandler(NXgetinfo(fFileHandle, &rank, dims, &type), PNEXUS_GET_META_INFO_ERROR, "couldn't get data info!")) return NX_ERROR; + + // calculate the needed size + size = dims[0]; + for (int i=1; iGetData()->FlushHistos(); + vector data; + for (int i=0; i0)) { + fNxEntry1->GetData()->SetHisto(data); + data.clear(); + data.push_back(*(i_data_ptr+i)); + } else { + data.push_back(*(i_data_ptr+i)); + } + } + fNxEntry1->GetData()->SetHisto(data); + data.clear(); + + // clean up + if (data_ptr) { + delete [] data_ptr; + } + + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'counts' data in histogram_data_1 group")) return NX_ERROR; + + // get grouping + if (!ErrorHandler(NXopendata(fFileHandle, "grouping"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'grouping' data in histogram_data_1 group!")) return NX_ERROR; + attLen = 1; + attType = NX_INT32; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, "available", sizeof(cstr)); + if (!ErrorHandler(NXgetattr(fFileHandle, cstr, &ival, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'grouping available' data in histogram_data_1 group!")) return NX_ERROR; + if (ival) { + vector grouping; + if (!ErrorHandler(GetIntVectorData(grouping), PNEXUS_GET_DATA_ERROR, "couldn't read 'grouping' data in histogram_data_1 group!")) return NX_ERROR; + fNxEntry1->GetData()->SetGrouping(grouping); + grouping.clear(); + } + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'grouping' data in histogram_data_1 group")) return NX_ERROR; + // check grouping vector for consistency + if (fNxEntry1->GetData()->GetGrouping()->size() != fNxEntry1->GetData()->GetNoOfHistos()) { + fNxEntry1->GetData()->FlushGrouping(); + cerr << endl << ">> **WARNING** grouping vector size != no of histograms which doesn't make sence, hence grouping will be ignored." << endl; + } + // check that the grouping values do make sense, i.e. allowed range is grouping value > 0 and grouping value <= # of histos + bool ok=true; + for (unsigned int i=0; iGetData()->GetGrouping()->size(); i++) { + if ((fNxEntry1->GetData()->GetGrouping()->at(i) == 0) || (fNxEntry1->GetData()->GetGrouping()->at(i) > (int)fNxEntry1->GetData()->GetNoOfHistos())) { + cerr << endl << ">> **WARNING** found grouping entry '" << fNxEntry1->GetData()->GetGrouping()->at(i) << "' which doesn't make sense, hence grouping will be ignored." << endl; + ok = false; + break; + } + } + if (!ok) + fNxEntry1->GetData()->FlushGrouping(); + + // get alpha + if (!ErrorHandler(NXopendata(fFileHandle, "alpha"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'alpha' data in histogram_data_1 group!")) return NX_ERROR; + attLen = 1; + attType = NX_INT32; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, "available", sizeof(cstr)); + if (!ErrorHandler(NXgetattr(fFileHandle, cstr, &ival, &attLen, &attType), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'alpha available' data in histogram_data_1 group!")) return NX_ERROR; + if (ival) { + if (!ErrorHandler(NXgetinfo(fFileHandle, &rank, dims, &type), PNEXUS_GET_META_INFO_ERROR, "couldn't get alpha info!")) return NX_ERROR; + // calculate the needed size + size = dims[0]; + for (int i=1; i> **ERROR** " << fErrorMsg << endl; + return NX_ERROR; + } + + // allocate locale memory to get the data + char *data_ptr = new char[size]; + if (data_ptr == 0) { + return NX_ERROR; + } + + // get the data + float *f_data_ptr = (float*) data_ptr; + status = NXgetdata(fFileHandle, f_data_ptr); + if (status != NX_OK) { + return NX_ERROR; + } + + // copy the data into the vector + fNxEntry1->GetData()->FlushAlpha(); + PNeXusAlpha1 alpha; + vector alphaVec; + for (int i=0; iGetData()->SetAlpha(alphaVec); + + // clean up + alphaVec.clear(); + if (data_ptr) { + delete [] data_ptr; + } + } + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'alpha' data in histogram_data_1 group")) return NX_ERROR; + + // close subgroup NXdata + NXclosegroup(fFileHandle); + + // close group NXentry + NXclosegroup(fFileHandle); + + // close file + NXclose(&fFileHandle); + + GroupHistoData(); + + fValid = true; + + return NX_OK; } -//----------------------------------------------------------------------------------------------------- -// SetFirstGoodBin (public) -//----------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------ +// ReadFileIdf2 (private) +//------------------------------------------------------------------------------------------ /** - *

Sets first good bin. The current muSR NeXus implementation has only a single first good bin for all detectors since it was - * tailored for pulsed muon sources (ISIS). This eventually needs to be changed. + *

Read the NeXus file of type IDF Version 2. * - * \param fgb first good bin to be set - * \param idx index of t0 + * return: + * - NX_OK on successfull reading + * - NX_ERROR on error. The error code/message will give the details. */ -void PNeXus::SetFirstGoodBin(unsigned int fgb, unsigned int idx) +int PNeXus::ReadFileIdf2() { - if (idx >= fData.fFirstGoodBin.size()) - fData.fFirstGoodBin.resize(idx+1); + // create first the necessary NXentry object for IDF Version 1 + fNxEntry2 = new PNeXusEntry2(); + if (fNxEntry2 == 0) { + fErrorCode = PNEXUS_OBJECT_INVOK_ERROR; + fErrorMsg = ">> **ERROR** couldn't invoke PNeXusEntry2 object."; + return NX_ERROR; + } - fData.fFirstGoodBin[idx] = fgb; + string str; + int ival, attlen, atttype; + float fval; + NXname data_value; + int rank, type, dims[32], size, noOfElements; + + // open file + NXstatus status; + status = NXopen(fFileName.c_str(), NXACC_READ, &fFileHandle); + if (status != NX_OK) { + fErrorCode = PNEXUS_FILE_OPEN_ERROR; + fErrorMsg = "PNeXus::ReadFile() **ERROR** Couldn't open file '"+fFileName+"' !"; + return NX_ERROR; + } + + // collect the NXroot attribute information + if (!ErrorHandler(GetStringAttr("file_name", str), PNEXUS_GET_ATTR_ERROR, "couldn't read NXroot 'file_name' attribute!")) return NX_ERROR; + fFileName = str; + if (!ErrorHandler(GetStringAttr("file_time", str), PNEXUS_GET_ATTR_ERROR, "couldn't read NXroot 'file_time' attribute!")) return NX_ERROR; + fFileTime = str; + if (!ErrorHandler(GetStringAttr("creator", str), PNEXUS_GET_ATTR_ERROR, "couldn't read NXroot 'creator' attribute!")) return NX_ERROR; + fCreator = str; + + attlen = VGNAMELENMAX - 1; + atttype = NX_CHAR; + str = string("HDF_version"); + status = NXgetattr(fFileHandle, (char *)str.c_str(), data_value, &attlen, &atttype); + if (status == NX_OK) { + fFileFormatVersion = string(data_value); + } + + if (!fFileFormatVersion.compare("n/a")) { + attlen = VGNAMELENMAX - 1; + atttype = NX_CHAR; + str = string("HDF5_version"); + status = NXgetattr(fFileHandle, (char *)str.c_str(), data_value, &attlen, &atttype); + if (status == NX_OK) { + fFileFormatVersion = string(data_value); + } + } + + if (!fFileFormatVersion.compare("n/a")) { + attlen = VGNAMELENMAX - 1; + atttype = NX_CHAR; + str = string("XML_version"); + status = NXgetattr(fFileHandle, (char *)str.c_str(), data_value, &attlen, &atttype); + if (status == NX_OK) { + fFileFormatVersion = string(data_value); + } + } + + // look for the first occurring NXentry + NXname nxname, nxclass; + int dataType; + // make sure any NXentry has been found + if (!SearchInGroup("NXentry", "class", nxname, nxclass, dataType)) { + fErrorCode = PNEXUS_NXENTRY_NOT_FOUND; + fErrorMsg = ">> **ERROR** Couldn't find any NXentry!"; + return NX_ERROR; + } + // open the NXentry group to obtain the necessary stuff + status = NXopengroup(fFileHandle, nxname, "NXentry"); + if (status != NX_OK) { + fErrorCode = PNEXUS_GROUP_OPEN_ERROR; + fErrorMsg = "PNeXus::ReadFileIdf2() **ERROR** Couldn't open the NeXus group '" + string(nxname) + "'!"; + return NX_ERROR; + } + + // definition + if (!ErrorHandler(NXopendata(fFileHandle, "definition"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'definition' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'definition' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'definition' data in NXentry!")) return NX_ERROR; + fNxEntry2->SetDefinition(str); + + // program_name and version + if (NXopendata(fFileHandle, "program_name") == NX_OK) { + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'program_name' data in NXentry!")) return NX_ERROR; + fNxEntry2->SetProgramName(str); + if (!ErrorHandler(GetStringAttr("version", str), PNEXUS_GET_ATTR_ERROR, "couldn't read 'program_name' attribute in NXentry!")) return NX_ERROR; + fNxEntry2->SetProgramVersion(str); + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'program_name' data in NXentry!")) return NX_ERROR; + } + + // run number + if (!ErrorHandler(NXopendata(fFileHandle, "run_number"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'run_number' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(NXgetdata(fFileHandle, &ival), PNEXUS_GET_DATA_ERROR, "couldn't read 'run_number' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'run_number' data in NXentry!")) return NX_ERROR; + fNxEntry2->SetRunNumber(ival); + + // title + if (!ErrorHandler(NXopendata(fFileHandle, "title"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'title' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'title' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'title' data in NXentry!")) return NX_ERROR; + fNxEntry2->SetTitle(str); + + // start time + if (!ErrorHandler(NXopendata(fFileHandle, "start_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'start_time' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'start_time' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'start_time' data in NXentry!")) return NX_ERROR; + if (fNxEntry2->SetStartTime(str) != NX_OK) { + fErrorCode = PNEXUS_TIME_FORMAT_ERROR; + fErrorMsg = "start time format is not ISO 8601 conform."; + } + + // end time + if (!ErrorHandler(NXopendata(fFileHandle, "end_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'end_time' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'end_time' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'end_time' data in NXentry!")) return NX_ERROR; + if (fNxEntry2->SetStopTime(str) != NX_OK) { + fErrorCode = PNEXUS_TIME_FORMAT_ERROR; + fErrorMsg = "stop time format is not ISO 8601 conform.."; + } + + // experiment identifier + if (!ErrorHandler(NXopendata(fFileHandle, "experiment_identifier"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'experiment_identifier' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'experiment_identifier' data in NXentry!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'experiment_identifier' data in NXentry!")) return NX_ERROR; + fNxEntry2->SetExperimentIdentifier(str); + + // find entry for NXuser + if (!SearchInGroup("NXuser", "class", nxname, nxclass, dataType)) { + fErrorCode = PNEXUS_NXUSER_NOT_FOUND; + fErrorMsg = ">> **ERROR** Couldn't find any NXuser int NXentry!"; + return NX_ERROR; + } + // open the NXuser + status = NXopengroup(fFileHandle, nxname, "NXuser"); + if (status != NX_OK) { + fErrorCode = PNEXUS_GROUP_OPEN_ERROR; + fErrorMsg = "PNeXus::ReadFileIdf2() **ERROR** Couldn't open NXuser '" + string(nxname) + "' in NXentry!"; + return NX_ERROR; + } + + // user name + if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'name' data in NXuser!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'name' data in NXuser!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'name' data in NXuser!")) return NX_ERROR; + fNxEntry2->GetUser()->SetName(str); + + // close group NXuser + NXclosegroup(fFileHandle); + + // open group NXsample + if (!ErrorHandler(NXopengroup(fFileHandle, "sample", "NXsample"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup sample!")) return NX_ERROR; + + // sample name + if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'name' data in NXsample!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'name' data in NXsample!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'name' data in NXsample!")) return NX_ERROR; + fNxEntry2->GetSample()->SetName(str); + + // sample description + if (NXopendata(fFileHandle, "description") == NX_OK) { + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'description' data in NXsample!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'description' data in NXsample!")) return NX_ERROR; + fNxEntry2->GetSample()->SetDescription(str); + } + + // temperature + if (NXopendata(fFileHandle, "temperature_1") == NX_OK) { + if (!ErrorHandler(NXgetdata(fFileHandle, &fval), PNEXUS_GET_DATA_ERROR, "couldn't read 'temperature_1' data in NXsample!")) return NX_ERROR; + if (!ErrorHandler(GetStringAttr("units", str), PNEXUS_GET_ATTR_ERROR, "couldn't read temperature units in NXsample!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'temperature_1' data in NXsample!")) return NX_ERROR; + fNxEntry2->GetSample()->SetPhysProp("temperature_1", fval, str); + } + + // temperature environment + if (NXopengroup(fFileHandle, "temperature_1_env", "NXenvironment") == NX_OK) { + // user name + if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'name' data in NXenvironment!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'name' data in NXenvironment!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'name' data in NXenvironment!")) return NX_ERROR; + fNxEntry2->GetSample()->SetEnvironmentTemp(str); + // close group NXenvironment + NXclosegroup(fFileHandle); + } + + // magnetic_field + if (NXopendata(fFileHandle, "magnetic_field_1") == NX_OK) { + if (!ErrorHandler(NXgetdata(fFileHandle, &fval), PNEXUS_GET_DATA_ERROR, "couldn't read 'magnetic_field_1' data in NXsample!")) return NX_ERROR; + if (!ErrorHandler(GetStringAttr("units", str), PNEXUS_GET_ATTR_ERROR, "couldn't read magnetic field units in NXsample!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'magnetic_field_1' data in NXsample!")) return NX_ERROR; + fNxEntry2->GetSample()->SetPhysProp("magnetic_field_1", fval, str); + } + + // magnetic field state + if (NXopendata(fFileHandle, "magnetic_field_state") == NX_OK) { + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'magnetic_field_state' data in NXsample!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'magnetic_field_state' data in NXsample!")) return NX_ERROR; + fNxEntry2->GetSample()->SetMagneticFieldState(str); + } + + // magnetic field environment + if (NXopengroup(fFileHandle, "magnetic_field_1_env", "NXenvironment") == NX_OK) { + // user name + if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'name' data in NXenvironment!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'name' data in NXenvironment!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'name' data in NXenvironment!")) return NX_ERROR; + fNxEntry2->GetSample()->SetEnvironmentField(str); + // close group NXenvironment + NXclosegroup(fFileHandle); + } + + // close group NXsample + NXclosegroup(fFileHandle); + + // open group NXinstrument + if (!ErrorHandler(NXopengroup(fFileHandle, "instrument", "NXinstrument"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup instrument in NXentry!")) return NX_ERROR; + + // name + if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'name' data in NXinstrument!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'name' data in NXinstrument!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'name' data in NXinstrument!")) return NX_ERROR; + fNxEntry2->GetInstrument()->SetName(str); + + // open group NXsource + if (!ErrorHandler(NXopengroup(fFileHandle, "source", "NXsource"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup instrument in NXinstrument!")) return NX_ERROR; + + // name + if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'name' data in NXsource!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'name' data in NXsource!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'name' data in NXsource!")) return NX_ERROR; + fNxEntry2->GetInstrument()->GetSource()->SetName(str); + + // type + if (!ErrorHandler(NXopendata(fFileHandle, "type"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'type' data in NXsource!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'type' data in NXsource!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'type' data in NXsource!")) return NX_ERROR; + fNxEntry2->GetInstrument()->GetSource()->SetType(str); + + // probe + if (!ErrorHandler(NXopendata(fFileHandle, "probe"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'probe' data in NXsource!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'probe' data in NXsource!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'probe' data in NXsource!")) return NX_ERROR; + fNxEntry2->GetInstrument()->GetSource()->SetProbe(str); + + // close group NXsource + NXclosegroup(fFileHandle); + + // open group NXbeamline + if (!ErrorHandler(NXopengroup(fFileHandle, "beamline", "NXbeamline"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup beamline in NXinstrument!")) return NX_ERROR; + + // beamline name + if (!ErrorHandler(NXopendata(fFileHandle, "beamline"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'beamline' data in NXbeamline!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'beamline' data in NXbeamline!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'beamline' data in NXbeamline!")) return NX_ERROR; + fNxEntry2->GetInstrument()->GetBeamline()->SetName(str); + + // close group NXbeamline + NXclosegroup(fFileHandle); + + // open group NXdetector + if (!ErrorHandler(NXopengroup(fFileHandle, "detector_1", "NXdetector"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open NeXus subgroup detector_1 in NXinstrument!")) return NX_ERROR; + + // description + if (!ErrorHandler(NXopendata(fFileHandle, "description"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'description' data in NXdetector!")) return NX_ERROR; + if (!ErrorHandler(GetStringData(str), PNEXUS_GET_DATA_ERROR, "couldn't read 'description' data in NXdetector!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'description' data in NXdetector!")) return NX_ERROR; + fNxEntry2->GetInstrument()->GetDetector()->SetDescription(str); + + // get the time resolution. This is a little bit complicated since it is either present as 'histogram_resolution' or needs to be extracted from the 'raw_time' vector + // 1st check if 'histogram_resolution' is found + if (SearchInGroup("histogram_resolution", "name", nxname, nxclass, dataType)) { + if (!ErrorHandler(NXopendata(fFileHandle, "histogram_resolution"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'histogram_resolution' data in NXdetector!")) return NX_ERROR; + if (!ErrorHandler(NXgetdata(fFileHandle, &ival), PNEXUS_GET_DATA_ERROR, "couldn't read 'histogram_resolution' data in NXdetector!")) return NX_ERROR; + if (!ErrorHandler(GetStringAttr("units", str), PNEXUS_GET_ATTR_ERROR, "couldn't read magnetic field units in NXsample!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'histogram_resolution' data in NXdetector!")) return NX_ERROR; + fNxEntry2->GetInstrument()->GetDetector()->SetTimeResolution((double)ival, str); + } else { // 2nd 'histogram_resolution' is not present, hence extract the time resolution from the 'raw_time' vector + if (!ErrorHandler(NXopendata(fFileHandle, "raw_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'raw_time' data in NXdetector!")) return NX_ERROR; + vector rawTime; + if (!ErrorHandler(GetDoubleVectorData(rawTime), PNEXUS_GET_DATA_ERROR, "couldn't get 'raw_time' data in NXdetector!")) return NX_ERROR; + if (!ErrorHandler(GetStringAttr("units", str), PNEXUS_GET_ATTR_ERROR, "couldn't read 'raw_time' units in NXdetector!")) return NX_ERROR; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'raw_time' data in NXdetector!")) return NX_ERROR; + fNxEntry2->GetInstrument()->GetDetector()->SetTimeResolution(rawTime[1]-rawTime[0], str); + rawTime.clear(); + } + + // get counts + if (!ErrorHandler(NXopendata(fFileHandle, "counts"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'counts' data in NXdetector!")) return NX_ERROR; + // check the dimensions of 'counts' + if (!ErrorHandler(NXgetinfo(fFileHandle, &rank, dims, &type), PNEXUS_GET_META_INFO_ERROR, "couldn't get 'counts' info in NXdetector!")) return NX_ERROR; + + // calculate the needed size + size = dims[0]; + for (int i=1; iGetInstrument()->GetDetector()->SetHisto(i_data_ptr); + + if (rank == 3) { // i.e. np, ns, ntc + fNxEntry2->GetInstrument()->GetDetector()->SetNoOfPeriods(dims[0]); + fNxEntry2->GetInstrument()->GetDetector()->SetNoOfSpectra(dims[1]); + fNxEntry2->GetInstrument()->GetDetector()->SetNoOfBins(dims[2]); + } else if (rank == 2) { // i.e. ns, ntc + fNxEntry2->GetInstrument()->GetDetector()->SetNoOfPeriods(-1); + fNxEntry2->GetInstrument()->GetDetector()->SetNoOfSpectra(dims[0]); + fNxEntry2->GetInstrument()->GetDetector()->SetNoOfBins(dims[1]); + } else if (rank == 1) { // i.e. ntc only + fNxEntry2->GetInstrument()->GetDetector()->SetNoOfPeriods(-1); + fNxEntry2->GetInstrument()->GetDetector()->SetNoOfSpectra(1); + fNxEntry2->GetInstrument()->GetDetector()->SetNoOfBins(dims[0]); + } else { + fNxEntry2->GetInstrument()->GetDetector()->SetNoOfPeriods(-1); + fNxEntry2->GetInstrument()->GetDetector()->SetNoOfSpectra(-1); + fNxEntry2->GetInstrument()->GetDetector()->SetNoOfBins(-1); + cerr << endl << ">> **ERROR** found rank=" << rank << " for NXinstrument:NXdetector:counts! Allowed ranks are 1, 2, or 3." << endl; + return NX_ERROR; + } + + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'counts' data in NXdetector!")) return NX_ERROR; + + // handle spectrum_index + if (!ErrorHandler(NXopendata(fFileHandle, "spectrum_index"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'spectrum_index' data in NXdetector!")) return NX_ERROR; + // check the dimensions of 'spectrum_index' + if (!ErrorHandler(NXgetinfo(fFileHandle, &rank, dims, &type), PNEXUS_GET_META_INFO_ERROR, "couldn't get 'spectrum_index' info in NXdetector!")) return NX_ERROR; + if (rank > 1) { + cerr << endl << ">> **ERROR** found rank=" << rank << " of spectrum_index in NXdetector. Rank needs to be == 1!" << endl; + return NX_ERROR; + } + if (dims[0] != fNxEntry2->GetInstrument()->GetDetector()->GetNoOfSpectra()) { + cerr << endl << ">> **ERROR** found spectrum_index dimension=" << dims[0] << " inconsistent with counts (" << fNxEntry2->GetInstrument()->GetDetector()->GetNoOfSpectra() << "!" << endl; + return NX_ERROR; + } + // allocate locale memory to get the data + data_ptr = new char[dims[0]*GetDataSize(type)]; + if (data_ptr == 0) { + return NX_ERROR; + } + // get the data + i_data_ptr = (int*) data_ptr; + status = NXgetdata(fFileHandle, i_data_ptr); + if (status != NX_OK) { + return NX_ERROR; + } + for (int i=0; iGetInstrument()->GetDetector()->SetSpectrumIndex(*(i_data_ptr+i), i); + + // clean up + if (data_ptr) { + delete [] data_ptr; + data_ptr = 0; + } + + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'spectrum_index' data in NXdetector!")) return NX_ERROR; + + // handle t0's + if (SearchInGroup("time_zero_bin", "name", nxname, nxclass, dataType)) { // check for 'time_zero_bin' + if (!ErrorHandler(NXopendata(fFileHandle, "time_zero_bin"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'time_zero_bin' data in NXdetector!")) return NX_ERROR; + + // check the dimensions of the 'time_zero_bin' vector + if (!ErrorHandler(NXgetinfo(fFileHandle, &rank, dims, &type), PNEXUS_GET_META_INFO_ERROR, "couldn't get 'time_zero_bin' info in NXdetector!")) return NX_ERROR; + + if (rank <= 2) { + fNxEntry2->GetInstrument()->GetDetector()->SetT0Tag(rank); + } else { + cerr << endl << ">> **ERROR** found 'time_zero_bin' info in NXdetector with rank=" << rank << ". Do not know how to handle." << endl; + return NX_ERROR; + } + + // calculate the needed size + size = dims[0]; + for (int i=1; iGetInstrument()->GetDetector()->SetT0(i_data_ptr); + + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'time_zero_bin' data in NXdetector!")) return NX_ERROR; + } else if (SearchInGroup("time_zero", "name", nxname, nxclass, dataType)) { // check for 'time_zero' + if (!ErrorHandler(NXopendata(fFileHandle, "time_zero"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'time_zero' data in NXdetector!")) return NX_ERROR; + // check the dimensions of the 'time_zero' vector + if (!ErrorHandler(NXgetinfo(fFileHandle, &rank, dims, &type), PNEXUS_GET_META_INFO_ERROR, "couldn't get 'time_zero' info in NXdetector!")) return NX_ERROR; + + if (rank <= 2) { + fNxEntry2->GetInstrument()->GetDetector()->SetT0Tag(rank); + } else { + cerr << endl << ">> **ERROR** found 'time_zero' info in NXdetector with rank=" << rank << ". Do not know how to handle." << endl; + return NX_ERROR; + } + + // calculate the needed size + size = dims[0]; + for (int i=1; iGetInstrument()->GetDetector()->GetTimeResolution(str) == 0.0) { + cerr << endl << ">> **ERROR** trying to set T0's based on 'time_zero'. Need a valid time resolution to do so, but this is not given." << endl; + return NX_ERROR; + } + // set the t0's based on the t0 time stamp and the time resolution + int *pt0 = 0; + if (rank == 1) { + pt0 = new int; + *pt0 = (int)(*f_data_ptr / (float)fNxEntry2->GetInstrument()->GetDetector()->GetTimeResolution(str)); + } else { // rank == 2 + pt0 = new int[noOfElements]; + for (int i=0; iGetInstrument()->GetDetector()->GetTimeResolution(str)); + } + } + fNxEntry2->GetInstrument()->GetDetector()->SetT0(pt0); + + // clean up + if (data_ptr) { + delete [] data_ptr; + } + + cerr << endl << ">> **WARNING** found only 'time_zero' will convert it to 'time_zero_bin' values" << endl; + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'time_zero' data in NXdetector!")) return NX_ERROR; + } else { + cerr << endl << ">> **WARNING** found neither 'time_zero_bin' nor 'time_zero' values ..." << endl; + } + + // handle first good bin + if (SearchInGroup("first_good_bin", "name", nxname, nxclass, dataType)) { + if (!ErrorHandler(NXopendata(fFileHandle, "first_good_bin"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'first_good_bin' data in NXdetector!")) return NX_ERROR; + + // check the dimensions of the 'first_good_bin' vector + if (!ErrorHandler(NXgetinfo(fFileHandle, &rank, dims, &type), PNEXUS_GET_META_INFO_ERROR, "couldn't get 'first_good_bin' info in NXdetector!")) return NX_ERROR; + + if (rank <= 2) { + if (rank != fNxEntry2->GetInstrument()->GetDetector()->GetT0Tag()) { + cerr << endl << ">> **ERROR** rank of 'first_good_bin' (" << rank << ") is different than t0 rank=" << rank << ". Do not know how to handle." << endl; + return NX_ERROR; + } + } else { + cerr << endl << ">> **ERROR** found 'first_good_bin' info in NXdetector with rank=" << rank << ". Do not know how to handle." << endl; + return NX_ERROR; + } + + // calculate the needed size + size = dims[0]; + for (int i=1; iGetInstrument()->GetDetector()->SetFirstGoodBin(i_data_ptr); + + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'first_good_bin' data in NXdetector!")) return NX_ERROR; + } else if (SearchInGroup("first_good_time", "name", nxname, nxclass, dataType)) { + if (!ErrorHandler(NXopendata(fFileHandle, "first_good_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'first_good_time' data in NXdetector!")) return NX_ERROR; + + // check the dimensions of the 'first_good_time' vector + if (!ErrorHandler(NXgetinfo(fFileHandle, &rank, dims, &type), PNEXUS_GET_META_INFO_ERROR, "couldn't get 'first_good_time' info in NXdetector!")) return NX_ERROR; + + if (rank <= 2) { + if (rank != fNxEntry2->GetInstrument()->GetDetector()->GetT0Tag()) { + cerr << endl << ">> **ERROR** rank of 'first_good_time' (" << rank << ") is different than t0 rank=" << rank << ". Do not know how to handle." << endl; + return NX_ERROR; + } + } else { + cerr << endl << ">> **ERROR** found 'first_good_time' info in NXdetector with rank=" << rank << ". Do not know how to handle." << endl; + return NX_ERROR; + } + + // calculate the needed size + size = dims[0]; + for (int i=1; iGetInstrument()->GetDetector()->GetTimeResolution(str)); + } else { // rank == 2 + p_fgb = new int[noOfElements]; + for (int i=0; iGetInstrument()->GetDetector()->GetTimeResolution(str)); + } + } + + fNxEntry2->GetInstrument()->GetDetector()->SetFirstGoodBin(p_fgb); + + if (data_ptr) { + delete [] data_ptr; + data_ptr = 0; + } + + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'first_good_time' data in NXdetector!")) return NX_ERROR; + } else { + cerr << endl << ">> **WARNING** found neither 'first_good_bin' nor 'first_good_time' values ..." << endl; + } + + // handle last good bin + if (SearchInGroup("last_good_bin", "name", nxname, nxclass, dataType)) { + if (!ErrorHandler(NXopendata(fFileHandle, "last_good_bin"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'last_good_bin' data in NXdetector!")) return NX_ERROR; + + // check the dimensions of the 'last_good_bin' vector + if (!ErrorHandler(NXgetinfo(fFileHandle, &rank, dims, &type), PNEXUS_GET_META_INFO_ERROR, "couldn't get 'last_good_bin' info in NXdetector!")) return NX_ERROR; + + if (rank <= 2) { + if (rank != fNxEntry2->GetInstrument()->GetDetector()->GetT0Tag()) { + cerr << endl << ">> **ERROR** rank of 'last_good_bin' (" << rank << ") is different than t0 rank=" << rank << ". Do not know how to handle." << endl; + return NX_ERROR; + } + } else { + cerr << endl << ">> **ERROR** found 'last_good_bin' info in NXdetector with rank=" << rank << ". Do not know how to handle." << endl; + return NX_ERROR; + } + + // calculate the needed size + size = dims[0]; + for (int i=1; iGetInstrument()->GetDetector()->SetLastGoodBin(i_data_ptr); + + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'last_good_bin' data in NXdetector!")) return NX_ERROR; + } else if (SearchInGroup("last_good_time", "name", nxname, nxclass, dataType)) { + if (!ErrorHandler(NXopendata(fFileHandle, "last_good_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open 'last_good_time' data in NXdetector!")) return NX_ERROR; + + // check the dimensions of the 'last_good_time' vector + if (!ErrorHandler(NXgetinfo(fFileHandle, &rank, dims, &type), PNEXUS_GET_META_INFO_ERROR, "couldn't get 'last_good_time' info in NXdetector!")) return NX_ERROR; + + if (rank <= 2) { + if (rank != fNxEntry2->GetInstrument()->GetDetector()->GetT0Tag()) { + cerr << endl << ">> **ERROR** rank of 'last_good_time' (" << rank << ") is different than t0 rank=" << rank << ". Do not know how to handle." << endl; + return NX_ERROR; + } + } else { + cerr << endl << ">> **ERROR** found 'last_good_time' info in NXdetector with rank=" << rank << ". Do not know how to handle." << endl; + return NX_ERROR; + } + + // calculate the needed size + size = dims[0]; + for (int i=1; iGetInstrument()->GetDetector()->GetTimeResolution(str)); + } else { // rank == 2 + p_lgb = new int[noOfElements]; + for (int i=0; iGetInstrument()->GetDetector()->GetTimeResolution(str)); + } + } + + fNxEntry2->GetInstrument()->GetDetector()->SetFirstGoodBin(p_lgb); + + if (data_ptr) { + delete [] data_ptr; + data_ptr = 0; + } + + if (!ErrorHandler(NXclosedata(fFileHandle), PNEXUS_CLOSE_DATA_ERROR, "couldn't close 'last_good_time' data in NXdetector!")) return NX_ERROR; + } else { + cerr << endl << ">> **WARNING** found neither 'last_good_bin' nor 'last_good_time' values ..." << endl; + } + + // close group NXdetector + NXclosegroup(fFileHandle); + + // close group NXinstrument + NXclosegroup(fFileHandle); + + // GET DATA : STILL MISSING : The real data are found under NXentry:NXinstrument:NXdetector + // CHECK if it is necessary to read ANY of these data + + // close group NXentry + NXclosegroup(fFileHandle); + + // close file + NXclose(&fFileHandle); + + fValid = true; + + return NX_OK; } -//----------------------------------------------------------------------------------------------------- -// SetLastGoodBin (public) -//----------------------------------------------------------------------------------------------------- -/** - *

Sets last good bin. The current muSR NeXus implementation has only a single last good bin for all detectors since it was - * tailored for pulsed muon sources (ISIS). This eventually needs to be changed. - * - * \param lgb to be set - * \param idx index of t0 - */ -void PNeXus::SetLastGoodBin(unsigned int lgb, unsigned int idx) -{ - if (idx >= fData.fLastGoodBin.size()) - fData.fLastGoodBin.resize(idx+1); - - fData.fLastGoodBin[idx] = lgb; -} - -//----------------------------------------------------------------------------------------------------- -// SetHisto (public) -//----------------------------------------------------------------------------------------------------- -/** - *

- * - * \param histoNo histogram number (start counting from 0) - * \param data histogram data vector to be set - */ -void PNeXus::SetHisto(unsigned int histoNo, vector &data) -{ - if (histoNo >= fData.fHisto.size()) - fData.fHisto.resize(histoNo+1); - - fData.fHisto[histoNo] = data; -} - -//----------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------ // GroupHistoData (private) -//----------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------ /** *

Feed the grouped histo data, based on the grouping vector and the raw histo data. */ NXstatus PNeXus::GroupHistoData() { - // check if NO grouping is whished, in which case fData.fHisto == fGroupedData - if (fData.fGrouping.size() == 0) { - for (unsigned int i=0; i groupingValue; - bool newGroup = true; - for (unsigned int i=0; iGetData()->GetGrouping()->size() == 0) { + return NX_OK; } - if (newGroup) - groupingValue.push_back(fData.fGrouping[i]); - } - // check that none of the grouping values is outside of the valid range - for (unsigned int i=0; i fData.fHisto.size()) { + // check that the grouping size is equal to the number of histograms + if (fNxEntry1->GetData()->GetGrouping()->size() != fNxEntry1->GetData()->GetNoOfHistos()) { fErrorCode = PNEXUS_HISTO_ERROR; - fErrorMsg = "PNeXus::GroupHistoData() **ERROR** grouping values out of range"; + fErrorMsg = "grouping vector size is unequal to the number of histos present!"; return NX_ERROR; } - } - // set fGroupedData to the proper size - fGroupedData.clear(); - fGroupedData.resize(groupingValue.size()); - for (unsigned int i=0; i groupingValue; + bool newGroup = true; + for (unsigned int i=0; iGetData()->GetGrouping()->size(); i++) { + newGroup = true; + for (unsigned int j=0; jGetData()->GetGrouping()->at(i)) { + newGroup = false; + break; + } + } + if (newGroup) + groupingValue.push_back(fNxEntry1->GetData()->GetGrouping()->at(i)); } - } - // cleanup - groupingValue.clear(); + // check that none of the grouping values is outside of the valid range + for (unsigned int i=0; i= fNxEntry1->GetData()->GetNoOfHistos()) { + fErrorCode = PNEXUS_HISTO_ERROR; + fErrorMsg = "grouping values out of range"; + return NX_ERROR; + } + } + + // set fGroupedHisto to the proper size + fGroupedHisto.clear(); + fGroupedHisto.resize(groupingValue.size()); + for (unsigned int i=0; iGetData()->GetHisto(0)->size()); + } + + for (unsigned int i=0; iGetData()->GetNoOfHistos(); i++) { + for (unsigned int j=0; jGetData()->GetHisto(i)->size(); j++) { + fGroupedHisto[fNxEntry1->GetData()->GetGrouping()->at(i)-1][j] += fNxEntry1->GetData()->GetHisto(i)->at(j); + } + } + + // cleanup + groupingValue.clear(); + } else if (fIdfVersion == 2) { + // will probably do nothing here + } else { + fErrorCode = PNEXUS_HISTO_ERROR; + fErrorMsg = "unsupported IDF"; + return NX_ERROR; + } return NX_OK; } -//----------------------------------------------------------------------------------------------------- -// TransformDate (private) -//----------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------ +// WriteFileIdf1 (private) +//------------------------------------------------------------------------------------------ /** - *

Transformed a given date to the form yyyy-mm-dd if possible. Allowed input dates are yyyy-mm-dd, - * yy-mm-dd, yyyy-MMM-dd, or yy-MMM-dd, where mm is the month as number and MMM the month as string, e.g. APR + *

Write the NeXus file of type IDF Version 1. * - *

return: transformed date + * return: + * - NX_OK on successfull writting + * - NX_ERROR on error. The error code/message will give the details. * - * \param date - * \param ok + * \param fileName file name + * \param access flag needed to know in which format (HDF4, HDF5, or XML) the file shall be written. */ -string PNeXus::TransformDate(string date, bool &ok) +int PNeXus::WriteFileIdf1(const char* fileName, const NXaccess access) { - string result = date; string str; - char cstr[128]; - int status, yy, mm, dd; + char cstr[1204]; + bool ok = false; + int size, idata; + float fdata; - ok = true; + memset(cstr, '\0', sizeof(cstr)); + snprintf(cstr, sizeof(cstr), "couldn't open file '%s' for writing", fileName); + if (!ErrorHandler(NXopen(fileName, access, &fFileHandle), PNEXUS_FILE_OPEN_ERROR, cstr)) return NX_ERROR; - switch(date.length()) { - case 8: // yy-mm-dd - if ((date[2] != '-') || (date[5] != '-')) { - ok = false; - } else { - status = sscanf(date.c_str(), "%d-%d-%d", &yy, &mm, &dd); - if (status != 3) { - ok = false; - } else { - if (yy >= 70) // i.e. 1970 - result = "19"+date; - else // i.e. 20YY - result = "20"+date; - } - } - break; - case 9: // yy-MMM-dd - if ((date[2] != '-') || (date[6] != '-')) { - ok = false; - } else { - str = date.substr(0,2); // yy - status = sscanf(str.c_str(), "%d", &yy); - if (status != 1) - ok = false; - str = date.substr(3,3); // MMM - mm = GetMonth(str); - if (mm == 0) - ok = false; - str = date.substr(7,2); // dd - status = sscanf(str.c_str(), "%d", &dd); - if (status != 1) - ok = false; - if (ok) { - if (yy >= 70) // i.e. 1970 - yy += 1900; - else - yy += 2000; - snprintf(cstr, sizeof(cstr), "%04d-%02d-%02d", yy, mm, dd); - result = cstr; - } - } - break; - case 10: // yyyy-mm-dd - if ((date[4] != '-') || (date[7] != '-')) { - ok = false; - } else { - status = sscanf(date.c_str(), "%04d-%02d-%02d", &yy, &mm, &dd); - if (status != 3) { - ok = false; - } else { - ok = true; - } - } - break; - case 11: // yyyy-MMM-dd - if ((date[4] != '-') || (date[8] != '-')) { - ok = false; - } else { - str = date.substr(0,4); // yyyy - status = sscanf(str.c_str(), "%d", &yy); - if (status != 1) - ok = false; - str = date.substr(5,3); // MMM - mm = GetMonth(str); - if (mm == 0) - ok = false; - str = date.substr(9,2); // dd - status = sscanf(str.c_str(), "%d", &dd); - if (status != 1) - ok = false; - if (ok) { - snprintf(cstr, sizeof(cstr), "%04d-%02d-%02d", yy, mm, dd); - result = cstr; - } - } - break; - default: - ok = false; - break; + // write NXfile attribute NeXus_version + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, NEXUS_VERSION, sizeof(cstr)); + if (!ErrorHandler(NXputattr(fFileHandle, "NeXus_version", cstr, strlen(cstr), NX_CHAR), PNEXUS_SET_ATTR_ERROR, "couldn't set NXfile attribute 'NeXus_version'")) return NX_ERROR; + + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry1->GetUser()->GetName().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputattr(fFileHandle, "user", cstr, strlen(cstr), NX_CHAR), PNEXUS_SET_ATTR_ERROR, "couldn't set NXfile attributes")) return NX_ERROR; + + // make group 'run' + if (!ErrorHandler(NXmakegroup(fFileHandle, "run", "NXentry"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'run'.")) return NX_ERROR; + // open group 'run' + if (!ErrorHandler(NXopengroup(fFileHandle, "run", "NXentry"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'run' for writting.")) return NX_ERROR; + + // write IDF_version + size = 1; + if (!ErrorHandler(NXmakedata(fFileHandle, "IDF_version", NX_INT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'IDF_version'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "IDF_version"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'IDF_version' for writting.")) return NX_ERROR; + idata = fIdfVersion; + if (!ErrorHandler(NXputdata(fFileHandle, &idata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'IDF_version'.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write program_name, and attribute version + size = fNxEntry1->GetProgramName().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "program_name", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'program_name'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "program_name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'program_name' for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry1->GetProgramName().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'program_name'.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry1->GetProgramVersion().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputattr(fFileHandle, "version", cstr, strlen(cstr), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'version' for 'program_name'")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write run 'number' + size = 1; + if (!ErrorHandler(NXmakedata(fFileHandle, "number", NX_INT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'number'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "number"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'number' for writting.")) return NX_ERROR; + idata = fNxEntry1->GetRunNumber(); + if (!ErrorHandler(NXputdata(fFileHandle, &idata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'number'.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write run 'title' + size = fNxEntry1->GetTitle().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "title", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'title'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "title"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'title' for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry1->GetTitle().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'title'.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write run 'notes' + size = fNxEntry1->GetNotes().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "notes", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'notes'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "notes"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'notes' for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry1->GetNotes().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'notes'.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write run 'analysis' + size = fNxEntry1->GetAnalysis().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "analysis", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'analysis'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "analysis"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'analysis' for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry1->GetAnalysis().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'analysis'.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write run 'lab' + size = fNxEntry1->GetLaboratory().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "lab", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'lab'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "lab"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'lab' for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry1->GetLaboratory().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'lab'.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write run 'beamline' + size = fNxEntry1->GetBeamline().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "beamline", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'beamline'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "beamline"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'beamline' for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry1->GetBeamline().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'beamline'.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write run 'start_time' + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry1->GetStartTime().c_str(), sizeof(cstr)); + size = strlen(cstr); + if (!ErrorHandler(NXmakedata(fFileHandle, "start_time", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'start_time'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "start_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'start_time' for writting.")) return NX_ERROR; + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'start_time'.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write run 'stop_time' + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry1->GetStopTime().c_str(), sizeof(cstr)); + size = strlen(cstr); + if (!ErrorHandler(NXmakedata(fFileHandle, "stop_time", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'stop_time'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "stop_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'stop_time' for writting.")) return NX_ERROR; + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'stop_time'.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write run 'switching_states' + size = 1; + if (!ErrorHandler(NXmakedata(fFileHandle, "switching_states", NX_INT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'switching_states'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "switching_states"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'switching_states' for writting.")) return NX_ERROR; + idata = fNxEntry1->GetSwitchingState(); + if (!ErrorHandler(NXputdata(fFileHandle, &idata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'switching_states'.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // make group 'user' + if (!ErrorHandler(NXmakegroup(fFileHandle, "user", "NXuser"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'user'.")) return NX_ERROR; + // open group 'user' + if (!ErrorHandler(NXopengroup(fFileHandle, "user", "NXuser"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'user' for writting.")) return NX_ERROR; + + // write user 'name' + size = fNxEntry1->GetUser()->GetName().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "name", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'name'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'name' for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry1->GetUser()->GetName().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'name'.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write user 'experiment_number' + size = fNxEntry1->GetUser()->GetExperimentNumber().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "experiment_number", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'experiment_number'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "experiment_number"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'experiment_number' for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry1->GetUser()->GetExperimentNumber().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'experiment_number'.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // close group 'user' + NXclosegroup(fFileHandle); + + // make group 'sample' + if (!ErrorHandler(NXmakegroup(fFileHandle, "sample", "NXuser"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'sample'.")) return NX_ERROR; + // open group 'sample' + if (!ErrorHandler(NXopengroup(fFileHandle, "sample", "NXuser"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'sample' for writting.")) return NX_ERROR; + + // write sample 'name' + size = fNxEntry1->GetSample()->GetName().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "name", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'name'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'name' for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry1->GetSample()->GetName().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'name'.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write sample 'temperature' + size = 1; + if (!ErrorHandler(NXmakedata(fFileHandle, "temperature", NX_FLOAT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'temperature'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "temperature"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'temperature' for writting.")) return NX_ERROR; + fdata = (float)fNxEntry1->GetSample()->GetPhysPropValue(string("temperature"), ok); + if (!ErrorHandler(NXputdata(fFileHandle, &fdata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'temperature'.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + str = string("n/a"); + fNxEntry1->GetSample()->GetPhysPropUnit(string("temperature"), str, ok); + strncpy(cstr, str.c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputattr(fFileHandle, "units", cstr, strlen(cstr), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'temperature'")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write sample 'magnetic_field' + size = 1; + if (!ErrorHandler(NXmakedata(fFileHandle, "magnetic_field", NX_FLOAT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'magnetic_field'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "magnetic_field"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'magnetic_field' for writting.")) return NX_ERROR; + fdata = (float)fNxEntry1->GetSample()->GetPhysPropValue(string("magnetic_field"), ok); + if (!ErrorHandler(NXputdata(fFileHandle, &fdata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'magnetic_field'.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + str = string("n/a"); + fNxEntry1->GetSample()->GetPhysPropUnit(string("magnetic_field"), str, ok); + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, str.c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputattr(fFileHandle, "units", cstr, strlen(cstr), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'magnetic_field'")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write sample 'shape' + size = fNxEntry1->GetSample()->GetShape().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "shape", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'shape'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "shape"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'shape' for writting.")) return NX_ERROR; + strncpy(cstr, fNxEntry1->GetSample()->GetShape().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'shape'.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write sample 'magnetic_field_state' + size = fNxEntry1->GetSample()->GetMagneticFieldState().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "magnetic_field_state", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'magnetic_field_state'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "magnetic_field_state"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'magnetic_field_state' for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry1->GetSample()->GetMagneticFieldState().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'magnetic_field_state'.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write sample 'magnetic_field_vector' + float *magFieldVec; + if (fNxEntry1->GetSample()->IsMagneticFieldVectorAvailable()) { + idata = 1; + size = fNxEntry1->GetSample()->GetMagneticFieldVector().size(); + magFieldVec = new float[size]; + for (int i=0; iGetSample()->GetMagneticFieldVector().at(i); + } else { + idata = 0; + size = 3; + magFieldVec = new float[size]; + for (int i=0; iGetSample()->GetMagneticFieldVectorCoordinateSystem().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputattr(fFileHandle, "coordinate_system", cstr, strlen(cstr), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'coordinate_system' for 'magnetic_field_vector'")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry1->GetSample()->GetMagneticFieldVectorUnits().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputattr(fFileHandle, "units", cstr, strlen(cstr), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'magnetic_field_vector'")) return NX_ERROR; + if (!ErrorHandler(NXputattr(fFileHandle, "available", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'available' for 'magnetic_field_vector'")) return NX_ERROR; + NXclosedata(fFileHandle); + if (magFieldVec) { + delete [] magFieldVec; + magFieldVec = 0; } - if (!ok) { - fErrorCode = PNEXUS_WRONG_DATE_FORMAT; - fErrorMsg = "PNeXus::TransformDate **ERROR** given date="+date+", is not of any of the the required forms!"; + // write sample 'environment' + size = fNxEntry1->GetSample()->GetEnvironment().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "environment", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'environment'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "environment"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'environment' for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry1->GetSample()->GetEnvironment().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'environment'.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // close group 'sample' + NXclosegroup(fFileHandle); + + // make group 'instrument' + if (!ErrorHandler(NXmakegroup(fFileHandle, "instrument", "NXinstrument"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'instrument'.")) return NX_ERROR; + // open group 'instrument' + if (!ErrorHandler(NXopengroup(fFileHandle, "instrument", "NXinstrument"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'instrument' for writting.")) return NX_ERROR; + + // write instrument 'name' + size = fNxEntry1->GetInstrument()->GetName().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "name", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'name'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'name' for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry1->GetInstrument()->GetName().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'name'.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // make group 'detector' + if (!ErrorHandler(NXmakegroup(fFileHandle, "detector", "NXdetector"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'detector'.")) return NX_ERROR; + // open group 'detector' + if (!ErrorHandler(NXopengroup(fFileHandle, "detector", "NXdetector"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'detector' for writting.")) return NX_ERROR; + + // write detector 'number' + size = 1; + if (!ErrorHandler(NXmakedata(fFileHandle, "number", NX_INT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'number'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "number"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'number' for writting.")) return NX_ERROR; + idata = fNxEntry1->GetInstrument()->GetDetector()->GetNumber(); + if (!ErrorHandler(NXputdata(fFileHandle, &idata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'number'.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // close group 'detector' + NXclosegroup(fFileHandle); + + // make group 'collimator' + if (!ErrorHandler(NXmakegroup(fFileHandle, "collimator", "NXcollimator"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'collimator'.")) return NX_ERROR; + // open group 'collimator' + if (!ErrorHandler(NXopengroup(fFileHandle, "collimator", "NXcollimator"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'collimator' for writting.")) return NX_ERROR; + + // write collimator 'type' + size = fNxEntry1->GetInstrument()->GetCollimator()->GetType().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "type", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'type'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "type"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'type' for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry1->GetInstrument()->GetCollimator()->GetType().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'type'.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // close group 'collimator' + NXclosegroup(fFileHandle); + + // make group 'beam' + if (!ErrorHandler(NXmakegroup(fFileHandle, "beam", "NXbeam"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'beam'.")) return NX_ERROR; + // open group 'beam' + if (!ErrorHandler(NXopengroup(fFileHandle, "beam", "NXbeam"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'beam' for writting.")) return NX_ERROR; + + size = 1; + if (!ErrorHandler(NXmakedata(fFileHandle, "total_counts", NX_FLOAT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'total_counts'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "total_counts"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'total_counts' for writting.")) return NX_ERROR; + fdata = (float)fNxEntry1->GetInstrument()->GetBeam()->GetTotalCounts(); + if (!ErrorHandler(NXputdata(fFileHandle, &fdata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'total_counts'.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry1->GetInstrument()->GetBeam()->GetUnits().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputattr(fFileHandle, "units", cstr, strlen(cstr), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'total_counts'")) return NX_ERROR; + NXclosedata(fFileHandle); + + // close group 'beam' + NXclosegroup(fFileHandle); + + // close group 'instrument' + NXclosegroup(fFileHandle); + + // make group 'histogram_data_1' + if (!ErrorHandler(NXmakegroup(fFileHandle, "histogram_data_1", "NXdata"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'histogram_data_1'.")) return NX_ERROR; + // open group 'histogram_data_1' + if (!ErrorHandler(NXopengroup(fFileHandle, "histogram_data_1", "NXdata"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'histogram_data_1' for writting.")) return NX_ERROR; + + // write data 'counts' + int *histo_data=0; + int histo_size[2]; + if (fNxEntry1->GetData()->GetNoOfHistos() == 0) { + histo_data = new int[1]; + histo_data[0] = -1; + histo_size[0] = 1; + histo_size[1] = 1; + } else { + int noOfHistos = fNxEntry1->GetData()->GetNoOfHistos(); + int histoLength = fNxEntry1->GetData()->GetHisto(0)->size(); + histo_data = new int[noOfHistos*histoLength]; + for (int i=0; iGetData()->GetHisto(i)->at(j); + histo_size[0] = noOfHistos; + histo_size[1] = histoLength; } - return result; + if (!ErrorHandler(NXmakedata(fFileHandle, "counts", NX_INT32, 2, histo_size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'counts'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "counts"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'counts' for writting.")) return NX_ERROR; + if (!ErrorHandler(NXputdata(fFileHandle, (void*)histo_data), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'counts'.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, "counts", sizeof(cstr)); + if (!ErrorHandler(NXputattr(fFileHandle, "units", cstr, strlen(cstr), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'counts'")) return NX_ERROR; + idata = 1; + if (!ErrorHandler(NXputattr(fFileHandle, "signal", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'signal' for 'counts'")) return NX_ERROR; + idata = fNxEntry1->GetData()->GetNoOfHistos(); + if (!ErrorHandler(NXputattr(fFileHandle, "number", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'number' for 'counts'")) return NX_ERROR; + idata = fNxEntry1->GetData()->GetT0(0); + if (!ErrorHandler(NXputattr(fFileHandle, "T0_bin", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'T0_bin' for 'counts'")) return NX_ERROR; + idata = fNxEntry1->GetData()->GetFirstGoodBin(0); + if (!ErrorHandler(NXputattr(fFileHandle, "first_good_bin", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'first_good_bin' for 'counts'")) return NX_ERROR; + idata = fNxEntry1->GetData()->GetLastGoodBin(0); + if (!ErrorHandler(NXputattr(fFileHandle, "last_good_bin", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'last_good_bin' for 'counts'")) return NX_ERROR; + fdata = (float)fNxEntry1->GetData()->GetTimeResolution("ps")/2.0; + if (!ErrorHandler(NXputattr(fFileHandle, "offset", &fdata, 1, NX_FLOAT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'offset' for 'counts'")) return NX_ERROR; + + if (histo_data) { + delete [] histo_data; + } + + // write data 'histogram_resolution' + if (!ErrorHandler(NXmakedata(fFileHandle, "histogram_resolution", NX_FLOAT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'histogram_resolution'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "histogram_resolution"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'histogram_resolution' for writting.")) return NX_ERROR; + fdata = fNxEntry1->GetData()->GetTimeResolution("ps"); + if (!ErrorHandler(NXputdata(fFileHandle, &fdata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'histogram_resolution'.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, "picoseconds", sizeof(cstr)); + if (!ErrorHandler(NXputattr(fFileHandle, "units", cstr, strlen(cstr), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'histogram_resolution'")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write data 'time_zero' always set to 0 since the T0_bin attribute of counts is relevant only + if (!ErrorHandler(NXmakedata(fFileHandle, "time_zero", NX_FLOAT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'time_zero'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "time_zero"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'time_zero' for writting.")) return NX_ERROR; + fdata = 0.0; + if (!ErrorHandler(NXputdata(fFileHandle, &fdata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'time_zero'.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, "microseconds", sizeof(cstr)); + if (!ErrorHandler(NXputattr(fFileHandle, "units", cstr, strlen(cstr), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'time_zero'")) return NX_ERROR; + idata = 0; + if (!ErrorHandler(NXputattr(fFileHandle, "available", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'available' for 'time_zero'")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write data 'raw_time' + int noOfHistos = fNxEntry1->GetData()->GetNoOfHistos(); + int histoLength = fNxEntry1->GetData()->GetHisto(0)->size(); + if (noOfHistos == 0) { + fErrorCode = PNEXUS_HISTO_ERROR; + fErrorMsg = "no data for writing present."; + return NX_ERROR; + } + float *raw_time = new float[histoLength]; + for (int i=0; iGetData()->GetTimeResolution("us") * (float)i; // raw time in (us) + } + size = histoLength; + if (!ErrorHandler(NXmakedata(fFileHandle, "raw_time", NX_FLOAT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'raw_time'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "raw_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'raw_time' for writting.")) return NX_ERROR; + if (!ErrorHandler(NXputdata(fFileHandle, raw_time), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'raw_time'.")) return NX_ERROR; + idata = 1; + if (!ErrorHandler(NXputattr(fFileHandle, "axis", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'axis' for 'raw_time'")) return NX_ERROR; + idata = 1; + if (!ErrorHandler(NXputattr(fFileHandle, "primary", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'primary' for 'raw_time'")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, "microseconds", sizeof(cstr)); + if (!ErrorHandler(NXputattr(fFileHandle, "units", cstr, strlen(cstr), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'raw_time'")) return NX_ERROR; + NXclosedata(fFileHandle); + if (raw_time) { + delete [] raw_time; + raw_time = 0; + } + + // write data 'corrected_time' + float *corrected_time = new float[histoLength]; + for (int i=0; iGetData()->GetTimeResolution("us") * (float)((int)i-(int)fNxEntry1->GetData()->GetT0(0)+1); // raw time in (us) + } + size = histoLength; + if (!ErrorHandler(NXmakedata(fFileHandle, "corrected_time", NX_FLOAT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'corrected_time'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "corrected_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'corrected_time' for writting.")) return NX_ERROR; + if (!ErrorHandler(NXputdata(fFileHandle, corrected_time), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'corrected_time'.")) return NX_ERROR; + idata = 1; + if (!ErrorHandler(NXputattr(fFileHandle, "axis", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'axis' for 'corrected_time'")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, "microseconds", sizeof(cstr)); + if (!ErrorHandler(NXputattr(fFileHandle, "units", cstr, strlen(cstr), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'corrected_time'")) return NX_ERROR; + NXclosedata(fFileHandle); + if (corrected_time) { + delete [] corrected_time; + corrected_time = 0; + } + + // write data 'grouping' + int *grouping = new int[noOfHistos]; + vector groupNo; // keep the number of different groupings + if (noOfHistos == (int)fNxEntry1->GetData()->GetGrouping()->size()) { // grouping vector seems to be properly defined + bool found; + groupNo.push_back(fNxEntry1->GetData()->GetGrouping()->at(0)); + for (int i=0; iGetData()->GetGrouping()->at(i); + found = false; + for (unsigned int j=0; jGetData()->GetGrouping()->at(i) == groupNo[j]) { + found = true; + break; + } + } + if (!found) { + groupNo.push_back(fNxEntry1->GetData()->GetGrouping()->at(i)); + } + } + } else { // grouping vector not available + for (int i=0; iGetData()->GetAlpha()->size() == 0) { + alpha = new float[3]; + alpha[0] = -1.0; + alpha[1] = -1.0; + alpha[2] = -1.0; + array_size[0] = 1; + array_size[1] = 3; + } else { + alpha = new float[fNxEntry1->GetData()->GetAlpha()->size()*3]; + for (unsigned int i=0; iGetData()->GetAlpha()->size(); i++) { + alpha[i] = (float)fNxEntry1->GetData()->GetAlpha()->at(i).GetGroupFirst(); + alpha[i+1] = (float)fNxEntry1->GetData()->GetAlpha()->at(i).GetGroupSecond(); + alpha[i+2] = (float)(float)fNxEntry1->GetData()->GetAlpha()->at(i).GetAlpha(); + } + array_size[0] = fNxEntry1->GetData()->GetAlpha()->size(); + array_size[1] = 3; + } + if (!ErrorHandler(NXmakedata(fFileHandle, "alpha", NX_FLOAT32, 2, array_size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'alpha'.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "alpha"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'alpha' for writting.")) return NX_ERROR; + if (!ErrorHandler(NXputdata(fFileHandle, (void*)alpha), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'alpha'.")) return NX_ERROR; + idata = fNxEntry1->GetData()->GetAlpha()->size(); + if (!ErrorHandler(NXputattr(fFileHandle, "available", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'available' for 'alpha'")) return NX_ERROR; + if (alpha) { + delete [] alpha; + } + + // close group 'histogram_data_1' + NXclosegroup(fFileHandle); + + // close group 'run' + NXclosegroup(fFileHandle); + + NXclose(&fFileHandle); + + return NX_OK; } -//----------------------------------------------------------------------------------------------------- -// GetMonth (private) -//----------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------ +// WriteFileIdf2 (private) +//------------------------------------------------------------------------------------------ /** - *

For a month string MMM (e.g. APR) return the month number + *

Write the NeXus file of type IDF Version 2. * - *

return: month number + * return: + * - NX_OK on successfull writting + * - NX_ERROR on error. The error code/message will give the details. * - * \param month string of the form MMM, e.g. APR + * \param fileName file name + * \param access flag needed to know in which format (HDF4, HDF5, or XML) the file shall be written. */ -int PNeXus::GetMonth(const string month) +int PNeXus::WriteFileIdf2(const char* fileName, const NXaccess access) { - int result = 0; + string str; + char cstr[1204]; + bool ok = false; + int size, idata; + float fdata; + double dval; + NXlink clink; - if (!month.compare("jan") || !month.compare("Jan") || !month.compare("JAN")) - result = 1; - else if (!month.compare("feb") || !month.compare("Feb") || !month.compare("FEB")) - result = 2; - else if (!month.compare("mar") || !month.compare("Mar") || !month.compare("MAR")) - result = 3; - else if (!month.compare("apr") || !month.compare("Apr") || !month.compare("APR")) - result = 4; - else if (!month.compare("may") || !month.compare("May") || !month.compare("MAY")) - result = 5; - else if (!month.compare("jun") || !month.compare("Jun") || !month.compare("JUN")) - result = 6; - else if (!month.compare("jul") || !month.compare("Jul") || !month.compare("JUL")) - result = 7; - else if (!month.compare("aug") || !month.compare("Aug") || !month.compare("AUG")) - result = 8; - else if (!month.compare("sep") || !month.compare("Sep") || !month.compare("SEP")) - result = 9; - else if (!month.compare("oct") || !month.compare("Oct") || !month.compare("OCT")) - result = 10; - else if (!month.compare("nov") || !month.compare("Nov") || !month.compare("NOV")) - result = 11; - else if (!month.compare("dec") || !month.compare("Dec") || !month.compare("DEC")) - result = 12; - else - result = 0; + memset(cstr, '\0', sizeof(cstr)); + snprintf(cstr, sizeof(cstr), "couldn't open file '%s' for writing", fileName); + if (!ErrorHandler(NXopen(fileName, access, &fFileHandle), PNEXUS_FILE_OPEN_ERROR, cstr)) return NX_ERROR; - return result; + // write NXroot attribute 'file_name' + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fileName, sizeof(cstr)); + if (!ErrorHandler(NXputattr(fFileHandle, "file_name", cstr, strlen(cstr), NX_CHAR), PNEXUS_SET_ATTR_ERROR, "couldn't set NXroot attribute 'file_name'")) return NX_ERROR; + + // write NXroot attribute 'creator' + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fCreator.c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputattr(fFileHandle, "creator", cstr, strlen(cstr), NX_CHAR), PNEXUS_SET_ATTR_ERROR, "couldn't set NXroot attribute 'creator'")) return NX_ERROR; + + // make group 'raw_data_1' + if (!ErrorHandler(NXmakegroup(fFileHandle, "raw_data_1", "NXentry"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'raw_data_1' in NXroot.")) return NX_ERROR; + // open group 'raw_data_1' + if (!ErrorHandler(NXopengroup(fFileHandle, "raw_data_1", "NXentry"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'raw_data_1' in NXroot for writting.")) return NX_ERROR; + + // write idf_version + size = 1; + if (!ErrorHandler(NXmakedata(fFileHandle, "idf_version", NX_INT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'idf_version' in NXentry.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "idf_version"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'idf_version' in NXentry for writting.")) return NX_ERROR; + idata = fIdfVersion; + if (!ErrorHandler(NXputdata(fFileHandle, &idata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'idf_version' in NXentry.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write run 'definition' + size = fNxEntry2->GetDefinition().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "definition", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'definition' in NXentry.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "definition"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'definition' in NXentry for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry2->GetDefinition().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'definition' in NXentry.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write run 'run_number' + size = 1; + if (!ErrorHandler(NXmakedata(fFileHandle, "run_number", NX_INT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'run_number' in NXentry.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "run_number"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'run_number' in NXentry for writting.")) return NX_ERROR; + idata = fNxEntry2->GetRunNumber(); + if (!ErrorHandler(NXputdata(fFileHandle, &idata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'run_number' in NXentry.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write run 'title' + size = fNxEntry2->GetTitle().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "title", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'title' in NXentry.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "title"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'title' in NXentry for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry2->GetTitle().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'title' in NXentry.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write run 'start_time' + size = fNxEntry2->GetStartTime().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "start_time", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'start_time' in NXentry.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "start_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'start_time' in NXentry for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry2->GetStartTime().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'start_time' in NXentry.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write run 'end_time' + size = fNxEntry2->GetStopTime().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "end_time", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'end_time' in NXentry.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "end_time"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'end_time' in NXentry for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry2->GetStopTime().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'end_time' in NXentry.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write run 'experiment_identifier' + size = fNxEntry2->GetExperimentIdentifier().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "experiment_identifier", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'experiment_identifier' in NXentry.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "experiment_identifier"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'experiment_identifier' in NXentry for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry2->GetExperimentIdentifier().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'experiment_identifier' in NXentry.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // make group 'user_1' + if (!ErrorHandler(NXmakegroup(fFileHandle, "user_1", "NXuser"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'user_1' in NXentry.")) return NX_ERROR; + // open group 'user' + if (!ErrorHandler(NXopengroup(fFileHandle, "user_1", "NXuser"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'user_1' in NXentry for writting.")) return NX_ERROR; + + // write user 'name' + size = fNxEntry2->GetUser()->GetName().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "name", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'name' in NXuser.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'name' in NXuser for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry2->GetUser()->GetName().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'name' in NXuser.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // close group 'user_1' + NXclosegroup(fFileHandle); + + // make group 'sample' + if (!ErrorHandler(NXmakegroup(fFileHandle, "sample", "NXsample"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'sample' in NXentry.")) return NX_ERROR; + // open group 'sample' + if (!ErrorHandler(NXopengroup(fFileHandle, "sample", "NXsample"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'sample' in NXentry for writting.")) return NX_ERROR; + + // write sample 'name' + size = fNxEntry2->GetSample()->GetName().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "name", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'name' in NXsample.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'name' in NXsample for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry2->GetSample()->GetName().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'name' in NXsample.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // check if description is present + if (fNxEntry2->GetSample()->GetDescription().compare("n/a")) { + // write sample 'description' + size = fNxEntry2->GetSample()->GetDescription().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "description", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'description' in NXsample.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "description"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'description' in NXsample for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry2->GetSample()->GetDescription().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'description' in NXsample.")) return NX_ERROR; + NXclosedata(fFileHandle); + } + + // check if temperature is present and if yes, write it into the file + ok=false; + dval = fNxEntry2->GetSample()->GetPhysPropValue("temperature", ok); + if (ok) { + fdata = (float)dval; + fNxEntry2->GetSample()->GetPhysPropUnit("temperature", str, ok); + } + if (ok) { + size = 1; + if (!ErrorHandler(NXmakedata(fFileHandle, "temperature_1", NX_FLOAT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'temperature_1' in NXsample.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "temperature_1"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'temperature_1' in NXsample for writting.")) return NX_ERROR; + if (!ErrorHandler(NXputdata(fFileHandle, (void*)&fdata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'temperature_1' in NXsample.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, str.c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputattr(fFileHandle, "units", cstr, strlen(cstr), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'temperature_1' in NXsample.")) return NX_ERROR; + NXclosedata(fFileHandle); + } + + // check if temperature environment info is present + if (fNxEntry2->GetSample()->GetEnvironmentTemp().compare("n/a")) { + // write sample 'temperature_1_env' + size = fNxEntry2->GetSample()->GetEnvironmentTemp().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "temperature_1_env", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'temperature_1_env' in NXsample.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "temperature_1_env"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'temperature_1_env' in NXsample for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry2->GetSample()->GetEnvironmentTemp().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'temperature_1_env' in NXsample.")) return NX_ERROR; + NXclosedata(fFileHandle); + } + + // check if magnetic field is present and if yes, write it into the file + ok=false; + dval = fNxEntry2->GetSample()->GetPhysPropValue("magnetic_field", ok); + if (ok) { + fdata = (float)dval; + fNxEntry2->GetSample()->GetPhysPropUnit("magnetic_field", str, ok); + } + if (ok) { + size = 1; + if (!ErrorHandler(NXmakedata(fFileHandle, "magnetic_field_1", NX_FLOAT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'magnetic_field_1' in NXsample.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "magnetic_field_1"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'magnetic_field_1' in NXsample for writting.")) return NX_ERROR; + if (!ErrorHandler(NXputdata(fFileHandle, (void*)&fdata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'magnetic_field_1' in NXsample.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, str.c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputattr(fFileHandle, "units", cstr, strlen(cstr), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'units' for 'magnetic_field_1' in NXsample.")) return NX_ERROR; + NXclosedata(fFileHandle); + } + + // check if magnetic field environment info is present + if (fNxEntry2->GetSample()->GetEnvironmentField().compare("n/a")) { + // write sample 'magnetic_field_1_env' + size = fNxEntry2->GetSample()->GetEnvironmentField().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "magnetic_field_1_env", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'magnetic_field_1_env' in NXsample.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "magnetic_field_1_env"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'magnetic_field_1_env' in NXsample for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry2->GetSample()->GetEnvironmentField().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'magnetic_field_1_env' in NXsample.")) return NX_ERROR; + NXclosedata(fFileHandle); + } + + // check if magnetic field state info is present + if (fNxEntry2->GetSample()->GetMagneticFieldState().compare("n/a")) { + // write sample 'magnetic_field_state' + size = fNxEntry2->GetSample()->GetMagneticFieldState().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "magnetic_field_state", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'magnetic_field_state' in NXsample.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "magnetic_field_state"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'magnetic_field_state' in NXsample for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry2->GetSample()->GetMagneticFieldState().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'magnetic_field_state' in NXsample.")) return NX_ERROR; + NXclosedata(fFileHandle); + } + + // close group 'sample' + NXclosegroup(fFileHandle); + + + // make group 'instrument' + if (!ErrorHandler(NXmakegroup(fFileHandle, "instrument", "NXinstrument"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'instrument' in NXentry.")) return NX_ERROR; + // open group 'instrument' + if (!ErrorHandler(NXopengroup(fFileHandle, "instrument", "NXinstrument"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'instrument' in NXentry for writting.")) return NX_ERROR; + + // write instrument 'name' + size = fNxEntry2->GetInstrument()->GetName().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "name", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'name' in NXinstrument.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'name' in NXinstrument for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry2->GetInstrument()->GetName().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'name' in NXinstrument.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // make group 'source' + if (!ErrorHandler(NXmakegroup(fFileHandle, "source", "NXsource"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'source' in NXinstrument.")) return NX_ERROR; + // open group 'source' + if (!ErrorHandler(NXopengroup(fFileHandle, "source", "NXsource"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'source' in NXentry for NXinstrument.")) return NX_ERROR; + + // write instrument 'name' + size = fNxEntry2->GetInstrument()->GetSource()->GetName().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "name", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'name' in NXsource.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'name' in NXsource for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry2->GetInstrument()->GetSource()->GetName().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'name' in NXsource.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write instrument 'type' + size = fNxEntry2->GetInstrument()->GetSource()->GetType().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "type", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'type' in NXsource.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "type"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'type' in NXsource for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry2->GetInstrument()->GetSource()->GetType().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'type' in NXsource.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write instrument 'probe' + size = fNxEntry2->GetInstrument()->GetSource()->GetProbe().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "probe", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'probe' in NXsource.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "probe"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'probe' in NXsource for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry2->GetInstrument()->GetSource()->GetProbe().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'probe' in NXsource.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // close group 'source' + NXclosegroup(fFileHandle); + + // make group 'beamline' + if (!ErrorHandler(NXmakegroup(fFileHandle, "beamline", "NXbeamline"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'beamline' in NXinstrument.")) return NX_ERROR; + // open group 'beamline' + if (!ErrorHandler(NXopengroup(fFileHandle, "beamline", "NXbeamline"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'beamline' in NXentry for NXinstrument.")) return NX_ERROR; + + // write instrument 'name' + size = fNxEntry2->GetInstrument()->GetBeamline()->GetName().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "name", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'name' in NXbeamline.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "name"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'name' in NXbeamline for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry2->GetInstrument()->GetBeamline()->GetName().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'name' in NXbeamline.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // close group 'beamline' + NXclosegroup(fFileHandle); + + // make group 'detector_1' + if (!ErrorHandler(NXmakegroup(fFileHandle, "detector_1", "NXdetector"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'detector_1' in NXinstrument.")) return NX_ERROR; + // open group 'detector_1' + if (!ErrorHandler(NXopengroup(fFileHandle, "detector_1", "NXdetector"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'detector_1' in NXinstrument.")) return NX_ERROR; + + // write detector_1 'description' + size = fNxEntry2->GetInstrument()->GetDetector()->GetDescription().length(); + if (!ErrorHandler(NXmakedata(fFileHandle, "description", NX_CHAR, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'description' in NXdetector.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "description"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'description' in NXdetector for writting.")) return NX_ERROR; + memset(cstr, '\0', sizeof(cstr)); + strncpy(cstr, fNxEntry2->GetInstrument()->GetDetector()->GetDescription().c_str(), sizeof(cstr)); + if (!ErrorHandler(NXputdata(fFileHandle, cstr), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'description' in NXdetector.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write detector_1 'counts' + int dims[3]; + if (fNxEntry2->GetInstrument()->GetDetector()->GetNoOfPeriods() > 0) { // counts[][][] + dims[0] = fNxEntry2->GetInstrument()->GetDetector()->GetNoOfPeriods(); + dims[1] = fNxEntry2->GetInstrument()->GetDetector()->GetNoOfSpectra(); + dims[2] = fNxEntry2->GetInstrument()->GetDetector()->GetNoOfBins(); + if (!ErrorHandler(NXmakedata(fFileHandle, "counts", NX_INT32, 3, dims), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'counts' in NXdetector.")) return NX_ERROR; + } else { + if (fNxEntry2->GetInstrument()->GetDetector()->GetNoOfSpectra() > 0) { // counts[][] + dims[0] = fNxEntry2->GetInstrument()->GetDetector()->GetNoOfSpectra(); + dims[1] = fNxEntry2->GetInstrument()->GetDetector()->GetNoOfBins(); + if (!ErrorHandler(NXmakedata(fFileHandle, "counts", NX_INT32, 2, dims), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'counts' in NXdetector.")) return NX_ERROR; + } else { // counts[] + dims[0] = fNxEntry2->GetInstrument()->GetDetector()->GetNoOfBins(); + if (!ErrorHandler(NXmakedata(fFileHandle, "counts", NX_INT32, 1, dims), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'counts' in NXdetector.")) return NX_ERROR; + } + } + if (!ErrorHandler(NXopendata(fFileHandle, "counts"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'counts' in NXdetector for writting.")) return NX_ERROR; + if (!ErrorHandler(NXputdata(fFileHandle, (void*)fNxEntry2->GetInstrument()->GetDetector()->GetHistos()), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'counts' in NXdetector.")) return NX_ERROR; + + // write 'counts' attributes + idata = 1; + if (!ErrorHandler(NXputattr(fFileHandle, "signal", &idata, 1, NX_INT32), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'signal' for 'counts' in NXdetector.")) return NX_ERROR; + + memset(cstr, '\0', sizeof(cstr)); + if (fNxEntry2->GetInstrument()->GetDetector()->GetNoOfPeriods() > 0) { // counts[][][] + strncpy(cstr, "[period_index, spectrum_index, raw_time_bin]", sizeof(cstr)); + } else { + if (fNxEntry2->GetInstrument()->GetDetector()->GetNoOfSpectra() > 0) { // counts[][] + strncpy(cstr, "[spectrum_index, raw_time_bin]", sizeof(cstr)); + } else { // counts[] + strncpy(cstr, "[raw_time_bin]", sizeof(cstr)); + } + } + if (!ErrorHandler(NXputattr(fFileHandle, "axes", cstr, strlen(cstr), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'axes' for 'counts' in NXdetector.")) return NX_ERROR; + + memset(cstr, '\0', sizeof(cstr)); + if (!fNxEntry2->GetInstrument()->GetSource()->GetProbe().compare("positive muons")) { + strncpy(cstr, "positron counts", sizeof(cstr)); + } else if (!fNxEntry2->GetInstrument()->GetSource()->GetProbe().compare("negative muons")) { + strncpy(cstr, "electron counts", sizeof(cstr)); + } else { + strncpy(cstr, "n/a", sizeof(cstr)); + } + if (!ErrorHandler(NXputattr(fFileHandle, "long_name", cstr, strlen(cstr), NX_CHAR), PNEXUS_PUT_ATTR_ERROR, "couldn't put attribute 'long_name' for 'counts' in NXdetector.")) return NX_ERROR; + // create link of 'counts' for NXdata + if (!ErrorHandler(NXgetdataID(fFileHandle, &clink), PNEXUS_LINKING_ERROR, "couldn't obtain link of 'counts' in NXdetector.")) return NX_ERROR; + NXclosedata(fFileHandle); + + // write detector_1 'spectrum_index' + size = fNxEntry2->GetInstrument()->GetDetector()->GetSpectrumIndexSize(); + int *p_idata = new int[size]; + for (int i=0; iGetInstrument()->GetDetector()->GetSpectrumIndex(i); + if (!ErrorHandler(NXmakedata(fFileHandle, "spectrum_index", NX_INT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'spectrum_index' in NXdetector.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "spectrum_index"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'spectrum_index' in NXdetector for writting.")) return NX_ERROR; + if (!ErrorHandler(NXputdata(fFileHandle, (void*)p_idata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'spectrum_index' in NXdetector.")) return NX_ERROR; + NXclosedata(fFileHandle); + if (p_idata) { + delete [] p_idata; + p_idata = 0; + } + + // write detector_1 'time_zero_bin' if present + if (fNxEntry2->GetInstrument()->GetDetector()->IsT0Present()) { + if (fNxEntry2->GetInstrument()->GetDetector()->GetT0Tag() == 1) { + size = 1; + idata = fNxEntry2->GetInstrument()->GetDetector()->GetT0(); + if (!ErrorHandler(NXmakedata(fFileHandle, "time_zero_bin", NX_INT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'time_zero_bin' in NXdetector.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "time_zero_bin"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'time_zero_bin' in NXdetector for writting.")) return NX_ERROR; + if (!ErrorHandler(NXputdata(fFileHandle, (void*)&idata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'time_zero_bin' in NXdetector.")) return NX_ERROR; + NXclosedata(fFileHandle); + } else if (fNxEntry2->GetInstrument()->GetDetector()->GetT0Tag() == 2) { + int dims[2]; + dims[0] = fNxEntry2->GetInstrument()->GetDetector()->GetNoOfPeriods(); + dims[1] = fNxEntry2->GetInstrument()->GetDetector()->GetNoOfSpectra(); + if (!ErrorHandler(NXmakedata(fFileHandle, "time_zero_bin", NX_INT32, 2, dims), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'time_zero_bin' in NXdetector.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "time_zero_bin"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'time_zero_bin' in NXdetector for writting.")) return NX_ERROR; + if (!ErrorHandler(NXputdata(fFileHandle, (void*)fNxEntry2->GetInstrument()->GetDetector()->GetT0s()), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'time_zero_bin' in NXdetector.")) return NX_ERROR; + NXclosedata(fFileHandle); + } else { + cerr << endl << ">> **WARNING** time_zero_bin with rank " << fNxEntry2->GetInstrument()->GetDetector()->GetT0Tag() << " requested. Do not know how to handle." << endl; + } + } + + // write detector_1 'first_good_bin' if present + if (fNxEntry2->GetInstrument()->GetDetector()->IsFirstGoodBinPresent()) { + if (fNxEntry2->GetInstrument()->GetDetector()->GetT0Tag() == 1) { + size = 1; + idata = fNxEntry2->GetInstrument()->GetDetector()->GetFirstGoodBin(); + if (!ErrorHandler(NXmakedata(fFileHandle, "first_good_bin", NX_INT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'first_good_bin' in NXdetector.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "first_good_bin"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'first_good_bin' in NXdetector for writting.")) return NX_ERROR; + if (!ErrorHandler(NXputdata(fFileHandle, (void*)&idata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'first_good_bin' in NXdetector.")) return NX_ERROR; + NXclosedata(fFileHandle); + } else if (fNxEntry2->GetInstrument()->GetDetector()->GetT0Tag() == 2) { + int dims[2]; + dims[0] = fNxEntry2->GetInstrument()->GetDetector()->GetNoOfPeriods(); + dims[1] = fNxEntry2->GetInstrument()->GetDetector()->GetNoOfSpectra(); + if (!ErrorHandler(NXmakedata(fFileHandle, "first_good_bin", NX_INT32, 2, dims), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'first_good_bin' in NXdetector.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "first_good_bin"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'first_good_bin' in NXdetector for writting.")) return NX_ERROR; + if (!ErrorHandler(NXputdata(fFileHandle, (void*)fNxEntry2->GetInstrument()->GetDetector()->GetFirstGoodBins()), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'first_good_bin' in NXdetector.")) return NX_ERROR; + NXclosedata(fFileHandle); + } else { + cerr << endl << ">> **WARNING** first_good_bin with rank " << fNxEntry2->GetInstrument()->GetDetector()->GetT0Tag() << " requested. Do not know how to handle." << endl; + } + } + + // write detector_1 'last_good_bin' if present + if (fNxEntry2->GetInstrument()->GetDetector()->IsLastGoodBinPresent()) { + if (fNxEntry2->GetInstrument()->GetDetector()->GetT0Tag() == 1) { + size = 1; + idata = fNxEntry2->GetInstrument()->GetDetector()->GetLastGoodBin(); + if (!ErrorHandler(NXmakedata(fFileHandle, "last_good_bin", NX_INT32, 1, &size), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'last_good_bin' in NXdetector.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "last_good_bin"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'last_good_bin' in NXdetector for writting.")) return NX_ERROR; + if (!ErrorHandler(NXputdata(fFileHandle, (void*)&idata), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'last_good_bin' in NXdetector.")) return NX_ERROR; + NXclosedata(fFileHandle); + } else if (fNxEntry2->GetInstrument()->GetDetector()->GetT0Tag() == 2) { + int dims[2]; + dims[0] = fNxEntry2->GetInstrument()->GetDetector()->GetNoOfPeriods(); + dims[1] = fNxEntry2->GetInstrument()->GetDetector()->GetNoOfSpectra(); + if (!ErrorHandler(NXmakedata(fFileHandle, "last_good_bin", NX_INT32, 2, dims), PNEXUS_MAKE_DATA_ERROR, "couldn't create data entry 'last_good_bin' in NXdetector.")) return NX_ERROR; + if (!ErrorHandler(NXopendata(fFileHandle, "last_good_bin"), PNEXUS_OPEN_DATA_ERROR, "couldn't open data 'last_good_bin' in NXdetector for writting.")) return NX_ERROR; + if (!ErrorHandler(NXputdata(fFileHandle, (void*)fNxEntry2->GetInstrument()->GetDetector()->GetLastGoodBins()), PNEXUS_PUT_DATA_ERROR, "couldn't put data 'last_good_bin' in NXdetector.")) return NX_ERROR; + NXclosedata(fFileHandle); + } else { + cerr << endl << ">> **WARNING** last_good_bin with rank " << fNxEntry2->GetInstrument()->GetDetector()->GetT0Tag() << " requested. Do not know how to handle." << endl; + } + } + + // close group 'detector_1' + NXclosegroup(fFileHandle); + + // close group 'instrument' + NXclosegroup(fFileHandle); + + // make group 'detector_1' NXdata + if (!ErrorHandler(NXmakegroup(fFileHandle, "detector_1", "NXdata"), PNEXUS_CREATE_GROUP_ERROR, "couldn't create group 'detector_1' in NXroot.")) return NX_ERROR; + // open group 'detector_1' NXdata + if (!ErrorHandler(NXopengroup(fFileHandle, "detector_1", "NXdata"), PNEXUS_GROUP_OPEN_ERROR, "couldn't open group 'detector_1' in NXroot.")) return NX_ERROR; + + if (!ErrorHandler(NXmakelink(fFileHandle, &clink), PNEXUS_LINKING_ERROR, "couldn't create link to 'counts' in NXdetector.")) return NX_ERROR; + + // close group 'detector_1' NXdata + NXclosegroup(fFileHandle); + + // close group 'raw_data_1' + NXclosegroup(fFileHandle); + + NXclose(&fFileHandle); + + return NX_OK; } -//----------------------------------------------------------------------------------------------------- -// end -//----------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------ +// IsValidIdf1 (private) +//------------------------------------------------------------------------------------------ +/** + *

Checks if the given data are representing a valid IDF Version 1. + * + * return: + * - true if a valid IDF Version 1 representation is present + * - false otherwise + * + * \param strict flag if true a strict NeXus validation is performed. + */ +bool PNeXus::IsValidIdf1(bool strict) +{ + bool valid = true; + + if (fIdfVersion != 1) { + cerr << endl << ">> **ERROR** wrong IDF version found, namely IDF " << fIdfVersion << ", instead of IDF 1" << endl; + return false; + } + + if (!fNxEntry1->IsValid(strict)) + valid = false; + + return valid; +} + +//------------------------------------------------------------------------------------------ +// IsValidIdf2 (private) +//------------------------------------------------------------------------------------------ +/** + *

Checks if the given data are representing a valid IDF Version 2. + * + * return: + * - true if a valid IDF Version 2 representation is present + * - false otherwise + * + * \param strict flag if true a strict NeXus validation is performed. + */ +bool PNeXus::IsValidIdf2(bool strict) +{ + if (fIdfVersion != 2) { + cerr << endl << ">> **ERROR** wrong IDF version found, namely IDF " << fIdfVersion << ", instead of IDF 2" << endl; + return false; + } + + if (!fCreator.compare("n/a")) { + if (strict) { + cerr << endl << ">> **ERROR** creator not set." << endl; + return false; + } else { + cerr << endl << ">> **WARNING** creator not set." << endl; + } + } + + if (!fFileName.compare("n/a")) { + if (strict) { + cerr << endl << ">> **ERROR** file name not given." << endl; + return false; + } else { + cerr << endl << ">> **WARNING** file name not given." << endl; + } + } + + if (!fFileTime.compare("n/a")) { + if (strict) { + cerr << endl << ">> **ERROR** no file creation time is given." << endl; + return false; + } else { + cerr << endl << ">> **WARNING** no file creation time is given." << endl; + } + } + + if (!fNxEntry2->IsValid(strict)) { + return false; + } + + return true; +} + +//------------------------------------------------------------------------------------------ +// SearchInGroup (private) +//------------------------------------------------------------------------------------------ +/** + *

Searches an entry (labelled by str) in the currently open group. If the tag=='name' + * a label is looked for, if the tag=='class' a class is looked for. + * + * return: + * - true if entry 'str' is found in the currently open group. nxname, nxclass keeps than the entry information. + * - false otherwise + * + * \param str label/class of the entry to be looked for + * \param tag either 'name' or 'class' + * \param nxname 'name' of the entry if entry is found + * \param nxclass 'class name' of the entry if entry is found + * \param dataType of the entry if entry is found + */ +bool PNeXus::SearchInGroup(string str, string tag, NXname &nxname, NXname &nxclass, int &dataType) +{ + bool found = false; + int status; + + do { + status = NXgetnextentry(fFileHandle, nxname, nxclass, &dataType); + if (!tag.compare("name")) { + if (!str.compare(nxname)) { + found = true; + break; + } + } else if (!tag.compare("class")) { + if (!str.compare(nxclass)) { + found = true; + break; + } + } else { + cerr << endl << ">> **ERROR** found tag='" << tag << "' which is not handled!" << endl; + break; + } + } while (!found && (status == NX_OK)); + + return found; +} diff --git a/src/external/nexus/PNeXus.h b/src/external/nexus/PNeXus.h index cf403db7..ebd1abff 100644 --- a/src/external/nexus/PNeXus.h +++ b/src/external/nexus/PNeXus.h @@ -32,7 +32,6 @@ #ifndef _PNEXUS_H_ #define _PNEXUS_H_ -#include #include #include using namespace std; @@ -43,271 +42,581 @@ using namespace std; #define VGNAMELENMAX 64 #endif /* VGNAMELENMAX */ -#define PNEXUS_FILE_OPEN_ERROR 0 -#define PNEXUS_GROUP_OPEN_ERROR 1 -#define PNEXUS_OPEN_DATA_ERROR 2 -#define PNEXUS_GET_DATA_ERROR 3 -#define PNEXUS_GET_ATTR_ERROR 4 -#define PNEXUS_CLOSE_DATA_ERROR 5 -#define PNEXUS_GET_META_INFO_ERROR 6 -#define PNEXUS_WRONG_META_INFO 7 -#define PNEXUS_WRONG_DATE_FORMAT 8 -#define PNEXUS_WRONG_TIME_FORMAT 9 -#define PNEXUS_INIT_GROUPDIR_ERROR 10 -#define PNEXUS_GET_GROUP_INFO_ERROR 11 -#define PNEXUS_GET_NEXT_ENTRY_ERROR 12 -#define PNEXUS_HISTO_ERROR 13 -#define PNEXUS_SET_ATTR_ERROR 14 -#define PNEXUS_CREATE_GROUP_ERROR 15 -#define PNEXUS_MAKE_DATA_ERROR 16 -#define PNEXUS_PUT_DATA_ERROR 17 -#define PNEXUS_PUT_ATTR_ERROR 18 +#define PNEXUS_NO_ERROR 0 +#define PNEXUS_NXENTRY_NOT_FOUND 1 +#define PNEXUS_FILE_OPEN_ERROR 2 +#define PNEXUS_GROUP_OPEN_ERROR 3 +#define PNEXUS_OPEN_DATA_ERROR 4 +#define PNEXUS_GET_DATA_ERROR 5 +#define PNEXUS_GET_ATTR_ERROR 6 +#define PNEXUS_CLOSE_DATA_ERROR 7 +#define PNEXUS_GET_META_INFO_ERROR 8 +#define PNEXUS_WRONG_META_INFO 9 +#define PNEXUS_WRONG_DATE_FORMAT 10 +#define PNEXUS_WRONG_TIME_FORMAT 11 +#define PNEXUS_INIT_GROUPDIR_ERROR 12 +#define PNEXUS_GET_GROUP_INFO_ERROR 13 +#define PNEXUS_GET_NEXT_ENTRY_ERROR 14 +#define PNEXUS_HISTO_ERROR 15 +#define PNEXUS_SET_ATTR_ERROR 16 +#define PNEXUS_CREATE_GROUP_ERROR 17 +#define PNEXUS_MAKE_DATA_ERROR 18 +#define PNEXUS_PUT_DATA_ERROR 19 +#define PNEXUS_PUT_ATTR_ERROR 20 +#define PNEXUS_IDF_NOT_IMPLEMENTED 21 +#define PNEXUS_VAILD_READ_IDF1_FILE 22 +#define PNEXUS_VAILD_READ_IDF2_FILE 23 +#define PNEXUS_OBJECT_INVOK_ERROR 24 +#define PNEXUS_TIME_FORMAT_ERROR 25 +#define PNEXUS_DATA_ERROR 26 +#define PNEXUS_NXUSER_NOT_FOUND 27 +#define PNEXUS_LINKING_ERROR 28 -//----------------------------------------------------------------------------- -/** - * - */ -typedef struct { - string fUserName; - string fExperimentNumber; -} PNeXusUser; +class PNeXusProp { + public: + PNeXusProp(); + virtual ~PNeXusProp() {} -//----------------------------------------------------------------------------- -/** - * - */ -typedef struct { - string fName; - double fTemperature; - string fTemperatureUnit; - double fMagneticField; - string fMagneticFieldUnit; - string fShape; - string fMagneticFieldState; ///< ZF, TF, LF - int fMagneticFieldVectorAvailable; - vector fMagneticFieldVector; - string fMagneticFieldVectorUnits; - string fMagneticFieldVectorCoordinateSystem; - string fEnvironment; -} PNeXusSample; + virtual string GetName() { return fName; } + virtual double GetValue() { return fValue; } + virtual string GetUnit() { return fUnit; } -//----------------------------------------------------------------------------- -/** - *

currently only put in the requiered ones. - */ -typedef struct { - int number; -} PNeXusDetector; + virtual void SetName(string name) { fName = name; } + virtual void SetValue(double val) { fValue = val; } + virtual void SetUnit(string unit) { fUnit = unit; } -//----------------------------------------------------------------------------- -/** - *

currently only put in the requiered ones. - */ -typedef struct { - string type; -} PNeXusCollimator; + private: + string fName; + double fValue; + string fUnit; +}; -//----------------------------------------------------------------------------- -/** - *

currently only put in the requiered ones. - */ -typedef struct { - double total_counts; - string total_counts_units; -} PNeXusBeam; +class PNeXusBeam1 { + public: + PNeXusBeam1() { fTotalCounts = 0; fUnits = "n/a"; } + virtual ~PNeXusBeam1() {} -//----------------------------------------------------------------------------- -/** - * - */ -typedef struct { - string name; - PNeXusDetector detector; - PNeXusCollimator collimator; - PNeXusBeam beam; -} PNeXusInstrument; + virtual bool IsValid(bool strict); -//----------------------------------------------------------------------------- -/** - * - */ -typedef struct { - unsigned int fGroupFirst; - unsigned int fGroupSecond; - double fAlphaVal; -} PNeXusAlpha; + virtual double GetTotalCounts() { return fTotalCounts; } + virtual string GetUnits() { return fUnits; } -//----------------------------------------------------------------------------- -/** - * - */ -typedef struct { - double fTimeResolution; ///< time resolution in (ns) - unsigned int fNumberOfHistos; - vector fT0; - vector fFirstGoodBin; - vector fLastGoodBin; - vector fHistoName; - vector< vector > fHisto; - vector fGrouping; - vector fAlpha; -} PNeXusData; + virtual void SetTotalCounts(double counts) { fTotalCounts = counts; } + virtual void SetUnits(string units) { fUnits = units; } + + private: + double fTotalCounts; ///< total number of counts + string fUnits; ///< 'units' in which total counts is given, e.g. 'Mev' +}; + +class PNeXusCollimator1 { + public: + PNeXusCollimator1() { fType = "n/a"; } + virtual ~PNeXusCollimator1() {} + + virtual bool IsValid(bool strict) { return true; } // currently only a dummy + + virtual string GetType() { return fType; } + virtual void SetType(string type) { fType = type; } + + private: + string fType; +}; + +class PNeXusDetector1 { + public: + PNeXusDetector1() { fNumber = 0; } + virtual ~PNeXusDetector1() {} + + virtual bool IsValid(bool strict); + + virtual int GetNumber() { return fNumber; } + virtual void SetNumber(int number) { fNumber = number; } + + private: + int fNumber; +}; + +class PNeXusInstrument1 { + public: + PNeXusInstrument1() { fName = "n/a"; } + virtual ~PNeXusInstrument1() {} + + virtual bool IsValid(bool strict); + + virtual string GetName() { return fName; } + virtual PNeXusDetector1* GetDetector() { return &fDetector; } + virtual PNeXusCollimator1* GetCollimator() { return &fCollimator; } + virtual PNeXusBeam1* GetBeam() { return &fBeam; } + + virtual void SetName(string name) { fName = name; } + virtual void SetDetector(PNeXusDetector1 &detector) { fDetector = detector; } + virtual void SetCollimator(PNeXusCollimator1 &collimator) { fCollimator = collimator; } + virtual void SetBeam(PNeXusBeam1 &beam) { fBeam = beam; } + + private: + string fName; ///< instrument name + PNeXusDetector1 fDetector; + PNeXusCollimator1 fCollimator; + PNeXusBeam1 fBeam; +}; + +class PNeXusSample1 { + public: + PNeXusSample1(); + virtual ~PNeXusSample1(); + + virtual bool IsValid(bool strict); + + virtual string GetName() { return fName; } + virtual string GetShape() { return fShape; } + virtual string GetMagneticFieldState() { return fMagneticFieldState; } + virtual string GetEnvironment() { return fEnvironment; } + virtual double GetPhysPropValue(string name, bool &ok); + virtual void GetPhysPropUnit(string name, string &unit, bool &ok); + virtual int IsMagneticFieldVectorAvailable() { return fMagneticFieldVectorAvailable; } + virtual vector GetMagneticFieldVector() { return fMagneticFieldVector; } + virtual string GetMagneticFieldVectorUnits() { return fMagneticFieldVectorUnits; } + virtual string GetMagneticFieldVectorCoordinateSystem() { return fMagneticFieldVectorCoordinateSystem; } + + virtual void SetName(string name) { fName = name; } + virtual void SetShape(string shape) { fShape = shape; } + virtual void SetMagneticFieldState(string magFieldState) { fMagneticFieldState = magFieldState; } + virtual void SetEnvironment(string env) { fEnvironment = env; } + virtual void SetPhysProp(string name, double value, string unit, int idx=-1); + virtual void SetMagneticFieldVectorAvailable(int avail) { fMagneticFieldVectorAvailable = avail; } + virtual void SetMagneticFieldVector(vector &magVec) { fMagneticFieldVector = magVec; } + virtual void SetMagneticFieldVectorCoordinateSystem(string coord) { fMagneticFieldVectorCoordinateSystem = coord; } + virtual void SetMagneticFieldUnits(string units) { fMagneticFieldVectorUnits = units; } + + private: + string fName; ///< sample name + string fShape; ///< sample orientation + string fMagneticFieldState; ///< magnetic field state, e.g. TF, ZF, ... + string fEnvironment; ///< sample environment, e.g. CCR, Konti-1, ... + vector fPhysProp; ///< collects the temperature, magnetic field + + int fMagneticFieldVectorAvailable; ///< flag '0' magnetic field vector not available, '1' magnetic field vector available + vector fMagneticFieldVector; ///< magnetic field vector + string fMagneticFieldVectorUnits; ///< units in which the magnetic field vector is given + string fMagneticFieldVectorCoordinateSystem; ///< coordinate system, e.g. 'cartesian' +}; + +class PNeXusUser1 { + public: + PNeXusUser1(); + virtual ~PNeXusUser1() {} + + virtual bool IsValid(bool strict) { return true; } // currently only a dummy + + virtual string GetName() { return fName; } + virtual string GetExperimentNumber() { return fExperimentNumber; } + + virtual void SetName(string name) { fName = name; } + virtual void SetExperimentNumber(string expNum) { fExperimentNumber = expNum; } + + private: + string fName; ///< user name + string fExperimentNumber; ///< experiment number, RB number at ISIS +}; + +class PNeXusAlpha1 { + public: + PNeXusAlpha1(); + virtual ~PNeXusAlpha1() {} + + virtual unsigned int GetGroupFirst() { return fGroupFirst; } + virtual unsigned int GetGroupSecond() { return fGroupSecond; } + virtual double GetAlpha() { return fAlphaVal; } + + virtual void SetGroupFirst(unsigned int val) { fGroupFirst = val; } + virtual void SetGroupSecond(unsigned int val) { fGroupSecond = val; } + virtual void SetAlpha(double val) { fAlphaVal = val; } + + private: + unsigned int fGroupFirst; + unsigned int fGroupSecond; + double fAlphaVal; +}; + +class PNeXusData1 { + public: + PNeXusData1() { fTimeResolution = 0.0; } + virtual ~PNeXusData1(); + + virtual bool IsValid(bool strict); + + virtual double GetTimeResolution(string units); + virtual vector *GetT0s() { return &fT0; } + virtual int GetT0(unsigned int idx); + virtual vector *GetFirstGoodBins() { return &fFirstGoodBin; } + virtual int GetFirstGoodBin(unsigned int idx); + virtual vector *GetLastGoodBins() { return &fLastGoodBin; } + virtual int GetLastGoodBin(unsigned int idx); + virtual vector *GetHistoNames() { return &fHistoName; } + virtual void GetHistoName(unsigned int idx, string &name, bool &ok); + virtual unsigned int GetNoOfHistos() { return fHisto.size(); } + virtual vector *GetHisto(unsigned int histoNo); + virtual vector *GetGrouping() { return &fGrouping; } + virtual vector *GetAlpha() { return &fAlpha; } + + virtual void SetTimeResolution(double val, string units); + virtual void SetT0(unsigned int t0, int idx=-1); + virtual void SetFirstGoodBin(unsigned int fgb, int idx=-1); + virtual void SetLastGoodBin(unsigned int lgb, int idx=-1); + virtual void FlushHistos(); + virtual void SetHisto(vector &data, int histoNo=-1); + virtual void FlushGrouping() { fGrouping.clear(); } + virtual void SetGrouping(vector &grouping) { fGrouping = grouping; } + virtual void FlushAlpha() { fAlpha.clear(); } + virtual void SetAlpha(vector &alpha) { fAlpha = alpha; } + + private: + double fTimeResolution; ///< time resolution in (ps) + vector fT0; + vector fFirstGoodBin; + vector fLastGoodBin; + vector fHistoName; + vector< vector > fHisto; + vector fGrouping; + vector fAlpha; +}; + +class PNeXusEntry1 { + public: + PNeXusEntry1(); + virtual ~PNeXusEntry1() {} + + virtual bool IsValid(bool strict); + + virtual string GetProgramName() { return fProgramName; } + virtual string GetProgramVersion() { return fProgramVersion; } + virtual int GetRunNumber() { return fRunNumber; } + virtual string GetTitle() { return fTitle; } + virtual string GetNotes() { return fNotes; } + virtual string GetAnalysis() { return fAnalysis; } + virtual string GetLaboratory() { return fLaboratory; } + virtual string GetBeamline() { return fBeamline; } + virtual string GetStartTime() { return fStartTime; } + virtual string GetStopTime() { return fStopTime; } + virtual int GetSwitchingState() { return fSwitchingState; } + virtual PNeXusUser1* GetUser() { return &fUser; } + virtual PNeXusSample1* GetSample() { return &fSample; } + virtual PNeXusInstrument1* GetInstrument() { return &fInstrument; } + virtual PNeXusData1* GetData() { return &fData; } + + virtual void SetProgramName(string name) { fProgramName = name; } + virtual void SetProgramVersion(string version) { fProgramVersion = version; } + virtual void SetRunNumber(int number) { fRunNumber = number; } + virtual void SetTitle(string title) { fTitle = title; } + virtual void SetNotes(string notes) { fNotes = notes; } + virtual void SetAnalysis(string analysis) { fAnalysis = analysis; } + virtual void SetLaboratory(string lab) { fLaboratory = lab; } + virtual void SetBeamline(string beamline) { fBeamline = beamline; } + virtual int SetStartTime(string time); + virtual int SetStopTime(string time); + virtual int SetSwitchingState(int state); + virtual void SetUser(PNeXusUser1 &user) { fUser = user; } + virtual void SetSample(PNeXusSample1 &sample) { fSample = sample; } + virtual void SetInstrument(PNeXusInstrument1 &instrument) { fInstrument = instrument; } + virtual void SetData(PNeXusData1 &data) { fData = data; } + + private: + string fProgramName; ///< name of the creating program + string fProgramVersion; ///< version of the creating program + int fRunNumber; ///< run number + string fTitle; ///< string containing the run title + string fNotes; ///< comments + string fAnalysis; ///< type of muon experiment "muonTD", "ALC", ... + string fLaboratory; ///< name of the laboratory where the data are taken, e.g. PSI, triumf, ISIS, J-Parc + string fBeamline; ///< name of the beamline used for the experiment, e.g. muE4 + string fStartTime; ///< start date/time of the run + string fStopTime; ///< stop date/time of the run + int fSwitchingState; ///< '1' normal data collection, '2' Red/Green mode + PNeXusUser1 fUser; ///< NXuser info IDF Version 1 + PNeXusSample1 fSample; ///< NXsample info IDF Version 1 + PNeXusInstrument1 fInstrument; ///< NXinstrument info IDF Version 1 + PNeXusData1 fData; ///< NXdata info IDF Version 1 +}; + +class PNeXusSource2 { + public: + PNeXusSource2(); + virtual ~PNeXusSource2() {} + + virtual bool IsValid(bool strict); + + virtual string GetName() { return fName; } + virtual string GetType() { return fType; } + virtual string GetProbe() { return fProbe; } + + virtual void SetName(string name) { fName = name; } + virtual void SetType(string type) { fType = type; } + virtual void SetProbe(string probe) { fProbe = probe; } + + private: + string fName; ///< facility name + string fType; ///< continous muon source, pulsed muon source, low energy muon source, ... + string fProbe; ///< positive muon, negative muon +}; + +class PNeXusBeamline2 { + public: + PNeXusBeamline2() { fName = "n/a"; } + virtual ~PNeXusBeamline2() {} + + virtual bool IsValid(bool strict); + + virtual string GetName() { return fName; } + + virtual void SetName(string name) { fName = name; } + + private: + string fName; +}; + +class PNeXusDetector2 { + public: + PNeXusDetector2(); + virtual ~PNeXusDetector2(); + + virtual bool IsValid(bool strict); + + virtual string GetDescription() { return fDescription; } + virtual double GetTimeResolution(string units); + virtual bool IsT0Present() { return (fT0 == 0) ? false : true; } + virtual int GetT0Tag() { return fT0Tag; } + virtual int GetT0(int idxp=-1, int idxs=-1); + virtual int* GetT0s() { return fT0; } + virtual bool IsFirstGoodBinPresent() { return (fFirstGoodBin == 0) ? false : true; } + virtual int GetFirstGoodBin(int idxp=-1, int idxs=-1); + virtual int* GetFirstGoodBins() { return fFirstGoodBin; } + virtual bool IsLastGoodBinPresent() { return (fFirstGoodBin == 0) ? false : true; } + virtual int GetLastGoodBin(int idxp=-1, int idxs=-1); + virtual int* GetLastGoodBins() { return fLastGoodBin; } + virtual int GetNoOfPeriods() { return fNoOfPeriods; } + virtual int GetNoOfSpectra() { return fNoOfSpectra; } + virtual int GetNoOfBins() { return fNoOfBins; } + virtual int GetHistoValue(int idx_p, int idx_s, int idx_b); + virtual int* GetHistos() { return fHisto; } + virtual unsigned int GetSpectrumIndexSize() { return fSpectrumIndex.size(); } + virtual vector *GetSpectrumIndex() { return &fSpectrumIndex; } + virtual int GetSpectrumIndex(unsigned int idx); + + virtual void SetDescription(string description) { fDescription = description; } + virtual void SetTimeResolution(double val, string units); + virtual void SetT0Tag(int tag) { fT0Tag = tag; } + virtual void SetT0(int *t0) { fT0 = t0; } + virtual void SetFirstGoodBin(int *fgb) { fFirstGoodBin = fgb; } + virtual void SetLastGoodBin(int *lgb) { fLastGoodBin = lgb; } + virtual void SetNoOfPeriods(int val) { fNoOfPeriods = val; } + virtual void SetNoOfSpectra(int val) { fNoOfSpectra = val; } + virtual void SetNoOfBins(int val) { fNoOfBins = val; } + virtual void SetHisto(int *histo) { fHisto = histo; } + virtual void SetSpectrumIndex(vector spectIdx) { fSpectrumIndex = spectIdx; } + virtual void SetSpectrumIndex(int spectIdx, int idx=-1); + + private: + string fDescription; ///< description of the detector + double fTimeResolution; ///< keeps the time resolution in (ps) + vector fSpectrumIndex; ///< list of global spectra + + int fNoOfPeriods; ///< number of periods or -1 if not defined + int fNoOfSpectra; ///< number of spectra or -1 if not defined + int fNoOfBins; ///< number of bins of a spectrum or -1 if not defined + + int fT0Tag; ///< tag for t0, fgb, lgb structure. -1==not present, 1==NX_INT, 2=NX_INT[np][ns] + int *fT0; + int *fFirstGoodBin; + int *fLastGoodBin; + int *fHisto; +}; + +class PNeXusInstrument2 { + public: + PNeXusInstrument2() { fName = "n/a"; } + virtual ~PNeXusInstrument2() {} + + virtual bool IsValid(bool strict); + + virtual string GetName() { return fName; } + virtual PNeXusSource2* GetSource() { return &fSource; } + virtual PNeXusBeamline2* GetBeamline() { return &fBeamline; } + virtual PNeXusDetector2* GetDetector() { return &fDetector; } + + virtual void SetName(string name) { fName = name; } + virtual void SetSource(PNeXusSource2 &source) { fSource = source; } + virtual void SetBeamline(PNeXusBeamline2 &beamline) { fBeamline = beamline; } + virtual void SetDetector(PNeXusDetector2 &detector) { fDetector = detector; } + + private: + string fName; ///< name of the instrument + PNeXusSource2 fSource; ///< details of the muon source used + PNeXusBeamline2 fBeamline; ///< beamline description + PNeXusDetector2 fDetector; ///< details of the detectors which also includes the data!! +}; + +class PNeXusSample2 { + public: + PNeXusSample2(); + virtual ~PNeXusSample2(); + + virtual bool IsValid(bool strict); + + virtual string GetName() { return fName; } + virtual string GetDescription() { return fDescription; } + virtual string GetMagneticFieldState() { return fMagneticFieldState; } + virtual string GetEnvironmentTemp() { return fEnvironmentTemp; } + virtual string GetEnvironmentField() { return fEnvironmentField; } + virtual double GetPhysPropValue(string name, bool &ok); + virtual void GetPhysPropUnit(string name, string &unit, bool &ok); + + virtual void SetName(string name) { fName = name; } + virtual void SetDescription(string description) { fDescription = description; } + virtual void SetMagneticFieldState(string magFieldState) { fMagneticFieldState = magFieldState; } + virtual void SetEnvironmentTemp(string env) { fEnvironmentTemp = env; } + virtual void SetEnvironmentField(string env) { fEnvironmentField = env; } + virtual void SetPhysProp(string name, double value, string unit, int idx=-1); + + private: + string fName; ///< sample name + string fDescription; ///< sample description + string fMagneticFieldState; ///< magnetic field state, e.g. TF, ZF, ... + string fEnvironmentTemp; ///< sample environment related to temperature, e.g. CCR, Konti-1, ... + string fEnvironmentField; ///< sample environment related to field, e.g. WEW-Bruker + vector fPhysProp; ///< collects the temperature, magnetic field +}; + +class PNeXusUser2 { + public: + PNeXusUser2() { fName = "n/a"; } + virtual ~PNeXusUser2() {} + + virtual bool IsValid(bool strict) { return true; } // currently only a dummy + + virtual string GetName() { return fName; } + + virtual void SetName(string name) { fName = name; } + + private: + string fName; ///< user name +}; + +class PNeXusEntry2 { + public: + PNeXusEntry2(); + virtual ~PNeXusEntry2() {} + + virtual bool IsValid(bool strict); + + virtual string GetDefinition() { return fDefinition; } + virtual string GetProgramName() { return fProgramName; } + virtual string GetProgramVersion() { return fProgramVersion; } + virtual int GetRunNumber() { return fRunNumber; } + virtual string GetTitle() { return fTitle; } + virtual string GetStartTime() { return fStartTime; } + virtual string GetStopTime() { return fStopTime; } + virtual string GetExperimentIdentifier() { return fExperimentIdentifier; } + virtual PNeXusUser2* GetUser() { return &fUser; } + virtual PNeXusSample2* GetSample() { return &fSample; } + virtual PNeXusInstrument2* GetInstrument() { return &fInstrument; } + + virtual void SetDefinition(string def) { fDefinition = def; } + virtual void SetProgramName(string name) { fProgramName = name; } + virtual void SetProgramVersion(string version) { fProgramVersion = version; } + virtual void SetRunNumber(int number) { fRunNumber = number; } + virtual void SetTitle(string title) { fTitle = title; } + virtual int SetStartTime(string time); + virtual int SetStopTime(string time); + virtual void SetExperimentIdentifier(string expId) { fExperimentIdentifier = expId; } + virtual void SetUser(PNeXusUser2 &user) { fUser = user; } + virtual void SetSample(PNeXusSample2 &sample) { fSample = sample; } + virtual void SetInstrument(PNeXusInstrument2 &instrument) { fInstrument = instrument; } + + private: + string fDefinition; ///< the template (DTD name) on which the entry was based, e.g. 'pulsedTD' + string fProgramName; ///< name of the creating program + string fProgramVersion; ///< version of the creating program + int fRunNumber; ///< run number + string fTitle; ///< string containing the run title + string fStartTime; ///< start date/time of the run + string fStopTime; ///< stop date/time of the run + string fExperimentIdentifier; ///< experiment number, (for ISIS, the RB number) + PNeXusUser2 fUser; ///< NXuser info IDF Version 2 + PNeXusSample2 fSample; ///< NXsample info IDF Version 2 + PNeXusInstrument2 fInstrument; ///< NXinstrument inf IDF Version 2 +}; -//----------------------------------------------------------------------------- -/** - * - */ class PNeXus { public: PNeXus(); PNeXus(const char* fileName); virtual ~PNeXus(); - virtual bool IsValid() { return fIsValid; } + virtual int GetIdfVersion() { return fIdfVersion; } + virtual string GetFileName() { return fFileName; } + virtual string GetFileTime() { return fFileTime; } + + virtual void SetIdfVersion(unsigned int idf); + virtual void SetFileName(string name) { fFileName = name; } + virtual void SetFileTime(string time) { fFileTime = time; } + + virtual PNeXusEntry1* GetEntryIdf1() { return fNxEntry1; } + virtual PNeXusEntry2* GetEntryIdf2() { return fNxEntry2; } + + virtual bool IsValid(bool strict=false); + virtual int GetErrorCode() { return fErrorCode; } + virtual string GetErrorMsg() { return fErrorMsg; } + + virtual vector* GetGroupedHisto(unsigned int idx); virtual int ReadFile(const char *fileName); - virtual int WriteFile(const char *fileName, const char *fileType="hdf4"); + virtual int WriteFile(const char *fileName, const char *fileType="hdf4", const unsigned int idf=2); + + virtual void SetCreator(string str) { fCreator = str; } virtual void Dump(); - virtual int GetErrorCode() { return fErrorCode; } - virtual string GetErrorMsg() { return fErrorMsg; } - virtual string GetFileName() { return fFileName; } - virtual int GetIDFVersion() { return fIDFVersion; } - virtual string GetProgramName() { return fProgramName; } - virtual string GetProgramVersion() { return fProgramVersion; } - - virtual int GetRunNumber() { return fRunNumber; } - virtual string GetRunTitle() { return fRunTitle; } - virtual string GetRunNotes() { return fNotes; } - virtual string GetAnalysisTag() { return fAnalysisTag; } - virtual string GetLab() { return fLab; } - virtual string GetBeamline() { return fBeamLine; } - virtual string GetStartDate() { return fStartDate; } - virtual string GetStartTime() { return fStartTime; } - virtual string GetStopDate() { return fStopDate; } - virtual string GetStopTime() { return fStopTime; } - virtual int GetSwitchingState() { return fSwitchingState; } - virtual string GetUser() { return fUser.fUserName; } - virtual string GetExperimentNumber() { return fUser.fExperimentNumber; } - virtual string GetSampleName() { return fSample.fName; } - virtual double GetSampleTemperature() { return fSample.fTemperature; } - virtual string GetSampleTemperartureUnits() { return fSample.fTemperatureUnit; } - virtual double GetMagneticField() { return fSample.fMagneticField; } - virtual string GetMagneticFieldUnits() { return fSample.fMagneticFieldUnit; } - virtual string GetMagneticFieldState() { return fSample.fMagneticFieldState; } - virtual int IsMagneticFieldVectorAvailable() { return fSample.fMagneticFieldVectorAvailable; } - virtual vector& GetMagneticFieldVector() { return fSample.fMagneticFieldVector; } - virtual string GetMagneticFieldVectorCoordSystem() { return fSample.fMagneticFieldVectorCoordinateSystem; } - virtual string GetMagneticFieldVectorUnits() { return fSample.fMagneticFieldVectorUnits; } - virtual string GetSampleEnvironment() { return fSample.fEnvironment; } - virtual string GetSampleShape() { return fSample.fShape; } - virtual string GetExperimentName() { return fInstrument.name; } - virtual int GetNoDetectors() { return fInstrument.detector.number; } - virtual string GetCollimatorType() { return fInstrument.collimator.type; } - virtual double GetTotalNoEvents() { return fInstrument.beam.total_counts; } - virtual string GetTotalNoEventsUnits() { return fInstrument.beam.total_counts_units; } - virtual double GetTimeResolution() { return fData.fTimeResolution; } - virtual int GetNoHistos() { return fData.fNumberOfHistos; } - virtual int IsAlphaAvailable() { return fData.fAlpha.size(); } - virtual vector *GetAlpha() { if (fData.fAlpha.size()) return &fData.fAlpha; else return 0; } - virtual unsigned int GetT0() { if (fData.fT0.size()) return fData.fT0[0]; else return 0; } - virtual unsigned int GetFirstGoodBin() { if (fData.fFirstGoodBin.size()) return fData.fFirstGoodBin[0]; else return 0; } - virtual unsigned int GetLastGoodBin() { if (fData.fLastGoodBin.size()) return fData.fLastGoodBin[0]; else return 0; } - virtual vector *GetHisto(unsigned int histoNo) { if (fData.fHisto.size() > histoNo) return &fData.fHisto[histoNo]; else return 0;} - virtual vector *GetGroupedHisto(unsigned int histoNo) { if (fGroupedData.size() > histoNo) return &fGroupedData[histoNo]; else return 0; } - - virtual void SetFileName(const char* fileName) { fFileName = fileName; } - virtual void SetFileName(string fileName) { fFileName = fileName; } - virtual void SetIDFVersion(int idfVersion) { fIDFVersion = idfVersion; } - virtual void SetProgramName(const char* progName) { fProgramName = progName; } - virtual void SetProgramName(string progName) { fProgramName = progName; } - virtual void SetProgramVersion(const char* progVersion) { fProgramVersion = progVersion; } - virtual void SetProgramVersion(string progVersion) { fProgramVersion = progVersion; } - virtual void SetRunNumber(int runNumber) { fRunNumber = runNumber; } - virtual void SetRunTitle(const char* runTitle) { fRunTitle = runTitle; } - virtual void SetRunTitle(string runTitle) { fRunTitle = runTitle; } - virtual void SetRunNotes(const char* notes) { fNotes = notes; } - virtual void SetRunNotes(string notes) { fNotes = notes; } - virtual void SetAnalysisTag(const char* analysisTag) { fAnalysisTag = analysisTag; } - virtual void SetAnalysisTag(string analysisTag) { fAnalysisTag = analysisTag; } - virtual void SetLab(const char *lab) { fLab = lab; } - virtual void SetLab(string lab) { fLab = lab; } - virtual void SetBeamline(const char *beamline) { fBeamLine = beamline; } - virtual void SetBeamline(string beamline) { fBeamLine = beamline; } - virtual bool SetStartDate(const char *date); - virtual bool SetStartDate(string date); - virtual bool SetStartTime(const char *time); - virtual bool SetStartTime(string time); - virtual bool SetStopDate(const char *date); - virtual bool SetStopDate(string date); - virtual bool SetStopTime(const char *time); - virtual bool SetStopTime(string time); - virtual void SetSwitchingState(int state) { fSwitchingState=state; } - virtual void SetUser(string user) { fUser.fUserName = user; } - virtual void SetExperimentNumber(string number) { fUser.fExperimentNumber = number; } - virtual void SetSampleName(string name) { fSample.fName = name; } - virtual void SetSampleTemperature(double temp) { fSample.fTemperature = temp; } - virtual void SetSampleTemperatureUints(string units) { fSample.fTemperatureUnit = units; } - virtual void SetMagneticField(double field) { fSample.fMagneticField = field; } - virtual void SetMagneticFieldUnits(string units) { fSample.fMagneticFieldUnit = units; } - virtual void SetMagneticFieldState(string state) { fSample.fMagneticFieldState = state; } - virtual void SetSampleEnvironment(string env) { fSample.fEnvironment = env; } - virtual void SetSampleShape(string shape) { fSample.fShape = shape; } - virtual void SetMagneticFieldVectorAvailable(int available) { fSample.fMagneticFieldVectorAvailable = available; } - virtual void SetMagneticFieldVector(vector& vec) {fSample.fMagneticFieldVectorAvailable = 1; fSample.fMagneticFieldVector = vec;} - virtual void SetMagneticFieldVectorCoordSystem(string coord) { fSample.fMagneticFieldVectorCoordinateSystem = coord; } - virtual void SetMagneticFieldVectorUnits(string units) { fSample.fMagneticFieldVectorUnits = units; } - virtual void SetExperimentName(string name) { fInstrument.name = name; } - virtual void SetNoDetectors(unsigned int no) { fInstrument.detector.number = no; } - virtual void SetCollimatorType(string type) { fInstrument.collimator.type = type; } - virtual void SetTimeResolution(double timeResolution) { fData.fTimeResolution = timeResolution; } // given in ns - virtual void SetT0(unsigned int t0, unsigned int idx=0); - virtual void SetFirstGoodBin(unsigned int fgb, unsigned int idx=0); - virtual void SetLastGoodBin(unsigned int lgb, unsigned int idx=0); - virtual void SetHisto(unsigned int histoNo, vector &data); - private: - bool fIsValid; - string fFileName; ///< read/write file name - NXhandle fFileHandle; ///< nexus file handle + bool fValid; + int fErrorCode; + string fErrorMsg; - string fErrorMsg; ///< stores the current error message - int fErrorCode; ///< indicates the current error code + string fNeXusVersion; ///< version of the NeXus API used in writing the file + string fFileFormatVersion; ///< version of the HDF, HDF5, or XML library used to create the file (IDF 2 only) - int fIDFVersion; - string fProgramName; - string fProgramVersion; + unsigned int fIdfVersion; ///< version of the instrument definition + string fFileName; ///< file name of the original NeXus file to assist identification if the external name has been changed + string fFileTime; ///< date and time of file creating (IDF 2 only) + NXhandle fFileHandle; - int fRunNumber; - string fRunTitle; - string fNotes; - string fAnalysisTag; ///< type of muon experiment - string fLab; - string fBeamLine; - string fStartDate; - string fStartTime; - string fStopDate; - string fStopTime; - int fSwitchingState; ///< red/green mode + string fCreator; ///< facility of program where the file originated - PNeXusUser fUser; - PNeXusSample fSample; - PNeXusInstrument fInstrument; - PNeXusData fData; - vector< vector > fGroupedData; + PNeXusEntry1 *fNxEntry1; ///< NXentry for IDF 1 + PNeXusEntry2 *fNxEntry2; ///< NXentry for IDF 2 + + vector< vector > fGroupedHisto; virtual void Init(); - virtual void CleanUp(); virtual bool ErrorHandler(NXstatus status, int errCode, const string &errMsg); virtual NXstatus GetStringData(string &str); virtual NXstatus GetStringAttr(string attr, string &str); virtual int GetDataSize(int type); virtual NXstatus GetDoubleVectorData(vector &data); virtual NXstatus GetIntVectorData(vector &data); + + virtual int ReadFileIdf1(); + virtual int ReadFileIdf2(); + + virtual int WriteFileIdf1(const char* fileName, const NXaccess access); + virtual int WriteFileIdf2(const char* fileName, const NXaccess access); + virtual NXstatus GroupHistoData(); - virtual string TransformDate(string date, bool &ok); - virtual int GetMonth(const string month); + + virtual bool IsValidIdf1(bool strict); + virtual bool IsValidIdf2(bool strict); + + virtual bool SearchInGroup(string str, string tag, NXname &nxname, NXname &nxclass, int &dataType); }; #endif // _PNEXUS_H_ diff --git a/src/include/PMusr.h b/src/include/PMusr.h index 9dfe21db..5f62216f 100644 --- a/src/include/PMusr.h +++ b/src/include/PMusr.h @@ -609,6 +609,7 @@ typedef struct { UInt_t rebin; ///< holds the number of bins to be packed UInt_t compressionTag; ///< 0=no compression, 1=gzip compression, 2=bzip2 compression TString compressFileName; ///< holds the name of the outputfile name in case of compression is used + UInt_t idf; ///< IDF version for NeXus files. } PAny2ManyInfo; #endif // _PMUSR_H_ diff --git a/src/include/PRunDataHandler.h b/src/include/PRunDataHandler.h index 9ffc1f08..fdaa639b 100644 --- a/src/include/PRunDataHandler.h +++ b/src/include/PRunDataHandler.h @@ -96,6 +96,7 @@ class PRunDataHandler virtual TString GetFileName(const TString extension, Bool_t &ok); virtual TString FileNameFromTemplate(TString &fileNameTemplate, Int_t run, TString &year, Bool_t &ok); virtual TString GetMonth(Int_t month); + virtual void SplitTimeDate(TString timeDate, TString &time, TString &date, Bool_t &ok); }; #endif // _PRUNDATAHANDLER_H_ diff --git a/src/tests/nexus/Makefile b/src/tests/nexus/Makefile index e67c9ed5..b8595e49 100644 --- a/src/tests/nexus/Makefile +++ b/src/tests/nexus/Makefile @@ -11,7 +11,7 @@ LD = g++ LDFLAGS = -g LIBS = -L /usr/local/lib -lNeXus -LIBS += -L /opt/hdf5/lib -lhdf5 +LIBS += -L /usr/lib -lhdf5 EXEC = nexus_read_test diff --git a/src/tests/nexus/nexus_read_test.cpp b/src/tests/nexus/nexus_read_test.cpp index 9b2ebf43..9b421ad1 100644 --- a/src/tests/nexus/nexus_read_test.cpp +++ b/src/tests/nexus/nexus_read_test.cpp @@ -30,6 +30,7 @@ ***************************************************************************/ #include +#include #include #include @@ -37,8 +38,6 @@ #include using namespace std; -#include "napi.h" - #include "PNeXus.h" //--------------------------------------------------------------------------------------- @@ -46,12 +45,13 @@ using namespace std; void nexus_read_test_syntax() { cout << endl << ">>---------------------------------------------------------------------------------------"; - cout << endl << ">> usage: nexus_read_test "; + cout << endl << ">> usage: nexus_read_test [ ]"; cout << endl << ">> This will try to read a nexus-files and send the relevant"; cout << endl << ">> information to the standard output."; cout << endl << ">> At the same time the read file is written back to , where"; cout << endl << ">> the extension will be added based on the ."; cout << endl << ">> : hdf4, hdf5, xml"; + cout << endl << ">> : 1 | 2"; cout << endl << ">>---------------------------------------------------------------------------------------"; cout << endl << endl; } @@ -60,29 +60,39 @@ void nexus_read_test_syntax() int main(int argc, char *argv[]) { - if (argc != 4) { + if ((argc != 5) && (argc !=2)) { nexus_read_test_syntax(); return -1; } PNeXus *nxs_file = new PNeXus(argv[1]); - if (nxs_file->IsValid()) { + if (nxs_file->IsValid(true)) { nxs_file->Dump(); - char filename[128]; - if (strstr(argv[3], "hdf") || strstr(argv[3], "xml")) { - snprintf(filename, sizeof(filename), "%s.%s", argv[2], argv[3]); - } else { - cerr << endl << "**ERROR** unkown nexus write format found" << endl; - nexus_read_test_syntax(); - return -1; - } + if (argc == 5) { + long int idf = strtol(argv[4], (char **)NULL, 10); + if ((idf != 1) && (idf != 2)) { + if (nxs_file) + delete nxs_file; + nexus_read_test_syntax(); + return -1; + } - if (nxs_file->WriteFile(filename, argv[3]) != NX_OK) { - cerr << endl << nxs_file->GetErrorMsg() << " (" << nxs_file->GetErrorCode() << ")" << endl << endl; - } else { - cout << endl << "file " << filename << " written successfully." << endl << endl; + char filename[128]; + if (strstr(argv[3], "hdf") || strstr(argv[3], "xml")) { + snprintf(filename, sizeof(filename), "%s.%s", argv[2], argv[3]); + } else { + cerr << endl << "**ERROR** unkown nexus write format found" << endl; + nexus_read_test_syntax(); + return -1; + } + + if (nxs_file->WriteFile(filename, argv[3], (unsigned int)idf) != NX_OK) { + cerr << endl << nxs_file->GetErrorMsg() << " (" << nxs_file->GetErrorCode() << ")" << endl << endl; + } else { + cout << endl << "file " << filename << " written successfully." << endl << endl; + } } }