// ************************************************************************************************ // // BornAgain: simulate and fit reflection and scattering // //! @file GUI/View/Fit/FitSessionController.cpp //! @brief Implements class FitSessionController //! //! @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/View/Fit/FitSessionController.h" #include "GUI/Model/Data/IntensityDataItem.h" #include "GUI/Model/Job/FitParameterContainerItem.h" #include "GUI/Model/Job/FitParameterItem.h" #include "GUI/Model/Job/FitSuiteItem.h" #include "GUI/Model/Job/JobItem.h" #include "GUI/Util/Error.h" #include "GUI/Util/Path.h" #include "GUI/View/Fit/FitLog.h" #include "GUI/View/Fit/FitObjectiveBuilder.h" #include "GUI/View/Fit/FitWorkerLauncher.h" #include "GUI/View/Fit/GUIFitObserver.h" namespace { const bool use_fit_objective = true; } FitSessionController::FitSessionController(QObject* parent) : QObject(parent) , m_jobItem(nullptr) , m_runFitManager(new FitWorkerLauncher(this)) , m_observer(new GUIFitObserver) , m_fitlog(new FitLog(this)) , m_block_progress_update(false) { connect(m_observer.get(), &GUIFitObserver::updateReady, this, &FitSessionController::onObserverUpdate); connect(m_runFitManager, &FitWorkerLauncher::fittingStarted, this, &FitSessionController::onFittingStarted); connect(m_runFitManager, &FitWorkerLauncher::fittingFinished, this, &FitSessionController::onFittingFinished); connect(m_runFitManager, &FitWorkerLauncher::fittingError, this, &FitSessionController::onFittingError); } FitSessionController::~FitSessionController() = default; void FitSessionController::setJobItem(JobItem* jobItem) { if (m_jobItem && m_jobItem != jobItem) throw Error("FitSuiteManager::setJobItem() -> JobItem was already set."); m_jobItem = jobItem; ASSERT(m_jobItem); // no need to unsubscribe from jobItem on jobItem destroy. FitSessionManager deletes // controller right after the jobItem. // Propagates update interval from FitSuiteItem to fit observer. connect(m_jobItem->fitSuiteItem(), &FitSuiteItem::updateIntervalChanged, m_observer.get(), &GUIFitObserver::setInterval, Qt::UniqueConnection); } void FitSessionController::onStartFittingRequest() { if (!m_jobItem) return; try { m_objectiveBuilder = std::make_unique<FitObjectiveBuilder>(m_jobItem); m_observer->setInterval(m_jobItem->fitSuiteItem()->updateInterval()); m_objectiveBuilder->attachObserver(m_observer); m_observer->finishedPlotting(); m_runFitManager->runFitting(m_objectiveBuilder); } catch (std::exception& e) { m_jobItem->setStatus(JobStatus::Failed); m_fitlog->append(e.what(), FitLogLevel::Error); emit fittingError(QString::fromStdString(e.what())); } } FitLog* FitSessionController::fitLog() { return m_fitlog.get(); } void FitSessionController::onStopFittingRequest() { m_runFitManager->interruptFitting(); } void FitSessionController::onObserverUpdate() { auto progressInfo = m_observer->progressInfo(); m_jobItem->simulatedDataItem()->setRawDataVector(progressInfo.simValues()); updateIterationCount(progressInfo); if (!use_fit_objective) updateFitParameterValues(progressInfo); updateLog(progressInfo); if (!progressInfo.logInfo().empty()) m_fitlog->append(progressInfo.logInfo(), FitLogLevel::Default); m_observer->finishedPlotting(); } void FitSessionController::onFittingStarted() { m_fitlog->clearLog(); m_jobItem->setStatus(JobStatus::Fitting); m_jobItem->setProgress(0); m_jobItem->setBeginTime(m_runFitManager->fitStart()); m_jobItem->setEndTime(QDateTime()); emit fittingStarted(); } void FitSessionController::onFittingFinished() { if (m_jobItem->getStatus() != JobStatus::Failed) m_jobItem->setStatus(JobStatus::Completed); m_jobItem->setEndTime(m_runFitManager->fitEnd()); m_jobItem->setProgress(100); if (m_jobItem->isCompleted()) m_fitlog->append("Done", FitLogLevel::Success); emit fittingFinished(); } void FitSessionController::onFittingError(const QString& text) { QString message; message.append("Current settings cause fitting failure.\n\n"); message.append(text); m_fitlog->append(message.toStdString(), FitLogLevel::Error); m_jobItem->setEndTime(m_runFitManager->fitEnd()); emit fittingError(message); } void FitSessionController::updateIterationCount(const FitProgressInfo& info) { FitSuiteItem* fitSuiteItem = m_jobItem->fitSuiteItem(); // FIXME FitFlowWidget updates chi2 and n_iteration on P_ITERATION_COUNT change // The order of two lines below is important fitSuiteItem->setChi2(info.chi2()); fitSuiteItem->setIterationCount(info.iterationCount()); } void FitSessionController::updateFitParameterValues(const FitProgressInfo& info) { FitParameterContainerItem* fitParContainer = m_jobItem->fitParameterContainerItem(); fitParContainer->setValuesInParameterContainer(info.parValues(), m_jobItem->parameterContainerItem()); } void FitSessionController::updateLog(const FitProgressInfo& info) { QString message = QString("NCalls:%1 chi2:%2 \n").arg(info.iterationCount()).arg(info.chi2()); FitParameterContainerItem* fitParContainer = m_jobItem->fitParameterContainerItem(); int index(0); for (FitParameterItem* item : fitParContainer->fitParameterItems()) { if (item->linkItems().empty()) continue; QString parinfo = QString(" %1 %2\n").arg(item->displayName()).arg(info.parValues()[index++]); message.append(parinfo); } m_fitlog->append(message.toStdString(), FitLogLevel::Default); }