feat(nmNum): 接通 Monte-Carlo 批量求解

- 生成复用预览样本,无有效样本时自动采样。
- 设置或模型变化后清除旧样本。
- 复用已有批量求解、停止处理与结果展示。
feature/Sensitivity-20261012
lvjunjie 1 week ago
parent 98d779931c
commit 924fbd724f

@ -111,7 +111,7 @@ class NMCALCULATION_EXPORT nmCalculationDllPebiSolverTask : public QThread {
typedef nmDataSensitive::RunResult SensitivityRunResult;
/**
* @brief 从源快照创建一份副本,按两种确定性方法的预览顺序串行求解全部组合。
* @brief 从源快照创建一份副本,按确定性组合或 Monte-Carlo 预览样本顺序串行求解。
* @param results 本次批量返回值;进入时清空,每组结果在更新下一组前独立保留。
* @return 全部组合遍历完成返回 true,单组成功与否读取对应 m_bSucceeded。
* 配置、基础网格或内存不足时返回 false;已完成的结果仍保留。

@ -156,8 +156,10 @@ public:
quint64 calculateModelCount(bool* overflow = nullptr) const;
// 返回与变量列表对应的基准单位值,未改变的参数保持模型值。
bool getRunValues(quint64 runIndex, QVector<double>& values) const;
// 每次打开 Monte-Carlo 预览重新分层抽样,预览滚动只读取本批结果。
// 每次预览重新分层抽样,生成和预览滚动复用本批结果。
bool resampleMonteCarlo(QString* error = nullptr);
// 采样设置改变后丢弃旧样本,下次预览或生成时重建。
void clearMonteCarloSamples();
int getMonteCarloCount() const;
void setMonteCarloCount(int count);
bool usesBaseUnits() const;

@ -921,7 +921,8 @@ nmCalculationDllPebiSolverTask* nmCalculationDllPebiSolverTask::createSensitivit
const nmDataSensitive::CalculationType type = frozenSampling.getCalculationType();
if(overflow || count == 0 || !frozenSampling.usesBaseUnits() ||
(type != nmDataSensitive::CALC_DETERMINISTIC &&
type != nmDataSensitive::CALC_DETERMINISTIC_MULTIVARIATE)) {
type != nmDataSensitive::CALC_DETERMINISTIC_MULTIVARIATE &&
type != nmDataSensitive::CALC_MONTE_CARLO)) {
if(errorMessage) *errorMessage = tr("Invalid sensitivity sampling configuration.");
return nullptr;
}
@ -995,7 +996,7 @@ bool nmCalculationDllPebiSolverTask::executeSensitivityBatch()
if(isCancelRequested()) return false;
SensitivityRunResult result;
result.m_nRunIndex = runIndex;
// 单参数法保持其余参数的模型值,多参数法按预览组合顺序逐组取值。
// 确定性组合和 Monte-Carlo 样本均按预览顺序读取,后台不重新采样。
bool inputReady = m_pSensitivitySampling->getRunValues(runIndex, result.m_vecParameters);
if(!inputReady) {
result.m_sFailureReason = tr("Invalid sensitivity sampling configuration.");

@ -573,6 +573,8 @@ nmDataSensitive::CalculationType nmDataSensitive::getCalculationType() const
void nmDataSensitive::setCalculationType(CalculationType type)
{
// 切换计算方法后不再沿用上一种方法的预览样本。
if(type != m_calcType) clearMonteCarloSamples();
m_calcType = type;
}
@ -666,11 +668,17 @@ int nmDataSensitive::getMonteCarloCount() const
void nmDataSensitive::setMonteCarloCount(int count)
{
// 次数变化后旧预览失效,下一次预览统一重新抽样。
if(count != m_monteCarloCount) m_monteCarloValues.clear();
// 次数变化后旧样本失效,下次预览或生成时重新抽样。
if(count != m_monteCarloCount) clearMonteCarloSamples();
m_monteCarloCount = count;
}
void nmDataSensitive::clearMonteCarloSamples()
{
// 只丢弃待计算样本,已完成批次的结果仍由结果窗口管理。
m_monteCarloValues.clear();
}
bool nmDataSensitive::resampleMonteCarlo(QString* error)
{
// 各参数的累积概率均分为 N 层,每层抽一点,再独立打乱,组成 N 组参数。
@ -717,6 +725,7 @@ QString nmDataSensitive::getModelSnapshotId() const
void nmDataSensitive::setModelSnapshotId(const QString& snapshotId)
{
// 仅在同步模型与范围后登记,编辑采样值不改变此标识。
if(snapshotId != m_modelSnapshotId) clearMonteCarloSamples();
m_modelSnapshotId = snapshotId;
}

@ -1062,8 +1062,7 @@ void nmWxSensitive::updateModelCount()
const bool monteCarlo = m_sensitiveData.getCalculationType() == nmDataSensitive::CALC_MONTE_CARLO;
modelCountLabel->setVisible(!monteCarlo);
m_monteCarloCount->setVisible(monteCarlo);
generateButton->setEnabled(!monteCarlo && m_pDataManager && !overflow && count > 0 && !m_calculationRunning);
generateButton->setToolTip(monteCarlo ? tr("Monte-Carlo sampling preview is available; calculation is not yet available.") : QString());
generateButton->setEnabled(m_pDataManager && !overflow && count > 0 && !m_calculationRunning);
}
bool nmWxSensitive::saveDataFromUI()
@ -1828,6 +1827,8 @@ void nmWxSensitive::onVariableParamChanged()
{
// 数据加载时不响应控件逐项赋值,编辑时只刷新所属参数页。
if (m_loading) return;
// 分布设置或次数改变后,生成必须重新抽样,不能使用旧预览。
m_sensitiveData.clearMonteCarloSamples();
for (int i = 0; i < m_numSpins.size(); ++i) {
if (sender() == m_numSpins[i] || sender() == m_minSpins[i] ||
sender() == m_maxSpins[i] || sender() == m_modeCombos[i] ||
@ -1857,6 +1858,8 @@ void nmWxSensitive::onTreeItemChanged(QTreeWidgetItem* item, int column)
{
Q_UNUSED(column);
if (m_loading) return;
// 勾选变量改变会改变整批组合;初始化树状态时保留已有样本。
if(item) m_sensitiveData.clearMonteCarloSamples();
// 分组选择与子项选择双向同步,禁用的参数不参与全选和总次数。
m_loading = true;
variablesTree->blockSignals(true);
@ -1884,12 +1887,18 @@ void nmWxSensitive::onTreeItemChanged(QTreeWidgetItem* item, int column)
void nmWxSensitive::onGenerateClicked()
{
// 本阶段只开放 Monte-Carlo 参数采样与预览,尚不启动数值计算。
if(calculationTypeCombo->currentIndex() == nmDataSensitive::CALC_MONTE_CARLO) return;
if(m_calculationRunning || !m_pDataManager || !saveDataFromUI()) return;
QString error;
QScopedPointer<nmCalculationDllPebiSolverTask> task;
try {
// 优先使用预览的整批样本;未预览或设置改变后才重新采样。
if(m_sensitiveData.getCalculationType() == nmDataSensitive::CALC_MONTE_CARLO) {
QVector<double> values;
if(!m_sensitiveData.getRunValues(0, values) && !m_sensitiveData.resampleMonteCarlo(&error)) {
QMessageBox::warning(this, tr("Sensitivity"), error);
return;
}
}
// 从最后成功结果恢复完整输入,当前编辑参数和全局拟压力配置不参与本次分析。
nmCalculationDllPebiSolverTask source(QString(), m_pDataManager, QString(), nullptr, false, true);
task.reset(source.createSensitivityBatch(m_sensitiveData, &error));
@ -1942,6 +1951,7 @@ void nmWxSensitive::setCalculationRunning(bool running)
calculationTypeCombo->setEnabled(!running);
variablesTree->setEnabled(!running);
detailStack->setEnabled(!running);
m_monteCarloCount->setEnabled(!running);
previewButton->setEnabled(!running);
cancelButton->setEnabled(!running);
if(running) {

Loading…
Cancel
Save