diff --git a/Code/Mantid/Framework/Algorithms/src/ConvertUnitsUsingDetectorTable.cpp b/Code/Mantid/Framework/Algorithms/src/ConvertUnitsUsingDetectorTable.cpp index c8447407931b792bd825d043a7be83db28ab16f5..56e03389a4fb325f540916b07f448ad71400e318 100644 --- a/Code/Mantid/Framework/Algorithms/src/ConvertUnitsUsingDetectorTable.cpp +++ b/Code/Mantid/Framework/Algorithms/src/ConvertUnitsUsingDetectorTable.cpp @@ -1,7 +1,9 @@ #include "MantidAlgorithms/ConvertUnitsUsingDetectorTable.h" #include "MantidAPI/ITableWorkspace.h" +#include "MantidDataObjects/TableWorkspace.h" #include "MantidAPI/AlgorithmFactory.h" +#include "MantidAPI/TableRow.h" #include "MantidKernel/ListValidator.h" #include "MantidKernel/UnitFactory.h" @@ -9,7 +11,6 @@ #include "MantidAlgorithms/ConvertUnitsUsingDetectorTable.h" #include "MantidAPI/WorkspaceValidators.h" -#include "MantidAPI/ITableWorkspace.h" #include "MantidAPI/AlgorithmFactory.h" #include "MantidAPI/Run.h" #include "MantidKernel/UnitFactory.h" @@ -22,6 +23,8 @@ #include <cfloat> #include <iostream> #include <limits> +#include <vector> +#include <algorithm> #include "MantidKernel/BoundedValidator.h" #include "MantidKernel/ListValidator.h" @@ -84,7 +87,7 @@ namespace Algorithms declareProperty("Target","",boost::make_shared<StringListValidator>(UnitFactory::Instance().getKeys()), "The name of the units to convert to (must be one of those registered in\n" "the Unit Factory)"); - declareProperty(new WorkspaceProperty<ITableWorkspace>("DetectorParameters", "", Direction::Input, PropertyMode::Optional), + declareProperty(new WorkspaceProperty<TableWorkspace>("DetectorParameters", "", Direction::Input, PropertyMode::Optional), "Name of a TableWorkspace containing the detector parameters to use instead of the IDF."); // TODO: Do we need this ? @@ -289,33 +292,34 @@ namespace Algorithms using namespace Geometry; // Let's see if we are using a TableWorkspace to override parameters - ITableWorkspace_sptr paramWS = getProperty("DetectorParameters"); + TableWorkspace_sptr paramWS = getProperty("DetectorParameters"); // See if we have supplied a DetectorParameters Workspace // TODO: Check if paramWS is NULL and if so throw an exception - // Some variables to hold our values - Column_const_sptr l1Column; - Column_const_sptr l2Column; - Column_const_sptr spectraColumn; - Column_const_sptr twoThetaColumn; - Column_const_sptr efixedColumn; - Column_const_sptr emodeColumn; - //std::vector<std::string> columnNames = paramWS->getColumnNames(); +// const std::string l1ColumnLabel("l1"); - // Now lets read the parameters + // Let's check all the columns exist and are readable try { - l1Column = paramWS->getColumn("l1"); - l2Column = paramWS->getColumn("l2"); - spectraColumn = paramWS->getColumn("spectra"); - twoThetaColumn = paramWS->getColumn("twotheta"); - efixedColumn = paramWS->getColumn("efixed"); - emodeColumn = paramWS->getColumn("emode"); + auto spectraColumnTmp = paramWS->getColumn("spectra"); + auto l1ColumnTmp = paramWS->getColumn("l1"); + auto l2ColumnTmp = paramWS->getColumn("l2"); + auto twoThetaColumnTmp = paramWS->getColumn("twotheta"); + auto efixedColumnTmp = paramWS->getColumn("efixed"); + auto emodeColumnTmp = paramWS->getColumn("emode"); } catch (...) { throw Exception::InstrumentDefinitionError("DetectorParameter TableWorkspace is not defined correctly."); } + // Now let's read them into some vectors. + auto l1Column = paramWS->getColVector<double>("l1"); + auto l2Column = paramWS->getColVector<double>("l2"); + auto twoThetaColumn = paramWS->getColVector<double>("twotheta"); + auto efixedColumn = paramWS->getColVector<double>("efixed"); + auto emodeColumn = paramWS->getColVector<int>("emode"); + auto spectraColumn = paramWS->getColVector<int>("spectra"); + EventWorkspace_sptr eventWS = boost::dynamic_pointer_cast<EventWorkspace>(outputWS); assert ( static_cast<bool>(eventWS) == m_inputEvents ); // Sanity check @@ -326,58 +330,88 @@ namespace Algorithms // Get the unit object for each workspace Kernel::Unit_const_sptr outputUnit = outputWS->getAxis(0)->unit(); - int emode = 0; - double l1, l2, twoTheta, efixed; - std::vector<double> emptyVec; int failedDetectorCount = 0; -// std::vector<std::string> parameters = outputWS->getInstrument()->getStringParameter("show-signed-theta"); -// bool bUseSignedVersion = (!parameters.empty()) && find(parameters.begin(), parameters.end(), "Always") != parameters.end(); -// function<double(IDetector_const_sptr)> thetaFunction = bUseSignedVersion ? bind(&MatrixWorkspace::detectorSignedTwoTheta, outputWS, _1) : bind(&MatrixWorkspace::detectorTwoTheta, outputWS, _1); - + //ConstColumnVector<int> spectraNumber = paramWS->getVector("spectra"); // TODO: Check why this parallel stuff breaks // Loop over the histograms (detector spectra) //PARALLEL_FOR1(outputWS) - for (int64_t i = 0; i < numberOfSpectra_i; ++i) + for (int64_t i = 0; i < numberOfSpectra_i; ++i) { + + // Lets find what row this spectrum ID appears in our detector table. + //PARALLEL_START_INTERUPT_REGION std::size_t wsid = i; try { + double deg2rad = M_PI / 180.; - specid_t spectraNumber = static_cast<specid_t>(spectraColumn->toDouble(i)); - wsid = outputWS->getIndexFromSpectrumNumber(spectraNumber); - g_log.debug() << "###### Spectra #" << spectraNumber << " ==> Workspace ID:" << wsid << std::endl; - l1 = l1Column->toDouble(wsid); - l2 = l2Column->toDouble(wsid); - twoTheta = deg2rad * twoThetaColumn->toDouble(wsid); - efixed = efixedColumn->toDouble(wsid); - emode = static_cast<int>(emodeColumn->toDouble(wsid)); - - g_log.debug() << "\tL1=" << l1 << ",L2=" << l2 << ",TT=" << twoTheta << ",EF=" << efixed - << ",EM=" << emode << std::endl; - - // Make local copies of the units. This allows running the loop in parallel - Unit * localFromUnit = fromUnit->clone(); - Unit * localOutputUnit = outputUnit->clone(); - /// @todo Don't yet consider hold-off (delta) - const double delta = 0.0; - // Convert the input unit to time-of-flight - localFromUnit->toTOF(outputWS->dataX(wsid),emptyVec,l1,l2,twoTheta,emode,efixed,delta); - // Convert from time-of-flight to the desired unit - localOutputUnit->fromTOF(outputWS->dataX(wsid),emptyVec,l1,l2,twoTheta,emode,efixed,delta); - // EventWorkspace part, modifying the EventLists. - if ( m_inputEvents ) - { - eventWS->getEventList(wsid).convertUnitsViaTof(localFromUnit, localOutputUnit); + + auto det = outputWS->getDetector(i); + int specid = det->getID(); + + //int spectraNumber = static_cast<int>(spectraColumn->toDouble(i)); + //wsid = outputWS->getIndexFromSpectrumNumber(spectraNumber); + g_log.debug() << "###### Spectra #" << specid << " ==> Workspace ID:" << wsid << std::endl; + + // Now we need to find the row that contains this spectrum + std::vector<int>::iterator specIter; + + specIter = std::find(spectraColumn.begin(), spectraColumn.end(), specid); + if (specIter != spectraColumn.end()) + { + size_t detectorRow = std::distance(spectraColumn.begin(), specIter); + double l1 = l1Column[detectorRow]; + double l2 = l2Column[detectorRow]; + double twoTheta = twoThetaColumn[detectorRow] * deg2rad; + double efixed = efixedColumn[detectorRow]; + int emode = emodeColumn[detectorRow]; + + g_log.debug() << "specId from detector table = " << spectraColumn[detectorRow] << std::endl; + + //l1 = l1Column->toDouble(detectorRow); + //l2 = l2Column->toDouble(detectorRow); + //twoTheta = deg2rad * twoThetaColumn->toDouble(detectorRow); + //efixed = efixedColumn->toDouble(detectorRow); + //emode = static_cast<int>(emodeColumn->toDouble(detectorRow)); + + g_log.debug() << "###### Spectra #" << specid << " ==> Det Table Row:" << detectorRow << std::endl; + + g_log.debug() << "\tL1=" << l1 << ",L2=" << l2 << ",TT=" << twoTheta << ",EF=" << efixed + << ",EM=" << emode << std::endl; + + // Make local copies of the units. This allows running the loop in parallel + Unit * localFromUnit = fromUnit->clone(); + Unit * localOutputUnit = outputUnit->clone(); + /// @todo Don't yet consider hold-off (delta) + const double delta = 0.0; + // Convert the input unit to time-of-flight + localFromUnit->toTOF(outputWS->dataX(wsid),emptyVec,l1,l2,twoTheta,emode,efixed,delta); + // Convert from time-of-flight to the desired unit + localOutputUnit->fromTOF(outputWS->dataX(wsid),emptyVec,l1,l2,twoTheta,emode,efixed,delta); + // EventWorkspace part, modifying the EventLists. + if ( m_inputEvents ) + { + eventWS->getEventList(wsid).convertUnitsViaTof(localFromUnit, localOutputUnit); + } + // Clear unit memory + delete localFromUnit; + delete localOutputUnit; + } - // Clear unit memory - delete localFromUnit; - delete localOutputUnit; + else { + // Not found + g_log.debug() << "Spectrum " << specid << " not found!" << std::endl ; + failedDetectorCount++; + outputWS->maskWorkspaceIndex(wsid); + } + + } catch (Exception::NotFoundError&) { // Get to here if exception thrown when calculating distance to detector @@ -392,7 +426,7 @@ namespace Algorithms } // loop over spectra //PARALLEL_CHECK_INTERUPT_REGION - if (failedDetectorCount != 0) + if (failedDetectorCount != 0) { g_log.information() << "Something went wrong for " << failedDetectorCount << " spectra. Masking spectrum." << std::endl; } diff --git a/Code/Mantid/Framework/Algorithms/src/CreatePSDBleedMask.cpp b/Code/Mantid/Framework/Algorithms/src/CreatePSDBleedMask.cpp index 0706a208c515bf4f9c5437c751c3448b0485609d..d5673114278cc95877da67d1d7f713fc9b3ac99f 100644 --- a/Code/Mantid/Framework/Algorithms/src/CreatePSDBleedMask.cpp +++ b/Code/Mantid/Framework/Algorithms/src/CreatePSDBleedMask.cpp @@ -73,12 +73,19 @@ void CreatePSDBleedMask::exec() { // We require the number of good frames. Check that we have this if (!inputWorkspace->run().hasProperty("goodfrm")) { throw std::invalid_argument( - "InputWorkspace does not contain the number of \"good frames\"."); + "InputWorkspace does not contain the number of \"good frames\".\n" + "(The sample log named: goodfrm with value, specifying number of good frames)"); } Kernel::PropertyWithValue<int> *frameProp = dynamic_cast<Kernel::PropertyWithValue<int> *>( inputWorkspace->run().getProperty("goodfrm")); - assert(frameProp); + if (!frameProp){ + throw std::invalid_argument( + "InputWorkspace has the number of \"good frames\" property (goodfrm log value)" + "but this property value is not integer."); + + } + int goodFrames = (*frameProp)(); // Store the other properties @@ -94,7 +101,7 @@ void CreatePSDBleedMask::exec() { // This algorithm assumes that the instrument geometry is tube based, i.e. the // parent CompAssembly // of the lowest detector in the tree is a "tube" and that all pixels in a - // tube are consectively ordered + // tube are consecutively ordered // with respect to spectra number const int numSpectra = static_cast<int>(inputWorkspace->getNumberHistograms()); diff --git a/Code/Mantid/Framework/Algorithms/src/NormaliseByCurrent.cpp b/Code/Mantid/Framework/Algorithms/src/NormaliseByCurrent.cpp index 33b441bb28a69d8b928da1d85cfb938a0a45f7ac..ec43dff2439ea8856ed70919068b0ae88c8cad2e 100644 --- a/Code/Mantid/Framework/Algorithms/src/NormaliseByCurrent.cpp +++ b/Code/Mantid/Framework/Algorithms/src/NormaliseByCurrent.cpp @@ -89,18 +89,18 @@ void NormaliseByCurrent::exec() { g_log.information() << "Normalisation current: " << charge << " uamps" << std::endl; - charge = 1.0 / charge; // Inverse of the charge to be multiplied by + + double invcharge = 1.0 / charge; // Inverse of the charge to be multiplied by // The operator overloads properly take into account of both EventWorkspaces // and doing it in place or not. - if (inputWS != outputWS) { - outputWS = inputWS * charge; + outputWS = inputWS * invcharge; setProperty("OutputWorkspace", outputWS); } else { - inputWS *= charge; + inputWS *= invcharge; } - + outputWS->mutableRun().addLogData(new Kernel::PropertyWithValue<double>("NormalizationFactor", charge)); outputWS->setYUnitLabel("Counts per microAmp.hour"); } diff --git a/Code/Mantid/Framework/Algorithms/test/ConvertUnitsUsingDetectorTableTest.h b/Code/Mantid/Framework/Algorithms/test/ConvertUnitsUsingDetectorTableTest.h index e17df8dd0c4c3e420a96dbff43bc8f9bf8fce023..8ac40be03b83e5f07ce283c89501b81d3a8f2370 100644 --- a/Code/Mantid/Framework/Algorithms/test/ConvertUnitsUsingDetectorTableTest.h +++ b/Code/Mantid/Framework/Algorithms/test/ConvertUnitsUsingDetectorTableTest.h @@ -35,8 +35,6 @@ public: TS_ASSERT( alg.isInitialized() ); } - - // TODO: Make this test useful void test_TofToLambda() { ConvertUnitsUsingDetectorTable myAlg; @@ -45,7 +43,11 @@ public: const std::string workspaceName("_ws_testConvertUsingDetectorTable"); int nBins = 10; - MatrixWorkspace_sptr WS = WorkspaceCreationHelper::Create2DWorkspaceBinned(2, nBins, 500.0, 50.0); +// MatrixWorkspace_sptr WS = WorkspaceCreationHelper::Create2DWorkspaceBinned(2, nBins, 500.0, 50.0); + MatrixWorkspace_sptr WS = WorkspaceCreationHelper::create2DWorkspaceWithFullInstrument(2,nBins, false, + false, true, + "TESTY"); + WS->getAxis(0)->unit() = UnitFactory::Instance().create("TOF"); AnalysisDataService::Instance().add(workspaceName,WS); @@ -72,7 +74,6 @@ public: myAlg.setPropertyValue("OutputWorkspace", workspaceName); myAlg.setPropertyValue("Target", "Wavelength"); myAlg.setProperty("DetectorParameters", pars); - myAlg.execute(); auto outWS = AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(workspaceName); @@ -83,8 +84,8 @@ public: // } // } - TS_ASSERT_DELTA( outWS->dataX(0)[0], 0.017982, 0.000001 ); - TS_ASSERT_DELTA( outWS->dataX(0)[9], 0.034166, 0.000001 ); + TS_ASSERT_DELTA( outWS->dataX(0)[0], 0.0, 0.000001 ); + TS_ASSERT_DELTA( outWS->dataX(0)[9], 0.000323676, 0.000001 ); // TS_ASSERT_DELTA( outWS->dataX(1)[0], 0.179818, 0.000001 ); // TS_ASSERT_DELTA( outWS->dataX(1)[9], 0.017982, 0.000001 ); diff --git a/Code/Mantid/Framework/Algorithms/test/NormaliseByCurrentTest.h b/Code/Mantid/Framework/Algorithms/test/NormaliseByCurrentTest.h index d8d221cdf3bec99702aad499a3704a5295a8b64b..26b86ac13f9052a4f1e58f9b59371a6eb5b19e6d 100644 --- a/Code/Mantid/Framework/Algorithms/test/NormaliseByCurrentTest.h +++ b/Code/Mantid/Framework/Algorithms/test/NormaliseByCurrentTest.h @@ -64,6 +64,14 @@ public: NormaliseByCurrent norm1; if ( !norm1.isInitialized() ) norm1.initialize(); + const MantidVec & Y = inWS->readY(0); + double initValue = Y[0]; + bool checkNormFactor=true; + if (initValue<=0){ + checkNormFactor = false; + } + double normFactor = initValue/expectedY; + TS_ASSERT_THROWS_NOTHING( norm1.setProperty("InputWorkspace", inWS) ); TS_ASSERT_THROWS_NOTHING( norm1.setPropertyValue("OutputWorkspace",wsNameOut) ); @@ -89,6 +97,16 @@ public: TS_ASSERT_EQUALS( output->YUnit(), "Counts" ); TS_ASSERT_EQUALS( output->YUnitLabel(), "Counts per microAmp.hour" ); + Kernel::Property * normLog(NULL); + TS_ASSERT_THROWS_NOTHING(normLog = output->run().getProperty("NormalizationFactor")); + Kernel::PropertyWithValue<double> *pFactor = dynamic_cast<Kernel::PropertyWithValue<double> *>(normLog); + TS_ASSERT(pFactor); + + if(checkNormFactor){ + double realFactor = (*pFactor)(); + TS_ASSERT_DELTA(realFactor,normFactor,1.e-5); + } + return output; } diff --git a/Code/Mantid/Framework/DataHandling/src/LoadCanSAS1D.cpp b/Code/Mantid/Framework/DataHandling/src/LoadCanSAS1D.cpp index c65700a0ab877467b7a9657ac2650fb8d9a94164..4d3cce48d980a65722c542de8796716507cdf66e 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadCanSAS1D.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadCanSAS1D.cpp @@ -184,6 +184,8 @@ LoadCanSAS1D::loadEntry(Poco::XML::Node *const workspaceData, MantidVec &E = dataWS->dataE(0); MantidVec &Dx = dataWS->dataDx(0); int vecindex = 0; + std::string yUnit = ""; + bool isCommon = true; // iterate through each Idata element and get the values of "Q", //"I" and "Idev" text nodes and fill X,Y,E vectors for (unsigned long index = 0; index < nBins; ++index) { @@ -212,6 +214,9 @@ LoadCanSAS1D::loadEntry(Poco::XML::Node *const workspaceData, // setting Y vector Element *iElem = elem->getChildElement("I"); check(qElem, "I"); + const std::string unit = iElem->getAttribute("unit"); + if (index == 0) yUnit = unit; + else if (unit != yUnit) isCommon = false; nodeVal = iElem->innerText(); std::stringstream y(nodeVal); y >> d; @@ -238,6 +243,7 @@ LoadCanSAS1D::loadEntry(Poco::XML::Node *const workspaceData, runLoadInstrument(instname, dataWS); dataWS->getAxis(0)->setUnit("MomentumTransfer"); + if (isCommon == true) dataWS->setYUnitLabel(yUnit); return dataWS; } /* This method throws not found error if a element is not found in the xml file diff --git a/Code/Mantid/Framework/DataHandling/src/LoadGSS.cpp b/Code/Mantid/Framework/DataHandling/src/LoadGSS.cpp index c8196031037e277217f18df4496d1ce16f3632a3..9ddea8c26c208e5ff974ed8f0dda42b49d17186a 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadGSS.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadGSS.cpp @@ -309,8 +309,6 @@ API::MatrixWorkspace_sptr LoadGSS::loadGSASFile(const std::string &filename, xPrev = -0.0; } - std::istringstream inputLine(currentLine, std::ios::in); - // It is different for the definition of X, Y, Z in SLOG and RALF format if (filetype == 'r') { // RALF @@ -318,16 +316,27 @@ API::MatrixWorkspace_sptr LoadGSS::loadGSASFile(const std::string &filename, // std::setw // For this reason we need to read the column values as string and then // convert to double - std::string xString, yString, eString; - inputLine >> std::setw(11) >> xString >> std::setw(18) >> yString >> - std::setw(18) >> eString; - xValue = boost::lexical_cast<double>(xString); - yValue = boost::lexical_cast<double>(yString); - eValue = boost::lexical_cast<double>(eString); + { + std::string str(currentLine, 15); + std::istringstream istr(str); + istr >> xValue; + } + { + std::string str(currentLine + 15, 18); + std::istringstream istr(str); + istr >> yValue; + } + { + std::string str(currentLine + 15 + 18, 18); + std::istringstream istr(str); + istr >> eValue; + } + xValue = (2 * xValue) - xPrev; } else if (filetype == 's') { // SLOG + std::istringstream inputLine(currentLine, std::ios::in); inputLine >> xValue >> yValue >> eValue; if (calslogx0) { // calculation of x0 must use the x'[0] diff --git a/Code/Mantid/Framework/DataHandling/src/RenameLog.cpp b/Code/Mantid/Framework/DataHandling/src/RenameLog.cpp index c9c3d2adcbb8d12739da455ec526f949b03faf4c..c7c11a1985f729e21c693f10878ee044caa320ac 100644 --- a/Code/Mantid/Framework/DataHandling/src/RenameLog.cpp +++ b/Code/Mantid/Framework/DataHandling/src/RenameLog.cpp @@ -42,7 +42,7 @@ void RenameLog::exec() { std::string origlogname = this->getProperty("OriginalLogName"); std::string newlogname = this->getProperty("NewLogName"); - Kernel::Property *property = matrixWS->run().getLogData(origlogname); + Kernel::Property *property = matrixWS->run().getLogData(origlogname)->clone(); Kernel::TimeSeriesProperty<double> *timeprop = dynamic_cast<Kernel::TimeSeriesProperty<double> *>(property); @@ -53,7 +53,7 @@ void RenameLog::exec() { } // std::cout << "Remove log" << origlogname << std::endl; - matrixWS->mutableRun().removeLogData(origlogname, false); + matrixWS->mutableRun().removeLogData(origlogname); // std::cout << "Change log name" << std::endl; timeprop->setName(newlogname); diff --git a/Code/Mantid/Framework/DataHandling/test/LoadGSSTest.h b/Code/Mantid/Framework/DataHandling/test/LoadGSSTest.h index be74e7532776f739ab4d720f976f12463da59731..62457964d0cb2c354ced585c4c5d66d0ab0c0f4c 100644 --- a/Code/Mantid/Framework/DataHandling/test/LoadGSSTest.h +++ b/Code/Mantid/Framework/DataHandling/test/LoadGSSTest.h @@ -4,12 +4,11 @@ #include "cxxtest/TestSuite.h" #include "MantidDataHandling/LoadGSS.h" #include "MantidAPI/AlgorithmManager.h" -//#include "MantidAPI/AnalysisDataService.h" -//#include <Poco/File.h> -//#include <fstream> +#include "MantidTestHelpers/ScopedFileHelper.h" using namespace Mantid; using Mantid::DataHandling::LoadGSS; +using ScopedFileHelper::ScopedFile; class LoadGSSTest : public CxxTest::TestSuite { @@ -24,13 +23,54 @@ public: TS_ASSERT_EQUALS( loader.version(), 1 ) } - void test_load_gss_txt() - { + void test_load_gss_txt() { API::IAlgorithm_sptr loader = createAlgorithm(); - loader->setPropertyValue("Filename","gss.txt"); - TS_ASSERT( loader->execute() ) + loader->setPropertyValue("Filename", "gss.txt"); + TS_ASSERT(loader->execute()) + API::MatrixWorkspace_const_sptr ws = loader->getProperty("OutputWorkspace"); // Check a few things in the workspace - checkWorkspace( loader->getProperty("OutputWorkspace"), 8, 816); + checkWorkspace(ws, 8, 816); + auto x1 = ws->readX(0)[99]; + auto x2 = ws->readX(0)[100]; + auto y = ws->readY(0)[99]; + TS_ASSERT_DELTA((x1 + x2)/2, 40844.0625, 1e-6); + TS_ASSERT_DELTA(y, 145304004.625, 1e-6); + } + + void test_large_x_values() { + std::string gss = + "LuBaCo4O7 HR BS P=2GPa Tcryo=130.0K " + " \n" + "# 1 Histograms\n" + "# File generated by Mantid:\n" + "# Instrument: WISH\n" + "# From workspace named : w21552-2foc\n" + "# with Y multiplied by the bin widths.\n" + "# Primary flight path 40m \n" + "# Total flight path 42.2222m, tth 58.308deg, DIFC 10398.8\n" + "# Data for spectrum :0\n" + "BANK 1 4399 4399 RALF 166409 124 166409 0.00074 FXYE\n" + " 115202.20029 123456.00000002 0.00000000\n" + " 115206.06310 1234567.00000003 0.00000000\n" + " 115209.92877 12345678.00000004 0.00000000\n" + " 115213.79731123456789.00000005 0.00000000\n" + " 115217.66873234567890.00000006 0.00000000"; + ScopedFile file(gss,"gss_large_x.txt"); + API::IAlgorithm_sptr loader = createAlgorithm(); + loader->setPropertyValue("Filename", file.getFileName()); + TS_ASSERT(loader->execute()) + API::MatrixWorkspace_const_sptr ws = loader->getProperty("OutputWorkspace"); + auto x1 = ws->readX(0)[0]; + auto x2 = ws->readX(0)[1]; + auto dx = x2 - x1; + auto y = ws->readY(0)[0] * dx; + TS_ASSERT_DELTA((x1 + x2)/2, 115202.20029, 1e-6); + TS_ASSERT_DELTA(y, 123456.00000002, 1e-10); + x1 = ws->readX(0)[3]; + x2 = ws->readX(0)[4]; + dx = x2 - x1; + y = ws->readY(0)[3] * dx; + TS_ASSERT_DELTA(y, 123456789.00000005, 1e-10); } void test_load_gss_ExtendedHeader_gsa() diff --git a/Code/Mantid/Framework/DataHandling/test/RenameLogTest.h b/Code/Mantid/Framework/DataHandling/test/RenameLogTest.h index 77a150b0a3c1ca17a7fa0b3b9c0a150d4448ff2a..09f66e3f81ff2fb07bea2c75a6369f1ab3902275 100644 --- a/Code/Mantid/Framework/DataHandling/test/RenameLogTest.h +++ b/Code/Mantid/Framework/DataHandling/test/RenameLogTest.h @@ -23,7 +23,6 @@ public: static RenameLogTest *createSuite() { return new RenameLogTest(); } static void destroySuite( RenameLogTest *suite ) { delete suite; } - void test_Init() { RenameLog renlog; @@ -32,37 +31,39 @@ public: TS_ASSERT(renlog.isInitialized()); } - void test_Rename(){ + void test_Rename() + { // 1. Generate workspace & 2 logs - API::MatrixWorkspace_sptr mWS = API::WorkspaceFactory::Instance().create("Workspace2D", 1, 100, 100); + API::MatrixWorkspace_sptr testWS = generateTestWorkspace(); - Kernel::TimeSeriesProperty<double> *p1 = new Kernel::TimeSeriesProperty<double>("OriginalLog"); + // 2. Add workspace to data serive + AnalysisDataService::Instance().addOrReplace("TestDummy", testWS); - int64_t t1_ns = 1000000; - int64_t dt_ns = 400; - double v1 = -1.0; - size_t num1 = 10; - std::vector<Kernel::DateAndTime> rawtimes; - std::vector<double> rawvalues; + // 3. Running + RenameLog renlog; + renlog.initialize(); - for (size_t i=0; i<num1; i ++){ - Kernel::DateAndTime time(t1_ns); - p1->addValue(time, v1); + TS_ASSERT_THROWS_NOTHING(renlog.setPropertyValue("Workspace", "TestDummy")); + TS_ASSERT_THROWS_NOTHING(renlog.setProperty("OriginalLogName", "OriginalLog")); + TS_ASSERT_THROWS_NOTHING(renlog.setProperty("NewLogName", "NewLog")); - rawtimes.push_back(time); - rawvalues.push_back(v1); + renlog.execute(); + TS_ASSERT(renlog.isExecuted()); - t1_ns += dt_ns; - v1 = -v1; - } + // 4. Check + API::MatrixWorkspace_sptr resultWS = AnalysisDataService::Instance().retrieveWS<API::MatrixWorkspace>("TestDummy"); + verifyLog(resultWS, "NewLog"); + } - mWS->mutableRun().addProperty(p1); - // 2. Add workspace to data serive - AnalysisDataService::Instance().addOrReplace("TestDummy", mWS); + void test_RenameCloned() + { + API::MatrixWorkspace_sptr testWS = generateTestWorkspace(); + API::MatrixWorkspace_sptr clonedWS = WorkspaceFactory::Instance().create(testWS); + AnalysisDataService::Instance().addOrReplace("TestDummy", testWS); + AnalysisDataService::Instance().addOrReplace("TestClonedDummy", clonedWS); - // 3. Running RenameLog renlog; renlog.initialize(); @@ -73,23 +74,69 @@ public: renlog.execute(); TS_ASSERT(renlog.isExecuted()); - // 4. Check - API::MatrixWorkspace_sptr rWS = AnalysisDataService::Instance().retrieveWS<API::MatrixWorkspace>("TestDummy"); + // Verify modified logs on original workspace + API::MatrixWorkspace_sptr resultWS = AnalysisDataService::Instance().retrieveWS<API::MatrixWorkspace>("TestDummy"); + verifyLog(resultWS, "NewLog"); + // Verify original logs on cloned workspace + resultWS = AnalysisDataService::Instance().retrieveWS<API::MatrixWorkspace>("TestClonedDummy"); + verifyLog(resultWS, "OriginalLog"); + } + + +private: + API::MatrixWorkspace_sptr generateTestWorkspace() + { + API::MatrixWorkspace_sptr testWS = API::WorkspaceFactory::Instance().create("Workspace2D", 1, 100, 100); + + Kernel::TimeSeriesProperty<double> *p1 = new Kernel::TimeSeriesProperty<double>("OriginalLog"); + + int64_t t1_ns = 1000000; + int64_t dt_ns = 400; + double v1 = -1.0; + + m_num1 = 10; + m_rawTimes.clear(); + m_rawValues.clear(); + + for (size_t i=0; i<m_num1; i ++) + { + Kernel::DateAndTime time(t1_ns); + p1->addValue(time, v1); + + m_rawTimes.push_back(time); + m_rawValues.push_back(v1); + + t1_ns += dt_ns; + v1 = -v1; + } + + testWS->mutableRun().addProperty(p1); + + return testWS; + } + + + void verifyLog(API::MatrixWorkspace_sptr resultWS, const std::string logName) + { Kernel::TimeSeriesProperty<double> *rp; - TS_ASSERT_THROWS_NOTHING(rp = dynamic_cast<Kernel::TimeSeriesProperty<double>* >(rWS->run().getProperty("NewLog"))); - rp = dynamic_cast<Kernel::TimeSeriesProperty<double>* >(rWS->run().getProperty("NewLog")); + TS_ASSERT_THROWS_NOTHING(rp = dynamic_cast<Kernel::TimeSeriesProperty<double>* >(resultWS->run().getProperty(logName))); + rp = dynamic_cast<Kernel::TimeSeriesProperty<double>* >(resultWS->run().getProperty(logName)); std::vector<Kernel::DateAndTime> newtimes = rp->timesAsVector(); - TS_ASSERT_EQUALS(newtimes.size(), num1); - for (size_t i=0; i<num1; i ++){ + TS_ASSERT_EQUALS(newtimes.size(), m_num1); + for (size_t i=0; i<m_num1; i ++) + { double newvalue; - TS_ASSERT_THROWS_NOTHING(newvalue= rp->getSingleValue(rawtimes[i])); - newvalue= rp->getSingleValue(rawtimes[i]); - TS_ASSERT_DELTA(newvalue, rawvalues[i], 1.0E-8); + TS_ASSERT_THROWS_NOTHING(newvalue= rp->getSingleValue(m_rawTimes[i])); + newvalue= rp->getSingleValue(m_rawTimes[i]); + TS_ASSERT_DELTA(newvalue, m_rawValues[i], 1.0E-8); } } + size_t m_num1; + std::vector<Kernel::DateAndTime> m_rawTimes; + std::vector<double> m_rawValues; }; diff --git a/Code/Mantid/Framework/LiveData/CMakeLists.txt b/Code/Mantid/Framework/LiveData/CMakeLists.txt index 1a49b0977c99f52beb1d313a2322c6725e5503b3..0d25e071433068114e847a4bbd2b5eb5d5b156d6 100644 --- a/Code/Mantid/Framework/LiveData/CMakeLists.txt +++ b/Code/Mantid/Framework/LiveData/CMakeLists.txt @@ -5,6 +5,7 @@ set ( SRC_FILES src/FakeISISHistoDAE.cpp src/FileEventDataListener.cpp src/ISISHistoDataListener.cpp + src/ISISLiveEventDataListener.cpp src/LiveDataAlgorithm.cpp src/LoadDAE/idc.cpp src/LoadDAE/idc.h @@ -14,7 +15,7 @@ set ( SRC_FILES src/MonitorLiveData.cpp src/SNSLiveEventDataListener.cpp src/StartLiveData.cpp - src/ISISLiveEventDataListener.cpp + src/TOPAZLiveEventDataListener.cpp ) set ( SRC_UNITY_IGNORE_FILES src/LoadDAE/idc.cpp @@ -29,13 +30,14 @@ set ( INC_FILES inc/MantidLiveData/FakeISISHistoDAE.h inc/MantidLiveData/FileEventDataListener.h inc/MantidLiveData/ISISHistoDataListener.h + inc/MantidLiveData/ISISLiveEventDataListener.h + inc/MantidLiveData/ISIS/TCPEventStreamDefs.h inc/MantidLiveData/LiveDataAlgorithm.h inc/MantidLiveData/LoadLiveData.h inc/MantidLiveData/MonitorLiveData.h inc/MantidLiveData/SNSLiveEventDataListener.h inc/MantidLiveData/StartLiveData.h - inc/MantidLiveData/ISISLiveEventDataListener.h - inc/MantidLiveData/ISIS/TCPEventStreamDefs.h + inc/MantidLiveData/TOPAZLiveEventDataListener.h ) set ( TEST_FILES diff --git a/Code/Mantid/Framework/LiveData/inc/MantidLiveData/TOPAZLiveEventDataListener.h b/Code/Mantid/Framework/LiveData/inc/MantidLiveData/TOPAZLiveEventDataListener.h new file mode 100644 index 0000000000000000000000000000000000000000..8c1dc6895401c8b2d4b2df873f5a93339735918a --- /dev/null +++ b/Code/Mantid/Framework/LiveData/inc/MantidLiveData/TOPAZLiveEventDataListener.h @@ -0,0 +1,129 @@ +#ifndef MANTID_LIVEDATA_TOPAZLIVEEVENTDATALISTENER_H_ +#define MANTID_LIVEDATA_TOPAZLIVEEVENTDATALISTENER_H_ + +//---------------------------------------------------------------------- +// Includes +//---------------------------------------------------------------------- +#include "MantidAPI/ILiveListener.h" +#include "MantidDataObjects/EventWorkspace.h" +#include "MantidKernel/MultiThreaded.h" + +#include <Poco/Timer.h> +#include <Poco/Net/StreamSocket.h> +#include <Poco/Net/DatagramSocket.h> +#include <Poco/Net/SocketAddress.h> +#include <Poco/Runnable.h> + +namespace Mantid { +namespace LiveData { + +/** An implementation of ILiveListener for use on the TOPAZ beamline at SNS. + Connects to the old DAS system and receives events from it. + + Copyright © 2015 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge + National Laboratory + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +class TOPAZLiveEventDataListener : public API::ILiveListener, + public Poco::Runnable { +public: + TOPAZLiveEventDataListener(); + virtual ~TOPAZLiveEventDataListener(); + + std::string name() const { return "TOPAZLiveEventDataListener"; } + bool supportsHistory() const { return false; } + bool buffersEvents() const { return true; } + + bool connect(const Poco::Net::SocketAddress &address); + void start(Kernel::DateAndTime startTime = Kernel::DateAndTime()); + boost::shared_ptr<API::Workspace> extractData(); + + ILiveListener::RunStatus runStatus(); + // Called by the MonitorLiveData algorithm. + + + int runNumber() const { return m_runNumber; }; + + bool isConnected(); + + virtual void run(); // the background thread. What gets executed when we + // call POCO::Thread::start() +protected: + +private: + + void initWorkspace(); + void initMonitorWorkspace(); + + void appendEvent(uint32_t pixelId, double tof, + const Mantid::Kernel::DateAndTime pulseTime); + // tof is "Time Of Flight" and is in units of microsecondss relative to the + // start of the pulse. (There's some documentation that says nanoseconds, + // but Russell Taylor assures me it's really in microseconds!) + // pulseTime is the start of the pulse relative to Jan 1, 1990. + // Both values are designed to be passed straight into the TofEvent + // constructor. + + // ILiveListener::RunStatus m_status; + // Currently commented out because we have yet to figure out how to + // get the run status from the data stream. If we ever re-enable + // this variable, make sure it's properly initialized in the + // constructo. + + bool m_workspaceInitialized; + + DataObjects::EventWorkspace_sptr + m_eventBuffer; ///< Used to buffer events between calls to extractData() + + // Names of any monitor logs (these must be manually removed during the call + // to extractData()) + std::vector<std::string> m_monitorLogs; + + std::string m_wsName; + detid2index_map m_indexMap; // maps pixel id's to workspace indexes + detid2index_map m_monitorIndexMap; // Same as above for the monitor workspace + + // We need these 2 strings to initialize m_buffer + //std::string m_instrumentName; + //std::string m_instrumentXML; + + Poco::Net::StreamSocket m_tcpSocket; // used for initial connection to event_catcher + Poco::Net::DatagramSocket m_dataSocket; // used to receive actual event data + Poco::Net::SocketAddress m_dataAddr; + bool m_isConnected; + + // Buffer to hold the UDP data pakcets + unsigned char *m_udpBuf; + unsigned int m_udpBufSize; + + int m_runNumber; + + Poco::FastMutex m_mutex; // protects m_eventBuffer & m_status + Poco::Thread m_thread; + bool m_stopThread; // background thread checks this periodically. + // If true, the thread exits + + // Holds on to any exceptions that were thrown in the background thread so + // that we can re-throw them in the forground thread + boost::shared_ptr<std::runtime_error> m_backgroundException; + +}; + +} // namespace LiveData +} // namespace Mantid + +#endif /* MANTID_LIVEDATA_TOPAZLIVEEVENTDATALISTENER_H_ */ diff --git a/Code/Mantid/Framework/LiveData/src/TOPAZLiveEventDataListener.cpp b/Code/Mantid/Framework/LiveData/src/TOPAZLiveEventDataListener.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cbff735f5fbca88ce0c06db2487967059b733193 --- /dev/null +++ b/Code/Mantid/Framework/LiveData/src/TOPAZLiveEventDataListener.cpp @@ -0,0 +1,665 @@ +#include "MantidAPI/AlgorithmManager.h" +#include "MantidAPI/LiveListenerFactory.h" +#include "MantidAPI/WorkspaceFactory.h" +#include "MantidLiveData/TOPAZLiveEventDataListener.h" +#include "MantidLiveData/Exception.h" +//#include "MantidDataObjects/Events.h" +//#include "MantidKernel/DateAndTime.h" +//#include "MantidKernel/Strings.h" +#include "MantidKernel/TimeSeriesProperty.h" +#include "MantidKernel/UnitFactory.h" +//#include "MantidKernel/WriteLock.h" +#include "MantidDataObjects/EventWorkspace.h" + +#include <Poco/Net/NetException.h> +#include <Poco/Net/StreamSocket.h> +#include <Poco/Net/DatagramSocket.h> +#include <Poco/Net/SocketAddress.h> +//#include <Poco/Net/SocketStream.h> +//#include <Poco/Timestamp.h> + +// Includes for parsing the XML device descriptions +//#include "Poco/DOM/DOMParser.h" +//#include "Poco/DOM/Document.h" +//#include "Poco/DOM/AutoPtr.h" +//#include "Poco/DOM/NodeList.h" +//#include "Poco/DOM/NamedNodeMap.h" + +//#include <Poco/Thread.h> +//#include <Poco/Runnable.h> + +//#include <time.h> +//#include <sstream> // for ostringstream +#include <string> +#include <fstream> +#include <exception> + +using namespace Mantid::Kernel; +using namespace Mantid::API; + +#if 0 +// Port numbers +// Basically, we're going to mimic the network protocol that iSawEV uses, +// since that's what the event_catcher util on TOPAZ knows to output +#define ISAW_EV_PORT 9000 +#define TEST_ISAW_EV_PORT 9100 + +// Not sure we'll need these +#define ISAW_EV_DATA_PORT 9012 +#define ISAW_EV_CMD_PORT 9013 +#define TEST_ISAW_EV_DATA_PORT 9112 +#define TEST_ISAW_EV_CMD_PORT 9113 +#endif + +// Time we'll wait on a receive call (in seconds) +// Also used when shutting down the thread so we know how long to wait there +#define RECV_TIMEOUT 30 + +// Names for a couple of time series properties +// TODO: Do we need the scan and pause properties? +//#define PAUSE_PROPERTY "pause" +//#define SCAN_PROPERTY "scan_index" +#define PROTON_CHARGE_PROPERTY "proton_charge" + + +// Data structures for the UDP packet that we receive. +// These were copied from the das_proto.h file in the LiveStreaming +// subversion repository +struct socket_header_struct +{ + int32_t iReceiveID; // unique identifier for message, sequence #... + int32_t iCommandID; // determines kind of packets to come... + int32_t iTotalBytes; // # of total bytes in payload! + int32_t Spare1; // size of PULSE_ID payload in bytes... + int32_t Spare2; // just a spare + int32_t Spare3; // detector bank id +}; + +typedef struct socket_header_struct COMMAND_HEADER, *COMMAND_HEADER_PTR; + +struct pulse_id_struct +{ + uint32_t pulseIDlow; + uint32_t pulseIDhigh; + uint64_t eventID; + double charge; // TODO: this *might* be in picoCoulombs, or it + // might be units of 10 picoCoulombs... +}; + +typedef struct pulse_id_struct PULSE_ID, *PULSE_ID_PTR; + +struct neutron_event_struct +{ + uint32_t tof; // Time-of-flight (we *think* units are 100ns - + // divide by 10 to get microseconds) + uint32_t pixelId; +}; + + +typedef struct neutron_event_struct NEUTRON_EVENT, *NEUTRON_EVENT_PTR; + + +/****************************************************************************** + * UDP Packet structure + * + * From what I've been able to deduce so far, this is how the UDP packets + * are organized: + * + * --------------------------------- + * | socket_header_struct | + * |-------------------------------| + * | first pulse_id_struct | + * |-------------------------------| + * | ... | + * |-------------------------------| + * | n'th pulse_id_struct | + * |-------------------------------| + * | first neutron_event_struct | + * |-------------------------------| + * | ... | + * |-------------------------------| + * | n'th neutron_event_struct | + * --------------------------------| + * + * There will be one or more neutron event structs. Excatly how many there + * are is determined from socket_header_struct.Spare1. This is the size in + * bytes occupied by pulse_id_structs. Dividing by sizeof(pulse_id_struct) + * gives the number of structs. + * + * The remaining data in the packet is neutron_event_structs. + * socket_header_struct.iTotalBytes - socket_header_struct.Spare1 is the size + * of the event data and dividing by sizeof(neutron_event_struct) gives the + * number of events. + * + * If you treat the event data in the packet as an array of + * neutron_event_structs, then the eventID value in each pulse_id_struct is + * the index into that array of the first event associated with that pulse. + * + * Note that socket_header_struct.iTotalBytes does NOT include the size of + * the socket_header_struct itself. Thus the total packet size is + * sizeof(socket_header_struct) + socket_header_struct.iTotalBytes. + ****************************************************************************/ + + +// Helper function to get a DateAndTime value from a pulse_id_struct +Mantid::Kernel::DateAndTime timeFromPulse(const pulse_id_struct *p) { + uint32_t seconds = p->pulseIDhigh; + uint32_t nanoseconds = p->pulseIDlow; + + // Make sure we pick the correct constructor (the Mac gets an ambiguous error) + return DateAndTime(static_cast<int64_t>(seconds), + static_cast<int64_t>(nanoseconds)); +} + + +namespace Mantid { +namespace LiveData { +DECLARE_LISTENER(TOPAZLiveEventDataListener) + +namespace { +/// static logger +Kernel::Logger g_log("SNSLiveEventDataListener"); +} + +/// Constructor +TOPAZLiveEventDataListener::TOPAZLiveEventDataListener() + : ILiveListener(), m_udpBufSize(32768), + m_runNumber(0),m_stopThread(false) +{ + + m_udpBuf = new unsigned char[m_udpBufSize]; + + // Note: not doing much actual initialization here. For reasons that + // are unclear, this object may get created and destroyed several times + // in the process of opening up the StartLiveData dialog box. As such + // we really want the constructor to be as quick as possible. Therefore, + // most of the initialization is done at the top of the run() function. +} + +/// Destructor +TOPAZLiveEventDataListener::~TOPAZLiveEventDataListener() +{ + + if (m_thread.isRunning()) + { + // Ask the thread to exit (and hope that it does - Poco doesn't + // seem to have an equivalent to pthread_cancel + m_stopThread = true; + try { + m_thread.join(RECV_TIMEOUT * 2 * 1000); + // *1000 because join() wants time in milliseconds + } catch (Poco::TimeoutException &) { + // And just what do we do here?!? + // Log a message, sure, but other than that we can either hang the + // Mantid process waiting for a thread that will apparently never + // exit or segfault because the thread is going to try to write to + // a buffer that's about to be deleted. + // Chose segfault - at least that's obvious. + g_log.fatal() << "TOPAZLiveEventDataListener failed to shut down " + << "its background thread! This should never happen and " + << "Mantid is pretty much guaranteed to crash shortly. " + << "Talk to the Mantid developer team." << std::endl; + } + } + + delete[] m_udpBuf; +} + +/// Connect to the TOPAZ event_catcher util (which will supply us with +/// events). +/// @param address The address to attempt to connect to +/// @return Returns true if the connection succeeds. False otherwise. +bool TOPAZLiveEventDataListener::connect( + const Poco::Net::SocketAddress &address) +// The SocketAddress class will throw various exceptions if it encounters an +// error. We're assuming the calling function will catch any exceptions that +// are important +// Note: Right now, it's the factory class that actually calls connect(), and +// it doesn't check the return value. (It does, however, trap the Poco +// exceptions.) +{ + bool rv = false; // assume failure + + try { + m_tcpSocket.connect(address); // BLOCKING connect + } catch (...) { + g_log.error() << "Connection to " << address.toString() << " failed." + << std::endl; + return false; + } + + Poco::Net::SocketAddress tcpAddress = m_tcpSocket.address(); + + m_tcpSocket.setReceiveTimeout(Poco::Timespan(RECV_TIMEOUT, 0)); + // POCO timespan is seconds, microseconds + + g_log.debug() << "Connected to " << tcpAddress.toString() + << std::endl; + + // After connecting to the main port (either 9000 or 9100 depending on + // whether or not we're in test mode), event_catcher will send us the port + // number it's using for the event data. + uint16_t dataPort; + try { + if (m_tcpSocket.receiveBytes(&dataPort, sizeof(dataPort)) != + sizeof(dataPort)) + { + g_log.error() << "Failed to read entire data port num from " + << tcpAddress.toString() << std::endl; + return false; + } + } catch (...) { + g_log.error() << "Error reading data port num from " + << tcpAddress.toString() << std::endl; + return false; + } + + // Note: No, there's no byte-swapping done on dataPort. It appears that + // event_catcher does no byte swapping on any of its sends, so we can't + // do any here. Since event_catcher pretty much has to run on the same + // machine as this code, endianness shouldn't actually be a problem. + + // Set up the socket address we'll use in the receiveFrom() calls + // Oddly: the only way to set the values on a SocketAddress is with + // operator= or the constructor... + // TODO: For now, we have to hard-code the address as localhost because + // that's what event-catcher sends to. It currently isn't smart enough + // to send UDP packets back to the address that actually connected to + // the TCP port... + m_dataAddr = Poco::Net::SocketAddress( + Poco::Net::IPAddress( "127.0.0.1"), dataPort); + + rv = m_isConnected = true; + // Note: we leave m_tcpSocket connected. Otherwise, event_catcher stops + // sending events to us. + return rv; +} + +/// Test to see if the object has connected to the DAS + +/// Test to see if the object has connected to the DAS +/// @return Returns true if connected. False otherwise. +bool TOPAZLiveEventDataListener::isConnected() { return m_isConnected; } + + +/// Start the background thread + +/// Starts the background thread which reads data from the network, parses +/// it and stores the resulting events in a temporary workspace. +/// @param startTime Ignored. This class doesn't have the capability to +/// replay historical data. +void TOPAZLiveEventDataListener::start(Kernel::DateAndTime startTime) +{ + (void)startTime; // Keep the compiler from complaining about unsed variable + + + // Initialize the workspace + // NOTE: initWorkspace() may take several seconds to complete. Most of the + // time seems to be spent loading the workspace. + initWorkspace(); + initMonitorWorkspace(); + m_workspaceInitialized = true; + + + m_thread.start(*this); +} + + +/// The main function for the background thread + +/// Loops until the forground thread requests it to stop. Reads data from the +/// network, parses it and stores the resulting events (and other metadata) in +/// a temporary workspace. +void TOPAZLiveEventDataListener::run() { + try { + + if (m_isConnected == false) // sanity check + { + throw std::runtime_error(std::string( + "TOPAZLiveEventDataListener::run(): No connection to event_catcher.")); + } + + m_dataSocket.bind(m_dataAddr); + m_dataSocket.setReceiveTimeout(Poco::Timespan( + RECV_TIMEOUT, 0)); // POCO timespan is seconds, microseconds + + Poco::Net::SocketAddress sendAddr; // address of the sender + // loop until the foreground thread tells us to stop + while (m_stopThread == false) + { + // it's possible that a stop request came in while we were sleeping... + if (m_stopThread) { + break; + } + + int bytesRead = 0; + try { + bytesRead = m_dataSocket.receiveFrom(m_udpBuf, m_udpBufSize, sendAddr); + } catch (Poco::TimeoutException &) { + if (m_stopThread == false) { + // Don't need to stop processing or anything - just log a warning + g_log.warning( "Timeout reading from the network. " + "Is event_catcher still sending?"); + } + + continue; // don't process the data in the buffer (since + // it's incomplete or otherwise bad) + + } catch (Poco::Net::NetException &e) { + std::string msg("m_dataSocket::receiveBytes(): "); + msg += e.name(); + throw std::runtime_error(msg); + } + + // If I understand things correctly, the data sitting in m_udpBuf will + // be organized as a socket_header_struct, followed by one or more + // pulse_id_structs, followed by zero or more neutron_event_structs. + // + // Lets start with some basic decoding and logging... + COMMAND_HEADER_PTR hdr = (COMMAND_HEADER_PTR)m_udpBuf; + unsigned long num_pulse_ids = hdr->Spare1 / sizeof(PULSE_ID); + unsigned long num_events = (hdr->iTotalBytes - hdr->Spare1) / + sizeof(NEUTRON_EVENT); + + + g_log.debug() << "Received UDP Packet. " << bytesRead << " bytes " + << num_pulse_ids << " pulses " << num_events + << " events" << std::endl; + + PULSE_ID_PTR pid = (PULSE_ID_PTR)(m_udpBuf + sizeof(COMMAND_HEADER)); + NEUTRON_EVENT_PTR events = + (NEUTRON_EVENT_PTR)(m_udpBuf + sizeof(COMMAND_HEADER) + + (num_pulse_ids * sizeof(PULSE_ID))); + + for (unsigned i = 0; i < num_pulse_ids; i++) + { + g_log.debug() << "Pulse ID: " << pid[i].pulseIDhigh << ", " + << pid[i].pulseIDlow << std::endl; + g_log.debug() << " Event ID: " << pid[i].eventID << std::endl; + g_log.debug() << " Charge: " << pid[i].charge << std::endl; + + // Figure out the event indexes that belong to this pulse + uint64_t firstEvent; + uint64_t lastEvent; + if (num_pulse_ids == 1) + { + // This 'if' test is something of a workaround for a problem + // we saw on the beamline: event_catcher was spewing + // eventID's that were much too high. + firstEvent = 0; + } + else + { + firstEvent = pid[i].eventID; + } + + if (i == num_pulse_ids - 1) // last pulse in the packet? + { + lastEvent = num_events - 1; + } + else + { + if (firstEvent == pid[i+1].eventID) + { + // this pulse had no events + continue; + } + lastEvent = pid[i+1].eventID - 1; + } + + // Timestamp for the events + Mantid::Kernel::DateAndTime eventTime = timeFromPulse(&pid[i]); + + Poco::ScopedLock<Poco::FastMutex> scopedLock(m_mutex); + // Save the pulse charge in the logs + // TODO: We're not sure what the units are on the charge value + // They *might* be picoCoulombs, or the might be units of 10pC + // (in which case we need to multiply by 10 because the + // property is definitely in picoCoulombs.) + m_eventBuffer->mutableRun() + .getTimeSeriesProperty<double>(PROTON_CHARGE_PROPERTY) + ->addValue(eventTime, pid[i].charge); + + if (firstEvent > lastEvent) + { + g_log.error() << "Invalid event indexes! firstEvent: " << firstEvent + << " lastEvent: " << lastEvent << std::endl; + g_log.error() << "No events will be processed!" << std::endl; + } + + for (uint64_t j = firstEvent; j <= lastEvent; j++) + { + // appendEvent needs tof to be in units of microseconds, but + // it comes from the ADARA stream in units of 100ns. + // TODO: We're not using the ADARA stream! Verify that event_catcher also + // uses 100ns. + appendEvent(events[j].pixelId, events[j].tof / 10.0, eventTime); + } + } // TODO: Verify that the mutex is unlocked when we go back to + // the top of the for i loop + } + + + + + // If we've gotten here, it's because the thread has thrown an otherwise + // uncaught exception. In such a case, the thread will exit and there's + // nothing we can do about that. We'll log an error and save a copy of + // the exception object so that we can re-throw it from the foreground + // thread (which will cause the algorithm to exit). + // NOTE: For the default exception handler, we actually create a new + // runtime_error object and throw that, since there's no exception object + // passed in to the handler. + } catch (std::runtime_error &e) { + // exception handler for generic runtime exceptions + g_log.fatal() << "Caught a runtime exception.\n" + << "Exception message: " << e.what() << ".\n" + << "Thread will exit." << std::endl; + m_isConnected = false; + + m_backgroundException = + boost::shared_ptr<std::runtime_error>(new std::runtime_error(e)); + + } catch (std::invalid_argument &e) { + // TimeSeriesProperty (and possibly some other things) can throw + // these errors + g_log.fatal() << "Caught an invalid argument exception.\n" + << "Exception message: " << e.what() << ".\n" + << "Thread will exit." << std::endl; + m_isConnected = false; + + std::string newMsg( + "Invalid argument exception thrown from the background thread: "); + newMsg += e.what(); + m_backgroundException = + boost::shared_ptr<std::runtime_error>(new std::runtime_error(newMsg)); + } catch (Poco::Exception &e) { // Generic POCO exception handler + g_log.fatal( "Uncaught POCO exception in TOPAZLiveEventDataListener network " + "read thread."); + g_log.fatal( std::string("Exception message:" ) + e.displayText()); + g_log.fatal( "Thread is exiting."); + m_isConnected = false; + + m_backgroundException = boost::shared_ptr<std::runtime_error>( + new std::runtime_error("Unknown error in backgound thread")); + } catch (...) { // Default exception handler + g_log.fatal( "Uncaught exception in TOPAZLiveEventDataListener network " + "read thread. Thread is exiting."); + m_isConnected = false; + + m_backgroundException = boost::shared_ptr<std::runtime_error>( + new std::runtime_error("Unknown error in backgound thread")); + } + + return; +} + + +/// Workspace initialization + +/// Set up the internal workspace where we'll accumulate events +void TOPAZLiveEventDataListener::initWorkspace() +{ + m_eventBuffer = boost::static_pointer_cast<DataObjects::EventWorkspace>( + WorkspaceFactory::Instance().create("EventWorkspace", 1, 1, 1)); + // The numbers in the create() function don't matter - they'll get overwritten + // down in initWorkspacePart2() when we load the instrument definition. + + // Create the time series properties we'll need + Property *prop = new TimeSeriesProperty<double>(PROTON_CHARGE_PROPERTY); + m_eventBuffer->mutableRun().addLogData(prop); + + // Use the LoadEmptyInstrument algorithm to create a proper workspace + // for the TOPAZ beamline + boost::shared_ptr<Algorithm> loadInst = + Mantid::API::AlgorithmManager::Instance().createUnmanaged( + "LoadInstrument"); + loadInst->initialize(); + loadInst->setChild(true); // keep the workspace out of the ADS + // loadInst->setProperty("InstrumentXML", m_instrumentXML); + loadInst->setProperty("InstrumentName", "TOPAZ"); + loadInst->setProperty("Workspace", m_eventBuffer); + + loadInst->execute(); + + m_eventBuffer->padSpectra(); // expands the workspace to the size of the just + // loaded instrument + + // Set the units + m_eventBuffer->getAxis(0)->unit() = UnitFactory::Instance().create("TOF"); + m_eventBuffer->setYUnit("Counts"); + + m_indexMap = m_eventBuffer->getDetectorIDToWorkspaceIndexMap( + true /* bool throwIfMultipleDets */); + + // The DAS sends out data using "DAS Pixel ID's". These might need to be + // translated into "Logical Pixel ID's". For TOPAZ, it's a 1:1 mapping, + // so we're not going to bother. If we add another beamline, though, + // this is where we'd load in the pixel mapping file. + +} + + +/// Creates a monitor workspace sized to the number of monitors, with the +/// monitor IDs set +void TOPAZLiveEventDataListener::initMonitorWorkspace() { + auto monitors = m_eventBuffer->getInstrument()->getMonitors(); + auto monitorsBuffer = WorkspaceFactory::Instance().create( + "EventWorkspace", monitors.size(), 1, 1); + WorkspaceFactory::Instance().initializeFromParent(m_eventBuffer, + monitorsBuffer, true); + // Set the id numbers + for (size_t i = 0; i < monitors.size(); ++i) { + monitorsBuffer->getSpectrum(i)->setDetectorID(monitors[i]); + } + + m_monitorIndexMap = monitorsBuffer->getDetectorIDToWorkspaceIndexMap(true); + + m_eventBuffer->setMonitorWorkspace(monitorsBuffer); +} + + +/// Adds an event to the workspace +void TOPAZLiveEventDataListener::appendEvent( + uint32_t pixelId, double tof, const Mantid::Kernel::DateAndTime pulseTime) +// NOTE: This function does NOT lock the mutex! Make sure you do that +// before calling this function! +{ + // It'd be nice to use operator[], but we might end up inserting a value.... + // Have to use find() instead. + detid2index_map::iterator it = m_indexMap.find(pixelId); + if (it != m_indexMap.end()) { + std::size_t workspaceIndex = it->second; + Mantid::DataObjects::TofEvent event(tof, pulseTime); + m_eventBuffer->getEventList(workspaceIndex).addEventQuickly(event); + } else { + // TODO: do we want to disable this warning? Most of the time, we + // shouldn't have any invalid ID's, but if we do, we'll probably + // get a lot and flood the log with messages... + //g_log.warning() << "Invalid pixel ID: " << pixelId << " (TofF: " + // << tof << " microseconds)" << std::endl; + } +} + +/// Retrieve buffered data + +/// Called by the foreground thread to fetch data that's accumulated in +/// the temporary workspace. The temporary workspace is left empty and +/// ready to receive more data. +/// @return shared pointer to a workspace containing the accumulated data +boost::shared_ptr<Workspace> TOPAZLiveEventDataListener::extractData() { + + // Check to see if the background thread has thrown an exception. If so, + // re-throw it here. + if (m_backgroundException) { + throw(*m_backgroundException); + } + + // Sanity check - make sure the workspace has been initialized + if (!m_workspaceInitialized) { + throw std::runtime_error("TOPAZLiveEventDataListener: " + "The workspace has not been initialized."); + } + + using namespace DataObjects; + + // Make a brand new EventWorkspace + EventWorkspace_sptr temp = boost::dynamic_pointer_cast<EventWorkspace>( + API::WorkspaceFactory::Instance().create( + "EventWorkspace", m_eventBuffer->getNumberHistograms(), 2, 1)); + + // Copy geometry over. + API::WorkspaceFactory::Instance().initializeFromParent(m_eventBuffer, temp, + false); + + // Clear out the old logs, except for the most recent entry + temp->mutableRun().clearOutdatedTimeSeriesLogValues(); + + // Clear out old monitor logs + // TODO: At present, there's no way for monitor logs to be added + // to m_monitorLogs. Either implement this feature, or remove + // m_monitorLogs! + for (unsigned i = 0; i < m_monitorLogs.size(); i++) { + temp->mutableRun().removeProperty(m_monitorLogs[i]); + } + m_monitorLogs.clear(); + + // Create a fresh monitor workspace and insert into the new 'main' workspace + auto monitorBuffer = m_eventBuffer->monitorWorkspace(); + auto newMonitorBuffer = WorkspaceFactory::Instance().create( + "EventWorkspace", monitorBuffer->getNumberHistograms(), 1, 1); + WorkspaceFactory::Instance().initializeFromParent(monitorBuffer, + newMonitorBuffer, false); + temp->setMonitorWorkspace(newMonitorBuffer); + + // Lock the mutex and swap the workspaces + { + Poco::ScopedLock<Poco::FastMutex> scopedLock(m_mutex); + std::swap(m_eventBuffer, temp); + } // mutex automatically unlocks here + + return temp; +} + +/// Check the status of the current run + +/// Called by the foreground thread check the status of the current run +/// @returns Returns an enum indicating beginning of a run, in the middle +/// of a run, ending a run or not in a run. +ILiveListener::RunStatus TOPAZLiveEventDataListener::runStatus() { + + // First up, check to see if the background thread has thrown an + // exception. If so, re-throw it here. + if (m_backgroundException) { + throw(*m_backgroundException); + } + + // until we figure out how to get run info from the stream, this is + // all we can do + return ILiveListener::RunStatus::NoRun; +} + + + +} // namespace LiveData +} // namespace Mantid diff --git a/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/CutMD.h b/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/CutMD.h index 2ad56c17ac8fb378531609d91df78f79a2aaeee7..4d3124c7d3792d6c6fedc0a94ead2f450c2fd944 100644 --- a/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/CutMD.h +++ b/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/CutMD.h @@ -48,8 +48,13 @@ public: virtual void init(); virtual void exec(); - -private: + + static const std::string InvAngstromSymbol; + static const std::string RLUSymbol; + static const std::string AutoMethod; + static const std::string RLUMethod; + static const std::string InvAngstromMethod; + }; } // namespace MDAlgorithms diff --git a/Code/Mantid/Framework/MDAlgorithms/src/CutMD.cpp b/Code/Mantid/Framework/MDAlgorithms/src/CutMD.cpp index 85cbdcd35b5dfc35efcb00e49987fd9cefa22d55..c74e52602e582e2954fbdb4fb7c45e169a56e6ad 100644 --- a/Code/Mantid/Framework/MDAlgorithms/src/CutMD.cpp +++ b/Code/Mantid/Framework/MDAlgorithms/src/CutMD.cpp @@ -4,9 +4,15 @@ #include "MantidAPI/Projection.h" #include "MantidGeometry/Crystal/OrientedLattice.h" #include "MantidKernel/ArrayProperty.h" +#include "MantidKernel/ListValidator.h" #include "MantidKernel/Matrix.h" #include "MantidKernel/System.h" +#include <boost/make_shared.hpp> +#include <boost/regex.hpp> +#include <boost/algorithm/string.hpp> + + using namespace Mantid::API; using namespace Mantid::Geometry; using namespace Mantid::Kernel; @@ -47,7 +53,7 @@ DblMatrix scaleProjection(const DblMatrix &inMatrix, orientedLattice.dstar(inMatrix[i][0], inMatrix[i][1], inMatrix[i][2]); if (inUnits[i] == outUnits[i]) continue; - else if (inUnits[i] == "a") { + else if (inUnits[i] == Mantid::MDAlgorithms::CutMD::InvAngstromSymbol) { // inv angstroms to rlu for (size_t j = 0; j < numDims; ++j) ret[i][j] *= dStar; @@ -178,6 +184,34 @@ std::vector<std::string> labelProjection(const DblMatrix &projection) { } return ret; } + +/** +Determine the original q units. Assumes first 3 dimensions by index are r,l,d +@param inws : Input workspace to extract dimension info from +@param logger : logging object +@return vector of markers +*/ +std::vector<std::string> findOriginalQUnits(IMDWorkspace const *const inws, + Mantid::Kernel::Logger &logger) { + std::vector<std::string> unitMarkers(3); + for (size_t i = 0; i < inws->getNumDims() && i < 3; ++i) { + auto units = inws->getDimension(i)->getUnits(); + const boost::regex re("A\\^-1", boost::regex::icase); + // Does the unit label look like it's in Angstroms? + std::string unitMarker; + if (boost::regex_match(units.ascii(), re)) { + unitMarker = Mantid::MDAlgorithms::CutMD::InvAngstromSymbol; + } else { + unitMarker = Mantid::MDAlgorithms::CutMD::RLUSymbol; + } + unitMarkers[i] = unitMarker; + logger.debug() << "In dimension with index " << i << " and units " + << units.ascii() << " taken to be of type " << unitMarker + << std::endl; + } + return unitMarkers; +} + } // anonymous namespace namespace Mantid { @@ -186,6 +220,12 @@ namespace MDAlgorithms { // Register the algorithm into the AlgorithmFactory DECLARE_ALGORITHM(CutMD) +const std::string CutMD::InvAngstromSymbol = "a"; +const std::string CutMD::RLUSymbol = "r"; +const std::string CutMD::AutoMethod = "Auto"; +const std::string CutMD::RLUMethod = "RLU"; +const std::string CutMD::InvAngstromMethod = "Q in A^-1"; + //---------------------------------------------------------------------------------------------- /** Constructor */ @@ -221,6 +261,23 @@ void CutMD::init() { "as output. True to create an " "MDHistoWorkspace as output. This is DND " "only in Horace terminology."); + + std::vector<std::string> propOptions; + propOptions.push_back(AutoMethod); + propOptions.push_back(RLUMethod); + propOptions.push_back(InvAngstromMethod); + char buffer[1024]; + std::sprintf(buffer, "How will the Q units of the input workspace be interpreted? This property will disappear in future versions of Mantid\n" + "%s : Figure it out based on the label units\n" + "%s : Force them to be rlu\n" + "%s : Force them to be inverse angstroms", AutoMethod.c_str(), RLUMethod.c_str(), InvAngstromMethod.c_str()); + + std::string help(buffer); + boost::algorithm::trim(help); + declareProperty( + "InterpretQDimensionUnits", AutoMethod, + boost::make_shared<StringListValidator>(propOptions), help + ); } void CutMD::exec() { @@ -293,8 +350,18 @@ void CutMD::exec() { std::vector<std::string> targetUnits(3); for (size_t i = 0; i < 3; ++i) - targetUnits[i] = projection.getUnit(i) == RLU ? "r" : "a"; - std::vector<std::string> originUnits(3, "r"); // TODO. This is a hack! + targetUnits[i] = + projection.getUnit(i) == RLU ? RLUSymbol : InvAngstromSymbol; + + const std::string determineUnitsMethod = this->getProperty("InterpretQDimensionUnits"); + std::vector<std::string> originUnits; + if ( determineUnitsMethod == AutoMethod ) { + originUnits = findOriginalQUnits(inWS.get(), g_log); + } else if (determineUnitsMethod == RLUMethod ) { + originUnits = std::vector<std::string>(3, RLUSymbol); + } else{ + originUnits = std::vector<std::string>(3, InvAngstromSymbol); + } DblMatrix scaledProjectionMatrix = scaleProjection(projectionMatrix, originUnits, targetUnits, eventInWS); @@ -333,7 +400,7 @@ void CutMD::exec() { } // Make labels - std::vector<std::string> labels = labelProjection(projectionMatrix); + std::vector<std::string> labels = labelProjection(scaledProjectionMatrix); // Either run RebinMD or SliceMD const std::string cutAlgName = noPix ? "BinMD" : "SliceMD"; @@ -356,7 +423,8 @@ void CutMD::exec() { std::vector<std::string> vec(numDims, "0"); for (size_t j = 0; j < 3; ++j) - vec[j] = boost::lexical_cast<std::string>(projectionMatrix[i][j]); + vec[j] = + boost::lexical_cast<std::string>(scaledProjectionMatrix[i][j]); vecStr = boost::algorithm::join(vec, ", "); } else { // Always orthogonal diff --git a/Code/Mantid/Framework/PythonInterface/mantid/kernel/src/Exports/DateAndTime.cpp b/Code/Mantid/Framework/PythonInterface/mantid/kernel/src/Exports/DateAndTime.cpp index 8c567746bd667815407025cbd35a2ef49cd8f30d..fa72f44044485cf5d8d8791376f36b9d3fff30ac 100644 --- a/Code/Mantid/Framework/PythonInterface/mantid/kernel/src/Exports/DateAndTime.cpp +++ b/Code/Mantid/Framework/PythonInterface/mantid/kernel/src/Exports/DateAndTime.cpp @@ -29,6 +29,7 @@ void export_DateAndTime() .def("totalNanoseconds", &DateAndTime::totalNanoseconds) .def("setToMinimum", &DateAndTime::setToMinimum) .def("__str__", &ISO8601StringPlusSpace) + .def("__long__", &DateAndTime::totalNanoseconds) .def(self == self) .def(self != self) // cppcheck-suppress duplicateExpression diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/LiquidsReflectometryReduction.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/LiquidsReflectometryReduction.py index 697a69ed09ffe16c58caad6de1017210a5715ff4..e061b670fc0f2eb1a55ebca17e50bd03ee11f5d2 100644 --- a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/LiquidsReflectometryReduction.py +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/LiquidsReflectometryReduction.py @@ -313,6 +313,8 @@ class LiquidsReflectometryReduction(PythonAlgorithm): tthd_value *= math.pi / 180.0 theta = math.fabs(tthd_value - thi_value) / 2. + if theta < 0.001: + logger.warning("thi and tthd are equal: is this a direct beam?") # Add the offset angle_offset_deg = self.getProperty("AngleOffset").value diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/PoldiCreatePeaksFromFile.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/PoldiCreatePeaksFromFile.py index 947822554abdb1ed690646132a16c4b56e88ab9c..41535766969853ba2fb47f9afb869188577e4529 100644 --- a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/PoldiCreatePeaksFromFile.py +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/PoldiCreatePeaksFromFile.py @@ -136,7 +136,7 @@ class PoldiCrystalFileParser(object): class PoldiCreatePeaksFromFile(PythonAlgorithm): def category(self): - return "SINQ\\POLDI" + return "SINQ\\Poldi" def name(self): return "PoldiLoadCrystalData" diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/TransformToIqt.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/TransformToIqt.py index 125a598495d33f64476f15a5df451bb92348000b..585b1e1baf00e2265aceaff2a271071eae0fe3e6 100644 --- a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/TransformToIqt.py +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/TransformToIqt.py @@ -221,8 +221,16 @@ class TransformToIqt(PythonAlgorithm): """ from IndirectCommon import CheckHistZero, CheckHistSame, CheckAnalysers + try: + CheckAnalysers(self._sample, self._resolution) + except ValueError: + # A genuine error the shows that the two runs are incompatible + raise + except: + # Checking could not be performed due to incomplete or no instrument + logger.warning('Could not check for matching analyser and reflection') + # Process resolution data - CheckAnalysers(self._sample, self._resolution) num_res_hist = CheckHistZero(self._resolution)[0] if num_res_hist > 1: CheckHistSame(self._sample, 'Sample', self._resolution, 'Resolution') diff --git a/Code/Mantid/MantidPlot/src/Graph.cpp b/Code/Mantid/MantidPlot/src/Graph.cpp index 47e6b3235c014d05e45d505a7e361589e0ac4c2f..bc2f85db81f72a10036fb8b91c2c24966108e8ad 100644 --- a/Code/Mantid/MantidPlot/src/Graph.cpp +++ b/Code/Mantid/MantidPlot/src/Graph.cpp @@ -61,6 +61,7 @@ #include "MantidAPI/AnalysisDataService.h" #include "Mantid/MantidMatrixCurve.h" #include "MantidQtAPI/PlotAxis.h" +#include "MantidQtAPI/QwtRasterDataMD.h" #include "MantidQtAPI/QwtWorkspaceSpectrumData.h" #include "Mantid/ErrorBarSettings.h" @@ -3074,6 +3075,35 @@ void Graph::updateScale() updateSecondaryAxis(QwtPlot::yRight); } + auto mantidCurve = dynamic_cast<MantidCurve*>(curve(0)); + auto dataCurve = dynamic_cast<DataCurve*>(curve(0)); + + if (mantidCurve) + { + setXAxisTitle(mantidCurve->mantidData()->getXAxisLabel()); + setYAxisTitle(mantidCurve->mantidData()->getYAxisLabel()); + } + else if (dataCurve && dataCurve->table()) + { + setXAxisTitle(dataCurve->table()->colLabel(0)); + setYAxisTitle(dataCurve->table()->colLabel(1).section(".",0,0)); + } + + Spectrogram* spec = spectrogram(); + if (spec) + { + auto specData = dynamic_cast<const MantidQt::API::QwtRasterDataMD*>(&spec->data()); + if (specData) + { + Mantid::API::IMDWorkspace_const_sptr ws = specData->getWorkspace(); + if(ws) + { + setXAxisTitle(MantidQt::API::PlotAxis(*ws, 0).title()); + setYAxisTitle(MantidQt::API::PlotAxis(*ws, 1).title()); + } + } + } + d_plot->replot();//TODO: avoid 2nd replot! d_zoomer[0]->setZoomBase(false); } diff --git a/Code/Mantid/MantidPlot/src/Mantid/MantidUI.cpp b/Code/Mantid/MantidPlot/src/Mantid/MantidUI.cpp index 80a11e9aa86075b78062b80f29a104fe54340780..5dd11e651811c3c146509769838d870c18003433 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/MantidUI.cpp +++ b/Code/Mantid/MantidPlot/src/Mantid/MantidUI.cpp @@ -617,11 +617,7 @@ MultiLayer* MantidUI::plotMDList(const QStringList& wsNames, const int plotAxis, // Using information from the first graph if( i == 0 && isGraphNew ) - { - g->setXAxisTitle(data->getXAxisLabel()); - g->setYAxisTitle(data->getYAxisLabel()); g->setAutoScale(); - } } } @@ -2770,8 +2766,6 @@ void MantidUI::importNumSeriesLog(const QString &wsName, const QString &logName, g->setCurvePen(iFilterCurve, pn); } } - g->setXAxisTitle(t->colLabel(0)); - g->setYAxisTitle(t->colLabel(1).section(".",0,0)); g->setTitle(label); g->setAutoScale(); @@ -2994,8 +2988,6 @@ void MantidUI::setUpBinGraph(MultiLayer* ml, const QString& Name, Mantid::API::M { xtitle = MantidQt::API::PlotAxis(*workspace, 1).title(); } - g->setXAxisTitle(xtitle); - g->setYAxisTitle(MantidQt::API::PlotAxis(false, *workspace).title()); } /** @@ -3221,8 +3213,6 @@ MultiLayer* MantidUI::plot1D(const QMultiMap<QString,int>& toPlot, bool spectrum return NULL; } - g->setXAxisTitle(firstCurve->mantidData()->getXAxisLabel()); - g->setYAxisTitle(firstCurve->mantidData()->getYAxisLabel()); g->setAutoScale(); /* The 'setAutoScale' above is needed to make sure that the plot initially encompasses all the * data points. However, this has the side-effect suggested by its name: all the axes become @@ -3404,9 +3394,6 @@ MultiLayer* MantidUI::drawSingleColorFillPlot(const QString & wsName, Graph::Cur appWindow()->setPreferences(plot); plot->setTitle(wsName); - using MantidQt::API::PlotAxis; - plot->setXAxisTitle(PlotAxis(*workspace, 0).title()); - plot->setYAxisTitle(PlotAxis(*workspace, 1).title()); Spectrogram *spgrm = new Spectrogram(wsName, workspace); plot->plotSpectrogram(spgrm, curveType); diff --git a/Code/Mantid/MantidQt/CustomDialogs/inc/MantidQtCustomDialogs/StartLiveDataDialog.h b/Code/Mantid/MantidQt/CustomDialogs/inc/MantidQtCustomDialogs/StartLiveDataDialog.h index 775d68f7d94fa27cb54e2c552771d2cb43d1217b..c21dd2e28ec172a19eb05e061919e1b95fa72dd6 100644 --- a/Code/Mantid/MantidQt/CustomDialogs/inc/MantidQtCustomDialogs/StartLiveDataDialog.h +++ b/Code/Mantid/MantidQt/CustomDialogs/inc/MantidQtCustomDialogs/StartLiveDataDialog.h @@ -33,6 +33,7 @@ public slots: private slots: void setDefaultAccumulationMethod(const QString&); + void updateUiElements(const QString&); void accept(); void initListenerPropLayout(const QString&); diff --git a/Code/Mantid/MantidQt/CustomDialogs/src/StartLiveDataDialog.cpp b/Code/Mantid/MantidQt/CustomDialogs/src/StartLiveDataDialog.cpp index 0450d6bd8eaf6fa7e9de4db4d790a0a66bc3f5d9..f041fd567b289cc83b2323f658f6c54aceb10982 100644 --- a/Code/Mantid/MantidQt/CustomDialogs/src/StartLiveDataDialog.cpp +++ b/Code/Mantid/MantidQt/CustomDialogs/src/StartLiveDataDialog.cpp @@ -170,6 +170,7 @@ void StartLiveDataDialog::initLayout() radioPostProcessClicked(); setDefaultAccumulationMethod( ui.cmbInstrument->currentText() ); + updateUiElements( ui.cmbInstrument->currentText()); //=========== Listener's properties ============= @@ -195,6 +196,7 @@ void StartLiveDataDialog::initLayout() connect(ui.cmbInstrument,SIGNAL(currentIndexChanged(const QString&)),this,SLOT(setDefaultAccumulationMethod(const QString&))); connect(ui.cmbInstrument,SIGNAL(currentIndexChanged(const QString&)),this,SLOT(initListenerPropLayout(const QString&))); + connect(ui.cmbInstrument,SIGNAL(currentIndexChanged(const QString&)),this,SLOT(updateUiElements(const QString&))); QHBoxLayout * buttonLayout = this->createDefaultButtonLayout(); ui.mainLayout->addLayout(buttonLayout); @@ -336,6 +338,37 @@ void StartLiveDataDialog::setDefaultAccumulationMethod(const QString& inst) } } +//------------------------------------------------------------------------------ +/** Another slot called when picking a different instrument. + * Disables UI elements that are not used by the instrument + * Currently, only TOPAZ listener uses this (and only for the + * "Starting Time" group. + * @param inst :: The instrument name. + */ +void StartLiveDataDialog::updateUiElements(const QString& inst) +{ + if ( inst.isEmpty() ) return; + try + { + if (inst == "TOPAZ") + { + ui.groupBox->setEnabled( false); + ui.radNow->setChecked( true); + } + else + { + ui.groupBox->setEnabled( true); + } + } + // If an exception is thrown, just swallow it and do nothing + // getInstrument can throw, particularly while we allow listener names to be passed in directly + catch( Mantid::Kernel::Exception::NotFoundError& ) + { + } +} + + + void StartLiveDataDialog::accept() { // Now manually set the StartTime property as there's a computation needed diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ConvFit.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ConvFit.cpp index 12332b355a81bae7ab824009ecda92d942f20615..7c9d0bad230e166bcc6a00253e4b57eed872a259 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ConvFit.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ConvFit.cpp @@ -591,7 +591,14 @@ namespace IDA { Mantid::Geometry::Instrument_const_sptr inst = AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(workspaceName)->getInstrument(); - std::string analyser = inst->getStringParameter("analyser")[0]; + std::vector<std::string> analysers = inst->getStringParameter("analyser"); + if(analysers.empty()) + { + g_log.warning("Could not load instrument resolution from parameter file"); + return 0.0; + } + + std::string analyser = analysers[0]; std::string idfDirectory = Mantid::Kernel::ConfigService::Instance().getString("instrumentDefinition.directory"); // If the analyser component is not already in the data file the laod it from the parameter file @@ -607,7 +614,7 @@ namespace IDA if(!loadParamFile->isExecuted()) { - g_log.error("Could not load parameter file, ensure instrument directory is in data search paths."); + g_log.warning("Could not load parameter file, ensure instrument directory is in data search paths."); return 0.0; } @@ -620,7 +627,7 @@ namespace IDA { UNUSED_ARG(e); - g_log.error("Could not load instrument resolution from parameter file"); + g_log.warning("Could not load instrument resolution from parameter file"); resolution = 0.0; } diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ISISEnergyTransfer.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ISISEnergyTransfer.cpp index a041765eac19c72fecf784542226b4c92b4a8824..fe70ea12006d77d231656f38e173223dc191d9dc 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ISISEnergyTransfer.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ISISEnergyTransfer.cpp @@ -499,7 +499,7 @@ namespace CustomInterfaces */ void ISISEnergyTransfer::pbRunEditing() { - emit updateRunButton(false, "Editing...", "Run numbers are curently being edited."); + emit updateRunButton(false, "Editing...", "Run numbers are currently being edited."); } /** @@ -507,7 +507,7 @@ namespace CustomInterfaces */ void ISISEnergyTransfer::pbRunFinding() { - emit updateRunButton(false, "Finding files...", "Searchig for data files for the run numbers entered..."); + emit updateRunButton(false, "Finding files...", "Searching for data files for the run numbers entered..."); m_uiForm.dsRunFiles->setEnabled(false); } @@ -518,7 +518,7 @@ namespace CustomInterfaces { if(!m_uiForm.dsRunFiles->isValid()) { - emit updateRunButton(false, "Invalid Run(s)", "Cannot find data files for some of the run numbers enetered."); + emit updateRunButton(false, "Invalid Run(s)", "Cannot find data files for some of the run numbers entered."); } else { diff --git a/Code/Mantid/MantidQt/MantidWidgets/src/DiagResults.cpp b/Code/Mantid/MantidQt/MantidWidgets/src/DiagResults.cpp index 2ed3b22179de14975a536e87ea3074b334c56d79..59a07507134df59424475602ddc2071de3f0e959 100644 --- a/Code/Mantid/MantidQt/MantidWidgets/src/DiagResults.cpp +++ b/Code/Mantid/MantidQt/MantidWidgets/src/DiagResults.cpp @@ -24,8 +24,19 @@ namespace /// the total number of tests that results are reported for here const int NUMTESTS = 5; /// the list of tests that we display results for - const QString TESTS[NUMTESTS] = + const QString TESTS[5] = { "Hard mask", "First detector vanadium test", "Second detector vanadium test", "Background test", "PSD Bleed test"}; + + int find_test(const std::string &test_name){ + int found = -1; + for(int i=0;i<5;i++){ + if (TESTS[i].toStdString()==test_name){ + found = i+1; + return found; + } + } + return found; + } } //---------------------- @@ -68,19 +79,28 @@ void DiagResults::updateResults(const QString & testSummary) } QStringList results = testSummary.split("\n"); + int nTestStrings = results.length(); + int end_count(0); // First result line is the header - for(int i = 1; i <= NUMTESTS; ++i) + for(int i = 0; i <= nTestStrings; ++i) { QString testName = results[i].section(":", 0, 1); std::string tn = testName.toStdString(); - if (tn[0] == '=')continue; - QString fieldValues = results[i].section(":", 1); - QStringList columns = fieldValues.split(QRegExp("\\s+"), QString::SkipEmptyParts); + if (tn[0] == '='){ + end_count++; + if (end_count>1)break; + else continue; + } + QStringList NameValPair = results[i].split(":"); + tn = NameValPair[0].toStdString(); + QStringList columns = NameValPair[1].split(QRegExp("\\s+"), QString::SkipEmptyParts); Q_ASSERT(columns.size() == 2); QString status; if( columns[0] == "None" ) status = "N/A"; else status = columns[1]; - updateRow(i+1, status); + int test_ind = find_test(tn); + if (test_ind<0)continue; + updateRow(test_ind+1, status); } } diff --git a/Code/Mantid/MantidQt/SliceViewer/inc/MantidQtSliceViewer/SliceViewer.h b/Code/Mantid/MantidQt/SliceViewer/inc/MantidQtSliceViewer/SliceViewer.h index 44d1de62fe2318853fca5e5bede13cbcd861fdff..471dde7ae2795feaa0dbad22236f347a731a9bae 100644 --- a/Code/Mantid/MantidQt/SliceViewer/inc/MantidQtSliceViewer/SliceViewer.h +++ b/Code/Mantid/MantidQt/SliceViewer/inc/MantidQtSliceViewer/SliceViewer.h @@ -178,6 +178,7 @@ public slots: void changeNormalizationNone(); void changeNormalizationVolume(); void changeNormalizationNumEvents(); + void onNormalizationChanged(const QString& normalizationKey); // Buttons or actions void clearLine(); @@ -354,6 +355,10 @@ private: /// Object for choosing a PeakTransformFactory based on the workspace type. Mantid::API::PeakTransformSelector m_peakTransformSelector; + static const QString NoNormalizationKey; + static const QString VolumeNormalizationKey; + static const QString NumEventsNormalizationKey; + }; } // namespace SliceViewer diff --git a/Code/Mantid/MantidQt/SliceViewer/inc/MantidQtSliceViewer/SliceViewer.ui b/Code/Mantid/MantidQt/SliceViewer/inc/MantidQtSliceViewer/SliceViewer.ui index 2101640e5e8e15a10b8557f0c3b5bab025e1a102..9414476bc32f0f92da829ed186475e2fdcb485cb 100644 --- a/Code/Mantid/MantidQt/SliceViewer/inc/MantidQtSliceViewer/SliceViewer.ui +++ b/Code/Mantid/MantidQt/SliceViewer/inc/MantidQtSliceViewer/SliceViewer.ui @@ -585,10 +585,13 @@ </widget> </item> <item> - <spacer name="horizontalSpacer"> + <spacer name="horizontalSpacer_6"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> + <property name="sizeType"> + <enum>QSizePolicy::Expanding</enum> + </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> @@ -605,6 +608,9 @@ <property name="fieldGrowthPolicy"> <enum>QFormLayout::FieldsStayAtSizeHint</enum> </property> + <property name="verticalSpacing"> + <number>0</number> + </property> <item row="0" column="0"> <widget class="QLabel" name="label"> <property name="text"> @@ -630,6 +636,7 @@ <string notr="true">QLabel { background-color : rgb(255, 255, 186); color : black; + font: 10px; }</string> </property> <property name="frameShape"> @@ -668,6 +675,7 @@ <string notr="true">QLabel { background-color : rgb(255, 255, 186); color : black; + font: 10px; }</string> </property> <property name="frameShape"> @@ -683,8 +691,11 @@ </item> <item row="2" column="0"> <widget class="QLabel" name="label_3"> + <property name="toolTip"> + <string/> + </property> <property name="text"> - <string>z=</string> + <string>Intensity=</string> </property> </widget> </item> @@ -706,6 +717,7 @@ <string notr="true">QLabel { background-color : rgb(255, 255, 186); color : black; + font: 10px; }</string> </property> <property name="frameShape"> @@ -719,6 +731,43 @@ </property> </widget> </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Norm=</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QComboBox" name="comboNormalization"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>70</width> + <height>0</height> + </size> + </property> + <property name="font"> + <font> + <pointsize>-1</pointsize> + <weight>50</weight> + <italic>false</italic> + <bold>false</bold> + </font> + </property> + <property name="toolTip"> + <string>Normalization option</string> + </property> + <property name="styleSheet"> + <string notr="true">font: 10px;</string> + </property> + </widget> + </item> </layout> </item> </layout> diff --git a/Code/Mantid/MantidQt/SliceViewer/src/SliceViewer.cpp b/Code/Mantid/MantidQt/SliceViewer/src/SliceViewer.cpp index 21ac4ff3106b0a6552597b1afe33f7fb352b9eed..a84838533c99acd118999bcdb73e7d592295d5b3 100644 --- a/Code/Mantid/MantidQt/SliceViewer/src/SliceViewer.cpp +++ b/Code/Mantid/MantidQt/SliceViewer/src/SliceViewer.cpp @@ -68,6 +68,10 @@ using MantidQt::API::AlgorithmRunner; namespace MantidQt { namespace SliceViewer { +const QString SliceViewer::NoNormalizationKey = "No"; +const QString SliceViewer::VolumeNormalizationKey = "Volume"; +const QString SliceViewer::NumEventsNormalizationKey = "# Events"; + //------------------------------------------------------------------------------ /** Constructor */ SliceViewer::SliceViewer(QWidget *parent) @@ -135,6 +139,7 @@ SliceViewer::SliceViewer(QWidget *parent) SLOT(autoRebin_toggled(bool))); QObject::connect(ui.btnPeakOverlay, SIGNAL(clicked()), this, SLOT(peakOverlay_clicked())); + // ----------- Other signals ---------------- QObject::connect(m_colorBar, SIGNAL(colorBarDoubleClicked()), this, SLOT(loadColorMapSlot())); @@ -222,7 +227,7 @@ void SliceViewer::saveSettings() { settings.setValue("TransparentZeros", (m_actionTransparentZeros->isChecked() ? 1 : 0)); settings.setValue("Normalization", - static_cast<int>(this->getNormalization())); + static_cast<int>(this->getNormalization())); settings.endGroup(); } @@ -336,14 +341,15 @@ void SliceViewer::initMenus() { QActionGroup *group = new QActionGroup(this); - action = new QAction(QPixmap(), "No Normalization", this); + const QString normalization = " Normalization"; + action = new QAction(QPixmap(), SliceViewer::NoNormalizationKey + normalization, this); m_menuView->addAction(action); action->setActionGroup(group); action->setCheckable(true); connect(action, SIGNAL(triggered()), this, SLOT(changeNormalizationNone())); m_actionNormalizeNone = action; - action = new QAction(QPixmap(), "Volume Normalization", this); + action = new QAction(QPixmap(), SliceViewer::VolumeNormalizationKey + normalization, this); m_menuView->addAction(action); action->setActionGroup(group); action->setCheckable(true); @@ -351,7 +357,7 @@ void SliceViewer::initMenus() { connect(action, SIGNAL(triggered()), this, SLOT(changeNormalizationVolume())); m_actionNormalizeVolume = action; - action = new QAction(QPixmap(), "Num. Events Normalization", this); + action = new QAction(QPixmap(), SliceViewer::NumEventsNormalizationKey + normalization, this); m_menuView->addAction(action); action->setActionGroup(group); action->setCheckable(true); @@ -359,6 +365,12 @@ void SliceViewer::initMenus() { SLOT(changeNormalizationNumEvents())); m_actionNormalizeNumEvents = action; + ui.comboNormalization->addItem(SliceViewer::NoNormalizationKey); + ui.comboNormalization->addItem(SliceViewer::VolumeNormalizationKey); + ui.comboNormalization->addItem(SliceViewer::NumEventsNormalizationKey); + + connect(this->ui.comboNormalization, SIGNAL(currentIndexChanged(const QString&)), SLOT(onNormalizationChanged(const QString&))); + // --------------- Color options Menu ---------------------------------------- m_menuColorOptions = new QMenu("&ColorMap", this); @@ -789,6 +801,20 @@ void SliceViewer::changeNormalizationNumEvents() { this->setNormalization(Mantid::API::NumEventsNormalization, true); } +/** + * @brief Slot to handle change in normalization kicked-off from the combo box. + * @param normalizationKey : Text key for type of normalization switched to. + */ +void SliceViewer::onNormalizationChanged(const QString& normalizationKey) { + if (normalizationKey == SliceViewer::NoNormalizationKey) { + changeNormalizationNone(); + } else if (normalizationKey == SliceViewer::VolumeNormalizationKey) { + changeNormalizationVolume(); + } else { + changeNormalizationNumEvents(); + } +} + //------------------------------------------------------------------------------ /** Set the normalization mode for viewing the data * @@ -811,6 +837,17 @@ void SliceViewer::setNormalization(Mantid::API::MDNormalization norm, m_actionNormalizeVolume->blockSignals(false); m_actionNormalizeNumEvents->blockSignals(false); + // Sync the normalization combobox. + this->ui.comboNormalization->setEnabled(false); // Avoid firing signals + if(norm == Mantid::API::NoNormalization) { + this->ui.comboNormalization->setCurrentIndex(0); + } else if (norm == Mantid::API::VolumeNormalization) { + this->ui.comboNormalization->setCurrentItem(1); + } else { + this->ui.comboNormalization->setCurrentIndex(2); + } + this->ui.comboNormalization->setEnabled(true); + m_data->setNormalization(norm); if (update) this->updateDisplay(); diff --git a/Code/Mantid/Testing/SystemTests/tests/analysis/ISIS_LETReduction.py b/Code/Mantid/Testing/SystemTests/tests/analysis/ISIS_LETReduction.py index a62dd1c28ee351050279198cf4b87e7c3bd937a3..e43851af224a57248ca19a95d08cc06db07303d7 100644 --- a/Code/Mantid/Testing/SystemTests/tests/analysis/ISIS_LETReduction.py +++ b/Code/Mantid/Testing/SystemTests/tests/analysis/ISIS_LETReduction.py @@ -168,7 +168,7 @@ class ReduceLET_MultiRep2015(ReductionWrapper): prop['bleed'] = False prop['norm_method']='current' prop['detector_van_range']=[2,7] - prop['background_range'] = [92000,98000] # TOF range for the calculating flat background + prop['background_range'] = [90000,95000] # TOF range for the calculating flat background prop['hardmaskOnly']='LET_hard.msk' # diag does not work well on LET. At present only use a hard mask RIB has created prop['check_background']=True @@ -244,12 +244,12 @@ class ReduceLET_MultiRep2015(ReductionWrapper): #---------------------------------------------------------------------------------------------------------------------- if __name__=="__main__": - maps_dir = r'd:\Data\MantidDevArea\Datastore\DataCopies\Testing\Data\SystemTest' - data_dir = r'd:\Data\Mantid_Testing\15_03_01' - ref_data_dir = r'd:\Data\MantidDevArea\Datastore\DataCopies\Testing\SystemTests\tests\analysis\reference' - config.setDataSearchDirs('{0};{1};{2}'.format(data_dir,maps_dir,ref_data_dir)) + #maps_dir = r'd:\Data\MantidDevArea\Datastore\DataCopies\Testing\Data\SystemTest' + #data_dir = r'd:\Data\Mantid_Testing\15_03_01' + #ref_data_dir = r'd:\Data\MantidDevArea\Datastore\DataCopies\Testing\SystemTests\tests\analysis\reference' + #config.setDataSearchDirs('{0};{1};{2}'.format(data_dir,maps_dir,ref_data_dir)) #config.appendDataSearchDir('d:/Data/Mantid_GIT/Test/AutoTestData') - config['defaultsave.directory'] = data_dir # folder to save resulting spe/nxspe files. Defaults are in + #config['defaultsave.directory'] = data_dir # folder to save resulting spe/nxspe files. Defaults are in # execute stuff from Mantid rd =ReduceLET_MultiRep2015() diff --git a/Code/Mantid/Testing/SystemTests/tests/analysis/ISIS_MERLINReduction.py b/Code/Mantid/Testing/SystemTests/tests/analysis/ISIS_MERLINReduction.py index 447b453e8b8d113501ec102099b69e9008ded767..4b63de346581345e122a8a1642f6b36e12cf513a 100644 --- a/Code/Mantid/Testing/SystemTests/tests/analysis/ISIS_MERLINReduction.py +++ b/Code/Mantid/Testing/SystemTests/tests/analysis/ISIS_MERLINReduction.py @@ -1,5 +1,6 @@ #pylint: disable=invalid-name """ Sample MERLIN reduction scrip """ +import os #os.environ["PATH"] = r"c:/Mantid/Code/builds/br_master/bin/Release;"+os.environ["PATH"] from Direct.ReductionWrapper import * @@ -52,7 +53,7 @@ class ReduceMERLIN(ReductionWrapper): #SaveNexus(ws,Filename = 'MARNewReduction.nxs') return outWS - def __init__(self): + def __init__(self,web_var=None): """ sets properties defaults for the instrument with Name""" ReductionWrapper.__init__(self,'MER',web_var) #---------------------------------------------------------------------------------------------------------------------- @@ -60,12 +61,12 @@ class ReduceMERLIN(ReductionWrapper): if __name__=="__main__": - maps_dir = 'd:/Data/MantidSystemTests/Data' - data_dir ='d:/Data/Mantid_Testing/14_11_27' - ref_data_dir = 'd:/Data/MantidSystemTests/SystemTests/AnalysisTests/ReferenceResults' - config.setDataSearchDirs('{0};{1};{2}'.format(data_dir,maps_dir,ref_data_dir)) + #maps_dir = 'd:/Data/MantidSystemTests/Data' + #data_dir ='d:/Data/Mantid_Testing/14_11_27' + #ref_data_dir = 'd:/Data/MantidSystemTests/SystemTests/AnalysisTests/ReferenceResults' + #config.setDataSearchDirs('{0};{1};{2}'.format(data_dir,maps_dir,ref_data_dir)) #config.appendDataSearchDir('d:/Data/Mantid_GIT/Test/AutoTestData') - config['defaultsave.directory'] = data_dir # folder to save resulting spe/nxspe files. Defaults are in + #config['defaultsave.directory'] = data_dir # folder to save resulting spe/nxspe files. Defaults are in # execute stuff from Mantid rd = ReduceMERLIN() diff --git a/Code/Mantid/Testing/SystemTests/tests/analysis/reference/MERLINReduction.nxs.md5 b/Code/Mantid/Testing/SystemTests/tests/analysis/reference/MERLINReduction.nxs.md5 index e3db5b70ad82c7eeb1c17ab951a8d17ec8297dd7..6e164f1d57e6c86967a93e8e475eedfdded59e55 100644 --- a/Code/Mantid/Testing/SystemTests/tests/analysis/reference/MERLINReduction.nxs.md5 +++ b/Code/Mantid/Testing/SystemTests/tests/analysis/reference/MERLINReduction.nxs.md5 @@ -1 +1 @@ -6ba46ef250726347c9d02243101cef41 \ No newline at end of file +38a0cb08ff52ea6e6bc01b91d614abe0 diff --git a/Code/Mantid/Vates/VatesSimpleGui/ViewWidgets/inc/MantidVatesSimpleGuiViewWidgets/StandardView.h b/Code/Mantid/Vates/VatesSimpleGui/ViewWidgets/inc/MantidVatesSimpleGuiViewWidgets/StandardView.h index 4107e3e0a0419e32b923fa839ce571f873d927fc..39cbc4be2bb2198635646fd851853899705cbdf2 100644 --- a/Code/Mantid/Vates/VatesSimpleGui/ViewWidgets/inc/MantidVatesSimpleGuiViewWidgets/StandardView.h +++ b/Code/Mantid/Vates/VatesSimpleGui/ViewWidgets/inc/MantidVatesSimpleGuiViewWidgets/StandardView.h @@ -5,6 +5,7 @@ #include "MantidVatesSimpleGuiViewWidgets/ViewBase.h" #include "MantidVatesSimpleGuiViewWidgets/WidgetDllOption.h" +#include <QMap> #include <QPointer> #include <QWidget> @@ -19,7 +20,8 @@ namespace Vates namespace SimpleGui { - class RebinnedSourcesManager; +class RebinnedSourcesManager; + /** * This class represents the initial view for the main program. It is meant to @@ -93,6 +95,8 @@ protected slots: private: Q_DISABLE_COPY(StandardView) + QString getAlgNameFromMenuLabel(const QString &menuLbl); + bool m_cameraReset; QPointer<pqPipelineSource> m_scaler; ///< Holder for the ScaleWorkspace Ui::StandardView m_ui; ///< The standard view's UI form @@ -111,6 +115,21 @@ private: QAction* m_sliceMDAction; QAction* m_cutMDAction; QAction* m_unbinAction; + + // name to show for the rebin actions on the rebin menu + static QString g_binMDName; + static QString g_sliceMDName; + static QString g_cutMDName; + /// name + a bit of description + static QString g_binMDLbl; + static QString g_sliceMDLbl; + static QString g_cutMDLbl; + /// tool tip text for the rebin algorithm + static QString g_binMDToolTipTxt; + static QString g_sliceMDToolTipTxt; + static QString g_cutMDToolTipTxt; + + static QMap<QString, QString> g_actionToAlgName; }; } // SimpleGui diff --git a/Code/Mantid/Vates/VatesSimpleGui/ViewWidgets/src/StandardView.cpp b/Code/Mantid/Vates/VatesSimpleGui/ViewWidgets/src/StandardView.cpp index f211899fd4b1c494e1da7698be7d95655d17cc7b..56107ed6f41a7e69ec685e3c370d88a5a451949d 100644 --- a/Code/Mantid/Vates/VatesSimpleGui/ViewWidgets/src/StandardView.cpp +++ b/Code/Mantid/Vates/VatesSimpleGui/ViewWidgets/src/StandardView.cpp @@ -24,11 +24,13 @@ #pragma warning enable 1170 #endif -#include <QHBoxLayout> #include <QAction> +#include <QHBoxLayout> +#include <QHelpEvent> #include <QMenu> #include <QMessageBox> #include <QString> +#include <QToolTip> namespace Mantid { @@ -37,6 +39,51 @@ namespace Vates namespace SimpleGui { +/** + * Simple class for a QMenu where the actions do show their tool tip + * strings (this does not happen by default with standard QMenu). + */ +class QMenuWithToolTip: public QMenu { +public: + QMenuWithToolTip(QWidget *parent): QMenu(parent) + { + } + + bool event(QEvent* e) { + if (QEvent::ToolTip == e->type()) { + // grab the action specific tooltip + QHelpEvent *he = dynamic_cast<QHelpEvent*>(e); + if (!he) + return false; + QAction* a = actionAt(he->pos()); + if (a && a->toolTip() != a->text()) { + QToolTip::showText(he->globalPos(), a->toolTip(), this); + return true; + } + } + return QMenu::event(e); + } +}; + +QString StandardView::g_binMDName = "BinMD"; +QString StandardView::g_sliceMDName = "SliceMD"; +QString StandardView::g_cutMDName = "CutMD"; +// important: these label strings must use the name of the corresponding +// Mantid algorithm as first token (before first space), as it will +// be used as a parameter when emitting the signal rebin +QString StandardView::g_binMDLbl = "Fast (" + g_binMDName + ")"; +QString StandardView::g_sliceMDLbl = "Complete (" + g_sliceMDName + ")"; +QString StandardView::g_cutMDLbl = "Horace style (" + g_cutMDName + ")"; + +const QString tipBefore = "Run the "; +const QString tipAfter = " Mantid algorithm (the algorithm dialog will show up)"; +QString StandardView::g_binMDToolTipTxt = tipBefore + g_binMDName + tipAfter; +QString StandardView::g_sliceMDToolTipTxt = tipBefore + g_sliceMDName + tipAfter; +QString StandardView::g_cutMDToolTipTxt = tipBefore + g_cutMDName + tipAfter; + +// To map action labels to algorithm names +QMap<QString, QString> StandardView::g_actionToAlgName; + /** * This function sets up the UI components, adds connections for the view's * buttons and creates the rendering view. @@ -52,6 +99,13 @@ namespace SimpleGui this->m_ui.setupUi(this); this->m_cameraReset = false; + // before setting the button-actions, register their algorithms + if (0 == g_actionToAlgName.size()) { + g_actionToAlgName.insert(g_binMDLbl, g_binMDName); + g_actionToAlgName.insert(g_sliceMDLbl, g_sliceMDName); + g_actionToAlgName.insert(g_cutMDLbl, g_cutMDName); + } + // Set up the buttons setupViewButtons(); @@ -83,15 +137,18 @@ void StandardView::setupViewButtons() { // Populate the rebin button - QMenu* rebinMenu = new QMenu(this->m_ui.rebinToolButton); + QMenuWithToolTip* rebinMenu = new QMenuWithToolTip(this->m_ui.rebinToolButton); - m_binMDAction = new QAction("BinMD", rebinMenu); + m_binMDAction = new QAction(g_binMDLbl, rebinMenu); + m_binMDAction->setToolTip(g_binMDToolTipTxt); m_binMDAction->setIconVisibleInMenu(false); - m_sliceMDAction = new QAction("SliceMD", rebinMenu); + m_sliceMDAction = new QAction(g_sliceMDLbl, rebinMenu); + m_sliceMDAction->setToolTip(g_sliceMDToolTipTxt); m_sliceMDAction->setIconVisibleInMenu(false); - m_cutMDAction = new QAction("CutMD", rebinMenu); + m_cutMDAction = new QAction(g_cutMDLbl, rebinMenu); + m_cutMDAction->setToolTip(g_cutMDToolTipTxt); m_cutMDAction->setIconVisibleInMenu(false); m_unbinAction = new QAction("Remove Rebinning", rebinMenu); @@ -281,7 +338,10 @@ void StandardView::setRebinAndUnbinButtons() void StandardView::onRebin() { if(QAction* action = dynamic_cast<QAction*>(sender())) { - emit rebin(action->text().toStdString()); } + // split always returns a list of at least one element + QString algName = getAlgNameFromMenuLabel(action->text()); + emit rebin(algName.toStdString()); + } } /** @@ -352,6 +412,28 @@ void StandardView::activeSourceChangeListener(pqPipelineSource* source) } } +/** + * Helper for the rebinning menu. For example it will give you the + * name of the algorithm CutMD ("CutMD" == g_cutMDName) if you pass + * its label in the rebinning menu (g_cutMDToolTipTxt). + * + * @param menuLbl label (what is shown in the action) + * @return Name of the corresponding Mantid algorithm + */ +QString StandardView::getAlgNameFromMenuLabel(const QString &menuLbl) +{ + QString res; + if (g_actionToAlgName.contains(menuLbl)) + { + res = g_actionToAlgName.value(menuLbl); + } + else { + // ideally an informative error would be given here but there doesn't seem to be + // a convnient way to do that in these view classes + } + return res; +} + } // SimpleGui } // Vates } // Mantid diff --git a/Code/Mantid/docs/source/algorithms/CreatePSDBleedMask-v1.rst b/Code/Mantid/docs/source/algorithms/CreatePSDBleedMask-v1.rst index 2631f3103b8945aa3cd9946aeb8b73cb52d8a208..63136938f0df1ea27c4bf0f413416d43b0ba1b82 100644 --- a/Code/Mantid/docs/source/algorithms/CreatePSDBleedMask-v1.rst +++ b/Code/Mantid/docs/source/algorithms/CreatePSDBleedMask-v1.rst @@ -9,17 +9,52 @@ Description ----------- -The diagnostic test attempts to find all tubes within the instrument -attached to the workspace. If successful, each tube is tested for -saturation above the level defined by the 'MaxTubeFramerate' property. -If any pixel, not including those marked to be ignored around the -equatorial region, are counting above this threshold then the entire -tube is masked. +The algorithm identifies tubes, which become saturated due to high +neutron flux from intense Bragg peaks affecting these tubes, +so these tubes are not counting neutrons but show constant high +counts over the tube in the elastic line(s) energy region(s). +After that, the algorithm generates masking workspace to +eliminate such tubes from the final results. -Restrictions on the input workspace -################################### +The bleeding effect occurs due to some old data acquisition electronics and +observed as homogeneous high counts reading, symmetric over the whole +tube length. The bleeding occurs at or close to incident energy or +specific energies in multirep mode, where bright Bragg reflections +hit the tube in positions, symmetric with respect to the tube centre. + +The example of the workspace, affected by the bleed current and requesting +bleeding corrections is presented on the following picture: + +.. image:: /images/BleedingSignal.png + + +First of all, the algorithm attempts to find all tubes +within the instrument attached to the workspace. If successful, +each tube is tested for saturation above the level defined by the +production of *MaxTubeFramerate* property by the *goodfrm* log value, +retrieved from the workspace. + +If the total signal, summed over all tubes pixels, excluding the number +of central pixels specified by the *NIgnoredCentralPixels* property, +exceeds the threshold, specified by the *MaxTubeFramerate* multiplied +by the *goodfrm* value, the tube becomes masked. + +The following image shows two instrument views obtained for two +converted to energy transfer workspaces, recorded for two incident +energies in multirep mode. Second energy range suffers from +bleeding signal as on the picture above, so proper +bleeding corrections are calculated. First energy range does not +show bleeding signal, so no bleeding corrections applied to it. + +.. image:: /images/BleedingCorrections.png + +Restrictions and requirements to the input workspace +#################################################### + +- The workspace must contain *goodfrm* log with value specifying the number of good frames recorded by the instrument. + +- The workspace should not be normalized as *goodfrm* is proportional to neutron flux over workspace so the *MaxTubeFramerate* have the meaning of the frame rate only for non-normalized workspaces. To obtain consistent results in a case when a workspace is normalized, user should divide *MaxTubeFramerate* by the normalization factor. -- The workspace must contain either raw counts or counts/us. Usage ----- diff --git a/Code/Mantid/docs/source/algorithms/EstimatePeakErrors-v1.rst b/Code/Mantid/docs/source/algorithms/EstimatePeakErrors-v1.rst index df06d774613de3f24f955af488933db8ef83553e..911eb9adcb33aa45b68b0dce86f815ec11417a45 100644 --- a/Code/Mantid/docs/source/algorithms/EstimatePeakErrors-v1.rst +++ b/Code/Mantid/docs/source/algorithms/EstimatePeakErrors-v1.rst @@ -13,7 +13,7 @@ This algorithm takes a function after it has been optimized by the Fit algorithm Usage ----- -(*At the moment the algorithm works properly if run from C++ only.*) +(*At the moment the algorithm works properly if run from C++ only.*):: import numpy as np @@ -34,5 +34,5 @@ Usage # Calculate peak parameter error estimates for the two Lorentzians. params = EstimatePeakErrors(fun) - + .. categories:: diff --git a/Code/Mantid/docs/source/algorithms/Integration-v1.rst b/Code/Mantid/docs/source/algorithms/Integration-v1.rst index 8c08efa8a3035af6e257f5f835350d295945d8d7..bfc0e907c7dcff5fa017a3e639e7780b806b0433 100644 --- a/Code/Mantid/docs/source/algorithms/Integration-v1.rst +++ b/Code/Mantid/docs/source/algorithms/Integration-v1.rst @@ -42,6 +42,18 @@ output will be a :ref:`MatrixWorkspace <MatrixWorkspace>`. :ref:`algm-Rebin` is recommended if you want to keep the workspace as an EventWorkspace. +**Integraton for event workspaces refers to internal binning, provided by +:ref:`algm-Rebin` or load algorithm and may ignore limits, provided as algorithm +input.** For example, attemtp to integrate loaded ISIS event workspace in the +range [18000,20000] yields workspace inegrated in the range [0,200000], +assuming the data were collected in the time range [0,20000]. This happens because +the event data would have single hisogram workspace bin in range [0,20000]. +To obtain integral in the desired range, user have to :ref:`algm-Rebin` first, +and one of the binning intervals have to start from 18000 and another (or the same) +end at 20000. + + + Usage ----- diff --git a/Code/Mantid/docs/source/algorithms/LRScalingFactors-v1.rst b/Code/Mantid/docs/source/algorithms/LRScalingFactors-v1.rst index d0089c67d3bedc2d7de3245a320de6ec61f0bcb7..5c5e42558ad61c3a8ab8ab19edae9ab299301461 100644 --- a/Code/Mantid/docs/source/algorithms/LRScalingFactors-v1.rst +++ b/Code/Mantid/docs/source/algorithms/LRScalingFactors-v1.rst @@ -25,17 +25,16 @@ no attenuators. The normalization run for a data set taken in a given slit setting configuration can then be expressed in terms of the standard 0-attenuator -data set with: - D_i = F_i D_0 - - Here's an example of runs and how they are related to F. - - run: 55889, att: 0, s1: 0.26, s2: 0.26 - run: 55890, att: 1, s1: 0.26, s2: 0.26 - run: 55891, att: 1, s1: 0.33, s2: 0.26 --> F = 55891 / 55890 - run: 55892, att: 1, s1: 0.45, s2: 0.26 --> F = 55892 / 55890 - run: 55895, att: 1, s1: 0.81, s2: 0.26 - run: 55896, att: 2, s1: 0.81, s2: 0.26 - run: 55897, att: 2, s1: 1.05, s2: 0.35 --> F = 55897 / 55896 * 55895 / 55890 +data set with: :math:`D_i = F_i D_0` + +Here's an example of runs and how they are related to F. + +- run: 55889, att: 0, s1: 0.26, s2: 0.26 +- run: 55890, att: 1, s1: 0.26, s2: 0.26 +- run: 55891, att: 1, s1: 0.33, s2: 0.26 --> F = 55891 / 55890 +- run: 55892, att: 1, s1: 0.45, s2: 0.26 --> F = 55892 / 55890 +- run: 55895, att: 1, s1: 0.81, s2: 0.26 +- run: 55896, att: 2, s1: 0.81, s2: 0.26 +- run: 55897, att: 2, s1: 1.05, s2: 0.35 --> F = 55897 / 55896 * 55895 / 55890 .. categories:: diff --git a/Code/Mantid/docs/source/algorithms/NormaliseByCurrent-v1.rst b/Code/Mantid/docs/source/algorithms/NormaliseByCurrent-v1.rst index 36166d1e969525558798af3afba1a6c5751b094d..39afe7aa6e7da9ad067908b140475a8b63b9a752 100644 --- a/Code/Mantid/docs/source/algorithms/NormaliseByCurrent-v1.rst +++ b/Code/Mantid/docs/source/algorithms/NormaliseByCurrent-v1.rst @@ -13,6 +13,9 @@ Normalises a workspace according to the good proton charge figure taken from the Input Workspace log data, which is stored in the workspace's `sample objects <../api/python/mantid/api/Sample.html>`__). Every data point (and its error) is divided by that number. +The good proton charge value is added to the normalized workspace +as the value of *NormalizationFactor* log. + ISIS Calculation Details ------------------------ @@ -56,10 +59,12 @@ Usage #Run the Algorithm wsN = NormaliseByCurrent(ws) + #norm_factor = wsN.getRun().getLogData('NormalizationFactor').value #Print results print "Before normalisation", ws.readY(0); print "After normalisation ", wsN.readY(0); + #print "Normalisation factor", norm_factor; Output: @@ -70,5 +75,4 @@ Output: Before normalisation [ 17. 12.] After normalisation [ 1.7 1.2] - .. categories:: diff --git a/Code/Mantid/docs/source/api/python/changes.rst b/Code/Mantid/docs/source/api/python/changes.rst index 4827b55cbd1d70cffca91580f71c5065c322b275..4bc9c8651fc33cdbc9dee6bd816c91a5185bbd3c 100644 --- a/Code/Mantid/docs/source/api/python/changes.rst +++ b/Code/Mantid/docs/source/api/python/changes.rst @@ -89,7 +89,7 @@ Changes * The *qti* module no longer exists. All user scripts should simply use the *mantidplot* module which contains all of the *qti* functionality but adds protection against crashes from closed windows. -* There have also been changes with Python algorithm syntax. For this it will be most beneficial to read the new tutorial `here <http://www.mantidproject.org/Intoduction_to_PythonAlgorithms`_. +* There have also been changes with Python algorithm syntax. Automatic Migration ------------------- diff --git a/Code/Mantid/docs/source/images/BleedingCorrections.png b/Code/Mantid/docs/source/images/BleedingCorrections.png new file mode 100644 index 0000000000000000000000000000000000000000..1d2a77844116e43d9b30436f3440edd55e4fd0bb Binary files /dev/null and b/Code/Mantid/docs/source/images/BleedingCorrections.png differ diff --git a/Code/Mantid/docs/source/images/BleedingSignal.png b/Code/Mantid/docs/source/images/BleedingSignal.png new file mode 100644 index 0000000000000000000000000000000000000000..f2eecea637caf5ffca96e54fa0b18a7d789d9db6 Binary files /dev/null and b/Code/Mantid/docs/source/images/BleedingSignal.png differ diff --git a/Code/Mantid/docs/source/interfaces/HFIRPowderReduction.rst b/Code/Mantid/docs/source/interfaces/HFIRPowderReduction.rst index f72c3d66bfae400565365ff7c4d9845025f6b7a7..44d329777caa023b14f52d84cc08a58a6644051a 100644 --- a/Code/Mantid/docs/source/interfaces/HFIRPowderReduction.rst +++ b/Code/Mantid/docs/source/interfaces/HFIRPowderReduction.rst @@ -15,28 +15,39 @@ Use cases for tabs ------------------ 1. **Raw Detectors**: Visualize the reading of detectors directly coming out of the raw data + - Plot N lines for N Pts.; - Highlight (make it thicker) the Pt that is interested; - New from Mantid: *ReadRawSpiceSignal(Pts)*; + 2. **Individual Detector**: Visual the readings of one detector across an experiment + - Plot the counts of any individual detector; - Able to change the X-axis from 2theta to arbitrary sample environment log; - New from Mantid: *ReadRawSpiceSignal(DetectorID, XLabel)*; + 3. **Normalized**: Reduce one scan each time + - Plot the reduced data - Automatically locate detector efficiency file - New from Mantid: *ConvertCWPDMDToSpectra(ExcludedDetectors=[])* - New from Mantid: *ConvertSpiceDataToRealSpace(DetectorEfficiencyTable)* + 4. **Multiple Scans**: Reduce a set of scans + - Reduce a set of scans and plot in 2D/water-fall mode; - Able to merge all the scans; - New from Mantid: *ConvertCWPDMDToSpectra(ExcludedDetectors=[])* + 5. **Vanadium**: strip vanadium peaks + - Strip vanadium peak with unit 'Degrees' because of the binning (range and step size) must be respected; - Peaks' position should be calculated and indicated auotmatically; - *Mantid::StripPeaks()* will be called instread of *StripVadadiumPeaks()* because the later one only works in d-spacing; + 6. **Advanced Setup** + - URL for raw data files; @@ -46,19 +57,29 @@ Workflow for *Normalization* Here is a typical use case for reduce data via tab *Noramlization* 1. User specifies *Exp No* and *Scan No* and push button *Load*; + - HFIR-PDR-GUI loads SPICE data according to experiment number and scan number; - HFIR-PDR-GUI checks whether vanadium correction file, i.e., detector efficiency file exists on server; - HFIR-PDR-GUI checks whether excluded detectors file exists on server; - HFIR-PDR-GUI checks log **m1** for wavelength and set to *Wavelength* ; + 2. User may specify detector efficient file; + 3. User specifies *Bin Size*; + 4. User pushes button *2Theta*, *dSpacng*, or *Q*; + - HFIR-PDF-GUI reduce data in unit of *2theta* by taking accounting of + - Detector efficiency; - Excluded detectors; + 5. HFIR-PDR-GUI plots the reduced data; + 6. User may rebin by different binning parameters or unit; + 7. User may push button *Next Scan* or *Prev Scan* to load and reduce other scans with current setup; + 8. User may save the result by pushing button *Save*; @@ -68,12 +89,16 @@ Workflow for *Raw Detectors* Here is a typical use case for reduce data via tab *Noramlization* 1. User specifies *Exp No* and *Scan No* and push button *Load*; + - HFIR-PDR-GUI loads SPICE data according to experiment number and scan number; - HFIR-PDR-GUI checks whether vanadium correction file, i.e., detector efficiency file exists on server; - HFIR-PDR-GUI checks whether excluded detectors file exists on server; - HFIR-PDR-GUI checks log **m1** for wavelength and set to *Wavelength* ; + 2. User specifies a *Pt.* number and push button *Plot Raw Detector*; + - HFIR-PDF-GUI plots the raw detector counts normalized by monitor count; + 3. User may push button *Previous Pt.* or *Next Pt.* for the other experiment points; @@ -105,11 +130,13 @@ Experiment setup and sample log =============================== 1. **Wavelength**: There are three settings for neutron wavelength, referenced by sample log *m1*. + - Ge 113: :math:`\lambda = 2.41 \AA`, m1 = 9.45 (The **error** can be 0.05, such that in Exp 231 scan0001, m1=9.5) - Ge 115: :math:`\lambda = 1.54 \AA`, m1 = 0 - Ge 117 :math:`\lambda = 1.12 \AA`, No used 2. **Collimator translation**: There are two status for collimator, which is specified by sample log *colltrans* + - *IN*: colltrans = 0 - *OUT*: colltrans = +/-80 @@ -119,25 +146,33 @@ Raw data correction files 1. **Detector efficiency**: - File name: *HB2A_exp0IJK__GE_abc_XY_vcorr.txt* where + - IJK is the experiment number - abc is the GE set up. It can be 113, 115 or 117 - XY is either IN or OUT. - Exmaple: *HB2A_exp0400__Ge_113_IN_vcorr.txt* + - Web address: *http://neutron.ornl.gov/user_data/hb2a/exp400/Datafiles/HB2A_exp0IJK__Ge_abc_IN_vcorr.txt* + - IJK is the experiment number - abc is the GE set up. It can be 113, 115 or 117 - XY is either IN or OUT. - Exmaple: *http://neutron.ornl.gov/user_data/hb2a/exp400/Datafiles/HB2A_exp0400__Ge_113_IN_vcorr.txt* 2. **Excluded detectors**: Some detectors might be exluded from the experiment for some reason. It is recorded in some excluded detectors' file. + - File name: *HB2A_exp0IJK__exclude_detectors.txt* + - IJK is the epxeriment number - Exmaple: *HB2A_exp0400__exclude_detectors.txt* + - Web address: *http://neutron.ornl.gov/user_data/hb2a/expIJK/Datafiles/HB2A_exp0IJK__exclude_detectors.txt* + - IJK is the experiment number - Example: *http://neutron.ornl.gov/user_data/hb2a/exp400/Datafiles/HB2A_exp0400__exclude_detectors.txt* 3. Detector gaps: The 2-theta gap (in unit degrees) can be changed among cycles. + - Location example: *http://neutron.ornl.gov/user_data/hb2a/exp400/Datafiles/HB2A_exp0400__gaps.txt* diff --git a/Code/Mantid/instrument/Facilities.xml b/Code/Mantid/instrument/Facilities.xml index 22bd76723f7e2ff7e87ebf025f0d0713ab5984f0..249b306b1a0b196046a028cd38b95e147ca82a34 100644 --- a/Code/Mantid/instrument/Facilities.xml +++ b/Code/Mantid/instrument/Facilities.xml @@ -409,6 +409,8 @@ <instrument name="TOPAZ" beamline="12"> <technique>Neutron Diffraction</technique> <technique>Single Crystal Diffraction</technique> + <livedata listener="TOPAZLiveEventDataListener" address="128.219.164.132:9000" /> + <!-- Act like we're ISawEV talking to event_catcher --> </instrument> <instrument name="HYSPEC" shortname="HYS" beamline="14B"> diff --git a/Code/Mantid/scripts/Inelastic/Direct/DirectEnergyConversion.py b/Code/Mantid/scripts/Inelastic/Direct/DirectEnergyConversion.py index 2a4a5ff3aaf42f6c3149dbe07cce291b79b86624..9254d69cf119d6718264162e10b26cacc418ceaa 100644 --- a/Code/Mantid/scripts/Inelastic/Direct/DirectEnergyConversion.py +++ b/Code/Mantid/scripts/Inelastic/Direct/DirectEnergyConversion.py @@ -239,15 +239,25 @@ class DirectEnergyConversion(object): diagnostics.__Reducer__ = self diag_params['sample_run'] = diag_sample - # Set up the background integrals for diagnostic purposes - result_ws = self.normalise(diag_sample, self.normalise_method) + # Set up the background integrals for diagnostic purposes. + # monitor-2 normalization in multirep mode goes per chunk + if (PropertyManager.incident_energy.multirep_mode() and self.normalise_method == 'monitor-2')\ + or self.bleed_test: # bleed test below needs no normalization so normalize cloned workspace + result_ws = diag_sample.get_ws_clone('sample_ws_clone') + wb_normalization_method = whiteintegrals.getRun().getLogData('DirectInelasticReductionNormalisedBy').value + result_ws = self.normalise(result_ws, wb_normalization_method) + name_to_clean = result_ws.name() + else: + result_ws = self.normalise(diag_sample, self.normalise_method) + name_to_clean = None #>>>here result workspace is being processed #-- not touching result ws bkgd_range = self.background_test_range - background_int = Integration(result_ws,\ - RangeLower=bkgd_range[0],RangeUpper=bkgd_range[1],\ - IncludePartialBins=True) + bin_size = 2*(bkgd_range[1]-bkgd_range[0]) + background_int = Rebin(result_ws,\ + Params=[bkgd_range[0],bin_size,bkgd_range[1]],\ + PreserveEvents=False,FullBinsOnly=False) total_counts = Integration(result_ws, IncludePartialBins=True) background_int = ConvertUnits(background_int, Target="Energy",EMode='Elastic', AlignBins=0) self.prop_man.log("Diagnose: finished convertUnits ",'information') @@ -257,6 +267,8 @@ class DirectEnergyConversion(object): diag_params.get('second_white',None)) diag_params['background_int'] = background_int diag_params['sample_counts'] = total_counts + else: # diag_sample is None + name_to_clean = None # extract existing white mask if one is defined and provide it for @@ -309,6 +321,10 @@ class DirectEnergyConversion(object): DeleteWorkspace(Workspace='total_counts') if 'second_white' in diag_params: DeleteWorkspace(Workspace=diag_params['second_white']) + if name_to_clean: + DeleteWorkspace(name_to_clean) + if name_to_clean+'_monitors' in mtd: + DeleteWorkspace(name_to_clean+'_monitors') DeleteWorkspace(Workspace=whiteintegrals) return diag_mask @@ -336,12 +352,11 @@ class DirectEnergyConversion(object): # and verify some other properties which can be wrong before starting a # long run. prop_man.log("****************************************************************") + prop_man.log("*** ISIS CONVERT TO ENERGY TRANSFER WRORKFLOW STARTED **********") prop_man.validate_properties() + prop_man.log("*** Loading or retrieving sample run: {0}".format(prop_man.sample_run)) prop_man.log("****************************************************************") - # inform user on what parameters have changed from script or gui - # if monovan present, check if abs_norm_ parameters are set - self.prop_man.log_changed_values('notice') # before trying to process new results, let's remove from memory old results # if any present and they are not needed any more (user have not renamed them) self._clear_old_results() @@ -363,6 +378,9 @@ class DirectEnergyConversion(object): format(PropertyManager.sample_run.get_workspace().name())) prop_man.log_changed_values('notice',False,oldChanges) prop_man.log("****************************************************************") + # inform user on what parameters have changed from script or gui + # if monovan present, check if abs_norm_ parameters are set + self.prop_man.log_changed_values('notice') masking = None masks_done = False @@ -446,6 +464,12 @@ class DirectEnergyConversion(object): cut_ind,num_ei_cuts) prop_man.log("*** Processing multirep chunk: #{0}/{1} for provisional energy: {2} meV".\ format(cut_ind,num_ei_cuts,ei_guess),'notice') + # do bleed corrections for chunk if necessary + bleed_mask = self._do_bleed_corrections(PropertyManager.sample_run,cut_ind) + if not bleed_mask is None: + mask_ws_name = PropertyManager.sample_run.get_workspace().name()+'_bleed_mask' + RenameWorkspace(bleed_mask,OutputWorkspace=mask_ws_name) + self._old_runs_list.append(mask_ws_name) else: # single energy uses single workspace and all TOF are used tof_range = None @@ -494,10 +518,9 @@ class DirectEnergyConversion(object): # END Main loop over incident energies #------------------------------------------------------------------------------------------ - end_time = time.time() - prop_man.log("*** Elapsed time = {0} sec".format(end_time - start_time),'notice') - # Must! clear backrgound ws (if present in multirep) to calculate background - # source for next workspace + + #Must! clear background ws (if present in multirep) to calculate background + #source for next workspace if 'bkgr_ws_source' in mtd: DeleteWorkspace('bkgr_ws_source') @@ -508,6 +531,11 @@ class DirectEnergyConversion(object): #prop_man.wb_run = None # clear combined mask self.spectra_masks = None + end_time = time.time() + prop_man.log("*** ISIS CONVERT TO ENERGY TRANSFER WRORKFLOW FINISHED *********") + prop_man.log("*** Elapsed time : {0:>9.2f} sec *********".\ + format(end_time - start_time),'notice') + prop_man.log("****************************************************************") return result def _do_abs_corrections(self,deltaE_ws_sample,cashed_mono_int,ei_guess,\ @@ -534,6 +562,9 @@ class DirectEnergyConversion(object): if self._multirep_mode: mono_ws_base = PropertyManager.monovan_run.chop_ws_part(mono_ws_base,tof_range,\ self._do_early_rebinning, cut_ind,num_ei_cuts) + # its pointless to do bleed for monovan run + #self._do_bleed_corrections(PropertyManager.monovan_run,cut_ind) + deltaE_ws_sample = self.apply_absolute_normalization(deltaE_ws_sample,PropertyManager.monovan_run,\ ei_guess,PropertyManager.wb_for_monovan_run,\ 'calculated') @@ -602,6 +633,24 @@ class DirectEnergyConversion(object): white_ws *= self.wb_scale_factor return white_ws + def _do_bleed_corrections(self,sample_run,nchunk): + """Calculate TOF-chunk specific bleed corrections, necessary in mutlirep mode + """ + if not self.prop_man.diag_bleed_test: + return None + + CUR_bleed_masks, failures = diagnostics.do_bleed_test(sample_run, self.prop_man.bleed_maxrate, self.prop_man.bleed_pixels) + if failures > 0: + diagnostics.add_masking(sample_run.get_workspace(),CUR_bleed_masks) + bleed_mask = CUR_bleed_masks + else: + DeleteWorkspace(CUR_bleed_masks) + bleed_mask = None + self.prop_man.log("*** Bleeding test for chunk #{0} masked {1} pixels".format(nchunk,failures),'notice') + return bleed_mask + + + def mono_sample(self, mono_run, ei_guess, white_run=None, map_file=None, spectra_masks=None, result_name=None, Tzero=None): """Convert a mono-chromatic sample run to DeltaE. @@ -611,7 +660,6 @@ class DirectEnergyConversion(object): if white_run: white_run = self.get_run_descriptor(white_run) - mono_s = self._do_mono(mono_run, ei_guess,\ white_run, map_file, spectra_masks, Tzero) # at this stage we would never need monitors for this workspace if they @@ -724,10 +772,13 @@ class DirectEnergyConversion(object): method,old_ws_name = self._normalize_to_monitor2(run,old_ws_name, range_offset,external_monitors_ws) break if case('current'): - NormaliseByCurrent(InputWorkspace=old_ws_name,OutputWorkspace=old_ws_name) + out_ws=NormaliseByCurrent(InputWorkspace=old_ws_name,OutputWorkspace=old_ws_name) + # NormalizationFactor log has been added by the algorithm themselves. break if case(): # default - raise RuntimeError('Normalization method {0} not found. It must be one of monitor-1, monitor-2, current, or None'.format(method)) + raise RuntimeError("""Normalization method {0} not found. + It must be one of monitor-1, monitor-2, current, or None""".\ + format(method)) #endCase @@ -778,10 +829,19 @@ class DirectEnergyConversion(object): kwargs['MonitorSpectrum'] = int(mon_spect) # shame TODO: change c++ algorithm, which need float monitor ID range_min = float(range[0] + range_offset) range_max = float(range[1] + range_offset) + kwargs['NormFactorWS'] = 'Monitor1_norm_ws' # Normalize to monitor 1 NormaliseToMonitor(InputWorkspace=old_name,OutputWorkspace=old_name,IntegrationRangeMin=range_min, IntegrationRangeMax=range_max,IncludePartialBins=True,**kwargs) + norm_mon1ws= mtd['Monitor1_norm_ws'] + norm_factor = norm_mon1ws.dataY(0) + if len(norm_factor)>1: + raise RuntimeError("Can not normalize by monitor spectra. Normalization range necessary") + AddSampleLog(old_name,LogName='NormalizationFactor',LogText=str(norm_factor[0]),LogType='Number') + if not self._debug_mode: + DeleteWorkspace(norm_mon1ws) + return ('monitor-1',old_name) # def _normalize_to_monitor2(self,run,old_name, range_offset=0.0,external_monitor_ws=None): @@ -795,23 +855,20 @@ class DirectEnergyConversion(object): else: mon_ws = run.get_monitors_ws() - - if not mon_ws: # no monitors - if self.__in_white_normalization: # we can normalize wb integrals by current separately as they often do not - # have monitors - self.normalise(run,'current',range_offset) - ws = run.get_workspace() - new_name = ws.name() - return ('current',new_name) - else: + if self.__in_white_normalization: # we normalize wb integrals by current separately as they often do not + # have monitors or are in fact wb workspace with some special ei + self.normalise(run,'current',range_offset) + ws = run.get_workspace() + new_name = ws.name() + return ('current',new_name) + else: + if not mon_ws: # no monitors ws = run.get_workspace() raise RuntimeError('Normalize by monitor-2:: Workspace {0} for run {1} does not have monitors in it'\ .format(ws.name(),run.run_number())) # - if self._debug_mode: - kwargs = {'NormFactorWS':'NormMon2_WS' + mon_ws.getName()} - else: - kwargs = {} + + kwargs = {'NormFactorWS':'NormMon2_WS' + mon_ws.getName()} mon_spect = self.prop_man.mon2_norm_spec mon_index = int(mon_ws.getIndexFromSpectrumNumber(mon_spect)) @@ -832,7 +889,8 @@ class DirectEnergyConversion(object): if mon_ws_name.find('_shifted') != -1: # monitor-2 normalization ranges have to be identified before the # instrument is shifted - raise RuntimeError("Instrument have been shifted but no time range has been identified. Monitor-2 normalization can not be performed ") + raise RuntimeError("""Instrument have been shifted but no time range has been identified. + Monitor-2 normalization can not be performed """) else: # instrument and workspace shifted, so TOF will be calculated wrt # shifted instrument @@ -843,6 +901,17 @@ class DirectEnergyConversion(object): # Normalize to monitor 2 NormaliseToMonitor(InputWorkspace=old_name,OutputWorkspace=old_name,IntegrationRangeMin=range_min, IntegrationRangeMax=range_max,IncludePartialBins=True,**kwargs) + + norm_ws_name = kwargs['NormFactorWS'] + norm_mon2ws = mtd[norm_ws_name] + norm_factor = norm_mon2ws.dataY(0) + if len(norm_factor)>1: + raise RuntimeError("Can not normalize by monitor spectra. Normalization range necessary") + + AddSampleLog(old_name,LogName='NormalizationFactor',LogText=str(norm_factor[0]),LogType='Number') + if not self._debug_mode: + DeleteWorkspace(norm_ws_name) + return ('monitor-2',old_name) #------------------------------------------------------------------------------- #------------------------------------------------------------------------------- @@ -855,7 +924,8 @@ class DirectEnergyConversion(object): spectra_id = self.prop_man.multirep_tof_specta_list if not spectra_id or len(spectra_id) == 0: - self.prop_man.log("*** WARNING! Multirep mode used but no closest and furthest spectra numbers defined in IDF (multirep_tof_specta_list)\n"\ + self.prop_man.log("""*** WARNING! Multirep mode used but no closest and furthest spectra numbers + defined in IDF (multirep_tof_specta_list)\n"""\ " Using first spectra to identify TOF range for the energy range requested.\n"\ " This is correct only if all detectors are equidistant from the sample",\ 'warning') @@ -929,7 +999,8 @@ class DirectEnergyConversion(object): mon1_peak = 0 en_bin = [energy_list[0],energy_list[1]-energy_list[0],energy_list[3]] self.prop_man.log("*** WARNING: message from multirep chunking procedure: get_TOF_for_energies:\n"\ - " not able to identify energy peak looking for TOF range for incident energy: {0}meV, binning: {1}\n"\ + " not able to identify energy peak looking for TOF range for incident energy:"\ + " {0}meV, binning: {1}\n"\ " Continuing under assumption that incident neutrons arrive at source at time=0".\ format(ei_guess,en_bin),'warning') else: @@ -944,14 +1015,18 @@ class DirectEnergyConversion(object): ind = workspace.getIndexFromSpectrumNumber(specID) ExtractSingleSpectrum(InputWorkspace=workspace, OutputWorkspace=template_ws_name, WorkspaceIndex=ind) if ei: - CreateWorkspace(OutputWorkspace=range_ws_name,NSpec = 1,DataX=energy_list,DataY=y,UnitX='DeltaE',ParentWorkspace=template_ws_name) + CreateWorkspace(OutputWorkspace=range_ws_name,NSpec = 1,DataX=energy_list,\ + DataY=y,UnitX='DeltaE',ParentWorkspace=template_ws_name) if src_name: MoveInstrumentComponent(Workspace=range_ws_name,ComponentName= src_name, X=mon1_pos.getX(), - Y=mon1_pos.getY(), Z=mon1_pos.getZ(), RelativePosition=False) - range_ws = ConvertUnits(InputWorkspace=range_ws_name,OutputWorkspace=range_ws_name,Target='TOF',EMode='Direct',EFixed=ei) + Y=mon1_pos.getY(), Z=mon1_pos.getZ(), RelativePosition=False) + range_ws = ConvertUnits(InputWorkspace=range_ws_name,OutputWorkspace=range_ws_name,\ + Target='TOF',EMode='Direct',EFixed=ei) else: - CreateWorkspace(OutputWorkspace=range_ws_name,NSpec = 1,DataX=energy_list,DataY=y,UnitX='Energy',ParentWorkspace=template_ws_name) - range_ws = ConvertUnits(InputWorkspace=range_ws_name,OutputWorkspace=range_ws_name,Target='TOF',EMode='Elastic') + CreateWorkspace(OutputWorkspace=range_ws_name,NSpec = 1,DataX=energy_list,\ + DataY=y,UnitX='Energy',ParentWorkspace=template_ws_name) + range_ws = ConvertUnits(InputWorkspace=range_ws_name,OutputWorkspace=range_ws_name,\ + Target='TOF',EMode='Elastic') x = range_ws.dataX(0)+mon1_peak TOF_range.append(x.tolist()) @@ -963,7 +1038,7 @@ class DirectEnergyConversion(object): TOF_range = TOF_range[0] return TOF_range - + # def save_results(self, workspace, save_file=None, formats=None): """ Save the result workspace to the specified filename using the list of formats specified in @@ -985,7 +1060,8 @@ class DirectEnergyConversion(object): if save_file is None: if workspace is None: - prop_man.log("DirectEnergyConversion:save_results: Nothing to do",'warning') + self.prop_man.log("DirectEnergyConversion:save_results: Nothing to save",\ + 'warning') return else: save_file = workspace.getName() @@ -1094,8 +1170,10 @@ class DirectEnergyConversion(object): else: mvir = prop_man.monovan_integr_range - prop_man.log('*** Evaluating the integral from the monovan run and calculate the correction factor ******','notice') - prop_man.log(' Using absolute units vanadium integration range : [{0:8f}:{1:8f}] ******'.format(mvir[0],mvir[1]),'notice') + prop_man.log('*** Evaluating the integral from the monovan run and calculate the correction factor ******',\ + 'notice') + prop_man.log(' Using absolute units vanadium integration range : [{0:8f}:{1:8f}] ******'.\ + format(mvir[0],mvir[1]),'notice') if not abs_norm_factor_is: abs_norm_factor_is = 'calculated' @@ -1110,8 +1188,9 @@ class DirectEnergyConversion(object): (anf_LibISIS,anf_SS2,anf_Puas,anf_TGP) = self.get_abs_normalization_factor(monovan_run,ei_monovan) - prop_man.log('*** Absolute correction factor(s): S^2: {0:10.4f}\n*** LibISIS: {1:10.4f} Poisson: {2:10.4f} TGP: {3:10.4f} '\ - .format(anf_LibISIS,anf_SS2,anf_Puas,anf_TGP),'notice') + prop_man.log("""*** Absolute correction factor(s): S^2: {0:10.4f} +*** LibISIS: {1:10.4f} Poisson: {2:10.4f} TGP: {3:10.4f} """\ + .format(anf_LibISIS,anf_SS2,anf_Puas,anf_TGP),'notice') prop_man.log('*** If these factors are substantially different, something is wrong ***','notice') absnorm_factor = anf_TGP # Store the factor for further usage @@ -1119,7 +1198,8 @@ class DirectEnergyConversion(object): # reset current monovan run to run number (if it makes sense) -- ## workspace is not good for further processing any more #end - prop_man.log('*** Using {0} value : {1} of absolute units correction factor (TGP)'.format(abs_norm_factor_is,absnorm_factor),'notice') + prop_man.log('*** Using {0} value : {1} of absolute units correction factor (TGP)'.\ + format(abs_norm_factor_is,absnorm_factor),'notice') prop_man.log('*******************************************************************************************','notice') sample_ws = sample_ws / absnorm_factor @@ -1239,17 +1319,16 @@ class DirectEnergyConversion(object): log_value = '\n--------> Absolute normalization factor is NaN <----------------------------------------------\n' else: log_value = '\n--------> Warning, Monovanadium has zero spectra <--------------------------------------------\n' - log1_value = \ - "--------> Processing workspace: {0}\n"\ - "--------> Monovan Integration range : min={1}, max={2} (meV)\n"\ - "--------> Summed: {3} spectra with total signal: {4} and error: {5}\n"\ - "--------> Dropped: {6} zero spectra\n"\ - "--------> Using mBarn/sR*fu normalization factor = {7} resulting in:\n"\ - "--------> Abs norm factors: LibISIS: {8}\n"\ - "--------> Abs norm factors: Sigma^2: {9}\n"\ - "--------> Abs norm factors: Poisson: {10}\n"\ - "--------> Abs norm factors: TGP : {11}\n"\ - .format(ws_name,minmax[0],minmax[1],nhist,sum(signal),sum(error),izerc,scale_factor, + log1_value = """--------> Processing workspace: {0} +--------> Monovan Integration range : min={1}, max={2} (meV) +--------> Summed: {3} spectra with total signal: {4} and error: {5} +--------> Dropped: {6} zero spectra +--------> Using mBarn/sR*fu normalization factor = {7} resulting in: +--------> Abs norm factors: LibISIS: {8} +--------> Abs norm factors: Sigma^2: {9} +--------> Abs norm factors: Poisson: {10} +--------> Abs norm factors: TGP : {11}\n"""\ + .format(ws_name,minmax[0],minmax[1],nhist,sum(signal),sum(error),izerc,scale_factor, norm_factor['LibISIS'],norm_factor['SigSq'],norm_factor['Poisson'],norm_factor['TGP']) log_value = log_value + log1_value propman.log(log_value,'error') @@ -1409,7 +1488,7 @@ class DirectEnergyConversion(object): bkgd_range = self.bkgd_range bkg_range_min = bkgd_range[0] + bin_offset bkg_range_max = bkgd_range[1] + bin_offset - if isinstance(result_ws,api.IEventWorkspace): + if isinstance(result_ws,api.IEventWorkspace) or PropertyManager.incident_energy.multirep_mode(): bkgr_ws = self._find_or_build_bkgr_ws(data_run,bkg_range_min,bkg_range_max,bin_offset) else: bkgr_ws = None @@ -1436,7 +1515,10 @@ class DirectEnergyConversion(object): energy_bins = PropertyManager.energy_bins.get_abs_range(self.prop_man) if energy_bins: Rebin(InputWorkspace=result_name,OutputWorkspace=result_name,Params= energy_bins,PreserveEvents=False) - if bkgr_ws: # remove background after converting units and rebinning + if bkgr_ws: + #apply data ws normalization to background workspace + data_run.export_normalization(bkgr_ws) + # remove background after converting units and rebinning RemoveBackground(InputWorkspace=result_name,OutputWorkspace=result_name,BkgWorkspace=bkgr_ws,EMode='Direct') DeleteWorkspace(bkgr_ws) else: @@ -1470,10 +1552,10 @@ class DirectEnergyConversion(object): if 'bkgr_ws_source' in mtd: #TODO: This is questionable operation, which may be unnecessary if remove background # uses time interval only. (and it probably does) - # need to check if bkgr_ws =mtd['bkgr_ws_source'] is enough here. + # need to check if bkgr_ws =mtd['bkgr_ws_source'] is enough here. # (and not delete it after bkg removal) bkgr_ws = CloneWorkspace(InputWorkspace='bkgr_ws_source',OutputWorkspace='bkgr_ws') - if time_shift != 0: # Workspace has probably been shifted, so to have + if time_shift != 0: # Workspace has probably been shifted, so to have # one needs to do appropriate shift here #correct units conversion as well CopyInstrumentParameters(result_ws,bkgr_ws) @@ -1482,12 +1564,6 @@ class DirectEnergyConversion(object): InstrumentParameter="DelayTime",Combine=True) else: # calculate background workspace for future usage bkgr_ws = Rebin(result_ws,Params=[bkg_range_min,(bkg_range_max - bkg_range_min) * 1.001,bkg_range_max],PreserveEvents=False) - if run.is_monws_separate(): - mon_ws = run.get_monitors_ws() - CloneWorkspace(mon_ws,OutputWorkspace="bkgr_ws_monitors") - bkgr_ws = self.normalise(bkgr_ws, self.normalise_method, time_shift) - if bkgr_ws.name()+"_monitors" in mtd: - DeleteWorkspace(bkgr_ws.name()+"_monitors") RenameWorkspace(InputWorkspace=bkgr_ws, OutputWorkspace='bkgr_ws_source') bkgr_ws = mtd['bkgr_ws_source'] @@ -1590,7 +1666,7 @@ class DirectEnergyConversion(object): delta = 2.0 * (upp - low) white_ws = Rebin(InputWorkspace=old_name,OutputWorkspace=old_name, Params=[low, delta, upp]) - # Why aren't we doing this... + # Why aren't we doing this...-> because integration does not work properly for event workspaces #Integration(white_ws, white_ws, RangeLower=low, RangeUpper=upp) AddSampleLog(white_ws,LogName = done_Log,LogText=done_log_VAL,LogType='String') run.synchronize_ws(white_ws) diff --git a/Code/Mantid/scripts/Inelastic/Direct/PropertyManager.py b/Code/Mantid/scripts/Inelastic/Direct/PropertyManager.py index 4aaaa59da6f5bf1bc39f2eee2499fcc435dbefb1..50887f347403868ca9da3d2b96cbc7e6605918e1 100644 --- a/Code/Mantid/scripts/Inelastic/Direct/PropertyManager.py +++ b/Code/Mantid/scripts/Inelastic/Direct/PropertyManager.py @@ -47,16 +47,20 @@ class PropertyManager(NonIDF_Properties): This is not considered a problem as only one instance of property manager is expected. If this need to be changed, adding property values to the __dict__ as values of _property_name keys should be safe. - 4) __getattr__ (and __setattr__ ) method are overloaded to provide call to a descriptor before the search in the system dictionary. - Custom __getattr__ naturally works only if Python does not find a property name in the __dict__ or __class__.__dict__ (and mro()), - e.g. in case when an descriptor is called through one of its synonym name. + 4) __getattr__ (and __setattr__ ) method are overloaded to provide call to a descriptor + before the search in the system dictionary. + Custom __getattr__ naturally works only if Python does not find a property name in the __dict__ or + __class__.__dict__ (and mro()), e.g. in case when an descriptor is called through one of its synonym name. - A problem will occur if a key with name equal to descriptor name is also present in __dict__. This name would override descriptor. - This is why any new descriptor should never place a key with its name in __dict__. Current design automatically remove IDF name - from __dict__ if a descriptor with such name exist, so further development should support this behavior. + A problem will occur if a key with name equal to descriptor name is also present in __dict__. + This name would override descriptor. + This is why any new descriptor should never place a key with its name in __dict__. + Current design automatically remove IDF name from __dict__ if a descriptor with such name exist, + so further development should support this behavior. - 5) In many places (descriptors, RunDescriptor itself), PropertyManager assumed to be a singleton, so most Descriptors are defined on a - class level. If this changes, careful refactoring will be necessary + 5) In many places (descriptors, RunDescriptor itself), PropertyManager assumed to be a singleton, + so most Descriptors are defined on a class level. + If this changes, careful refactoring will be necessary Copyright © 2014 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory @@ -131,7 +135,6 @@ class PropertyManager(NonIDF_Properties): class_decor = '_'+type(self).__name__+'__' - result = {} for key,val in prop_dict.iteritems(): new_key = class_decor+key object.__setattr__(self,new_key,val) @@ -177,7 +180,7 @@ class PropertyManager(NonIDF_Properties): if name in self.__descriptors: super(PropertyManager,self).__setattr__(name,val) else: - other_prop=prop_helpers.gen_setter(self.__dict__,name,val) + prop_helpers.gen_setter(self.__dict__,name,val) # record the fact that the property have changed self.__changed_properties.add(name) @@ -326,7 +329,8 @@ class PropertyManager(NonIDF_Properties): try: result[key] = getattr(self,key) except KeyError: - self.log('--- Diagnostics property {0} is not found in instrument properties. Default value: {1} is used instead \n'.format(key,value),'warning') + self.log("--- Diagnostics property {0} is not found in instrument properties." + "Default value: {1} is used instead \n".format(key,val),'warning') return result # @@ -358,7 +362,7 @@ class PropertyManager(NonIDF_Properties): old_changes[prop_name] = getattr(self,prop_name) - param_list = prop_helpers.get_default_idf_param_list(pInstrument) + param_list = prop_helpers.get_default_idf_param_list(pInstrument,self.__subst_dict) # remove old changes which are not related to IDF (not to reapply it again) for prop_name in old_changes: if not prop_name in param_list: @@ -370,7 +374,9 @@ class PropertyManager(NonIDF_Properties): for name in dependencies: if name in param_list: modified = True - break + # old parameter have been modified through compound parameter. + #its old value is irrelevant + param_list[name] = getattr(self,name) if not modified: del old_changes[prop_name] #end @@ -393,8 +399,12 @@ class PropertyManager(NonIDF_Properties): setattr(self,key,val) new_val = getattr(self,key) except: - self.log("property {0} have not been found in existing IDF. Ignoring this property"\ - .format(key),'warning') + try: + cur_val = getattr(self,key) + except: + cur_val = "Undefined" + self.log("Retrieving or reapplying script property {0} failed. Property value remains: {1}"\ + .format(key,cur_val),'warning') continue if isinstance(new_val,api.Workspace) and isinstance(cur_val,api.Workspace): # do simplified workspace comparison which is appropriate here @@ -402,7 +412,8 @@ class PropertyManager(NonIDF_Properties): new_val.getNumberHistograms() == cur_val.getNumberHistograms() and \ new_val.getNEvents() == cur_val.getNEvents() and \ new_val.getAxis(0).getUnit().unitID() == cur_val.getAxis(0).getUnit().unitID(): - new_val =1; cur_val = 1 + new_val = 1 + cur_val = 1 # #end if new_val != cur_val: @@ -424,34 +435,40 @@ class PropertyManager(NonIDF_Properties): # Walk through the complex properties first and then through simple properties for key,val in sorted_param.iteritems(): - if not key in old_changes_list: - # complex properties change through their dependencies so we are setting them first + # complex properties may change through their dependencies so we are setting them first + if isinstance(val,prop_helpers.ComplexProperty): + public_name = key[1:] + else: + # no complex properties left so we have simple key-value pairs + public_name = key + if not public_name in old_changes_list: if isinstance(val,prop_helpers.ComplexProperty): - public_name = key[1:] - prop_new_val = val.__get__(param_list) + prop_idf_val = val.__get__(param_list) else: - # no complex properties left so we have simple key-value pairs - public_name = key - prop_new_val = val + prop_idf_val = val try: # this is reliability check, and except ideally should never be hit. May occur if old IDF contains # properties, not present in recent IDF. cur_val = getattr(self,public_name) except: - self.log("property {0} have not been found in existing IDF. Ignoring this property"\ + self.log("Can not retrieve property {0} value from existing reduction parameters. Ignoring this property"\ .format(public_name),'warning') continue - if prop_new_val !=cur_val : - setattr(self,public_name,prop_new_val) - # Dependencies removed either properties are equal or not - try: - dependencies = val.dependencies() - except: - dependencies =[] - for dep_name in dependencies: - # delete dependent properties not to deal with them again - del sorted_param[dep_name] + if prop_idf_val !=cur_val : + setattr(self,public_name,prop_idf_val) + else: + pass + # Dependencies removed either properties are equal or not. + # or if public_name for property in old change list. Remove dependencies + # too, as property has been set up as whole. + try: + dependencies = val.dependencies() + except: + dependencies =[] + for dep_name in dependencies: + # delete dependent properties not to deal with them again + del sorted_param[dep_name] #end @@ -635,9 +652,9 @@ class PropertyManager(NonIDF_Properties): momovan_properties=['sample_mass','sample_rmm'] changed_prop = self.getChangedProperties() non_changed = [] - for property in momovan_properties: - if not property in changed_prop: - non_changed.append(property) + for prop in momovan_properties: + if not prop in changed_prop: + non_changed.append(prop) return non_changed # @@ -705,7 +722,8 @@ class PropertyManager(NonIDF_Properties): # if provided without arguments it returns the list of the parameters available # """ - # raise KeyError(' Help for this class is not yet implemented: see {0}_Parameter.xml in the file for the parameters description'.format()) + # raise KeyError(' Help for this class is not yet implemented: see {0}_Parameter.xml + # in the file for the parameters description'.format()) if __name__=="__main__": pass diff --git a/Code/Mantid/scripts/Inelastic/Direct/ReductionHelpers.py b/Code/Mantid/scripts/Inelastic/Direct/ReductionHelpers.py index 94eb463395b9cb7cbe85a94554e8ccff520319e1..d43bfcad1cdc46b28105a287aa61552136ebbb9e 100644 --- a/Code/Mantid/scripts/Inelastic/Direct/ReductionHelpers.py +++ b/Code/Mantid/scripts/Inelastic/Direct/ReductionHelpers.py @@ -97,7 +97,14 @@ def get_default_idf_param_list(pInstrument,synonims_list=None): params = pInstrument.getParameterNames() par_list = {} for name in params: - par_list[name] = get_default_parameter(pInstrument,name) + if synonims_list: + if name in synonims_list: + key_name=synonims_list[name] + else: + key_name = name + else: + key_name = name + par_list[key_name] = get_default_parameter(pInstrument,name) return par_list diff --git a/Code/Mantid/scripts/Inelastic/Direct/ReductionWrapper.py b/Code/Mantid/scripts/Inelastic/Direct/ReductionWrapper.py index 065faeb8835bec6f2be1f770e5b42f85983ef4c3..ef4c40b385650695d83f1f29e75e0da102d959cf 100644 --- a/Code/Mantid/scripts/Inelastic/Direct/ReductionWrapper.py +++ b/Code/Mantid/scripts/Inelastic/Direct/ReductionWrapper.py @@ -80,7 +80,7 @@ class ReductionWrapper(object): is interpreted as time to wait until check for specified run file if this file have not been find immediately. - if this variable is 0 or false and the the file have not been found, + if this variable is 0 or false and the file have not been found, reduction will fail """ return self._wait_for_file diff --git a/Code/Mantid/scripts/Inelastic/Direct/RunDescriptor.py b/Code/Mantid/scripts/Inelastic/Direct/RunDescriptor.py index c0c8a33fcce5b6beda210900dd702d0617ff5c3a..5153eb01a73841770f8b117a716d7c1f210d5d75 100644 --- a/Code/Mantid/scripts/Inelastic/Direct/RunDescriptor.py +++ b/Code/Mantid/scripts/Inelastic/Direct/RunDescriptor.py @@ -1070,6 +1070,41 @@ class RunDescriptor(PropDescriptor): RunDescriptor._logger('load_data: Copying detectors positions from workspace {0}: '.format(ws_calibration.name()),'debug') CopyInstrumentParameters(InputWorkspace=ws_calibration,OutputWorkspace=loaded_ws) AddSampleLog(Workspace=loaded_ws,LogName="calibrated",LogText=str(ws_calibration)) +#-------------------------------------------------------------------------------------------------------------------- + def export_normalization(self,other_workspace): + """Method applies normalization, present on current workspace to other_workspace provided + as argument. + + If other workspace is already normalized, the method modifies that normalization to match + the normalization of current workspace + + WARNING! this operation makes sense in special circumstances only (e.g. the initial workspace + has been split in parts) + """ + source_ws = self.get_workspace() + + if isinstance(other_workspace,api.MatrixWorkspace): + targ_ws = other_workspace + else: + targ_ws = mtd[other_workspace] + + if not 'NormalizationFactor' in source_ws.getRun(): + raise RuntimeError(""" Can not change normalization of target workspace {0} + as source workspace {1} is not normalized"""\ + .format(source_ws.name(),targ_ws.name())) + TargFactor = source_ws.getRun().getLogData('NormalizationFactor').value + if 'NormalizationFactor' in targ_ws.getRun(): + OldFactor = targ_ws.getRun().getLogData('NormalizationFactor').value + if abs(OldFactor-TargFactor)<1.e-5: # Already normalized + return + + NormFactor=TargFactor/OldFactor + other_workspace/=NormFactor + else: + other_workspace/=TargFactor + AddSampleLog(other_workspace,LogName='NormalizationFactor',LogText=str(TargFactor),LogType='Number') + + #-------------------------------------------------------------------------------------------------------------------- @staticmethod def copy_spectrum2monitors(data_ws,mon_ws,spectraID): @@ -1413,9 +1448,9 @@ class RunDescriptorDependent(RunDescriptor): def get_ws_clone(self,clone_name='ws_clone'): if self._has_own_value: - return super(RunDescriptorDependent,self).get_ws_clone() + return super(RunDescriptorDependent,self).get_ws_clone(clone_name) else: - return self._host.get_ws_clone() + return self._host.get_ws_clone(clone_name) def chop_ws_part(self,origin,tof_range,rebin,chunk_num,n_chunks): if self._has_own_value: @@ -1473,16 +1508,24 @@ class RunDescriptorDependent(RunDescriptor): return super(RunDescriptorDependent,self).clear_monitors() else: return self._host.clear_monitors() + def get_masking(self,noutputs=None): if self._has_own_value: return super(RunDescriptorDependent,self).get_masking(noutputs) else: return self._host.get_masking(noutputs) + def add_masked_ws(self,masked_ws): if self._has_own_value: return super(RunDescriptorDependent,self).add_masked_ws(masked_ws) else: return self._host.add_masked_ws(masked_ws) + + def export_normalization(self,other_workspace): + if self._has_own_value: + return super(RunDescriptorDependent,self).export_normalization(other_workspace) + else: + return self._host.export_normalization(other_workspace) #-------------------------------------------------------------------------------------------------------------------- #-------------------------------------------------------------------------------------------------------------------- def build_run_file_name(run_num,inst,file_path='',fext=''): diff --git a/Code/Mantid/scripts/Inelastic/Direct/diagnostics.py b/Code/Mantid/scripts/Inelastic/Direct/diagnostics.py index 363ca3c9bbc5c2d9de8e5e782461145d120e151b..e5ceefd9d964e862bb4eb0a7d706889c7f55c2d6 100644 --- a/Code/Mantid/scripts/Inelastic/Direct/diagnostics.py +++ b/Code/Mantid/scripts/Inelastic/Direct/diagnostics.py @@ -12,6 +12,7 @@ from mantid.simpleapi import * from mantid.kernel.funcreturns import lhs_info import os import Direct.RunDescriptor as RunDescriptor +from Direct.PropertyManager import PropertyManager # Reference to reducer used if necessary for working with run descriptors (in diagnostics) __Reducer__ = None @@ -64,7 +65,7 @@ def diagnose(white_int,**kwargs): # Map the test number to the results # Each element is the mask workspace name then the number of failures - test_results = [ [None, None], [None, None], [None, None], [None, None], [None, None]] + test_results = {} # Hard mask hardmask_file = kwargs.get('hard_mask_file', None) @@ -89,20 +90,19 @@ def diagnose(white_int,**kwargs): # Find out how many detectors we hard masked _dummy_ws,masked_list = ExtractMask(InputWorkspace='hard_mask_ws') DeleteWorkspace('_dummy_ws') - test_results[0][0] = os.path.basename(parser.hard_mask_file) - test_results[0][1] = len(masked_list) + test_results['Hard mask:'] = [os.path.basename(parser.hard_mask_file),len(masked_list)] DeleteWorkspace('hard_mask_ws') if not parser.use_hard_mask_only : # White beam Test if white_mask: - test_results[1] = ['white_mask cache global', num_failed] + test_results['First detector vanadium test:'] = ['white_mask cache global', num_failed] else: __white_masks, num_failed = do_white_test(white_int, parser.tiny, parser.huge, parser.van_out_lo, parser.van_out_hi, parser.van_lo, parser.van_hi, parser.van_sig, start_index, end_index) - test_results[1] = [str(__white_masks), num_failed] + test_results['First detector vanadium test:'] = [str(__white_masks), num_failed] add_masking(white_int, __white_masks, start_index, end_index) if van_mask: add_masking(van_mask, __white_masks, start_index, end_index) @@ -110,12 +110,12 @@ def diagnose(white_int,**kwargs): # Second white beam test if 'second_white' in kwargs: #NOT IMPLEMENTED - raise NotImplementedError("Second white is not yet implemented") + raise NotImplementedError("Second detector vanadium test") __second_white_masks, num_failed = do_second_white_test(white_int, parser.second_white, parser.tiny, parser.huge,\ parser.van_out_lo, parser.van_out_hi,\ parser.van_lo, parser.van_hi, parser.variation,\ parser.van_sig, start_index, end_index) - test_results[2] = [str(__second_white_masks), num_failed] + test_results['Second detector vanadium test:'] = [str(__second_white_masks), num_failed] add_masking(white_int, __second_white_masks, start_index, end_index) #TODO #add_masking(van_mask, __second_white_masks, start_index, end_index) @@ -129,6 +129,7 @@ def diagnose(white_int,**kwargs): maskZero, zero_count_failures = FindDetectorsOutsideLimits(InputWorkspace=parser.sample_counts,\ StartWorkspaceIndex=start_index, EndWorkspaceIndex=end_index,\ LowThreshold=1e-10, HighThreshold=1e100) + test_results['Zero total count sample check:'] = [str(maskZero),zero_count_failures] add_masking(white_int, maskZero, start_index, end_index) DeleteWorkspace(maskZero) # @@ -138,18 +139,18 @@ def diagnose(white_int,**kwargs): add_masking(parser.background_int, white_int) __bkgd_mask, failures = do_background_test(parser.background_int, parser.samp_lo,\ parser.samp_hi, parser.samp_sig, parser.samp_zero, start_index, end_index) - test_results[3] = [str(__bkgd_mask), zero_count_failures + failures] + test_results['Background test:'] = [str(__bkgd_mask), zero_count_failures + failures] add_masking(white_int, __bkgd_mask, start_index, end_index) DeleteWorkspace(__bkgd_mask) # # Bleed test - # - if hasattr(parser, 'bleed_test') and parser.bleed_test: + # (bleed test in multirep mode calculated per TOF region) + if hasattr(parser, 'bleed_test') and parser.bleed_test and not PropertyManager.incident_energy.multirep_mode(): if not hasattr(parser, 'sample_run'): raise RuntimeError("Bleed test requested but the sample_run keyword has not been provided") __bleed_masks, failures = do_bleed_test(parser.sample_run, parser.bleed_maxrate, parser.bleed_pixels) - test_results[4] = [str(__bleed_masks), failures] + test_results['PSD Bleed test:'] = [str(__bleed_masks), failures] add_masking(white_int, __bleed_masks) DeleteWorkspace(__bleed_masks) # endif not hard_mask_only @@ -293,7 +294,7 @@ def normalise_background(background_int, white_int, second_white_int=None): """ if second_white_int is None: - #quetly divide background integral by white beam integral not reporting about possible 0 in + #quietly divide background integral by white beam integral not reporting about possible 0 in #wb integral (they will be removed by diag anyway) background_int = Divide(LHSWorkspace=background_int,RHSWorkspace=white_int,WarnOnZeroDivide='0') else: @@ -343,7 +344,7 @@ def do_bleed_test(sample_run, max_framerate, ignored_pixels): max_framerate - The maximum allowed framerate in a tube. If None, the instrument defaults are used. ignored_pixels - The number of central pixels to ignore. If None, the instrument defaults are used. """ - # NOTE: it was deployed on loaded workspace and now it works on normalized workspace. Is this acceptable? + #NOTE: Should be deployed on non-normalized workspace only! logger.notice('Running PSD bleed test') # Load the sample run if __Reducer__: # Try to use generic loader which would work with files or workspaces alike @@ -355,9 +356,9 @@ def do_bleed_test(sample_run, max_framerate, ignored_pixels): data_ws = sample_run.get_workspace() # this will load data if necessary ws_name = data_ws.name()+'_bleed' - if max_framerate is None: + if max_framerate is None: #get defaults max_framerate = float(data_ws.getInstrument().getNumberParameter('max-tube-framerate')[0]) - if ignored_pixels is None: + if ignored_pixels is None: #get defaults ignored_pixels = int(data_ws.getInstrument().getNumberParameter('num-ignored-pixels')[0]) else: # Make sure it is an int @@ -369,17 +370,17 @@ def do_bleed_test(sample_run, max_framerate, ignored_pixels): ws_name = lhs_names[0] else: ws_name = '__do_bleed__test' - # Check if all necessary logs present in the workspace,as nxs workspace log names are diffferent + # Check if all necessary logs present in the workspace,as nxs workspace log names are different # from a raw file workspace logs. try: - nFrames= data_ws.run().getLogData('goodfrm').value + nFrames= data_ws.getRun().getLogData('goodfrm').value except RuntimeError: try: - nFrames = len(data_ws.run().getLogData('good_frame_log').value) - AddSampleLog(Workspace=data_ws, LogName='goodfrm', LogText=str(nFrames), LogType='Number') + nFrames = data_ws.getRun().getLogData('good_frames').lastValue() + AddSampleLog(Workspace=data_ws, LogName='goodfrm', LogText=str(int(nFrames)), LogType='Number') except RuntimeError: - raise RuntimeError("""Can not run bleed test as no appropriate good frame log is found in the workspace: {0}\n - Disable bleed test by setting diag_bleed_test=False or add 'goodfrm' log value to the workspace\n"""\ + raise RuntimeError("Bleed test fails as no appropriate 'good_frames' or 'goodfrm' log is loaded with ws: {0}\n" + "Disable bleed test by setting diag_bleed_test=False or add 'goodfrm' log to the workspace\n"\ .format(data_ws.name())) @@ -396,52 +397,33 @@ def print_test_summary(test_results,test_name=None): Input: test_results - A list or tuple containing either the number of failed spectra or None indicating that the test was not run + IMPORTANT: The output of this function is used as + input for GUI, so the keys names, = sign and : are control + symbols of MantidWidgets->DiagResults method. """ - num_diags = 5 - if len(test_results) != num_diags: - raise ValueError("Invalid input for print_test_summary. A list of %d numbers is expected." % num_diags) - - tests_run=False - for failures in test_results: - if failures is not None: - tests_run = True - if tests_run == False: + if len(test_results) == 0: print "No tests have been run!" return - summary = ( - ['Hard mask:',test_results[0]], \ - ['First white beam test:',test_results[1]], \ - ['Second white beam test:',test_results[2]], \ - ['Background test:',test_results[3]], \ - ['PSD Bleed test :',test_results[4]] \ - ) if test_name == None: print '======== Diagnostic Test Summary ' else: print '======== Diagnostic Test Summary {0} '.format(test_name) - max_name_length = -1 - max_ws_length = -1 - for key in range(num_diags): - result = summary[key] - name_length = len(str(result[0])) - ws_length = len(str(result[1][0])) - if name_length > max_name_length: - max_name_length = name_length - if ws_length > max_ws_length: - max_ws_length = ws_length - - max_name_length += 2 - max_ws_length += 2 - for result in summary: - test_name = str(result[0]) - workspace = str(result[1][0]) - nfailed = str(result[1][1]) - line = test_name + ' '*(max_name_length-len(test_name)) + \ - workspace + ' '*(max_ws_length-len(workspace)) + str(nfailed) - print line + max_test_len = 0 + max_ws_len = 0 + for t_name in test_results: + if len(t_name)>max_test_len: + max_test_len = len(t_name) + t_result = test_results[t_name] + if len(t_result[0])>max_ws_len : + max_ws_len = len(t_result[0]) + format_string = " {{0:<{0}}} {{1:<{1}}} {{2:>10}}".format(max_test_len,max_ws_len) + + for t_name in test_results: + t_result = test_results[t_name] + print format_string.format(t_name,t_result[0],t_result[1]) # Append a new line print '================================================================' print '' diff --git a/Code/Mantid/scripts/Inelastic/IndirectCommon.py b/Code/Mantid/scripts/Inelastic/IndirectCommon.py index 2f44373b804fb5707c1b4f93f908c8341e786c6d..7f9a29fa68eb4efb78234b5acad8c5a903b5d158 100644 --- a/Code/Mantid/scripts/Inelastic/IndirectCommon.py +++ b/Code/Mantid/scripts/Inelastic/IndirectCommon.py @@ -43,9 +43,11 @@ def getInstrRun(ws_name): raise RuntimeError("Could not find run number associated with workspace.") instrument = workspace.getInstrument().getName() - facility = config.getFacility() - instrument = facility.instrument(instrument).filePrefix(int(run_number)) - instrument = instrument.lower() + if instrument != '': + facility = config.getFacility() + instrument = facility.instrument(instrument).filePrefix(int(run_number)) + instrument = instrument.lower() + return instrument, run_number diff --git a/Code/Mantid/scripts/Interface/reduction_gui/widgets/sans/eqsans_instrument.py b/Code/Mantid/scripts/Interface/reduction_gui/widgets/sans/eqsans_instrument.py index 34738beaa8a52f362bf9fa4c83266c1ecdb8671b..e8070b2590b7a243d88d6a111061d9b6a462f598 100644 --- a/Code/Mantid/scripts/Interface/reduction_gui/widgets/sans/eqsans_instrument.py +++ b/Code/Mantid/scripts/Interface/reduction_gui/widgets/sans/eqsans_instrument.py @@ -235,6 +235,7 @@ class SANSInstrumentWidget(BaseWidget): | QtGui.QFileDialog.DontResolveSymlinks) if output_dir: self._summary.output_dir_edit.setText(output_dir) + self._settings.emit_key_value("OUTPUT_DIR", output_dir) def _tof_clicked(self, is_checked): self._summary.low_tof_edit.setEnabled(not is_checked) @@ -458,7 +459,7 @@ class SANSInstrumentWidget(BaseWidget): # Config Mask m.use_config_mask = self._summary.config_mask_chk.isChecked() - # Mask detector IDs + # Mask detector IDs m.use_mask_file = self._summary.mask_check.isChecked() m.mask_file = unicode(self._summary.mask_edit.text()) m.detector_ids = self._masked_detectors @@ -483,6 +484,7 @@ class SANSInstrumentWidget(BaseWidget): self._settings.data_output_dir = m.output_directory self._settings.emit_key_value("DARK_CURRENT", str(self._summary.dark_file_edit.text())) + self._settings.emit_key_value("OUTPUT_DIR", m.output_directory) return m def _show_help(self): diff --git a/Code/Mantid/scripts/Interface/reduction_gui/widgets/sans/stitcher.py b/Code/Mantid/scripts/Interface/reduction_gui/widgets/sans/stitcher.py index 322e7bad78da629d9019bb3564eb86e6fd71b1bc..6f74307917cd72561ae0515895227d76eecbff0c 100644 --- a/Code/Mantid/scripts/Interface/reduction_gui/widgets/sans/stitcher.py +++ b/Code/Mantid/scripts/Interface/reduction_gui/widgets/sans/stitcher.py @@ -40,7 +40,7 @@ class StitcherWidget(BaseWidget): settings = GeneralSettings() self._settings = settings - # Connect do UI data update + # Connect do UI data update self._settings.data_updated.connect(self._data_updated) self._low_q_data = None @@ -55,7 +55,7 @@ class StitcherWidget(BaseWidget): self._referenceID = 0 self._graph = "StitchedData" - self._output_dir = None + self._output_dir = self._settings.data_output_dir self._stitcher = None self._plotted = False diff --git a/Code/Mantid/scripts/test/DirectEnergyConversionTest.py b/Code/Mantid/scripts/test/DirectEnergyConversionTest.py index c8f735d78d24c63759e8d676865d68bf5dc98691..30215714ea48a958c9f2503ab07043c47ccaa3c0 100644 --- a/Code/Mantid/scripts/test/DirectEnergyConversionTest.py +++ b/Code/Mantid/scripts/test/DirectEnergyConversionTest.py @@ -475,6 +475,79 @@ class DirectEnergyConversionTest(unittest.TestCase): rez = CheckWorkspacesMatch(result[1],result2[1]) self.assertEqual(rez,'Success!') + def test_abs_multirep_with_bkg_and_bleed(self): + # create test workspace + run_monitors=CreateSampleWorkspace(Function='Multiple Peaks', NumBanks=4, BankPixelWidth=1,\ + NumEvents=100000, XUnit='Energy', XMin=3, XMax=200, BinWidth=0.1) + LoadInstrument(run_monitors,InstrumentName='MARI') + ConvertUnits(InputWorkspace='run_monitors', OutputWorkspace='run_monitors', Target='TOF') + run_monitors = mtd['run_monitors'] + tof = run_monitors.dataX(3) + tMin = tof[0] + tMax = tof[-1] + run = CreateSampleWorkspace( Function='Multiple Peaks',WorkspaceType='Event',NumBanks=8, BankPixelWidth=1,\ + NumEvents=100000, XUnit='TOF',xMin=tMin,xMax=tMax) + LoadInstrument(run,InstrumentName='MARI') + AddSampleLog(run,LogName='gd_prtn_chrg',LogText='1.',LogType='Number') + + # build "monovanadium" + mono = CloneWorkspace(run) + mono_monitors = CloneWorkspace(run_monitors) + + # build "White-beam" + wb_ws = Rebin(run,Params=[tMin,1,tMax],PreserveEvents=False) + + # build "second run" to ensure repeated execution + run2 = CloneWorkspace(run) + run2_monitors = CloneWorkspace(run_monitors) + + # Run multirep + tReducer = DirectEnergyConversion(run.getInstrument()) + tReducer.prop_man.run_diagnostics=True + tReducer.hard_mask_file=None + tReducer.map_file=None + tReducer.prop_man.check_background = True + tReducer.prop_man.background_range=[0.99*tMax,tMax] + tReducer.prop_man.monovan_mapfile=None + tReducer.save_format=None + tReducer.prop_man.normalise_method='monitor-2' + + tReducer.prop_man.bleed = True + tReducer.norm_mon_integration_range=[tMin,tMax] + + AddSampleLog(run,LogName='good_frames',LogText='1.',LogType='Number Series') + result = tReducer.convert_to_energy(wb_ws,run,[67.,122.],[-2,0.02,0.8],None,mono) + + self.assertEqual(len(result),2) + + ws1=result[0] + self.assertEqual(ws1.getAxis(0).getUnit().unitID(),'DeltaE') + x = ws1.readX(0) + self.assertAlmostEqual(x[0],-2*67.) + self.assertAlmostEqual(x[-1],0.8*67.) + + ws2=result[1] + self.assertEqual(ws2.getAxis(0).getUnit().unitID(),'DeltaE') + x = ws2.readX(0) + self.assertAlmostEqual(x[0],-2*122.) + self.assertAlmostEqual(x[-1],0.8*122.) + + # test another ws + # rename samples from previous workspace to avoid deleting them on current run + for ind,item in enumerate(result): + result[ind]=RenameWorkspace(item,OutputWorkspace='SampleRez#'+str(ind)) + # + AddSampleLog(run2,LogName='goodfrm',LogText='1',LogType='Number') + result2 = tReducer.convert_to_energy(None,run2) + + rez = CheckWorkspacesMatch(result[0],result2[0]) + self.assertEqual(rez,'Success!') + rez = CheckWorkspacesMatch(result[1],result2[1]) + self.assertEqual(rez,'Success!') + + if __name__=="__main__": - unittest.main() + test = DirectEnergyConversionTest('test_abs_multirep_with_bkg_and_bleed') + test.test_abs_multirep_with_bkg_and_bleed() + #unittest.main() diff --git a/Code/Mantid/scripts/test/DirectPropertyManagerTest.py b/Code/Mantid/scripts/test/DirectPropertyManagerTest.py index 3cde32203d62d3f31d2418f1448dbec9dd845d73..d66a64e42218484930968c785bd15b2d8b09f508 100644 --- a/Code/Mantid/scripts/test/DirectPropertyManagerTest.py +++ b/Code/Mantid/scripts/test/DirectPropertyManagerTest.py @@ -1,5 +1,5 @@ import os -#os.environ["PATH"] = r"c:/Mantid/Code/builds/br_master/bin/Release;" + os.environ["PATH"] +os.environ["PATH"] = r"c:/Mantid/Code/builds/br_master/bin/Release;" + os.environ["PATH"] from mantid.simpleapi import * from mantid import api import unittest @@ -871,7 +871,7 @@ class DirectPropertyManagerTest(unittest.TestCase): # verify if changed properties list does not change anything changed_prop = propman1.update_defaults_from_instrument(ws.getInstrument()) - self.assertEqual(len(changed_prop),4) + self.assertEqual(len(changed_prop),2) self.assertFalse(propman1.use_hard_mask_only) self.assertEqual(propman1.hard_mask_file,'a_hard_mask_file.msk') self.assertTrue(propman1.run_diagnostics) diff --git a/Code/Mantid/scripts/test/RunDescriptorTest.py b/Code/Mantid/scripts/test/RunDescriptorTest.py index 1a8956188d831f8528713b6652ad8d616819aebc..1641dd7473a4127973932757700fe3ec959433fb 100644 --- a/Code/Mantid/scripts/test/RunDescriptorTest.py +++ b/Code/Mantid/scripts/test/RunDescriptorTest.py @@ -541,6 +541,37 @@ class RunDescriptorTest(unittest.TestCase): real_fext=PropertyManager.sample_run.get_fext() self.assertEqual('.nxs',real_fext) + def test_change_normalization(self): + propman = self.prop_man + a_wksp=CreateSampleWorkspace(Function='Multiple Peaks',WorkspaceType='Event', + NumBanks=4, BankPixelWidth=1, NumEvents=100, XUnit='TOF', + XMin=2000, XMax=20000, BinWidth=1) + source_wksp = a_wksp/5. + propman.sample_run = source_wksp + self.assertRaises(RuntimeError,PropertyManager.sample_run.export_normalization,a_wksp) + + AddSampleLog(source_wksp,LogName='NormalizationFactor',LogText='5.',LogType='Number') + PropertyManager.sample_run.export_normalization(a_wksp) + + rez = CheckWorkspacesMatch(Workspace1=source_wksp,Workspace2=a_wksp,Tolerance=1.e-8) + self.assertEqual(rez,'Success!') + + # divide by 20 to get final normalization factor equal 100 (ws/(5*20)) + a_wksp/= 20 + AddSampleLog(a_wksp,LogName='NormalizationFactor',LogText='100',LogType='Number') + # export normalization by 5 + PropertyManager.sample_run.export_normalization(a_wksp) + rez = CheckWorkspacesMatch(Workspace1=source_wksp,Workspace2=a_wksp,Tolerance=1.e-6) + self.assertEqual(rez,'Success!') + self.assertAlmostEqual(a_wksp.getRun().getLogData('NormalizationFactor').value,5.) + + propman.sample_run = None + DeleteWorkspace(a_wksp) + + + + +