|
|
|
@ -13,6 +13,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
#include "ZxPlot.h"
|
|
|
|
#include "ZxPlot.h"
|
|
|
|
#include "ZxObjCurve.h"
|
|
|
|
#include "ZxObjCurve.h"
|
|
|
|
|
|
|
|
#include "ZxObjCurveBar.h"
|
|
|
|
#include "ZxSubTitle.h"
|
|
|
|
#include "ZxSubTitle.h"
|
|
|
|
#include "tCurvePlotScene.h"
|
|
|
|
#include "tCurvePlotScene.h"
|
|
|
|
#include "tCurvePlotView.h"
|
|
|
|
#include "tCurvePlotView.h"
|
|
|
|
@ -74,6 +75,9 @@
|
|
|
|
#include <QSet>
|
|
|
|
#include <QSet>
|
|
|
|
#include <QHash>
|
|
|
|
#include <QHash>
|
|
|
|
#include <QPushButton>
|
|
|
|
#include <QPushButton>
|
|
|
|
|
|
|
|
#include <QSpinBox>
|
|
|
|
|
|
|
|
#include <QLabel>
|
|
|
|
|
|
|
|
#include <cmath>
|
|
|
|
#include <QMdiSubWindow>
|
|
|
|
#include <QMdiSubWindow>
|
|
|
|
#include "ZxMdiArea.h"
|
|
|
|
#include "ZxMdiArea.h"
|
|
|
|
|
|
|
|
|
|
|
|
@ -108,6 +112,25 @@ namespace
|
|
|
|
QPointer<nmCalculationDllPebiSolverTask> s_pRunningSolverTask;
|
|
|
|
QPointer<nmCalculationDllPebiSolverTask> s_pRunningSolverTask;
|
|
|
|
QPointer<nmSubWndMain> s_pPendingSolverWindow;
|
|
|
|
QPointer<nmSubWndMain> s_pPendingSolverWindow;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class nmSensitivityHistogramBars : public ZxObjCurveBar
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
explicit nmSensitivityHistogramBars(double binWidth) : m_binWidth(binWidth) {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
|
|
|
void drawSubBars(QPainter* painter, QVector<QPointF>& points) override
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// 框架柱宽使用绘图坐标,按分箱宽度换算,缩放和窗口调整后仍对应同一区间。
|
|
|
|
|
|
|
|
const ZxSubAxisX* axis = getAxisX();
|
|
|
|
|
|
|
|
const double left = axis->getRangeMin();
|
|
|
|
|
|
|
|
m_dBarWidth = 0.95 * qAbs(axis->getPosForValue(left + m_binWidth) - axis->getPosForValue(left));
|
|
|
|
|
|
|
|
ZxObjCurveBar::drawSubBars(painter, points);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
double m_binWidth;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 历史窗口仍是单流量曲线结构,因此显示与双对数、半对数后处理一致的制度参考相。
|
|
|
|
// 历史窗口仍是单流量曲线结构,因此显示与双对数、半对数后处理一致的制度参考相。
|
|
|
|
const QVector<double>* resultReferenceRatesOf(
|
|
|
|
const QVector<double>* resultReferenceRatesOf(
|
|
|
|
const nmPebiResultWellSnapshot* pWell)
|
|
|
|
const nmPebiResultWellSnapshot* pWell)
|
|
|
|
@ -1875,6 +1898,7 @@ void nmSubWndMain::onShowSensitiveResults()
|
|
|
|
iSubWndFitting* fitting = m_pResultRefreshFitting.data();
|
|
|
|
iSubWndFitting* fitting = m_pResultRefreshFitting.data();
|
|
|
|
if(!manager || !fitting || !manager->getSensitiveData()) return;
|
|
|
|
if(!manager || !fitting || !manager->getSensitiveData()) return;
|
|
|
|
updateSensitiveHistoryPlot();
|
|
|
|
updateSensitiveHistoryPlot();
|
|
|
|
|
|
|
|
updateSensitiveHistogramPlot();
|
|
|
|
QSharedPointer<nmDataSensitive::BatchResult> result = manager->getSensitiveData()->getResults();
|
|
|
|
QSharedPointer<nmDataSensitive::BatchResult> result = manager->getSensitiveData()->getResults();
|
|
|
|
iGuiPlot* plot = fitting->getPlotByTag(s_Tag_ChartSensitivity);
|
|
|
|
iGuiPlot* plot = fitting->getPlotByTag(s_Tag_ChartSensitivity);
|
|
|
|
if(result.isNull()) {
|
|
|
|
if(result.isNull()) {
|
|
|
|
@ -2089,6 +2113,152 @@ void nmSubWndMain::updateSensitiveHistoryPlot()
|
|
|
|
if(created || restore) area->relayoutSubWnds();
|
|
|
|
if(created || restore) area->relayoutSubWnds();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void nmSubWndMain::updateSensitiveHistogramPlot()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
iSubWndFitting* fitting = m_pResultRefreshFitting.data();
|
|
|
|
|
|
|
|
nmDataAnalyzeManager* manager = m_pResultRefreshDataManager.data();
|
|
|
|
|
|
|
|
if(!fitting || !fitting->m_pMdiArea || !manager || !manager->getSensitiveData()) return;
|
|
|
|
|
|
|
|
ZxMdiArea* area = fitting->m_pMdiArea;
|
|
|
|
|
|
|
|
QMdiSubWindow* window = area->findChild<QMdiSubWindow*>("nmSensitivityHistogram");
|
|
|
|
|
|
|
|
const QSharedPointer<nmDataSensitive::BatchResult> batch = manager->getSensitiveData()->getResults();
|
|
|
|
|
|
|
|
if(batch.isNull() || batch->m_calcType != nmDataSensitive::CALC_MONTE_CARLO) {
|
|
|
|
|
|
|
|
// 新模型或确定性批次不保留上一批 Monte-Carlo 的统计图。
|
|
|
|
|
|
|
|
if(window) {
|
|
|
|
|
|
|
|
area->removeSubWindow(window);
|
|
|
|
|
|
|
|
delete window;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
const bool created = window == nullptr;
|
|
|
|
|
|
|
|
if(created) {
|
|
|
|
|
|
|
|
QWidget* page = new QWidget;
|
|
|
|
|
|
|
|
QVBoxLayout* layout = new QVBoxLayout(page);
|
|
|
|
|
|
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
|
|
|
|
QHBoxLayout* options = new QHBoxLayout;
|
|
|
|
|
|
|
|
options->addWidget(new QLabel(tr("Bins:"), page));
|
|
|
|
|
|
|
|
QSpinBox* bins = new QSpinBox(page);
|
|
|
|
|
|
|
|
bins->setObjectName("nmHistogramBins");
|
|
|
|
|
|
|
|
bins->setRange(1, 1000);
|
|
|
|
|
|
|
|
bins->setValue(20);
|
|
|
|
|
|
|
|
options->addWidget(bins);
|
|
|
|
|
|
|
|
QLabel* count = new QLabel(page);
|
|
|
|
|
|
|
|
count->setObjectName("nmHistogramCount");
|
|
|
|
|
|
|
|
options->addWidget(count);
|
|
|
|
|
|
|
|
options->addStretch();
|
|
|
|
|
|
|
|
layout->addLayout(options);
|
|
|
|
|
|
|
|
iGuiPlot* plot = new iGuiPlot(true, page);
|
|
|
|
|
|
|
|
plot->initUI(tr("Sensitivity histogram plot"));
|
|
|
|
|
|
|
|
plot->setDropEnabled(false);
|
|
|
|
|
|
|
|
plot->setAxisX(tr("Goodness of fit"), "MPa");
|
|
|
|
|
|
|
|
plot->setAxisY(tr("Count"));
|
|
|
|
|
|
|
|
plot->setAxisY(tr("Cumulative probability"), "%", true);
|
|
|
|
|
|
|
|
plot->m_pPlot->setBKColor(Qt::white);
|
|
|
|
|
|
|
|
for(int i = 0; i < 3; ++i) {
|
|
|
|
|
|
|
|
plot->setAxisXYLog(false, i);
|
|
|
|
|
|
|
|
plot->getAxisOf(i)->setVisible(true);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
layout->addWidget(plot);
|
|
|
|
|
|
|
|
window = area->addSubWindow(page);
|
|
|
|
|
|
|
|
window->setObjectName("nmSensitivityHistogram");
|
|
|
|
|
|
|
|
window->setWindowTitle(tr("Sensitivity histogram plot"));
|
|
|
|
|
|
|
|
window->setAttribute(Qt::WA_DeleteOnClose, false);
|
|
|
|
|
|
|
|
connect(bins, SIGNAL(valueChanged(int)), this, SLOT(updateSensitiveHistogramPlot()));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
iGuiPlot* plot = window->findChild<iGuiPlot*>();
|
|
|
|
|
|
|
|
const int binCount = window->findChild<QSpinBox*>("nmHistogramBins")->value();
|
|
|
|
|
|
|
|
QVector<double> errors;
|
|
|
|
|
|
|
|
for(size_t i = 0; i < batch->m_runs.size(); ++i) {
|
|
|
|
|
|
|
|
const nmDataSensitive::RunResult& run = batch->m_runs[i];
|
|
|
|
|
|
|
|
// 显示勾选不改变统计总体,失败、取消或无有效误差的运行不参与统计。
|
|
|
|
|
|
|
|
if(run.m_bSucceeded && !run.m_bCancelled && qIsFinite(run.m_dPressureRmse) && run.m_dPressureRmse >= 0.0)
|
|
|
|
|
|
|
|
errors.append(run.m_dPressureRmse);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
window->findChild<QLabel*>("nmHistogramCount")->setText(errors.isEmpty()
|
|
|
|
|
|
|
|
? tr("No valid goodness-of-fit results") : tr("Valid runs: %1").arg(errors.size()));
|
|
|
|
|
|
|
|
double minimum = 0.0, maximum = 1.0;
|
|
|
|
|
|
|
|
if(!errors.isEmpty()) {
|
|
|
|
|
|
|
|
minimum = maximum = errors.first();
|
|
|
|
|
|
|
|
for(int i = 1; i < errors.size(); ++i) {
|
|
|
|
|
|
|
|
minimum = qMin(minimum, errors[i]);
|
|
|
|
|
|
|
|
maximum = qMax(maximum, errors[i]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 单个结果或全部相同的误差也需要非零横轴范围。
|
|
|
|
|
|
|
|
if(minimum == maximum) {
|
|
|
|
|
|
|
|
const double padding = qMax(1e-6, maximum * 0.05);
|
|
|
|
|
|
|
|
minimum = qMax(0.0, minimum - padding);
|
|
|
|
|
|
|
|
maximum += padding;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
const double width = (maximum - minimum) / binCount;
|
|
|
|
|
|
|
|
QVector<int> counts(binCount, 0);
|
|
|
|
|
|
|
|
for(int i = 0; i < errors.size(); ++i) {
|
|
|
|
|
|
|
|
// 等宽分箱,左闭右开,最大误差归入最后一箱。
|
|
|
|
|
|
|
|
const int index = qMin(binCount - 1, int((errors[i] - minimum) / width));
|
|
|
|
|
|
|
|
++counts[index];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for(int i = plot->m_pPlot->getObjCount() - 1; i >= 0; --i)
|
|
|
|
|
|
|
|
if(qobject_cast<ZxObjCurve*>(plot->m_pPlot->getObjByIndex(i))) plot->m_pPlot->removeObjByIndex(i);
|
|
|
|
|
|
|
|
plot->clearUndoStack();
|
|
|
|
|
|
|
|
QVector<iCurveDesc> curves;
|
|
|
|
|
|
|
|
int largestCount = 0;
|
|
|
|
|
|
|
|
if(!errors.isEmpty()) {
|
|
|
|
|
|
|
|
QVector<QPointF> bars;
|
|
|
|
|
|
|
|
iCurveDesc cumulative;
|
|
|
|
|
|
|
|
cumulative.m_sCurveName = tr("Cumulative probability");
|
|
|
|
|
|
|
|
cumulative.m_oCurveType = POT_CurveDiscrete;
|
|
|
|
|
|
|
|
cumulative.m_bUseY2 = true;
|
|
|
|
|
|
|
|
cumulative.m_bLineVisible = true;
|
|
|
|
|
|
|
|
cumulative.m_bPointVisible = true;
|
|
|
|
|
|
|
|
cumulative.m_oPen.setColor(Qt::red);
|
|
|
|
|
|
|
|
cumulative.m_oDot = ZxDot(DTS_Circle, Qt::red, 0.6, true);
|
|
|
|
|
|
|
|
int total = 0;
|
|
|
|
|
|
|
|
for(int i = 0; i < binCount; ++i) {
|
|
|
|
|
|
|
|
const double center = minimum + (i + 0.5) * width;
|
|
|
|
|
|
|
|
bars.append(QPointF(center, counts[i]));
|
|
|
|
|
|
|
|
largestCount = qMax(largestCount, counts[i]);
|
|
|
|
|
|
|
|
total += counts[i];
|
|
|
|
|
|
|
|
cumulative.m_vecX.append(center);
|
|
|
|
|
|
|
|
cumulative.m_vecY.append(100.0 * total / errors.size());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 普通曲线入口不支持柱状图,直接使用框架柱状图元,累计概率仍走已有曲线入口。
|
|
|
|
|
|
|
|
nmSensitivityHistogramBars* histogram = new nmSensitivityHistogramBars(width);
|
|
|
|
|
|
|
|
histogram->init(tr("Count"), plot->m_pPlot->getMainAxisX(), plot->m_pPlot->getMainAxisY());
|
|
|
|
|
|
|
|
plot->m_pPlot->addOneObj(histogram);
|
|
|
|
|
|
|
|
histogram->setAllValues(bars);
|
|
|
|
|
|
|
|
histogram->setCurvePty(QPen(Qt::black), ZxDot(), true, false);
|
|
|
|
|
|
|
|
histogram->setBarFill(true);
|
|
|
|
|
|
|
|
histogram->setBarFillClr(QColor(85, 107, 47));
|
|
|
|
|
|
|
|
histogram->setShow3D(false);
|
|
|
|
|
|
|
|
histogram->setPinValue(false);
|
|
|
|
|
|
|
|
curves.append(cumulative);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
plot->setCurveData(curves);
|
|
|
|
|
|
|
|
plot->runUpdate();
|
|
|
|
|
|
|
|
plot->setReadOnly(true);
|
|
|
|
|
|
|
|
VecDouble ticksX, ticksY, ticksY2;
|
|
|
|
|
|
|
|
for(int i = 0; i <= 5; ++i) {
|
|
|
|
|
|
|
|
ticksX.append(minimum + i * (maximum - minimum) / 5.0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 累计概率轴每隔 10% 标注,100% 以上保留顶部留白。
|
|
|
|
|
|
|
|
for(int i = 0; i <= 10; ++i) ticksY2.append(i * 10.0);
|
|
|
|
|
|
|
|
// 小样本时放大计数轴范围,使柱子保持较低;样本较多时自动扩展并留出顶部空间。
|
|
|
|
|
|
|
|
const double countRange = qMax(16.0, largestCount * 1.2);
|
|
|
|
|
|
|
|
const double step = std::ceil(countRange / 8.0);
|
|
|
|
|
|
|
|
const double top = std::ceil(countRange / step) * step;
|
|
|
|
|
|
|
|
// 保持原轴范围,将主刻度加密一倍,计数刻度仍使用整数。
|
|
|
|
|
|
|
|
const double tickStep = qMax(1.0, std::ceil(step / 2.0));
|
|
|
|
|
|
|
|
for(double tick = 0.0; tick < top; tick += tickStep) ticksY.append(tick);
|
|
|
|
|
|
|
|
ticksY.append(top);
|
|
|
|
|
|
|
|
plot->setAxisScaleX(minimum, maximum, ticksX);
|
|
|
|
|
|
|
|
plot->setAxisScaleY(0.0, top, ticksY);
|
|
|
|
|
|
|
|
plot->setAxisScaleY2(0.0, 110.0, ticksY2);
|
|
|
|
|
|
|
|
const bool restore = window->isHidden();
|
|
|
|
|
|
|
|
plot->show();
|
|
|
|
|
|
|
|
window->widget()->show();
|
|
|
|
|
|
|
|
window->show();
|
|
|
|
|
|
|
|
if(created || restore) area->relayoutSubWnds();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmSubWndMain::startSolverTask()
|
|
|
|
void nmSubWndMain::startSolverTask()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if(!m_bSolverStartPending) {
|
|
|
|
if(!m_bSolverStartPending) {
|
|
|
|
|