// ************************************************************************************************ // // BornAgain: simulate and fit reflection and scattering // //! @file GUI/Models/InstrumentItems.cpp //! @brief Implement class InstrumentItem and all its children //! //! @homepage http://www.bornagainproject.org //! @license GNU General Public License v3 or higher (see COPYING) //! @copyright Forschungszentrum Jülich GmbH 2018 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) // // ************************************************************************************************ #include "GUI/Models/InstrumentItems.h" #include "Base/Const/Units.h" #include "Base/Pixel/RectangularPixel.h" #include "Core/Simulation/DepthProbeSimulation.h" #include "Device/Coord/CoordSystem1D.h" #include "Device/Detector/RectangularDetector.h" #include "Device/Detector/SphericalDetector.h" #include "Device/Instrument/CoordSystem2D.h" #include "Device/Instrument/Instrument.h" #include "GUI/Models/BackgroundItems.h" #include "GUI/Models/BeamWavelengthItem.h" #include "GUI/Models/DataItem.h" #include "GUI/Models/DetectorItems.h" #include "GUI/Models/Error.h" #include "GUI/Models/GroupItem.h" #include "GUI/Models/ItemFileNameUtils.h" #include "GUI/Models/JobItemUtils.h" #include "GUI/Models/MaskItems.h" #include "GUI/Models/PointwiseAxisItem.h" #include "GUI/Models/RealDataItem.h" #include "GUI/Models/SessionModel.h" #include "GUI/Models/SpecularBeamInclinationItem.h" #include "GUI/Models/TransformToDomain.h" #include "GUI/utils/GUIHelpers.h" namespace { const QString background_group_label = "Type"; const QStringList instrument_names{"GISASInstrument", "OffSpecularInstrument", "SpecularInstrument"}; BasicAxisItem* addAxisGroupProperty(SessionItem* parent, const QString& tag) { BasicAxisItem* axisItem = parent->addProperty<BasicAxisItem>(tag); axisItem->setToolTip("Incoming alpha range [deg]"); axisItem->titleItem()->setVisible(false); axisItem->binsItem()->setToolTip("Number of points in scan"); axisItem->lowerBoundItem()->setToolTip("Starting value [deg]"); axisItem->upperBoundItem()->setToolTip("Ending value [deg]"); axisItem->setTitle("alpha_i"); axisItem->setLowerBound(0.0); axisItem->setUpperBound(10.0); return axisItem; } } // namespace // ************************************************************************************************ // class InstrumentItem // ************************************************************************************************ const QString InstrumentItem::P_IDENTIFIER = "Identifier"; const QString InstrumentItem::P_BEAM = "Beam"; const QString InstrumentItem::P_BACKGROUND = "Background"; QString InstrumentItem::id() const { return getItemValue(P_IDENTIFIER).toString(); } void InstrumentItem::setId(const QString& id) { setItemValue(P_IDENTIFIER, id); } void InstrumentItem::setName(const QString& instrumentName) { setItemName(instrumentName); } QString InstrumentItem::name() const { return itemName(); } BeamItem* InstrumentItem::beamItem() const { return item<BeamItem>(P_BEAM); } BackgroundItem* InstrumentItem::backgroundItem() const { return &groupItem<BackgroundItem>(P_BACKGROUND); } GroupItem* InstrumentItem::backgroundGroup() { return item<GroupItem>(P_BACKGROUND); } bool InstrumentItem::alignedWith(const RealDataItem* item) const { return shape() == item->shape(); } std::unique_ptr<Instrument> InstrumentItem::createInstrument() const { std::unique_ptr<Instrument> result(new Instrument); result->setBeam(*beamItem()->createBeam()); return result; } InstrumentItem::InstrumentItem(const QString& modelType) : SessionItem(modelType) { setItemName(modelType); addProperty(P_IDENTIFIER, GUIHelpers::createUuid())->setVisible(false); } void InstrumentItem::initBackgroundGroup() { auto item = addGroupProperty(P_BACKGROUND, "Background group"); item->setDisplayName(background_group_label); item->setToolTip("Background type"); } template <typename T> void InstrumentItem::addBeam() { addProperty<T>(P_BEAM); } template <typename T> T* InstrumentItem::beam() const { return item<T>(P_BEAM); } // ************************************************************************************************ // class SpecularInstrumentItem // ************************************************************************************************ SpecularInstrumentItem::SpecularInstrumentItem() : InstrumentItem("SpecularInstrument") { addBeam<SpecularBeamItem>(); initBackgroundGroup(); beam<SpecularBeamItem>()->updateFileName( ItemFileNameUtils::instrumentDataFileName(*this)); } SpecularBeamItem* SpecularInstrumentItem::beamItem() const { return beam<SpecularBeamItem>(); } SpecularInstrumentItem::~SpecularInstrumentItem() = default; std::vector<int> SpecularInstrumentItem::shape() const { const auto axis_item = beamItem()->currentInclinationAxisItem(); return {axis_item->binCount()}; } void SpecularInstrumentItem::updateToRealData(const RealDataItem* item) { if (shape().size() != item->shape().size()) throw Error("Error in SpecularInstrumentItem::updateToRealData: The type " "of instrument is incompatible with passed data shape."); const auto& data = item->nativeOutputData()->axis(0); beamItem()->updateToData(data, item->nativeDataUnits()); } bool SpecularInstrumentItem::alignedWith(const RealDataItem* item) const { const QString native_units = item->nativeDataUnits(); if (native_units == "nbins") { return beamItem()->currentInclinationAxisItem()->modelType() == BasicAxisItem::M_TYPE && shape() == item->shape(); } else { auto axis_item = dynamic_cast<PointwiseAxisItem*>(beamItem()->currentInclinationAxisItem()); if (!axis_item) return false; if (axis_item->getUnitsLabel() != native_units) return false; auto instrument_axis = axis_item->axis(); if (!instrument_axis) return false; if (!item->hasNativeData()) return false; const auto& native_axis = item->nativeOutputData()->axis(0); return *instrument_axis == native_axis; } } ICoordSystem* SpecularInstrumentItem::createCoordSystem() const { const auto instrument = createInstrument(); auto axis_item = beamItem()->currentInclinationAxisItem(); if (auto pointwise_axis = dynamic_cast<PointwiseAxisItem*>(axis_item)) { if (!pointwise_axis->containsNonXMLData()) // workaround for loading project return nullptr; Axes::Coords native_units = JobItemUtils::coordsFromName(pointwise_axis->getUnitsLabel()); return new AngularReflectometryCoordinates(instrument->beam().wavelength(), *pointwise_axis->axis(), native_units); } return new AngularReflectometryCoordinates(instrument->beam().wavelength(), *axis_item->createAxis(1.0), Axes::Coords::DEGREES); } QString SpecularInstrumentItem::defaultName() const { return "Specular"; } // ************************************************************************************************ // class Instrument2DItem // ************************************************************************************************ const QString Instrument2DItem::P_DETECTOR = "Detector"; Instrument2DItem::Instrument2DItem(const QString& modelType) : InstrumentItem(modelType) { addBeam<GISASBeamItem>(); addGroupProperty(P_DETECTOR, "Detector group"); initBackgroundGroup(); setDefaultTag(P_DETECTOR); } Instrument2DItem::~Instrument2DItem() = default; DetectorItem* Instrument2DItem::detectorItem() const { return &groupItem<DetectorItem>(P_DETECTOR); } GroupItem* Instrument2DItem::detectorGroup() { return item<GroupItem>(P_DETECTOR); } void Instrument2DItem::setDetectorGroup(const QString& modelType) { setGroupProperty(P_DETECTOR, modelType); } void Instrument2DItem::clearMasks() { detectorItem()->clearMasks(); } void Instrument2DItem::importMasks(const MaskContainerItem* maskContainer) { detectorItem()->importMasks(maskContainer); } std::unique_ptr<Instrument> Instrument2DItem::createInstrument() const { auto result = InstrumentItem::createInstrument(); result->setDetector(*detectorItem()->createDetector()); return result; } // ************************************************************************************************ // class GISASInstrumentItem // ************************************************************************************************ GISASInstrumentItem::GISASInstrumentItem() : Instrument2DItem("GISASInstrument") {} std::vector<int> GISASInstrumentItem::shape() const { auto detector_item = detectorItem(); return {detector_item->xSize(), detector_item->ySize()}; } void GISASInstrumentItem::updateToRealData(const RealDataItem* item) { if (!item) return; const auto data_shape = item->shape(); if (shape().size() != data_shape.size()) throw Error("Error in GISASInstrumentItem::updateToRealData: The type of " "instrument is incompatible with passed data shape."); detectorItem()->setXSize(data_shape[0]); detectorItem()->setYSize(data_shape[1]); } QString GISASInstrumentItem::defaultName() const { return "GISAS"; } ICoordSystem* GISASInstrumentItem::createCoordSystem() const { const auto instrument = createInstrument(); instrument->initDetector(); return instrument->createScatteringCoords(); } // ************************************************************************************************ // class OffSpecularInstrumentItem // ************************************************************************************************ const QString OffSpecularInstrumentItem::P_ALPHA_AXIS = "Alpha axis"; OffSpecularInstrumentItem::OffSpecularInstrumentItem() : Instrument2DItem("OffSpecularInstrument") { BasicAxisItem* axis_item = addAxisGroupProperty(this, P_ALPHA_AXIS); auto inclination_item = axis_item->lowerBoundItem(); auto beam_item = beamItem(); beam_item->setInclinationAngle(inclination_item->value().toDouble()); beam_item->inclinationAngleItem()->setEnabled(false); inclination_item->mapper()->setOnValueChange([beam_item, inclination_item]() { beam_item->setInclinationAngle(inclination_item->value().toDouble()); }); } std::vector<int> OffSpecularInstrumentItem::shape() const { const int x_size = item<BasicAxisItem>(P_ALPHA_AXIS)->binCount(); auto detector_item = detectorItem(); return {x_size, detector_item->ySize()}; } void OffSpecularInstrumentItem::updateToRealData(const RealDataItem* dataItem) { if (!dataItem) return; const auto data_shape = dataItem->shape(); if (shape().size() != data_shape.size()) throw Error("Error in OffSpecularInstrumentItem::updateToRealData: The type of " "instrument is incompatible with passed data shape."); item<BasicAxisItem>(P_ALPHA_AXIS)->setBinCount(data_shape[0]); detectorItem()->setYSize(data_shape[1]); } QString OffSpecularInstrumentItem::defaultName() const { return "OffSpecular"; } ICoordSystem* OffSpecularInstrumentItem::createCoordSystem() const { const auto instrument = createInstrument(); instrument->initDetector(); const auto axis_item = item<BasicAxisItem>(OffSpecularInstrumentItem::P_ALPHA_AXIS); const auto detector2d = dynamic_cast<const IDetector2D*>(instrument->getDetector()); const auto axes = detector2d->axesClippedToRegionOfInterest(); ASSERT(axes.size() == 2); const IAxis& yAxis = *axes[1]; const Beam& beam = instrument->beam(); const auto alphaAxis = axis_item->createAxis(Units::deg); if (const auto* rectDetector = dynamic_cast<const RectangularDetector*>(detector2d)) { std::unique_ptr<RectangularPixel> detectorPixel(rectDetector->regionOfInterestPixel()); return OffSpecularCoordinates::createForRectangularDetector(beam, *alphaAxis, *detectorPixel, yAxis); } else if (dynamic_cast<const SphericalDetector*>(detector2d)) return OffSpecularCoordinates::createForSphericalDetector(beam, *alphaAxis, yAxis); ASSERT(0); return nullptr; } // ************************************************************************************************ // class DepthProbeInstrumentItem // ************************************************************************************************ const QString DepthProbeInstrumentItem::P_Z_AXIS = "Z axis"; DepthProbeInstrumentItem::DepthProbeInstrumentItem() : InstrumentItem("DepthProbeInstrument") { setItemName("DepthProbeInstrument"); addBeam<SpecularBeamItem>(); auto axisItem = beamItem()->currentInclinationAxisItem(); axisItem->setLowerBound(0.0); axisItem->setUpperBound(1.0); axisItem->setBinCount(500); auto axis = addProperty<BasicAxisItem>(P_Z_AXIS); axis->setLowerBound(-100.0); axis->setUpperBound(100.0); axis->titleItem()->setVisible(false); axis->binsItem()->setToolTip("Number of points in scan across sample bulk"); axis->lowerBoundItem()->setToolTip("Starting value below sample horizont in nm"); axis->upperBoundItem()->setToolTip("Ending value above sample horizont in nm"); } SpecularBeamItem* DepthProbeInstrumentItem::beamItem() const { return beam<SpecularBeamItem>(); } std::unique_ptr<Instrument> DepthProbeInstrumentItem::createInstrument() const { throw std::runtime_error("DepthProbeInstrumentItem::createInstrument()"); } std::vector<int> DepthProbeInstrumentItem::shape() const { return std::vector<int>(); // no certain shape to avoid linking to real data } void DepthProbeInstrumentItem::updateToRealData(const RealDataItem*) { throw std::runtime_error("DepthProbeInstrumentItem::updateToRealData()"); } QString DepthProbeInstrumentItem::defaultName() const { return "DepthProbe"; } std::unique_ptr<DepthProbeSimulation> DepthProbeInstrumentItem::createSimulation() const { std::unique_ptr<DepthProbeSimulation> simulation = std::make_unique<DepthProbeSimulation>(); const auto axis_item = beamItem()->currentInclinationAxisItem(); auto axis = axis_item->createAxis(Units::deg); simulation->setBeamParameters(beamItem()->wavelength(), static_cast<int>(axis->size()), axis->lowerBound(), axis->upperBound()); auto depthAxisItem = dynamic_cast<BasicAxisItem*>(getItem(P_Z_AXIS)); auto depthAxis = depthAxisItem->createAxis(1.0); simulation->setZSpan(depthAxis->size(), depthAxis->lowerBound(), depthAxis->upperBound()); TransformToDomain::setBeamDistribution(ParameterDistribution::BeamWavelength, *beamItem()->wavelengthItem(), *simulation.get()); TransformToDomain::setBeamDistribution(ParameterDistribution::BeamInclinationAngle, *beamItem()->inclinationAngleItem(), *simulation.get()); return simulation; } ICoordSystem* DepthProbeInstrumentItem::createCoordSystem() const { return createSimulation()->createCoordSystem(); }