From 9955c37a93a543049c7d47c860d83b946d9f681c Mon Sep 17 00:00:00 2001 From: Anne de Jong Date: Sun, 1 Jan 2017 20:18:22 +0100 Subject: [PATCH] Almost finished solver. Plot update needs to run in main thread. Use signaling for progress --- src/gui/CMakeLists.txt | 2 + src/gui/mainwindow.cpp | 38 ++++-- src/gui/mainwindow.h | 2 +- src/gui/mainwindow.ui | 12 ++ src/gui/solver.ui | 188 ---------------------------- src/gui/solver_dialog.cpp | 133 ++++++++++++++++++++ src/gui/solver_dialog.h | 64 +++++++--- src/gui/solver_dialog.ui | 223 +++++++++++++++++++++++----------- src/gui/solver_worker.cpp | 95 +++++++++++++++ src/gui/solver_worker.h | 48 ++++++++ src/main.cpp | 9 +- src/protobuf/solver.proto | 16 +++ src/protobuf/system.proto | 2 + src/solver/brent.cpp | 9 +- src/solver/brent.h | 4 +- src/solver/newton_raphson.cpp | 2 +- src/solver/solver.cpp | 46 +------ src/solver/solver.h | 12 +- src/solver/system.h | 5 +- 19 files changed, 556 insertions(+), 354 deletions(-) delete mode 100644 src/gui/solver.ui create mode 100644 src/gui/solver_dialog.cpp create mode 100644 src/gui/solver_worker.cpp create mode 100644 src/gui/solver_worker.h create mode 100644 src/protobuf/solver.proto diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index a484a45..439966d 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -7,6 +7,8 @@ add_library(tasmet_gui add_duct_dialog.cpp add_ductbc_dialog.cpp about_dialog.cpp + solver_dialog.cpp + solver_worker.cpp ) target_link_libraries(tasmet_gui qcustomplot) diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index b34f0a5..4a16bde 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -7,26 +7,30 @@ ////////////////////////////////////////////////////////////////////// #include "mainwindow.h" -#include "tasmet_config.h" #include "ui_mainwindow.h" + +#include + #include #include #include #include #include -#include "gas.h" +#include +#include + +#include "tasmet_config.h" #include "tasmet_tracer.h" #include "tasmet_assert.h" +#include "tasmet_exception.h" #include "add_duct_dialog.h" #include "add_ductbc_dialog.h" #include "tasystem.h" -#include "tasmet_exception.h" -#include -#include -#include -#include "about_dialog.h" -#include +#include "about_dialog.h" +#include "solver_dialog.h" + +#include using google::protobuf::TextFormat; const QString default_system_name = QString("Unsaved TaSMET Model") + @@ -426,7 +430,7 @@ void TaSMETMainWindow::on_segmentid_valueChanged(int id) { _window->removesegment->setEnabled(is_segment); } -void TaSMETMainWindow::on_name_textEdited() { +void TaSMETMainWindow::on_segmentname_textEdited() { auto& ductmap = *_system.mutable_ducts(); auto& ductbcmap = *_system.mutable_ductbcs(); @@ -444,8 +448,22 @@ void TaSMETMainWindow::on_name_textEdited() { void TaSMETMainWindow::on_actionSolve_triggered() { TRACE(15,"actionSolve()"); - TaSystem sys(_system); + + SolverDialog *d; + try { + d = new SolverDialog(this,_system); + } + catch(TaSMETError &e) { + e.show_user("Solver failed to initialize"); + return; + } + d->exec(); + // Solution is put in system, system updated from solver + // dialog. Therefore we are now probably dirty + changed(); + + delete d; } bool TaSMETMainWindow::isDirty() const { TRACE(15,"isDirty()"); diff --git a/src/gui/mainwindow.h b/src/gui/mainwindow.h index ce987ae..54c89b0 100644 --- a/src/gui/mainwindow.h +++ b/src/gui/mainwindow.h @@ -54,7 +54,7 @@ private slots: void on_addsegment_clicked(); void on_removesegment_clicked(); void on_segmentid_valueChanged(int i); - void on_name_textEdited(); + void on_segmentname_textEdited(); // Couple slots to functions void on_actionNew_triggered() { newModel();} diff --git a/src/gui/mainwindow.ui b/src/gui/mainwindow.ui index 0e49e9e..d9dd039 100644 --- a/src/gui/mainwindow.ui +++ b/src/gui/mainwindow.ui @@ -347,6 +347,8 @@ Solver + + @@ -419,6 +421,16 @@ Solve... + + + Reinitialize solver + + + + + Posprocess model... + + diff --git a/src/gui/solver.ui b/src/gui/solver.ui deleted file mode 100644 index 6f3db4b..0000000 --- a/src/gui/solver.ui +++ /dev/null @@ -1,188 +0,0 @@ - - - Dialog - - - - 0 - 0 - 730 - 362 - - - - TaSMET Solver - - - - - - - - - 0 - 0 - - - - Solver Settings - - - - - - Maximum iterations: - - - - - - - Solver type - - - - - - - - - - - 0 - 0 - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Solution tolerance: - - - - - - - Residual tolerance: - - - - - - - - 0 - 0 - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - 0 - 0 - - - - Solve! - - - - - - Solve - - - - - - - Stop - - - - - - - Single iteration - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - 0 - 0 - - - - Progress - - - - - - - - - - - - - - - - QCustomPlot - QWidget -
qcustomplot.h
- 1 -
-
- - -
diff --git a/src/gui/solver_dialog.cpp b/src/gui/solver_dialog.cpp new file mode 100644 index 0000000..7974f37 --- /dev/null +++ b/src/gui/solver_dialog.cpp @@ -0,0 +1,133 @@ +// solver_dialog.cpp +// +// last-edit-by: J.A. de Jong +// +// Description: +// +////////////////////////////////////////////////////////////////////// + +#include "solver_dialog.h" +#include "ui_solver_dialog.h" +#include "system.pb.h" +#include "tasmet_exception.h" +#include "tasmet_constants.h" +#include "tasmet_tracer.h" +#include "solver.pb.h" +#include +#include "solver_worker.h" + +SolverDialog::SolverDialog(QWidget* parent, + pb::System& sys): + QDialog(parent), + _sys(sys), + _sparams(sys.solverparams()), + _dialog(new Ui::solver_dialog()) +{ + + TRACE(15,"SolverDialog"); + if(_dialog==nullptr) throw TaSMETBadAlloc(); + + _dialog->setupUi(this); + + _dialog->funtol->setValidator(new QDoubleValidator(constants::min_funtol, + constants::max_funtol, + constants::field_decimals)); + + _dialog->reltol->setValidator(new QDoubleValidator(constants::min_funtol, + constants::max_funtol, + constants::field_decimals)); + + // _dialog->maxiter->setValidator(new QIntValidator(constants::min_maxiter, + // constants::max_maxiter)); + + for(int solvertype = pb::SolverType_MIN;solvertype<=pb::SolverType_MAX;solvertype++){ + _dialog->solvertype->addItem(QString::fromStdString(SolverType_Name((pb::SolverType) solvertype))); + } + + _plot = _dialog->progress_plot; + _plot->xAxis->setLabel("Iteration [-]"); + _plot->yAxis->setLabel("Residual [-]"); + + _plot->yAxis->setScaleType(QCPAxis::stLogarithmic); + + QSharedPointer ticker(new QCPAxisTickerLog()); + _plot->yAxis->setTicker(ticker); + + _plot->replot(); + set(_sys.solverparams()); + +} +SolverDialog::~SolverDialog() { + if(_solver_worker) + _solver_worker->solver_stop(); +} +void SolverDialog::set(const pb::SolverParams& sol) { + TRACE(15,"set"); + + _dialog->solvertype->setCurrentIndex((int) sol.solvertype()); + _dialog->funtol->setText(QString::number(sol.funtol())); + _dialog->reltol->setText(QString::number(sol.reltol())); + +} +void SolverDialog::changed() { + +} +void SolverDialog::on_stop_clicked(){ + tasmet_assert(_solver_worker!=nullptr,"No _solver_worker"); + _solver_worker->solver_stop(); +} +void SolverDialog::on_solve_clicked() { + TRACE(15,"on_solve_clicked"); + _dialog->solve->setEnabled(false); + _dialog->singleiteration->setEnabled(false); + _dialog->stop->setEnabled(true); + + assert(!_solver_worker); + + _solver_worker = new SolverWorker(_sys); + QThread* thread = new QThread; + + _solver_worker->moveToThread(thread); + + + connect(thread, &QThread::started, + _solver_worker, &SolverWorker::solver_start); + + connect(_solver_worker, &SolverWorker::solver_stopped, + thread, &QThread::quit); + + connect(_solver_worker, &SolverWorker::solver_stopped, + this, &SolverDialog::solver_stopped); + + connect(thread, &QThread::finished, + thread, &QThread::deleteLater); + + connect(thread, &QThread::finished, + _solver_worker, &SolverWorker::deleteLater); + + + thread->start(); + + +} +void SolverDialog::on_singleiteration_clicked() { + _dialog->solve->setEnabled(false); + _dialog->singleiteration->setEnabled(false); + _dialog->stop->setEnabled(true); + + +} + +void SolverDialog::solver_stopped() { + _dialog->solve->setEnabled(true); + _dialog->singleiteration->setEnabled(true); + _dialog->stop->setEnabled(false); + + // stop the solver and delete it + if(_solver_worker!=nullptr) { + _solver_worker->solver_stop(); + _solver_worker = nullptr; + } +} + +////////////////////////////////////////////////////////////////////// diff --git a/src/gui/solver_dialog.h b/src/gui/solver_dialog.h index 0d5e522..5a44f21 100644 --- a/src/gui/solver_dialog.h +++ b/src/gui/solver_dialog.h @@ -8,25 +8,59 @@ #pragma once #ifndef SOLVER_DIALOG_H #define SOLVER_DIALOG_H +#include +#include + +#include "solver.pb.h" + +namespace pb { + class System; +} +namespace Ui { + class solver_dialog; +} +class QCustomPlot; +class SolverWorker; +class SolverDialog: public QDialog { + Q_OBJECT + + pb::System& _sys; // Reference to system + pb::SolverParams _sparams; + + Ui::solver_dialog* _dialog; + + QCustomPlot* _plot; + + SolverWorker* _solver_worker = nullptr; + +public: + + SolverDialog(QWidget* parent, + pb::System& sys); + + ~SolverDialog(); + + void set(const pb::SolverParams&); -// Put here, temporarily - window->funtol->setText(QString::number(constants::default_funtol)); - window->funtol->setValidator(new QDoubleValidator(constants::min_funtol, - constants::max_funtol, - constants::field_decimals)); - window->reltol->setText(QString::number(constants::default_funtol)); - window->reltol->setValidator(new QDoubleValidator(constants::min_funtol, - constants::max_funtol, - constants::field_decimals)); +private slots: + // Connected to the signal from the solver thread. Reactivates the + // buttons + void solver_stopped(); - window->maxiter->setText(QString::number(constants::default_maxiter)); - window->maxiter->setValidator(new QIntValidator(constants::min_maxiter, - constants::max_maxiter)); + void on_solve_clicked(); + void on_singleiteration_clicked(); + void on_stop_clicked(); - for(const SolverType& t: SolverType_vec){ - window->solvertype->addItem(SolverTypeToString(t)); - } + void on_funtol_textChanged() { changed();} + void on_reltol_textChanged() { changed();} + void on_solvertype_currentIndexChanged(int) { changed();} +private: + // Called whenever the user changes input values + void changed(); + +}; + #endif // SOLVER_DIALOG_H diff --git a/src/gui/solver_dialog.ui b/src/gui/solver_dialog.ui index 7017a49..a452900 100644 --- a/src/gui/solver_dialog.ui +++ b/src/gui/solver_dialog.ui @@ -1,86 +1,165 @@ - Dialog - + solver_dialog + 0 0 - 747 - 336 + 730 + 362 - Dialog + TaSMET Solver - - - - 10 - 40 - 306 - 168 - - - - Solver Settings - - - - - - Maximum iterations: - - - - - - - Solver type - - - - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Solution tolerance: - - - - - - - Residual tolerance: - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - + + + + + + + + 0 + 0 + + + + Solver Settings + + + + + + Solver type + + + + + + + + + + + 0 + 0 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Solution tolerance: + + + + + + + Residual tolerance: + + + + + + + + 0 + 0 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + 0 + 0 + + + + Solve! + + + + + + Solve + + + + + + + Stop + + + + + + + Single iteration + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + 0 + 0 + + + + Progress + + + + + + + + + + + + QCustomPlot + QWidget +
qcustomplot.h
+ 1 +
+
diff --git a/src/gui/solver_worker.cpp b/src/gui/solver_worker.cpp new file mode 100644 index 0000000..9a697f3 --- /dev/null +++ b/src/gui/solver_worker.cpp @@ -0,0 +1,95 @@ +// solver_thread.cpp +// +// last-edit-by: J.A. de Jong +// +// Description: +// +////////////////////////////////////////////////////////////////////// + +#include "solver_worker.h" +#include +#include "tasmet_tracer.h" +#include "system.pb.h" +#include +#include +SolverWorker::SolverWorker(pb::System& sys): + _run(false) +{ + +} +SolverWorker::~SolverWorker(){ + TRACE(15,"~SolverWorker"); +} +void SolverWorker::solver_stop() { + _run = false; +} + +void SolverWorker::solver_start() { + TRACE(15,"SolverWorker::solver_start()"); + using namespace std::placeholders; // for _1, _2 etc. + + _run = true; + + progress_callback callback = std::bind(&SolverWorker::pg_callback, + this,_1); + + SolverProgress p; + // For testing purposes + while(true) { + TRACE(15,"Solver start virtual iteration"); + + + SolverAction action = callback(p); + if(action == Stop) break; + sleep(1); + + p.fun_err/=10; + p.rel_err/=10; + p.iteration++; + } + + emit solver_stopped(); +} +SolverAction SolverWorker::pg_callback(SolverProgress pg) { + TRACE(15,"pg_callback"); + + if(!_run) return Stop; + + + // QCPGraph *_funer,*_reler,*_funtol,*_reltol; + + // _funer = _plot->addGraph(); + // _reler = _plot->addGraph(); + + TRACE(15,"SolverWorker::pg_callback()"); + + // VARTRACE(15,progress.fun_err); + // VARTRACE(15,progress.iteration); + // _funer->addData(progress.iteration,progress.fun_err); + // _reler->addData(progress.iteration,progress.rel_err); + + // _plot->xAxis->setRange(0,progress.iteration); + + // _plot->replot(); + + // if(progress.done) { + // // We are done! + + // QString m = "Solver reached a converged solution."; + + // QMessageBox::information(this, + // "Solver done", + // m); + + + // } + + + return Continue; + + +} + + + +////////////////////////////////////////////////////////////////////// diff --git a/src/gui/solver_worker.h b/src/gui/solver_worker.h new file mode 100644 index 0000000..9b78064 --- /dev/null +++ b/src/gui/solver_worker.h @@ -0,0 +1,48 @@ +// solver_worker.h +// +// Author: J.A. de Jong +// +// Description: +// Worker which solves the system, updates the progress graph, +// etc +////////////////////////////////////////////////////////////////////// +#pragma once +#ifndef SOLVER_WORKER_H +#define SOLVER_WORKER_H +#include "tasmet_types.h" +#include "solver.h" +#include "system.h" +#include +#include +namespace pb{ + class System; +} + +Q_DECLARE_METATYPE(SolverProgress); + +class SolverWorker: public QObject { + Q_OBJECT + + std::atomic _run; + Solver* _solver; +public: + + SolverWorker(pb::System& sys); + ~SolverWorker(); + void solver_stop(); + +public slots: + void solver_start(); +signals: + + // This signal is emitted when the solver is stopped + void solver_stopped(); + void progress(SolverProgress); +private: + SolverAction pg_callback(SolverProgress pg); + +}; + + +#endif // SOLVER_WORKER_H +////////////////////////////////////////////////////////////////////// diff --git a/src/main.cpp b/src/main.cpp index 3f2691f..6e842c5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -45,22 +45,17 @@ int main(int argc, char *argv[]) { // Q_INIT_RESOURCE(application); INITTRACE(15); - std::cout << "hoid" << std::endl; // Initialize PythonQt // PythonQt::init(PythonQt::IgnoreSiteModule | PythonQt::RedirectStdOut); PythonQt::init(); - std::cout << "hoid" << std::endl; + PythonQt* pyqt = PythonQt::self(); PythonQtObjectPtr context = pyqt->getMainModule(); - std::cout << "hoid" << std::endl; + QVariant rv = context.evalScript("from math import *\n"); if(pyqt->hadError()) { return -1; } - std::cout << "hoid" << std::endl; - // std::cout << rv.typeName() << std::endl; - std::cout << (rv.isNull()?"true": "false") << std::endl; - std::cout << "hoid" << std::endl; QApplication app(argc, argv); diff --git a/src/protobuf/solver.proto b/src/protobuf/solver.proto new file mode 100644 index 0000000..3e8c570 --- /dev/null +++ b/src/protobuf/solver.proto @@ -0,0 +1,16 @@ +syntax = "proto2"; +package pb; + +enum SolverType { + NewtonRaphson = 0; +} + +// Parameters the solver requires to operate +message SolverParams { + optional SolverType solvertype = 1 [default=NewtonRaphson]; + optional double reltol = 3 [default = 1e-6]; + optional double funtol = 4 [default = 1e-6]; + optional double fun_err = 5 [default = 1e0]; + optional double rel_err = 2 [default = 1e0]; + optional uint32 iteration = 6 [default = 0]; +} diff --git a/src/protobuf/system.proto b/src/protobuf/system.proto index 444de6d..a0b3501 100644 --- a/src/protobuf/system.proto +++ b/src/protobuf/system.proto @@ -4,6 +4,7 @@ package pb; import "duct.proto"; import "ductbc.proto"; import "gas.proto"; +import "solver.proto"; enum SystemType { TaSystem = 0; @@ -18,4 +19,5 @@ message System { map ducts = 7; map ductbcs = 8; repeated double solution = 9; + optional SolverParams solverparams = 10; } diff --git a/src/solver/brent.cpp b/src/solver/brent.cpp index 064384f..8c1a5eb 100644 --- a/src/solver/brent.cpp +++ b/src/solver/brent.cpp @@ -17,16 +17,15 @@ const d eps = std::numeric_limits::epsilon(); -Brent::Brent(const NoGradientNonlinearSystem& sys,us maxiter,d reltol): +Brent::Brent(const NoGradientNonlinearSystem& sys,d reltol): Solver(sys), - _reltol(reltol), - _maxiter(maxiter) + _reltol(reltol) { TRACE(15,"Brent::Brent"); #ifdef TASMET_DEBUG bool ok=true; - ok&=(maxiter>0); + ok&=(reltol >= eps); if(!ok){ @@ -89,7 +88,7 @@ void Brent::start_implementation(NoGradientNonlinearSystem& system, bool bisec_flag; SolverProgress progress; - while(_running && progress.iteration <=_maxiter) { + while(true) { if((fa!=fc) && (fb!=fc)){ // Inverse quadratic interpolation diff --git a/src/solver/brent.h b/src/solver/brent.h index 82299e5..6c8a648 100644 --- a/src/solver/brent.h +++ b/src/solver/brent.h @@ -12,9 +12,9 @@ class Brent: public Solver,d> { d _reltol; - us _maxiter; + public: - Brent(const NoGradientNonlinearSystem& sys,us maxiter=10000,d reltol=1e-6); + Brent(const NoGradientNonlinearSystem& sys,d reltol=1e-6); protected: void start_implementation(NoGradientNonlinearSystem& sys,progress_callback*); }; diff --git a/src/solver/newton_raphson.cpp b/src/solver/newton_raphson.cpp index 975bc13..d74006c 100644 --- a/src/solver/newton_raphson.cpp +++ b/src/solver/newton_raphson.cpp @@ -26,7 +26,7 @@ void NewtonRaphson::start_implementation(GradientNonlinearSystem& system, SolverProgress progress; SolverAction action; - while (_running && progress.iteration<=_maxiter) { + while (true) { sdmat jac=system.jacobian(); diff --git a/src/solver/solver.cpp b/src/solver/solver.cpp index 6e1fac6..e8bb695 100644 --- a/src/solver/solver.cpp +++ b/src/solver/solver.cpp @@ -20,70 +20,28 @@ static void SolverThread(Solver* solver, template Solver::Solver(const system_T& sys){ _sys = sys.copy(); - if(!_sys) - throw TaSMETBadAlloc(); - _running = false; + if(!_sys) throw TaSMETBadAlloc(); } template Solver::~Solver(){ - stop(); - assert(!_running); - assert(!_solver_thread); delete _sys; } template -void Solver::start(progress_callback* callback,bool wait){ - - if(_running){ - assert(_solver_thread); - throw TaSMETError("Solver already running"); - } - - assert(_solver_thread == nullptr); - - if(!wait) { - this->_solver_thread = new std::thread(SolverThread, - this, - _sys, - callback); - if(!_solver_thread) - throw TaSMETBadAlloc(); - } - else { +void Solver::start(progress_callback* callback){ TRACE(15,"Waiting for solver..."); start_implementation(*_sys,callback); - } } -template -void Solver::stop() { - _running = false; - - if(_solver_thread){ - - _solver_thread->join(); - delete _solver_thread; - _solver_thread = nullptr; - - } -} template result_T Solver::getSolution() { - if(_running){ - throw TaSMETError("Solver is running"); - } - - // Cleanup thread resources - stop(); - return _sys->getSolution(); } template diff --git a/src/solver/solver.h b/src/solver/solver.h index c668387..a84cdc4 100644 --- a/src/solver/solver.h +++ b/src/solver/solver.h @@ -21,11 +21,12 @@ struct SolverProgress { size_t iteration = 0; - d fun_err; - d rel_err; + d fun_err = 1e0; + d rel_err = 1e0; bool done = false; }; + enum SolverAction{ Continue=0, Stop = 1 @@ -38,22 +39,17 @@ class Solver { protected: system_T* _sys = nullptr; - std::thread* _solver_thread = nullptr; - std::atomic _running; public: Solver(const system_T& sys); Solver(const Solver&)=delete; Solver& operator=(const Solver&)=delete; - void start(progress_callback* callback=nullptr,bool wait=true); - void stop(); // Stops the solver + void start(progress_callback* callback); // Returns the solution of the problem result_T getSolution(); virtual ~Solver(); - template - friend void SolverThread(Solver*,Y*,progress_callback*); protected: virtual void start_implementation(system_T& sys,progress_callback*)=0; }; diff --git a/src/solver/system.h b/src/solver/system.h index 3a0b555..6d128e9 100644 --- a/src/solver/system.h +++ b/src/solver/system.h @@ -19,7 +19,10 @@ class NoGradientNonlinearSystem{ public: virtual T residual() const=0; - virtual T residual(const T& guess) { updateSolution(guess); return residual();} + virtual T residual(const T& guess) { + updateSolution(guess); + return residual(); + } // Obtain an initial guess of the solution virtual T getSolution() const=0;