diff --git a/Framework/API/inc/MantidAPI/ExperimentInfo.h b/Framework/API/inc/MantidAPI/ExperimentInfo.h index f31cba3a94c58fd2c9fc117d937d649ae31e7486..04a28cd65fee13843f52eb0dcf894653c7a0673b 100644 --- a/Framework/API/inc/MantidAPI/ExperimentInfo.h +++ b/Framework/API/inc/MantidAPI/ExperimentInfo.h @@ -104,6 +104,8 @@ public: const Run &run() const; /// Writable version of the run object Run &mutableRun(); + void setSharedRun(Kernel::cow_ptr<Run> run); + /// Access a log for this experiment. Kernel::Property *getLog(const std::string &log) const; /// Access a single value from a log for this experiment. @@ -187,10 +189,6 @@ protected: boost::shared_ptr<ModeratorModel> m_moderatorModel; /// Description of the choppers for this experiment. std::list<boost::shared_ptr<ChopperModel>> m_choppers; - /// The information on the sample environment - boost::shared_ptr<Sample> m_sample; - /// The run information - boost::shared_ptr<Run> m_run; /// Parameters modifying the base instrument boost::shared_ptr<Geometry::ParameterMap> m_parmap; /// The base (unparametrized) instrument @@ -218,15 +216,18 @@ private: // Loads the xml from an instrument file with some basic error handling std::string loadInstrumentXML(const std::string &filename); + + /// The information on the sample environment + Kernel::cow_ptr<Sample> m_sample; + /// The run information + Kernel::cow_ptr<Run> m_run; + /// Detector grouping information mutable std::unordered_map<detid_t, size_t> m_det2group; void cacheDefaultDetectorGrouping() const; // Not thread-safe void invalidateAllSpectrumDefinitions(); mutable std::once_flag m_defaultDetectorGroupingCached; - /// Mutex to protect against cow_ptr copying - mutable std::recursive_mutex m_mutex; - mutable std::unique_ptr<Beamline::SpectrumInfo> m_spectrumInfo; mutable std::unique_ptr<SpectrumInfo> m_spectrumInfoWrapper; mutable std::mutex m_spectrumInfoMutex; diff --git a/Framework/API/inc/MantidAPI/WorkspaceFactory.h b/Framework/API/inc/MantidAPI/WorkspaceFactory.h index 4afc17aa358bb398ec64bb7098878ca9c4b18550..75d1b95df77630b56343a5b79360b1677fa5e8b6 100644 --- a/Framework/API/inc/MantidAPI/WorkspaceFactory.h +++ b/Framework/API/inc/MantidAPI/WorkspaceFactory.h @@ -82,10 +82,6 @@ public: MatrixWorkspace &child, const bool differentSize) const; - void initializeFromParentWithoutLogs(const MatrixWorkspace &parent, - MatrixWorkspace &child, - const bool differentSize) const; - /// Create a ITableWorkspace boost::shared_ptr<ITableWorkspace> createTable(const std::string &className = "TableWorkspace") const; diff --git a/Framework/API/src/ExperimentInfo.cpp b/Framework/API/src/ExperimentInfo.cpp index aad114140a74c02c5f3d0f3b7556509f6e6f6b48..3eb512910b36fe4eb1418b035d51e54ae03edf13 100644 --- a/Framework/API/src/ExperimentInfo.cpp +++ b/Framework/API/src/ExperimentInfo.cpp @@ -66,8 +66,7 @@ Kernel::Logger g_log("ExperimentInfo"); /** Constructor */ ExperimentInfo::ExperimentInfo() - : m_moderatorModel(), m_choppers(), m_sample(new Sample()), - m_run(new Run()), m_parmap(new ParameterMap()), + : m_moderatorModel(), m_choppers(), m_parmap(new ParameterMap()), sptr_instrument(new Instrument()) { m_parmap->setInstrument(sptr_instrument.get()); } @@ -91,7 +90,7 @@ ExperimentInfo::~ExperimentInfo() = default; */ void ExperimentInfo::copyExperimentInfoFrom(const ExperimentInfo *other) { m_sample = other->m_sample; - m_run = other->m_run->clone(); + m_run = other->m_run; this->setInstrument(other->getInstrument()); if (other->m_moderatorModel) m_moderatorModel = other->m_moderatorModel->clone(); @@ -602,30 +601,17 @@ ChopperModel &ExperimentInfo::chopperModel(const size_t index) const { */ const Sample &ExperimentInfo::sample() const { populateIfNotLoaded(); - std::lock_guard<std::recursive_mutex> lock(m_mutex); return *m_sample; } /** Get a reference to the Sample associated with this workspace. * This non-const method will copy the sample if it is shared between * more than one workspace, and the reference returned will be to the copy. -* Can ONLY be taken by reference! * @return reference to sample object */ Sample &ExperimentInfo::mutableSample() { populateIfNotLoaded(); - // Use a double-check for sharing so that we only - // enter the critical region if absolutely necessary - if (!m_sample.unique()) { - std::lock_guard<std::recursive_mutex> lock(m_mutex); - // Check again because another thread may have taken copy - // and dropped reference count since previous check - if (!m_sample.unique()) { - boost::shared_ptr<Sample> oldData = m_sample; - m_sample = boost::make_shared<Sample>(*oldData); - } - } - return *m_sample; + return m_sample.access(); } /** Get a constant reference to the Run object associated with this workspace. @@ -633,30 +619,22 @@ Sample &ExperimentInfo::mutableSample() { */ const Run &ExperimentInfo::run() const { populateIfNotLoaded(); - std::lock_guard<std::recursive_mutex> lock(m_mutex); return *m_run; } /** Get a reference to the Run object associated with this workspace. * This non-const method will copy the Run object if it is shared between * more than one workspace, and the reference returned will be to the copy. -* Can ONLY be taken by reference! * @return reference to Run object */ Run &ExperimentInfo::mutableRun() { populateIfNotLoaded(); - // Use a double-check for sharing so that we only - // enter the critical region if absolutely necessary - if (!m_run.unique()) { - std::lock_guard<std::recursive_mutex> lock(m_mutex); - // Check again because another thread may have taken copy - // and dropped reference count since previous check - if (!m_run.unique()) { - boost::shared_ptr<Run> oldData = m_run; - m_run = boost::make_shared<Run>(*oldData); - } - } - return *m_run; + return m_run.access(); +} + +/// Set the run object. Use in particular to clear run without copying old run. +void ExperimentInfo::setSharedRun(Kernel::cow_ptr<Run> run) { + m_run = std::move(run); } /** diff --git a/Framework/API/src/WorkspaceFactory.cpp b/Framework/API/src/WorkspaceFactory.cpp index 3f29b9c437032a8e04ffd6246cd999831e9bc021..88525dfa5d5b01926b840b8c6e748effe8e890b0 100644 --- a/Framework/API/src/WorkspaceFactory.cpp +++ b/Framework/API/src/WorkspaceFactory.cpp @@ -78,29 +78,20 @@ WorkspaceFactoryImpl::create(const MatrixWorkspace_const_sptr &parent, } /** Initialize a workspace from its parent - * This sets values such as instrument, units, sample, spectramap. + * This sets values such as title, instrument, units, sample, spectramap. * This does NOT copy any data. - * This does NOT copy any sample logs, i.e., Run object of the workspace won't - * be copied - * from its parent. - * @brief WorkspaceFactoryImpl::initializeFromParentWithoutLogs - * @param parent - * @param child - * @param differentSize + * + * @param parent :: the parent workspace + * @param child :: the child workspace + * @param differentSize :: A flag to indicate if the two workspace will be + *different sizes */ -void WorkspaceFactoryImpl::initializeFromParentWithoutLogs( +void WorkspaceFactoryImpl::initializeFromParent( const MatrixWorkspace &parent, MatrixWorkspace &child, const bool differentSize) const { child.setTitle(parent.getTitle()); child.setComment(parent.getComment()); - child.setInstrument(parent.getInstrument()); // This call also copies the - // SHARED POINTER to the - // parameter map - - // This call will (should) perform a COPY of the parameter map. - child.instrumentParameters(); - child.m_sample = parent.m_sample; - child.m_run = boost::make_shared<API::Run>(); + child.copyExperimentInfoFrom(&parent); child.setYUnit(parent.m_YUnit); child.setYUnitLabel(parent.m_YUnitLabel); child.setDistribution(parent.isDistribution()); @@ -144,22 +135,6 @@ void WorkspaceFactoryImpl::initializeFromParentWithoutLogs( } } -/** Initialize a workspace from its parent - * This sets values such as title, instrument, units, sample, spectramap. - * This does NOT copy any data. - * - * @param parent :: the parent workspace - * @param child :: the child workspace - * @param differentSize :: A flag to indicate if the two workspace will be - *different sizes - */ -void WorkspaceFactoryImpl::initializeFromParent( - const MatrixWorkspace &parent, MatrixWorkspace &child, - const bool differentSize) const { - initializeFromParentWithoutLogs(parent, child, differentSize); - child.m_run = parent.m_run; -} - /** Creates a new instance of the class with the given name, and allocates * memory for the arrays * @param className The name of the class you wish to create diff --git a/Framework/Algorithms/src/CropToComponent.cpp b/Framework/Algorithms/src/CropToComponent.cpp index 239cb9e95d8181babd577311dacbe786b7da90a9..42d0b5b62483a5d7a7bf11c9fcf29f5364435095 100644 --- a/Framework/Algorithms/src/CropToComponent.cpp +++ b/Framework/Algorithms/src/CropToComponent.cpp @@ -1,45 +1,27 @@ #include "MantidAPI/AlgorithmManager.h" #include "MantidAPI/MatrixWorkspace.h" #include "MantidAlgorithms/CropToComponent.h" -#include "MantidGeometry/IDetector.h" #include "MantidGeometry/Instrument.h" +#include "MantidGeometry/Instrument/ComponentInfo.h" #include "MantidKernel/ArrayProperty.h" +#include "MantidIndexing/Conversion.h" +#include "MantidIndexing/GlobalSpectrumIndex.h" +#include "MantidIndexing/IndexInfo.h" namespace { - -void getDetectors( - Mantid::API::MatrixWorkspace_sptr workspace, - const std::vector<std::string> &componentNames, - std::vector<Mantid::Geometry::IDetector_const_sptr> &detectors) { - auto instrument = workspace->getInstrument(); +std::vector<size_t> +getDetectorIndices(const Mantid::API::MatrixWorkspace &workspace, + const std::vector<std::string> &componentNames) { + const auto &compInfo = workspace.componentInfo(); + const auto instrument = workspace.getInstrument(); + std::vector<size_t> detIndices; for (const auto &componentName : componentNames) { - instrument->getDetectorsInBank(detectors, componentName); - } -} - -void getWorkspaceIndices( - Mantid::API::MatrixWorkspace_sptr workspace, - std::vector<Mantid::Geometry::IDetector_const_sptr> &detectors, - std::vector<size_t> &workspaceIndices) { - const auto numberOfDetectors = static_cast<int>(detectors.size()); - std::vector<Mantid::detid_t> detectorIds(numberOfDetectors); - - PARALLEL_FOR_NO_WSP_CHECK() - for (int index = 0; index < numberOfDetectors; ++index) { - auto det = detectors[index]; - detectorIds[index] = det->getID(); + const auto comp = instrument->getComponentByName(componentName); + const auto compIndex = compInfo.indexOf(comp->getComponentID()); + const auto indices = compInfo.detectorsInSubtree(compIndex); + detIndices.insert(detIndices.end(), indices.begin(), indices.end()); } - - // Get the corresponding workspace indices - auto detIdToWorkspaceIndexMap = - workspace->getDetectorIDToWorkspaceIndexMap(true); - PARALLEL_FOR_NO_WSP_CHECK() - for (int index = 0; index < numberOfDetectors; ++index) { - workspaceIndices[index] = detIdToWorkspaceIndexMap[detectorIds[index]]; - } - - // Sort the workspace indices - std::sort(workspaceIndices.begin(), workspaceIndices.end()); + return detIndices; } } @@ -99,12 +81,13 @@ void CropToComponent::exec() { getProperty("InputWorkspace"); // Get all detectors - std::vector<Mantid::Geometry::IDetector_const_sptr> detectors; - getDetectors(inputWorkspace, componentNames, detectors); + const auto &detectorIndices = + getDetectorIndices(*inputWorkspace, componentNames); // Get the corresponding workspace indices from the detectors - std::vector<size_t> workspaceIndices(detectors.size()); - getWorkspaceIndices(inputWorkspace, detectors, workspaceIndices); + const auto &workspaceIndices = + inputWorkspace->indexInfo().globalSpectrumIndicesFromDetectorIndices( + detectorIndices); // Run ExtractSpectra in order to obtain the cropped workspace auto extract_alg = Mantid::API::AlgorithmManager::Instance().createUnmanaged( @@ -113,7 +96,8 @@ void CropToComponent::exec() { extract_alg->initialize(); extract_alg->setProperty("InputWorkspace", inputWorkspace); extract_alg->setProperty("OutputWorkspace", "dummy"); - extract_alg->setProperty("WorkspaceIndexList", workspaceIndices); + extract_alg->setProperty("WorkspaceIndexList", + Indexing::castVector<size_t>(workspaceIndices)); extract_alg->execute(); Mantid::API::MatrixWorkspace_sptr outputWorkspace = extract_alg->getProperty("OutputWorkspace"); diff --git a/Framework/Algorithms/src/FilterEvents.cpp b/Framework/Algorithms/src/FilterEvents.cpp index 936759f7cd0cd4699be40d6da404bf941cc301b1..d92c13eaeaac35f67096b5c0968f56bfc1083a58 100644 --- a/Framework/Algorithms/src/FilterEvents.cpp +++ b/Framework/Algorithms/src/FilterEvents.cpp @@ -1092,7 +1092,9 @@ void FilterEvents::createOutputWorkspaces() { } boost::shared_ptr<EventWorkspace> optws = - createWithoutLogs<DataObjects::EventWorkspace>(*m_eventWS); + create<EventWorkspace>(*m_eventWS); + // Clear Run without copying first. + optws->setSharedRun(Kernel::make_cow<Run>()); m_outputWorkspacesMap.emplace(wsgroup, optws); // Add information, including title and comment, to output workspace @@ -1202,7 +1204,9 @@ void FilterEvents::createOutputWorkspacesMatrixCase() { // create new workspace from input EventWorkspace and all the sample logs // are copied to the new one boost::shared_ptr<EventWorkspace> optws = - createWithoutLogs<DataObjects::EventWorkspace>(*m_eventWS); + create<EventWorkspace>(*m_eventWS); + // Clear Run without copying first. + optws->setSharedRun(Kernel::make_cow<Run>()); m_outputWorkspacesMap.emplace(wsgroup, optws); // TODO/ISSUE/NOW - How about comment and info similar to @@ -1292,7 +1296,9 @@ void FilterEvents::createOutputWorkspacesTableSplitterCase() { // create new workspace boost::shared_ptr<EventWorkspace> optws = - createWithoutLogs<DataObjects::EventWorkspace>(*m_eventWS); + create<EventWorkspace>(*m_eventWS); + // Clear Run without copying first. + optws->setSharedRun(Kernel::make_cow<Run>()); m_outputWorkspacesMap.emplace(wsgroup, optws); // TODO/NOW/ISSUE -- How about comment and info? diff --git a/Framework/Algorithms/test/CropToComponentTest.h b/Framework/Algorithms/test/CropToComponentTest.h index 7fe5592e554f0ca84fba0bbaf88ba2239366a945..7f3f1d58e7d5d457cbb8857c466499963f11aa01 100644 --- a/Framework/Algorithms/test/CropToComponentTest.h +++ b/Framework/Algorithms/test/CropToComponentTest.h @@ -91,6 +91,9 @@ public: auto inputWorkspace = getSampleWorkspace(numberOfBanks, numberOfPixelsPerBank); std::vector<std::string> componentNames = {"bank3"}; + // Clearing some IDs in bank2 should not cause issues, compare + // test_throws_if_no_spectrum_for_detector. + inputWorkspace->getSpectrum(9).clearDetectorIDs(); // Act Mantid::Algorithms::CropToComponent crop; @@ -110,6 +113,29 @@ public: std::iota(expectedIDs.begin(), expectedIDs.end(), 27); } + void test_throws_if_no_spectrum_for_detector() { + // Arrange + int numberOfBanks = 4; + int numberOfPixelsPerBank = 3; + + auto inputWorkspace = + getSampleWorkspace(numberOfBanks, numberOfPixelsPerBank); + std::vector<std::string> componentNames = {"bank3"}; + // Clear some IDs in bank3. + inputWorkspace->getSpectrum(18).clearDetectorIDs(); + + // Act + Mantid::Algorithms::CropToComponent crop; + crop.setChild(true); + crop.initialize(); + crop.setProperty("InputWorkspace", inputWorkspace); + crop.setProperty("OutputWorkspace", "dummy"); + crop.setProperty("ComponentNames", componentNames); + TS_ASSERT_THROWS_EQUALS( + crop.execute(), const std::runtime_error &e, std::string(e.what()), + "Some of the requested detectors do not have a corresponding spectrum"); + } + void test_that_incorrect_component_name_is_not_accepeted() { // Arrange int numberOfBanks = 4; diff --git a/Framework/CurveFitting/CMakeLists.txt b/Framework/CurveFitting/CMakeLists.txt index 154f38ab4e310a0482a1541e4fd09ac1d4e0020c..18aa84dbcfa919e253f8369bb664e7b6643adedf 100644 --- a/Framework/CurveFitting/CMakeLists.txt +++ b/Framework/CurveFitting/CMakeLists.txt @@ -32,14 +32,12 @@ set ( SRC_FILES src/CostFunctions/CostFuncUnweightedLeastSquares.cpp src/FitMW.cpp src/FuncMinimizers/BFGS_Minimizer.cpp - src/FuncMinimizers/DTRSMinimizer.cpp src/FuncMinimizers/DampedGaussNewtonMinimizer.cpp src/FuncMinimizers/DerivMinimizer.cpp src/FuncMinimizers/FABADAMinimizer.cpp src/FuncMinimizers/FRConjugateGradientMinimizer.cpp src/FuncMinimizers/LevenbergMarquardtMDMinimizer.cpp src/FuncMinimizers/LevenbergMarquardtMinimizer.cpp - src/FuncMinimizers/MoreSorensenMinimizer.cpp src/FuncMinimizers/PRConjugateGradientMinimizer.cpp src/FuncMinimizers/SimplexMinimizer.cpp src/FuncMinimizers/SteepestDescentMinimizer.cpp @@ -196,14 +194,12 @@ set ( INC_FILES inc/MantidCurveFitting/FortranMatrix.h inc/MantidCurveFitting/FortranVector.h inc/MantidCurveFitting/FuncMinimizers/BFGS_Minimizer.h - inc/MantidCurveFitting/FuncMinimizers/DTRSMinimizer.h inc/MantidCurveFitting/FuncMinimizers/DampedGaussNewtonMinimizer.h inc/MantidCurveFitting/FuncMinimizers/DerivMinimizer.h inc/MantidCurveFitting/FuncMinimizers/FABADAMinimizer.h inc/MantidCurveFitting/FuncMinimizers/FRConjugateGradientMinimizer.h inc/MantidCurveFitting/FuncMinimizers/LevenbergMarquardtMDMinimizer.h inc/MantidCurveFitting/FuncMinimizers/LevenbergMarquardtMinimizer.h - inc/MantidCurveFitting/FuncMinimizers/MoreSorensenMinimizer.h inc/MantidCurveFitting/FuncMinimizers/PRConjugateGradientMinimizer.h inc/MantidCurveFitting/FuncMinimizers/SimplexMinimizer.h inc/MantidCurveFitting/FuncMinimizers/SteepestDescentMinimizer.h @@ -356,16 +352,15 @@ set ( TEST_FILES FortranMatrixTest.h FortranVectorTest.h FuncMinimizers/BFGSTest.h - FuncMinimizers/DTRSMinimizerTest.h FuncMinimizers/DampedGaussNewtonMinimizerTest.h FuncMinimizers/ErrorMessagesTest.h FuncMinimizers/FABADAMinimizerTest.h FuncMinimizers/FRConjugateGradientTest.h FuncMinimizers/LevenbergMarquardtMDTest.h FuncMinimizers/LevenbergMarquardtTest.h - FuncMinimizers/MoreSorensenTest.h FuncMinimizers/PRConjugateGradientTest.h FuncMinimizers/SimplexTest.h + FuncMinimizers/TrustRegionMinimizerTest.h FunctionDomain1DSpectrumCreatorTest.h FunctionFactoryConstraintTest.h FunctionParameterDecoratorFitTest.h diff --git a/Framework/CurveFitting/inc/MantidCurveFitting/FuncMinimizers/DTRSMinimizer.h b/Framework/CurveFitting/inc/MantidCurveFitting/FuncMinimizers/DTRSMinimizer.h deleted file mode 100644 index d0e9051cc3f5ce461583ce896a2de9ed42e67d8e..0000000000000000000000000000000000000000 --- a/Framework/CurveFitting/inc/MantidCurveFitting/FuncMinimizers/DTRSMinimizer.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef MANTID_CURVEFITTING_DTRSMINIMIZER_H_ -#define MANTID_CURVEFITTING_DTRSMINIMIZER_H_ - -#include "MantidCurveFitting/FuncMinimizers/TrustRegionMinimizer.h" - -namespace Mantid { -namespace CurveFitting { -namespace FuncMinimisers { - -/** A GALAHAD trust region minimizer. - - Copyright © 2009 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge - National Laboratory & European Spallation Source - - 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/>. - - File change history is stored at: <https://github.com/mantidproject/mantid>. - Code Documentation is available at: <http://doxygen.mantidproject.org> -*/ -class DLLExport DTRSMinimizer : public TrustRegionMinimizer { -public: - /// constructor and destructor - DTRSMinimizer(); - /// Name of the minimizer. - std::string name() const override; - -private: - void calculateStep(const DoubleFortranMatrix &J, const DoubleFortranVector &f, - const DoubleFortranMatrix &hf, - const DoubleFortranVector &g, double Delta, - DoubleFortranVector &d, double &normd, - const NLLS::nlls_options &options) override; - - void solveDtrs(const DoubleFortranMatrix &J, const DoubleFortranVector &f, - const DoubleFortranMatrix &hf, double Delta, - DoubleFortranVector &d, double &normd, - const NLLS::nlls_options &options); - - // Used for calculating step - DoubleFortranMatrix m_A, m_ev; - DoubleFortranVector m_ew, m_v, m_v_trans, m_d_trans; - NLLS::all_eig_symm_work m_all_eig_symm_ws; - DoubleFortranVector m_scale; -}; - -} // namespace FuncMinimisers -} // namespace CurveFitting -} // namespace Mantid - -#endif /*MANTID_CURVEFITTING_DTRSMINIMIZER_H_*/ diff --git a/Framework/CurveFitting/inc/MantidCurveFitting/FuncMinimizers/MoreSorensenMinimizer.h b/Framework/CurveFitting/inc/MantidCurveFitting/FuncMinimizers/MoreSorensenMinimizer.h deleted file mode 100644 index 63bddd5fea8f084195d37795b35779ed2b4f4615..0000000000000000000000000000000000000000 --- a/Framework/CurveFitting/inc/MantidCurveFitting/FuncMinimizers/MoreSorensenMinimizer.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef MANTID_CURVEFITTING_MORESORENSENMINIMIZER_H_ -#define MANTID_CURVEFITTING_MORESORENSENMINIMIZER_H_ - -//---------------------------------------------------------------------- -// Includes -//---------------------------------------------------------------------- -#include "MantidCurveFitting/FuncMinimizers/TrustRegionMinimizer.h" - -namespace Mantid { -namespace CurveFitting { -namespace FuncMinimisers { - -/** A More-Sorensen trust region minimizer. - - Copyright © 2009 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge - National Laboratory & European Spallation Source - - 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/>. - - File change history is stored at: <https://github.com/mantidproject/mantid>. - Code Documentation is available at: <http://doxygen.mantidproject.org> -*/ -class DLLExport MoreSorensenMinimizer : public TrustRegionMinimizer { -public: - /// constructor and destructor - MoreSorensenMinimizer(); - /// Name of the minimizer. - std::string name() const override; - -private: - void calculateStep(const DoubleFortranMatrix &J, const DoubleFortranVector &f, - const DoubleFortranMatrix &hf, - const DoubleFortranVector &g, double Delta, - DoubleFortranVector &d, double &normd, - const NLLS::nlls_options &options) override; - - void solveSubproblem(const DoubleFortranMatrix &J, - const DoubleFortranVector &f, - const DoubleFortranMatrix &hf, double Delta, - DoubleFortranVector &d, double &nd, - const NLLS::nlls_options &options); - - bool getPdShift(double &sigma, DoubleFortranVector &d, - const NLLS::nlls_options &options); - - // Used for calculating step - DoubleFortranMatrix m_A, m_LtL, m_AplusSigma; - DoubleFortranVector m_v, m_q, m_y1; - NLLS::min_eig_symm_work m_min_eig_symm_ws; - DoubleFortranVector m_scale; -}; - -} // namespace FuncMinimisers -} // namespace CurveFitting -} // namespace Mantid - -#endif /*MANTID_CURVEFITTING_MORESORENSENMINIMIZER_H_*/ diff --git a/Framework/CurveFitting/inc/MantidCurveFitting/FuncMinimizers/TrustRegionMinimizer.h b/Framework/CurveFitting/inc/MantidCurveFitting/FuncMinimizers/TrustRegionMinimizer.h index 753bd294192d6f29fdf85aba9ba54219b1077f20..f971fe2e1b7a2ebe11d374b1556ad5e602bc3ac3 100644 --- a/Framework/CurveFitting/inc/MantidCurveFitting/FuncMinimizers/TrustRegionMinimizer.h +++ b/Framework/CurveFitting/inc/MantidCurveFitting/FuncMinimizers/TrustRegionMinimizer.h @@ -14,7 +14,7 @@ namespace Mantid { namespace CurveFitting { namespace FuncMinimisers { -/** A base class for least squares trust region minimizers. +/** Trust Region minimizer class using the DTRS method of GALAHAD. Copyright © 2009 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge National Laboratory & European Spallation Source @@ -48,6 +48,8 @@ public: bool iterate(size_t) override; /// Return current value of the cost function double costFunctionVal() override; + /// Name of the minimizer. + std::string name() const override; private: /// Evaluate the fitting function and calculate the residuals. @@ -58,12 +60,10 @@ private: void evalHF(const DoubleFortranVector &x, const DoubleFortranVector &f, DoubleFortranMatrix &h) const; /// Find a correction vector to the parameters. - virtual void calculateStep(const DoubleFortranMatrix &J, - const DoubleFortranVector &f, - const DoubleFortranMatrix &hf, - const DoubleFortranVector &g, double Delta, - DoubleFortranVector &d, double &normd, - const NLLS::nlls_options &options) = 0; + void calculateStep(const DoubleFortranMatrix &J, const DoubleFortranVector &f, + const DoubleFortranMatrix &hf, double Delta, + DoubleFortranVector &d, double &normd, + const NLLS::nlls_options &options); /// Stored cost function boost::shared_ptr<CostFunctions::CostFuncLeastSquares> m_leastSquares; @@ -81,6 +81,12 @@ private: NLLS::nlls_inform m_inform; /// Temporary and helper objects NLLS::NLLS_workspace m_workspace; + + // Used for calculating step in DTRS method + DoubleFortranMatrix m_A, m_ev; + DoubleFortranVector m_ew, m_v, m_v_trans, m_d_trans; + NLLS::all_eig_symm_work m_all_eig_symm_ws; + DoubleFortranVector m_scale; }; } // namespace FuncMinimisers diff --git a/Framework/CurveFitting/src/FuncMinimizers/DTRSMinimizer.cpp b/Framework/CurveFitting/src/FuncMinimizers/DTRSMinimizer.cpp deleted file mode 100644 index b2841bb337973c2bda5d657f514f6cd5ebe998c6..0000000000000000000000000000000000000000 --- a/Framework/CurveFitting/src/FuncMinimizers/DTRSMinimizer.cpp +++ /dev/null @@ -1,1005 +0,0 @@ -// This code was originally translated from Fortran code on -// https://ccpforge.cse.rl.ac.uk/gf/project/ral_nlls June 2016 -//---------------------------------------------------------------------- -// Includes -//---------------------------------------------------------------------- -#include "MantidCurveFitting/FuncMinimizers/DTRSMinimizer.h" -#include "MantidCurveFitting/RalNlls/TrustRegion.h" -#include "MantidAPI/FuncMinimizerFactory.h" - -#include <algorithm> -#include <limits> - -namespace Mantid { -namespace CurveFitting { -namespace FuncMinimisers { - -// clang-format off -///@cond nodoc -DECLARE_FUNCMINIMIZER(DTRSMinimizer, Trust Region) -///@endcond -// clang-format on - -DTRSMinimizer::DTRSMinimizer() : TrustRegionMinimizer() {} - -/** Name of the minimizer. - */ -std::string DTRSMinimizer::name() const { return "Trust Region"; } - -namespace { - -const double HUGEST = std::numeric_limits<double>::max(); -const double EPSILON_MCH = std::numeric_limits<double>::epsilon(); -const double LARGEST = HUGEST; -const double LOWER_DEFAULT = -0.5 * LARGEST; -const double UPPER_DEFAULT = LARGEST; -const double POINT4 = 0.4; -const double ZERO = 0.0; -const double ONE = 1.0; -const double TWO = 2.0; -const double THREE = 3.0; -const double FOUR = 4.0; -const double SIX = 6.0; -const double HALF = 0.5; -const double ONESIXTH = ONE / SIX; -const double SIXTH = ONESIXTH; -const double ONE_THIRD = ONE / THREE; -const double TWO_THIRDS = TWO / THREE; -const double THREE_QUARTERS = 0.75; -const double TWENTY_FOUR = 24.0; -const int MAX_DEGREE = 3; -const int HISTORY_MAX = 100; -const double TEN_EPSILON_MCH = 10.0 * EPSILON_MCH; -const double ROOTS_TOL = TEN_EPSILON_MCH; -const double INFINITE_NUMBER = HUGEST; - -/** Replacement for FORTRAN's SIGN intrinsic function - */ -inline double sign(double x, double y) { return y >= 0.0 ? fabs(x) : -fabs(x); } - -//! - - - - - - - - - - - - - - - - - - - - - - - -//! control derived type with component defaults -//! - - - - - - - - - - - - - - - - - - - - - - - -struct dtrs_control_type { - //! maximum degree of Taylor approximant allowed - int taylor_max_degree = 3; - - //! any entry of H that is smaller than h_min * MAXVAL( H ) we be treated as - // zero - double h_min = EPSILON_MCH; - - //! lower and upper bounds on the multiplier, if known - double lower = LOWER_DEFAULT; - double upper = UPPER_DEFAULT; - - //! stop when | ||x|| - radius | <= - //! max( stop_normal * radius, stop_absolute_normal ) - double stop_normal = EPSILON_MCH; - double stop_absolute_normal = EPSILON_MCH; - - //! is the solution is REQUIRED to lie on the boundary (i.e., is the - // constraint - //! an equality)? - bool equality_problem = false; -}; - -//! - - - - - - - - - - - - - - - - - - - - - - - - -//! history derived type with component defaults -//! - - - - - - - - - - - - - - - - - - - - - - - - -struct dtrs_history_type { - // - //! value of lambda - double lambda = 0.0; - - //! corresponding value of ||x(lambda)||_M - double x_norm = 0.0; -}; - -//! - - - - - - - - - - - - - - - - - - - - - - - -//! inform derived type with component defaults -//! - - - - - - - - - - - - - - - - - - - - - - - -struct dtrs_inform_type { - // - - //! the number of (||x||_M,lambda) pairs in the history - int len_history = 0; - - //! the value of the quadratic function - double obj = HUGEST; - - //! the M-norm of x, ||x||_M - double x_norm = 0.0; - - //! the Lagrange multiplier corresponding to the trust-region constraint - double multiplier = 0.0; - - //! a lower bound max(0,-lambda_1), where lambda_1 is the left-most - //! eigenvalue of (H,M) - double pole = 0.0; - - //! has the hard case occurred? - bool hard_case = false; - - //! history information - std::vector<dtrs_history_type> history; -}; - -/** Get the largest of the four values. - * @param a :: Value number 1. - * @param b :: Value number 2. - * @param c :: Value number 3. - * @param d :: Value number 4. - */ -double biggest(double a, double b, double c, double d) { - return std::max(std::max(a, b), std::max(c, d)); -} - -/** Get the largest of the three values. - * @param a :: Value number 1. - * @param b :: Value number 2. - * @param c :: Value number 3. - */ -double biggest(double a, double b, double c) { - return std::max(std::max(a, b), c); -} - -/** Find the largest by absolute value element of a vector. - * @param v :: The searched vector. - */ -double maxAbsVal(const DoubleFortranVector &v) { - auto p = v.indicesOfMinMaxElements(); - return std::max(fabs(v.get(p.first)), fabs(v.get(p.second))); -} - -/** Find the minimum and maximum elements of a vector. - * @param v :: The searched vector. - * @returns :: A pair of doubles where the first is the minimum and - * the second is the maxumum. - */ -std::pair<double, double> minMaxValues(const DoubleFortranVector &v) { - auto p = v.indicesOfMinMaxElements(); - return std::make_pair(v.get(p.first), v.get(p.second)); -} - -/** Compute the 2-norm of a vector which is a square root of the - * sum of squares of its elements. - * @param v :: The vector. - */ -double twoNorm(const DoubleFortranVector &v) { - if (v.size() == 0) - return 0.0; - return gsl_blas_dnrm2(v.gsl()); -} - -/** Get the dot-product of two vectors of the same size. - * @param v1 :: The first vector. - * @param v2 :: The second vector. - */ -double dotProduct(const DoubleFortranVector &v1, - const DoubleFortranVector &v2) { - return v1.dot(v2); -} - -/** Find the maximum element in the first n elements of a vector. - * @param v :: The vector. - * @param n :: The number of elements to examine. - */ -double maxVal(const DoubleFortranVector &v, int n) { - double res = std::numeric_limits<double>::lowest(); - for (int i = 1; i <= n; ++i) { - auto val = v(i); - if (val > res) { - res = val; - } - } - return res; -} - -/** Find the number and values of real roots of the quadratic equation - * - * a2 * x**2 + a1 * x + a0 = 0 - * - * where a0, a1 and a2 are real - * @param a0 :: The free coefficient. - * @param a1 :: The coefficient at the linear term. - * @param a2 :: The coefficient at the quadratic term. - * @param tol :: A tolerance for comparing doubles. - * @param nroots :: The output number of real roots. - * @param root1 :: The first real root if nroots > 0. - * @param root2 :: The second real root if nroots = 2. - */ -void rootsQuadratic(double a0, double a1, double a2, double tol, int &nroots, - double &root1, double &root2) { - - auto rhs = tol * a1 * a1; - if (fabs(a0 * a2) > rhs) { // really is quadratic - root2 = a1 * a1 - FOUR * a2 * a0; - if (fabs(root2) <= pow(EPSILON_MCH * a1, 2)) { // numerical double root - nroots = 2; - root1 = -HALF * a1 / a2; - root2 = root1; - } else if (root2 < ZERO) { // complex not real roots - nroots = 0; - root1 = ZERO; - root2 = ZERO; - } else { // distint real roots - auto d = -HALF * (a1 + sign(sqrt(root2), a1)); - nroots = 2; - root1 = d / a2; - root2 = a0 / d; - if (root1 > root2) { - d = root1; - root1 = root2; - root2 = d; - } - } - } else if (a2 == ZERO) { - if (a1 == ZERO) { - if (a0 == ZERO) { // the function is zero - nroots = 1; - root1 = ZERO; - root2 = ZERO; - } else { // the function is constant - nroots = 0; - root1 = ZERO; - root2 = ZERO; - } - } else { // the function is linear - nroots = 1; - root1 = -a0 / a1; - root2 = ZERO; - } - } else { // very ill-conditioned quadratic - nroots = 2; - if (-a1 / a2 > ZERO) { - root1 = ZERO; - root2 = -a1 / a2; - } else { - root1 = -a1 / a2; - root2 = ZERO; - } - } - - // perfom a Newton iteration to ensure that the roots are accurate - - if (nroots >= 1) { - auto p = (a2 * root1 + a1) * root1 + a0; - auto pprime = TWO * a2 * root1 + a1; - if (pprime != ZERO) { - root1 = root1 - p / pprime; - } - if (nroots == 2) { - p = (a2 * root2 + a1) * root2 + a0; - pprime = TWO * a2 * root2 + a1; - if (pprime != ZERO) { - root2 = root2 - p / pprime; - } - } - } -} - -/** Find the number and values of real roots of the cubic equation - * - * a3 * x**3 + a2 * x**2 + a1 * x + a0 = 0 - * - * where a0, a1, a2 and a3 are real - * @param a0 :: The free coefficient. - * @param a1 :: The coefficient at the linear term. - * @param a2 :: The coefficient at the quadratic term. - * @param a3 :: The coefficient at the cubic term. - * @param tol :: A tolerance for comparing doubles. - * @param nroots :: The output number of real roots. - * @param root1 :: The first real root. - * @param root2 :: The second real root if nroots > 1. - * @param root3 :: The third real root if nroots == 3. - */ -void rootsCubic(double a0, double a1, double a2, double a3, double tol, - int &nroots, double &root1, double &root2, double &root3) { - - // Check to see if the cubic is actually a quadratic - if (a3 == ZERO) { - rootsQuadratic(a0, a1, a2, tol, nroots, root1, root2); - root3 = INFINITE_NUMBER; - return; - } - - // Deflate the polnomial if the trailing coefficient is zero - if (a0 == ZERO) { - root1 = ZERO; - rootsQuadratic(a1, a2, a3, tol, nroots, root2, root3); - nroots = nroots + 1; - return; - } - - // 1. Use Nonweiler's method (CACM 11:4, 1968, pp269) - - double c0 = a0 / a3; - double c1 = a1 / a3; - double c2 = a2 / a3; - - double s = c2 / THREE; - double t = s * c2; - double b = 0.5 * (s * (TWO_THIRDS * t - c1) + c0); - t = (t - c1) / THREE; - double c = t * t * t; - double d = b * b - c; - - // 1 real + 2 equal real or 2 complex roots - if (d >= ZERO) { - d = pow(sqrt(d) + fabs(b), ONE_THIRD); - if (d != ZERO) { - if (b > ZERO) { - b = -d; - } else { - b = d; - } - c = t / b; - } - d = sqrt(THREE_QUARTERS) * (b - c); - b = b + c; - c = -0.5 * b - s; - root1 = b - s; - if (d == ZERO) { - nroots = 3; - root2 = c; - root3 = c; - } else { - nroots = 1; - } - } else { // 3 real roots - if (b == ZERO) { - d = TWO_THIRDS * atan(ONE); - } else { - d = atan(sqrt(-d) / fabs(b)) / THREE; - } - if (b < ZERO) { - b = TWO * sqrt(t); - } else { - b = -TWO * sqrt(t); - } - c = cos(d) * b; - t = -sqrt(THREE_QUARTERS) * sin(d) * b - HALF * c; - d = -t - c - s; - c = c - s; - t = t - s; - if (fabs(c) > fabs(t)) { - root3 = c; - } else { - root3 = t; - t = c; - } - if (fabs(d) > fabs(t)) { - root2 = d; - } else { - root2 = t; - t = d; - } - root1 = t; - nroots = 3; - } - - // reorder the roots - - if (nroots == 3) { - if (root1 > root2) { - double a = root2; - root2 = root1; - root1 = a; - } - if (root2 > root3) { - double a = root3; - if (root1 > root3) { - a = root1; - root1 = root3; - } - root3 = root2; - root2 = a; - } - } - - // perfom a Newton iteration to ensure that the roots are accurate - double p = ((a3 * root1 + a2) * root1 + a1) * root1 + a0; - double pprime = (THREE * a3 * root1 + TWO * a2) * root1 + a1; - if (pprime != ZERO) { - root1 = root1 - p / pprime; - // p = ((a3 * root1 + a2) * root1 + a1) * root1 + a0; // never used - } - - if (nroots == 3) { - p = ((a3 * root2 + a2) * root2 + a1) * root2 + a0; - pprime = (THREE * a3 * root2 + TWO * a2) * root2 + a1; - if (pprime != ZERO) { - root2 = root2 - p / pprime; - // p = ((a3 * root2 + a2) * root2 + a1) * root2 + a0; // never used - } - - p = ((a3 * root3 + a2) * root3 + a1) * root3 + a0; - pprime = (THREE * a3 * root3 + TWO * a2) * root3 + a1; - if (pprime != ZERO) { - root3 = root3 - p / pprime; - // p = ((a3 * root3 + a2) * root3 + a1) * root3 + a0; // never used - } - } -} - -/** Compute pi_beta = ||x||^beta and its derivatives - * Extracted wholesale from module RAL_NLLS_RQS - * - * @param max_order :: Maximum order of derivative. - * @param beta :: Power. - * @param x_norm2 :: (0) value of ||x||^2, - * (i) ith derivative of ||x||^2, i = 1, max_order - * @param pi_beta :: (0) value of ||x||^beta, - * (i) ith derivative of ||x||^beta, i = 1, max_order - */ -void dtrsPiDerivs(int max_order, double beta, - const DoubleFortranVector &x_norm2, - DoubleFortranVector &pi_beta) { - double hbeta = HALF * beta; - pi_beta(0) = pow(x_norm2(0), hbeta); - pi_beta(1) = hbeta * (pow(x_norm2(0), (hbeta - ONE))) * x_norm2(1); - if (max_order == 1) - return; - pi_beta(2) = hbeta * (pow(x_norm2(0), (hbeta - TWO))) * - ((hbeta - ONE) * pow(x_norm2(1), 2) + x_norm2(0) * x_norm2(2)); - if (max_order == 2) - return; - pi_beta(3) = hbeta * (pow(x_norm2(0), (hbeta - THREE))) * - (x_norm2(3) * pow(x_norm2(0), 2) + - (hbeta - ONE) * (THREE * x_norm2(0) * x_norm2(1) * x_norm2(2) + - (hbeta - TWO) * pow(x_norm2(1), 3))); -} - -/** Set initial values for the TRS control parameters - * - * @param control :: A structure containing control information. - */ -void dtrsInitialize(dtrs_control_type &control) { - control.stop_normal = pow(EPSILON_MCH, 0.75); - control.stop_absolute_normal = pow(EPSILON_MCH, 0.75); -} - -/** Solve the trust-region subproblem - * - * minimize 1/2 <x, H x> + <c, x> + f - * subject to ||x||_2 <= radius or ||x||_2 = radius - * - * where H is diagonal, using a secular iteration - * - * @param n :: The number of unknowns. - * @param radius :: The trust-region radius. - * @param f :: The value of constant term for the quadratic function - * @param c :: A vector of values for the linear term c. - * @param h :: A vector of values for the diagonal matrix H. - * @param x :: The required solution vector x. - * @param control :: A structure containing control information. - * @param inform :: A structure containing information. - */ -void dtrsSolveMain(int n, double radius, double f, const DoubleFortranVector &c, - const DoubleFortranVector &h, DoubleFortranVector &x, - const dtrs_control_type &control, dtrs_inform_type &inform) { - - // set initial values - - if (x.len() != n) { - x.allocate(n); - } - x.zero(); - inform.x_norm = ZERO; - inform.obj = f; - inform.hard_case = false; - double delta_lambda = ZERO; - - // Check that arguments are OK - if (n < 0) { - throw std::runtime_error( - "Number of unknowns for trust-region subproblem is negative."); - } - if (radius < 0) { - throw std::runtime_error( - "Trust-region radius for trust-region subproblem is negative"); - } - - DoubleFortranVector x_norm2(0, MAX_DEGREE), pi_beta(0, MAX_DEGREE); - - // compute the two-norm of c and the extreme eigenvalues of H - - double c_norm = twoNorm(c); - double lambda_min = 0.0; - double lambda_max = 0.0; - std::tie(lambda_min, lambda_max) = minMaxValues(h); - - double lambda = 0.0; - //! check for the trivial case - if (c_norm == ZERO && lambda_min >= ZERO) { - if (control.equality_problem) { - int i_hard = 1; // TODO: is init value of 1 correct? - for (int i = 1; i <= n; ++i) { // do i = 1, n - if (h(i) == lambda_min) { - i_hard = i; - break; - } - } - x(i_hard) = ONE / radius; - inform.x_norm = radius; - inform.obj = f + lambda_min * radius * radius; - } - return; - } - - // construct values lambda_l and lambda_u for which lambda_l <= - // lambda_optimal - // <= lambda_u, and ensure that all iterates satisfy lambda_l <= lambda - // <= lambda_u - - double c_norm_over_radius = c_norm / radius; - double lambda_l = 0.0, lambda_u = 0.0; - if (control.equality_problem) { - lambda_l = - biggest(control.lower, -lambda_min, c_norm_over_radius - lambda_max); - lambda_u = std::min(control.upper, c_norm_over_radius - lambda_min); - } else { - lambda_l = biggest(control.lower, ZERO, -lambda_min, - c_norm_over_radius - lambda_max); - lambda_u = std::min(control.upper, - std::max(ZERO, c_norm_over_radius - lambda_min)); - } - lambda = lambda_l; - - // check for the "hard case" - if (lambda == -lambda_min) { - int i_hard = 1; // TODO: is init value of 1 correct? - double c2 = ZERO; - inform.hard_case = true; - for (int i = 1; i <= n; ++i) { // for_do(i, 1, n) - if (h(i) == lambda_min) { - if (fabs(c(i)) > EPSILON_MCH * c_norm) { - inform.hard_case = false; - c2 = c2 + pow(c(i), 2); - } else { - i_hard = i; - } - } - } - - // the hard case may occur - if (inform.hard_case) { - for (int i = 1; i <= n; ++i) { // for_do(i, 1, n) - if (h(i) != lambda_min) { - x(i) = -c(i) / (h(i) + lambda); - } else { - x(i) = ZERO; - } - } - inform.x_norm = twoNorm(x); - - // the hard case does occur - - if (inform.x_norm <= radius) { - if (inform.x_norm < radius) { - - // compute the step alpha so that x + alpha e_i_hard lies on the - // trust-region - // boundary and gives the smaller value of q - - auto utx = x(i_hard) / radius; - auto distx = - (radius - inform.x_norm) * ((radius + inform.x_norm) / radius); - auto alpha = sign( - distx / (fabs(utx) + sqrt(pow(utx, 2) + distx / radius)), utx); - - // record the optimal values - - x(i_hard) = x(i_hard) + alpha; - } - inform.x_norm = twoNorm(x); - inform.obj = f + HALF * (dotProduct(c, x) - lambda * pow(radius, 2)); - return; - - // the hard case didn't occur after all - } else { - inform.hard_case = false; - - // compute the first derivative of ||x|(lambda)||^2 - radius^2 - auto w_norm2 = ZERO; - for (int i = 1; i <= n; ++i) { // for_do(i, 1, n) - if (h(i) != lambda_min) - w_norm2 = w_norm2 + pow(c(i), 2) / pow((h(i) + lambda), 3); - } - x_norm2(1) = -TWO * w_norm2; - - // compute the newton correction - - lambda = lambda + (pow(inform.x_norm, 2) - pow(radius, 2)) / x_norm2(1); - lambda_l = std::max(lambda_l, lambda); - } - - // there is a singularity at lambda. compute the point for which the - // sum of squares of the singular terms is equal to radius^2 - } else { - lambda = lambda + std::max(sqrt(c2) / radius, lambda * EPSILON_MCH); - lambda_l = std::max(lambda_l, lambda); - } - } - - // the iterates will all be in the L region. Prepare for the main loop - auto max_order = std::max(1, std::min(MAX_DEGREE, control.taylor_max_degree)); - - // start the main loop - for (;;) { - - // if h(lambda) is positive definite, solve h(lambda) x = - c - - for (int i = 1; i <= n; ++i) { // for_do(i, 1, n) - x(i) = -c(i) / (h(i) + lambda); - } - - // compute the two-norm of x - inform.x_norm = twoNorm(x); - x_norm2(0) = pow(inform.x_norm, 2); - - // if the newton step lies within the trust region, exit - - if (lambda == ZERO && inform.x_norm <= radius) { - inform.obj = f + HALF * dotProduct(c, x); - return; - } - - //! the current estimate gives a good approximation to the required - //! root - - if (fabs(inform.x_norm - radius) <= - std::max(control.stop_normal * radius, control.stop_absolute_normal)) { - break; - } - - lambda_l = std::max(lambda_l, lambda); - - // record, for the future, values of lambda which give small ||x|| - if (inform.len_history < HISTORY_MAX) { - dtrs_history_type history_item; - history_item.lambda = lambda; - history_item.x_norm = inform.x_norm; - inform.history.push_back(history_item); - inform.len_history = inform.len_history + 1; - } - - // a lambda in L has been found. It is now simply a matter of applying - // a variety of Taylor-series-based methods starting from this lambda - - // precaution against rounding producing lambda outside L - - if (lambda > lambda_u) { - throw std::runtime_error( - "Lambda for trust-region subproblem is ill conditioned"); - } - - // compute first derivatives of x^T M x - - // form ||w||^2 = x^T H^-1(lambda) x - - double w_norm2 = ZERO; - for (int i = 1; i <= n; ++i) { // for_do(i, 1, n) - w_norm2 = w_norm2 + pow(c(i), 2) / pow(h(i) + lambda, 3); - } - - // compute the first derivative of x_norm2 = x^T M x - x_norm2(1) = -TWO * w_norm2; - - // compute pi_beta = ||x||^beta and its first derivative when beta = - 1 - double beta = -ONE; - dtrsPiDerivs(1, beta, x_norm2, pi_beta); - - // compute the Newton correction (for beta = - 1) - - delta_lambda = -(pi_beta(0) - pow((radius), beta)) / pi_beta(1); - - DoubleFortranVector lambda_new(3); - int n_lambda = 1; - lambda_new(n_lambda) = lambda + delta_lambda; - - if (max_order >= 3) { - - // compute the second derivative of x^T x - - double z_norm2 = ZERO; - for (int i = 1; i <= n; ++i) { // for_do(i, 1, n) - z_norm2 = z_norm2 + pow(c(i), 2) / pow((h(i) + lambda), 4); - } - x_norm2(2) = SIX * z_norm2; - - // compute the third derivatives of x^T x - - double v_norm2 = ZERO; - for (int i = 1; i <= n; ++i) { // for_do(i, 1, n) - v_norm2 = v_norm2 + pow(c(i), 2) / pow((h(i) + lambda), 5); - } - x_norm2(3) = -TWENTY_FOUR * v_norm2; - - // compute pi_beta = ||x||^beta and its derivatives when beta = 2 - - beta = TWO; - dtrsPiDerivs(max_order, beta, x_norm2, pi_beta); - - // compute the "cubic Taylor approximaton" step (beta = 2) - - auto a_0 = pi_beta(0) - pow((radius), beta); - auto a_1 = pi_beta(1); - auto a_2 = HALF * pi_beta(2); - auto a_3 = SIXTH * pi_beta(3); - auto a_max = biggest(fabs(a_0), fabs(a_1), fabs(a_2), fabs(a_3)); - if (a_max > ZERO) { - a_0 = a_0 / a_max; - a_1 = a_1 / a_max; - a_2 = a_2 / a_max; - a_3 = a_3 / a_max; - } - int nroots = 0; - double root1 = 0, root2 = 0, root3 = 0; - - rootsCubic(a_0, a_1, a_2, a_3, ROOTS_TOL, nroots, root1, root2, root3); - n_lambda = n_lambda + 1; - if (nroots == 3) { - lambda_new(n_lambda) = lambda + root3; - } else { - lambda_new(n_lambda) = lambda + root1; - } - - // compute pi_beta = ||x||^beta and its derivatives when beta = - 0.4 - - beta = -POINT4; - dtrsPiDerivs(max_order, beta, x_norm2, pi_beta); - - // compute the "cubic Taylor approximaton" step (beta = - 0.4) - - a_0 = pi_beta(0) - pow((radius), beta); - a_1 = pi_beta(1); - a_2 = HALF * pi_beta(2); - a_3 = SIXTH * pi_beta(3); - a_max = biggest(fabs(a_0), fabs(a_1), fabs(a_2), fabs(a_3)); - if (a_max > ZERO) { - a_0 = a_0 / a_max; - a_1 = a_1 / a_max; - a_2 = a_2 / a_max; - a_3 = a_3 / a_max; - } - rootsCubic(a_0, a_1, a_2, a_3, ROOTS_TOL, nroots, root1, root2, root3); - n_lambda = n_lambda + 1; - if (nroots == 3) { - lambda_new(n_lambda) = lambda + root3; - } else { - lambda_new(n_lambda) = lambda + root1; - } - } - - // compute the best Taylor improvement - - auto lambda_plus = maxVal(lambda_new, n_lambda); - delta_lambda = lambda_plus - lambda; - lambda = lambda_plus; - - // improve the lower bound if possible - - lambda_l = std::max(lambda_l, lambda_plus); - - // check that the best Taylor improvement is significant - - if (fabs(delta_lambda) < EPSILON_MCH * std::max(ONE, fabs(lambda))) { - break; - } - - } // for(;;) -} - -/** Solve the trust-region subproblem - * - * minimize q(x) = 1/2 <x, H x> + <c, x> + f - * subject to ||x||_2 <= radius or ||x||_2 = radius - * - * where H is diagonal, using a secular iteration - * - * @param n :: The number of unknowns. - * @param radius :: The trust-region radius. - * @param f :: The value of constant term for the quadratic function. - * @param c :: A vector of values for the linear term c. - * @param h :: A vector of values for the diagonal matrix H. - * @param x :: The required solution vector x. - * @param control :: A structure containing control information. - * @param inform :: A structure containing information. - */ -void dtrsSolve(int n, double radius, double f, const DoubleFortranVector &c, - const DoubleFortranVector &h, DoubleFortranVector &x, - const dtrs_control_type &control, dtrs_inform_type &inform) { - // scale the problem to solve instead - // minimize q_s(x_s) = 1/2 <x_s, H_s x_s> + <c_s, x_s> + f_s - // subject to ||x_s||_2 <= radius_s or ||x_s||_2 = radius_s - - // where H_s = H / s_h and c_s = c / s_c for scale factors s_h and s_c - - // This corresponds to - // radius_s = ( s_h / s_c ) radius, - // f_s = ( s_h / s_c^2 ) f - // and the solution may be recovered as - // x = ( s_c / s_h ) x_s - // lambda = s_h lambda_s - // q(x) = ( s_c^2 / s_ h ) q_s(x_s) - - // scale H by the largest H and remove relatively tiny H - - DoubleFortranVector h_scale(n); - auto scale_h = maxAbsVal(h); // MAXVAL( ABS( H ) ) - if (scale_h > ZERO) { - for (int i = 1; i <= n; ++i) { // do i = 1, n - if (fabs(h(i)) >= control.h_min * scale_h) { - h_scale(i) = h(i) / scale_h; - } else { - h_scale(i) = ZERO; - } - } - } else { - scale_h = ONE; - h_scale.zero(); - } - - // scale c by the largest c and remove relatively tiny c - - DoubleFortranVector c_scale(n); - auto scale_c = maxAbsVal(c); // maxval( abs( c ) ) - if (scale_c > ZERO) { - for (int i = 1; i <= n; ++i) { // do i = 1, n - if (fabs(c(i)) >= control.h_min * scale_c) { - c_scale(i) = c(i) / scale_c; - } else { - c_scale(i) = ZERO; - } - } - } else { - scale_c = ONE; - c_scale.zero(); - } - - double radius_scale = (scale_h / scale_c) * radius; - double f_scale = (scale_h / pow(scale_c, 2)) * f; - - auto control_scale = control; - if (control_scale.lower != LOWER_DEFAULT) { - control_scale.lower = control_scale.lower / scale_h; - } - if (control_scale.upper != UPPER_DEFAULT) { - control_scale.upper = control_scale.upper / scale_h; - } - - // solve the scaled problem - - dtrsSolveMain(n, radius_scale, f_scale, c_scale, h_scale, x, control_scale, - inform); - - // unscale the solution, function value, multiplier and related values - - // x = ( scale_c / scale_h ) * x - x *= scale_c / scale_h; - inform.obj *= pow(scale_c, 2) / scale_h; - inform.multiplier *= scale_h; - inform.pole *= scale_h; - for (size_t i = 0; i < inform.history.size(); - ++i) { // do i = 1, inform.len_history - inform.history[i].lambda *= scale_h; - inform.history[i].x_norm *= scale_c / scale_h; - } -} - -} // namespace - -/** Solve the trust-region subproblem using - * the DTRS method from Galahad - * - * This method needs H to be diagonal, so we need to - * pre-process - * - * main output d, the soln to the TR subproblem - * @param J :: The Jacobian. - * @param f :: The residuals. - * @param hf :: The Hessian (sort of). - * @param Delta :: The raduis of the trust region. - * @param d :: The output vector of corrections to the parameters giving the - * solution to the TR subproblem. - * @param normd :: The 2-norm of d. - * @param options :: The options. - */ -void DTRSMinimizer::solveDtrs(const DoubleFortranMatrix &J, - const DoubleFortranVector &f, - const DoubleFortranMatrix &hf, double Delta, - DoubleFortranVector &d, double &normd, - const NLLS::nlls_options &options) { - - dtrs_control_type dtrs_options; - dtrs_inform_type dtrs_inform; - - // The code finds - // d = arg min_p w^T p + 0.5 * p^T D p - // s.t. ||p|| \leq Delta - // - // where D is diagonal - // - // our probem in naturally in the form - // - // d = arg min_p v^T p + 0.5 * p^T H p - // s.t. ||p|| \leq Delta - // - // first, find the matrix H and vector v - // Set A = J^T J - NLLS::matmultInner(J, m_A); - // add any second order information... - // so A = J^T J + HF - m_A += hf; - - // now form v = J^T f - NLLS::multJt(J, f, m_v); - - // if scaling needed, do it - if (options.scale != 0) { - applyScaling(J, m_A, m_v, m_scale, options); - } - - // Now that we have the unprocessed matrices, we need to get an - // eigendecomposition to make A diagonal - // - NLLS::allEigSymm(m_A, m_ew, m_ev); - - // We can now change variables, setting y = Vp, getting - // Vd = arg min_(Vx) v^T p + 0.5 * (Vp)^T D (Vp) - // s.t. ||x|| \leq Delta - // <=> - // Vd = arg min_(Vx) V^Tv^T (Vp) + 0.5 * (Vp)^T D (Vp) - // s.t. ||x|| \leq Delta - // <=> - // we need to get the transformed vector v - NLLS::multJt(m_ev, m_v, m_v_trans); - - // we've now got the vectors we need, pass to dtrsSolve - dtrsInitialize(dtrs_options); - - auto n = J.len2(); - if (m_v_trans.len() != n) { - m_v_trans.allocate(n); - } - - for (int ii = 1; ii <= n; ++ii) { // for_do(ii, 1,n) - if (fabs(m_v_trans(ii)) < EPSILON_MCH) { - m_v_trans(ii) = ZERO; - } - if (fabs(m_ew(ii)) < EPSILON_MCH) { - m_ew(ii) = ZERO; - } - } - - dtrsSolve(n, Delta, ZERO, m_v_trans, m_ew, m_d_trans, dtrs_options, - dtrs_inform); - - // and return the un-transformed vector - NLLS::multJ(m_ev, m_d_trans, d); - - normd = NLLS::norm2(d); // ! ||d||_D - - if (options.scale != 0) { - for (int ii = 1; ii <= n; ++ii) { // for_do(ii, 1, n) - d(ii) = d(ii) / m_scale(ii); - } - } - -} // solveDtrs - -/** Implements the abstarct method of TrustRegionMinimizer. - */ -void DTRSMinimizer::calculateStep(const DoubleFortranMatrix &J, - const DoubleFortranVector &f, - const DoubleFortranMatrix &hf, - const DoubleFortranVector &, double Delta, - DoubleFortranVector &d, double &normd, - const NLLS::nlls_options &options) { - solveDtrs(J, f, hf, Delta, d, normd, options); -} - -} // namespace FuncMinimisers -} // namespace CurveFitting -} // namespace Mantid diff --git a/Framework/CurveFitting/src/FuncMinimizers/MoreSorensenMinimizer.cpp b/Framework/CurveFitting/src/FuncMinimizers/MoreSorensenMinimizer.cpp deleted file mode 100644 index 8f74fef1df4fe243710fa92746e98c6f6d632ef1..0000000000000000000000000000000000000000 --- a/Framework/CurveFitting/src/FuncMinimizers/MoreSorensenMinimizer.cpp +++ /dev/null @@ -1,320 +0,0 @@ -// This code was originally translated from Fortran code on -// https://ccpforge.cse.rl.ac.uk/gf/project/ral_nlls June 2016 -//---------------------------------------------------------------------- -// Includes -//---------------------------------------------------------------------- -#include "MantidCurveFitting/FuncMinimizers/MoreSorensenMinimizer.h" -#include "MantidAPI/FuncMinimizerFactory.h" -#include "MantidCurveFitting/RalNlls/TrustRegion.h" -#include <cmath> - -namespace Mantid { -namespace CurveFitting { -namespace FuncMinimisers { - -// clang-format off -///@cond nodoc -DECLARE_FUNCMINIMIZER(MoreSorensenMinimizer,More-Sorensen) -///@endcond -// clang-format on - -MoreSorensenMinimizer::MoreSorensenMinimizer() : TrustRegionMinimizer() {} - -/// Name of the minimizer. -std::string MoreSorensenMinimizer::name() const { return "More-Sorensen"; } - -namespace { - -/** Solve a system of linear equations. The system's matrix must be - * positive-definite. - * @param A :: A matrix of a system of equations. - * Must be positive-definite for success. - * @param b :: A vector of the right-hand side. - * @param LtL :: A work matrix. - * @param x :: A vector that receives the solution. - * @return true if successful - */ -bool solveSpd(const DoubleFortranMatrix &A, const DoubleFortranVector &b, - DoubleFortranMatrix &LtL, DoubleFortranVector &x) { - // Fortran code uses this: - // dposv('L', n, 1, LtL, n, x, n, inform.external_return) - // This is the GSL replacement: - LtL = A; - auto res = gsl_linalg_cholesky_decomp(LtL.gsl()); - if (res == GSL_EDOM) { - // Matrix is not positive definite, return for retry. - return false; - } - gsl_linalg_cholesky_solve(LtL.gsl(), b.gsl(), x.gsl()); - return true; -} - -/** Calculate the leftmost eigenvalue of a matrix. - * @param A :: A matrix to analyse. - * @param sigma :: A variable to receive the value of the smallest - * eigenvalue. - * @param y :: A vector that receives the corresponding eigenvector. - */ -void minEigSymm(const DoubleFortranMatrix &A, double &sigma, - DoubleFortranVector &y) { - auto M = A; - DoubleFortranVector ew; - DoubleFortranMatrix ev; - M.eigenSystem(ew, ev); - auto ind = ew.sortIndices(); - int imin = static_cast<int>(ind[0]) + 1; - sigma = ew(imin); - int n = static_cast<int>(A.size1()); - y.allocate(n); - for (int i = 1; i <= n; ++i) { - y(i) = ev(i, imin); - } -} - -/** Calculate AplusSigma = A + sigma * I - * @param sigma :: The value of the diagonal shift. - * @param AplusSigma :: The resulting matrix. - */ -void shiftMatrix(const DoubleFortranMatrix &A, double sigma, - DoubleFortranMatrix &AplusSigma) { - AplusSigma = A; - auto n = A.len1(); - for (int i = 1; i <= n; ++i) { // for_do(i,1,n) - AplusSigma(i, i) = AplusSigma(i, i) + sigma; - } -} - -/** Negate a vector - * @param v :: A vector. - */ -DoubleFortranVector negative(const DoubleFortranVector &v) { - DoubleFortranVector neg = v; - neg *= -1.0; - return neg; -} - -/** A subroutine to find the optimal beta such that - * || d || = Delta, where d = a + beta * b - * - * uses the approach from equation (3.20b), - * "Methods for non-linear least squares problems" (2nd edition, 2004) - * by Madsen, Nielsen and Tingleff - * @param a :: The first vector. - * @param b :: The second vector. - * @param Delta :: The Delta. - * @param beta :: The beta. - * @return true if successful - */ -bool findBeta(const DoubleFortranVector &a, const DoubleFortranVector &b, - double Delta, double &beta) { - - auto c = a.dot(b); - - auto norma2 = pow(NLLS::norm2(a), 2); - auto normb2 = pow(NLLS::norm2(b), 2); - - double discrim = pow(c, 2) + (normb2) * (pow(Delta, 2) - norma2); - if (discrim < NLLS::ZERO) { - return false; - } - - if (c <= 0) { - beta = (-c + sqrt(discrim)) / normb2; - } else { - beta = (pow(Delta, 2) - norma2) / (c + sqrt(discrim)); - } - return true; -} - -} // namespace - -/** Given an indefinite matrix m_A, find a shift sigma -* such that (A + sigma I) is positive definite. -* @param sigma :: The result (shift). -* @param d :: A solution vector to the system of linear equations -* with the found positive defimnite matrix. The RHS vector is -m_v. -* @param options :: The options. -* @return true if successful -*/ -bool MoreSorensenMinimizer::getPdShift(double &sigma, DoubleFortranVector &d, - const NLLS::nlls_options &options) { - int no_shifts = 0; - bool successful_shift = false; - while (!successful_shift) { - shiftMatrix(m_A, sigma, m_AplusSigma); - successful_shift = solveSpd(m_AplusSigma, negative(m_v), m_LtL, d); - if (!successful_shift) { - // We try again with a shifted sigma, but no too many times. - no_shifts = no_shifts + 1; - if (no_shifts == 10) { - return false; - } - sigma = sigma + (pow(10.0, no_shifts)) * options.more_sorensen_shift; - } - } - return true; -} - -/** Solve the trust-region subproblem using - * the method of More and Sorensen - * - * Using the implementation as in Algorithm 7.3.6 - * of Trust Region Methods - * - * main output d, the soln to the TR subproblem - * @param J :: The Jacobian. - * @param f :: The residuals. - * @param hf :: The Hessian (sort of). - * @param Delta :: The raduis of the trust region. - * @param d :: The output vector of corrections to the parameters giving the - * solution to the TR subproblem. - * @param nd :: The 2-norm of d. - * @param options :: The options. - */ -void MoreSorensenMinimizer::solveSubproblem(const DoubleFortranMatrix &J, - const DoubleFortranVector &f, - const DoubleFortranMatrix &hf, - double Delta, - DoubleFortranVector &d, double &nd, - const NLLS::nlls_options &options) { - - // The code finds - // d = arg min_p v^T p + 0.5 * p^T A p - // s.t. ||p|| \leq Delta - // - // set A and v for the model being considered here... - - // Set A = J^T J - NLLS::matmultInner(J, m_A); - // add any second order information... - // so A = J^T J + HF - m_A += hf; - // now form v = J^T f - NLLS::multJt(J, f, m_v); - - // if scaling needed, do it - if (options.scale != 0) { - applyScaling(J, m_A, m_v, m_scale, options); - } - - auto n = J.len2(); - auto scaleBack = [n, &d, &options, this]() { - if (options.scale != 0) { - for (int i = 1; i <= n; ++i) { - d(i) = d(i) / m_scale(i); - } - } - }; - - auto local_ms_shift = options.more_sorensen_shift; - // d = -A\v - DoubleFortranVector negv = m_v; - negv *= -1.0; - bool matrix_ok = solveSpd(m_A, negv, m_LtL, d); - double sigma = 0.0; - if (matrix_ok) { - // A is symmetric positive definite.... - sigma = NLLS::ZERO; - } else { - // shift and try again - minEigSymm(m_A, sigma, m_y1); - sigma = -(sigma - local_ms_shift); - // find a shift that makes (A + sigma I) positive definite - bool ok = getPdShift(sigma, d, options); - if (!ok) { - scaleBack(); - return; - } - } - - nd = NLLS::norm2(d); - if (!std::isfinite(nd)) { - throw std::runtime_error("Step is NaN or infinite."); - } - - // now, we're not in the trust region initally, so iterate.... - auto sigma_shift = NLLS::ZERO; - int no_restarts = 0; - // set 'small' in the context of the algorithm - double epsilon = - std::max(options.more_sorensen_tol * Delta, options.more_sorensen_tiny); - int it = 1; - for (; it <= options.more_sorensen_maxits; ++it) { - - if (nd <= Delta + epsilon) { - // we're within the tr radius - if (fabs(sigma) < options.more_sorensen_tiny || - fabs(nd - Delta) < epsilon) { - // we're good....exit - break; - } - if (m_y1.len() == n) { - double alpha = 0.0; - if (findBeta(d, m_y1, Delta, alpha)) { - DoubleFortranVector tmp = m_y1; - tmp *= alpha; - d += tmp; - } - } - // also good....exit - break; - } - - // m_q = R'\d - // DTRSM( "Left", "Lower", "No Transpose", "Non-unit", n, 1, one, m_LtL, n, - // m_q, n ); - for (int j = 1; j <= m_LtL.len1(); ++j) { - for (int k = j + 1; k <= m_LtL.len1(); ++k) { - m_LtL(j, k) = 0.0; - } - } - m_LtL.solve(d, m_q); - - auto nq = NLLS::norm2(m_q); - sigma_shift = (pow((nd / nq), 2)) * ((nd - Delta) / Delta); - if (fabs(sigma_shift) < options.more_sorensen_tiny * fabs(sigma)) { - if (no_restarts < 1) { - // find a shift that makes (A + sigma I) positive definite - bool ok = getPdShift(sigma, d, options); - if (!ok) { - break; - } - no_restarts = no_restarts + 1; - } else { - // we're not going to make progress...jump out - throw std::runtime_error("Not making progress."); - } - } else { - sigma = sigma + sigma_shift; - } - - shiftMatrix(m_A, sigma, m_AplusSigma); - DoubleFortranVector negv = m_v; - negv *= -1.0; - bool matrix_ok = solveSpd(m_AplusSigma, negv, m_LtL, d); - if (!matrix_ok) { - break; - } - - nd = NLLS::norm2(d); - } - - if (it == options.more_sorensen_maxits) { - // maxits reached, not converged - throw std::runtime_error("No convergence in maximum number of iterations."); - } - scaleBack(); -} - -/** Implements the abstract method of TrustRegionMinimizer. - */ -void MoreSorensenMinimizer::calculateStep( - const DoubleFortranMatrix &J, const DoubleFortranVector &f, - const DoubleFortranMatrix &hf, const DoubleFortranVector &, double Delta, - DoubleFortranVector &d, double &normd, const NLLS::nlls_options &options) { - solveSubproblem(J, f, hf, Delta, d, normd, options); -} - -} // namespace FuncMinimisers -} // namespace CurveFitting -} // namespace Mantid diff --git a/Framework/CurveFitting/src/FuncMinimizers/TrustRegionMinimizer.cpp b/Framework/CurveFitting/src/FuncMinimizers/TrustRegionMinimizer.cpp index 4cde25729569d2e769c9369f1fc427a4777460c3..2b4c732e7120eec05e4b92c7c63ccbf038b4e4d0 100644 --- a/Framework/CurveFitting/src/FuncMinimizers/TrustRegionMinimizer.cpp +++ b/Framework/CurveFitting/src/FuncMinimizers/TrustRegionMinimizer.cpp @@ -5,6 +5,7 @@ //---------------------------------------------------------------------- #include "MantidCurveFitting/FuncMinimizers/TrustRegionMinimizer.h" #include "MantidCurveFitting/RalNlls/TrustRegion.h" +#include "MantidAPI/FuncMinimizerFactory.h" #include <cmath> @@ -12,11 +13,21 @@ namespace Mantid { namespace CurveFitting { namespace FuncMinimisers { +// clang-format off +///@cond nodoc +DECLARE_FUNCMINIMIZER(TrustRegionMinimizer, Trust Region) +///@endcond +// clang-format on + TrustRegionMinimizer::TrustRegionMinimizer() : m_function() { declareProperty("InitialRadius", 100.0, "Initial radius of the trust region."); } +/** Name of the minimizer. +*/ +std::string TrustRegionMinimizer::name() const { return "Trust Region"; } + /** Initialise the minimizer. * @param costFunction :: The cost function to minimize. Must be the least * squares. @@ -241,7 +252,7 @@ bool TrustRegionMinimizer::iterate(size_t) { return true; } // Calculate the step d that the model thinks we should take next - calculateStep(w.J, w.f, w.hf, w.g, w.Delta, w.d, w.normd, options); + calculateStep(w.J, w.f, w.hf, w.Delta, w.d, w.normd, options); // Accept the step? w.Xnew = X; @@ -405,6 +416,973 @@ bool TrustRegionMinimizer::iterate(size_t) { return true; } +/** DTRS method **/ +namespace { + +const double HUGEST = std::numeric_limits<double>::max(); +const double EPSILON_MCH = std::numeric_limits<double>::epsilon(); +const double LARGEST = HUGEST; +const double LOWER_DEFAULT = -0.5 * LARGEST; +const double UPPER_DEFAULT = LARGEST; +const double POINT4 = 0.4; +const double ZERO = 0.0; +const double ONE = 1.0; +const double TWO = 2.0; +const double THREE = 3.0; +const double FOUR = 4.0; +const double SIX = 6.0; +const double HALF = 0.5; +const double ONESIXTH = ONE / SIX; +const double SIXTH = ONESIXTH; +const double ONE_THIRD = ONE / THREE; +const double TWO_THIRDS = TWO / THREE; +const double THREE_QUARTERS = 0.75; +const double TWENTY_FOUR = 24.0; +const int MAX_DEGREE = 3; +const int HISTORY_MAX = 100; +const double TEN_EPSILON_MCH = 10.0 * EPSILON_MCH; +const double ROOTS_TOL = TEN_EPSILON_MCH; +const double INFINITE_NUMBER = HUGEST; + +/** Replacement for FORTRAN's SIGN intrinsic function +*/ +inline double sign(double x, double y) { return y >= 0.0 ? fabs(x) : -fabs(x); } + +//! - - - - - - - - - - - - - - - - - - - - - - - +//! control derived type with component defaults +//! - - - - - - - - - - - - - - - - - - - - - - - +struct control_type { + //! maximum degree of Taylor approximant allowed + int taylor_max_degree = 3; + + //! any entry of H that is smaller than h_min * MAXVAL( H ) we be treated as + // zero + double h_min = EPSILON_MCH; + + //! lower and upper bounds on the multiplier, if known + double lower = LOWER_DEFAULT; + double upper = UPPER_DEFAULT; + + //! stop when | ||x|| - radius | <= + //! max( stop_normal * radius, stop_absolute_normal ) + double stop_normal = EPSILON_MCH; + double stop_absolute_normal = EPSILON_MCH; + + //! is the solution is REQUIRED to lie on the boundary (i.e., is the + // constraint + //! an equality)? + bool equality_problem = false; +}; + +//! - - - - - - - - - - - - - - - - - - - - - - - - +//! history derived type with component defaults +//! - - - - - - - - - - - - - - - - - - - - - - - - +struct history_type { + // + //! value of lambda + double lambda = 0.0; + + //! corresponding value of ||x(lambda)||_M + double x_norm = 0.0; +}; + +//! - - - - - - - - - - - - - - - - - - - - - - - +//! inform derived type with component defaults +//! - - - - - - - - - - - - - - - - - - - - - - - +struct inform_type { + // + + //! the number of (||x||_M,lambda) pairs in the history + int len_history = 0; + + //! the value of the quadratic function + double obj = HUGEST; + + //! the M-norm of x, ||x||_M + double x_norm = 0.0; + + //! the Lagrange multiplier corresponding to the trust-region constraint + double multiplier = 0.0; + + //! a lower bound max(0,-lambda_1), where lambda_1 is the left-most + //! eigenvalue of (H,M) + double pole = 0.0; + + //! has the hard case occurred? + bool hard_case = false; + + //! history information + std::vector<history_type> history; +}; + +/** Get the largest of the four values. +* @param a :: Value number 1. +* @param b :: Value number 2. +* @param c :: Value number 3. +* @param d :: Value number 4. +*/ +double biggest(double a, double b, double c, double d) { + return std::max(std::max(a, b), std::max(c, d)); +} + +/** Get the largest of the three values. +* @param a :: Value number 1. +* @param b :: Value number 2. +* @param c :: Value number 3. +*/ +double biggest(double a, double b, double c) { + return std::max(std::max(a, b), c); +} + +/** Find the largest by absolute value element of a vector. +* @param v :: The searched vector. +*/ +double maxAbsVal(const DoubleFortranVector &v) { + auto p = v.indicesOfMinMaxElements(); + return std::max(fabs(v.get(p.first)), fabs(v.get(p.second))); +} + +/** Find the minimum and maximum elements of a vector. +* @param v :: The searched vector. +* @returns :: A pair of doubles where the first is the minimum and +* the second is the maxumum. +*/ +std::pair<double, double> minMaxValues(const DoubleFortranVector &v) { + auto p = v.indicesOfMinMaxElements(); + return std::make_pair(v.get(p.first), v.get(p.second)); +} + +/** Compute the 2-norm of a vector which is a square root of the +* sum of squares of its elements. +* @param v :: The vector. +*/ +double twoNorm(const DoubleFortranVector &v) { + if (v.size() == 0) + return 0.0; + return gsl_blas_dnrm2(v.gsl()); +} + +/** Get the dot-product of two vectors of the same size. +* @param v1 :: The first vector. +* @param v2 :: The second vector. +*/ +double dotProduct(const DoubleFortranVector &v1, + const DoubleFortranVector &v2) { + return v1.dot(v2); +} + +/** Find the maximum element in the first n elements of a vector. +* @param v :: The vector. +* @param n :: The number of elements to examine. +*/ +double maxVal(const DoubleFortranVector &v, int n) { + double res = std::numeric_limits<double>::lowest(); + for (int i = 1; i <= n; ++i) { + auto val = v(i); + if (val > res) { + res = val; + } + } + return res; +} + +/** Find the number and values of real roots of the quadratic equation +* +* a2 * x**2 + a1 * x + a0 = 0 +* +* where a0, a1 and a2 are real +* @param a0 :: The free coefficient. +* @param a1 :: The coefficient at the linear term. +* @param a2 :: The coefficient at the quadratic term. +* @param tol :: A tolerance for comparing doubles. +* @param nroots :: The output number of real roots. +* @param root1 :: The first real root if nroots > 0. +* @param root2 :: The second real root if nroots = 2. +*/ +void rootsQuadratic(double a0, double a1, double a2, double tol, int &nroots, + double &root1, double &root2) { + + auto rhs = tol * a1 * a1; + if (fabs(a0 * a2) > rhs) { // really is quadratic + root2 = a1 * a1 - FOUR * a2 * a0; + if (fabs(root2) <= pow(EPSILON_MCH * a1, 2)) { // numerical double root + nroots = 2; + root1 = -HALF * a1 / a2; + root2 = root1; + } else if (root2 < ZERO) { // complex not real roots + nroots = 0; + root1 = ZERO; + root2 = ZERO; + } else { // distint real roots + auto d = -HALF * (a1 + sign(sqrt(root2), a1)); + nroots = 2; + root1 = d / a2; + root2 = a0 / d; + if (root1 > root2) { + d = root1; + root1 = root2; + root2 = d; + } + } + } else if (a2 == ZERO) { + if (a1 == ZERO) { + if (a0 == ZERO) { // the function is zero + nroots = 1; + root1 = ZERO; + root2 = ZERO; + } else { // the function is constant + nroots = 0; + root1 = ZERO; + root2 = ZERO; + } + } else { // the function is linear + nroots = 1; + root1 = -a0 / a1; + root2 = ZERO; + } + } else { // very ill-conditioned quadratic + nroots = 2; + if (-a1 / a2 > ZERO) { + root1 = ZERO; + root2 = -a1 / a2; + } else { + root1 = -a1 / a2; + root2 = ZERO; + } + } + + // perfom a Newton iteration to ensure that the roots are accurate + + if (nroots >= 1) { + auto p = (a2 * root1 + a1) * root1 + a0; + auto pprime = TWO * a2 * root1 + a1; + if (pprime != ZERO) { + root1 = root1 - p / pprime; + } + if (nroots == 2) { + p = (a2 * root2 + a1) * root2 + a0; + pprime = TWO * a2 * root2 + a1; + if (pprime != ZERO) { + root2 = root2 - p / pprime; + } + } + } +} + +/** Find the number and values of real roots of the cubic equation +* +* a3 * x**3 + a2 * x**2 + a1 * x + a0 = 0 +* +* where a0, a1, a2 and a3 are real +* @param a0 :: The free coefficient. +* @param a1 :: The coefficient at the linear term. +* @param a2 :: The coefficient at the quadratic term. +* @param a3 :: The coefficient at the cubic term. +* @param tol :: A tolerance for comparing doubles. +* @param nroots :: The output number of real roots. +* @param root1 :: The first real root. +* @param root2 :: The second real root if nroots > 1. +* @param root3 :: The third real root if nroots == 3. +*/ +void rootsCubic(double a0, double a1, double a2, double a3, double tol, + int &nroots, double &root1, double &root2, double &root3) { + + // Check to see if the cubic is actually a quadratic + if (a3 == ZERO) { + rootsQuadratic(a0, a1, a2, tol, nroots, root1, root2); + root3 = INFINITE_NUMBER; + return; + } + + // Deflate the polnomial if the trailing coefficient is zero + if (a0 == ZERO) { + root1 = ZERO; + rootsQuadratic(a1, a2, a3, tol, nroots, root2, root3); + nroots = nroots + 1; + return; + } + + // 1. Use Nonweiler's method (CACM 11:4, 1968, pp269) + + double c0 = a0 / a3; + double c1 = a1 / a3; + double c2 = a2 / a3; + + double s = c2 / THREE; + double t = s * c2; + double b = 0.5 * (s * (TWO_THIRDS * t - c1) + c0); + t = (t - c1) / THREE; + double c = t * t * t; + double d = b * b - c; + + // 1 real + 2 equal real or 2 complex roots + if (d >= ZERO) { + d = pow(sqrt(d) + fabs(b), ONE_THIRD); + if (d != ZERO) { + if (b > ZERO) { + b = -d; + } else { + b = d; + } + c = t / b; + } + d = sqrt(THREE_QUARTERS) * (b - c); + b = b + c; + c = -0.5 * b - s; + root1 = b - s; + if (d == ZERO) { + nroots = 3; + root2 = c; + root3 = c; + } else { + nroots = 1; + } + } else { // 3 real roots + if (b == ZERO) { + d = TWO_THIRDS * atan(ONE); + } else { + d = atan(sqrt(-d) / fabs(b)) / THREE; + } + if (b < ZERO) { + b = TWO * sqrt(t); + } else { + b = -TWO * sqrt(t); + } + c = cos(d) * b; + t = -sqrt(THREE_QUARTERS) * sin(d) * b - HALF * c; + d = -t - c - s; + c = c - s; + t = t - s; + if (fabs(c) > fabs(t)) { + root3 = c; + } else { + root3 = t; + t = c; + } + if (fabs(d) > fabs(t)) { + root2 = d; + } else { + root2 = t; + t = d; + } + root1 = t; + nroots = 3; + } + + // reorder the roots + + if (nroots == 3) { + if (root1 > root2) { + double a = root2; + root2 = root1; + root1 = a; + } + if (root2 > root3) { + double a = root3; + if (root1 > root3) { + a = root1; + root1 = root3; + } + root3 = root2; + root2 = a; + } + } + + // perfom a Newton iteration to ensure that the roots are accurate + double p = ((a3 * root1 + a2) * root1 + a1) * root1 + a0; + double pprime = (THREE * a3 * root1 + TWO * a2) * root1 + a1; + if (pprime != ZERO) { + root1 = root1 - p / pprime; + // p = ((a3 * root1 + a2) * root1 + a1) * root1 + a0; // never used + } + + if (nroots == 3) { + p = ((a3 * root2 + a2) * root2 + a1) * root2 + a0; + pprime = (THREE * a3 * root2 + TWO * a2) * root2 + a1; + if (pprime != ZERO) { + root2 = root2 - p / pprime; + // p = ((a3 * root2 + a2) * root2 + a1) * root2 + a0; // never used + } + + p = ((a3 * root3 + a2) * root3 + a1) * root3 + a0; + pprime = (THREE * a3 * root3 + TWO * a2) * root3 + a1; + if (pprime != ZERO) { + root3 = root3 - p / pprime; + // p = ((a3 * root3 + a2) * root3 + a1) * root3 + a0; // never used + } + } +} + +/** Compute pi_beta = ||x||^beta and its derivatives +* Extracted wholesale from module RAL_NLLS_RQS +* +* @param max_order :: Maximum order of derivative. +* @param beta :: Power. +* @param x_norm2 :: (0) value of ||x||^2, +* (i) ith derivative of ||x||^2, i = 1, max_order +* @param pi_beta :: (0) value of ||x||^beta, +* (i) ith derivative of ||x||^beta, i = 1, max_order +*/ +void PiBetaDerivs(int max_order, double beta, + const DoubleFortranVector &x_norm2, + DoubleFortranVector &pi_beta) { + double hbeta = HALF * beta; + pi_beta(0) = pow(x_norm2(0), hbeta); + pi_beta(1) = hbeta * (pow(x_norm2(0), (hbeta - ONE))) * x_norm2(1); + if (max_order == 1) + return; + pi_beta(2) = hbeta * (pow(x_norm2(0), (hbeta - TWO))) * + ((hbeta - ONE) * pow(x_norm2(1), 2) + x_norm2(0) * x_norm2(2)); + if (max_order == 2) + return; + pi_beta(3) = hbeta * (pow(x_norm2(0), (hbeta - THREE))) * + (x_norm2(3) * pow(x_norm2(0), 2) + + (hbeta - ONE) * (THREE * x_norm2(0) * x_norm2(1) * x_norm2(2) + + (hbeta - TWO) * pow(x_norm2(1), 3))); +} + +/** Set initial values for the TRS control parameters +* +* @param control :: A structure containing control information. +*/ +void intitializeControl(control_type &control) { + control.stop_normal = pow(EPSILON_MCH, 0.75); + control.stop_absolute_normal = pow(EPSILON_MCH, 0.75); +} + +/** Solve the trust-region subproblem +* +* minimize 1/2 <x, H x> + <c, x> + f +* subject to ||x||_2 <= radius or ||x||_2 = radius +* +* where H is diagonal, using a secular iteration +* +* @param n :: The number of unknowns. +* @param radius :: The trust-region radius. +* @param f :: The value of constant term for the quadratic function +* @param c :: A vector of values for the linear term c. +* @param h :: A vector of values for the diagonal matrix H. +* @param x :: The required solution vector x. +* @param control :: A structure containing control information. +* @param inform :: A structure containing information. +*/ +void solveSubproblemMain(int n, double radius, double f, + const DoubleFortranVector &c, + const DoubleFortranVector &h, DoubleFortranVector &x, + const control_type &control, inform_type &inform) { + + // set initial values + + if (x.len() != n) { + x.allocate(n); + } + x.zero(); + inform.x_norm = ZERO; + inform.obj = f; + inform.hard_case = false; + double delta_lambda = ZERO; + + // Check that arguments are OK + if (n < 0) { + throw std::runtime_error( + "Number of unknowns for trust-region subproblem is negative."); + } + if (radius < 0) { + throw std::runtime_error( + "Trust-region radius for trust-region subproblem is negative"); + } + + DoubleFortranVector x_norm2(0, MAX_DEGREE), pi_beta(0, MAX_DEGREE); + + // compute the two-norm of c and the extreme eigenvalues of H + + double c_norm = twoNorm(c); + double lambda_min = 0.0; + double lambda_max = 0.0; + std::tie(lambda_min, lambda_max) = minMaxValues(h); + + double lambda = 0.0; + //! check for the trivial case + if (c_norm == ZERO && lambda_min >= ZERO) { + if (control.equality_problem) { + int i_hard = 1; // TODO: is init value of 1 correct? + for (int i = 1; i <= n; ++i) { // do i = 1, n + if (h(i) == lambda_min) { + i_hard = i; + break; + } + } + x(i_hard) = ONE / radius; + inform.x_norm = radius; + inform.obj = f + lambda_min * radius * radius; + } + return; + } + + // construct values lambda_l and lambda_u for which lambda_l <= + // lambda_optimal + // <= lambda_u, and ensure that all iterates satisfy lambda_l <= lambda + // <= lambda_u + + double c_norm_over_radius = c_norm / radius; + double lambda_l = 0.0, lambda_u = 0.0; + if (control.equality_problem) { + lambda_l = + biggest(control.lower, -lambda_min, c_norm_over_radius - lambda_max); + lambda_u = std::min(control.upper, c_norm_over_radius - lambda_min); + } else { + lambda_l = biggest(control.lower, ZERO, -lambda_min, + c_norm_over_radius - lambda_max); + lambda_u = std::min(control.upper, + std::max(ZERO, c_norm_over_radius - lambda_min)); + } + lambda = lambda_l; + + // check for the "hard case" + if (lambda == -lambda_min) { + int i_hard = 1; // TODO: is init value of 1 correct? + double c2 = ZERO; + inform.hard_case = true; + for (int i = 1; i <= n; ++i) { // for_do(i, 1, n) + if (h(i) == lambda_min) { + if (fabs(c(i)) > EPSILON_MCH * c_norm) { + inform.hard_case = false; + c2 = c2 + pow(c(i), 2); + } else { + i_hard = i; + } + } + } + + // the hard case may occur + if (inform.hard_case) { + for (int i = 1; i <= n; ++i) { // for_do(i, 1, n) + if (h(i) != lambda_min) { + x(i) = -c(i) / (h(i) + lambda); + } else { + x(i) = ZERO; + } + } + inform.x_norm = twoNorm(x); + + // the hard case does occur + + if (inform.x_norm <= radius) { + if (inform.x_norm < radius) { + + // compute the step alpha so that x + alpha e_i_hard lies on the + // trust-region + // boundary and gives the smaller value of q + + auto utx = x(i_hard) / radius; + auto distx = + (radius - inform.x_norm) * ((radius + inform.x_norm) / radius); + auto alpha = sign( + distx / (fabs(utx) + sqrt(pow(utx, 2) + distx / radius)), utx); + + // record the optimal values + + x(i_hard) = x(i_hard) + alpha; + } + inform.x_norm = twoNorm(x); + inform.obj = f + HALF * (dotProduct(c, x) - lambda * pow(radius, 2)); + return; + + // the hard case didn't occur after all + } else { + inform.hard_case = false; + + // compute the first derivative of ||x|(lambda)||^2 - radius^2 + auto w_norm2 = ZERO; + for (int i = 1; i <= n; ++i) { // for_do(i, 1, n) + if (h(i) != lambda_min) + w_norm2 = w_norm2 + pow(c(i), 2) / pow((h(i) + lambda), 3); + } + x_norm2(1) = -TWO * w_norm2; + + // compute the newton correction + + lambda = lambda + (pow(inform.x_norm, 2) - pow(radius, 2)) / x_norm2(1); + lambda_l = std::max(lambda_l, lambda); + } + + // there is a singularity at lambda. compute the point for which the + // sum of squares of the singular terms is equal to radius^2 + } else { + lambda = lambda + std::max(sqrt(c2) / radius, lambda * EPSILON_MCH); + lambda_l = std::max(lambda_l, lambda); + } + } + + // the iterates will all be in the L region. Prepare for the main loop + auto max_order = std::max(1, std::min(MAX_DEGREE, control.taylor_max_degree)); + + // start the main loop + for (;;) { + + // if h(lambda) is positive definite, solve h(lambda) x = - c + + for (int i = 1; i <= n; ++i) { // for_do(i, 1, n) + x(i) = -c(i) / (h(i) + lambda); + } + + // compute the two-norm of x + inform.x_norm = twoNorm(x); + x_norm2(0) = pow(inform.x_norm, 2); + + // if the newton step lies within the trust region, exit + + if (lambda == ZERO && inform.x_norm <= radius) { + inform.obj = f + HALF * dotProduct(c, x); + return; + } + + //! the current estimate gives a good approximation to the required + //! root + + if (fabs(inform.x_norm - radius) <= + std::max(control.stop_normal * radius, control.stop_absolute_normal)) { + break; + } + + lambda_l = std::max(lambda_l, lambda); + + // record, for the future, values of lambda which give small ||x|| + if (inform.len_history < HISTORY_MAX) { + history_type history_item; + history_item.lambda = lambda; + history_item.x_norm = inform.x_norm; + inform.history.push_back(history_item); + inform.len_history = inform.len_history + 1; + } + + // a lambda in L has been found. It is now simply a matter of applying + // a variety of Taylor-series-based methods starting from this lambda + + // precaution against rounding producing lambda outside L + + if (lambda > lambda_u) { + throw std::runtime_error( + "Lambda for trust-region subproblem is ill conditioned"); + } + + // compute first derivatives of x^T M x + + // form ||w||^2 = x^T H^-1(lambda) x + + double w_norm2 = ZERO; + for (int i = 1; i <= n; ++i) { // for_do(i, 1, n) + w_norm2 = w_norm2 + pow(c(i), 2) / pow(h(i) + lambda, 3); + } + + // compute the first derivative of x_norm2 = x^T M x + x_norm2(1) = -TWO * w_norm2; + + // compute pi_beta = ||x||^beta and its first derivative when beta = - 1 + double beta = -ONE; + PiBetaDerivs(1, beta, x_norm2, pi_beta); + + // compute the Newton correction (for beta = - 1) + + delta_lambda = -(pi_beta(0) - pow((radius), beta)) / pi_beta(1); + + DoubleFortranVector lambda_new(3); + int n_lambda = 1; + lambda_new(n_lambda) = lambda + delta_lambda; + + if (max_order >= 3) { + + // compute the second derivative of x^T x + + double z_norm2 = ZERO; + for (int i = 1; i <= n; ++i) { // for_do(i, 1, n) + z_norm2 = z_norm2 + pow(c(i), 2) / pow((h(i) + lambda), 4); + } + x_norm2(2) = SIX * z_norm2; + + // compute the third derivatives of x^T x + + double v_norm2 = ZERO; + for (int i = 1; i <= n; ++i) { // for_do(i, 1, n) + v_norm2 = v_norm2 + pow(c(i), 2) / pow((h(i) + lambda), 5); + } + x_norm2(3) = -TWENTY_FOUR * v_norm2; + + // compute pi_beta = ||x||^beta and its derivatives when beta = 2 + + beta = TWO; + PiBetaDerivs(max_order, beta, x_norm2, pi_beta); + + // compute the "cubic Taylor approximaton" step (beta = 2) + + auto a_0 = pi_beta(0) - pow((radius), beta); + auto a_1 = pi_beta(1); + auto a_2 = HALF * pi_beta(2); + auto a_3 = SIXTH * pi_beta(3); + auto a_max = biggest(fabs(a_0), fabs(a_1), fabs(a_2), fabs(a_3)); + if (a_max > ZERO) { + a_0 = a_0 / a_max; + a_1 = a_1 / a_max; + a_2 = a_2 / a_max; + a_3 = a_3 / a_max; + } + int nroots = 0; + double root1 = 0, root2 = 0, root3 = 0; + + rootsCubic(a_0, a_1, a_2, a_3, ROOTS_TOL, nroots, root1, root2, root3); + n_lambda = n_lambda + 1; + if (nroots == 3) { + lambda_new(n_lambda) = lambda + root3; + } else { + lambda_new(n_lambda) = lambda + root1; + } + + // compute pi_beta = ||x||^beta and its derivatives when beta = - 0.4 + + beta = -POINT4; + PiBetaDerivs(max_order, beta, x_norm2, pi_beta); + + // compute the "cubic Taylor approximaton" step (beta = - 0.4) + + a_0 = pi_beta(0) - pow((radius), beta); + a_1 = pi_beta(1); + a_2 = HALF * pi_beta(2); + a_3 = SIXTH * pi_beta(3); + a_max = biggest(fabs(a_0), fabs(a_1), fabs(a_2), fabs(a_3)); + if (a_max > ZERO) { + a_0 = a_0 / a_max; + a_1 = a_1 / a_max; + a_2 = a_2 / a_max; + a_3 = a_3 / a_max; + } + rootsCubic(a_0, a_1, a_2, a_3, ROOTS_TOL, nroots, root1, root2, root3); + n_lambda = n_lambda + 1; + if (nroots == 3) { + lambda_new(n_lambda) = lambda + root3; + } else { + lambda_new(n_lambda) = lambda + root1; + } + } + + // compute the best Taylor improvement + + auto lambda_plus = maxVal(lambda_new, n_lambda); + delta_lambda = lambda_plus - lambda; + lambda = lambda_plus; + + // improve the lower bound if possible + + lambda_l = std::max(lambda_l, lambda_plus); + + // check that the best Taylor improvement is significant + + if (fabs(delta_lambda) < EPSILON_MCH * std::max(ONE, fabs(lambda))) { + break; + } + + } // for(;;) +} + +/** Solve the trust-region subproblem +* +* minimize q(x) = 1/2 <x, H x> + <c, x> + f +* subject to ||x||_2 <= radius or ||x||_2 = radius +* +* where H is diagonal, using a secular iteration +* +* @param n :: The number of unknowns. +* @param radius :: The trust-region radius. +* @param f :: The value of constant term for the quadratic function. +* @param c :: A vector of values for the linear term c. +* @param h :: A vector of values for the diagonal matrix H. +* @param x :: The required solution vector x. +* @param control :: A structure containing control information. +* @param inform :: A structure containing information. +*/ +void solveSubproblem(int n, double radius, double f, + const DoubleFortranVector &c, const DoubleFortranVector &h, + DoubleFortranVector &x, const control_type &control, + inform_type &inform) { + // scale the problem to solve instead + // minimize q_s(x_s) = 1/2 <x_s, H_s x_s> + <c_s, x_s> + f_s + // subject to ||x_s||_2 <= radius_s or ||x_s||_2 = radius_s + + // where H_s = H / s_h and c_s = c / s_c for scale factors s_h and s_c + + // This corresponds to + // radius_s = ( s_h / s_c ) radius, + // f_s = ( s_h / s_c^2 ) f + // and the solution may be recovered as + // x = ( s_c / s_h ) x_s + // lambda = s_h lambda_s + // q(x) = ( s_c^2 / s_ h ) q_s(x_s) + + // scale H by the largest H and remove relatively tiny H + + DoubleFortranVector h_scale(n); + auto scale_h = maxAbsVal(h); // MAXVAL( ABS( H ) ) + if (scale_h > ZERO) { + for (int i = 1; i <= n; ++i) { // do i = 1, n + if (fabs(h(i)) >= control.h_min * scale_h) { + h_scale(i) = h(i) / scale_h; + } else { + h_scale(i) = ZERO; + } + } + } else { + scale_h = ONE; + h_scale.zero(); + } + + // scale c by the largest c and remove relatively tiny c + + DoubleFortranVector c_scale(n); + auto scale_c = maxAbsVal(c); // maxval( abs( c ) ) + if (scale_c > ZERO) { + for (int i = 1; i <= n; ++i) { // do i = 1, n + if (fabs(c(i)) >= control.h_min * scale_c) { + c_scale(i) = c(i) / scale_c; + } else { + c_scale(i) = ZERO; + } + } + } else { + scale_c = ONE; + c_scale.zero(); + } + + double radius_scale = (scale_h / scale_c) * radius; + double f_scale = (scale_h / pow(scale_c, 2)) * f; + + auto control_scale = control; + if (control_scale.lower != LOWER_DEFAULT) { + control_scale.lower = control_scale.lower / scale_h; + } + if (control_scale.upper != UPPER_DEFAULT) { + control_scale.upper = control_scale.upper / scale_h; + } + + // solve the scaled problem + + solveSubproblemMain(n, radius_scale, f_scale, c_scale, h_scale, x, + control_scale, inform); + + // unscale the solution, function value, multiplier and related values + + // x = ( scale_c / scale_h ) * x + x *= scale_c / scale_h; + inform.obj *= pow(scale_c, 2) / scale_h; + inform.multiplier *= scale_h; + inform.pole *= scale_h; + for (size_t i = 0; i < inform.history.size(); + ++i) { // do i = 1, inform.len_history + inform.history[i].lambda *= scale_h; + inform.history[i].x_norm *= scale_c / scale_h; + } +} + +} // namespace + +/** Solve the trust-region subproblem using +* the DTRS method from Galahad +* +* This method needs H to be diagonal, so we need to +* pre-process +* +* main output d, the soln to the TR subproblem +* @param J :: The Jacobian. +* @param f :: The residuals. +* @param hf :: The Hessian (sort of). +* @param Delta :: The raduis of the trust region. +* @param d :: The output vector of corrections to the parameters giving the +* solution to the TR subproblem. +* @param normd :: The 2-norm of d. +* @param options :: The options. +*/ +void TrustRegionMinimizer::calculateStep(const DoubleFortranMatrix &J, + const DoubleFortranVector &f, + const DoubleFortranMatrix &hf, + double Delta, DoubleFortranVector &d, + double &normd, + const NLLS::nlls_options &options) { + + control_type controlOptions; + inform_type inform; + + // The code finds + // d = arg min_p w^T p + 0.5 * p^T D p + // s.t. ||p|| \leq Delta + // + // where D is diagonal + // + // our probem in naturally in the form + // + // d = arg min_p v^T p + 0.5 * p^T H p + // s.t. ||p|| \leq Delta + // + // first, find the matrix H and vector v + // Set A = J^T J + NLLS::matmultInner(J, m_A); + // add any second order information... + // so A = J^T J + HF + m_A += hf; + + // now form v = J^T f + NLLS::multJt(J, f, m_v); + + // if scaling needed, do it + if (options.scale != 0) { + applyScaling(J, m_A, m_v, m_scale, options); + } + + // Now that we have the unprocessed matrices, we need to get an + // eigendecomposition to make A diagonal + // + NLLS::allEigSymm(m_A, m_ew, m_ev); + + // We can now change variables, setting y = Vp, getting + // Vd = arg min_(Vx) v^T p + 0.5 * (Vp)^T D (Vp) + // s.t. ||x|| \leq Delta + // <=> + // Vd = arg min_(Vx) V^Tv^T (Vp) + 0.5 * (Vp)^T D (Vp) + // s.t. ||x|| \leq Delta + // <=> + // we need to get the transformed vector v + NLLS::multJt(m_ev, m_v, m_v_trans); + + // we've now got the vectors we need, pass to solveSubproblem + intitializeControl(controlOptions); + + auto n = J.len2(); + if (m_v_trans.len() != n) { + m_v_trans.allocate(n); + } + + for (int ii = 1; ii <= n; ++ii) { // for_do(ii, 1,n) + if (fabs(m_v_trans(ii)) < EPSILON_MCH) { + m_v_trans(ii) = ZERO; + } + if (fabs(m_ew(ii)) < EPSILON_MCH) { + m_ew(ii) = ZERO; + } + } + + solveSubproblem(n, Delta, ZERO, m_v_trans, m_ew, m_d_trans, controlOptions, + inform); + + // and return the un-transformed vector + NLLS::multJ(m_ev, m_d_trans, d); + + normd = NLLS::norm2(d); // ! ||d||_D + + if (options.scale != 0) { + for (int ii = 1; ii <= n; ++ii) { // for_do(ii, 1, n) + d(ii) = d(ii) / m_scale(ii); + } + } + +} // calculateStep + /** Return the current value of the cost function. */ double TrustRegionMinimizer::costFunctionVal() { return m_leastSquares->val(); } diff --git a/Framework/CurveFitting/test/FuncMinimizers/MoreSorensenTest.h b/Framework/CurveFitting/test/FuncMinimizers/MoreSorensenTest.h deleted file mode 100644 index 5d4265a6da5b158951c5137a3083972abe210cc5..0000000000000000000000000000000000000000 --- a/Framework/CurveFitting/test/FuncMinimizers/MoreSorensenTest.h +++ /dev/null @@ -1,329 +0,0 @@ -#ifndef CURVEFITTING_MORESORNSENTTEST_H_ -#define CURVEFITTING_MORESORNSENTTEST_H_ - -#include <cxxtest/TestSuite.h> - -#include "MantidCurveFitting/CostFunctions/CostFuncLeastSquares.h" -#include "MantidCurveFitting/FuncMinimizers/MoreSorensenMinimizer.h" -#include "MantidCurveFitting/Functions/UserFunction.h" -#include "MantidAPI/FunctionDomain1D.h" -#include "MantidAPI/FunctionValues.h" -#include "MantidCurveFitting/Constraints/BoundaryConstraint.h" - -#include <sstream> - -using namespace Mantid; -using namespace Mantid::CurveFitting; -using namespace Mantid::CurveFitting::FuncMinimisers; -using namespace Mantid::CurveFitting::CostFunctions; -using namespace Mantid::CurveFitting::Constraints; -using namespace Mantid::CurveFitting::Functions; -using namespace Mantid::API; - -class MoreSorensenTest : public CxxTest::TestSuite { -public: - // This pair of boilerplate methods prevent the suite being created statically - // This means the constructor isn't called when running other tests - static MoreSorensenTest *createSuite() { return new MoreSorensenTest(); } - static void destroySuite(MoreSorensenTest *suite) { delete suite; } - - void test_Linear() { - API::FunctionDomain1D_sptr domain( - new API::FunctionDomain1DVector(0.0, 10.0, 20)); - API::FunctionValues mockData(*domain); - UserFunction dataMaker; - dataMaker.setAttributeValue("Formula", "a*x+b"); - dataMaker.setParameter("a", 1.1); - dataMaker.setParameter("b", 2.2); - dataMaker.function(*domain, mockData); - - API::FunctionValues_sptr values(new API::FunctionValues(*domain)); - values->setFitDataFromCalculated(mockData); - values->setFitWeights(1.0); - - boost::shared_ptr<UserFunction> fun = boost::make_shared<UserFunction>(); - fun->setAttributeValue("Formula", "a*x+b"); - fun->setParameter("a", 1.); - fun->setParameter("b", 2.); - - boost::shared_ptr<CostFuncLeastSquares> costFun = - boost::make_shared<CostFuncLeastSquares>(); - costFun->setFittingFunction(fun, domain, values); - TS_ASSERT_EQUALS(costFun->nParams(), 2); - - MoreSorensenMinimizer s; - s.initialize(costFun); - TS_ASSERT(s.minimize()); - - TS_ASSERT_DELTA(fun->getParameter("a"), 1.1, 0.01); - TS_ASSERT_DELTA(fun->getParameter("b"), 2.2, 0.01); - TS_ASSERT_EQUALS(s.getError(), "success"); - } - - void test_Gaussian() { - API::FunctionDomain1D_sptr domain( - new API::FunctionDomain1DVector(0.0, 10.0, 20)); - API::FunctionValues mockData(*domain); - UserFunction dataMaker; - dataMaker.setAttributeValue("Formula", "a*x+b+h*exp(-s*x^2)"); - dataMaker.setParameter("a", 1.1); - dataMaker.setParameter("b", 2.2); - dataMaker.setParameter("h", 3.3); - dataMaker.setParameter("s", 0.2); - dataMaker.function(*domain, mockData); - - API::FunctionValues_sptr values(new API::FunctionValues(*domain)); - values->setFitDataFromCalculated(mockData); - values->setFitWeights(1.0); - - boost::shared_ptr<UserFunction> fun = boost::make_shared<UserFunction>(); - fun->setAttributeValue("Formula", "a*x+b+h*exp(-s*x^2)"); - fun->setParameter("a", 1.); - fun->setParameter("b", 2.); - fun->setParameter("h", 3.); - fun->setParameter("s", 0.1); - - boost::shared_ptr<CostFuncLeastSquares> costFun = - boost::make_shared<CostFuncLeastSquares>(); - costFun->setFittingFunction(fun, domain, values); - - MoreSorensenMinimizer s; - s.initialize(costFun); - TS_ASSERT(s.minimize()); - TS_ASSERT_DELTA(costFun->val(), 0.0, 0.0001); - TS_ASSERT_DELTA(fun->getParameter("a"), 1.1, 0.001); - TS_ASSERT_DELTA(fun->getParameter("b"), 2.2, 0.001); - TS_ASSERT_DELTA(fun->getParameter("h"), 3.3, 0.001); - TS_ASSERT_DELTA(fun->getParameter("s"), 0.2, 0.001); - TS_ASSERT_EQUALS(s.getError(), "success"); - } - - void test_Gaussian_fixed() { - API::FunctionDomain1D_sptr domain( - new API::FunctionDomain1DVector(0.0, 10.0, 20)); - API::FunctionValues mockData(*domain); - UserFunction dataMaker; - dataMaker.setAttributeValue("Formula", "a*x+b+h*exp(-s*x^2)"); - dataMaker.setParameter("a", 1.1); - dataMaker.setParameter("b", 2.2); - dataMaker.setParameter("h", 3.3); - dataMaker.setParameter("s", 0.2); - dataMaker.function(*domain, mockData); - - API::FunctionValues_sptr values(new API::FunctionValues(*domain)); - values->setFitDataFromCalculated(mockData); - values->setFitWeights(1.0); - - boost::shared_ptr<UserFunction> fun = boost::make_shared<UserFunction>(); - fun->setAttributeValue("Formula", "a*x+b+h*exp(-s*x^2)"); - fun->setParameter("a", 1.); - fun->setParameter("b", 2.5); - fun->setParameter("h", 3.); - fun->setParameter("s", 0.1); - fun->fix(0); - - boost::shared_ptr<CostFuncLeastSquares> costFun = - boost::make_shared<CostFuncLeastSquares>(); - costFun->setFittingFunction(fun, domain, values); - TS_ASSERT_EQUALS(costFun->nParams(), 3); - - MoreSorensenMinimizer s; - s.initialize(costFun); - TS_ASSERT(s.minimize()); - TS_ASSERT_DELTA(costFun->val(), 0.2, 0.01); - TS_ASSERT_DELTA(fun->getParameter("a"), 1., 0.000001); - TS_ASSERT_DELTA(fun->getParameter("b"), 2.90, 0.01); - TS_ASSERT_DELTA(fun->getParameter("h"), 2.67, 0.01); - TS_ASSERT_DELTA(fun->getParameter("s"), 0.27, 0.01); - TS_ASSERT_EQUALS(s.getError(), "success"); - } - - void test_Gaussian_tied() { - API::FunctionDomain1D_sptr domain( - new API::FunctionDomain1DVector(0.0, 10.0, 20)); - API::FunctionValues mockData(*domain); - UserFunction dataMaker; - dataMaker.setAttributeValue("Formula", "a*x+b+h*exp(-s*x^2)"); - dataMaker.setParameter("a", 1.1); - dataMaker.setParameter("b", 2.2); - dataMaker.setParameter("h", 3.3); - dataMaker.setParameter("s", 0.2); - dataMaker.function(*domain, mockData); - - API::FunctionValues_sptr values(new API::FunctionValues(*domain)); - values->setFitDataFromCalculated(mockData); - values->setFitWeights(1.0); - - boost::shared_ptr<UserFunction> fun = boost::make_shared<UserFunction>(); - fun->setAttributeValue("Formula", "a*x+b+h*exp(-s*x^2)"); - fun->setParameter("a", 1.); - fun->setParameter("b", 2.5); - fun->setParameter("h", 3.); - fun->setParameter("s", 0.1); - fun->tie("a", "1"); - - boost::shared_ptr<CostFuncLeastSquares> costFun = - boost::make_shared<CostFuncLeastSquares>(); - costFun->setFittingFunction(fun, domain, values); - TS_ASSERT_EQUALS(costFun->nParams(), 3); - - MoreSorensenMinimizer s; - s.initialize(costFun); - TS_ASSERT(s.minimize()); - TS_ASSERT_DELTA(costFun->val(), 0.2, 0.01); - TS_ASSERT_DELTA(fun->getParameter("a"), 1., 0.000001); - TS_ASSERT_DELTA(fun->getParameter("b"), 2.90, 0.01); - TS_ASSERT_DELTA(fun->getParameter("h"), 2.67, 0.01); - TS_ASSERT_DELTA(fun->getParameter("s"), 0.27, 0.01); - TS_ASSERT_EQUALS(s.getError(), "success"); - } - - void xtest_Gaussian_tied_with_formula() { - API::FunctionDomain1D_sptr domain( - new API::FunctionDomain1DVector(0.0, 10.0, 20)); - API::FunctionValues mockData(*domain); - UserFunction dataMaker; - dataMaker.setAttributeValue("Formula", "a*x+b+h*exp(-s*x^2)"); - dataMaker.setParameter("a", 1.1); - dataMaker.setParameter("b", 2.2); - dataMaker.setParameter("h", 3.3); - dataMaker.setParameter("s", 0.2); - dataMaker.function(*domain, mockData); - - API::FunctionValues_sptr values(new API::FunctionValues(*domain)); - values->setFitDataFromCalculated(mockData); - values->setFitWeights(1.0); - - boost::shared_ptr<UserFunction> fun = boost::make_shared<UserFunction>(); - fun->setAttributeValue("Formula", "a*x+b+h*exp(-s*x^2)"); - fun->setParameter("a", 1.); - fun->setParameter("b", 2.); - fun->setParameter("h", 3.); - fun->setParameter("s", 0.1); - fun->tie("b", "2*a+0.1"); - - boost::shared_ptr<CostFuncLeastSquares> costFun = - boost::make_shared<CostFuncLeastSquares>(); - costFun->setFittingFunction(fun, domain, values); - TS_ASSERT_EQUALS(costFun->nParams(), 3); - - MoreSorensenMinimizer s; - s.initialize(costFun); - TS_ASSERT(s.minimize()); - TS_ASSERT_DELTA(costFun->val(), 0.002, 0.01); - - double a = fun->getParameter("a"); - TS_ASSERT_DELTA(a, 1.0895, 0.01); - TS_ASSERT_DELTA(fun->getParameter("b"), 2 * a + 0.1, 0.0001); - TS_ASSERT_DELTA(fun->getParameter("h"), 3.23, 0.01); - TS_ASSERT_DELTA(fun->getParameter("s"), 0.207, 0.001); - TS_ASSERT_EQUALS(s.getError(), "success"); - } - - void xtest_Linear_constrained() { - API::FunctionDomain1D_sptr domain( - new API::FunctionDomain1DVector(0.0, 10.0, 20)); - API::FunctionValues mockData(*domain); - UserFunction dataMaker; - dataMaker.setAttributeValue("Formula", "a*x+b"); - dataMaker.setParameter("a", 1.1); - dataMaker.setParameter("b", 2.2); - dataMaker.function(*domain, mockData); - - API::FunctionValues_sptr values(new API::FunctionValues(*domain)); - values->setFitDataFromCalculated(mockData); - values->setFitWeights(1.0); - - boost::shared_ptr<UserFunction> fun = boost::make_shared<UserFunction>(); - fun->setAttributeValue("Formula", "a*x+b"); - fun->setParameter("a", 1.); - fun->setParameter("b", 2.); - - fun->addConstraint( - Kernel::make_unique<BoundaryConstraint>(fun.get(), "a", 0, 0.5)); - - boost::shared_ptr<CostFuncLeastSquares> costFun = - boost::make_shared<CostFuncLeastSquares>(); - costFun->setFittingFunction(fun, domain, values); - TS_ASSERT_EQUALS(costFun->nParams(), 2); - - MoreSorensenMinimizer s; - s.initialize(costFun); - TS_ASSERT(s.minimize()); - - TS_ASSERT_DELTA(fun->getParameter("a"), 0.5, 0.1); - TS_ASSERT_DELTA(fun->getParameter("b"), 5.2, 0.2); - TS_ASSERT_EQUALS(s.getError(), "success"); - } - - void xtest_Linear_constrained1() { - API::FunctionDomain1D_sptr domain( - new API::FunctionDomain1DVector(0.0, 10.0, 20)); - API::FunctionValues mockData(*domain); - UserFunction dataMaker; - dataMaker.setAttributeValue("Formula", "a^2*x+b"); - dataMaker.setParameter("a", 1); - dataMaker.setParameter("b", 2); - dataMaker.function(*domain, mockData); - - API::FunctionValues_sptr values(new API::FunctionValues(*domain)); - values->setFitDataFromCalculated(mockData); - values->setFitWeights(1.0); - - boost::shared_ptr<UserFunction> fun = boost::make_shared<UserFunction>(); - fun->setAttributeValue("Formula", "a^2*x+b"); - fun->setParameter("a", -0.5); - fun->setParameter("b", 2.2); - - // lower bound is made > 0 because function's derivative over "a" at a=0 is - // 0 - fun->addConstraint( - Kernel::make_unique<BoundaryConstraint>(fun.get(), "a", 0.001, 2.0)); - - boost::shared_ptr<CostFuncLeastSquares> costFun = - boost::make_shared<CostFuncLeastSquares>(); - costFun->setFittingFunction(fun, domain, values); - TS_ASSERT_EQUALS(costFun->nParams(), 2); - - MoreSorensenMinimizer s; - s.initialize(costFun); - TS_ASSERT(s.minimize()); - - // std::cerr << "a=" << fun->getParameter("a") << '\n'; - // std::cerr << "b=" << fun->getParameter("b") << '\n'; - - TS_ASSERT_DELTA(costFun->val(), 0.00, 0.0001); - TS_ASSERT_DELTA(fun->getParameter("a"), 1.0, 0.01); - TS_ASSERT_DELTA(fun->getParameter("b"), 2.0, 0.01); - TS_ASSERT_EQUALS(s.getError(), "success"); - } - - void xtest_cannot_reach_tolerance() { - API::FunctionDomain1D_sptr domain( - new API::FunctionDomain1DVector(0.0, 1.0, 10)); - API::FunctionValues mockData(*domain); - UserFunction dataMaker; - dataMaker.setAttributeValue("Formula", "a*x"); - dataMaker.setParameter("a", 1.0); - dataMaker.function(*domain, mockData); - - API::FunctionValues_sptr values(new API::FunctionValues(*domain)); - values->setFitDataFromCalculated(mockData); - values->setFitWeights(1.0); - - boost::shared_ptr<UserFunction> fun = boost::make_shared<UserFunction>(); - fun->setAttributeValue("Formula", "a+b+0*x"); - - boost::shared_ptr<CostFuncLeastSquares> costFun = - boost::make_shared<CostFuncLeastSquares>(); - costFun->setFittingFunction(fun, domain, values); - - MoreSorensenMinimizer s; - s.initialize(costFun); - TS_ASSERT(!s.minimize()); - - TS_ASSERT_EQUALS(s.getError(), "cannot reach the specified tolerance in F"); - } -}; - -#endif /*CURVEFITTING_MORESORNSENTTEST_H_*/ diff --git a/Framework/CurveFitting/test/FuncMinimizers/DTRSMinimizerTest.h b/Framework/CurveFitting/test/FuncMinimizers/TrustRegionMinimizerTest.h similarity index 93% rename from Framework/CurveFitting/test/FuncMinimizers/DTRSMinimizerTest.h rename to Framework/CurveFitting/test/FuncMinimizers/TrustRegionMinimizerTest.h index 7712495c097799f9350ff12651c135d3776e6cc3..84dc5edc84b21210b4aeabdff0cc76e45d3f986d 100644 --- a/Framework/CurveFitting/test/FuncMinimizers/DTRSMinimizerTest.h +++ b/Framework/CurveFitting/test/FuncMinimizers/TrustRegionMinimizerTest.h @@ -1,10 +1,10 @@ -#ifndef CURVEFITTING_DTRSMINIMIZERTTEST_H_ -#define CURVEFITTING_DTRSMINIMIZERTTEST_H_ +#ifndef CURVEFITTING_TRUSTREGIONMINIMIZERTTEST_H_ +#define CURVEFITTING_TRUSTREGIONMINIMIZERTTEST_H_ #include <cxxtest/TestSuite.h> #include "MantidCurveFitting/CostFunctions/CostFuncLeastSquares.h" -#include "MantidCurveFitting/FuncMinimizers/DTRSMinimizer.h" +#include "MantidCurveFitting/FuncMinimizers/TrustRegionMinimizer.h" #include "MantidCurveFitting/Functions/UserFunction.h" #include "MantidAPI/FunctionDomain1D.h" #include "MantidAPI/FunctionValues.h" @@ -20,12 +20,14 @@ using namespace Mantid::CurveFitting::Constraints; using namespace Mantid::CurveFitting::Functions; using namespace Mantid::API; -class DTRSMinimizerTest : public CxxTest::TestSuite { +class TrustRegionMinimizerTest : public CxxTest::TestSuite { public: // This pair of boilerplate methods prevent the suite being created statically // This means the constructor isn't called when running other tests - static DTRSMinimizerTest *createSuite() { return new DTRSMinimizerTest(); } - static void destroySuite(DTRSMinimizerTest *suite) { delete suite; } + static TrustRegionMinimizerTest *createSuite() { + return new TrustRegionMinimizerTest(); + } + static void destroySuite(TrustRegionMinimizerTest *suite) { delete suite; } void test_Linear() { API::FunctionDomain1D_sptr domain( @@ -51,7 +53,7 @@ public: costFun->setFittingFunction(fun, domain, values); TS_ASSERT_EQUALS(costFun->nParams(), 2); - DTRSMinimizer s; + TrustRegionMinimizer s; s.initialize(costFun); TS_ASSERT(s.minimize()); @@ -87,7 +89,7 @@ public: boost::make_shared<CostFuncLeastSquares>(); costFun->setFittingFunction(fun, domain, values); - DTRSMinimizer s; + TrustRegionMinimizer s; s.initialize(costFun); TS_ASSERT(s.minimize()); TS_ASSERT_DELTA(costFun->val(), 0.0, 0.0001); @@ -127,7 +129,7 @@ public: costFun->setFittingFunction(fun, domain, values); TS_ASSERT_EQUALS(costFun->nParams(), 3); - DTRSMinimizer s; + TrustRegionMinimizer s; s.initialize(costFun); TS_ASSERT(s.minimize()); // TS_ASSERT_DELTA(costFun->val(), 0.2, 0.01); @@ -167,7 +169,7 @@ public: costFun->setFittingFunction(fun, domain, values); TS_ASSERT_EQUALS(costFun->nParams(), 3); - DTRSMinimizer s; + TrustRegionMinimizer s; s.initialize(costFun); TS_ASSERT(s.minimize()); // TS_ASSERT_DELTA(costFun->val(), 0.2, 0.01); @@ -205,7 +207,7 @@ public: costFun->setFittingFunction(fun, domain, values); TS_ASSERT_EQUALS(costFun->nParams(), 2); - DTRSMinimizer s; + TrustRegionMinimizer s; s.initialize(costFun); TS_ASSERT(s.minimize()); @@ -243,7 +245,7 @@ public: costFun->setFittingFunction(fun, domain, values); TS_ASSERT_EQUALS(costFun->nParams(), 2); - DTRSMinimizer s; + TrustRegionMinimizer s; s.initialize(costFun); TS_ASSERT(s.minimize()); @@ -254,4 +256,4 @@ public: } }; -#endif /*CURVEFITTING_DTRSMINIMIZERTTEST_H_*/ +#endif /*CURVEFITTING_TRUSTREGIONMINIMIZERTTEST_H_*/ diff --git a/Framework/CurveFitting/test/RalNlls/NLLSTest.h b/Framework/CurveFitting/test/RalNlls/NLLSTest.h index 94337fb13bf1564c746c5be59f78579c007ca852..ae6dc165970b3004d3af2cbe5efd2ea14a01010e 100644 --- a/Framework/CurveFitting/test/RalNlls/NLLSTest.h +++ b/Framework/CurveFitting/test/RalNlls/NLLSTest.h @@ -16,19 +16,6 @@ using namespace Mantid::API; class NLLSTest : public CxxTest::TestSuite { public: - void test_More_Sorensen_ExpDecay() { - auto ws = make_exp_decay_workspace(); - Fit fit; - fit.initialize(); - fit.setPropertyValue("Function", "name=ExpDecay"); - fit.setProperty("InputWorkspace", ws); - fit.setProperty("Minimizer", "More-Sorensen"); - fit.execute(); - IFunction_sptr fun = fit.getProperty("Function"); - TS_ASSERT_DELTA(fun->getParameter(0), 60.195, 0.001); - TS_ASSERT_DELTA(fun->getParameter(1), 2.16815, 0.00001); - } - void test_Galahad_ExpDecay() { auto ws = make_exp_decay_workspace(); Fit fit; diff --git a/Framework/DataObjects/inc/MantidDataObjects/PeaksWorkspace.h b/Framework/DataObjects/inc/MantidDataObjects/PeaksWorkspace.h index 9d122261e5cbac7c2551c1ab99ab934c88b1c919..2bdbd533ff90e4c116fa85a5fb295ed050095792 100644 --- a/Framework/DataObjects/inc/MantidDataObjects/PeaksWorkspace.h +++ b/Framework/DataObjects/inc/MantidDataObjects/PeaksWorkspace.h @@ -283,11 +283,6 @@ private: /// Coordinates Kernel::SpecialCoordinateSystem m_coordSystem; - - // adapter for logs() function, which create reference to this class itself - // and does not allow to delete the shared pointers, - // returned by logs() function when they go out of scope - API::LogManager_sptr m_logCash; }; /// Typedef for a shared pointer to a peaks workspace. diff --git a/Framework/DataObjects/inc/MantidDataObjects/WorkspaceCreation.h b/Framework/DataObjects/inc/MantidDataObjects/WorkspaceCreation.h index 572cc2c8032cc9cbc1b8e48234faf1c4fb0d9c5c..7a3d895a65962c561b7996c1fcbf5202b00bcabf 100644 --- a/Framework/DataObjects/inc/MantidDataObjects/WorkspaceCreation.h +++ b/Framework/DataObjects/inc/MantidDataObjects/WorkspaceCreation.h @@ -145,10 +145,6 @@ fixDistributionFlag(API::MatrixWorkspace &workspace, MANTID_DATAOBJECTS_DLL void initializeFromParent(const API::MatrixWorkspace &parent, API::MatrixWorkspace &ws); - -MANTID_DATAOBJECTS_DLL void -initializeFromParentWithoutLogs(const API::MatrixWorkspace &parent, - API::MatrixWorkspace &ws); } /** This is the create() method that all the other create() methods call. @@ -193,46 +189,6 @@ std::unique_ptr<T> create(const P &parent, const IndexArg &indexArg, return ws; } -/** create a new workspace with empty run, i.e., without logs - * this is for initializeFromParentWithoutLogs - */ -template <class T, class P, class IndexArg, class HistArg, - class = typename std::enable_if< - std::is_base_of<API::MatrixWorkspace, P>::value>::type> -std::unique_ptr<T> createWithoutLogs(const P &parent, const IndexArg &indexArg, - const HistArg &histArg) { - // Figure out (dynamic) target type: - // - Type is same as parent if T is base of parent - // - If T is not base of parent, conversion may occur. Currently only - // supported for EventWorkspace - std::unique_ptr<T> ws; - if (std::is_base_of<API::HistoWorkspace, T>::value && - parent.id() == "EventWorkspace") { - // Drop events, create Workspace2D or T whichever is more derived. - ws = detail::createHelper<T>(); - } else { - try { - // If parent is more derived than T: create type(parent) - ws = dynamic_cast<const T &>(parent).cloneEmpty(); - } catch (std::bad_cast &) { - // If T is more derived than parent: create T - ws = detail::createConcreteHelper<T>(); - } - } - - // The instrument is also copied by initializeFromParentWithoutLogs, - // but if indexArg is - // IndexInfo and contains non-empty spectrum definitions the initialize call - // will fail due to invalid indices in the spectrum definitions. Therefore, we - // copy the instrument first. This should be cleaned up once we figure out the - // future of WorkspaceFactory. - ws->setInstrument(parent.getInstrument()); - ws->initialize(indexArg, HistogramData::Histogram(histArg)); - detail::initializeFromParentWithoutLogs(parent, *ws); - detail::fixDistributionFlag(*ws, histArg); - return ws; -} - template <class T, class IndexArg, class HistArg, typename std::enable_if< !std::is_base_of<API::MatrixWorkspace, IndexArg>::value>::type * = @@ -269,19 +225,6 @@ std::unique_ptr<T> create(const P &parent) { return ws; } -template <class T, class P, - typename std::enable_if<std::is_base_of<API::MatrixWorkspace, - P>::value>::type * = nullptr> -std::unique_ptr<T> createWithoutLogs(const P &parent) { - const auto numHistograms = parent.getNumberHistograms(); - auto ws = createWithoutLogs<T>(parent, numHistograms, - detail::stripData(parent.histogram(0))); - for (size_t i = 0; i < numHistograms; ++i) { - ws->setSharedX(i, parent.sharedX(i)); - } - return ws; -} - // Templating with HistArg clashes with the IndexArg template above. Could be // fixed with many enable_if cases, but for now we simply provide 3 variants // (Histogram, BinEdges, Points) by hand. diff --git a/Framework/DataObjects/src/PeaksWorkspace.cpp b/Framework/DataObjects/src/PeaksWorkspace.cpp index d38c71e134721f311ab3efe3b8022464955b96ca..301c714e2c6f5a474e161fe5bc73a93c2e900315 100644 --- a/Framework/DataObjects/src/PeaksWorkspace.cpp +++ b/Framework/DataObjects/src/PeaksWorkspace.cpp @@ -877,15 +877,9 @@ PeaksWorkspace::getSpecialCoordinateSystem() const { struct NullDeleter { template <typename T> void operator()(T *) {} }; -/**Get access to shared pointer containing workspace porperties, cashes the - shared pointer - into internal class variable to not allow shared pointer being deleted */ +/**Get access to shared pointer containing workspace porperties */ API::LogManager_sptr PeaksWorkspace::logs() { - if (m_logCash) - return m_logCash; - - m_logCash = API::LogManager_sptr(&(this->mutableRun()), NullDeleter()); - return m_logCash; + return API::LogManager_sptr(&(this->mutableRun()), NullDeleter()); } /** Get constant access to shared pointer containing workspace porperties; diff --git a/Framework/DataObjects/src/WorkspaceCreation.cpp b/Framework/DataObjects/src/WorkspaceCreation.cpp index ee7eec6e4da2548fd14374624570d646ea2e19f1..92ecff01b8353a8025e330d6a02b29052576f128 100644 --- a/Framework/DataObjects/src/WorkspaceCreation.cpp +++ b/Framework/DataObjects/src/WorkspaceCreation.cpp @@ -52,24 +52,6 @@ void initializeFromParent(const API::MatrixWorkspace &parent, static_cast<void>(ws.mutableX(0)); } -/** Initialize a MatrixWorkspace from its parent including instrument, unit, - * number of spectra but without Run (i.e., logs) - * @brief initializeFromParentWithoutLogs - * @param parent - * @param ws - */ -void initializeFromParentWithoutLogs(const API::MatrixWorkspace &parent, - API::MatrixWorkspace &ws) { - bool differentSize = (parent.x(0).size() != ws.x(0).size()) || - (parent.y(0).size() != ws.y(0).size()); - API::WorkspaceFactory::Instance().initializeFromParentWithoutLogs( - parent, ws, differentSize); - // For EventWorkspace, `ws.y(0)` put entry 0 in the MRU. However, clients - // would typically expect an empty MRU and fail to clear it. This dummy call - // removes the entry from the MRU. - static_cast<void>(ws.mutableX(0)); -} - template <> void fixDistributionFlag(API::MatrixWorkspace &workspace, const HistogramData::Histogram &histArg) { diff --git a/Framework/DataObjects/test/WorkspaceCreationTest.h b/Framework/DataObjects/test/WorkspaceCreationTest.h index 7aba97e764b0e0ff9746a0ec22625d3d908b95ba..dccc28d59ee2daf4cd37e614521de4aaf4094033 100644 --- a/Framework/DataObjects/test/WorkspaceCreationTest.h +++ b/Framework/DataObjects/test/WorkspaceCreationTest.h @@ -265,7 +265,9 @@ public: const std::string &name1 = "Log4"; const std::string &value1 = "6.4a"; parent->mutableRun().addProperty(name1, value1); - const auto ws = createWithoutLogs<Workspace2D>(*parent); + const auto ws = create<Workspace2D>(*parent); + TS_ASSERT_EQUALS(&parent->run(), &ws->run()); + ws->setSharedRun(Kernel::make_cow<Run>()); check_indices(*ws); check_zeroed_data(*ws); check_instrument(*ws); diff --git a/Framework/Indexing/CMakeLists.txt b/Framework/Indexing/CMakeLists.txt index b3d274a33d63b6a6b30f0c1f0f9e465b38956e95..e5b69e1a9de4cb53c493f6632026201e14b6fb41 100644 --- a/Framework/Indexing/CMakeLists.txt +++ b/Framework/Indexing/CMakeLists.txt @@ -9,6 +9,7 @@ set ( SRC_FILES ) set ( INC_FILES + inc/MantidIndexing/Conversion.h inc/MantidIndexing/DetectorID.h inc/MantidIndexing/DllConfig.h inc/MantidIndexing/Extract.h @@ -27,6 +28,7 @@ set ( INC_FILES ) set ( TEST_FILES + ConversionTest.h DetectorIDTest.h ExtractTest.h GlobalSpectrumIndexTest.h diff --git a/Framework/Indexing/inc/MantidIndexing/Conversion.h b/Framework/Indexing/inc/MantidIndexing/Conversion.h new file mode 100644 index 0000000000000000000000000000000000000000..01a24ac96ee6b30db13af350acf4eb613a3058ca --- /dev/null +++ b/Framework/Indexing/inc/MantidIndexing/Conversion.h @@ -0,0 +1,79 @@ +#ifndef MANTID_INDEXING_CONVERSION_H_ +#define MANTID_INDEXING_CONVERSION_H_ + +#include "MantidIndexing/DllConfig.h" +#include "MantidIndexing/GlobalSpectrumIndex.h" +#include "MantidIndexing/SpectrumNumber.h" + +namespace Mantid { +namespace Indexing { + +/** Conversion helpers from and to (vectors of) integer types such as + SpectrumNumber and GlobalSpectrumIndex. + + @author Simon Heybrock + @date 2017 + + Copyright © 2017 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge + National Laboratory & European Spallation Source + + 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/>. + + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ + +/** Convert std::vector<In> to std::vector<Out> by static-casting all elements. + * + * `In` must be an integral type and `Out` can be any type inheriting + * Indexing::IndexType, such as SpectrumNumber or GlobalSpectrumIndex. The + * conversion is done using static_cast without any range check. It is the + * responsibility of the caller to not pass any vectors containing elements that + * suffer from loss of data in a static_cast. */ +template < + class Out, class In, + typename std::enable_if<std::is_integral<In>::value>::type * = nullptr> +std::vector<Out> castVector(const std::vector<In> &indices) { + std::vector<Out> converted; + converted.reserve(indices.size()); + for (const auto index : indices) + converted.push_back(static_cast<typename Out::underlying_type>(index)); + return converted; +} + +/** Convert std::vector<In> to std::vector<Out> by static-casting all elements. + * + * `Out` must be an integral type and `In` can be any type inheriting + * Indexing::IndexType, such as SpectrumNumber or GlobalSpectrumIndex. The + * conversion is done using static_cast without any range check. It is the + * responsibility of the caller to not pass any vectors containing elements that + * suffer from loss of data in a static_cast. */ +template < + class Out, class In, + typename std::enable_if<!std::is_integral<In>::value>::type * = nullptr> +std::vector<Out> castVector(const std::vector<In> &indices) { + std::vector<Out> converted; + converted.reserve(indices.size()); + for (const auto index : indices) + converted.push_back( + static_cast<Out>(static_cast<typename In::underlying_type>(index))); + return converted; +} + +} // namespace Indexing +} // namespace Mantid + +#endif /* MANTID_INDEXING_CONVERSION_H_ */ diff --git a/Framework/Indexing/inc/MantidIndexing/IndexInfo.h b/Framework/Indexing/inc/MantidIndexing/IndexInfo.h index a9e3267c6cf46d4e2c80e344ae3814dea4b905b3..ded7d80947676efd3964bfb915c3ced210242a57 100644 --- a/Framework/Indexing/inc/MantidIndexing/IndexInfo.h +++ b/Framework/Indexing/inc/MantidIndexing/IndexInfo.h @@ -114,6 +114,9 @@ public: SpectrumIndexSet makeIndexSet(const std::vector<GlobalSpectrumIndex> &globalIndices) const; + std::vector<GlobalSpectrumIndex> globalSpectrumIndicesFromDetectorIndices( + const std::vector<size_t> &detectorIndices) const; + bool isOnThisPartition(GlobalSpectrumIndex globalIndex) const; Parallel::StorageMode storageMode() const; diff --git a/Framework/Indexing/inc/MantidIndexing/IndexType.h b/Framework/Indexing/inc/MantidIndexing/IndexType.h index 0920f29b6b2c3cb7a2d681b364268db96e07bd35..3778d2036e468801181bc85327e8bd4223ad1ae6 100644 --- a/Framework/Indexing/inc/MantidIndexing/IndexType.h +++ b/Framework/Indexing/inc/MantidIndexing/IndexType.h @@ -37,6 +37,7 @@ template <class Derived, class Int, class = typename std::enable_if<std::is_integral<Int>::value>::type> class IndexType { public: + using underlying_type = Int; IndexType() noexcept : m_data(0) {} IndexType(Int data) noexcept : m_data(data) {} explicit operator Int() const noexcept { return m_data; } diff --git a/Framework/Indexing/src/IndexInfo.cpp b/Framework/Indexing/src/IndexInfo.cpp index ed94a21c0f3ea752281ea0bb409c97a9d6f27f84..0f27f32d743d749a200521fbba012f08cbba6cee 100644 --- a/Framework/Indexing/src/IndexInfo.cpp +++ b/Framework/Indexing/src/IndexInfo.cpp @@ -193,6 +193,55 @@ SpectrumIndexSet IndexInfo::makeIndexSet( return m_spectrumNumberTranslator->makeIndexSet(globalIndices); } +/** Map a vector of detector indices to a vector of global spectrum indices. + * + * The mapping is based on the held spectrum definitions. Throws if any spectrum + * maps to more than one detectors. Throws if there is no 1:1 mapping from + * detectors to spectra, such as when some of the detectors have no matching + * spectrum. */ +std::vector<GlobalSpectrumIndex> +IndexInfo::globalSpectrumIndicesFromDetectorIndices( + const std::vector<size_t> &detectorIndices) const { + if (!m_spectrumDefinitions) + throw std::runtime_error("IndexInfo::" + "globalSpectrumIndicesFromDetectorIndices -- no " + "spectrum definitions available"); + std::vector<char> detectorMap; + for (const auto &index : detectorIndices) { + // IndexInfo has no knowledge of the maximum detector index so we workaround + // this knowledge gap by assuming below that any index beyond the end of the + // map is 0. + if (index >= detectorMap.size()) + detectorMap.resize(index + 1, 0); + detectorMap[index] = 1; + } + + std::vector<GlobalSpectrumIndex> spectrumIndices; + for (size_t i = 0; i < size(); ++i) { + const auto &spectrumDefinition = m_spectrumDefinitions->operator[](i); + if (spectrumDefinition.size() == 1) { + const auto detectorIndex = spectrumDefinition[0].first; + if (detectorMap.size() > detectorIndex && + detectorMap[detectorIndex] != 0) { + if (detectorMap[detectorIndex] > 1) + throw std::runtime_error( + "Multiple spectra correspond to the same detector"); + // Increment flag to catch two spectra mapping to same detector. + ++detectorMap[detectorIndex]; + spectrumIndices.push_back(i); + } + } + if (spectrumDefinition.size() > 1) + throw std::runtime_error("SpectrumDefinition contains multiple entries. " + "No unique mapping from detector to spectrum " + "possible"); + } + if (detectorIndices.size() != spectrumIndices.size()) + throw std::runtime_error( + "Some of the requested detectors do not have a corresponding spectrum"); + return spectrumIndices; +} + /// Returns true if the given global index is on this partition. bool IndexInfo::isOnThisPartition(GlobalSpectrumIndex globalIndex) const { // A map from global index to partition might be faster, consider adding this diff --git a/Framework/Indexing/test/ConversionTest.h b/Framework/Indexing/test/ConversionTest.h new file mode 100644 index 0000000000000000000000000000000000000000..c84c095f2aa21c32769426aed290e8f13d01750f --- /dev/null +++ b/Framework/Indexing/test/ConversionTest.h @@ -0,0 +1,57 @@ +#ifndef MANTID_INDEXING_CONVERSIONTEST_H_ +#define MANTID_INDEXING_CONVERSIONTEST_H_ + +#include <cxxtest/TestSuite.h> + +#include "MantidIndexing/Conversion.h" +#include "MantidIndexing/GlobalSpectrumIndex.h" +#include "MantidIndexing/SpectrumNumber.h" + +using namespace Mantid::Indexing; + +class ConversionTest : public CxxTest::TestSuite { +public: + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static ConversionTest *createSuite() { return new ConversionTest(); } + static void destroySuite(ConversionTest *suite) { delete suite; } + + void test_just_a_static_cast() { + std::vector<SpectrumNumber> in{-1}; + TS_ASSERT_EQUALS(castVector<size_t>(in), + (std::vector<size_t>{18446744073709551615ul})); + } + + void test_GlobalSpectrumIndex() { + std::vector<GlobalSpectrumIndex> in{0, 1, 2}; + TS_ASSERT_EQUALS(castVector<size_t>(in), (std::vector<size_t>{0, 1, 2})); + TS_ASSERT_EQUALS(castVector<int64_t>(in), (std::vector<int64_t>{0, 1, 2})); + TS_ASSERT_EQUALS(castVector<int32_t>(in), (std::vector<int32_t>{0, 1, 2})); + } + + void test_to_GlobalSpectrumIndex() { + std::vector<int64_t> in{0, 1, 2}; + TS_ASSERT_EQUALS(castVector<GlobalSpectrumIndex>(in), + (std::vector<GlobalSpectrumIndex>{0, 1, 2})); + std::vector<size_t> in2{0, 1, 2}; + TS_ASSERT_EQUALS(castVector<GlobalSpectrumIndex>(in2), + (std::vector<GlobalSpectrumIndex>{0, 1, 2})); + } + + void test_SpectrumNumber() { + std::vector<SpectrumNumber> in{-1, 1, 2}; + TS_ASSERT_EQUALS(castVector<int64_t>(in), (std::vector<int64_t>{-1, 1, 2})); + TS_ASSERT_EQUALS(castVector<int32_t>(in), (std::vector<int32_t>{-1, 1, 2})); + } + + void test_to_SpectrumNumber() { + std::vector<int32_t> in{-1, 1, 2}; + TS_ASSERT_EQUALS(castVector<SpectrumNumber>(in), + (std::vector<SpectrumNumber>{-1, 1, 2})); + std::vector<int64_t> in2{-1, 1, 2}; + TS_ASSERT_EQUALS(castVector<SpectrumNumber>(in2), + (std::vector<SpectrumNumber>{-1, 1, 2})); + } +}; + +#endif /* MANTID_INDEXING_CONVERSIONTEST_H_ */ diff --git a/Framework/Indexing/test/IndexInfoTest.h b/Framework/Indexing/test/IndexInfoTest.h index 0b5434423faf6cf5f578e5259c938497ca2cee88..7e16ef641f317550f9d78c9cb4fc31629f3c6ae8 100644 --- a/Framework/Indexing/test/IndexInfoTest.h +++ b/Framework/Indexing/test/IndexInfoTest.h @@ -187,6 +187,90 @@ public: TS_ASSERT_EQUALS(info.spectrumDefinitions().get(), defs.get()); } + void test_globalSpectrumIndicesFromDetectorIndices_fails_without_spec_defs() { + IndexInfo info(3); + std::vector<size_t> detectorIndices{6, 8}; + TS_ASSERT_THROWS_EQUALS( + info.globalSpectrumIndicesFromDetectorIndices(detectorIndices), + const std::runtime_error &e, std::string(e.what()), + "IndexInfo::globalSpectrumIndicesFromDetectorIndices -- no spectrum " + "definitions available"); + } + + void test_globalSpectrumIndicesFromDetectorIndices_fails_multiple() { + IndexInfo info(3); + std::vector<size_t> detectorIndices{6, 8}; + std::vector<SpectrumDefinition> specDefs(3); + specDefs[0].add(6); + specDefs[1].add(7); + specDefs[1].add(77); + specDefs[2].add(8); + info.setSpectrumDefinitions(specDefs); + TS_ASSERT_THROWS_EQUALS( + info.globalSpectrumIndicesFromDetectorIndices(detectorIndices), + const std::runtime_error &e, std::string(e.what()), + "SpectrumDefinition contains multiple entries. No unique mapping from " + "detector to spectrum possible"); + } + + void test_globalSpectrumIndicesFromDetectorIndices_fails_missing() { + IndexInfo info(3); + std::vector<size_t> detectorIndices{6, 8}; + std::vector<SpectrumDefinition> specDefs(3); + // Nothing maps to 8 + specDefs[0].add(6); + specDefs[1].add(7); + info.setSpectrumDefinitions(specDefs); + TS_ASSERT_THROWS_EQUALS( + info.globalSpectrumIndicesFromDetectorIndices(detectorIndices), + const std::runtime_error &e, std::string(e.what()), + "Some of the requested detectors do not have a corresponding spectrum"); + } + + void test_globalSpectrumIndicesFromDetectorIndices_fails_conflict() { + IndexInfo info(3); + std::vector<size_t> detectorIndices{6, 8}; + std::vector<SpectrumDefinition> specDefs(3); + // Two indices map to same detector. + specDefs[0].add(6); + specDefs[1].add(6); + specDefs[2].add(8); + info.setSpectrumDefinitions(specDefs); + TS_ASSERT_THROWS_EQUALS( + info.globalSpectrumIndicesFromDetectorIndices(detectorIndices), + const std::runtime_error &e, std::string(e.what()), + "Multiple spectra correspond to the same detector"); + } + + void test_globalSpectrumIndicesFromDetectorIndices_fails_conflict_miss() { + IndexInfo info(3); + std::vector<size_t> detectorIndices{6, 8}; + std::vector<SpectrumDefinition> specDefs(3); + // Two indices map to same detector, but additionally one is missing. + specDefs[0].add(6); + specDefs[1].add(6); + info.setSpectrumDefinitions(specDefs); + TS_ASSERT_THROWS_EQUALS( + info.globalSpectrumIndicesFromDetectorIndices(detectorIndices), + const std::runtime_error &e, std::string(e.what()), + "Multiple spectra correspond to the same detector"); + } + + void test_globalSpectrumIndicesFromDetectorIndices() { + IndexInfo info(3); + std::vector<size_t> detectorIndices{6, 8}; + std::vector<SpectrumDefinition> specDefs(3); + specDefs[0].add(6); + specDefs[1].add(7); + specDefs[2].add(8); + info.setSpectrumDefinitions(specDefs); + const auto &indices = + info.globalSpectrumIndicesFromDetectorIndices(detectorIndices); + TS_ASSERT_EQUALS(indices.size(), detectorIndices.size()); + TS_ASSERT_EQUALS(indices[0], 0); + TS_ASSERT_EQUALS(indices[1], 2); + } + void test_StorageMode_Cloned() { runParallel(run_StorageMode_Cloned); // Trivial: Run with one partition. diff --git a/Framework/PythonInterface/plugins/algorithms/EnggFocus.py b/Framework/PythonInterface/plugins/algorithms/EnggFocus.py index 7e658088f19150698b68417007aa4537b38f98ac..28ecf993a48d5403c04f3ac7fa2bba021ee4fa61 100644 --- a/Framework/PythonInterface/plugins/algorithms/EnggFocus.py +++ b/Framework/PythonInterface/plugins/algorithms/EnggFocus.py @@ -62,7 +62,7 @@ class EnggFocus(PythonAlgorithm): self.declareProperty(self.INDICES_PROP_NAME, '', direction=Direction.Input, doc='Sets the spectrum numbers for the detectors ' - 'that should be considered in the focussing operation (all others will be ' + 'that should be considered in the focusing operation (all others will be ' 'ignored). This option cannot be used together with Bank, as they overlap. ' 'You can give multiple ranges, for example: "0-99", or "0-9, 50-59, 100-109".') @@ -160,8 +160,28 @@ class EnggFocus(PythonAlgorithm): # converting units), so I guess that's what users will expect self._convert_to_distribution(input_ws) + if bank: + self._add_bank_number(input_ws, bank) + self.setProperty("OutputWorkspace", input_ws) + def _bank_to_int(self, bank): + if bank == "North": + return "1" + if bank == "South": + return "2" + if bank in ("1", "2"): + return bank + raise RuntimeError("Invalid value for bank: \"{}\" of type {}".format(bank, type(bank))) + + def _add_bank_number(self, ws, bank): + alg = self.createChildAlgorithm("AddSampleLog") + alg.setProperty("Workspace", ws) + alg.setProperty("LogName", "bankid") + alg.setProperty("LogText", self._bank_to_int(bank)) + alg.setProperty("LogType", "Number") + alg.execute() + def _mask_bins(self, wks, min_bins, max_bins): """ Mask multiple ranges of bins, given multiple pairs min-max diff --git a/Testing/Data/SystemTest/ISIS_Powder/input/PEARL00098494.nxs.md5 b/Testing/Data/SystemTest/ISIS_Powder/input/PEARL00098494.nxs.md5 new file mode 100644 index 0000000000000000000000000000000000000000..bb209053d4c2864afe53af5a6f21b89dd8721439 --- /dev/null +++ b/Testing/Data/SystemTest/ISIS_Powder/input/PEARL00098494.nxs.md5 @@ -0,0 +1 @@ +c13f59f8155dc56e59d0412511b80b5d \ No newline at end of file diff --git a/Testing/SystemTests/tests/analysis/ISIS_PowderPearlTest.py b/Testing/SystemTests/tests/analysis/ISIS_PowderPearlTest.py index 12eba9b03a1fccf20ec220cd46884c632d8a35b0..1b3731a5357d07a680ea3bad2742901ab1ce33df 100644 --- a/Testing/SystemTests/tests/analysis/ISIS_PowderPearlTest.py +++ b/Testing/SystemTests/tests/analysis/ISIS_PowderPearlTest.py @@ -122,9 +122,34 @@ class FocusTest(stresstesting.MantidStressTest): mantid.mtd.clear() +class CreateCalTest(stresstesting.MantidStressTest): + + calibration_results = None + existing_config = config["datasearch.directories"] + + def requiredFiles(self): + return _gen_required_files() + + def runTest(self): + setup_mantid_paths() + self.calibration_results = run_create_cal() + + def valid(self): + return ceria_validator(self.calibration_results) + + def cleanup(self): + try: + _try_delete(spline_path) + _try_delete(output_dir) + finally: + config['datasearch.directories'] = self.existing_config + mantid.mtd.clear() + + def _gen_required_files(): required_run_numbers = ["98472", "98485", # create_van - "98507", "98472_splined"] # Focus (Si) + "98507", "98472_splined", # Focus (Si) + "98494"] # create_cal (Ce) # Generate file names of form "INSTxxxxx.nxs" - PEARL requires 000 padding input_files = [os.path.join(input_dir, (inst_name + "000" + number + ".nxs")) for number in required_run_numbers] @@ -132,6 +157,12 @@ def _gen_required_files(): return input_files +def run_create_cal(): + ceria_run = 98494 + inst_obj = setup_inst_object(tt_mode="tt88", focus_mode="all") + return inst_obj.create_cal(run_number=ceria_run) + + def run_vanadium_calibration(focus_mode): vanadium_run = 98507 # Choose arbitrary run in the cycle 17_1 @@ -162,6 +193,11 @@ def focus_validation(results): return _compare_ws(reference_file_name=reference_file_name, results=results) +def ceria_validator(results): + reference_file_name = "ISIS_Powder-PEARL00098494_grouped.nxs" + return _compare_ws(reference_file_name=reference_file_name, results=results) + + def _compare_ws(reference_file_name, results): ref_ws = mantid.Load(Filename=reference_file_name) diff --git a/Testing/SystemTests/tests/analysis/reference/ISIS_Powder-PEARL98494_grouped.nxs.md5 b/Testing/SystemTests/tests/analysis/reference/ISIS_Powder-PEARL98494_grouped.nxs.md5 new file mode 100644 index 0000000000000000000000000000000000000000..85159fd3ae5819d82f207365d911baf49b3d05a1 --- /dev/null +++ b/Testing/SystemTests/tests/analysis/reference/ISIS_Powder-PEARL98494_grouped.nxs.md5 @@ -0,0 +1 @@ +b957a6b0928e12a8c86284ea010df429 \ No newline at end of file diff --git a/docs/source/release/v3.12.0/diffraction.rst b/docs/source/release/v3.12.0/diffraction.rst index 5de79b4872045e64e6092cf0a641cb8a82d6aced..79174ecb3939e6ada94c7ff15821a054b6fecc3b 100644 --- a/docs/source/release/v3.12.0/diffraction.rst +++ b/docs/source/release/v3.12.0/diffraction.rst @@ -13,6 +13,7 @@ Powder Diffraction ------------------ - Some new functionality for POLARIS in the ISIS Powder scripts. Adjusted some default parameters and output unsplined vanadium workspace by default +- ISIS_Powder scripts for PEARL now support creation of grouping .cal files from ceria run(s) - The ``CalibrationFile`` is now optional in :ref:`SNSPowderReduction <algm-SNSPowderReduction>`. In this case time focussing will use :ref:`ConvertUnits <algm-ConvertUnits>` and the instrument geometry. Care must be taken to supply a ``GroupingFile`` otherwise all of the spectra will be kept separate. - New algorithm :ref:`algm-EstimateDivergence` estimates the beam divergence due to finite slit size - :ref:`PDCalibration <algm-PDCalibration>` returns three more diagnostic workspaces: one for the fitted peak heights, one for the fitted peak widths, and one for observed resolution. diff --git a/docs/source/release/v3.12.0/framework.rst b/docs/source/release/v3.12.0/framework.rst index b6bc937cb3b1db9a97698e9d71c8b81c05f267b7..6b6eed76cd9adb64b0492a33f27d810a460cab79 100644 --- a/docs/source/release/v3.12.0/framework.rst +++ b/docs/source/release/v3.12.0/framework.rst @@ -38,6 +38,7 @@ Performance ----------- - Improved performance for second and consecutive loads of instrument geometry, particularly for instruments with many detector pixels. This affects :ref:`LoadEmptyInstrument <algm-LoadEmptyInstrument>` and load algorithms that are using it. +- Up to 30% performance improvement for :ref:`CropToComponent <algm-CropToComponent>` based on ongoing work on Instrument-2.0. Python ------ diff --git a/docs/source/release/v3.12.0/indirect_inelastic.rst b/docs/source/release/v3.12.0/indirect_inelastic.rst index e4d374817645fff48b81eb22fd105102e3e38583..6759792bee11b44c4a2c2cfa8e89ca33c6649c2c 100644 --- a/docs/source/release/v3.12.0/indirect_inelastic.rst +++ b/docs/source/release/v3.12.0/indirect_inelastic.rst @@ -37,6 +37,7 @@ New Improved ######## - The Plot Guess Feature in the ConvFit Interface is now enabled for the diffusion functions. +- The Plot Guess Feature in the MSDFit Interface is now implemented for the three models introduced in release v3.11 (MsdGauss, MsdPeters and MsdYi). Bugfixes ######## diff --git a/docs/source/techniques/ISISPowder-Pearl-v1.rst b/docs/source/techniques/ISISPowder-Pearl-v1.rst index 76d56a89537ef4ddf6eaafc95f80d397b6f512fb..404a1e8f8328e79ee45d53439a66c5d8b45118b7 100644 --- a/docs/source/techniques/ISISPowder-Pearl-v1.rst +++ b/docs/source/techniques/ISISPowder-Pearl-v1.rst @@ -55,6 +55,7 @@ The following methods can be executed on a PEARL object: - :ref:`create_vanadium_pearl_isis-powder-diffraction-ref` - :ref:`focus_pearl_isis-powder-diffraction-ref` +- :ref:`create_cal_pearl_isis-powder-diffraction-ref` For information on creating a PEARL object see: :ref:`creating_pearl_object-isis-powder-diffraction-ref` @@ -113,7 +114,7 @@ The following parameter is required if The following parameter may also be optionally set: - :ref:`file_ext_pearl_isis-powder-diffraction-ref` - + Example ======= @@ -131,6 +132,32 @@ Example run_number="100-110", tt_mode="tt88", vanadium_normalisation=True) +.. _create_cal_pearl_isis-powder-diffraction-ref: + +create_cal +^^^^^^^^^^ +The *create_cal* method creates the offset calibration file for PEARL +scripts. The following parameters are required: + +- :ref:`calibration_mapping_file_pearl_isis-powder-diffraction-ref` +- :ref:`focus_mode_pearl_isis-powder-diffraction-ref` +- :ref:`long_mode_pearl_isis-powder-diffraction-ref` +- :ref:`run_number_pearl_isis-powder-diffraction-ref` + +Example +======= + +.. code-block:: python + + # Notice how the filename ends with .yaml + cal_mapping_file = r"C:\path\to\cal_mapping.yaml" + + pearl_example.create_cal(run_number=95671, + tt_mode="tt70", + long_mode=True, + calibration_mapping_file=cal_mapping_file) + + .. _calibration_mapping_pearl_isis-powder-diffraction-ref: Calibration Mapping File @@ -462,7 +489,8 @@ Example Input: run_number ^^^^^^^^^^ Specifies the run number(s) to process when calling the -:ref:`focus_pearl_isis-powder-diffraction-ref` method. +:ref:`focus_pearl_isis-powder-diffraction-ref` and +:ref:`create_cal_isis-powder-diffraction-ref` methods. This parameter accepts a single value or a range of values with the following syntax: @@ -573,6 +601,104 @@ requires the user to restart Mantid for the new values to take effect. Please read :ref:`instrument_advanced_properties_isis-powder-diffraction-ref` before proceeding to change values within the advanced configuration file. +.. _create_cal_rebin_1_params_pearl_isis-powder-diffraction-ref: + +create_cal_rebin_1_params +^^^^^^^^^^^^^^^^^^^^^^^^^ +The rebin parameters to use in the first rebin operation in +:ref:`create_cal_pearl_isis-powder-diffraction-ref`. On PEARL this is +set to the following: + +.. code-block:: python + + # Long mode OFF: + create_cal_rebin_1_params: "100,-0.0006,19950" + + # Long mode ON: + create_cal_rebin_1_params: "20300,-0.0006,39990" + + +.. _create_cal_rebin_2_params_pearl_isis-powder-diffraction-ref: + +create_cal_rebin_2_params +^^^^^^^^^^^^^^^^^^^^^^^^^ +The rebin parameters to use in the second rebin operation in +:ref:`create_cal_pearl_isis-powder-diffraction-ref`. On PEARL this is +set to the following: + +.. code-block:: python + + create_cal_rebin_2_params: "1.8,0.002,2.1" + + +.. _cross_corr_reference_spectra_pearl_isis-powder-diffraction-ref: + +cross_corr_reference_spectra +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The Workspace Index of the spectra to correlate all other spectra +against in the cross-correlation step of +:ref:`create_cal_pearl_isis-powder-diffraction-ref`. On PEARL this is +set to the following: + +.. code-block:: python + + cross_corr_reference_spectra: 20 + + +.. _cross_corr_ws_index_max_pearl_isis-powder-diffraction-ref: + +cross_corr_ws_index_max +^^^^^^^^^^^^^^^^^^^^^^^ +The workspace index of the last member of the range of spectra to +cross-correlate against in +:ref:`create_cal_pearl_isis-powder-diffraction-ref`. On PEARL this is +set to the following: + +.. code-block:: python + + cross_corr_ws_index_max: 1063 + + +.. _cross_corr_ws_index_min_pearl_isis-powder-diffraction-ref: + +cross_corr_ws_index_min +^^^^^^^^^^^^^^^^^^^^^^^ +The workspace index of the first member of the range of spectra to +cross-correlate against in +:ref:`create_cal_pearl_isis-powder-diffraction-ref`. On PEARL this is +set to the following: + +.. code-block:: python + + cross_corr_ws_index_min: 9 + + +.. _cros_cor_x_max_pearl_isis-powder-diffraction-ref: + +cross_cor_x_max +^^^^^^^^^^^^^^^ +The ending point of the region to be cross correlated in +:ref:`create_cal_pearl_isis-powder-diffraction-ref`. On PEARL this is +set to the following: + +.. code-block:: python + + cross_corr_x_max: 2.1 + + +.. _cros_corr_x_min_pearl_isis-powder-diffraction-ref: + +cross_cor_x_min +^^^^^^^^^^^^^^^ +The starting point of the region to be cross correlated in +:ref:`create_cal_pearl_isis-powder-diffraction-ref`. On PEARL this is +set to the following: + +.. code-block:: python + + cross_corr_x_min: 1.8 + + .. _focused_cropping_values_pearl_isis-powder-diffraction-ref: focused_cropping_values @@ -630,6 +756,58 @@ On PEARL this is set to the following TOF windows: ] +.. _get_det_offsets_d_ref_pearl_isis-powder-diffraction-ref: + +get_det_offsets_d_ref +^^^^^^^^^^^^^^^^^^^^^ +Center of reference peak in d-space for GetDetectorOffsets in +:ref:`create_cal_pearl_isis-powder-diffraction-ref`. On PEARL this is +set to the following: + +.. code-block:: python + + get_det_offsets_d_ref: 1.912795 + + +.. _get_det_offsets_step_pearl_isis-powder-diffraction-ref: + +get_det_offsets_step +^^^^^^^^^^^^^^^^^^^^ +Step size used to bin d-spacing data in GetDetectorOffsets when +running :ref:`create_cal_pearl_isis-powder-diffraction-ref`. On PEARL +this is set to the following: + +.. code-block:: python + + get_det_offsets_step: 0.002 + + +.. _get_det_offsets_x_max_pearl_isis-powder-diffraction-ref: + +get_det_offsets_x_max +^^^^^^^^^^^^^^^^^^^^^ +Maximum of CrossCorrelation data to search for peak, usually negative, +in :ref:`create_cal_pearl_isis-powder-diffraction-ref`. On PEARL this +is set to the following: + +.. code-block:: python + + get_det_offsets_x_max: -200 + + +.. _get_det_offsets_x_min_pearl_isis-powder-diffraction-ref: + +get_det_offsets_x_min +^^^^^^^^^^^^^^^^^^^^^ +Minimum of CrossCorrelation data to search for peak, usually negative, +in :ref:`create_cal_pearl_isis-powder-diffraction-ref`. On PEARL this +is set to the following: + +.. code-block:: python + + get_det_offsets_x_min: -200 + + .. _monitor_lambda_crop_range_pearl_isis-powder-diffraction-ref: monitor_lambda_crop_range diff --git a/instrument/Facilities.xml b/instrument/Facilities.xml index b218298efb24d2994ad442ec7f41235c8731dfab..061fd364bd169b7259d6a12d23059867425af0ab 100644 --- a/instrument/Facilities.xml +++ b/instrument/Facilities.xml @@ -799,14 +799,14 @@ <instrument name="ISIS_Histogram"> <technique>Test Listener</technique> <livedata> - <connection name="histo" address="localhost:56789" listener="ISISHistoDataListener" /> + <connection name="histo" address="127.0.0.1:56789" listener="ISISHistoDataListener" /> </livedata> </instrument> <instrument name="ISIS_Event"> <technique>Test Listener</technique> <livedata> - <connection name="event" address="localhost:59876" listener="ISISLiveEventDataListener" /> + <connection name="event" address="127.0.0.1:59876" listener="ISISLiveEventDataListener" /> </livedata> </instrument> diff --git a/instrument/SEQUOIA_Definition-20120404-20171113.xml b/instrument/SEQUOIA_Definition-20120404-20171113.xml new file mode 100644 index 0000000000000000000000000000000000000000..c2e6b15bc303f5afc3df1adc4cc0f067899bb3f0 --- /dev/null +++ b/instrument/SEQUOIA_Definition-20120404-20171113.xml @@ -0,0 +1,1826 @@ +<?xml version='1.0' encoding='ASCII'?> +<!-- For help on the notation used to specify an Instrument Definition File + see http://www.mantidproject.org/IDF --> +<instrument xmlns="http://www.mantidproject.org/IDF/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.mantidproject.org/IDF/1.0 http://schema.mantidproject.org/IDF/1.0/IDFSchema.xsd" + valid-to="2017-11-13 23:59:59" name="SEQUOIA" valid-from="2012-04-04 14:15:46.929098"> + <!--For runs after 19889 --> + <defaults> + <length unit="metre"/> + <angle unit="degree"/> + <reference-frame> + <along-beam axis="z"/> + <pointing-up axis="y"/> + <handedness val="right"/> + </reference-frame> + </defaults> + <!--SOURCE AND SAMPLE POSITION--> + <component type="moderator"> + <location z="-20.0114"/> + </component> + <type is="Source" name="moderator"/> + <component type="sample-position"> + <location y="0.0" x="0.0" z="0.0"/> + </component> + <type is="SamplePos" name="sample-position"/> + <!--MONITORS--> + <component type="monitors" idlist="monitors"> + <location/> + </component> + <type name="monitors"> + <component type="monitor"> + <location z="-1.77808" name="monitor1"/> + <location z="8.99184" name="monitor2"/> + </component> + </type> + <component type="B row" idlist="B row"> + <location/> + </component> + <type name="B row"> + <component type="B1"> + <location/> + </component> + <component type="B2"> + <location/> + </component> + <component type="B3"> + <location/> + </component> + <component type="B4"> + <location/> + </component> + <component type="B5"> + <location/> + </component> + <component type="B6"> + <location/> + </component> + <component type="B7"> + <location/> + </component> + <component type="B8"> + <location/> + </component> + <component type="B9"> + <location/> + </component> + <component type="B10"> + <location/> + </component> + <component type="B11"> + <location/> + </component> + <component type="B12"> + <location/> + </component> + <component type="B13"> + <location/> + </component> + <component type="B14"> + <location/> + </component> + <component type="B15"> + <location/> + </component> + <component type="B16"> + <location/> + </component> + <component type="B17"> + <location/> + </component> + <component type="B18"> + <location/> + </component> + <component type="B19"> + <location/> + </component> + <component type="B20"> + <location/> + </component> + <component type="B21"> + <location/> + </component> + <component type="B22"> + <location/> + </component> + <component type="B23"> + <location/> + </component> + <component type="B24"> + <location/> + </component> + <component type="B25"> + <location/> + </component> + <component type="B26"> + <location/> + </component> + <component type="B27"> + <location/> + </component> + <component type="B28"> + <location/> + </component> + <component type="B29"> + <location/> + </component> + <component type="B30"> + <location/> + </component> + <component type="B31"> + <location/> + </component> + <component type="B32"> + <location/> + </component> + <component type="B33"> + <location/> + </component> + <component type="B34"> + <location/> + </component> + <component type="B35"> + <location/> + </component> + <component type="B36"> + <location/> + </component> + <component type="B37"> + <location/> + </component> + </type> + <type name="B1"> + <component type="eightpack"> + <location y="-1.30353562" x="4.60563316108" z="2.76377527"> + <rot axis-z="0" axis-x="0" axis-y="1" val="239.032627"/> + </location> + </component> + </type> + <type name="B2"> + <component type="eightpack"> + <location y="-1.303534985" x="4.5367332938" z="2.8754758848"> + <rot axis-z="0" axis-x="0" axis-y="1" val="236.732627"/> + </location> + </component> + </type> + <type name="B3"> + <component type="eightpack"> + <location y="-1.29480004006" x="4.31906484166" z="3.19313298634"> + <rot axis-z="0" axis-x="0" axis-y="1" val="233.52406849"/> + </location> + </component> + </type> + <type name="B4"> + <component type="eightpack"> + <location y="-1.29480004006" x="4.18256210476" z="3.36996307718"> + <rot axis-z="0" axis-x="0" axis-y="1" val="231.14100232"/> + </location> + </component> + </type> + <type name="B5"> + <component type="eightpack"> + <location y="-1.29480004006" x="4.0394219694" z="3.54026506322"> + <rot axis-z="0" axis-x="0" axis-y="1" val="228.76774758"/> + </location> + </component> + </type> + <type name="B6"> + <component type="eightpack"> + <location y="-1.29480004006" x="3.88900961084" z="3.70488877512"> + <rot axis-z="0" axis-x="0" axis-y="1" val="226.38891513"/> + </location> + </component> + </type> + <type name="B7"> + <component type="eightpack"> + <location y="-1.29480004006" x="3.71577988364" z="3.87745665452"> + <rot axis-z="0" axis-x="0" axis-y="1" val="223.78023066"/> + </location> + </component> + </type> + <type name="B8"> + <component type="eightpack"> + <location y="-1.29480004006" x="3.55275443372" z="4.0273746986"> + <rot axis-z="0" axis-x="0" axis-y="1" val="221.41717399"/> + </location> + </component> + </type> + <type name="B9"> + <component type="eightpack"> + <location y="-1.29480004006" x="3.38385871106" z="4.17028409782"> + <rot axis-z="0" axis-x="0" axis-y="1" val="219.05662215"/> + </location> + </component> + </type> + <type name="B10"> + <component type="eightpack"> + <location y="-1.29480004006" x="3.20998708014" z="4.30555569164"> + <rot axis-z="0" axis-x="0" axis-y="1" val="216.70619963"/> + </location> + </component> + </type> + <type name="B11"> + <component type="eightpack"> + <location y="-1.29480004006" x="3.02996424486" z="4.43407703226"> + <rot axis-z="0" axis-x="0" axis-y="1" val="214.34619963"/> + </location> + </component> + </type> + <type name="B12"> + <component type="eightpack"> + <location y="-1.29480004006" x="2.84480731774" z="4.5550843874"> + <rot axis-z="0" axis-x="0" axis-y="1" val="211.98619963"/> + </location> + </component> + </type> + <type name="B13"> + <component type="eightpack"> + <location y="-1.29480004006" x="2.65482458366" z="4.6683646917"> + <rot axis-z="0" axis-x="0" axis-y="1" val="209.62619963"/> + </location> + </component> + </type> + <type name="B14"> + <component type="eightpack"> + <location y="-1.29480004006" x="2.4603383163" z="4.77372578146"> + <rot axis-z="0" axis-x="0" axis-y="1" val="207.26619963"/> + </location> + </component> + </type> + <type name="B15"> + <component type="eightpack"> + <location y="-1.29480004006" x="2.22317726585" z="4.88876471826"> + <rot axis-z="0" axis-x="0" axis-y="1" val="204.45377863"/> + </location> + </component> + </type> + <type name="B16"> + <component type="eightpack"> + <location y="-1.29480004006" x="2.01893382776" z="4.97651229486"> + <rot axis-z="0" axis-x="0" axis-y="1" val="202.08202669"/> + </location> + </component> + </type> + <type name="B17"> + <component type="eightpack"> + <location y="-1.29480004006" x="1.81182191864" z="5.05564016048"> + <rot axis-z="0" axis-x="0" axis-y="1" val="199.71647921"/> + </location> + </component> + </type> + <type name="B18"> + <component type="eightpack"> + <location y="-1.29480004006" x="1.60068188829" z="5.12639931284"> + <rot axis-z="0" axis-x="0" axis-y="1" val="197.3406045"/> + </location> + </component> + </type> + <type name="B19"> + <component type="eightpack"> + <location y="-1.29479999993" x="1.38823150065" z="5.18797469134"> + <rot axis-z="0" axis-x="0" axis-y="1" val="194.9806045"/> + </location> + </component> + </type> + <type name="B20"> + <component type="eightpack"> + <location y="-1.29479999993" x="1.17342296557" z="5.24073913242"> + <rot axis-z="0" axis-x="0" axis-y="1" val="192.6206045"/> + </location> + </component> + </type> + <type name="B21"> + <component type="eightpack"> + <location y="-1.29479999993" x="0.92524769992" z="5.29019704722"> + <rot axis-z="0" axis-x="0" axis-y="1" val="189.9206045"/> + </location> + </component> + </type> + <type name="B22"> + <component type="eightpack"> + <location y="-1.29479999993" x="0.706622533158" z="5.32380997974"> + <rot axis-z="0" axis-x="0" axis-y="1" val="187.5606045"/> + </location> + </component> + </type> + <type name="B23"> + <component type="eightpack"> + <location y="-1.29479999993" x="0.48679868223" z="5.34839182796"> + <rot axis-z="0" axis-x="0" axis-y="1" val="185.2006045"/> + </location> + </component> + </type> + <type name="B24"> + <component type="eightpack"> + <location y="-1.29479999993" x="0.266149046568" z="5.3639008927"> + <rot axis-z="0" axis-x="0" axis-y="1" val="182.8406045"/> + </location> + </component> + </type> + <type name="B25"> + <component type="eightpack"> + <location y="-1.29479999993" x="0.045047927176" z="5.37031086464"> + <rot axis-z="0" axis-x="0" axis-y="1" val="180.4806045"/> + </location> + </component> + </type> + <type name="B26"> + <component type="eightpack"> + <location y="-1.29479999993" x="-0.207962706248" z="5.36647180196"> + <rot axis-z="0" axis-x="0" axis-y="1" val="177.78077169"/> + </location> + </component> + </type> + <type name="B27"> + <component type="eightpack"> + <location y="-1.29479999993" x="-0.428767559532" z="5.35335656276"> + <rot axis-z="0" axis-x="0" axis-y="1" val="175.42077169"/> + </location> + </component> + </type> + <type name="B28"> + <component type="eightpack"> + <location y="-1.29479999993" x="-0.648845069592" z="5.3311601149"> + <rot axis-z="0" axis-x="0" axis-y="1" val="173.06077169"/> + </location> + </component> + </type> + <type name="B29"> + <component type="eightpack"> + <location y="-1.29479999993" x="-0.867821906974" z="5.29992011642"> + <rot axis-z="0" axis-x="0" axis-y="1" val="170.70077169"/> + </location> + </component> + </type> + <type name="B30"> + <component type="eightpack"> + <location y="-1.29479999993" x="-1.08532660785" z="5.25968955934"> + <rot axis-z="0" axis-x="0" axis-y="1" val="168.34077169"/> + </location> + </component> + </type> + <type name="B31"> + <component type="eightpack"> + <location y="-1.29479999993" x="-1.30099020726" z="5.21053668838"> + <rot axis-z="0" axis-x="0" axis-y="1" val="165.98077169"/> + </location> + </component> + </type> + <type name="B32"> + <component type="eightpack"> + <location y="-1.29480004006" x="-1.51446642013" z="5.15261142704"> + <rot axis-z="0" axis-x="0" axis-y="1" val="163.62077169"/> + </location> + </component> + </type> + <type name="B33"> + <component type="eightpack"> + <location y="-1.29480004006" x="-1.72664610677" z="5.08615521502"> + <rot axis-z="0" axis-x="0" axis-y="1" val="161.24869638"/> + </location> + </component> + </type> + <type name="B34"> + <component type="eightpack"> + <location y="-1.29480004006" x="-1.95342748836" z="5.00448226686"> + <rot axis-z="0" axis-x="0" axis-y="1" val="158.67752934"/> + </location> + </component> + </type> + <type name="B35"> + <component type="eightpack"> + <location y="-1.29479999993" x="-2.15779859854" z="4.91969141536"> + <rot axis-z="0" axis-x="0" axis-y="1" val="156.31752934"/> + </location> + </component> + </type> + <type name="B36"> + <component type="eightpack"> + <location y="-1.29480004006" x="-2.35202492205" z="4.83001371308"> + <rot axis-z="0" axis-x="0" axis-y="1" val="154.03575652"/> + </location> + </component> + </type> + <type name="B37"> + <component type="eightpack"> + <location y="-1.29480004006" x="-2.54885044204" z="4.72893432164"> + <rot axis-z="0" axis-x="0" axis-y="1" val="151.67575652"/> + </location> + </component> + </type> + <component type="C row" idlist="C row"> + <location/> + </component> + <type name="C row"> + <component type="C1"> + <location/> + </component> + <component type="C2"> + <location/> + </component> + <component type="C3"> + <location/> + </component> + <component type="C4"> + <location/> + </component> + <component type="C5"> + <location/> + </component> + <component type="C6"> + <location/> + </component> + <component type="C7"> + <location/> + </component> + <component type="C8"> + <location/> + </component> + <component type="C9"> + <location/> + </component> + <component type="C10"> + <location/> + </component> + <component type="C11"> + <location/> + </component> + <component type="C12"> + <location/> + </component> + <component type="C13"> + <location/> + </component> + <component type="C14"> + <location/> + </component> + <component type="C15"> + <location/> + </component> + <component type="C16"> + <location/> + </component> + <component type="C17"> + <location/> + </component> + <component type="C18"> + <location/> + </component> + <component type="C19"> + <location/> + </component> + <component type="C20"> + <location/> + </component> + <component type="C21"> + <location/> + </component> + <component type="C22"> + <location/> + </component> + <component type="C23"> + <location/> + </component> + <component type="C24"> + <location/> + </component> + <component type="C25T"> + <location/> + </component> + <component type="C26T"> + <location/> + </component> + <component type="C25B"> + <location/> + </component> + <component type="C26B"> + <location/> + </component> + <component type="C27"> + <location/> + </component> + <component type="C28"> + <location/> + </component> + <component type="C29"> + <location/> + </component> + <component type="C30"> + <location/> + </component> + <component type="C31"> + <location/> + </component> + <component type="C32"> + <location/> + </component> + <component type="C33"> + <location/> + </component> + <component type="C34"> + <location/> + </component> + <component type="C35"> + <location/> + </component> + <component type="C36"> + <location/> + </component> + <component type="C37"> + <location/> + </component> + </type> + <type name="C1"> + <component type="eightpack"> + <location y="-0.0389000000002" x="4.6875281408" z="2.89948601712"> + <rot axis-z="0" axis-x="0" axis-y="1" val="238.261"/> + </location> + </component> + </type> + <type name="C2"> + <component type="eightpack"> + <location y="-0.0389000000002" x="4.56415688386" z="3.09005035024"> + <rot axis-z="0" axis-x="0" axis-y="1" val="235.901"/> + </location> + </component> + </type> + <type name="C3"> + <component type="eightpack"> + <location y="-0.0389000000002" x="4.43304318622" z="3.27537285488"> + <rot axis-z="0" axis-x="0" axis-y="1" val="233.541"/> + </location> + </component> + </type> + <type name="C4"> + <component type="eightpack"> + <location y="-0.0389000000002" x="4.29500585244" z="3.45438429518"> + <rot axis-z="0" axis-x="0" axis-y="1" val="231.191"/> + </location> + </component> + </type> + <type name="C5"> + <component type="eightpack"> + <location y="-0.0389000000002" x="4.14967007504" z="3.62768575198"> + <rot axis-z="0" axis-x="0" axis-y="1" val="228.8397"/> + </location> + </component> + </type> + <type name="C6"> + <component type="eightpack"> + <location y="-0.0389000000002" x="3.99533264178" z="3.7969858655"> + <rot axis-z="0" axis-x="0" axis-y="1" val="226.4581"/> + </location> + </component> + </type> + <type name="C7"> + <component type="eightpack"> + <location y="-0.0389000000002" x="3.81281980386" z="3.97800034472"> + <rot axis-z="0" axis-x="0" axis-y="1" val="223.7854"/> + </location> + </component> + </type> + <type name="C8"> + <component type="eightpack"> + <location y="-0.0389000000002" x="3.64602183672" z="4.13139723376"> + <rot axis-z="0" axis-x="0" axis-y="1" val="221.4289"/> + </location> + </component> + </type> + <type name="C9"> + <component type="eightpack"> + <location y="-0.0389000000002" x="3.47173969862" z="4.27892822902"> + <rot axis-z="0" axis-x="0" axis-y="1" val="219.0544"/> + </location> + </component> + </type> + <type name="C10"> + <component type="eightpack"> + <location y="-0.0389000000002" x="3.29203392138" z="4.41864602934"> + <rot axis-z="0" axis-x="0" axis-y="1" val="216.6873"/> + </location> + </component> + </type> + <type name="C11"> + <component type="eightpack"> + <location y="-0.0389000000002" x="3.1075271606" z="4.55031056154"> + <rot axis-z="0" axis-x="0" axis-y="1" val="214.3302"/> + </location> + </component> + </type> + <type name="C12"> + <component type="eightpack"> + <location y="-0.0389000000002" x="2.9161672309" z="4.67522753548"> + <rot axis-z="0" axis-x="0" axis-y="1" val="211.9538"/> + </location> + </component> + </type> + <type name="C13"> + <component type="eightpack"> + <location y="-0.0389000000002" x="2.71943750244" z="4.79241055994"> + <rot axis-z="0" axis-x="0" axis-y="1" val="209.5726"/> + </location> + </component> + </type> + <type name="C14"> + <component type="eightpack"> + <location y="-0.0389000000002" x="2.49170903842" z="4.91467906064"> + <rot axis-z="0" axis-x="0" axis-y="1" val="206.8847"/> + </location> + </component> + </type> + <type name="C15"> + <component type="eightpack"> + <location y="-0.0389000000002" x="2.28458593857" z="5.01427223712"> + <rot axis-z="0" axis-x="0" axis-y="1" val="204.4948"/> + </location> + </component> + </type> + <type name="C16"> + <component type="eightpack"> + <location y="-0.0389000000002" x="2.07474382485" z="5.10464666648"> + <rot axis-z="0" axis-x="0" axis-y="1" val="202.1189"/> + </location> + </component> + </type> + <type name="C17"> + <component type="eightpack"> + <location y="-0.0389000000002" x="1.86274311677" z="5.18575022284"> + <rot axis-z="0" axis-x="0" axis-y="1" val="199.7585"/> + </location> + </component> + </type> + <type name="C18"> + <component type="eightpack"> + <location y="-0.0389000000002" x="1.64894478364" z="5.25773702578"> + <rot axis-z="0" axis-x="0" axis-y="1" val="197.4126"/> + </location> + </component> + </type> + <type name="C19"> + <component type="eightpack"> + <location y="-0.0389000000002" x="1.43260531538" z="5.3206941592"> + <rot axis-z="0" axis-x="0" axis-y="1" val="195.0696"/> + </location> + </component> + </type> + <type name="C20"> + <component type="eightpack"> + <location y="-0.0389000000002" x="1.21326640912" z="5.37493873956"> + <rot axis-z="0" axis-x="0" axis-y="1" val="192.72"/> + </location> + </component> + </type> + <type name="C21"> + <component type="eightpack"> + <location y="-0.0389000000002" x="0.955168885442" z="5.42691364704"> + <rot axis-z="0" axis-x="0" axis-y="1" val="189.98215784"/> + </location> + </component> + </type> + <type name="C22"> + <component type="eightpack"> + <location y="-0.0389000000002" x="0.73405361952" z="5.46121787104"> + <rot axis-z="0" axis-x="0" axis-y="1" val="187.65536244"/> + </location> + </component> + </type> + <type name="C23"> + <component type="eightpack"> + <location y="-0.0389000000002" x="0.511432623056" z="5.48666909522"> + <rot axis-z="0" axis-x="0" axis-y="1" val="185.32536244"/> + </location> + </component> + </type> + <type name="C24"> + <component type="eightpack"> + <location y="-0.0389000000002" x="0.287949459406" z="5.50292524"> + <rot axis-z="0" axis-x="0" axis-y="1" val="182.99536244"/> + </location> + </component> + </type> + <type name="C25T"> + <component type="eightpack-top"> + <location y="0.431673" x="0.17600619612" z="5.50764222828"> + <rot axis-z="0" axis-x="0" axis-y="1" val="181.83036244"/> + </location> + </component> + </type> + <type name="C26T"> + <component type="eightpack-top"> + <location y="0.431673" x="-0.0480523141568" z="5.51024428302"> + <rot axis-z="0" axis-x="0" axis-y="1" val="179.50036244"/> + </location> + </component> + </type> + <type name="C25B"> + <component type="eightpack-bottom"> + <location y="-0.468884" x="0.17600619612" z="5.50764222828"> + <rot axis-z="0" axis-x="0" axis-y="1" val="181.83036244"/> + </location> + </component> + </type> + <type name="C26B"> + <component type="eightpack-bottom"> + <location y="-0.468884" x="-0.0480523141568" z="5.51024428302"> + <rot axis-z="0" axis-x="0" axis-y="1" val="179.50036244"/> + </location> + </component> + </type> + <type name="C27"> + <component type="eightpack"> + <location y="-0.0389000000002" x="-0.42281525673" z="5.4947435952"> + <rot axis-z="0" axis-x="0" axis-y="1" val="175.5998157"/> + </location> + </component> + </type> + <type name="C28"> + <component type="eightpack"> + <location y="-0.0389000000002" x="-0.658224241588" z="5.47153733224"> + <rot axis-z="0" axis-x="0" axis-y="1" val="173.1403"/> + </location> + </component> + </type> + <type name="C29"> + <component type="eightpack"> + <location y="-0.0389000000002" x="-0.882973593968" z="5.43979205156"> + <rot axis-z="0" axis-x="0" axis-y="1" val="170.7803"/> + </location> + </component> + </type> + <type name="C30"> + <component type="eightpack"> + <location y="-0.0389000000002" x="-1.10621978349" z="5.39879295398"> + <rot axis-z="0" axis-x="0" axis-y="1" val="168.4203"/> + </location> + </component> + </type> + <type name="C31"> + <component type="eightpack"> + <location y="-0.0389000000002" x="-1.32781484906" z="5.34867441312"> + <rot axis-z="0" axis-x="0" axis-y="1" val="166.0581"/> + </location> + </component> + </type> + <type name="C32"> + <component type="eightpack"> + <location y="-0.0389000000002" x="-1.55303519339" z="5.28766236598"> + <rot axis-z="0" axis-x="0" axis-y="1" val="163.632"/> + </location> + </component> + </type> + <type name="C33"> + <component type="eightpack"> + <location y="-0.0389000000002" x="-1.77201739703" z="5.21830976402"> + <rot axis-z="0" axis-x="0" axis-y="1" val="161.2437"/> + </location> + </component> + </type> + <type name="C34"> + <component type="eightpack"> + <location y="-0.0389000000002" x="-1.99960889486" z="5.13516569612"> + <rot axis-z="0" axis-x="0" axis-y="1" val="158.7243"/> + </location> + </component> + </type> + <type name="C35"> + <component type="eightpack"> + <location y="-0.0389000000002" x="-2.21150431485" z="5.04742302172"> + <rot axis-z="0" axis-x="0" axis-y="1" val="156.3396"/> + </location> + </component> + </type> + <type name="C36"> + <component type="eightpack"> + <location y="-0.0389000000002" x="-2.41598945122" z="4.95276649526"> + <rot axis-z="0" axis-x="0" axis-y="1" val="153.9966"/> + </location> + </component> + </type> + <type name="C37"> + <component type="eightpack"> + <location y="-0.0389000000002" x="-2.61672177696" z="4.8497781084"> + <rot axis-z="0" axis-x="0" axis-y="1" val="151.6507"/> + </location> + </component> + </type> + <component type="D row" idlist="D row"> + <location/> + </component> + <type name="D row"> + <component type="D1"> + <location/> + </component> + <component type="D2"> + <location/> + </component> + <component type="D3"> + <location/> + </component> + <component type="D4"> + <location/> + </component> + <component type="D5"> + <location/> + </component> + <component type="D6"> + <location/> + </component> + <component type="D7"> + <location/> + </component> + <component type="D8"> + <location/> + </component> + <component type="D9"> + <location/> + </component> + <component type="D10"> + <location/> + </component> + <component type="D11"> + <location/> + </component> + <component type="D12"> + <location/> + </component> + <component type="D13"> + <location/> + </component> + <component type="D14"> + <location/> + </component> + <component type="D15"> + <location/> + </component> + <component type="D16"> + <location/> + </component> + <component type="D17"> + <location/> + </component> + <component type="D18"> + <location/> + </component> + <component type="D19"> + <location/> + </component> + <component type="D20"> + <location/> + </component> + <component type="D21"> + <location/> + </component> + <component type="D22"> + <location/> + </component> + <component type="D23"> + <location/> + </component> + <component type="D24"> + <location/> + </component> + <component type="D25"> + <location/> + </component> + <component type="D26"> + <location/> + </component> + <component type="D27"> + <location/> + </component> + <component type="D28"> + <location/> + </component> + <component type="D29"> + <location/> + </component> + <component type="D30"> + <location/> + </component> + <component type="D31"> + <location/> + </component> + <component type="D32"> + <location/> + </component> + <component type="D33"> + <location/> + </component> + <component type="D34"> + <location/> + </component> + <component type="D35"> + <location/> + </component> + <component type="D36"> + <location/> + </component> + <component type="D37"> + <location/> + </component> + </type> + <type name="D1"> + <component type="eightpack"> + <location y="1.22910000001" x="4.57110507672" z="2.8206377674"> + <rot axis-z="0" axis-x="0" axis-y="1" val="238.323"/> + </location> + </component> + </type> + <type name="D2"> + <component type="eightpack"> + <location y="1.22910000001" x="4.45107939094" z="3.00647489636"> + <rot axis-z="0" axis-x="0" axis-y="1" val="235.963"/> + </location> + </component> + </type> + <type name="D3"> + <component type="eightpack"> + <location y="1.22910000001" x="4.3234927881" z="3.18720437996"> + <rot axis-z="0" axis-x="0" axis-y="1" val="233.603"/> + </location> + </component> + </type> + <type name="D4"> + <component type="eightpack"> + <location y="1.22909999011" x="4.18920230992" z="3.36178283446"> + <rot axis-z="0" axis-x="0" axis-y="1" val="231.25338983"/> + </location> + </component> + </type> + <type name="D5"> + <component type="eightpack"> + <location y="1.22909999011" x="4.046441869" z="3.53235379888"> + <rot axis-z="0" axis-x="0" axis-y="1" val="228.88056701"/> + </location> + </component> + </type> + <type name="D6"> + <component type="eightpack"> + <location y="1.22909999011" x="3.89757971054" z="3.69601869888"> + <rot axis-z="0" axis-x="0" axis-y="1" val="226.52047601"/> + </location> + </component> + </type> + <type name="D7"> + <component type="eightpack"> + <location y="1.22909999011" x="3.72157876206" z="3.87194811842"> + <rot axis-z="0" axis-x="0" axis-y="1" val="223.86555961"/> + </location> + </component> + </type> + <type name="D8"> + <component type="eightpack"> + <location y="1.22909999011" x="3.5583230586" z="4.02249205914"> + <rot axis-z="0" axis-x="0" axis-y="1" val="221.49618627"/> + </location> + </component> + </type> + <type name="D9"> + <component type="eightpack"> + <location y="1.22909999011" x="3.39320041704" z="4.16270439492"> + <rot axis-z="0" axis-x="0" axis-y="1" val="219.18496575"/> + </location> + </component> + </type> + <type name="D10"> + <component type="eightpack"> + <location y="1.22909999011" x="3.21560850146" z="4.30135873788"> + <rot axis-z="0" axis-x="0" axis-y="1" val="216.78104403"/> + </location> + </component> + </type> + <type name="D11"> + <component type="eightpack"> + <location y="1.22909999011" x="3.03576816772" z="4.43013621718"> + <rot axis-z="0" axis-x="0" axis-y="1" val="214.42104403"/> + </location> + </component> + </type> + <type name="D12"> + <component type="eightpack"> + <location y="1.22909999011" x="2.8507685936" z="4.55138591118"> + <rot axis-z="0" axis-x="0" axis-y="1" val="212.06104403"/> + </location> + </component> + </type> + <type name="D13"> + <component type="eightpack"> + <location y="1.22909999011" x="2.66093309824" z="4.6649148256"> + <rot axis-z="0" axis-x="0" axis-y="1" val="209.70104403"/> + </location> + </component> + </type> + <type name="D14"> + <component type="eightpack"> + <location y="1.22909999011" x="2.43095921681" z="4.7887819515"> + <rot axis-z="0" axis-x="0" axis-y="1" val="206.914"/> + </location> + </component> + </type> + <type name="D15"> + <component type="eightpack"> + <location y="1.22910000001" x="2.23344308175" z="4.88411720152"> + <rot axis-z="0" axis-x="0" axis-y="1" val="204.574"/> + </location> + </component> + </type> + <type name="D16"> + <component type="eightpack"> + <location y="1.22910000001" x="2.03256714746" z="4.97097887692"> + <rot axis-z="0" axis-x="0" axis-y="1" val="202.239"/> + </location> + </component> + </type> + <type name="D17"> + <component type="eightpack"> + <location y="1.22910000001" x="1.82756698926" z="5.04997495972"> + <rot axis-z="0" axis-x="0" axis-y="1" val="199.895"/> + </location> + </component> + </type> + <type name="D18"> + <component type="eightpack"> + <location y="1.22910000001" x="1.61940384297" z="5.12050738406"> + <rot axis-z="0" axis-x="0" axis-y="1" val="197.55"/> + </location> + </component> + </type> + <type name="D19"> + <component type="eightpack"> + <location y="1.22910000001" x="1.40898478401" z="5.1823505446"> + <rot axis-z="0" axis-x="0" axis-y="1" val="195.21"/> + </location> + </component> + </type> + <type name="D20"> + <component type="eightpack"> + <location y="1.22910000001" x="1.19621786562" z="5.23555709554"> + <rot axis-z="0" axis-x="0" axis-y="1" val="192.87"/> + </location> + </component> + </type> + <type name="D21"> + <component type="eightpack"> + <location y="1.22910000001" x="0.985335317362" z="5.28028000076"> + <rot axis-z="0" axis-x="0" axis-y="1" val="190.5702"/> + </location> + </component> + </type> + <type name="D22"> + <component type="eightpack"> + <location y="1.22910000001" x="0.78360224407" z="5.3139241067"> + <rot axis-z="0" axis-x="0" axis-y="1" val="188.3885"/> + </location> + </component> + </type> + <type name="D23"> + <component type="eightpack"> + <location y="1.22910000001" x="0.565047087836" z="5.3415347703"> + <rot axis-z="0" axis-x="0" axis-y="1" val="186.0385"/> + </location> + </component> + </type> + <type name="D24"> + <component type="eightpack"> + <location y="1.22910000001" x="0.345548983018" z="5.36021154524"> + <rot axis-z="0" axis-x="0" axis-y="1" val="183.6885"/> + </location> + </component> + </type> + <type name="D25"> + <component type="eightpack"> + <location y="1.22910000001" x="0.125469660185" z="5.3698723692"> + <rot axis-z="0" axis-x="0" axis-y="1" val="181.3385"/> + </location> + </component> + </type> + <type name="D26"> + <component type="eightpack"> + <location y="1.22910000001" x="-0.0948207044146" z="5.37050099634"> + <rot axis-z="0" axis-x="0" axis-y="1" val="178.9885"/> + </location> + </component> + </type> + <type name="D27"> + <component type="eightpack"> + <location y="1.22910000001" x="-0.45265876194" z="5.35136387434"> + <rot axis-z="0" axis-x="0" axis-y="1" val="175.165"/> + </location> + </component> + </type> + <type name="D28"> + <component type="eightpack"> + <location y="1.22910000001" x="-0.671703998982" z="5.3283026403"> + <rot axis-z="0" axis-x="0" axis-y="1" val="172.815"/> + </location> + </component> + </type> + <type name="D29"> + <component type="eightpack"> + <location y="1.22910000001" x="-0.889619420656" z="5.29627912548"> + <rot axis-z="0" axis-x="0" axis-y="1" val="170.465"/> + </location> + </component> + </type> + <type name="D30"> + <component type="eightpack"> + <location y="1.22910000001" x="-1.10603848998" z="5.25534719566"> + <rot axis-z="0" axis-x="0" axis-y="1" val="168.115"/> + </location> + </component> + </type> + <type name="D31"> + <component type="eightpack"> + <location y="1.22910000001" x="-1.32059718735" z="5.20557569754"> + <rot axis-z="0" axis-x="0" axis-y="1" val="165.765"/> + </location> + </component> + </type> + <type name="D32"> + <component type="eightpack"> + <location y="1.22910000001" x="-1.53293721022" z="5.1470570414"> + <rot axis-z="0" axis-x="0" axis-y="1" val="163.415"/> + </location> + </component> + </type> + <type name="D33"> + <component type="eightpack"> + <location y="1.22910000001" x="-1.74030639162" z="5.0807040372"> + <rot axis-z="0" axis-x="0" axis-y="1" val="161.092"/> + </location> + </component> + </type> + <type name="D34"> + <component type="eightpack"> + <location y="1.22910000001" x="-1.96180272434" z="5.0013049707"> + <rot axis-z="0" axis-x="0" axis-y="1" val="158.582"/> + </location> + </component> + </type> + <type name="D35"> + <component type="eightpack"> + <location y="1.22910000001" x="-2.16506783826" z="4.91676592972"> + <rot axis-z="0" axis-x="0" axis-y="1" val="156.234"/> + </location> + </component> + </type> + <type name="D36"> + <component type="eightpack"> + <location y="1.22910000001" x="-2.36510173842" z="4.8237238044"> + <rot axis-z="0" axis-x="0" axis-y="1" val="153.881"/> + </location> + </component> + </type> + <type name="D37"> + <component type="eightpack"> + <location y="1.22910000001" x="-2.56172303758" z="4.7222335451"> + <rot axis-z="0" axis-x="0" axis-y="1" val="151.521"/> + </location> + </component> + </type> + <!--STANDARD 8-PACK--> + <type name="eightpack"> + <properties/> + <component type="tube"> + <location x="-0.096012" name="tube1"/> + <location x="-0.06858" name="tube2"/> + <location x="-0.041148" name="tube3"/> + <location x="-0.013716" name="tube4"/> + <location x="0.013716" name="tube5"/> + <location x="0.041148" name="tube6"/> + <location x="0.06858" name="tube7"/> + <location x="0.096012" name="tube8"/> + </component> + </type> + <!--8-PACK ABOVE BEAMSTOP--> + <type name="eightpack-top"> + <properties/> + <component type="tube-top"> + <location x="-0.096012" name="tube1"/> + <location x="-0.06858" name="tube2"/> + <location x="-0.041148" name="tube3"/> + <location x="-0.013716" name="tube4"/> + <location x="0.013716" name="tube5"/> + <location x="0.041148" name="tube6"/> + <location x="0.06858" name="tube7"/> + <location x="0.096012" name="tube8"/> + </component> + </type> + <!--8-PACK BELOW BEAMSTOP--> + <type name="eightpack-bottom"> + <properties/> + <component type="tube-bottom"> + <location x="-0.096012" name="tube1"/> + <location x="-0.06858" name="tube2"/> + <location x="-0.041148" name="tube3"/> + <location x="-0.013716" name="tube4"/> + <location x="0.013716" name="tube5"/> + <location x="0.041148" name="tube6"/> + <location x="0.06858" name="tube7"/> + <location x="0.096012" name="tube8"/> + </component> + </type> + <!--STANDARD 128 PIXEL TUBE--> + <type name="tube" outline="yes"> + <properties/> + <component type="pixel"> + <location y="-0.5953125" name="pixel1"/> + <location y="-0.5859375" name="pixel2"/> + <location y="-0.5765625" name="pixel3"/> + <location y="-0.5671875" name="pixel4"/> + <location y="-0.5578125" name="pixel5"/> + <location y="-0.5484375" name="pixel6"/> + <location y="-0.5390625" name="pixel7"/> + <location y="-0.5296875" name="pixel8"/> + <location y="-0.5203125" name="pixel9"/> + <location y="-0.5109375" name="pixel10"/> + <location y="-0.5015625" name="pixel11"/> + <location y="-0.4921875" name="pixel12"/> + <location y="-0.4828125" name="pixel13"/> + <location y="-0.4734375" name="pixel14"/> + <location y="-0.4640625" name="pixel15"/> + <location y="-0.4546875" name="pixel16"/> + <location y="-0.4453125" name="pixel17"/> + <location y="-0.4359375" name="pixel18"/> + <location y="-0.4265625" name="pixel19"/> + <location y="-0.4171875" name="pixel20"/> + <location y="-0.4078125" name="pixel21"/> + <location y="-0.3984375" name="pixel22"/> + <location y="-0.3890625" name="pixel23"/> + <location y="-0.3796875" name="pixel24"/> + <location y="-0.3703125" name="pixel25"/> + <location y="-0.3609375" name="pixel26"/> + <location y="-0.3515625" name="pixel27"/> + <location y="-0.3421875" name="pixel28"/> + <location y="-0.3328125" name="pixel29"/> + <location y="-0.3234375" name="pixel30"/> + <location y="-0.3140625" name="pixel31"/> + <location y="-0.3046875" name="pixel32"/> + <location y="-0.2953125" name="pixel33"/> + <location y="-0.2859375" name="pixel34"/> + <location y="-0.2765625" name="pixel35"/> + <location y="-0.2671875" name="pixel36"/> + <location y="-0.2578125" name="pixel37"/> + <location y="-0.2484375" name="pixel38"/> + <location y="-0.2390625" name="pixel39"/> + <location y="-0.2296875" name="pixel40"/> + <location y="-0.2203125" name="pixel41"/> + <location y="-0.2109375" name="pixel42"/> + <location y="-0.2015625" name="pixel43"/> + <location y="-0.1921875" name="pixel44"/> + <location y="-0.1828125" name="pixel45"/> + <location y="-0.1734375" name="pixel46"/> + <location y="-0.1640625" name="pixel47"/> + <location y="-0.1546875" name="pixel48"/> + <location y="-0.1453125" name="pixel49"/> + <location y="-0.1359375" name="pixel50"/> + <location y="-0.1265625" name="pixel51"/> + <location y="-0.1171875" name="pixel52"/> + <location y="-0.1078125" name="pixel53"/> + <location y="-0.0984375" name="pixel54"/> + <location y="-0.0890625" name="pixel55"/> + <location y="-0.0796875" name="pixel56"/> + <location y="-0.0703125" name="pixel57"/> + <location y="-0.0609375" name="pixel58"/> + <location y="-0.0515625" name="pixel59"/> + <location y="-0.0421875" name="pixel60"/> + <location y="-0.0328125" name="pixel61"/> + <location y="-0.0234375" name="pixel62"/> + <location y="-0.0140625" name="pixel63"/> + <location y="-0.0046875" name="pixel64"/> + <location y="0.0046875" name="pixel65"/> + <location y="0.0140625" name="pixel66"/> + <location y="0.0234375" name="pixel67"/> + <location y="0.0328125" name="pixel68"/> + <location y="0.0421875" name="pixel69"/> + <location y="0.0515625" name="pixel70"/> + <location y="0.0609375" name="pixel71"/> + <location y="0.0703125" name="pixel72"/> + <location y="0.0796875" name="pixel73"/> + <location y="0.0890625" name="pixel74"/> + <location y="0.0984375" name="pixel75"/> + <location y="0.1078125" name="pixel76"/> + <location y="0.1171875" name="pixel77"/> + <location y="0.1265625" name="pixel78"/> + <location y="0.1359375" name="pixel79"/> + <location y="0.1453125" name="pixel80"/> + <location y="0.1546875" name="pixel81"/> + <location y="0.1640625" name="pixel82"/> + <location y="0.1734375" name="pixel83"/> + <location y="0.1828125" name="pixel84"/> + <location y="0.1921875" name="pixel85"/> + <location y="0.2015625" name="pixel86"/> + <location y="0.2109375" name="pixel87"/> + <location y="0.2203125" name="pixel88"/> + <location y="0.2296875" name="pixel89"/> + <location y="0.2390625" name="pixel90"/> + <location y="0.2484375" name="pixel91"/> + <location y="0.2578125" name="pixel92"/> + <location y="0.2671875" name="pixel93"/> + <location y="0.2765625" name="pixel94"/> + <location y="0.2859375" name="pixel95"/> + <location y="0.2953125" name="pixel96"/> + <location y="0.3046875" name="pixel97"/> + <location y="0.3140625" name="pixel98"/> + <location y="0.3234375" name="pixel99"/> + <location y="0.3328125" name="pixel100"/> + <location y="0.3421875" name="pixel101"/> + <location y="0.3515625" name="pixel102"/> + <location y="0.3609375" name="pixel103"/> + <location y="0.3703125" name="pixel104"/> + <location y="0.3796875" name="pixel105"/> + <location y="0.3890625" name="pixel106"/> + <location y="0.3984375" name="pixel107"/> + <location y="0.4078125" name="pixel108"/> + <location y="0.4171875" name="pixel109"/> + <location y="0.4265625" name="pixel110"/> + <location y="0.4359375" name="pixel111"/> + <location y="0.4453125" name="pixel112"/> + <location y="0.4546875" name="pixel113"/> + <location y="0.4640625" name="pixel114"/> + <location y="0.4734375" name="pixel115"/> + <location y="0.4828125" name="pixel116"/> + <location y="0.4921875" name="pixel117"/> + <location y="0.5015625" name="pixel118"/> + <location y="0.5109375" name="pixel119"/> + <location y="0.5203125" name="pixel120"/> + <location y="0.5296875" name="pixel121"/> + <location y="0.5390625" name="pixel122"/> + <location y="0.5484375" name="pixel123"/> + <location y="0.5578125" name="pixel124"/> + <location y="0.5671875" name="pixel125"/> + <location y="0.5765625" name="pixel126"/> + <location y="0.5859375" name="pixel127"/> + <location y="0.5953125" name="pixel128"/> + </component> + </type> + <!--SMALL TOP 128 PIXEL TUBE--> + <type name="tube-top" outline="yes"> + <properties/> + <component type="pixel-top"> + <location y="-0.12852796875" name="pixel1"/> + <location y="-0.12650390625" name="pixel2"/> + <location y="-0.12447984375" name="pixel3"/> + <location y="-0.12245578125" name="pixel4"/> + <location y="-0.12043171875" name="pixel5"/> + <location y="-0.11840765625" name="pixel6"/> + <location y="-0.11638359375" name="pixel7"/> + <location y="-0.11435953125" name="pixel8"/> + <location y="-0.11233546875" name="pixel9"/> + <location y="-0.11031140625" name="pixel10"/> + <location y="-0.10828734375" name="pixel11"/> + <location y="-0.10626328125" name="pixel12"/> + <location y="-0.10423921875" name="pixel13"/> + <location y="-0.10221515625" name="pixel14"/> + <location y="-0.10019109375" name="pixel15"/> + <location y="-0.09816703125" name="pixel16"/> + <location y="-0.09614296875" name="pixel17"/> + <location y="-0.09411890625" name="pixel18"/> + <location y="-0.09209484375" name="pixel19"/> + <location y="-0.09007078125" name="pixel20"/> + <location y="-0.08804671875" name="pixel21"/> + <location y="-0.08602265625" name="pixel22"/> + <location y="-0.08399859375" name="pixel23"/> + <location y="-0.08197453125" name="pixel24"/> + <location y="-0.07995046875" name="pixel25"/> + <location y="-0.07792640625" name="pixel26"/> + <location y="-0.07590234375" name="pixel27"/> + <location y="-0.07387828125" name="pixel28"/> + <location y="-0.07185421875" name="pixel29"/> + <location y="-0.06983015625" name="pixel30"/> + <location y="-0.06780609375" name="pixel31"/> + <location y="-0.06578203125" name="pixel32"/> + <location y="-0.06375796875" name="pixel33"/> + <location y="-0.06173390625" name="pixel34"/> + <location y="-0.05970984375" name="pixel35"/> + <location y="-0.05768578125" name="pixel36"/> + <location y="-0.05566171875" name="pixel37"/> + <location y="-0.05363765625" name="pixel38"/> + <location y="-0.05161359375" name="pixel39"/> + <location y="-0.04958953125" name="pixel40"/> + <location y="-0.04756546875" name="pixel41"/> + <location y="-0.04554140625" name="pixel42"/> + <location y="-0.04351734375" name="pixel43"/> + <location y="-0.04149328125" name="pixel44"/> + <location y="-0.03946921875" name="pixel45"/> + <location y="-0.03744515625" name="pixel46"/> + <location y="-0.03542109375" name="pixel47"/> + <location y="-0.03339703125" name="pixel48"/> + <location y="-0.03137296875" name="pixel49"/> + <location y="-0.02934890625" name="pixel50"/> + <location y="-0.02732484375" name="pixel51"/> + <location y="-0.02530078125" name="pixel52"/> + <location y="-0.02327671875" name="pixel53"/> + <location y="-0.02125265625" name="pixel54"/> + <location y="-0.01922859375" name="pixel55"/> + <location y="-0.01720453125" name="pixel56"/> + <location y="-0.01518046875" name="pixel57"/> + <location y="-0.01315640625" name="pixel58"/> + <location y="-0.01113234375" name="pixel59"/> + <location y="-0.00910828125" name="pixel60"/> + <location y="-0.00708421875" name="pixel61"/> + <location y="-0.00506015625" name="pixel62"/> + <location y="-0.00303609375" name="pixel63"/> + <location y="-0.00101203125" name="pixel64"/> + <location y="0.00101203125" name="pixel65"/> + <location y="0.00303609375" name="pixel66"/> + <location y="0.00506015625" name="pixel67"/> + <location y="0.00708421875" name="pixel68"/> + <location y="0.00910828125" name="pixel69"/> + <location y="0.01113234375" name="pixel70"/> + <location y="0.01315640625" name="pixel71"/> + <location y="0.01518046875" name="pixel72"/> + <location y="0.01720453125" name="pixel73"/> + <location y="0.01922859375" name="pixel74"/> + <location y="0.02125265625" name="pixel75"/> + <location y="0.02327671875" name="pixel76"/> + <location y="0.02530078125" name="pixel77"/> + <location y="0.02732484375" name="pixel78"/> + <location y="0.02934890625" name="pixel79"/> + <location y="0.03137296875" name="pixel80"/> + <location y="0.03339703125" name="pixel81"/> + <location y="0.03542109375" name="pixel82"/> + <location y="0.03744515625" name="pixel83"/> + <location y="0.03946921875" name="pixel84"/> + <location y="0.04149328125" name="pixel85"/> + <location y="0.04351734375" name="pixel86"/> + <location y="0.04554140625" name="pixel87"/> + <location y="0.04756546875" name="pixel88"/> + <location y="0.04958953125" name="pixel89"/> + <location y="0.05161359375" name="pixel90"/> + <location y="0.05363765625" name="pixel91"/> + <location y="0.05566171875" name="pixel92"/> + <location y="0.05768578125" name="pixel93"/> + <location y="0.05970984375" name="pixel94"/> + <location y="0.06173390625" name="pixel95"/> + <location y="0.06375796875" name="pixel96"/> + <location y="0.06578203125" name="pixel97"/> + <location y="0.06780609375" name="pixel98"/> + <location y="0.06983015625" name="pixel99"/> + <location y="0.07185421875" name="pixel100"/> + <location y="0.07387828125" name="pixel101"/> + <location y="0.07590234375" name="pixel102"/> + <location y="0.07792640625" name="pixel103"/> + <location y="0.07995046875" name="pixel104"/> + <location y="0.08197453125" name="pixel105"/> + <location y="0.08399859375" name="pixel106"/> + <location y="0.08602265625" name="pixel107"/> + <location y="0.08804671875" name="pixel108"/> + <location y="0.09007078125" name="pixel109"/> + <location y="0.09209484375" name="pixel110"/> + <location y="0.09411890625" name="pixel111"/> + <location y="0.09614296875" name="pixel112"/> + <location y="0.09816703125" name="pixel113"/> + <location y="0.10019109375" name="pixel114"/> + <location y="0.10221515625" name="pixel115"/> + <location y="0.10423921875" name="pixel116"/> + <location y="0.10626328125" name="pixel117"/> + <location y="0.10828734375" name="pixel118"/> + <location y="0.11031140625" name="pixel119"/> + <location y="0.11233546875" name="pixel120"/> + <location y="0.11435953125" name="pixel121"/> + <location y="0.11638359375" name="pixel122"/> + <location y="0.11840765625" name="pixel123"/> + <location y="0.12043171875" name="pixel124"/> + <location y="0.12245578125" name="pixel125"/> + <location y="0.12447984375" name="pixel126"/> + <location y="0.12650390625" name="pixel127"/> + <location y="0.12852796875" name="pixel128"/> + </component> + </type> + <!--SMALL BOTTOM 128 PIXEL TUBE--> + <type name="tube-bottom" outline="yes"> + <properties/> + <component type="pixel-bottom"> + <location y="-0.165448257812" name="pixel1"/> + <location y="-0.162842773438" name="pixel2"/> + <location y="-0.160237289063" name="pixel3"/> + <location y="-0.157631804687" name="pixel4"/> + <location y="-0.155026320312" name="pixel5"/> + <location y="-0.152420835937" name="pixel6"/> + <location y="-0.149815351563" name="pixel7"/> + <location y="-0.147209867188" name="pixel8"/> + <location y="-0.144604382812" name="pixel9"/> + <location y="-0.141998898437" name="pixel10"/> + <location y="-0.139393414062" name="pixel11"/> + <location y="-0.136787929688" name="pixel12"/> + <location y="-0.134182445313" name="pixel13"/> + <location y="-0.131576960937" name="pixel14"/> + <location y="-0.128971476562" name="pixel15"/> + <location y="-0.126365992187" name="pixel16"/> + <location y="-0.123760507813" name="pixel17"/> + <location y="-0.121155023437" name="pixel18"/> + <location y="-0.118549539062" name="pixel19"/> + <location y="-0.115944054687" name="pixel20"/> + <location y="-0.113338570312" name="pixel21"/> + <location y="-0.110733085938" name="pixel22"/> + <location y="-0.108127601562" name="pixel23"/> + <location y="-0.105522117187" name="pixel24"/> + <location y="-0.102916632812" name="pixel25"/> + <location y="-0.100311148437" name="pixel26"/> + <location y="-0.0977056640625" name="pixel27"/> + <location y="-0.0951001796875" name="pixel28"/> + <location y="-0.0924946953125" name="pixel29"/> + <location y="-0.0898892109375" name="pixel30"/> + <location y="-0.0872837265625" name="pixel31"/> + <location y="-0.0846782421875" name="pixel32"/> + <location y="-0.0820727578125" name="pixel33"/> + <location y="-0.0794672734375" name="pixel34"/> + <location y="-0.0768617890625" name="pixel35"/> + <location y="-0.0742563046875" name="pixel36"/> + <location y="-0.0716508203125" name="pixel37"/> + <location y="-0.0690453359375" name="pixel38"/> + <location y="-0.0664398515625" name="pixel39"/> + <location y="-0.0638343671875" name="pixel40"/> + <location y="-0.0612288828125" name="pixel41"/> + <location y="-0.0586233984375" name="pixel42"/> + <location y="-0.0560179140625" name="pixel43"/> + <location y="-0.0534124296875" name="pixel44"/> + <location y="-0.0508069453125" name="pixel45"/> + <location y="-0.0482014609375" name="pixel46"/> + <location y="-0.0455959765625" name="pixel47"/> + <location y="-0.0429904921875" name="pixel48"/> + <location y="-0.0403850078125" name="pixel49"/> + <location y="-0.0377795234375" name="pixel50"/> + <location y="-0.0351740390625" name="pixel51"/> + <location y="-0.0325685546875" name="pixel52"/> + <location y="-0.0299630703125" name="pixel53"/> + <location y="-0.0273575859375" name="pixel54"/> + <location y="-0.0247521015625" name="pixel55"/> + <location y="-0.0221466171875" name="pixel56"/> + <location y="-0.0195411328125" name="pixel57"/> + <location y="-0.0169356484375" name="pixel58"/> + <location y="-0.0143301640625" name="pixel59"/> + <location y="-0.0117246796875" name="pixel60"/> + <location y="-0.0091191953125" name="pixel61"/> + <location y="-0.0065137109375" name="pixel62"/> + <location y="-0.0039082265625" name="pixel63"/> + <location y="-0.0013027421875" name="pixel64"/> + <location y="0.0013027421875" name="pixel65"/> + <location y="0.0039082265625" name="pixel66"/> + <location y="0.0065137109375" name="pixel67"/> + <location y="0.0091191953125" name="pixel68"/> + <location y="0.0117246796875" name="pixel69"/> + <location y="0.0143301640625" name="pixel70"/> + <location y="0.0169356484375" name="pixel71"/> + <location y="0.0195411328125" name="pixel72"/> + <location y="0.0221466171875" name="pixel73"/> + <location y="0.0247521015625" name="pixel74"/> + <location y="0.0273575859375" name="pixel75"/> + <location y="0.0299630703125" name="pixel76"/> + <location y="0.0325685546875" name="pixel77"/> + <location y="0.0351740390625" name="pixel78"/> + <location y="0.0377795234375" name="pixel79"/> + <location y="0.0403850078125" name="pixel80"/> + <location y="0.0429904921875" name="pixel81"/> + <location y="0.0455959765625" name="pixel82"/> + <location y="0.0482014609375" name="pixel83"/> + <location y="0.0508069453125" name="pixel84"/> + <location y="0.0534124296875" name="pixel85"/> + <location y="0.0560179140625" name="pixel86"/> + <location y="0.0586233984375" name="pixel87"/> + <location y="0.0612288828125" name="pixel88"/> + <location y="0.0638343671875" name="pixel89"/> + <location y="0.0664398515625" name="pixel90"/> + <location y="0.0690453359375" name="pixel91"/> + <location y="0.0716508203125" name="pixel92"/> + <location y="0.0742563046875" name="pixel93"/> + <location y="0.0768617890625" name="pixel94"/> + <location y="0.0794672734375" name="pixel95"/> + <location y="0.0820727578125" name="pixel96"/> + <location y="0.0846782421875" name="pixel97"/> + <location y="0.0872837265625" name="pixel98"/> + <location y="0.0898892109375" name="pixel99"/> + <location y="0.0924946953125" name="pixel100"/> + <location y="0.0951001796875" name="pixel101"/> + <location y="0.0977056640625" name="pixel102"/> + <location y="0.100311148437" name="pixel103"/> + <location y="0.102916632813" name="pixel104"/> + <location y="0.105522117188" name="pixel105"/> + <location y="0.108127601563" name="pixel106"/> + <location y="0.110733085938" name="pixel107"/> + <location y="0.113338570312" name="pixel108"/> + <location y="0.115944054688" name="pixel109"/> + <location y="0.118549539063" name="pixel110"/> + <location y="0.121155023438" name="pixel111"/> + <location y="0.123760507813" name="pixel112"/> + <location y="0.126365992187" name="pixel113"/> + <location y="0.128971476563" name="pixel114"/> + <location y="0.131576960938" name="pixel115"/> + <location y="0.134182445313" name="pixel116"/> + <location y="0.136787929688" name="pixel117"/> + <location y="0.139393414062" name="pixel118"/> + <location y="0.141998898438" name="pixel119"/> + <location y="0.144604382813" name="pixel120"/> + <location y="0.147209867188" name="pixel121"/> + <location y="0.149815351563" name="pixel122"/> + <location y="0.152420835937" name="pixel123"/> + <location y="0.155026320313" name="pixel124"/> + <location y="0.157631804688" name="pixel125"/> + <location y="0.160237289063" name="pixel126"/> + <location y="0.162842773438" name="pixel127"/> + <location y="0.165448257812" name="pixel128"/> + </component> + </type> + <!--PIXEL FOR STANDARD 128 PIXEL TUBE--> + <type is="detector" name="pixel"> + <cylinder id="cyl-approx"> + <centre-of-bottom-base p="0.0" r="0.0" t="0.0"/> + <axis y="1.0" x="0.0" z="0.0"/> + <radius val="0.0127"/> + <height val="0.009375"/> + </cylinder> + <algebra val="cyl-approx"/> + </type> + <!--PIXEL FOR SMALL TOP 128 PIXEL TUBE--> + <type is="detector" name="pixel-top"> + <cylinder id="cyl-approx"> + <centre-of-bottom-base p="0.0" r="0.0" t="0.0"/> + <axis y="1.0" x="0.0" z="0.0"/> + <radius val="0.0127"/> + <height val="0.0020240625"/> + </cylinder> + <algebra val="cyl-approx"/> + </type> + <!--PIXEL FOR SMALL BOTTOM 128 PIXEL TUBE--> + <type is="detector" name="pixel-bottom"> + <cylinder id="cyl-approx"> + <centre-of-bottom-base p="0.0" r="0.0" t="0.0"/> + <axis y="1.0" x="0.0" z="0.0"/> + <radius val="0.0127"/> + <height val="0.002605484375"/> + </cylinder> + <algebra val="cyl-approx"/> + </type> + <!--MONITOR SHAPE--> + <!--FIXME: Do something real here.--> + <type is="monitor" name="monitor"> + <cylinder id="cyl-approx"> + <centre-of-bottom-base y="0.0" x="0.0" z="0.0"/> + <axis y="0.0" x="0.0" z="1.0"/> + <radius val="0.01"/> + <height val="0.03"/> + </cylinder> + <algebra val="cyl-approx"/> + </type> + <!--DETECTOR IDs--> + <idlist idname="B row"> + <id start="37888" end="38911"/> + <id start="38912" end="39935"/> + <id start="39936" end="40959"/> + <id start="40960" end="41983"/> + <id start="41984" end="43007"/> + <id start="43008" end="44031"/> + <id start="44032" end="45055"/> + <id start="45056" end="46079"/> + <id start="46080" end="47103"/> + <id start="47104" end="48127"/> + <id start="48128" end="49151"/> + <id start="49152" end="50175"/> + <id start="50176" end="51199"/> + <id start="51200" end="52223"/> + <id start="52224" end="53247"/> + <id start="53248" end="54271"/> + <id start="54272" end="55295"/> + <id start="55296" end="56319"/> + <id start="56320" end="57343"/> + <id start="57344" end="58367"/> + <id start="58368" end="59391"/> + <id start="59392" end="60415"/> + <id start="60416" end="61439"/> + <id start="61440" end="62463"/> + <id start="62464" end="63487"/> + <id start="63488" end="64511"/> + <id start="64512" end="65535"/> + <id start="65536" end="66559"/> + <id start="66560" end="67583"/> + <id start="67584" end="68607"/> + <id start="68608" end="69631"/> + <id start="69632" end="70655"/> + <id start="70656" end="71679"/> + <id start="71680" end="72703"/> + <id start="72704" end="73727"/> + <id start="73728" end="74751"/> + <id start="74752" end="75775"/> + </idlist> + <idlist idname="C row"> + <id start="75776" end="76799"/> + <id start="76800" end="77823"/> + <id start="77824" end="78847"/> + <id start="78848" end="79871"/> + <id start="79872" end="80895"/> + <id start="80896" end="81919"/> + <id start="81920" end="82943"/> + <id start="82944" end="83967"/> + <id start="83968" end="84991"/> + <id start="84992" end="86015"/> + <id start="86016" end="87039"/> + <id start="87040" end="88063"/> + <id start="88064" end="89087"/> + <id start="89088" end="90111"/> + <id start="90112" end="91135"/> + <id start="91136" end="92159"/> + <id start="92160" end="93183"/> + <id start="93184" end="94207"/> + <id start="94208" end="95231"/> + <id start="95232" end="96255"/> + <id start="96256" end="97279"/> + <id start="97280" end="98303"/> + <id start="98304" end="99327"/> + <id start="99328" end="100351"/> + <id start="100352" end="101375"/> + <id start="101376" end="102399"/> + <id start="102400" end="103423"/> + <id start="103424" end="104447"/> + <id start="104448" end="105471"/> + <id start="105472" end="106495"/> + <id start="106496" end="107519"/> + <id start="107520" end="108543"/> + <id start="108544" end="109567"/> + <id start="109568" end="110591"/> + <id start="110592" end="111615"/> + <id start="111616" end="112639"/> + <id start="112640" end="113663"/> + <id start="113664" end="114687"/> + <id start="114688" end="115711"/> + </idlist> + <idlist idname="D row"> + <id start="115712" end="116735"/> + <id start="116736" end="117759"/> + <id start="117760" end="118783"/> + <id start="118784" end="119807"/> + <id start="119808" end="120831"/> + <id start="120832" end="121855"/> + <id start="121856" end="122879"/> + <id start="122880" end="123903"/> + <id start="123904" end="124927"/> + <id start="124928" end="125951"/> + <id start="125952" end="126975"/> + <id start="126976" end="127999"/> + <id start="128000" end="129023"/> + <id start="129024" end="130047"/> + <id start="130048" end="131071"/> + <id start="131072" end="132095"/> + <id start="132096" end="133119"/> + <id start="133120" end="134143"/> + <id start="134144" end="135167"/> + <id start="135168" end="136191"/> + <id start="136192" end="137215"/> + <id start="137216" end="138239"/> + <id start="138240" end="139263"/> + <id start="139264" end="140287"/> + <id start="140288" end="141311"/> + <id start="141312" end="142335"/> + <id start="142336" end="143359"/> + <id start="143360" end="144383"/> + <id start="144384" end="145407"/> + <id start="145408" end="146431"/> + <id start="146432" end="147455"/> + <id start="147456" end="148479"/> + <id start="148480" end="149503"/> + <id start="149504" end="150527"/> + <id start="150528" end="151551"/> + <id start="151552" end="152575"/> + <id start="152576" end="153599"/> + </idlist> + <!--MONITOR IDs--> + <idlist idname="monitors"> + <id val="-1"/> + <id val="-2"/> + </idlist> + <!--DETECTOR PARAMETERS--> + <component-link name="B row"> + <parameter name="tube_pressure"> + <value units="atm" val="10.0"/> + </parameter> + <parameter name="tube_thickness"> + <value units="metre" val="0.0008"/> + </parameter> + <parameter name="tube_temperature"> + <value units="K" val="290.0"/> + </parameter> + </component-link> + <component-link name="C row"> + <parameter name="tube_pressure"> + <value units="atm" val="10.0"/> + </parameter> + <parameter name="tube_thickness"> + <value units="metre" val="0.0008"/> + </parameter> + <parameter name="tube_temperature"> + <value units="K" val="290.0"/> + </parameter> + </component-link> + <component-link name="D row"> + <parameter name="tube_pressure"> + <value units="atm" val="10.0"/> + </parameter> + <parameter name="tube_thickness"> + <value units="metre" val="0.0008"/> + </parameter> + <parameter name="tube_temperature"> + <value units="K" val="290.0"/> + </parameter> + </component-link> +</instrument> diff --git a/instrument/SEQUOIA_Definition.xml b/instrument/SEQUOIA_Definition.xml index ae0a7cf3160892b2a4e763b9bc8bb9dfe66fe886..bfc60dbcf2292a9c39c90b8c10c9d06a05964ce6 100644 --- a/instrument/SEQUOIA_Definition.xml +++ b/instrument/SEQUOIA_Definition.xml @@ -4,7 +4,7 @@ <instrument xmlns="http://www.mantidproject.org/IDF/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mantidproject.org/IDF/1.0 http://schema.mantidproject.org/IDF/1.0/IDFSchema.xsd" - valid-to="2100-01-31 23:59:59" name="SEQUOIA" valid-from="2012-04-04 14:15:46.929098"> + valid-to="2100-01-31 23:59:59" name="SEQUOIA" valid-from="2017-11-14 00:00:00"> <!--For runs after 19889 --> <defaults> <length unit="metre"/> @@ -150,265 +150,305 @@ <location/> </component> </type> + + + <type name="B1"> <component type="eightpack"> - <location y="-1.30353562" x="4.60563316108" z="2.76377527"> - <rot axis-z="0" axis-x="0" axis-y="1" val="239.032627"/> + <location x="4.60563316" y="-1.30353562" z="2.76377527"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-120.96737300"/> </location> </component> </type> + <type name="B2"> <component type="eightpack"> - <location y="-1.303534985" x="4.5367332938" z="2.8754758848"> - <rot axis-z="0" axis-x="0" axis-y="1" val="236.732627"/> + <location x="4.53673329" y="-1.30353498" z="2.87547588"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-123.26737300"/> </location> </component> </type> + <type name="B3"> <component type="eightpack"> - <location y="-1.29480004006" x="4.31906484166" z="3.19313298634"> - <rot axis-z="0" axis-x="0" axis-y="1" val="233.52406849"/> + <location x="4.32508796" y="-1.29480004" z="3.17838441"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-126.03975949"/> </location> </component> </type> + <type name="B4"> <component type="eightpack"> - <location y="-1.29480004006" x="4.18256210476" z="3.36996307718"> - <rot axis-z="0" axis-x="0" axis-y="1" val="231.14100232"/> + <location x="4.18866859" y="-1.29480004" z="3.35768576"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-130.73635071"/> </location> </component> </type> + <type name="B5"> <component type="eightpack"> - <location y="-1.29480004006" x="4.0394219694" z="3.54026506322"> - <rot axis-z="0" axis-x="0" axis-y="1" val="228.76774758"/> + <location x="4.03408387" y="-1.29480004" z="3.52380664"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-133.23225242"/> </location> </component> </type> + <type name="B6"> <component type="eightpack"> - <location y="-1.29480004006" x="3.88900961084" z="3.70488877512"> - <rot axis-z="0" axis-x="0" axis-y="1" val="226.38891513"/> + <location x="3.89604104" y="-1.29480004" z="3.69605380"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-133.59755814"/> </location> </component> </type> + <type name="B7"> <component type="eightpack"> - <location y="-1.29480004006" x="3.71577988364" z="3.87745665452"> - <rot axis-z="0" axis-x="0" axis-y="1" val="223.78023066"/> + <location x="3.71929890" y="-1.29480004" z="3.86302362"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-134.43907120"/> </location> </component> </type> + <type name="B8"> <component type="eightpack"> - <location y="-1.29480004006" x="3.55275443372" z="4.0273746986"> - <rot axis-z="0" axis-x="0" axis-y="1" val="221.41717399"/> + <location x="3.55198594" y="-1.29480004" z="4.01116995"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-140.58282601"/> </location> </component> </type> + <type name="B9"> <component type="eightpack"> - <location y="-1.29480004006" x="3.38385871106" z="4.17028409782"> - <rot axis-z="0" axis-x="0" axis-y="1" val="219.05662215"/> + <location x="3.38288547" y="-1.29480004" z="4.15489954"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-138.94337785"/> </location> </component> </type> + <type name="B10"> <component type="eightpack"> - <location y="-1.29480004006" x="3.20998708014" z="4.30555569164"> - <rot axis-z="0" axis-x="0" axis-y="1" val="216.70619963"/> + <location x="3.20856785" y="-1.29480004" z="4.28814601"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-145.29380037"/> </location> </component> </type> + <type name="B11"> <component type="eightpack"> - <location y="-1.29480004006" x="3.02996424486" z="4.43407703226"> - <rot axis-z="0" axis-x="0" axis-y="1" val="214.34619963"/> + <location x="3.02000290" y="-1.29480004" z="4.41187658"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-145.65316791"/> </location> </component> </type> + <type name="B12"> <component type="eightpack"> - <location y="-1.29480004006" x="2.84480731774" z="4.5550843874"> - <rot axis-z="0" axis-x="0" axis-y="1" val="211.98619963"/> + <location x="2.83477748" y="-1.29480004" z="4.53219306"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-150.01380037"/> </location> </component> </type> + <type name="B13"> <component type="eightpack"> - <location y="-1.29480004006" x="2.65482458366" z="4.6683646917"> - <rot axis-z="0" axis-x="0" axis-y="1" val="209.62619963"/> + <location x="2.64110812" y="-1.29480004" z="4.64038880"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-150.37571272"/> </location> </component> </type> + <type name="B14"> <component type="eightpack"> - <location y="-1.29480004006" x="2.4603383163" z="4.77372578146"> - <rot axis-z="0" axis-x="0" axis-y="1" val="207.26619963"/> + <location x="2.42016459" y="-1.29480004" z="4.75766497"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-154.20290073"/> </location> </component> </type> + <type name="B15"> <component type="eightpack"> - <location y="-1.29480004006" x="2.22317726585" z="4.88876471826"> - <rot axis-z="0" axis-x="0" axis-y="1" val="204.45377863"/> + <location x="2.21521234" y="-1.29480004" z="4.84460733"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-155.53900705"/> </location> </component> </type> + <type name="B16"> <component type="eightpack"> - <location y="-1.29480004006" x="2.01893382776" z="4.97651229486"> - <rot axis-z="0" axis-x="0" axis-y="1" val="202.08202669"/> + <location x="2.00923259" y="-1.29480004" z="4.93196737"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-157.91138643"/> </location> </component> </type> + <type name="B17"> <component type="eightpack"> - <location y="-1.29480004006" x="1.81182191864" z="5.05564016048"> - <rot axis-z="0" axis-x="0" axis-y="1" val="199.71647921"/> + <location x="1.79989540" y="-1.29480004" z="5.00713336"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-162.28352079"/> </location> </component> </type> + <type name="B18"> <component type="eightpack"> - <location y="-1.29480004006" x="1.60068188829" z="5.12639931284"> - <rot axis-z="0" axis-x="0" axis-y="1" val="197.3406045"/> + <location x="1.58897659" y="-1.29480004" z="5.07332939"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-160.76312018"/> </location> </component> </type> + <type name="B19"> <component type="eightpack"> - <location y="-1.29479999993" x="1.38823150065" z="5.18797469134"> - <rot axis-z="0" axis-x="0" axis-y="1" val="194.9806045"/> + <location x="1.38823150" y="-1.29480000" z="5.18797469"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-165.01939550"/> </location> </component> </type> + <type name="B20"> <component type="eightpack"> - <location y="-1.29479999993" x="1.17342296557" z="5.24073913242"> - <rot axis-z="0" axis-x="0" axis-y="1" val="192.6206045"/> + <location x="1.15312896" y="-1.29480000" z="5.17106157"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-165.74312465"/> </location> </component> </type> + <type name="B21"> <component type="eightpack"> - <location y="-1.29479999993" x="0.92524769992" z="5.29019704722"> - <rot axis-z="0" axis-x="0" axis-y="1" val="189.9206045"/> + <location x="0.90484069" y="-1.29480000" z="5.21640688"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-168.07939550"/> </location> </component> </type> + <type name="B22"> <component type="eightpack"> - <location y="-1.29479999993" x="0.706622533158" z="5.32380997974"> - <rot axis-z="0" axis-x="0" axis-y="1" val="187.5606045"/> + <location x="0.67538331" y="-1.29480000" z="5.22415270"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-170.43939550"/> </location> </component> </type> + <type name="B23"> <component type="eightpack"> - <location y="-1.29479999993" x="0.48679868223" z="5.34839182796"> - <rot axis-z="0" axis-x="0" axis-y="1" val="185.2006045"/> + <location x="0.44007592" y="-1.29480000" z="5.26821910"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-172.79939550"/> </location> </component> </type> + <type name="B24"> <component type="eightpack"> - <location y="-1.29479999993" x="0.266149046568" z="5.3639008927"> - <rot axis-z="0" axis-x="0" axis-y="1" val="182.8406045"/> + <location x="0.18960402" y="-1.29480000" z="5.28024042"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-175.15939550"/> </location> </component> </type> + <type name="B25"> <component type="eightpack"> - <location y="-1.29479999993" x="0.045047927176" z="5.37031086464"> - <rot axis-z="0" axis-x="0" axis-y="1" val="180.4806045"/> + <location x="0.01466154" y="-1.29480000" z="5.33357573"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-177.51939550"/> </location> </component> </type> + <type name="B26"> <component type="eightpack"> - <location y="-1.29479999993" x="-0.207962706248" z="5.36647180196"> - <rot axis-z="0" axis-x="0" axis-y="1" val="177.78077169"/> + <location x="-0.09080780" y="-1.29480000" z="5.31769720"> + <rot axis-z="0" axis-x="0" axis-y="1" val="175.78077169"/> </location> </component> </type> + <type name="B27"> <component type="eightpack"> - <location y="-1.29479999993" x="-0.428767559532" z="5.35335656276"> - <rot axis-z="0" axis-x="0" axis-y="1" val="175.42077169"/> + <location x="-0.35773309" y="-1.29480000" z="5.26980698"> + <rot axis-z="0" axis-x="0" axis-y="1" val="173.42077169"/> </location> </component> </type> + <type name="B28"> <component type="eightpack"> - <location y="-1.29479999993" x="-0.648845069592" z="5.3311601149"> - <rot axis-z="0" axis-x="0" axis-y="1" val="173.06077169"/> + <location x="-0.59159503" y="-1.29480000" z="5.24629441"> + <rot axis-z="0" axis-x="0" axis-y="1" val="171.06077169"/> </location> </component> </type> + <type name="B29"> <component type="eightpack"> - <location y="-1.29479999993" x="-0.867821906974" z="5.29992011642"> - <rot axis-z="0" axis-x="0" axis-y="1" val="170.70077169"/> + <location x="-0.83175778" y="-1.29480000" z="5.22538666"> + <rot axis-z="0" axis-x="0" axis-y="1" val="172.70077169"/> </location> </component> </type> + <type name="B30"> <component type="eightpack"> - <location y="-1.29479999993" x="-1.08532660785" z="5.25968955934"> - <rot axis-z="0" axis-x="0" axis-y="1" val="168.34077169"/> + <location x="-1.04781390" y="-1.29480000" z="5.18902672"> + <rot axis-z="0" axis-x="0" axis-y="1" val="170.34077169"/> </location> </component> </type> + <type name="B31"> <component type="eightpack"> - <location y="-1.29479999993" x="-1.30099020726" z="5.21053668838"> - <rot axis-z="0" axis-x="0" axis-y="1" val="165.98077169"/> + <location x="-1.27165523" y="-1.29480000" z="5.15219040"> + <rot axis-z="0" axis-x="0" axis-y="1" val="165.97471636"/> </location> </component> </type> + <type name="B32"> <component type="eightpack"> - <location y="-1.29480004006" x="-1.51446642013" z="5.15261142704"> - <rot axis-z="0" axis-x="0" axis-y="1" val="163.62077169"/> + <location x="-1.48889791" y="-1.29480004" z="5.10215436"> + <rot axis-z="0" axis-x="0" axis-y="1" val="163.61590264"/> </location> </component> </type> + <type name="B33"> <component type="eightpack"> - <location y="-1.29480004006" x="-1.72664610677" z="5.08615521502"> - <rot axis-z="0" axis-x="0" axis-y="1" val="161.24869638"/> + <location x="-1.69931559" y="-1.29480004" z="5.03778769"> + <rot axis-z="0" axis-x="0" axis-y="1" val="163.24869638"/> </location> </component> </type> + <type name="B34"> <component type="eightpack"> - <location y="-1.29480004006" x="-1.95342748836" z="5.00448226686"> - <rot axis-z="0" axis-x="0" axis-y="1" val="158.67752934"/> + <location x="-1.93536281" y="-1.29480004" z="4.96546377"> + <rot axis-z="0" axis-x="0" axis-y="1" val="158.67455058"/> </location> </component> </type> + <type name="B35"> <component type="eightpack"> - <location y="-1.29479999993" x="-2.15779859854" z="4.91969141536"> - <rot axis-z="0" axis-x="0" axis-y="1" val="156.31752934"/> + <location x="-2.13714329" y="-1.29480000" z="4.88845537"> + <rot axis-z="0" axis-x="0" axis-y="1" val="156.31649375"/> </location> </component> </type> + <type name="B36"> <component type="eightpack"> - <location y="-1.29480004006" x="-2.35202492205" z="4.83001371308"> - <rot axis-z="0" axis-x="0" axis-y="1" val="154.03575652"/> + <location x="-2.33740510" y="-1.29480004" z="4.79624681"> + <rot axis-z="0" axis-x="0" axis-y="1" val="156.03575652"/> </location> </component> </type> + <type name="B37"> <component type="eightpack"> - <location y="-1.29480004006" x="-2.54885044204" z="4.72893432164"> - <rot axis-z="0" axis-x="0" axis-y="1" val="151.67575652"/> + <location x="-2.53854557" y="-1.29480004" z="4.70435809"> + <rot axis-z="0" axis-x="0" axis-y="1" val="153.67575652"/> </location> </component> </type> + <component type="C row" idlist="C row"> <location/> </component> @@ -531,279 +571,322 @@ <location/> </component> </type> + + <type name="C1"> <component type="eightpack"> - <location y="-0.0389000000002" x="4.6875281408" z="2.89948601712"> - <rot axis-z="0" axis-x="0" axis-y="1" val="238.261"/> + <location x="4.68752814" y="-0.03890000" z="2.89948602"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-121.73900000"/> </location> </component> </type> + <type name="C2"> <component type="eightpack"> - <location y="-0.0389000000002" x="4.56415688386" z="3.09005035024"> - <rot axis-z="0" axis-x="0" axis-y="1" val="235.901"/> + <location x="4.56415688" y="-0.03890000" z="3.09005035"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-124.09900000"/> </location> </component> </type> + <type name="C3"> <component type="eightpack"> - <location y="-0.0389000000002" x="4.43304318622" z="3.27537285488"> - <rot axis-z="0" axis-x="0" axis-y="1" val="233.541"/> + <location x="4.44152039" y="-0.03890000" z="3.26875643"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-128.43191466"/> </location> </component> </type> + <type name="C4"> <component type="eightpack"> - <location y="-0.0389000000002" x="4.29500585244" z="3.45438429518"> - <rot axis-z="0" axis-x="0" axis-y="1" val="231.191"/> + <location x="4.30245611" y="-0.03890000" z="3.44722278"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-129.54935813"/> </location> </component> </type> + <type name="C5"> <component type="eightpack"> - <location y="-0.0389000000002" x="4.14967007504" z="3.62768575198"> - <rot axis-z="0" axis-x="0" axis-y="1" val="228.8397"/> + <location x="4.15046910" y="-0.03890000" z="3.62726009"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-131.16090642"/> </location> </component> </type> + <type name="C6"> <component type="eightpack"> - <location y="-0.0389000000002" x="3.99533264178" z="3.7969858655"> - <rot axis-z="0" axis-x="0" axis-y="1" val="226.4581"/> + <location x="4.00043213" y="-0.03890000" z="3.79150338"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-134.87781995"/> </location> </component> </type> + <type name="C7"> <component type="eightpack"> - <location y="-0.0389000000002" x="3.81281980386" z="3.97800034472"> - <rot axis-z="0" axis-x="0" axis-y="1" val="223.7854"/> + <location x="3.81887648" y="-0.03890000" z="3.96989553"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-136.21201782"/> </location> </component> </type> + <type name="C8"> <component type="eightpack"> - <location y="-0.0389000000002" x="3.64602183672" z="4.13139723376"> - <rot axis-z="0" axis-x="0" axis-y="1" val="221.4289"/> + <location x="3.65103853" y="-0.03890000" z="4.12467528"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-138.57202879"/> </location> </component> </type> + <type name="C9"> <component type="eightpack"> - <location y="-0.0389000000002" x="3.47173969862" z="4.27892822902"> - <rot axis-z="0" axis-x="0" axis-y="1" val="219.0544"/> + <location x="3.47772803" y="-0.03890000" z="4.27168431"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-142.94560000"/> </location> </component> </type> + <type name="C10"> <component type="eightpack"> - <location y="-0.0389000000002" x="3.29203392138" z="4.41864602934"> - <rot axis-z="0" axis-x="0" axis-y="1" val="216.6873"/> + <location x="3.29697500" y="-0.03890000" z="4.41286144"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-144.49150348"/> </location> </component> </type> + <type name="C11"> <component type="eightpack"> - <location y="-0.0389000000002" x="3.1075271606" z="4.55031056154"> - <rot axis-z="0" axis-x="0" axis-y="1" val="214.3302"/> + <location x="3.11095084" y="-0.03890000" z="4.54489513"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-147.37791861"/> </location> </component> </type> + <type name="C12"> <component type="eightpack"> - <location y="-0.0389000000002" x="2.9161672309" z="4.67522753548"> - <rot axis-z="0" axis-x="0" axis-y="1" val="211.9538"/> + <location x="2.91972054" y="-0.03890000" z="4.67012515"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-148.04570659"/> </location> </component> </type> + <type name="C13"> <component type="eightpack"> - <location y="-0.0389000000002" x="2.71943750244" z="4.79241055994"> - <rot axis-z="0" axis-x="0" axis-y="1" val="209.5726"/> + <location x="2.72318215" y="-0.03890000" z="4.78720431"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-150.42728504"/> </location> </component> </type> + <type name="C14"> <component type="eightpack"> - <location y="-0.0389000000002" x="2.49170903842" z="4.91467906064"> - <rot axis-z="0" axis-x="0" axis-y="1" val="206.8847"/> + <location x="2.49518898" y="-0.03890000" z="4.91013204"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-153.11550665"/> </location> </component> </type> + <type name="C15"> <component type="eightpack"> - <location y="-0.0389000000002" x="2.28458593857" z="5.01427223712"> - <rot axis-z="0" axis-x="0" axis-y="1" val="204.4948"/> + <location x="2.29085022" y="-0.03890000" z="5.00755538"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-157.50520000"/> </location> </component> </type> + <type name="C16"> <component type="eightpack"> - <location y="-0.0389000000002" x="2.07474382485" z="5.10464666648"> - <rot axis-z="0" axis-x="0" axis-y="1" val="202.1189"/> + <location x="2.07971832" y="-0.03890000" z="5.09933415"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-159.67747624"/> </location> </component> </type> + <type name="C17"> <component type="eightpack"> - <location y="-0.0389000000002" x="1.86274311677" z="5.18575022284"> - <rot axis-z="0" axis-x="0" axis-y="1" val="199.7585"/> + <location x="1.86823359" y="-0.03890000" z="5.17927085"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-159.31237483"/> </location> </component> </type> + <type name="C18"> <component type="eightpack"> - <location y="-0.0389000000002" x="1.64894478364" z="5.25773702578"> - <rot axis-z="0" axis-x="0" axis-y="1" val="197.4126"/> + <location x="1.65373656" y="-0.03890000" z="5.25175170"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-162.58521231"/> </location> </component> </type> + <type name="C19"> <component type="eightpack"> - <location y="-0.0389000000002" x="1.43260531538" z="5.3206941592"> - <rot axis-z="0" axis-x="0" axis-y="1" val="195.0696"/> + <location x="1.43601246" y="-0.03890000" z="5.31531240"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-164.92911667"/> </location> </component> </type> + <type name="C20"> <component type="eightpack"> - <location y="-0.0389000000002" x="1.21326640912" z="5.37493873956"> - <rot axis-z="0" axis-x="0" axis-y="1" val="192.72"/> + <location x="1.21525847" y="-0.03890000" z="5.36844336"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-167.27933073"/> </location> </component> </type> + <type name="C21"> <component type="eightpack"> - <location y="-0.0389000000002" x="0.955168885442" z="5.42691364704"> - <rot axis-z="0" axis-x="0" axis-y="1" val="189.98215784"/> + <location x="0.96734789" y="-0.03890000" z="5.41757004"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-170.01252792"/> </location> </component> </type> + <type name="C22"> <component type="eightpack"> - <location y="-0.0389000000002" x="0.73405361952" z="5.46121787104"> - <rot axis-z="0" axis-x="0" axis-y="1" val="187.65536244"/> + <location x="0.74310975" y="-0.03890000" z="5.44833626"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-172.34084747"/> </location> </component> </type> + <type name="C23"> <component type="eightpack"> - <location y="-0.0389000000002" x="0.511432623056" z="5.48666909522"> - <rot axis-z="0" axis-x="0" axis-y="1" val="185.32536244"/> + <location x="0.51611317" y="-0.03890000" z="5.46889453"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-174.67333539"/> </location> </component> </type> + <type name="C24"> <component type="eightpack"> - <location y="-0.0389000000002" x="0.287949459406" z="5.50292524"> - <rot axis-z="0" axis-x="0" axis-y="1" val="182.99536244"/> + <location x="0.28602580" y="-0.03890000" z="5.49485310"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-177.00407517"/> </location> </component> </type> + + <type name="C25T"> <component type="eightpack-top"> - <location y="0.431673" x="0.17600619612" z="5.50764222828"> - <rot axis-z="0" axis-x="0" axis-y="1" val="181.83036244"/> + <location x="0.09212690" y="0.43167300" z="5.46198943"> + <rot axis-z="0" axis-x="0" axis-y="1" val="179.83036244"/> </location> </component> </type> + <type name="C26T"> <component type="eightpack-top"> - <location y="0.431673" x="-0.0480523141568" z="5.51024428302"> - <rot axis-z="0" axis-x="0" axis-y="1" val="179.50036244"/> + <location x="-0.19589577" y="0.43167300" z="5.48742613"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-178.49963756"/> </location> </component> </type> + <type name="C25B"> <component type="eightpack-bottom"> - <location y="-0.468884" x="0.17600619612" z="5.50764222828"> - <rot axis-z="0" axis-x="0" axis-y="1" val="181.83036244"/> + <location x="0.03848187" y="-0.46888400" z="5.51449283"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-176.16963756"/> </location> </component> </type> + <type name="C26B"> <component type="eightpack-bottom"> - <location y="-0.468884" x="-0.0480523141568" z="5.51024428302"> - <rot axis-z="0" axis-x="0" axis-y="1" val="179.50036244"/> + <location x="-0.16610316" y="-0.46888400" z="5.50534429"> + <rot axis-z="0" axis-x="0" axis-y="1" val="177.50036244"/> </location> </component> </type> + + <type name="C27"> <component type="eightpack"> - <location y="-0.0389000000002" x="-0.42281525673" z="5.4947435952"> - <rot axis-z="0" axis-x="0" axis-y="1" val="175.5998157"/> + <location x="-0.43450969" y="-0.03890000" z="5.47475185"> + <rot axis-z="0" axis-x="0" axis-y="1" val="175.59410540"/> </location> </component> </type> + <type name="C28"> <component type="eightpack"> - <location y="-0.0389000000002" x="-0.658224241588" z="5.47153733224"> - <rot axis-z="0" axis-x="0" axis-y="1" val="173.1403"/> + <location x="-0.66081582" y="-0.03890000" z="5.45973585"> + <rot axis-z="0" axis-x="0" axis-y="1" val="173.13995799"/> </location> </component> </type> + <type name="C29"> <component type="eightpack"> - <location y="-0.0389000000002" x="-0.882973593968" z="5.43979205156"> - <rot axis-z="0" axis-x="0" axis-y="1" val="170.7803"/> + <location x="-0.88535518" y="-0.03890000" z="5.43136348"> + <rot axis-z="0" axis-x="0" axis-y="1" val="170.77959045"/> </location> </component> </type> + <type name="C30"> <component type="eightpack"> - <location y="-0.0389000000002" x="-1.10621978349" z="5.39879295398"> - <rot axis-z="0" axis-x="0" axis-y="1" val="168.4203"/> + <location x="-1.10847049" y="-0.03890000" z="5.39200274"> + <rot axis-z="0" axis-x="0" axis-y="1" val="168.41988227"/> </location> </component> </type> + <type name="C31"> <component type="eightpack"> - <location y="-0.0389000000002" x="-1.32781484906" z="5.34867441312"> - <rot axis-z="0" axis-x="0" axis-y="1" val="166.0581"/> + <location x="-1.33021840" y="-0.03890000" z="5.34243710"> + <rot axis-z="0" axis-x="0" axis-y="1" val="166.05724233"/> </location> </component> </type> + <type name="C32"> <component type="eightpack"> - <location y="-0.0389000000002" x="-1.55303519339" z="5.28766236598"> - <rot axis-z="0" axis-x="0" axis-y="1" val="163.632"/> + <location x="-1.54970752" y="-0.03890000" z="5.28566674"> + <rot axis-z="0" axis-x="0" axis-y="1" val="163.63206010"/> </location> </component> </type> + <type name="C33"> <component type="eightpack"> - <location y="-0.0389000000002" x="-1.77201739703" z="5.21830976402"> - <rot axis-z="0" axis-x="0" axis-y="1" val="161.2437"/> + <location x="-1.76631632" y="-0.03890000" z="5.21922266"> + <rot axis-z="0" axis-x="0" axis-y="1" val="161.24445453"/> </location> </component> </type> + <type name="C34"> <component type="eightpack"> - <location y="-0.0389000000002" x="-1.99960889486" z="5.13516569612"> - <rot axis-z="0" axis-x="0" axis-y="1" val="158.7243"/> + <location x="-1.99821145" y="-0.03890000" z="5.13779260"> + <rot axis-z="0" axis-x="0" axis-y="1" val="158.72507943"/> </location> </component> </type> + <type name="C35"> <component type="eightpack"> - <location y="-0.0389000000002" x="-2.21150431485" z="5.04742302172"> - <rot axis-z="0" axis-x="0" axis-y="1" val="156.3396"/> + <location x="-2.20688155" y="-0.03890000" z="5.05127937"> + <rot axis-z="0" axis-x="0" axis-y="1" val="156.34197478"/> </location> </component> </type> + <type name="C36"> <component type="eightpack"> - <location y="-0.0389000000002" x="-2.41598945122" z="4.95276649526"> - <rot axis-z="0" axis-x="0" axis-y="1" val="153.9966"/> + <location x="-2.41521494" y="-0.03890000" z="4.95518370"> + <rot axis-z="0" axis-x="0" axis-y="1" val="153.99717971"/> </location> </component> </type> + <type name="C37"> <component type="eightpack"> - <location y="-0.0389000000002" x="-2.61672177696" z="4.8497781084"> - <rot axis-z="0" axis-x="0" axis-y="1" val="151.6507"/> + <location x="-2.61905356" y="-0.03890000" z="4.85174310"> + <rot axis-z="0" axis-x="0" axis-y="1" val="151.65074678"/> </location> </component> </type> + <component type="D row" idlist="D row"> <location/> </component> @@ -920,265 +1003,303 @@ <location/> </component> </type> + <type name="D1"> <component type="eightpack"> - <location y="1.22910000001" x="4.57110507672" z="2.8206377674"> - <rot axis-z="0" axis-x="0" axis-y="1" val="238.323"/> + <location x="4.57110508" y="1.22910000" z="2.82063777"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-121.67700000"/> </location> </component> </type> + <type name="D2"> <component type="eightpack"> - <location y="1.22910000001" x="4.45107939094" z="3.00647489636"> - <rot axis-z="0" axis-x="0" axis-y="1" val="235.963"/> + <location x="4.45107939" y="1.22910000" z="3.00647490"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-124.03700000"/> </location> </component> </type> + <type name="D3"> <component type="eightpack"> - <location y="1.22910000001" x="4.3234927881" z="3.18720437996"> - <rot axis-z="0" axis-x="0" axis-y="1" val="233.603"/> + <location x="4.32562254" y="1.22910000" z="3.17527848"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-126.39481562"/> </location> </component> </type> + <type name="D4"> <component type="eightpack"> - <location y="1.22909999011" x="4.18920230992" z="3.36178283446"> - <rot axis-z="0" axis-x="0" axis-y="1" val="231.25338983"/> + <location x="4.19024547" y="1.22909999" z="3.34888797"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-130.16527786"/> </location> </component> </type> + <type name="D5"> <component type="eightpack"> - <location y="1.22909999011" x="4.046441869" z="3.53235379888"> - <rot axis-z="0" axis-x="0" axis-y="1" val="228.88056701"/> + <location x="4.04057606" y="1.22909999" z="3.51400427"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-133.11943299"/> </location> </component> </type> + <type name="D6"> <component type="eightpack"> - <location y="1.22909999011" x="3.89757971054" z="3.69601869888"> - <rot axis-z="0" axis-x="0" axis-y="1" val="226.52047601"/> + <location x="3.89992629" y="1.22909999" z="3.68747804"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-133.47127159"/> </location> </component> </type> + <type name="D7"> <component type="eightpack"> - <location y="1.22909999011" x="3.72157876206" z="3.87194811842"> - <rot axis-z="0" axis-x="0" axis-y="1" val="223.86555961"/> + <location x="3.71712561" y="1.22909999" z="3.85067850"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-138.09352040"/> </location> </component> </type> + <type name="D8"> <component type="eightpack"> - <location y="1.22909999011" x="3.5583230586" z="4.02249205914"> - <rot axis-z="0" axis-x="0" axis-y="1" val="221.49618627"/> + <location x="3.55149251" y="1.22909999" z="4.00021060"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-140.18327205"/> </location> </component> </type> + <type name="D9"> <component type="eightpack"> - <location y="1.22909999011" x="3.39320041704" z="4.16270439492"> - <rot axis-z="0" axis-x="0" axis-y="1" val="219.18496575"/> + <location x="3.38537409" y="1.22909999" z="4.14327501"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-142.81503425"/> </location> </component> </type> + <type name="D10"> <component type="eightpack"> - <location y="1.22909999011" x="3.21560850146" z="4.30135873788"> - <rot axis-z="0" axis-x="0" axis-y="1" val="216.78104403"/> + <location x="3.20567983" y="1.22909999" z="4.27410397"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-143.21838586"/> </location> </component> </type> + <type name="D11"> <component type="eightpack"> - <location y="1.22909999011" x="3.03576816772" z="4.43013621718"> - <rot axis-z="0" axis-x="0" axis-y="1" val="214.42104403"/> + <location x="3.02514131" y="1.22909999" z="4.40098907"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-145.57869146"/> </location> </component> </type> + <type name="D12"> <component type="eightpack"> - <location y="1.22909999011" x="2.8507685936" z="4.55138591118"> - <rot axis-z="0" axis-x="0" axis-y="1" val="212.06104403"/> + <location x="2.83822852" y="1.22909999" z="4.51862094"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-147.93482291"/> </location> </component> </type> + <type name="D13"> <component type="eightpack"> - <location y="1.22909999011" x="2.66093309824" z="4.6649148256"> - <rot axis-z="0" axis-x="0" axis-y="1" val="209.70104403"/> + <location x="2.64462431" y="1.22909999" z="4.62399219"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-150.30161665"/> </location> </component> </type> + <type name="D14"> <component type="eightpack"> - <location y="1.22909999011" x="2.43095921681" z="4.7887819515"> - <rot axis-z="0" axis-x="0" axis-y="1" val="206.914"/> + <location x="2.42209904" y="1.22909999" z="4.74028095"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-153.06925621"/> </location> </component> </type> + <type name="D15"> <component type="eightpack"> - <location y="1.22910000001" x="2.23344308175" z="4.88411720152"> - <rot axis-z="0" axis-x="0" axis-y="1" val="204.574"/> + <location x="2.22069737" y="1.22910000" z="4.83068516"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-155.42172481"/> </location> </component> </type> + <type name="D16"> <component type="eightpack"> - <location y="1.22910000001" x="2.03256714746" z="4.97097887692"> - <rot axis-z="0" axis-x="0" axis-y="1" val="202.239"/> + <location x="2.02116273" y="1.22910000" z="4.91162670"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-157.76440699"/> </location> </component> </type> + <type name="D17"> <component type="eightpack"> - <location y="1.22910000001" x="1.82756698926" z="5.04997495972"> - <rot axis-z="0" axis-x="0" axis-y="1" val="199.895"/> + <location x="1.80807003" y="1.22910000" z="4.98762876"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-162.10500000"/> </location> </component> </type> + <type name="D18"> <component type="eightpack"> - <location y="1.22910000001" x="1.61940384297" z="5.12050738406"> - <rot axis-z="0" axis-x="0" axis-y="1" val="197.55"/> + <location x="1.59732827" y="1.22910000" z="5.05348200"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-162.44065504"/> </location> </component> </type> + <type name="D19"> <component type="eightpack"> - <location y="1.22910000001" x="1.40898478401" z="5.1823505446"> - <rot axis-z="0" axis-x="0" axis-y="1" val="195.21"/> + <location x="1.38212538" y="1.22910000" z="5.09666056"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-162.79000000"/> </location> </component> </type> + <type name="D20"> <component type="eightpack"> - <location y="1.22910000001" x="1.19621786562" z="5.23555709554"> - <rot axis-z="0" axis-x="0" axis-y="1" val="192.87"/> + <location x="1.16544269" y="1.22910000" z="5.14003543"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-169.13000000"/> </location> </component> </type> + <type name="D21"> <component type="eightpack"> - <location y="1.22910000001" x="0.985335317362" z="5.28028000076"> - <rot axis-z="0" axis-x="0" axis-y="1" val="190.5702"/> + <location x="0.92532593" y="1.22910000" z="5.17057445"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-167.42980000"/> </location> </component> </type> + <type name="D22"> <component type="eightpack"> - <location y="1.22910000001" x="0.78360224407" z="5.3139241067"> - <rot axis-z="0" axis-x="0" axis-y="1" val="188.3885"/> + <location x="0.71060011" y="1.22910000" z="5.18736890"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-173.61150000"/> </location> </component> </type> + <type name="D23"> <component type="eightpack"> - <location y="1.22910000001" x="0.565047087836" z="5.3415347703"> - <rot axis-z="0" axis-x="0" axis-y="1" val="186.0385"/> + <location x="0.48164236" y="1.22910000" z="5.20368182"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-175.96150000"/> </location> </component> </type> + <type name="D24"> <component type="eightpack"> - <location y="1.22910000001" x="0.345548983018" z="5.36021154524"> - <rot axis-z="0" axis-x="0" axis-y="1" val="183.6885"/> + <location x="0.26369605" y="1.22910000" z="5.21159572"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-174.31150000"/> </location> </component> </type> + <type name="D25"> <component type="eightpack"> - <location y="1.22910000001" x="0.125469660185" z="5.3698723692"> - <rot axis-z="0" axis-x="0" axis-y="1" val="181.3385"/> + <location x="0.06617699" y="1.22910000" z="5.21734046"> + <rot axis-z="0" axis-x="0" axis-y="1" val="-178.84091257"/> </location> </component> </type> + <type name="D26"> <component type="eightpack"> - <location y="1.22910000001" x="-0.0948207044146" z="5.37050099634"> - <rot axis-z="0" axis-x="0" axis-y="1" val="178.9885"/> + <location x="-0.16676294" y="1.22910000" z="5.22006198"> + <rot axis-z="0" axis-x="0" axis-y="1" val="176.98850000"/> </location> </component> </type> + <type name="D27"> <component type="eightpack"> - <location y="1.22910000001" x="-0.45265876194" z="5.35136387434"> - <rot axis-z="0" axis-x="0" axis-y="1" val="175.165"/> + <location x="-0.40194500" y="1.22910000" z="5.21464587"> + <rot axis-z="0" axis-x="0" axis-y="1" val="173.16500000"/> </location> </component> </type> + <type name="D28"> <component type="eightpack"> - <location y="1.22910000001" x="-0.671703998982" z="5.3283026403"> - <rot axis-z="0" axis-x="0" axis-y="1" val="172.815"/> + <location x="-0.62471030" y="1.22910000" z="5.20497816"> + <rot axis-z="0" axis-x="0" axis-y="1" val="172.86287063"/> </location> </component> </type> + <type name="D29"> <component type="eightpack"> - <location y="1.22910000001" x="-0.889619420656" z="5.29627912548"> - <rot axis-z="0" axis-x="0" axis-y="1" val="170.465"/> + <location x="-0.84698897" y="1.22910000" z="5.18880218"> + <rot axis-z="0" axis-x="0" axis-y="1" val="170.54161119"/> </location> </component> </type> + <type name="D30"> <component type="eightpack"> - <location y="1.22910000001" x="-1.10603848998" z="5.25534719566"> - <rot axis-z="0" axis-x="0" axis-y="1" val="168.115"/> + <location x="-1.06502357" y="1.22910000" z="5.15994415"> + <rot axis-z="0" axis-x="0" axis-y="1" val="168.09928742"/> </location> </component> </type> + <type name="D31"> <component type="eightpack"> - <location y="1.22910000001" x="-1.32059718735" z="5.20557569754"> - <rot axis-z="0" axis-x="0" axis-y="1" val="165.765"/> + <location x="-1.27963336" y="1.22910000" z="5.12817396"> + <rot axis-z="0" axis-x="0" axis-y="1" val="163.76500000"/> </location> </component> </type> + <type name="D32"> <component type="eightpack"> - <location y="1.22910000001" x="-1.53293721022" z="5.1470570414"> - <rot axis-z="0" axis-x="0" axis-y="1" val="163.415"/> + <location x="-1.49766720" y="1.22910000" z="5.08217793"> + <rot axis-z="0" axis-x="0" axis-y="1" val="165.41500000"/> </location> </component> </type> + <type name="D33"> <component type="eightpack"> - <location y="1.22910000001" x="-1.74030639162" z="5.0807040372"> - <rot axis-z="0" axis-x="0" axis-y="1" val="161.092"/> + <location x="-1.70438263" y="1.22910000" z="5.01426999"> + <rot axis-z="0" axis-x="0" axis-y="1" val="161.08460684"/> </location> </component> </type> + <type name="D34"> <component type="eightpack"> - <location y="1.22910000001" x="-1.96180272434" z="5.0013049707"> - <rot axis-z="0" axis-x="0" axis-y="1" val="158.582"/> + <location x="-1.93321328" y="1.22910000" z="4.94302276"> + <rot axis-z="0" axis-x="0" axis-y="1" val="160.58200000"/> </location> </component> </type> + <type name="D35"> <component type="eightpack"> - <location y="1.22910000001" x="-2.16506783826" z="4.91676592972"> - <rot axis-z="0" axis-x="0" axis-y="1" val="156.234"/> + <location x="-2.14240965" y="1.22910000" z="4.86983735"> + <rot axis-z="0" axis-x="0" axis-y="1" val="157.56952266"/> </location> </component> </type> + <type name="D36"> <component type="eightpack"> - <location y="1.22910000001" x="-2.36510173842" z="4.8237238044"> - <rot axis-z="0" axis-x="0" axis-y="1" val="153.881"/> + <location x="-2.34599340" y="1.22910000" z="4.78604450"> + <rot axis-z="0" axis-x="0" axis-y="1" val="153.87709788"/> </location> </component> </type> + <type name="D37"> <component type="eightpack"> - <location y="1.22910000001" x="-2.56172303758" z="4.7222335451"> - <rot axis-z="0" axis-x="0" axis-y="1" val="151.521"/> + <location x="-2.54477954" y="1.22910000" z="4.69529234"> + <rot axis-z="0" axis-x="0" axis-y="1" val="151.52119987"/> </location> </component> </type> + <!--STANDARD 8-PACK--> <type name="eightpack"> <properties/> diff --git a/qt/scientific_interfaces/CMakeLists.txt b/qt/scientific_interfaces/CMakeLists.txt index e979e681458c44050ef2a469c5abd13d92cd78ac..25a9fa612bf0b3eab71e068b2ce4bf437bbb6835 100644 --- a/qt/scientific_interfaces/CMakeLists.txt +++ b/qt/scientific_interfaces/CMakeLists.txt @@ -30,6 +30,7 @@ set ( TEST_FILES test/ALCLatestFileFinderTest.h test/ALCPeakFittingModelTest.h test/ALCPeakFittingPresenterTest.h + test/EnggDiffFittingModelTest.h test/EnggDiffFittingPresenterTest.h test/EnggDiffractionPresenterTest.h test/IO_MuonGroupingTest.h diff --git a/qt/scientific_interfaces/EnggDiffraction/CMakeLists.txt b/qt/scientific_interfaces/EnggDiffraction/CMakeLists.txt index 985648af0074a208841bb5256b44c65d0f60990f..e5e0b3be88254d266056ec745716c1e6405d0117 100644 --- a/qt/scientific_interfaces/EnggDiffraction/CMakeLists.txt +++ b/qt/scientific_interfaces/EnggDiffraction/CMakeLists.txt @@ -1,4 +1,5 @@ set ( SRC_FILES + EnggDiffFittingModel.cpp EnggDiffFittingPresenter.cpp EnggDiffFittingViewQtWidget.cpp EnggDiffractionPresenter.cpp @@ -9,6 +10,7 @@ set ( SRC_FILES # IMPORTANT: Include files are required in the MOC_FILES set. Scroll down to find it. set ( INC_FILES EnggDiffCalibSettings.h + EnggDiffFittingModel.h EnggDiffFittingPresWorker.h EnggDiffFittingPresenter.h EnggDiffFittingViewQtWidget.h @@ -21,6 +23,7 @@ set ( INC_FILES ) set ( MOC_FILES + EnggDiffFittingModel.h EnggDiffFittingPresenter.h EnggDiffFittingPresWorker.h EnggDiffFittingViewQtWidget.h diff --git a/qt/scientific_interfaces/EnggDiffraction/EnggDiffFittingModel.cpp b/qt/scientific_interfaces/EnggDiffraction/EnggDiffFittingModel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6c9cd5cedc9430afbbe35531f3efaf034bc8f7be --- /dev/null +++ b/qt/scientific_interfaces/EnggDiffraction/EnggDiffFittingModel.cpp @@ -0,0 +1,133 @@ +#include "EnggDiffFittingModel.h" + +#include "MantidAPI/AlgorithmManager.h" +#include "MantidAPI/AnalysisDataService.h" +#include "MantidAPI/MatrixWorkspace.h" +#include "MantidAPI/MatrixWorkspace_fwd.h" +#include "MantidAPI/Run.h" +#include "MantidAPI/WorkspaceGroup.h" +#include "MantidKernel/PropertyWithValue.h" + +#include <algorithm> +#include <numeric> + +using namespace Mantid; + +namespace { + +template <typename T> void insertInOrder(const T &item, std::vector<T> &vec) { + vec.insert(std::upper_bound(vec.begin(), vec.end(), item), item); +} + +bool isDigit(const std::string &text) { + return std::all_of(text.cbegin(), text.cend(), ::isdigit); +} + +} // anonymous namespace + +namespace MantidQT { +namespace CustomInterfaces { + +void EnggDiffFittingModel::addWorkspace(const int runNumber, const size_t bank, + const API::MatrixWorkspace_sptr ws) { + m_wsMap[bank - 1][runNumber] = ws; +} + +API::MatrixWorkspace_sptr +EnggDiffFittingModel::getWorkspace(const int runNumber, const size_t bank) { + if (bank < 1 || bank > m_wsMap.size()) { + return nullptr; + } + if (m_wsMap[bank - 1].find(runNumber) == m_wsMap[bank - 1].end()) { + return nullptr; + } + return m_wsMap[bank - 1][runNumber]; +} + +std::vector<int> EnggDiffFittingModel::getAllRunNumbers() const { + std::vector<int> runNumbers; + + for (const auto &workspaces : m_wsMap) { + for (const auto &kvPair : workspaces) { + const auto runNumber = kvPair.first; + if (std::find(runNumbers.begin(), runNumbers.end(), runNumber) == + runNumbers.end()) { + insertInOrder(runNumber, runNumbers); + } + } + } + + return runNumbers; +} + +void EnggDiffFittingModel::loadWorkspaces(const std::string &filename) { + auto loadAlg = API::AlgorithmManager::Instance().create("Load"); + loadAlg->initialize(); + + loadAlg->setPropertyValue("Filename", filename); + loadAlg->setPropertyValue("OutputWorkspace", FOCUSED_WS_NAME); + loadAlg->execute(); + + API::AnalysisDataServiceImpl &ADS = API::AnalysisDataService::Instance(); + if (filename.find(",") == std::string::npos) { // Only 1 run loaded + const auto ws = ADS.retrieveWS<API::MatrixWorkspace>(FOCUSED_WS_NAME); + addWorkspace(ws->getRunNumber(), guessBankID(ws), ws); + } else { + const auto group_ws = ADS.retrieveWS<API::WorkspaceGroup>(FOCUSED_WS_NAME); + for (auto iter = group_ws->begin(); iter != group_ws->end(); ++iter) { + const auto ws = boost::dynamic_pointer_cast<API::MatrixWorkspace>(*iter); + addWorkspace(ws->getRunNumber(), guessBankID(ws), ws); + } + } +} + +std::vector<std::pair<int, size_t>> +EnggDiffFittingModel::getRunNumbersAndBanksIDs() { + std::vector<std::pair<int, size_t>> pairs; + + const auto runNumbers = getAllRunNumbers(); + for (const auto runNumber : runNumbers) { + for (size_t i = 0; i < m_wsMap.size(); ++i) { + if (m_wsMap[i].find(runNumber) != m_wsMap[i].end()) { + pairs.push_back(std::pair<int, size_t>(runNumber, i + 1)); + } + } + } + return pairs; +} + +size_t +EnggDiffFittingModel::guessBankID(API::MatrixWorkspace_const_sptr ws) const { + if (ws->run().hasProperty("bankid")) { + const auto log = dynamic_cast<Kernel::PropertyWithValue<int> *>( + ws->run().getLogData("bankid")); + return boost::lexical_cast<size_t>(log->value()); + } + + // couldn't get it from sample logs - try using the old naming convention + auto name = ws->getName(); + std::vector<std::string> chunks; + boost::split(chunks, name, boost::is_any_of("_")); + bool isNum = isDigit(chunks.back()); + if (!chunks.empty() && isNum) { + try { + return boost::lexical_cast<size_t>(chunks.back()); + } catch (boost::exception &) { + // If we get a bad cast or something goes wrong then + // the file is probably not what we were expecting + // so throw a runtime error + throw std::runtime_error( + "Failed to fit file: The data was not what is expected. " + "Does the file contain a focused workspace?"); + } + } + + throw std::runtime_error("Could not guess run number from input workspace. " + "Are you sure it has been focused correctly?"); +} + +const std::string EnggDiffFittingModel::FOCUSED_WS_NAME = + "engggui_fitting_focused_ws"; + +} // namespace CustomInterfaces +} // namespace MantidQT diff --git a/qt/scientific_interfaces/EnggDiffraction/EnggDiffFittingModel.h b/qt/scientific_interfaces/EnggDiffraction/EnggDiffFittingModel.h new file mode 100644 index 0000000000000000000000000000000000000000..9993fa1315b0b009a8e8ff0a20f0847a5cb082e6 --- /dev/null +++ b/qt/scientific_interfaces/EnggDiffraction/EnggDiffFittingModel.h @@ -0,0 +1,38 @@ +#ifndef MANTIDQTCUSTOMINTERFACES_ENGGDIFFRACTION_ENGGDIFFFITTINGMODEL_H_ +#define MANTIDQTCUSTOMINTERFACES_ENGGDIFFRACTION_ENGGDIFFFITTINGMODEL_H_ + +#include "DllConfig.h" +#include "MantidAPI/MatrixWorkspace_fwd.h" + +#include <array> +#include <unordered_map> +#include <vector> + +using namespace Mantid; + +namespace MantidQT { +namespace CustomInterfaces { + +class MANTIDQT_ENGGDIFFRACTION_DLL EnggDiffFittingModel { +public: + API::MatrixWorkspace_sptr getWorkspace(const int runNumber, + const size_t bank); + std::vector<int> getAllRunNumbers() const; + void loadWorkspaces(const std::string &filename); + std::vector<std::pair<int, size_t>> getRunNumbersAndBanksIDs(); + void addWorkspace(const int runNumber, const size_t bank, + const API::MatrixWorkspace_sptr ws); + +private: + static const size_t MAX_BANKS = 2; + static const std::string FOCUSED_WS_NAME; + std::array<std::unordered_map<int, API::MatrixWorkspace_sptr>, MAX_BANKS> + m_wsMap; + + size_t guessBankID(API::MatrixWorkspace_const_sptr) const; +}; + +} // namespace CustomInterfaces +} // namespace MantidQT + +#endif // MANTIDQTCUSTOMINTERFACES_ENGGDIFFRACTION_ENGGDIFFFITTINGMODEL_H_ diff --git a/qt/scientific_interfaces/EnggDiffraction/EnggDiffFittingPresenter.cpp b/qt/scientific_interfaces/EnggDiffraction/EnggDiffFittingPresenter.cpp index 09808dcdff316a764bf5b3de387bdfb1784ce812..385f141bdac433f4b9e83598548e44aad63f594a 100644 --- a/qt/scientific_interfaces/EnggDiffraction/EnggDiffFittingPresenter.cpp +++ b/qt/scientific_interfaces/EnggDiffraction/EnggDiffFittingPresenter.cpp @@ -1,6 +1,7 @@ #include "EnggDiffFittingPresenter.h" #include "MantidAPI/AlgorithmManager.h" #include "MantidAPI/AnalysisDataService.h" +#include "MantidAPI/WorkspaceGroup.h" #include "MantidAPI/ITableWorkspace.h" #include "MantidAPI/MatrixWorkspace.h" #include "MantidAPI/Run.h" @@ -9,6 +10,7 @@ #include "MantidQtWidgets/LegacyQwt/QwtHelper.h" #include "EnggDiffFittingPresWorker.h" +#include <boost/algorithm/string.hpp> #include <boost/lexical_cast.hpp> #include <cctype> #include <fstream> @@ -29,6 +31,7 @@ Mantid::Kernel::Logger g_log("EngineeringDiffractionGUI"); const bool EnggDiffFittingPresenter::g_useAlignDetectors = true; +// MOVE THIS TO THE MODEL const std::string EnggDiffFittingPresenter::g_focusedFittingWSName = "engggui_fitting_focused_ws"; @@ -124,6 +127,10 @@ void EnggDiffFittingPresenter::notify( case IEnggDiffFittingPresenter::LogMsg: processLogMsg(); break; + + case IEnggDiffFittingPresenter::selectRun: + processSelectRun(); + break; } } @@ -345,6 +352,16 @@ std::vector<std::string> EnggDiffFittingPresenter::processFullPathInput( return foundFullFilePaths; } +void EnggDiffFittingPresenter::processSelectRun() { + const auto workspaceID = m_view->getFittingListWidgetCurrentValue(); + std::vector<std::string> tokens; + + boost::split(tokens, workspaceID, boost::is_any_of("_")); + const auto ws = + m_model.getWorkspace(std::stoi(tokens[0]), std::stoi(tokens[1])); + plotFocusedFile(false, ws); +} + /** * Takes the full path of a file which has been selected through * browse, the run number the user has input and stores the @@ -703,40 +720,60 @@ EnggDiffFittingPresenter::enableMultiRun(const std::string &firstRun, void EnggDiffFittingPresenter::processStart() {} -void EnggDiffFittingPresenter::processLoad() { - // while file text-area is not empty - // while directory vector is not empty - // if loaded here set a global variable true so doesnt load again? - - try { - MatrixWorkspace_sptr focusedWS; - const std::string focusedFile = m_view->getFittingRunNo(); - Poco::Path selectedfPath(focusedFile); +size_t EnggDiffFittingPresenter::findBankID( + Mantid::API::MatrixWorkspace_sptr ws) const { + // MOVE THIS TO THE MODEL + size_t bankID = 1; - if (!focusedFile.empty() && selectedfPath.isFile()) { - runLoadAlg(focusedFile, focusedWS); - setDifcTzero(focusedWS); - convertUnits(g_focusedFittingWSName); - plotFocusedFile(false); + auto name = ws->getName(); + std::vector<std::string> chunks; + boost::split(chunks, name, boost::is_any_of("_")); + bool isNum = isDigit(chunks.back()); + if (!chunks.empty() && isNum) { + try { + bankID = boost::lexical_cast<size_t>(chunks.back()); + } catch (boost::exception &) { + // If we get a bad cast or something goes wrong then + // the file is probably not what we were expecting + // so throw a runtime error + throw std::runtime_error( + "Failed to fit file: The data was not what is expected. " + "Does the file contain focused " + + m_view->getCurrentInstrument() + " workspace?"); + } + } + return bankID; +} - m_view->showStatus( - "Focused file loaded! (Click 'Select " - "Peak' to activate peak picker tool, hold Shift + Click " - "Peak, Click 'Add Peak')"); +void EnggDiffFittingPresenter::processLoad() { + const std::string filenames = m_view->getFittingRunNo(); - } else { - m_view->userWarning("No File Found", - "Please select a focused file to load"); - m_view->showStatus("Error while plotting the focused workspace"); - } - } catch (std::invalid_argument &ia) { - m_view->userWarning( - "Failed to load the selected focus file", - "The focus file failed to load, please check the logger for more" - " information."); - g_log.error("Failed to load file. Error message: "); - g_log.error(ia.what()); + try { + m_model.loadWorkspaces(filenames); + } catch (Poco::PathSyntaxException &ex) { + warnFileNotFound(ex); + return; + } catch (std::invalid_argument &ex) { + warnFileNotFound(ex); + return; + } catch (Mantid::Kernel::Exception::NotFoundError &ex) { + warnFileNotFound(ex); + return; } + + const auto runNoBankPairs = m_model.getRunNumbersAndBanksIDs(); + std::vector<std::string> workspaceIDs; + std::transform( + runNoBankPairs.begin(), runNoBankPairs.end(), + std::back_inserter(workspaceIDs), [](const std::pair<int, size_t> &pair) { + return std::to_string(pair.first) + "_" + std::to_string(pair.second); + }); + m_view->enableFittingListWidget(true); + m_view->clearFittingListWidget(); + std::for_each(workspaceIDs.begin(), workspaceIDs.end(), + [&](const std::string &workspaceID) { + m_view->addRunNoItem(workspaceID); + }); } void EnggDiffFittingPresenter::processShutDown() { @@ -921,31 +958,7 @@ std::string EnggDiffFittingPresenter::validateFittingexpectedPeaks( } void EnggDiffFittingPresenter::setDifcTzero(MatrixWorkspace_sptr wks) const { - size_t bankID = 1; - // attempt to guess bankID - this should be done in code that is currently - // in the view - auto fittingFilename = m_view->getFittingRunNo(); - Poco::File fittingFile(fittingFilename); - if (fittingFile.exists()) { - Poco::Path path(fittingFile.path()); - auto name = path.getBaseName(); - std::vector<std::string> chunks; - boost::split(chunks, name, boost::is_any_of("_")); - bool isNum = isDigit(chunks.back()); - if (!chunks.empty() && isNum) { - try { - bankID = boost::lexical_cast<size_t>(chunks.back()); - } catch (boost::exception &) { - // If we get a bad cast or something goes wrong then - // the file is probably not what we were expecting - // so throw a runtime error - throw std::runtime_error( - "Failed to fit file: The data was not what is expected. " - "Does the file contain focused " + - m_view->getCurrentInstrument() + " workspace?"); - } - } - } + const auto bankID = findBankID(wks); const std::string units = "none"; auto &run = wks->mutableRun(); @@ -1714,19 +1727,20 @@ bool EnggDiffFittingPresenter::isDigit(const std::string &text) const { return std::all_of(text.cbegin(), text.cend(), ::isdigit); } -void EnggDiffFittingPresenter::plotFocusedFile(bool plotSinglePeaks) { - AnalysisDataServiceImpl &ADS = Mantid::API::AnalysisDataService::Instance(); +void EnggDiffFittingPresenter::warnFileNotFound(const std::exception &ex) { + m_view->showStatus("Error while loading focused run"); + m_view->userWarning("Invalid file selected", + "Mantid could not load the selected file. " + "Are you sure it exists? " + "See the logger for more information"); + g_log.error("Failed to load file. Error message: "); + g_log.error(ex.what()); +} - if (!ADS.doesExist(g_focusedFittingWSName)) { - g_log.error() << "Focused workspace could not be plotted as there is no " + - g_focusedFittingWSName + " workspace found.\n"; - m_view->showStatus("Error while plotting focused workspace"); - return; - } +void EnggDiffFittingPresenter::plotFocusedFile( + bool plotSinglePeaks, MatrixWorkspace_sptr focusedPeaksWS) { try { - auto focusedPeaksWS = - ADS.retrieveWS<MatrixWorkspace>(g_focusedFittingWSName); auto focusedData = QwtHelper::curveDataFromWs(focusedPeaksWS); // Check that the number of curves to plot isn't excessive @@ -1774,7 +1788,9 @@ void EnggDiffFittingPresenter::plotFitPeaksCurves() { m_view->resetCanvas(); // plots focused workspace - plotFocusedFile(m_fittingFinishedOK); + throw new std::runtime_error("Plotting fit not yet implemented"); + // TODO: sort out what to do here + // plotFocusedFile(m_fittingFinishedOK); if (m_fittingFinishedOK) { g_log.debug() << "single peaks fitting being plotted now.\n"; diff --git a/qt/scientific_interfaces/EnggDiffraction/EnggDiffFittingPresenter.h b/qt/scientific_interfaces/EnggDiffraction/EnggDiffFittingPresenter.h index 16a27d250fd3b5584acff3ed512af6568b773d33..a0736a7ff9c3ad763c8f6214084b5b7935e8e6cb 100644 --- a/qt/scientific_interfaces/EnggDiffraction/EnggDiffFittingPresenter.h +++ b/qt/scientific_interfaces/EnggDiffraction/EnggDiffFittingPresenter.h @@ -4,6 +4,7 @@ #include "MantidAPI/ITableWorkspace_fwd.h" #include "MantidAPI/MatrixWorkspace_fwd.h" #include "DllConfig.h" +#include "EnggDiffFittingModel.h" #include "IEnggDiffFittingPresenter.h" #include "IEnggDiffFittingView.h" #include "IEnggDiffractionCalibration.h" @@ -87,7 +88,8 @@ public: std::string tableName, size_t row, std::string &startX, std::string &endX); - void plotFocusedFile(bool plotSinglePeaks); + void plotFocusedFile(bool plotSinglePeaks, + Mantid::API::MatrixWorkspace_sptr focusedPeaksWS); void plotFitPeaksCurves(); @@ -145,8 +147,12 @@ protected slots: void fittingRunNoChanged(); private: + size_t findBankID(Mantid::API::MatrixWorkspace_sptr ws) const; + bool isDigit(const std::string &text) const; + void warnFileNotFound(const std::exception &ex); + // Methods related single peak fits virtual void startAsyncFittingWorker(const std::vector<std::string> &focusedRunNo, @@ -225,8 +231,14 @@ private: /// Associated view for this presenter (MVP pattern) IEnggDiffFittingView *const m_view; + /// Associated model for this presenter + MantidQT::CustomInterfaces::EnggDiffFittingModel m_model; + /// Holds if the view is in the process of being closed bool m_viewHasClosed; + + /// Handle the user selecting a different run to plot + void processSelectRun(); }; } // namespace CustomInterfaces diff --git a/qt/scientific_interfaces/EnggDiffraction/EnggDiffFittingViewQtWidget.cpp b/qt/scientific_interfaces/EnggDiffraction/EnggDiffFittingViewQtWidget.cpp index a24d6a2e1ce8424d48f94a57ced94c48f010caba..cc00e278ccb5ce34fd2ebd59f4a8ae533ba2e82b 100644 --- a/qt/scientific_interfaces/EnggDiffraction/EnggDiffFittingViewQtWidget.cpp +++ b/qt/scientific_interfaces/EnggDiffraction/EnggDiffFittingViewQtWidget.cpp @@ -86,11 +86,8 @@ void EnggDiffFittingViewQtWidget::doSetup() { connect(m_ui.lineEdit_pushButton_run_num, SIGNAL(textEdited(const QString &)), this, SLOT(resetFittingMode())); - connect(m_ui.lineEdit_pushButton_run_num, SIGNAL(editingFinished()), this, - SLOT(FittingRunNo())); - connect(m_ui.lineEdit_pushButton_run_num, SIGNAL(returnPressed()), this, - SLOT(FittingRunNo())); + SLOT(loadClicked())); connect(this, SIGNAL(getBanks()), this, SLOT(FittingRunNo())); @@ -126,6 +123,10 @@ void EnggDiffFittingViewQtWidget::doSetup() { connect(m_ui.pushButton_plot_separate_window, SIGNAL(released()), SLOT(plotSeparateWindow())); + connect(m_ui.listWidget_fitting_run_num, + SIGNAL(itemClicked(QListWidgetItem *)), this, + SLOT(listWidget_fitting_run_num_clicked(QListWidgetItem *))); + // Tool-tip button connect(m_ui.pushButton_tooltip, SIGNAL(released()), SLOT(showToolTipHelp())); @@ -276,6 +277,12 @@ void EnggDiffFittingViewQtWidget::listViewFittingRun() { } } +void EnggDiffFittingViewQtWidget::listWidget_fitting_run_num_clicked( + QListWidgetItem *clickedItem) { + const auto label = clickedItem->text(); + m_presenter->notify(IEnggDiffFittingPresenter::selectRun); +} + void EnggDiffFittingViewQtWidget::resetFittingMode() { // resets the global variable so the list view widgets // adds the run number to for single runs too @@ -459,17 +466,17 @@ void EnggDiffFittingViewQtWidget::browseFitFocusedRun() { std::string nexusFormat = "Nexus file with calibration table: NXS, NEXUS" "(*.nxs *.nexus);;"; - QString path( - QFileDialog::getOpenFileName(this, tr("Open Focused File "), prevPath, - QString::fromStdString(nexusFormat))); + QStringList paths( + QFileDialog::getOpenFileNames(this, tr("Open Focused File "), prevPath, + QString::fromStdString(nexusFormat))); - if (path.isEmpty()) { + if (paths.isEmpty()) { return; } - MantidQt::API::AlgorithmInputHistory::Instance().setPreviousDirectory(path); - setFittingRunNo(path.toStdString()); - getBanks(); + // MantidQt::API::AlgorithmInputHistory::Instance().setPreviousDirectory(paths[0]); + setFittingRunNo(paths.join(",").toStdString()); + // getBanks(); } void EnggDiffFittingViewQtWidget::setFittingRunNo(const std::string &path) { @@ -504,6 +511,11 @@ int EnggDiffFittingViewQtWidget::getFittingListWidgetCurrentRow() const { return m_ui.listWidget_fitting_run_num->currentRow(); } +std::string +EnggDiffFittingViewQtWidget::getFittingListWidgetCurrentValue() const { + return m_ui.listWidget_fitting_run_num->currentItem()->text().toStdString(); +} + void EnggDiffFittingViewQtWidget::setFittingListWidgetCurrentRow( int idx) const { m_ui.listWidget_fitting_run_num->setCurrentRow(idx); diff --git a/qt/scientific_interfaces/EnggDiffraction/EnggDiffFittingViewQtWidget.h b/qt/scientific_interfaces/EnggDiffraction/EnggDiffFittingViewQtWidget.h index 10c6c31e17dda70ac1e9017f930301e5cd783d00..3cfeef3885e8f1ec32f39b41fac008790c330d2a 100644 --- a/qt/scientific_interfaces/EnggDiffraction/EnggDiffFittingViewQtWidget.h +++ b/qt/scientific_interfaces/EnggDiffraction/EnggDiffFittingViewQtWidget.h @@ -107,6 +107,8 @@ public: int getFittingListWidgetCurrentRow() const override; + std::string getFittingListWidgetCurrentValue() const override; + void setFittingListWidgetCurrentRow(int idx) const override; std::string fittingPeaksData() const override; @@ -190,6 +192,7 @@ private slots: void showToolTipHelp(); void setBankDir(int idx); void listViewFittingRun(); + void listWidget_fitting_run_num_clicked(QListWidgetItem *listWidget); private: /// Setup the interface (tab UI) diff --git a/qt/scientific_interfaces/EnggDiffraction/EnggDiffractionQtTabFitting.ui b/qt/scientific_interfaces/EnggDiffraction/EnggDiffractionQtTabFitting.ui index 6ed8a033782e74f7c3703f23581ba38e5c86d190..7c50ba5e7e6a7d6f0dfc577b33d0a77bd92943df 100644 --- a/qt/scientific_interfaces/EnggDiffraction/EnggDiffractionQtTabFitting.ui +++ b/qt/scientific_interfaces/EnggDiffraction/EnggDiffractionQtTabFitting.ui @@ -373,7 +373,7 @@ </property> <property name="maximumSize"> <size> - <width>50</width> + <width>60</width> <height>16777215</height> </size> </property> diff --git a/qt/scientific_interfaces/EnggDiffraction/IEnggDiffFittingPresenter.h b/qt/scientific_interfaces/EnggDiffraction/IEnggDiffFittingPresenter.h index 11f2a885682c810a1d456d34545755602453017a..8c5d25fd1c42f84a9168994e56e7faf43188ea6b 100644 --- a/qt/scientific_interfaces/EnggDiffraction/IEnggDiffFittingPresenter.h +++ b/qt/scientific_interfaces/EnggDiffraction/IEnggDiffFittingPresenter.h @@ -52,6 +52,7 @@ public: savePeaks, ///< Save the peaks list ShutDown, ///< closing the interface LogMsg, ///< need to send a message to the Mantid log system + selectRun, ///< update plot with new run selected from list widget }; /** diff --git a/qt/scientific_interfaces/EnggDiffraction/IEnggDiffFittingView.h b/qt/scientific_interfaces/EnggDiffraction/IEnggDiffFittingView.h index c1638eaeae7dda02b3dc830d457db61081f731d0..8fe38b27bc9a9a06f2deb04a59e78a1c6d96d857 100644 --- a/qt/scientific_interfaces/EnggDiffraction/IEnggDiffFittingView.h +++ b/qt/scientific_interfaces/EnggDiffraction/IEnggDiffFittingView.h @@ -191,6 +191,11 @@ public: */ virtual int getFittingListWidgetCurrentRow() const = 0; + /** + * @return The text on the current selected row of the list widget + */ + virtual std::string getFittingListWidgetCurrentValue() const = 0; + /** * Sets the current row of the fitting list widget * diff --git a/qt/scientific_interfaces/Indirect/ConvFit.cpp b/qt/scientific_interfaces/Indirect/ConvFit.cpp index 457726a04c4205336eaa3bdfd79ab5737fa535a2..f87f091e91fec1da7f2d7e65f97e7fe6ba72eaec 100644 --- a/qt/scientific_interfaces/Indirect/ConvFit.cpp +++ b/qt/scientific_interfaces/Indirect/ConvFit.cpp @@ -481,7 +481,7 @@ void ConvFit::algorithmComplete(bool error, const QString &outputWSName) { updatePlot(); updatePlotRange(); - std::string paramWsName = outputPrefix + "_Parameters"; + const std::string paramWsName = outputPrefix + "_Parameters"; if (AnalysisDataService::Instance().doesExist(paramWsName)) { QString prefixPrefix = "f1.f1."; @@ -828,8 +828,8 @@ CompositeFunction_sptr ConvFit::createFunction(bool tieCentres, func = FunctionFactory::Instance().createFunction("DeltaFunction"); index = model->addFunction(func); std::string parName = createParName(index); - populateFunction(func, model, m_properties["Delta Function"], parName, - false); + populateFunction(func, model, m_properties["Delta Function"], false, + parName); } // ------------------------------------------------------------ @@ -880,7 +880,7 @@ CompositeFunction_sptr ConvFit::createFunction(bool tieCentres, index = model->addFunction(product); prefix1 = createParName(index, subIndex); - populateFunction(func, model, m_properties["FitFunction1"], prefix1, false); + populateFunction(func, model, m_properties["FitFunction1"], false, prefix1); // Add 2nd Lorentzian if (fitTypeIndex == 2) { @@ -898,8 +898,8 @@ CompositeFunction_sptr ConvFit::createFunction(bool tieCentres, index = model->addFunction(product); prefix2 = createParName(index, subIndex); - populateFunction(func, model, m_properties["FitFunction2"], prefix2, - false); + populateFunction(func, model, m_properties["FitFunction2"], false, + prefix2); } } @@ -1033,39 +1033,6 @@ QtProperty *ConvFit::createFitType(QtProperty *fitTypeGroup, return fitTypeGroup; } -/** - * Populates the properties of a function with given values - * @param func The function currently being added to the composite - * @param comp A composite function of the previously called functions - * @param group The QtProperty representing the fit type - * @param pref The index of the functions eg. (f0.f1) - * @param tie Bool to state if parameters are to be tied together - */ -void ConvFit::populateFunction(IFunction_sptr func, IFunction_sptr comp, - QtProperty *group, const std::string &pref, - bool tie) { - // Get sub-properties of group and apply them as parameters on the function - // object - QList<QtProperty *> props = group->subProperties(); - - for (int i = 0; i < props.size(); i++) { - if (tie || !props[i]->subProperties().isEmpty()) { - std::string name = pref + props[i]->propertyName().toStdString(); - std::string value = props[i]->valueText().toStdString(); - comp->tie(name, value); - } else { - std::string propName = props[i]->propertyName().toStdString(); - double propValue = props[i]->valueText().toDouble(); - if (propValue != 0.0) { - if (func->hasAttribute(propName)) - func->setAttributeValue(propName, propValue); - else - func->setParameter(propName, propValue); - } - } - } -} - /** * Generate a string to describe the fit type selected by the user. * Used when naming the resultant workspaces. @@ -1237,8 +1204,8 @@ void ConvFit::updateProperties(int specNo) { } void ConvFit::updateProperties(int specNo, const QString &fitFunction) { - bool isTwoLorentzian = fitFunction == "Lorentzian 2"; - bool specOutOfBounds = + const bool isTwoLorentzian = fitFunction == "Lorentzian 2"; + const bool specOutOfBounds = specNo < minimumSpectrum() || maximumSpectrum() < specNo; for (auto ¶m : getFunctionParameters(fitFunction)) { @@ -1294,7 +1261,7 @@ void ConvFit::plotGuess() { // Do nothing if there is not a sample and resolution if (m_uiForm.dsResInput->isValid() && m_uiForm.ckPlotGuess->isChecked()) { extendResolutionWorkspace(); - bool tieCentres = (m_uiForm.cbFitType->currentIndex() == 2); + const bool tieCentres = (m_uiForm.cbFitType->currentIndex() == 2); IndirectDataAnalysisTab::plotGuess(m_uiForm.ppPlotTop, createFunction(tieCentres, true)); } else { diff --git a/qt/scientific_interfaces/Indirect/ConvFit.h b/qt/scientific_interfaces/Indirect/ConvFit.h index ec0880e97a052fb1be26c5b948aa63271b9e5f2a..5997d389e80ef443dcc9114fdac067b428d9cfd3 100644 --- a/qt/scientific_interfaces/Indirect/ConvFit.h +++ b/qt/scientific_interfaces/Indirect/ConvFit.h @@ -60,9 +60,6 @@ private: QtProperty *createFitType(QtProperty *, const bool & = true); void createTemperatureCorrection(Mantid::API::CompositeFunction_sptr product); - void populateFunction(Mantid::API::IFunction_sptr func, - Mantid::API::IFunction_sptr comp, QtProperty *group, - const std::string &pref, bool tie); QString fitTypeString() const; QString backgroundString() const; QString minimizerString(QString outputName) const; diff --git a/qt/scientific_interfaces/Indirect/IndirectDataAnalysisTab.cpp b/qt/scientific_interfaces/Indirect/IndirectDataAnalysisTab.cpp index fa7d733233e80703d9df0379cbd45245a5bba92b..5fb5de2db6395b103e62d1f3b0517e929a611aae 100644 --- a/qt/scientific_interfaces/Indirect/IndirectDataAnalysisTab.cpp +++ b/qt/scientific_interfaces/Indirect/IndirectDataAnalysisTab.cpp @@ -2,7 +2,9 @@ #include "MantidAPI/AnalysisDataService.h" #include "MantidAPI/FunctionDomain1D.h" +#include "MantidAPI/FunctionFactory.h" #include "MantidAPI/MatrixWorkspace.h" +#include "MantidAPI/WorkspaceFactory.h" #include "MantidAPI/WorkspaceGroup.h" #include "boost/shared_ptr.hpp" @@ -167,6 +169,9 @@ void IndirectDataAnalysisTab::plotCurrentPreview() { /** * Plots the selected spectrum of the input workspace in this indirect data * analysis tab. + * + * @param previewPlot The preview plot widget in which to plot the input + * input workspace. */ void IndirectDataAnalysisTab::plotInput( MantidQt::MantidWidgets::PreviewPlot *previewPlot) { @@ -204,6 +209,15 @@ void IndirectDataAnalysisTab::updatePlot( } } +/** + * Plots the data in the workspace with the specified name. Plots the + * sample and fit spectrum in the specified top preview plot. Plots + * the diff spectra in the specified difference preview plot. + * + * @param workspaceName The name of the workspace to plot. + * @param fitPreviewPlot The fit preview plot. + * @param diffPreviewPlot The difference preview plot. + */ void IndirectDataAnalysisTab::updatePlot( const std::string &workspaceName, MantidQt::MantidWidgets::PreviewPlot *fitPreviewPlot, @@ -228,6 +242,17 @@ void IndirectDataAnalysisTab::updatePlot( } } +/** + * Plots the workspace at the index specified by the selected + * spectrum, in the specified workspace group. Plots the sample + * and fit spectrum in the specified top preview plot. Plots + * the diff spectra in the specified difference preview plot. + * + * @param outputWS The group workspace containing the + * workspaced to plot. + * @param fitPreviewPlot The fit preview plot. + * @param diffPreviewPlot The difference preview plot. + */ void IndirectDataAnalysisTab::updatePlot( WorkspaceGroup_sptr outputWS, MantidQt::MantidWidgets::PreviewPlot *fitPreviewPlot, @@ -241,6 +266,15 @@ void IndirectDataAnalysisTab::updatePlot( } } +/** + * Plots the data in the specified workspace. Plots the sample + * and fit spectrum in the specified top preview plot. Plots + * the diff spectra in the specified difference preview plot. + * + * @param outputWS The workspace to plot. + * @param fitPreviewPlot The fit preview plot. + * @param diffPreviewPlot The difference preview plot. + */ void IndirectDataAnalysisTab::updatePlot( MatrixWorkspace_sptr outputWS, MantidQt::MantidWidgets::PreviewPlot *fitPreviewPlot, @@ -259,6 +293,18 @@ void IndirectDataAnalysisTab::updatePlot( } } +/* + * Updates the plot range with the specified name, to match the range of + * the sample curve. + * + * @param rangeName The name of the range to update. + * @param previewPlot The preview plot widget, in which the range + * is specified. + * @param startRangePropName The name of the property specifying the start + * value for the range. + * @parma endRangePropName The name of the property specifying the end + * value for the range. + */ void IndirectDataAnalysisTab::updatePlotRange( const QString &rangeName, MantidQt::MantidWidgets::PreviewPlot *previewPlot, const QString &startRangePropName, const QString &endRangePropName) { @@ -273,6 +319,14 @@ void IndirectDataAnalysisTab::updatePlotRange( } } +/* + * Plots a guess of the fit for the specified function, in the + * specified preview plot widget. + * + * @param previewPlot The preview plot widget in which to plot + * the guess. + * @param function The function to fit. + */ void IndirectDataAnalysisTab::plotGuess( MantidQt::MantidWidgets::PreviewPlot *previewPlot, IFunction_sptr function) { @@ -280,25 +334,40 @@ void IndirectDataAnalysisTab::plotGuess( if (inputWorkspace()) { auto guessWs = createGuessWorkspace(function); - previewPlot->addSpectrum("Guess", guessWs, 0, Qt::green); + + // Check whether the guess workspace has enough data points + // to plot + if (guessWs->x(0).size() >= 2) { + previewPlot->addSpectrum("Guess", guessWs, 0, Qt::green); + } } } +/* + * Creates a guess workspace, for approximating a fit with the specified + * function on the input workspace. + * + * @param func The function to fit. + * @return A guess workspace containing the guess data for the fit. + */ MatrixWorkspace_sptr IndirectDataAnalysisTab::createGuessWorkspace(IFunction_sptr func) { const auto inputWS = inputWorkspace(); const auto startX = m_dblManager->value(m_properties["StartX"]); const auto endX = m_dblManager->value(m_properties["EndX"]); - const size_t binIndexLow = inputWS->binIndexOf(startX); - const size_t binIndexHigh = inputWS->binIndexOf(endX); - const size_t nData = binIndexHigh - binIndexLow; + const auto binIndexLow = inputWS->binIndexOf(startX); + const auto binIndexHigh = inputWS->binIndexOf(endX); + const auto nData = binIndexHigh - binIndexLow; const auto &xPoints = inputWS->points(0); std::vector<double> dataX(nData); std::copy(&xPoints[binIndexLow], &xPoints[binIndexLow + nData], dataX.begin()); - std::vector<double> dataY = computeOutput(func, dataX); + const auto dataY = computeOutput(func, dataX); + + if (dataY.empty()) + return WorkspaceFactory::Instance().create("Workspace2D", 1, 1, 1); IAlgorithm_sptr createWsAlg = createWorkspaceAlgorithm("__GuessAnon", 1, dataX, dataY); @@ -306,9 +375,21 @@ IndirectDataAnalysisTab::createGuessWorkspace(IFunction_sptr func) { return createWsAlg->getProperty("OutputWorkspace"); } +/* + * Computes the output vector of applying the specified function to + * the specified input vector. + * + * @param func The function to apply. + * @param dataX Vector of input data. + * @return Vector containing values calculated from applying + * the specified function to the input data. + */ std::vector<double> IndirectDataAnalysisTab::computeOutput(IFunction_sptr func, const std::vector<double> &dataX) { + if (dataX.empty()) + return std::vector<double>(); + FunctionDomain1DVector domain(dataX); FunctionValues outputData(domain); func->function(domain, outputData); @@ -320,6 +401,17 @@ IndirectDataAnalysisTab::computeOutput(IFunction_sptr func, return dataY; } +/* + * Generates and returns an algorithm for creating a workspace, with + * the specified name, number of spectra and containing the specified + * x data and y data. + * + * @param workspaceName The name of the workspace to create. + * @param numSpec The number of spectra in the workspace to create. + * @param dataX The x data to add to the created workspace. + * @param dataY The y data to add to the created workspace. + * @return An algorithm for creating the workspace. + */ IAlgorithm_sptr IndirectDataAnalysisTab::createWorkspaceAlgorithm( const std::string &workspaceName, int numSpec, const std::vector<double> &dataX, const std::vector<double> &dataY) { @@ -335,6 +427,86 @@ IAlgorithm_sptr IndirectDataAnalysisTab::createWorkspaceAlgorithm( return createWsAlg; } +/** + * Create and populates a function with given values + * @param funcName The name of the function to create and populate populate + * @param group The QtProperty representing the fit type + * @param comp A composite function of the previously called functions to + * be used in tie + * @param tie Bool to state if parameters are to be tied together + * @param pref The index of the functions eg. (f0.f1) + */ +IFunction_sptr IndirectDataAnalysisTab::createPopulatedFunction( + const std::string &funcName, IFunction_sptr comp, QtProperty *group, + bool tie, const std::string &pref) { + IFunction_sptr func = FunctionFactory::Instance().createFunction(funcName); + populateFunction(func, comp, group, tie, pref); + return func; +} + +/** + * Create and populates a function with given values + * @param func The function to populate + * @param group The QtProperty representing the fit type + * @param tie Bool to state if parameters are to be tied together + * @param pref The index of the functions eg. (f0.f1) + */ +IFunction_sptr +IndirectDataAnalysisTab::createPopulatedFunction(const std::string &funcName, + QtProperty *group, bool tie, + const std::string &pref) { + IFunction_sptr func = FunctionFactory::Instance().createFunction(funcName); + populateFunction(func, group, tie, pref); + return func; +} + +/** + * Populates the properties of a function with given values + * @param func The function to populate + * @param group The QtProperty representing the fit type + * @param tie Bool to state if parameters are to be tied together + * @param pref The index of the functions eg. (f0.f1) + */ +void IndirectDataAnalysisTab::populateFunction(IFunction_sptr func, + QtProperty *group, bool tie, + const std::string &pref) { + populateFunction(func, func, group, tie, pref); +} + +/** + * Populates the properties of a function with given values + * @param func The function currently being added to the composite + * @param comp A composite function of the previously called functions + * @param group The QtProperty representing the fit type + * @param pref The index of the functions eg. (f0.f1) + * @param tie Bool to state if parameters are to be tied together + */ +void IndirectDataAnalysisTab::populateFunction(IFunction_sptr func, + IFunction_sptr comp, + QtProperty *group, bool tie, + const std::string &pref) { + // Get sub-properties of group and apply them as parameters on the function + // object + QList<QtProperty *> props = group->subProperties(); + + for (const auto &prop : props) { + if (tie || !prop->subProperties().isEmpty()) { + std::string name = pref + prop->propertyName().toStdString(); + std::string value = prop->valueText().toStdString(); + comp->tie(name, value); + } else { + std::string propName = prop->propertyName().toStdString(); + double propValue = prop->valueText().toDouble(); + if (propValue != 0.0) { + if (func->hasAttribute(propName)) + func->setAttributeValue(propName, propValue); + else + func->setParameter(propName, propValue); + } + } + } +} + } // namespace IDA } // namespace CustomInterfaces } // namespace MantidQt diff --git a/qt/scientific_interfaces/Indirect/IndirectDataAnalysisTab.h b/qt/scientific_interfaces/Indirect/IndirectDataAnalysisTab.h index a29dd0d2f9fc83b110a28cd76c6f9002e7921225..ccfa93c5b9dd6b33f759bfbb9e49d68a6b2c1771 100644 --- a/qt/scientific_interfaces/Indirect/IndirectDataAnalysisTab.h +++ b/qt/scientific_interfaces/Indirect/IndirectDataAnalysisTab.h @@ -118,6 +118,22 @@ protected: const std::vector<double> &dataX, const std::vector<double> &dataY); + Mantid::API::IFunction_sptr + createPopulatedFunction(const std::string &funcName, + Mantid::API::IFunction_sptr comp, QtProperty *group, + bool tie = false, const std::string &pref = ""); + + Mantid::API::IFunction_sptr + createPopulatedFunction(const std::string &funcName, QtProperty *group, + bool tie = false, const std::string &pref = ""); + + void populateFunction(Mantid::API::IFunction_sptr func, QtProperty *group, + bool tie = false, const std::string &pref = ""); + + void populateFunction(Mantid::API::IFunction_sptr func, + Mantid::API::IFunction_sptr comp, QtProperty *group, + bool tie = false, const std::string &pref = ""); + /// DoubleEditorFactory DoubleEditorFactory *m_dblEdFac; /// QtCheckBoxFactory diff --git a/qt/scientific_interfaces/Indirect/IqtFit.cpp b/qt/scientific_interfaces/Indirect/IqtFit.cpp index 0b5607670ead1534dc13da5f91bf7ae4102af387..0925d7b60de9672e9cc1485c3e18c42cf4472cb8 100644 --- a/qt/scientific_interfaces/Indirect/IqtFit.cpp +++ b/qt/scientific_interfaces/Indirect/IqtFit.cpp @@ -426,20 +426,21 @@ CompositeFunction_sptr IqtFit::createFunction(bool tie) { } if (fitType == 2) { - fname = "StretchedExp"; + result->addFunction(createPopulatedFunction( + "StretchExp", m_properties["StretchedExp"], tie)); } else { - fname = "Exponential1"; + result->addFunction( + createPopulatedFunction("ExpDecay", m_properties["Exponential1"], tie)); } - result->addFunction(createExponentialFunction(fname, tie)); - if (fitType == 1 || fitType == 3) { if (fitType == 1) { - fname = "Exponential2"; + result->addFunction(createPopulatedFunction( + "ExpDecay", m_properties["Exponential2"], tie)); } else { - fname = "StretchedExp"; + result->addFunction(createPopulatedFunction( + "StretchExp", m_properties["StretchedExp"], tie)); } - result->addFunction(createExponentialFunction(fname, tie)); } // Return CompositeFunction object to caller. @@ -447,51 +448,11 @@ CompositeFunction_sptr IqtFit::createFunction(bool tie) { return result; } -IFunction_sptr IqtFit::createExponentialFunction(const QString &name, - bool tie) { - IFunction_sptr result; - if (name.startsWith("Exp")) { - IFunction_sptr result = - FunctionFactory::Instance().createFunction("ExpDecay"); - result->setParameter( - "Height", m_dblManager->value(m_properties[name + ".Intensity"])); - result->setParameter("Lifetime", - m_dblManager->value(m_properties[name + ".Tau"])); - if (tie) { - result->tie("Height", - m_properties[name + ".Intensity"]->valueText().toStdString()); - result->tie("Lifetime", - m_properties[name + ".Tau"]->valueText().toStdString()); - } - result->applyTies(); - return result; - } else { - IFunction_sptr result = - FunctionFactory::Instance().createFunction("StretchExp"); - result->setParameter( - "Height", m_dblManager->value(m_properties[name + ".Intensity"])); - result->setParameter("Lifetime", - m_dblManager->value(m_properties[name + ".Tau"])); - result->setParameter("Stretching", - m_dblManager->value(m_properties[name + ".Beta"])); - if (tie) { - result->tie("Height", - m_properties[name + ".Intensity"]->valueText().toStdString()); - result->tie("Lifetime", - m_properties[name + ".Tau"]->valueText().toStdString()); - result->tie("Stretching", - m_properties[name + ".Beta"]->valueText().toStdString()); - } - result->applyTies(); - return result; - } -} - QtProperty *IqtFit::createExponential(const QString &name) { QtProperty *expGroup = m_grpManager->addProperty(name); - m_properties[name + ".Intensity"] = m_dblManager->addProperty("Intensity"); + m_properties[name + ".Intensity"] = m_dblManager->addProperty("Height"); m_dblManager->setDecimals(m_properties[name + ".Intensity"], NUM_DECIMALS); - m_properties[name + ".Tau"] = m_dblManager->addProperty("Tau"); + m_properties[name + ".Tau"] = m_dblManager->addProperty("Lifetime"); m_dblManager->setDecimals(m_properties[name + ".Tau"], NUM_DECIMALS); expGroup->addSubProperty(m_properties[name + ".Intensity"]); expGroup->addSubProperty(m_properties[name + ".Tau"]); @@ -500,9 +461,9 @@ QtProperty *IqtFit::createExponential(const QString &name) { QtProperty *IqtFit::createStretchedExp(const QString &name) { QtProperty *prop = m_grpManager->addProperty(name); - m_properties[name + ".Intensity"] = m_dblManager->addProperty("Intensity"); - m_properties[name + ".Tau"] = m_dblManager->addProperty("Tau"); - m_properties[name + ".Beta"] = m_dblManager->addProperty("Beta"); + m_properties[name + ".Intensity"] = m_dblManager->addProperty("Height"); + m_properties[name + ".Tau"] = m_dblManager->addProperty("Lifetime"); + m_properties[name + ".Beta"] = m_dblManager->addProperty("Stretching"); m_dblManager->setRange(m_properties[name + ".Beta"], 0, 1); m_dblManager->setDecimals(m_properties[name + ".Intensity"], NUM_DECIMALS); m_dblManager->setDecimals(m_properties[name + ".Tau"], NUM_DECIMALS); diff --git a/qt/scientific_interfaces/Indirect/IqtFit.h b/qt/scientific_interfaces/Indirect/IqtFit.h index e1eb02576977c002ff9da68be9ef28efb53e63bc..86275cde486f8478458c582ce2527d84f795f754 100644 --- a/qt/scientific_interfaces/Indirect/IqtFit.h +++ b/qt/scientific_interfaces/Indirect/IqtFit.h @@ -59,8 +59,6 @@ private slots: private: boost::shared_ptr<Mantid::API::CompositeFunction> createFunction(bool tie = false); - boost::shared_ptr<Mantid::API::IFunction> - createExponentialFunction(const QString &name, bool tie = false); QtProperty *createExponential(const QString &); QtProperty *createStretchedExp(const QString &); void setDefaultParameters(const QString &name); diff --git a/qt/scientific_interfaces/Indirect/MSDFit.cpp b/qt/scientific_interfaces/Indirect/MSDFit.cpp index 3a8abd797cb8ab2eb57b8c204795ab10f1e34e0e..1289fd3a2d748a23b1a56d606750feabfa399fce 100644 --- a/qt/scientific_interfaces/Indirect/MSDFit.cpp +++ b/qt/scientific_interfaces/Indirect/MSDFit.cpp @@ -1,6 +1,7 @@ #include "MSDFit.h" #include "../General/UserInputValidator.h" #include "MantidAPI/AnalysisDataService.h" +#include "MantidAPI/FunctionFactory.h" #include "MantidAPI/WorkspaceGroup.h" #include "MantidQtWidgets/LegacyQwt/RangeSelector.h" @@ -36,11 +37,14 @@ void MSDFit::setup() { m_properties["EndX"] = m_dblManager->addProperty("EndX"); m_dblManager->setDecimals(m_properties["EndX"], NUM_DECIMALS); - m_properties["Gaussian"] = createModel("Gaussian", {"Intensity", "MSD"}); - m_properties["Peters"] = createModel("Peters", {"Intensity", "MSD", "Beta"}); - m_properties["Yi"] = createModel("Yi", {"Intensity", "MSD", "Sigma"}); + m_properties["Gaussian"] = createModel("MsdGauss", {"Height", "MSD"}); + m_properties["Peters"] = createModel("MsdPeters", {"Height", "MSD", "Beta"}); + m_properties["Yi"] = createModel("MsdYi", {"Height", "MSD", "Sigma"}); auto fitRangeSelector = m_uiForm.ppPlotTop->addRangeSelector("MSDRange"); + m_dblManager->setValue(m_properties["StartX"], + fitRangeSelector->getMinimum()); + m_dblManager->setValue(m_properties["EndX"], fitRangeSelector->getMaximum()); modelSelection(m_uiForm.cbModelInput->currentIndex()); @@ -64,6 +68,13 @@ void MSDFit::setup() { connect(m_uiForm.spPlotSpectrum, SIGNAL(valueChanged(int)), this, SLOT(updatePlot())); + connect(m_dblManager, SIGNAL(propertyChanged(QtProperty *)), this, + SLOT(plotGuess())); + connect(m_uiForm.ckPlotGuess, SIGNAL(stateChanged(int)), this, + SLOT(plotGuess())); + connect(m_uiForm.cbModelInput, SIGNAL(currentIndexChanged(int)), this, + SLOT(plotGuess())); + connect(m_uiForm.spSpectraMin, SIGNAL(valueChanged(int)), this, SLOT(specMinChanged(int))); connect(m_uiForm.spSpectraMax, SIGNAL(valueChanged(int)), this, @@ -90,7 +101,8 @@ void MSDFit::run() { dataName.left(dataName.lastIndexOf("_")).toStdString() + "_s" + std::to_string(specMin) + "_to_s" + std::to_string(specMax) + "_" + model.toStdString() + "_msd"; - m_parameterToProperty = createParameterToPropertyMap(model); + m_parameterToProperty = + createParameterToPropertyMap(m_properties[model]->propertyName()); IAlgorithm_sptr msdAlg = msdFitAlgorithm(modelToAlgorithmProperty(model), specMin, specMax); @@ -113,7 +125,8 @@ void MSDFit::singleFit() { m_pythonExportWsName = dataName.left(dataName.lastIndexOf("_")).toStdString() + "_s" + std::to_string(fitSpec) + "_" + model.toStdString() + "_msd"; - m_parameterToProperty = createParameterToPropertyMap(model); + m_parameterToProperty = + createParameterToPropertyMap(m_properties[model]->propertyName()); IAlgorithm_sptr msdAlg = msdFitAlgorithm(modelToAlgorithmProperty(model), fitSpec, fitSpec); @@ -212,6 +225,23 @@ void MSDFit::updatePlot() { IndirectDataAnalysisTab::updatePlotRange("MSDRange", m_uiForm.ppPlotTop); } +void MSDFit::plotGuess() { + + if (m_uiForm.ckPlotGuess->isChecked()) { + QString modelName = m_uiForm.cbModelInput->currentText(); + IndirectDataAnalysisTab::plotGuess(m_uiForm.ppPlotTop, + createFunction(modelName)); + } else { + m_uiForm.ppPlotTop->removeSpectrum("Guess"); + } +} + +IFunction_sptr MSDFit::createFunction(const QString &modelName) { + return createPopulatedFunction( + m_properties[modelName]->propertyName().toStdString(), + m_properties[modelName]); +} + /** * Called when new data has been loaded by the data selector. * @@ -313,12 +343,23 @@ QtProperty *MSDFit::createModel(const QString &modelName, } void MSDFit::modelSelection(int selected) { - QString model = m_uiForm.cbModelInput->itemText(selected); + auto model = m_uiForm.cbModelInput->itemText(selected); m_msdTree->clear(); m_msdTree->addProperty(m_properties["StartX"]); m_msdTree->addProperty(m_properties["EndX"]); m_msdTree->addProperty(m_properties[model]); + + if (!m_pythonExportWsName.empty()) { + auto idx = m_pythonExportWsName.rfind("_"); + m_pythonExportWsName = m_pythonExportWsName.substr(0, idx); + idx = m_pythonExportWsName.rfind("_"); + m_pythonExportWsName = m_pythonExportWsName.substr(0, idx); + m_pythonExportWsName += "_" + model.toStdString() + "_msd"; + } + + m_uiForm.ckPlotGuess->setChecked(false); + updatePlot(); } /* @@ -333,7 +374,7 @@ void MSDFit::modelSelection(int selected) { QHash<QString, QString> MSDFit::createParameterToPropertyMap(const QString &model) { QHash<QString, QString> parameterToProperty; - parameterToProperty["Height"] = model + ".Intensity"; + parameterToProperty["Height"] = model + ".Height"; parameterToProperty["MSD"] = model + ".MSD"; if (model == "Peters") diff --git a/qt/scientific_interfaces/Indirect/MSDFit.h b/qt/scientific_interfaces/Indirect/MSDFit.h index e9d58afaa3e8e1ba13b211d5180a2dbdadeb2390..d329bec176d3c27167bb71c85efcd37c5b6fc6e8 100644 --- a/qt/scientific_interfaces/Indirect/MSDFit.h +++ b/qt/scientific_interfaces/Indirect/MSDFit.h @@ -4,6 +4,8 @@ #include "IndirectDataAnalysisTab.h" #include "ui_MSDFit.h" +#include "MantidAPI/IFunction.h" + namespace MantidQt { namespace CustomInterfaces { namespace IDA { @@ -33,6 +35,7 @@ private slots: void modelSelection(int selected); void updatePlot(); void updateProperties(int specNo); + void plotGuess(); private: Mantid::API::IAlgorithm_sptr msdFitAlgorithm(const std::string &model, @@ -41,6 +44,7 @@ private: const std::vector<QString> &modelParameters); QHash<QString, QString> createParameterToPropertyMap(const QString &model); std::string modelToAlgorithmProperty(const QString &model); + Mantid::API::IFunction_sptr createFunction(const QString &modelName); Ui::MSDFit m_uiForm; QtTreePropertyBrowser *m_msdTree; diff --git a/qt/scientific_interfaces/Indirect/MSDFit.ui b/qt/scientific_interfaces/Indirect/MSDFit.ui index 20d0a5a06ddc59f4c30fe2d486b2fc4dd424f07c..dfc78073f9d7549b9e2633ad2912a9cfede37194 100644 --- a/qt/scientific_interfaces/Indirect/MSDFit.ui +++ b/qt/scientific_interfaces/Indirect/MSDFit.ui @@ -56,9 +56,6 @@ </item> <item> <layout class="QHBoxLayout" name="loMSDFit"> - <item> - <layout class="QVBoxLayout" name="properties"/> - </item> <item> <layout class="QVBoxLayout" name="loMSDPlot"> <item> diff --git a/qt/scientific_interfaces/test/EnggDiffFittingModelTest.h b/qt/scientific_interfaces/test/EnggDiffFittingModelTest.h new file mode 100644 index 0000000000000000000000000000000000000000..c9c4837854e491533172322acffa59099f8c24b4 --- /dev/null +++ b/qt/scientific_interfaces/test/EnggDiffFittingModelTest.h @@ -0,0 +1,86 @@ +#ifndef MANTID_CUSTOMINTERFACES_ENGGDIFFFITTINGMODELTEST_H_ +#define MANTID_CUSTOMINTERFACES_ENGGDIFFFITTINGMODELTEST_H_ + +#include <cxxtest/TestSuite.h> + +#include "MantidAPI/MatrixWorkspace.h" +#include "MantidAPI/WorkspaceFactory.h" + +#include "../EnggDiffraction/EnggDiffFittingModel.h" + +#include <vector> + +// Lets us have pairs inside assertion macros +typedef std::pair<int, size_t> RunBankPair; + +using namespace Mantid; +using namespace MantidQT::CustomInterfaces; + +namespace { + +void addSampleWorkspaceToModel(const int runNumber, const int bank, + EnggDiffFittingModel &model) { + API::MatrixWorkspace_sptr ws = + API::WorkspaceFactory::Instance().create("Workspace2D", 1, 10, 10); + model.addWorkspace(runNumber, bank, ws); +} + +} // anonymous namespace + +class EnggDiffFittingModelTest : public CxxTest::TestSuite { +public: + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static EnggDiffFittingModelTest *createSuite() { + return new EnggDiffFittingModelTest(); + } + static void destroySuite(EnggDiffFittingModelTest *suite) { delete suite; } + + void test_addAndGetWorkspace() { + auto model = EnggDiffFittingModel(); + API::MatrixWorkspace_sptr ws = + API::WorkspaceFactory::Instance().create("Workspace2D", 1, 10, 10); + const int runNumber = 100; + const int bank = 1; + TS_ASSERT_THROWS_NOTHING(model.addWorkspace(runNumber, bank, ws)); + const auto retrievedWS = model.getWorkspace(runNumber, bank); + + TS_ASSERT(retrievedWS != nullptr); + TS_ASSERT_EQUALS(ws, retrievedWS); + } + + void test_getAllRunNumbers() { + auto model = EnggDiffFittingModel(); + + addSampleWorkspaceToModel(123, 1, model); + addSampleWorkspaceToModel(456, 2, model); + addSampleWorkspaceToModel(789, 1, model); + addSampleWorkspaceToModel(123, 2, model); + + const auto runNumbers = model.getAllRunNumbers(); + + TS_ASSERT_EQUALS(runNumbers.size(), 3); + TS_ASSERT_EQUALS(runNumbers[0], 123); + TS_ASSERT_EQUALS(runNumbers[1], 456); + TS_ASSERT_EQUALS(runNumbers[2], 789); + } + + void test_getRunNumbersAndBankIDs() { + auto model = EnggDiffFittingModel(); + + addSampleWorkspaceToModel(123, 1, model); + addSampleWorkspaceToModel(456, 2, model); + addSampleWorkspaceToModel(789, 1, model); + addSampleWorkspaceToModel(123, 2, model); + + const auto runNoBankPairs = model.getRunNumbersAndBanksIDs(); + + TS_ASSERT_EQUALS(runNoBankPairs.size(), 4); + TS_ASSERT_EQUALS(runNoBankPairs[0], RunBankPair(123, 1)); + TS_ASSERT_EQUALS(runNoBankPairs[1], RunBankPair(123, 2)); + TS_ASSERT_EQUALS(runNoBankPairs[2], RunBankPair(456, 2)); + TS_ASSERT_EQUALS(runNoBankPairs[3], RunBankPair(789, 1)); + } +}; + +#endif \ No newline at end of file diff --git a/qt/scientific_interfaces/test/EnggDiffFittingViewMock.h b/qt/scientific_interfaces/test/EnggDiffFittingViewMock.h index c244b4d9bfa2af4b9a52918c54cebe1e0aa398db..5356e30ee18b5aa36be7ecd517adf151fc3ddbc5 100644 --- a/qt/scientific_interfaces/test/EnggDiffFittingViewMock.h +++ b/qt/scientific_interfaces/test/EnggDiffFittingViewMock.h @@ -99,6 +99,9 @@ public: // sets the current row of the fitting list widget MOCK_CONST_METHOD1(setFittingListWidgetCurrentRow, void(int idx)); + // gets current value of the fitting list widget + MOCK_CONST_METHOD0(getFittingListWidgetCurrentValue, std::string()); + // sets the peak list according to the QString given MOCK_CONST_METHOD1(setPeakList, void(const std::string &peakList)); diff --git a/qt/widgets/common/inc/MantidQtWidgets/Common/HintingLineEdit.h b/qt/widgets/common/inc/MantidQtWidgets/Common/HintingLineEdit.h index 9f36d636a57b76d7375a3c5994780e198db33dc5..41904b5de4d32db7f000e781c05272c20d17ef06 100644 --- a/qt/widgets/common/inc/MantidQtWidgets/Common/HintingLineEdit.h +++ b/qt/widgets/common/inc/MantidQtWidgets/Common/HintingLineEdit.h @@ -64,6 +64,9 @@ protected: protected slots: void updateHints(const QString &text); void hideHints(); + +private: + static QPalette createFixedPalette(); }; } // namespace MantidWidgets } // namepsace MantidQt diff --git a/qt/widgets/common/src/HintingLineEdit.cpp b/qt/widgets/common/src/HintingLineEdit.cpp index a085a2f56797f97cc7b7473e29ccfc900dc01797..ada5ffda61f3bb72bac710f01581a3e5a3a36198 100644 --- a/qt/widgets/common/src/HintingLineEdit.cpp +++ b/qt/widgets/common/src/HintingLineEdit.cpp @@ -5,6 +5,7 @@ #include <QLabel> #include <QStyle> #include <QToolTip> +#include <boost/algorithm/string.hpp> namespace MantidQt { namespace MantidWidgets { @@ -20,7 +21,7 @@ HintingLineEdit::HintingLineEdit( m_hintLabel->setWordWrap(true); m_hintLabel->setIndent(1); m_hintLabel->setAutoFillBackground(true); - m_hintLabel->setPalette(QToolTip::palette()); + m_hintLabel->setPalette(createFixedPalette()); m_hintLabel->setForegroundRole(QPalette::ToolTipText); m_hintLabel->setBackgroundRole(QPalette::ToolTipBase); m_hintLabel->ensurePolished(); @@ -30,6 +31,29 @@ HintingLineEdit::HintingLineEdit( connect(this, SIGNAL(editingFinished()), this, SLOT(hideHints())); } +/** Fixes the colour pallete so that the hints are readable. + + Tooltips use the inactive text colours since they are never 'in focus'. + This causes problems on some linux distributions (specifically Fedora 26 + and Ubuntu 16.04) where the inactive text colours are particularly light + and lack contrast with the colour used for the background. + + This method creates a modified tooltip pallete which uses the active + window text colour instead. + + @returns A tooltip pallete with contrasting tooltip text and background + colours. +*/ +QPalette HintingLineEdit::createFixedPalette() { + auto toolTipPalette = QToolTip::palette(); + + auto const activeTextColour = + toolTipPalette.color(QPalette::ColorGroup::Active, QPalette::WindowText); + toolTipPalette.setColor(QPalette::ColorGroup::Inactive, QPalette::ToolTipText, + activeTextColour); + return toolTipPalette; +} + HintingLineEdit::~HintingLineEdit() {} /** Handle a key press event. diff --git a/scripts/Diffraction/isis_powder/pearl.py b/scripts/Diffraction/isis_powder/pearl.py index b677f99b10339e40d5ac5ffc0ae05ddfb1047584..e1ac04b9647d9b370561b358d1f22d7f4d49977c 100644 --- a/scripts/Diffraction/isis_powder/pearl.py +++ b/scripts/Diffraction/isis_powder/pearl.py @@ -4,7 +4,8 @@ import mantid.simpleapi as mantid from isis_powder.routines import common, instrument_settings from isis_powder.abstract_inst import AbstractInst -from isis_powder.pearl_routines import pearl_algs, pearl_output, pearl_advanced_config, pearl_param_mapping +from isis_powder.pearl_routines import pearl_advanced_config, pearl_algs, pearl_calibration_algs, pearl_output, \ + pearl_param_mapping class Pearl(AbstractInst): @@ -41,6 +42,31 @@ class Pearl(AbstractInst): else: self._run_create_vanadium() + def create_cal(self, **kwargs): + self._switch_long_mode_inst_settings(kwargs.get("long_mode")) + self._inst_settings.update_attributes(kwargs=kwargs) + run_details = self._get_run_details(self._inst_settings.run_number) + + cross_correlate_params = {"ReferenceSpectra": self._inst_settings.reference_spectra, + "WorkspaceIndexMin": self._inst_settings.cross_corr_ws_min, + "WorkspaceIndexMax": self._inst_settings.cross_corr_ws_max, + "XMin": self._inst_settings.cross_corr_x_min, + "XMax": self._inst_settings.cross_corr_x_max} + get_detector_offsets_params = {"DReference": self._inst_settings.d_reference, + "Step": self._inst_settings.get_det_offsets_step, + "XMin": self._inst_settings.get_det_offsets_x_min, + "XMax": self._inst_settings.get_det_offsets_x_max} + + return pearl_calibration_algs.create_calibration(calibration_runs=self._inst_settings.run_number, + instrument=self, + offset_file_name=run_details.offset_file_path, + grouping_file_name=run_details.grouping_file_path, + calibration_dir=self._inst_settings.calibration_dir, + rebin_1_params=self._inst_settings.cal_rebin_1, + rebin_2_params=self._inst_settings.cal_rebin_2, + cross_correlate_params=cross_correlate_params, + get_det_offset_params=get_detector_offsets_params) + def _run_create_vanadium(self): # Provides a minimal wrapper so if we have tt_mode 'all' we can loop round return self._create_vanadium(run_number_string=self._inst_settings.run_in_range, diff --git a/scripts/Diffraction/isis_powder/pearl_routines/pearl_advanced_config.py b/scripts/Diffraction/isis_powder/pearl_routines/pearl_advanced_config.py index 39341287ba9d8fd04f2dcac2c23c6f58a3bc1b4d..7d7cbe90991a867ebb04b42b1c6672f4f847b5a0 100644 --- a/scripts/Diffraction/isis_powder/pearl_routines/pearl_advanced_config.py +++ b/scripts/Diffraction/isis_powder/pearl_routines/pearl_advanced_config.py @@ -9,14 +9,15 @@ general_params = { "spline_coefficient": 60, "file_names": { - "vanadium_absorb_filename": "pearl_absorp_sphere_10mm_newinst2_long.nxs", - "tt88_grouping_filename": "pearl_group_12_1_TT88.cal", - "tt70_grouping_filename": "pearl_group_12_1_TT70.cal", - "tt35_grouping_filename": "pearl_group_12_1_TT35.cal" + "vanadium_absorb_filename": "pearl_absorp_sphere_10mm_newinst2_long.nxs", + "tt88_grouping_filename": "pearl_group_12_1_TT88.cal", + "tt70_grouping_filename": "pearl_group_12_1_TT70.cal", + "tt35_grouping_filename": "pearl_group_12_1_TT35.cal" }, } long_mode_off_params = { + "create_cal_rebin_1_params": "100,-0.0006,19950", "monitor_lambda_crop_range": (0.03, 6.00), "monitor_integration_range": (0.6, 5.0), # This needs to be greater than the bank TOF cropping values or you will get data that divides to 0/inf @@ -41,6 +42,7 @@ long_mode_off_params = { } long_mode_on_params = { + "create_cal_rebin_1_params": "20300,-0.0006,39990", "monitor_lambda_crop_range": (5.9, 12.0), "monitor_integration_range": (6, 10), # raw_data_tof_cropping needs to be have smaller/larger values than the bank TOF cropping values or @@ -65,6 +67,22 @@ long_mode_on_params = { ] } +calibration_params = { + "create_cal_rebin_2_params": "1.8,0.002,2.1", + "create_cal_cross_correlate_params": { + "cross_corr_reference_spectra": 20, + "cross_corr_ws_index_min": 9, + "cross_corr_ws_index_max": 1063, + "cross_corr_x_min": 1.8, + "cross_corr_x_max": 2.1 + }, + "create_cal_get_detector_offsets_params": { + "get_det_offsets_step": 0.002, + "get_det_offsets_x_min": -200, + "get_det_offsets_x_max": 200, + "get_det_offsets_d_ref": 1.912795 + } +} variable_help = { "long_mode_<on/off>_params": { @@ -100,6 +118,22 @@ variable_help = { "workspace. This is used to normalise the workspace current.", "spline_coefficient": "The coefficient to use whilst calculating a spline for each bank during " "a vanadium calibration." + }, + + "calibration_params": { + "create_cal_rebin_1_params": "The parameters for the first rebin step used to create a calibration file", + "create_cal_rebin_2_params": "The parameters for the second rebin step used to create a calibration file", + "cross_corr_reference_spectra": "The Workspace Index of the spectra to correlate all other spectra against", + "cross_corr_ws_index_min": "The workspace index of the first member of the range of spectra to cross-correlate " + "against", + "cross_corr_ws_index_max": "The workspace index of the last member of the range of spectra to cross-correlate " + "against", + "cross_corr_x_min": "The starting point of the region to be cross correlated", + "cross_corr_x_max": "The ending point of the region to be cross correlated", + "get_det_offsets_step": "Step size used to bin d-spacing data in GetDetectorOffsets", + "get_det_offsets_x_min": "Minimum of CrossCorrelation data to search for peak, usually negative", + "get_det_offsets_x_max": "Maximum of CrossCorrelation data to search for peak, usually positive", + "get_det_offsets_d_ref": "Center of reference peak in d-space" } } @@ -107,6 +141,7 @@ variable_help = { def get_all_adv_variables(is_long_mode_on=False): long_mode_params = long_mode_on_params if is_long_mode_on else long_mode_off_params advanced_config_dict = {} + advanced_config_dict.update(calibration_params) advanced_config_dict.update(general_params) advanced_config_dict.update(long_mode_params) return advanced_config_dict diff --git a/scripts/Diffraction/isis_powder/pearl_routines/pearl_calibration_algs.py b/scripts/Diffraction/isis_powder/pearl_routines/pearl_calibration_algs.py index 39d0f90d2734f79dee6a1e60bf71413c9ea220f1..d85053d168add6538d7032ec4f7385be882076ec 100644 --- a/scripts/Diffraction/isis_powder/pearl_routines/pearl_calibration_algs.py +++ b/scripts/Diffraction/isis_powder/pearl_routines/pearl_calibration_algs.py @@ -3,92 +3,49 @@ from __future__ import (absolute_import, division, print_function) import os import mantid.simpleapi as mantid import isis_powder.routines.common as common -from isis_powder.routines.common_enums import INPUT_BATCHING - -# TODO this entire file needs cleaning and refactoring - - -def create_calibration(self, calibration_runs, offset_file_name, grouping_file_name): - input_ws_list = common.load_current_normalised_ws_list(run_number_string=calibration_runs, instrument=self, +from isis_powder.routines.common_enums import INPUT_BATCHING, WORKSPACE_UNITS + + +def create_calibration(calibration_runs, instrument, offset_file_name, grouping_file_name, calibration_dir, + rebin_1_params, rebin_2_params, cross_correlate_params, get_det_offset_params): + """ + Create a calibration file from (usually) a ceria run + :param calibration_runs: Run number(s) for this run + :param instrument: The PEARL instrument object + :param offset_file_name: Name of the file to write detector offset information to + :param grouping_file_name: Name of grouping calibration file + :param calibration_dir: Path to directory containing calibration information + :param rebin_1_params: Parameters for the first rebin step (as a string in the usual format) + :param rebin_2_params: Parameters for the second rebin step (as a string in the usual format) + :param cross_correlate_params: Parameters for CrossCorrelate (as a dictionary PropertyName: PropertyValue) + :param get_det_offset_params: Parameters for GetDetectorOffsets (as a dictionary PropertyName: PropertyValue) + """ + input_ws_list = common.load_current_normalised_ws_list(run_number_string=calibration_runs, instrument=instrument, input_batching=INPUT_BATCHING.Summed) - input_ws = input_ws_list[0] - run_details = self._get_run_details(calibration_runs) - - if run_details.instrument_version == "new" or run_details.instrument_version == "new2": - input_ws = mantid.Rebin(InputWorkspace=input_ws, Params="100,-0.0006,19950") - - d_spacing_cal = mantid.ConvertUnits(InputWorkspace=input_ws, Target="dSpacing") - d_spacing_cal = mantid.Rebin(InputWorkspace=d_spacing_cal, Params="1.8,0.002,2.1") - - if run_details.instrument_version == "new2": - cross_cor_ws = mantid.CrossCorrelate(InputWorkspace=d_spacing_cal, ReferenceSpectra=20, - WorkspaceIndexMin=9, WorkspaceIndexMax=1063, XMin=1.8, XMax=2.1) - - elif run_details.instrument_version == "new": - cross_cor_ws = mantid.CrossCorrelate(InputWorkspace=d_spacing_cal, ReferenceSpectra=20, - WorkspaceIndexMin=9, WorkspaceIndexMax=943, XMin=1.8, XMax=2.1) - else: - cross_cor_ws = mantid.CrossCorrelate(InputWorkspace=d_spacing_cal, ReferenceSpectra=500, - WorkspaceIndexMin=1, WorkspaceIndexMax=1440, XMin=1.8, XMax=2.1) - if self._old_api_uses_full_paths: # Workaround for old API setting full paths - grouping_file_path = grouping_file_name - offset_file_path = offset_file_name - else: - offset_file_path = os.path.join(self.calibration_dir, offset_file_name) - grouping_file_path = os.path.join(self.calibration_dir, grouping_file_name) - - # Ceo Cell refined to 5.4102(3) so 220 is 1.912795 - offset_output_path = mantid.GetDetectorOffsets(InputWorkspace=cross_cor_ws, Step=0.002, DReference=1.912795, - XMin=-200, XMax=200, GroupingFileName=offset_file_path) - del offset_output_path # This isn't used so delete it to keep linters happy - aligned_ws = mantid.AlignDetectors(InputWorkspace=input_ws, CalibrationFile=offset_file_path) - cal_grouped_ws = mantid.DiffractionFocussing(InputWorkspace=aligned_ws, GroupingFileName=grouping_file_path) - - common.remove_intermediate_workspace(d_spacing_cal) - common.remove_intermediate_workspace(cross_cor_ws) - common.remove_intermediate_workspace(aligned_ws) - common.remove_intermediate_workspace(cal_grouped_ws) + input_ws = input_ws_list[0] + calibration_ws = mantid.Rebin(InputWorkspace=input_ws, Params=rebin_1_params) -def do_silicon_calibration(self, runs_to_process, cal_file_name, grouping_file_name): - # TODO fix all of this as the script is too limited to be useful - create_si_ws = common.load_current_normalised_ws_list(run_number_string=runs_to_process, instrument=self) - cycle_details = self._get_label_information(runs_to_process) - instrument_version = cycle_details["instrument_version"] + if calibration_ws.getAxis(0).getUnit().unitID() != WORKSPACE_UNITS.d_spacing: + calibration_ws = mantid.ConvertUnits(InputWorkspace=calibration_ws, Target="dSpacing") - if instrument_version == "new" or instrument_version == "new2": - create_si_ws = mantid.Rebin(InputWorkspace=create_si_ws, Params="100,-0.0006,19950") + rebinned = mantid.Rebin(InputWorkspace=calibration_ws, Params=rebin_2_params) + cross_correlated = mantid.CrossCorrelate(InputWorkspace=rebinned, **cross_correlate_params) - create_si_d_spacing_ws = mantid.ConvertUnits(InputWorkspace=create_si_ws, Target="dSpacing") + offset_file = os.path.join(calibration_dir, offset_file_name) + # Offsets workspace must be referenced as string so it can be deleted, as simpleapi doesn't recognise it as a ws + offsets_ws_name = "offsets" + mantid.GetDetectorOffsets(InputWorkspace=cross_correlated, GroupingFileName=offset_file, + OutputWorkspace=offsets_ws_name, **get_det_offset_params) - if instrument_version == "new2": - create_si_d_spacing_rebin_ws = mantid.Rebin(InputWorkspace=create_si_d_spacing_ws, Params="1.71,0.002,2.1") - create_si_cross_corr_ws = mantid.CrossCorrelate(InputWorkspace=create_si_d_spacing_rebin_ws, - ReferenceSpectra=20, WorkspaceIndexMin=9, - WorkspaceIndexMax=1063, XMin=1.71, XMax=2.1) - elif instrument_version == "new": - create_si_d_spacing_rebin_ws = mantid.Rebin(InputWorkspace=create_si_d_spacing_ws, Params="1.85,0.002,2.05") - create_si_cross_corr_ws = mantid.CrossCorrelate(InputWorkspace=create_si_d_spacing_rebin_ws, - ReferenceSpectra=20, WorkspaceIndexMin=9, - WorkspaceIndexMax=943, XMin=1.85, XMax=2.05) - elif instrument_version == "old": - create_si_d_spacing_rebin_ws = mantid.Rebin(InputWorkspace=create_si_d_spacing_ws, Params="3,0.002,3.2") - create_si_cross_corr_ws = mantid.CrossCorrelate(InputWorkspace=create_si_d_spacing_rebin_ws, - ReferenceSpectra=500, WorkspaceIndexMin=1, - WorkspaceIndexMax=1440, XMin=3, XMax=3.2) - else: - raise NotImplementedError("The instrument version is not supported for creating a silicon calibration") + rebinned_tof = mantid.ConvertUnits(InputWorkspace=rebinned, Target="TOF") + aligned = mantid.AlignDetectors(InputWorkspace=rebinned_tof, CalibrationFile=offset_file) - common.remove_intermediate_workspace(create_si_d_spacing_ws) - common.remove_intermediate_workspace(create_si_d_spacing_rebin_ws) + grouping_file = os.path.join(calibration_dir, grouping_file_name) + focused = mantid.DiffractionFocussing(InputWorkspace=aligned, GroupingFileName=grouping_file, + OutputWorkspace=instrument._generate_output_file_name(calibration_runs) + + "_grouped") - calibration_output_path = self.calibration_dir + cal_file_name - create_si_offsets_ws = mantid.GetDetectorOffsets(InputWorkspace=create_si_cross_corr_ws, - Step=0.002, DReference=1.920127251, XMin=-200, XMax=200, - GroupingFileName=calibration_output_path) - create_si_aligned_ws = mantid.AlignDetectors(InputWorkspace=create_si_ws, - CalibrationFile=calibration_output_path) - grouping_output_path = self.calibration_dir + grouping_file_name - create_si_grouped_ws = mantid.DiffractionFocussing(InputWorkspace=create_si_aligned_ws, - GroupingFileName=grouping_output_path) - del create_si_offsets_ws, create_si_grouped_ws + common.remove_intermediate_workspace([calibration_ws, rebinned, cross_correlated, rebinned_tof, aligned, + offsets_ws_name]) + return focused diff --git a/scripts/Diffraction/isis_powder/pearl_routines/pearl_param_mapping.py b/scripts/Diffraction/isis_powder/pearl_routines/pearl_param_mapping.py index 5c3de085f7b9607beced67d1407b4c99129d4062..cc7902e28d57964fd80280658f4116da6471ad8a 100644 --- a/scripts/Diffraction/isis_powder/pearl_routines/pearl_param_mapping.py +++ b/scripts/Diffraction/isis_powder/pearl_routines/pearl_param_mapping.py @@ -6,32 +6,43 @@ from isis_powder.pearl_routines.pearl_enums import PEARL_FOCUS_MODES, PEARL_TT_M # Maps friendly user name (ext_name) -> script name (int_name) attr_mapping = \ [ - ParamMapEntry(ext_name="attenuation_file_path", int_name="attenuation_file_path"), - ParamMapEntry(ext_name="config_file", int_name="config_file_name"), - ParamMapEntry(ext_name="calibration_mapping_file", int_name="cal_mapping_path"), - ParamMapEntry(ext_name="calibration_directory", int_name="calibration_dir"), - ParamMapEntry(ext_name="do_absorb_corrections", int_name="absorb_corrections"), - ParamMapEntry(ext_name="file_ext", int_name="file_extension", optional=True), - ParamMapEntry(ext_name="focused_cropping_values", int_name="tof_cropping_values"), - ParamMapEntry(ext_name="focus_mode", int_name="focus_mode", enum_class=PEARL_FOCUS_MODES), - ParamMapEntry(ext_name="long_mode", int_name="long_mode"), - ParamMapEntry(ext_name="monitor_lambda_crop_range", int_name="monitor_lambda"), - ParamMapEntry(ext_name="monitor_integration_range", int_name="monitor_integration_range"), - ParamMapEntry(ext_name="monitor_mask_regions", int_name="monitor_mask_regions"), - ParamMapEntry(ext_name="monitor_spectrum_number", int_name="monitor_spec_no"), - ParamMapEntry(ext_name="monitor_spline_coefficient", int_name="monitor_spline"), - ParamMapEntry(ext_name="output_directory", int_name="output_dir"), - ParamMapEntry(ext_name="perform_attenuation", int_name="perform_atten"), - ParamMapEntry(ext_name="raw_data_tof_cropping", int_name="raw_data_crop_vals"), - ParamMapEntry(ext_name="run_in_cycle", int_name="run_in_range"), - ParamMapEntry(ext_name="run_number", int_name="run_number"), - ParamMapEntry(ext_name="spline_coefficient", int_name="spline_coefficient"), - ParamMapEntry(ext_name="tt88_grouping_filename", int_name="tt88_grouping"), - ParamMapEntry(ext_name="tt70_grouping_filename", int_name="tt70_grouping"), - ParamMapEntry(ext_name="tt35_grouping_filename", int_name="tt35_grouping"), - ParamMapEntry(ext_name="tt_mode", int_name="tt_mode", enum_class=PEARL_TT_MODES), - ParamMapEntry(ext_name="user_name", int_name="user_name"), - ParamMapEntry(ext_name="vanadium_absorb_filename", int_name="van_absorb_file"), - ParamMapEntry(ext_name="vanadium_tof_cropping", int_name="van_tof_cropping"), - ParamMapEntry(ext_name="vanadium_normalisation", int_name="van_norm") + ParamMapEntry(ext_name="attenuation_file_path", int_name="attenuation_file_path"), + ParamMapEntry(ext_name="config_file", int_name="config_file_name"), + ParamMapEntry(ext_name="calibration_mapping_file", int_name="cal_mapping_path"), + ParamMapEntry(ext_name="calibration_directory", int_name="calibration_dir"), + ParamMapEntry(ext_name="create_cal_rebin_1_params", int_name="cal_rebin_1"), + ParamMapEntry(ext_name="create_cal_rebin_2_params", int_name="cal_rebin_2"), + ParamMapEntry(ext_name="cross_corr_reference_spectra", int_name="reference_spectra"), + ParamMapEntry(ext_name="cross_corr_ws_index_max", int_name="cross_corr_ws_max"), + ParamMapEntry(ext_name="cross_corr_ws_index_min", int_name="cross_corr_ws_min"), + ParamMapEntry(ext_name="cross_corr_x_min", int_name="cross_corr_x_min"), + ParamMapEntry(ext_name="cross_corr_x_max", int_name="cross_corr_x_max"), + ParamMapEntry(ext_name="do_absorb_corrections", int_name="absorb_corrections"), + ParamMapEntry(ext_name="file_ext", int_name="file_extension", optional=True), + ParamMapEntry(ext_name="focused_cropping_values", int_name="tof_cropping_values"), + ParamMapEntry(ext_name="focus_mode", int_name="focus_mode", enum_class=PEARL_FOCUS_MODES), + ParamMapEntry(ext_name="get_det_offsets_d_ref", int_name="d_reference"), + ParamMapEntry(ext_name="get_det_offsets_step", int_name="get_det_offsets_step"), + ParamMapEntry(ext_name="get_det_offsets_x_min", int_name="get_det_offsets_x_min"), + ParamMapEntry(ext_name="get_det_offsets_x_max", int_name="get_det_offsets_x_max"), + ParamMapEntry(ext_name="long_mode", int_name="long_mode"), + ParamMapEntry(ext_name="monitor_lambda_crop_range", int_name="monitor_lambda"), + ParamMapEntry(ext_name="monitor_integration_range", int_name="monitor_integration_range"), + ParamMapEntry(ext_name="monitor_mask_regions", int_name="monitor_mask_regions"), + ParamMapEntry(ext_name="monitor_spectrum_number", int_name="monitor_spec_no"), + ParamMapEntry(ext_name="monitor_spline_coefficient", int_name="monitor_spline"), + ParamMapEntry(ext_name="output_directory", int_name="output_dir"), + ParamMapEntry(ext_name="perform_attenuation", int_name="perform_atten"), + ParamMapEntry(ext_name="raw_data_tof_cropping", int_name="raw_data_crop_vals"), + ParamMapEntry(ext_name="run_in_cycle", int_name="run_in_range"), + ParamMapEntry(ext_name="run_number", int_name="run_number"), + ParamMapEntry(ext_name="spline_coefficient", int_name="spline_coefficient"), + ParamMapEntry(ext_name="tt88_grouping_filename", int_name="tt88_grouping"), + ParamMapEntry(ext_name="tt70_grouping_filename", int_name="tt70_grouping"), + ParamMapEntry(ext_name="tt35_grouping_filename", int_name="tt35_grouping"), + ParamMapEntry(ext_name="tt_mode", int_name="tt_mode", enum_class=PEARL_TT_MODES), + ParamMapEntry(ext_name="user_name", int_name="user_name"), + ParamMapEntry(ext_name="vanadium_absorb_filename", int_name="van_absorb_file"), + ParamMapEntry(ext_name="vanadium_tof_cropping", int_name="van_tof_cropping"), + ParamMapEntry(ext_name="vanadium_normalisation", int_name="van_norm") ] diff --git a/scripts/Diffraction/isis_powder/routines/common.py b/scripts/Diffraction/isis_powder/routines/common.py index 60378e6b51879c9c45a1d3d3fca28eb1baba9624..61fe1cb7f64e66f0e8f90105d47253c641430414 100644 --- a/scripts/Diffraction/isis_powder/routines/common.py +++ b/scripts/Diffraction/isis_powder/routines/common.py @@ -206,8 +206,11 @@ def get_first_run_number(run_number_string): :return: The first run for the user input of runs """ run_numbers = generate_run_numbers(run_number_string=run_number_string) - if isinstance(run_numbers, list): - run_numbers = run_numbers[0] + + if not run_numbers: + raise RuntimeError("Attempted to load empty set of workspaces. Please input at least one valid run number") + + run_numbers = run_numbers[0] return run_numbers