Compare commits

...

22 Commits

Author SHA1 Message Date
lvjunjie ed9cc46ac5 refactor(nmNum): 移除含水饱和度自动拟合选项
- 移除自动拟合窗口中的含水饱和度选项、范围配置和参数写回
- LM 与 PSO 固定使用模型当前含水饱和度,旧拟合配置不再启用该参数
- 保留储层参数面板及正常求解逻辑,通过四个相关源文件的编译语法检查
2 days ago
lvjunjie e3d49941bc fix(nmNum): 每次打开拟合窗口时重建参数范围
- 每次打开自动拟合窗口时,按当前模型参数重新生成上下限
- 移除沿用和校正历史范围的分支,复用现有范围计算规则
- 通过编译语法检查和差异检查
2 days ago
lvjunjie 529655f205 refactor(nmNum): 将拟合流程改为高度预调和双目标联合 LM
- 保留渗透率高度预调,移除逐参数形状扫描及井储、表皮复查
- 联合 LM 先优化整体形状误差,再优化总误差,共用步长、阻尼和停滞规则
- 形状达标、确认停滞或达到额度后切换目标,两段独立计算迭代和求解额度
- 更新拟合日志和策略元数据,通过编译语法检查及联合 LM 行为验证
3 days ago
lvjunjie 32b1ba3939 fix(nmNum): 补充网格尺寸保存校验和中文错误提示
- 保存前拦截空值、非数字、非正数及非有限值,避免无效设置被静默忽略
- 输入错误时保留设置窗口和原有网格尺寸,允许纠正后重新保存
- 补充中文错误提示并同步编译翻译资源
- 通过 14 项对话框验证,覆盖无效输入、有效数值及取消操作
3 days ago
lvjunjie 266a4d234a fix(nmNum): 修复压裂井成果加载时的空指针崩溃
- 压裂直井和多段压裂井复用基类已初始化的储层引用
- 储层尚未发布时跳过厚度读取,避免构造阶段访问空指针
- 加载时由成果 JSON 恢复实际裂缝高度,保留正常新建井的默认值逻辑
- 通过 Release 编译及四组现有成果加载验证,核对 38 项参数一致
3 days ago
lvjunjie f43eb568d8 refactor(nmNum): 移除分层采样并按对数时间跨度确定采样点数
- 删除 LM 分层采样选项、逐层加密逻辑及专用诊断字段
- 每个对数时间数量级取 20 个间隔,采样点包含区间两端
- 整体误差、形状误差及前期指标共用采样网格
- 形状斜率跨度保持约为对数时间范围的 10%,同步适配残差维度和窗口权重
- 更新拟合记录中的采样策略描述,翻译资源留待统一调整
3 days ago
lvjunjie a8053b23a0 refactor(nmNum): 将井储和表皮调整统一为单参数 LM 策略
- 按第一时间窗口形状误差的预计下降量选择参数、方向和初始步长
- 改善后步长翻倍,拒绝后减半,连续拒绝三次后切换参数
- 移除间距定向及初始形状变差时的扩步试探逻辑
- 同步拟合策略记录和中文日志资源
3 days ago
lvjunjie 8d807413c4 refactor(nmNum): 取消高度调整次数限制 2 weeks ago
lvjunjie 452123b06a refactor(nmNum): 将 LM 形状阶段改为逐参数单轮调整 2 weeks ago
lvjunjie 50f149970c refactor(nmNum): 简化 LM 形状阶段联合调整与停滞确认
- 移除时间窗口选参、三参数上限及子集枚举,每步联合调整全部有效非井储表皮参数
- 连续两次改善不足后刷新灵敏度,仅做一次全局联合确认,累计有效改善门槛设为 10%
- 放宽形状扩步条件,取消实际与预测改善比值限制,保留预测下降、步长和边界约束
- 确认步求解失败时有限重试,避免将失败误判为收敛
- 同步拟合轨迹元数据、尝试次数日志及中文翻译
2 weeks ago
lvjunjie 43b7c770ed perf(nmNum): 跳过 LM 形状阶段冻结参数的灵敏度试算
- 普通形状调整仅计算可调参数的灵敏度,跳过冻结的井储和表皮
- 保留井储表皮初调、回检及整体 LM 阶段的灵敏度重建逻辑
- 按当前子阶段所需参数列数显示重建结果,同步轨迹策略元数据和注释
2 weeks ago
lvjunjie 08de98335a refactor(nmNum): 分离 LM 三阶段拟合验收目标
- 渗透率预调整仅按上下误差验收,不再因总误差达标跳过预调整
- 移除形状阶段总误差上限、预测缩步及总误差达标提前退出
- 井储表皮回检仅按前期形状改善与整体形状容差验收
- 保留第三阶段按整体误差联合微调,同步阶段误差日志、轨迹元数据及中文翻译
2 weeks ago
lvjunjie 68c79378bb feat(nmNum): 优化 LM 前期形态引导与分阶段参数调整
- 按初始曲线间距固定井储和表皮方向,小步起调,形状变差时同向扩步
- 前期采用双曲线斜率形状误差,放大第二阶段灵敏度试探步长
- 渗透率预调整仅看上下偏移,表皮搜索下限设为 0
- 补充间距与方向诊断日志,保留第三阶段原有逻辑
2 weeks ago
lvjunjie 7e5255203e refactor(nmNum): 统一 LM 形状阶段联合搜索与扩步策略
- 移除每轮半长专项试探及配套渗透率补偿
- 保留局部窗口 Fisher 筛选,每轮全局步联合求解全部有效且有响应的非井储、表皮参数
- 将可靠方向扩步推广至所有形状调整参数,扩步失败或效果更差时保留原候选
- 保留井储表皮冻结、条件回检及完整轮次停滞确认
- 更新拟合轨迹策略元数据并清理失效中文翻译
2 weeks ago
lvjunjie f0fbef51ad feat: 优化 LM 分阶段拟合与自适应形状搜索
- 根据目标压力与导数的相对斜率差,依次调整井储和表皮
- 形状阶段冻结井储与表皮,前期走势退化时回检并保护整体误差与形状
- 增加每轮半长探索与渗透率补偿,对可靠的导流能力联合方向尝试扩步
- 取消形状阶段固定配额,按完整搜索轮次和灵敏度刷新确认停滞,独立分配整体 LM 预算
- 整体阶段联合优化全部有效自由参数,完善局部差分、边界回退和失败重试
- 同步预调整进度显示、拟合轨迹诊断及中文翻译
2 weeks ago
lvjunjie cd8bd9bafe perf(nmNum): 优化 LM 灵敏度重建时机与采样加密衔接
- 取消累计步长触发重建,连续两步实际改善不足预测的 25% 时刷新模型
- 将兜底刷新间隔调整为接受 10 步,避免微小预测量和单纯约束拒绝触发重建
- 合并临近的采样加密与灵敏度重建,提前考虑剩余求解预算
- 阶段切换时清除旧目标的预测失准计数,保留必要的失败恢复与停滞确认
- 补充重建原因日志、跟踪记录及中文翻译
2 weeks ago
lvjunjie 57c3d613f6 feat(nmNum): 新增 LM 高度与形状预调整的三阶段拟合
- 优先根据压力与压力导数的上下偏差调整渗透率,对齐曲线高度
- 在固定对数时间网格计算双曲线斜率残差,优先改善形状并限制整体偏离
- 按形状改善与剩余预算切换阶段,第三阶段沿用仅按整体误差接受候选的 LM
- 联合缓存数值与形状灵敏度,复用阶段间模型并同步已接受参数及曲线
- 迭代候选求解失败后交由信赖域缩步,避免相同参数重复重试
- 补充三阶段拟合日志、跟踪诊断字段及中文翻译
2 weeks ago
lvjunjie e027bf14d6 feat(nmNum): 新增 LM 可切换目标点分层采样
- 拟合界面新增采样方式选择,默认保留原有固定 80 点模式
- 按目标有效时间点逐层加密,补充时间窗口边界与重叠区锚点
- 结合对数时间权重计算窗口误差与 Fisher 矩阵,切层后重建灵敏度
- 使用全部有效目标点验收候选参数,按停滞情况与剩余预算推进层级
- 补充分层采样日志、全目标点对比误差及中文翻译
3 weeks ago
lvjunjie 45f73f2b26 fix(nmNum): 修复拟合参数同步与 LM 浮点边界问题
- 拟合结束后按模型实际参数刷新初值,无论成功或失败均重新生成范围
- 手工范围仅用于本轮拟合,切换目标井时读取模型最新参数
- 同步储层数据副本,避免后续保存设置覆盖为旧值
- 修正 LM 参数逆变换的端点与范围限制,避免浮点误差触发越界失败
3 weeks ago
lvjunjie 046453ff0a feat(nmNum): 新增时间窗口 Fisher 参数筛选
- 优先处理误差能量最大的时间窗口,结合 Fisher 矩阵与残差推荐参数
- 比较最多 3 个推荐参数的组合,按全局预测改善量选择调整方案
- 沿用 LM 步长约束和全局误差验收,无效或改善不足时切换窗口
- 增加全局选参回退,避免部分窗口无效导致提前终止
3 weeks ago
lvjunjie 8f4e35a3ed fix(nmNum): 补齐 LM 拟合翻译并移除时间分段日志 3 weeks ago
lvjunjie 90b6c5914a feat(nmNum): 增加 LM 固定对数时间窗口与分段误差诊断 3 weeks ago

Binary file not shown.

@ -664,6 +664,34 @@ Reason: %1</source>
</context>
<context>
<name>nmCalculationAutoFitLM</name>
<message>
<source>Failed to refresh the sampling layer from the current curve.</source>
<translation>无法根据当前曲线刷新采样层级。</translation>
</message>
<message>
<source>LM sampling refined: %1 / %2 target points; full-target error: %3</source>
<translation>LM 采样加密:%1 / %2 个目标点;全目标点误差:%3</translation>
</message>
<message>
<source>LM sampling: layered target points (%1 / %2); total-stage acceptance uses all valid target points.</source>
<translation>LM 采样方式:目标点分层采样(%1 / %2 点),整体阶段使用全部有效目标点验收。</translation>
</message>
<message>
<source>LM sampling: fixed 80 points (original mode).</source>
<translation>LM 采样方式:固定 80 点(原模式)。</translation>
</message>
<message>
<source>Full-target comparison error: initial=%1; final=%2</source>
<translation>全目标点对比误差:初始=%1;最终=%2</translation>
</message>
<message>
<source>Unavailable</source>
<translation>不可用</translation>
</message>
<message>
<source>Budget exhausted before the full sampling layer; convergence is not confirmed.</source>
<translation>预算已耗尽,尚未进入完整采样层,未确认收敛。</translation>
</message>
<message>
<source>=== User Stop Request Received ===</source>
<translation>=== 用户停止请求已接收 ===</translation>
@ -897,8 +925,8 @@ Reason: %1</source>
<translation>=== LM 自动拟合 - 局部最优 ===</translation>
</message>
<message>
<source>Max iterations reached. Best error: %1, Iterations: %2</source>
<translation>达到最大迭代次数。最佳误差:%1,迭代次数:%2</translation>
<source>Total-stage budget reached. Best error: %1, Cumulative iterations: %2</source>
<translation>整体阶段预算已用尽。最佳误差:%1,累计迭代次数:%2</translation>
</message>
<message>
<source>=== LM AUTOMATIC FITTING - MAX ITERATIONS ===</source>
@ -940,17 +968,13 @@ Reason: %1</source>
<source>=== Starting LM Main Loop ===</source>
<translation>=== 开始 LM 主循环 ===</translation>
</message>
<message>
<source>LM starting point error: %1; evaluation budget: %2</source>
<translation>LM 起点误差:%1;最大评估次数:%2</translation>
</message>
<message>
<source>No effective improvement for %1 consecutive steps; rebuilding sensitivity model for confirmation</source>
<translation>连续 %1 次无有效改善,正在重建灵敏度模型进行确认</translation>
</message>
<message>
<source>Effective improvement threshold: max(%1, %2% of baseline error); %3 consecutive ineffective steps trigger convergence confirmation</source>
<translation>有效改善阈值:取 %1 与基准误差的 %2% 中较大值;连续 %3 次无有效改善后进行收敛确认</translation>
<source>Total-stage effective improvement threshold: max(%1, %2% of baseline error); %3 consecutive ineffective steps trigger convergence confirmation</source>
<translation>整体阶段有效改善阈值:取 %1 与基准误差的 %2% 中较大值;连续 %3 次无有效改善后进行收敛确认</translation>
</message>
<message>
<source>Sensitivity probe accepted: error reduced to %1</source>
@ -1037,8 +1061,8 @@ Reason: %1</source>
<translation>检测到局部最优</translation>
</message>
<message>
<source>Maximum iterations reached</source>
<translation>达到最大迭代次数</translation>
<source>Total-stage iteration or evaluation budget reached</source>
<translation>整体阶段迭代或评估预算已用尽</translation>
</message>
<message>
<source>Stopped by user request</source>
@ -1056,6 +1080,242 @@ Reason: %1</source>
<source>Unknown reason</source>
<translation>未知原因</translation>
</message>
<message>
<source>Failed to load configuration from data manager</source>
<translation>从数据管理器加载配置失败</translation>
</message>
<message>
<source>Auto fitting is already running</source>
<translation>自动拟合正在运行</translation>
</message>
<message>
<source>No parameters enabled for optimization</source>
<translation>未启用任何优化参数</translation>
</message>
<message>
<source>Target LogLog data is empty or insufficient</source>
<translation>目标双对数数据为空或不足</translation>
</message>
<message>
<source>Target LogLog data arrays have inconsistent sizes</source>
<translation>目标双对数数据数组长度不一致</translation>
</message>
<message>
<source>Failed to apply final parameters: %1</source>
<translation>应用最终参数失败:%1</translation>
</message>
<message>
<source>Failed to apply final parameters due to unknown error</source>
<translation>应用最终参数时发生未知错误</translation>
</message>
<message>
<source>Target well name is empty</source>
<translation>目标井名称为空</translation>
</message>
<message>
<source>LM stage 1: align curve height using permeability only; no fixed evaluation limit, effective improvement threshold 10%.</source>
<translation>LM 阶段一:仅调整渗透率对齐曲线高度,不限制试算次数,有效改善门槛为 10%。</translation>
</message>
<message>
<source>Permeability alignment: k=%1, height error=%2, shape error=%3, result=%4</source>
<translation>渗透率对齐:k=%1,上下误差=%2,形状误差=%3,结果=%4</translation>
</message>
<message>
<source>LM stage 3: original LM fitting; accept by total error only.</source>
<translation>LM 阶段三:按原有 LM 拟合,仅依据整体误差接受调整。</translation>
</message>
<message>
<source>Sensitivity probe accepted: total error=%1</source>
<translation>采用灵敏度试算点:整体误差=%1</translation>
</message>
<message>
<source>Sensitivity probe accepted: early relative-slope matching error=%1</source>
<translation>灵敏度试算点已接受:前期相对斜率匹配误差=%1</translation>
</message>
<message>
<source>Shape stage ended: %1</source>
<translation>形状阶段结束:%1</translation>
</message>
<message>
<source>Permeability alignment ended: %1</source>
<translation>渗透率高度对齐结束:%1</translation>
</message>
<message>
<source>no feasible permeability step or step too small</source>
<translation>无可行的渗透率调整步或步长过小</translation>
</message>
<message>
<source>stopped by user</source>
<translation>用户停止</translation>
</message>
<message>
<source>pressure and derivative height directions conflict</source>
<translation>压力与压力导数的上下调整方向冲突</translation>
</message>
<message>
<source>curve height is approximately aligned</source>
<translation>曲线高度已大致对齐</translation>
</message>
<message>
<source>2 consecutive steps without effective height improvement</source>
<translation>连续 2 步上下偏差没有明显改善</translation>
</message>
<message>
<source>permeability reached its bound</source>
<translation>渗透率已到达范围边界</translation>
</message>
<message>
<source>Rebuilding sensitivity model: %1</source>
<translation>正在重建灵敏度模型:%1</translation>
</message>
<message>
<source>initial sensitivity model</source>
<translation>首次建立灵敏度模型</translation>
</message>
<message>
<source>sampling layer changed</source>
<translation>采样层级已改变</translation>
</message>
<message>
<source>no valid sensitivity model</source>
<translation>没有有效的灵敏度模型</translation>
</message>
<message>
<source>solver failure before stage switch</source>
<translation>阶段切换前发生求解失败</translation>
</message>
<message>
<source>10 accepted steps since last rebuild</source>
<translation>距上次重建已接受 10 步调整,进行兜底刷新</translation>
</message>
<message>
<source>confirm stagnation after ineffective steps</source>
<translation>连续无有效改善,重新确认停滞</translation>
</message>
<message>
<source>sampling refinement merged with pending rebuild</source>
<translation>提前加密采样,与本次重建合并处理</translation>
</message>
<message>
<source>no feasible descent step</source>
<translation>未找到可行的下降方向</translation>
</message>
<message>
<source>2 consecutive solver failures</source>
<translation>连续 2 次候选求解失败</translation>
</message>
<message>
<source>2 consecutive steps with actual improvement below 25% of prediction</source>
<translation>连续 2 步实际改善不足预测的 25%</translation>
</message>
<message>
<source>inaccurate model at minimum trust radius</source>
<translation>最小信赖半径下仍连续预测失准</translation>
</message>
<message>
<source>no remaining shape parameters</source>
<translation>没有其他勾选参数需要调整形状</translation>
</message>
<message>
<source>Early adjustment: select storage or skin by predicted first-window shape reduction; halve the step after rejection and switch after 3 consecutive rejections.</source>
<translation>前期预调整:按第一时间窗口形状误差的预计下降量选择井储或表皮;拒绝后步长减半,连续拒绝三次换参数。</translation>
</message>
<message>
<source>all wellbore parameters visited once</source>
<translation>已勾选的井储、表皮参数均已完成一轮调整</translation>
</message>
<message>
<source>Early wellbore adjustment ended: %1</source>
<translation>井储和表皮前期预调整结束:%1</translation>
</message>
<message>
<source>Shape fitting continues with storage and skin fixed; a conditional wellbore recheck follows.</source>
<translation>继续调整形状,暂时固定井储和表皮;随后按条件进行一次井储表皮回检。</translation>
</message>
<message>
<source>2 consecutive steps without effective early improvement</source>
<translation>连续 2 步前期误差没有明显改善</translation>
</message>
<message>
<source>no feasible early adjustment direction or parameter at bound</source>
<translation>没有明确可行的前期调整方向,或参数已到边界</translation>
</message>
<message>
<source>wellbore storage relative-slope matching</source>
<translation>井储前期相对斜率匹配</translation>
</message>
<message>
<source>skin relative-slope matching</source>
<translation>表皮前期相对斜率匹配</translation>
</message>
<message>
<source>Wellbore storage trial: relative-slope matching error=%1, C=%2 -&gt; %3</source>
<translation>井储试调:相对斜率匹配误差=%1,井储=%2 → %3</translation>
</message>
<message>
<source>Wellbore recheck: early shape error=%1, shape limit=%2.</source>
<translation>井储表皮回检:前期形状误差=%1,整体形状误差上限=%2。</translation>
</message>
<message>
<source>early relative-slope error restored within tolerance</source>
<translation>前期相对斜率匹配误差已恢复到容差范围内</translation>
</message>
<message>
<source>new sensitivity for wellbore recheck</source>
<translation>为井储表皮回检重建灵敏度</translation>
</message>
<message>
<source>wellbore recheck completed during sensitivity evaluation</source>
<translation>井储表皮回检在灵敏度评价期间完成</translation>
</message>
<message>
<source>Sensitivity probe accepted: shape error=%1</source>
<translation>接受灵敏度探针:形状误差=%1</translation>
</message>
<message>
<source>fresh sensitivity at single-parameter shape entry</source>
<translation>进入单参数形状调整时重建灵敏度</translation>
</message>
<message>
<source>Adaptive fitting counts: %1 shape attempts, %2 total-stage iterations, %3 total evaluations.</source>
<translation>自适应拟合统计:形状尝试 %1 次,整体阶段迭代 %2 次,累计评估 %3 次。</translation>
</message>
<message>
<source>LM stage 2: adjust each shape parameter once; double the step after improvement, halve it after rejection, and switch after 3 consecutive rejections.</source>
<translation>LM 第二阶段:逐个调整形状参数,每个参数只调一轮;改善后步长翻倍,拒绝后减半,连续拒绝三次后换下一个参数。</translation>
</message>
<message>
<source>LM starting point error: %1; independent total-stage evaluation budget: %2</source>
<translation>LM 起点误差:%1;整体阶段独立评估预算:%2</translation>
</message>
<message>
<source>Shape search step %1: shape error=%2, accumulated improvement=%3, required=%4.</source>
<translation>形状尝试 %1:形状误差=%2,累计改善=%3,有效改善门槛=%4。</translation>
</message>
<message>
<source>Total-stage budget starts now: %1 iterations, %2 evaluations; pre-adjustment is counted separately.</source>
<translation>整体阶段预算开始计数:%1 次迭代、%2 次评估;预调整单独计数。</translation>
</message>
<message>
<source>Unable to build a valid shape sensitivity model.</source>
<translation>无法建立有效的形状灵敏度模型。</translation>
</message>
<message>
<source>confirm shape stagnation after 2 ineffective steps</source>
<translation>连续两次形状改善不足,刷新灵敏度确认停滞</translation>
</message>
<message>
<source>full sensitivity at total-stage entry</source>
<translation>进入整体阶段时建立完整灵敏度</translation>
</message>
<message>
<source>all shape parameters visited once</source>
<translation>所有形状参数均已完成一轮调整</translation>
</message>
<message>
<source>no valid early sensitivity model</source>
<translation>无有效的早期灵敏度模型</translation>
</message>
</context>
<context>
<name>nmCalculationSolver</name>
@ -3762,6 +4022,22 @@ Supported types: Vertical, Vertical Fractured, and Horizontal Multi-Fractured We
</context>
<context>
<name>nmWxAutomaticFitting</name>
<message>
<source>LM sampling:</source>
<translation>LM 采样方式:</translation>
</message>
<message>
<source>Fixed sampling (80 points)</source>
<translation>固定采样(80 点)</translation>
</message>
<message>
<source>Layered target sampling</source>
<translation>目标点分层采样</translation>
</message>
<message>
<source>Layered sampling uses target-point subsets for directions and all valid target points for acceptance.</source>
<translation>逐层增加目标曲线采样点,并始终使用全部有效目标点判断拟合是否改善。</translation>
</message>
<message>
<source>Automatic fitting</source>
<translation>自动拟合</translation>
@ -4017,6 +4293,14 @@ Supported types: Vertical, Vertical Fractured, and Horizontal Multi-Fractured We
<source>The minimum value of %1 must be greater than zero for automatic fitting.</source>
<translation>自动拟合时,%1 的最小值和初始值必须大于零。</translation>
</message>
<message>
<source>Data manager is unavailable!</source>
<translation>数据管理器不可用!</translation>
</message>
<message>
<source>Gas pseudo-pressure data is unavailable or invalid.</source>
<translation>气体拟压力数据不可用或无效。</translation>
</message>
</context>
<context>
<name>nmWxAutomaticfitting</name>
@ -4299,6 +4583,10 @@ Supported types: Vertical, Vertical Fractured, and Horizontal Multi-Fractured We
<source>Fitting Curve</source>
<translation>拟合曲线</translation>
</message>
<message>
<source>Pre-adjustment</source>
<translation>预调整中</translation>
</message>
</context>
<context>
<name>nmWxChangeAnal</name>
@ -4974,6 +5262,14 @@ Please check your input coordinates.</source>
</context>
<context>
<name>nmWxGridDlg</name>
<message>
<source>Input Error</source>
<translation>输入错误</translation>
</message>
<message>
<source>Grid size must be a finite number greater than zero.</source>
<translation>网格大小必须是大于零的有限数值。</translation>
</message>
<message>
<source>GridType</source>
<translation>网格类型:</translation>

@ -11,14 +11,29 @@
#include "nmCalculation_global.h"
// 双对数曲线误差分解。total 是 LM 候选接受和排序的唯一依据,
// 其余诊断量用于有限差分灵敏度分析和信赖域选参。
// 固定对数时间窗口的误差诊断。时间边界为不含重叠区的基础边界;
// rmsError 是局部加权均方根,energy 是对整体残差能量的贡献。
struct AutoFitTimeWindowLM {
double timeMin;
double timeMax;
double weightSum;
double rmsError;
double energy;
AutoFitTimeWindowLM()
: timeMin(0.0), timeMax(0.0), weightSum(0.0), rmsError(0.0), energy(0.0)
{}
};
// 双对数曲线误差分解。预调整使用纵向偏差,形状阶段使用双曲线斜率残差,
// 整体阶段只使用 total 接受候选;时间窗口和残差用于 Fisher 选参。
struct AutoFitObjectiveBreakdownLM {
bool valid;
double total;
double pressureLoss;
double derivativeLoss;
QVector<double> residualVector;
QVector<AutoFitTimeWindowLM> timeWindows;
double verticalCommonBias;
double verticalLoss;
bool verticalReliable;
@ -26,6 +41,18 @@ struct AutoFitObjectiveBreakdownLM {
double horizontalLoss;
bool horizontalReliable;
bool registrationAmbiguous;
// 与整体误差共用 log-time 采样网格,斜率跨度约为完整对数时域的 10%。
QVector<double> shapeResiduals;
// 前期数值和形状残差沿用同一网格与斜率跨度,均含第一窗口权重。
// earlyParallelResiduals/Loss 沿用历史字段名,现为压力、导数分别匹配目标的形状误差。
// earlyParallelBias 仍记录相对斜率偏差,仅供诊断。
QVector<double> earlyValueResiduals;
QVector<double> earlyParallelResiduals;
double earlyValueLoss;
double earlyParallelLoss;
double earlyParallelBias;
double pressureVerticalBias;
double derivativeVerticalBias;
double shapeLoss;
double lateDerivativeSlopeBias;
double lateDerivativeTrendLoss;
@ -43,6 +70,11 @@ struct AutoFitObjectiveBreakdownLM {
, horizontalLoss(std::numeric_limits<double>::quiet_NaN())
, horizontalReliable(false)
, registrationAmbiguous(false)
, earlyValueLoss(std::numeric_limits<double>::quiet_NaN())
, earlyParallelLoss(std::numeric_limits<double>::quiet_NaN())
, earlyParallelBias(std::numeric_limits<double>::quiet_NaN())
, pressureVerticalBias(0.0)
, derivativeVerticalBias(0.0)
, shapeLoss(std::numeric_limits<double>::quiet_NaN())
, lateDerivativeSlopeBias(std::numeric_limits<double>::quiet_NaN())
, lateDerivativeTrendLoss(std::numeric_limits<double>::quiet_NaN())
@ -110,7 +142,7 @@ private:
AutoFitObjectiveBreakdownLM* breakdown,
QVector<QVector<double> >* curve,
int* elapsedMs);
double evaluateFitness(const QVector<double>& parameters);
double evaluateFitness(const QVector<double>& parameters, bool retrySolver = true);
void applyParametersToDataManager(const QVector<double>& parameters);
void updateReservoirParameters(const QVector<double>& parameters);
@ -146,7 +178,7 @@ private:
bool validateInitialValues() const;
bool validateSolverResult(const QVector<QVector<double> >& result) const;
double calculateLogLogCurveError(const QVector<QVector<double> >& target,
const QVector<QVector<double> >& result) const;
const QVector<QVector<double> >& result);
private:
bool m_isRunning;
@ -172,6 +204,9 @@ private:
QVector<double> m_parameterUpper;
QVector<int> m_enabledParamIndices;
QVector<QVector<double> > m_targetLogLogData;
// 首次有效评价后固定,后续候选必须覆盖同一时间区间。
double m_comparisonTimeMin;
double m_comparisonTimeMax;
QString m_targetWellName;
int m_maxIterations;

@ -47,13 +47,6 @@ public:
nmDataAttribute& getPorosityMin();
void setPorosityMin(const nmDataAttribute& porosityMin);
// Getter and Setter for swiMax
nmDataAttribute& getSwiMax();
void setSwiMax(const nmDataAttribute& swiMax);
// Getter and Setter for swiMin
nmDataAttribute& getSwiMin();
void setSwiMin(const nmDataAttribute& swiMin);
// Getter and Setter for fractureConductivityMax
nmDataAttribute& getFractureConductivityMax();
void setFractureConductivityMax(const nmDataAttribute& fractureConductivityMax);
@ -95,8 +88,6 @@ public:
bool getPorositySelected() const;
void setPorositySelected(bool selected);
bool getSwiSelected() const;
void setSwiSelected(bool selected);
bool getFractureConductivitySelected() const;
void setFractureConductivitySelected(bool selected);
@ -110,7 +101,6 @@ private:
nmDataAttribute m_skinMax;
nmDataAttribute m_wellboreStorageMax;
nmDataAttribute m_porosityMax;
nmDataAttribute m_swiMax;
nmDataAttribute m_fractureConductivityMax;
nmDataAttribute m_fractureHalfLengthMax;
@ -119,7 +109,6 @@ private:
nmDataAttribute m_skinMin;
nmDataAttribute m_wellboreStorageMin;
nmDataAttribute m_porosityMin;
nmDataAttribute m_swiMin;
nmDataAttribute m_fractureConductivityMin;
nmDataAttribute m_fractureHalfLengthMin;
@ -134,7 +123,6 @@ private:
bool m_skinSelected; // 是否选择表皮系数进行拟合
bool m_wellboreStorageSelected; // 是否选择井筒储集系数进行拟合
bool m_porositySelected; // 是否选择孔隙度进行拟合
bool m_swiSelected; // 是否选择初始含水饱和度进行拟合
bool m_fractureConductivitySelected; // 是否选择裂缝导流能力进行拟合
bool m_fractureHalfLengthSelected; // 是否选择裂缝半长进行拟合
};

@ -62,13 +62,12 @@ private:
void updateRangeForParameter(int parameterIndex, double centerValue);
void setParameterRange(int parameterIndex, double minValue, double maxValue);
bool getPhysicalParameterRange(int parameterIndex, double& minValue, double& maxValue);
void normalizeSavedParameterRanges();
bool validateParameterTable(QString& errorMessage, int parameterIndex = -1);
void startAutoFitting(const QVector<QVector<double>>& targetData, const QStringList& selectedParams, const QString& targetWellName);
void cleanupFitting();
void updateBestParametersToTable();
void refreshParametersFromModel();
private:
@ -90,7 +89,6 @@ private:
QCheckBox* m_sCheckBox; // 表皮系数
QCheckBox* m_cCheckBox; // 井筒储集系数
QCheckBox* m_phiCheckBox; // 孔隙度
QCheckBox* m_swiCheckBox; // 初始含水饱和度
QCheckBox* m_dfcCheckBox; // 裂缝导流能力
QCheckBox* m_fractureHalfLengthCheckBox; // 裂缝半长
@ -101,10 +99,6 @@ private:
// 数据成员
nmDataReservoir reservoirData;
QVector<nmDataVerticalWell> m_verticalWells;
QVector<nmDataHorizontalWell> m_horizontalWells;
QVector<nmDataVerticalFracturedWell> m_verticalFracturedWells;
QVector<nmDataHorizontalFracturedWell> m_horizontalFracturedWells;
nmDataAutomaticFitting automaticFittingData;
// 自动拟合相关成员

File diff suppressed because it is too large Load Diff

@ -2721,7 +2721,7 @@ void nmCalculationAutoFitPSO::loadParameterBounds()
m_parameterSelected[1] = fittingData.getSkinSelected();
m_parameterSelected[2] = fittingData.getWellboreStorageSelected();
m_parameterSelected[3] = fittingData.getPorositySelected();
m_parameterSelected[4] = fittingData.getSwiSelected();
m_parameterSelected[4] = false; // 含水饱和度固定,保留索引以兼容物理参数和追踪记录。
// 获取参数边界
m_parameterLower.resize(5);
@ -2739,8 +2739,8 @@ void nmCalculationAutoFitPSO::loadParameterBounds()
m_parameterLower[3] = fittingData.getPorosityMin().getValue().toDouble();
m_parameterUpper[3] = fittingData.getPorosityMax().getValue().toDouble();
m_parameterLower[4] = fittingData.getSwiMin().getValue().toDouble();
m_parameterUpper[4] = fittingData.getSwiMax().getValue().toDouble();
m_parameterLower[4] = dataManager->getReservoirDataCopy().getSwi().getValue().toDouble();
m_parameterUpper[4] = m_parameterLower[4];
// 更新启用参数索引
m_enabledParamIndices.clear();
@ -4184,9 +4184,6 @@ void nmCalculationAutoFitPSO::updateReservoirParameters(const QVector<double>& p
reservoirData.getPorosity().setValue(value);
break;
case 4: // 初始含水饱和度
reservoirData.getSwi().setValue(value);
break;
}
paramIndex++;

@ -7,7 +7,6 @@ nmDataAutomaticFitting::nmDataAutomaticFitting()
m_skinSelected = true; // 默认选中
m_wellboreStorageSelected = true; // 默认选中
m_porositySelected = true; // 默认选中
m_swiSelected = false; // 默认不选中
m_fractureConductivitySelected = false; // 仅压裂井可用,默认不选中
m_fractureHalfLengthSelected = false; // 仅压裂井可用,默认不选中
@ -16,7 +15,6 @@ nmDataAutomaticFitting::nmDataAutomaticFitting()
m_skinMax = nmDataAttribute("Skin Max", QVariant(), "");
m_wellboreStorageMax = nmDataAttribute("Wellbore Storage Max", QVariant(), "m^3/MPa");
m_porosityMax = nmDataAttribute("Porosity Max", QVariant(), "");
m_swiMax = nmDataAttribute("Swi Max", QVariant(), "");
m_fractureConductivityMax = nmDataAttribute("Fracture Conductivity Max", QVariant(), "md.m");
m_fractureHalfLengthMax = nmDataAttribute("Fracture Half Length Max", QVariant(), "m");
@ -24,7 +22,6 @@ nmDataAutomaticFitting::nmDataAutomaticFitting()
m_skinMin = nmDataAttribute("Skin Min", QVariant(), "");
m_wellboreStorageMin = nmDataAttribute("Wellbore Storage Min", QVariant(), "m^3/MPa");
m_porosityMin = nmDataAttribute("Porosity Min", QVariant(), "");
m_swiMin = nmDataAttribute("Swi Min", QVariant(), "");
m_fractureConductivityMin = nmDataAttribute("Fracture Conductivity Min", QVariant(), "mD.m");
m_fractureHalfLengthMin = nmDataAttribute("Fracture Half Length Min", QVariant(), "m");
@ -52,7 +49,6 @@ nmDataAutomaticFitting& nmDataAutomaticFitting::operator=(const nmDataAutomaticF
m_skinSelected = other.m_skinSelected;
m_wellboreStorageSelected = other.m_wellboreStorageSelected;
m_porositySelected = other.m_porositySelected;
m_swiSelected = other.m_swiSelected;
m_fractureConductivitySelected = other.m_fractureConductivitySelected;
m_fractureHalfLengthSelected = other.m_fractureHalfLengthSelected;
@ -61,7 +57,6 @@ nmDataAutomaticFitting& nmDataAutomaticFitting::operator=(const nmDataAutomaticF
m_skinMax = other.m_skinMax;
m_wellboreStorageMax = other.m_wellboreStorageMax;
m_porosityMax = other.m_porosityMax;
m_swiMax = other.m_swiMax;
m_fractureConductivityMax = other.m_fractureConductivityMax;
m_fractureHalfLengthMax = other.m_fractureHalfLengthMax;
@ -70,7 +65,6 @@ nmDataAutomaticFitting& nmDataAutomaticFitting::operator=(const nmDataAutomaticF
m_skinMin = other.m_skinMin;
m_wellboreStorageMin = other.m_wellboreStorageMin;
m_porosityMin = other.m_porosityMin;
m_swiMin = other.m_swiMin;
m_fractureConductivityMin = other.m_fractureConductivityMin;
m_fractureHalfLengthMin = other.m_fractureHalfLengthMin;
@ -92,7 +86,6 @@ rapidjson::Value nmDataAutomaticFitting::ToJsonValue(rapidjson::Document::Alloca
fittingObject.AddMember("SkinSelected", m_skinSelected, allocator);
fittingObject.AddMember("WellboreStorageSelected", m_wellboreStorageSelected, allocator);
fittingObject.AddMember("PorositySelected", m_porositySelected, allocator);
fittingObject.AddMember("SwiSelected", m_swiSelected, allocator);
fittingObject.AddMember("FractureConductivitySelected", m_fractureConductivitySelected, allocator);
fittingObject.AddMember("FractureHalfLengthSelected", m_fractureHalfLengthSelected, allocator);
@ -101,7 +94,6 @@ rapidjson::Value nmDataAutomaticFitting::ToJsonValue(rapidjson::Document::Alloca
fittingObject.AddMember("SkinMax", m_skinMax.ToJsonValue(allocator), allocator);
fittingObject.AddMember("WellboreStorageMax", m_wellboreStorageMax.ToJsonValue(allocator), allocator);
fittingObject.AddMember("PorosityMax", m_porosityMax.ToJsonValue(allocator), allocator);
fittingObject.AddMember("SwiMax", m_swiMax.ToJsonValue(allocator), allocator);
fittingObject.AddMember("FractureConductivityMax", m_fractureConductivityMax.ToJsonValue(allocator), allocator);
fittingObject.AddMember("FractureHalfLengthMax", m_fractureHalfLengthMax.ToJsonValue(allocator), allocator);
@ -110,7 +102,6 @@ rapidjson::Value nmDataAutomaticFitting::ToJsonValue(rapidjson::Document::Alloca
fittingObject.AddMember("SkinMin", m_skinMin.ToJsonValue(allocator), allocator);
fittingObject.AddMember("WellboreStorageMin", m_wellboreStorageMin.ToJsonValue(allocator), allocator);
fittingObject.AddMember("PorosityMin", m_porosityMin.ToJsonValue(allocator), allocator);
fittingObject.AddMember("SwiMin", m_swiMin.ToJsonValue(allocator), allocator);
fittingObject.AddMember("FractureConductivityMin", m_fractureConductivityMin.ToJsonValue(allocator), allocator);
fittingObject.AddMember("FractureHalfLengthMin", m_fractureHalfLengthMin.ToJsonValue(allocator), allocator);
@ -138,9 +129,6 @@ void nmDataAutomaticFitting::FromJsonValue(const rapidjson::Value& jsonValue)
if (jsonValue.HasMember("PorositySelected") && jsonValue["PorositySelected"].IsBool()) {
m_porositySelected = jsonValue["PorositySelected"].GetBool();
}
if (jsonValue.HasMember("SwiSelected") && jsonValue["SwiSelected"].IsBool()) {
m_swiSelected = jsonValue["SwiSelected"].GetBool();
}
if (jsonValue.HasMember("FractureConductivitySelected") && jsonValue["FractureConductivitySelected"].IsBool()) {
m_fractureConductivitySelected = jsonValue["FractureConductivitySelected"].GetBool();
}
@ -163,9 +151,6 @@ void nmDataAutomaticFitting::FromJsonValue(const rapidjson::Value& jsonValue)
if (jsonValue.HasMember("PorosityMax") && jsonValue["PorosityMax"].IsObject()) {
m_porosityMax.FromJsonValue(jsonValue["PorosityMax"]);
}
if (jsonValue.HasMember("SwiMax") && jsonValue["SwiMax"].IsObject()) {
m_swiMax.FromJsonValue(jsonValue["SwiMax"]);
}
if (jsonValue.HasMember("FractureConductivityMax") && jsonValue["FractureConductivityMax"].IsObject()) {
m_fractureConductivityMax.FromJsonValue(jsonValue["FractureConductivityMax"]);
}
@ -187,9 +172,6 @@ void nmDataAutomaticFitting::FromJsonValue(const rapidjson::Value& jsonValue)
if (jsonValue.HasMember("PorosityMin") && jsonValue["PorosityMin"].IsObject()) {
m_porosityMin.FromJsonValue(jsonValue["PorosityMin"]);
}
if (jsonValue.HasMember("SwiMin") && jsonValue["SwiMin"].IsObject()) {
m_swiMin.FromJsonValue(jsonValue["SwiMin"]);
}
if (jsonValue.HasMember("FractureConductivityMin") && jsonValue["FractureConductivityMin"].IsObject()) {
m_fractureConductivityMin.FromJsonValue(jsonValue["FractureConductivityMin"]);
}
@ -226,8 +208,6 @@ void nmDataAutomaticFitting::setWellboreStorageSelected(bool selected) { m_wellb
bool nmDataAutomaticFitting::getPorositySelected() const { return m_porositySelected; }
void nmDataAutomaticFitting::setPorositySelected(bool selected) { m_porositySelected = selected; }
bool nmDataAutomaticFitting::getSwiSelected() const { return m_swiSelected; }
void nmDataAutomaticFitting::setSwiSelected(bool selected) { m_swiSelected = selected; }
bool nmDataAutomaticFitting::getFractureConductivitySelected() const { return m_fractureConductivitySelected; }
void nmDataAutomaticFitting::setFractureConductivitySelected(bool selected) { m_fractureConductivitySelected = selected; }
@ -249,8 +229,6 @@ void nmDataAutomaticFitting::setWellboreStorageMax(const nmDataAttribute& wellbo
nmDataAttribute& nmDataAutomaticFitting::getPorosityMax() { return m_porosityMax; }
void nmDataAutomaticFitting::setPorosityMax(const nmDataAttribute& porosityMax) { m_porosityMax = porosityMax; }
nmDataAttribute& nmDataAutomaticFitting::getSwiMax() { return m_swiMax; }
void nmDataAutomaticFitting::setSwiMax(const nmDataAttribute& swiMax) { m_swiMax = swiMax; }
nmDataAttribute& nmDataAutomaticFitting::getFractureConductivityMax() { return m_fractureConductivityMax; }
void nmDataAutomaticFitting::setFractureConductivityMax(const nmDataAttribute& fractureConductivityMax) { m_fractureConductivityMax = fractureConductivityMax; }
@ -271,8 +249,6 @@ void nmDataAutomaticFitting::setWellboreStorageMin(const nmDataAttribute& wellbo
nmDataAttribute& nmDataAutomaticFitting::getPorosityMin() { return m_porosityMin; }
void nmDataAutomaticFitting::setPorosityMin(const nmDataAttribute& porosityMin) { m_porosityMin = porosityMin; }
nmDataAttribute& nmDataAutomaticFitting::getSwiMin() { return m_swiMin; }
void nmDataAutomaticFitting::setSwiMin(const nmDataAttribute& swiMin) { m_swiMin = swiMin; }
nmDataAttribute& nmDataAutomaticFitting::getFractureConductivityMin() { return m_fractureConductivityMin; }
void nmDataAutomaticFitting::setFractureConductivityMin(const nmDataAttribute& fractureConductivityMin) { m_fractureConductivityMin = fractureConductivityMin; }

@ -4,7 +4,10 @@
#include "nmDataReservoir.h"
nmDataHorizontalFracturedWell::nmDataHorizontalFracturedWell() : nmDataHorizontalWell() {
m_pReservoir = nmDataAnalyzeManager::getCurrentInstance()->getReservoirData();
m_pReservoir = nmDataWellBase::m_pReservoir;
// 候选加载阶段尚未发布储层对象,裂缝高度随后会由 JSON 恢复。
const QVariant oInitialFractureHeight = m_pReservoir == nullptr
? QVariant(0.0) : m_pReservoir->getThickness().getValue();
double horizontalSectionLength = 200.0;
m_wellLength.setValue(horizontalSectionLength);
@ -15,7 +18,7 @@ nmDataHorizontalFracturedWell::nmDataHorizontalFracturedWell() : nmDataHorizonta
m_dFc = nmDataAttribute("dFc", 1000, "md.m", UNIT_TYPE_CONDUCTIVITY, QStringList(), QStringList() << "md.ft" << "md.m" << "m^3");
m_numberOfFractures = nmDataAttribute("Number of fractures", 15, "", UNIT_TYPE_DIMENSIONLESS, QStringList(), QStringList());
m_fractureHalfLength = nmDataAttribute("Fracture half length", 50.0, "m", UNIT_TYPE_LENGTH, QStringList(), QStringList() << "ft" << "m" << "cm" << "mm" << "in" << "0.1 in" << "mile" << "km");
m_fractureHeight = nmDataAttribute("Fracture height", m_pReservoir->getThickness().getValue().toDouble(), "m", UNIT_TYPE_LENGTH, QStringList(), QStringList() << "ft" << "m" << "cm" << "mm" << "in" << "0.1 in" << "mile" << "km");
m_fractureHeight = nmDataAttribute("Fracture height", oInitialFractureHeight.toDouble(), "m", UNIT_TYPE_LENGTH, QStringList(), QStringList() << "ft" << "m" << "cm" << "mm" << "in" << "0.1 in" << "mile" << "km");
m_fractureMidPointHeight = nmDataAttribute("Fracture mid-point height", (m_fractureHeight.getValue().toDouble())/2, "m", UNIT_TYPE_LENGTH, QStringList(), QStringList() << "ft" << "m" << "cm" << "mm" << "in" << "0.1 in" << "mile" << "km");
m_width = nmDataAttribute("Width", 0.00328084, "m", UNIT_TYPE_LENGTH, QStringList(), QStringList() << "ft" << "m" << "cm" << "mm" << "in" << "0.1 in" << "mile" << "km");
m_fractureAngle = nmDataAttribute("Fracture angle", 90.0000, "o", UNIT_TYPE_ANGLE, QStringList(), QStringList() << "o" << "radian");

@ -6,12 +6,15 @@
#include "nmDataReservoir.h"
nmDataVerticalFracturedWell::nmDataVerticalFracturedWell() : nmDataVerticalWell() {
m_pReservoir = nmDataAnalyzeManager::getCurrentInstance()->getReservoirData();
m_pReservoir = nmDataWellBase::m_pReservoir;
// 候选加载阶段尚未发布储层对象,裂缝高度随后会由 JSON 恢复。
const QVariant oInitialFractureHeight = m_pReservoir == nullptr
? QVariant(0.0) : m_pReservoir->getThickness().getValue();
m_fractureModel = nmDataAttribute("Fracture model", "Finite conductivity", "", UNIT_TYPE_DIMENSIONLESS, QStringList() << "Infinite conductivity", QStringList());
m_dFc = nmDataAttribute("dFc", 1000.0, "md.m", UNIT_TYPE_CONDUCTIVITY, QStringList(), QStringList() << "md.ft" << "md.m" << "m^3");
m_fractureHalfLength = nmDataAttribute("Fracture half length", 20.0, "m", UNIT_TYPE_LENGTH, QStringList(), QStringList() << "ft" << "m" << "cm" << "mm" << "in" << "0.1 in" << "mile" << "km");
m_fractureHeight = nmDataAttribute("Fracture height", m_pReservoir->getThickness().getValue() , "m", UNIT_TYPE_LENGTH, QStringList(), QStringList() << "ft" << "m" << "cm" << "mm" << "in" << "0.1 in" << "mile" << "km");
m_fractureHeight = nmDataAttribute("Fracture height", oInitialFractureHeight, "m", UNIT_TYPE_LENGTH, QStringList(), QStringList() << "ft" << "m" << "cm" << "mm" << "in" << "0.1 in" << "mile" << "km");
m_fractureMidPointHeight = nmDataAttribute("Fracture mid-point height", (m_fractureHeight.getValue().toDouble())/2 , "m", UNIT_TYPE_LENGTH, QStringList(), QStringList() << "ft" << "m" << "cm" << "mm" << "in" << "0.1 in" << "mile" << "km");
m_width = nmDataAttribute("Width", 0.00328084, "m", UNIT_TYPE_LENGTH, QStringList(), QStringList() << "ft" << "m" << "cm" << "mm" << "in" << "0.1 in" << "mile" << "km");
m_fractureAngle = nmDataAttribute("Fracture angle", 0.0000, "o", UNIT_TYPE_ANGLE, QStringList(), QStringList() << "o" << "radian");

@ -119,7 +119,7 @@ void nmWxAutomaticFitting::updateParameterVisibility(QTableWidget* table, NM_SOL
setParameterRowVisible(table, row, true);
}
setParameterRowVisible(table, 4, eType == SMT_Oil_Water_TwoPhase); // Swi
setParameterRowVisible(table, 4, false); // 原含水饱和度索引保留,不再参与拟合。
// Dfc 只属于垂直压裂井和多段压裂水平井。普通井隐藏并取消勾选,
// 防止切换目标井后不可见的裂缝参数仍进入拟合参数向量。
@ -140,16 +140,16 @@ void nmWxAutomaticFitting::updateParameterVisibility(QTableWidget* table, NM_SOL
renumberVisibleParameterRows(table);
}
// 获取参数的系统物理边界,并为 Swi 叠加当前储层饱和度约束。
// 获取可拟合参数的系统物理边界,索引 4 不再作为拟合参数。
bool nmWxAutomaticFitting::getPhysicalParameterRange(int parameterIndex,
double& minValue, double& maxValue)
{
static const char* parameterNames[] = {
"Result_K", "Result_W_Skin", "Result_W_C", "Result_phi",
"Result_Swi", "Result_W_Dfc",
"", "Result_W_Dfc",
"W_FractureHalfLength"
};
if(parameterIndex < 0 || parameterIndex >= 7) {
if(parameterIndex < 0 || parameterIndex >= 7 || parameterIndex == 4) {
return false;
}
@ -157,20 +157,12 @@ bool nmWxAutomaticFitting::getPhysicalParameterRange(int parameterIndex,
return false;
}
if(parameterIndex == 4) {
double soi = reservoirData.getSoi().getValue().toDouble();
double sgi = reservoirData.getSgi().getValue().toDouble();
if(nmAutoFitUiIsFinite(soi) && nmAutoFitUiIsFinite(sgi)) {
if(soi < 0.0 || sgi < 0.0 || soi + sgi > 1.0) {
// Soi+Sgi 超过 1 时没有可行的 Swi,固定到物理下限,避免继续搜索非法区间。
minValue = 0.0;
maxValue = 0.0;
} else {
maxValue = qMin(maxValue, 1.0 - soi - sgi);
}
}
// 自动拟合的表皮搜索下界固定为 0,自动范围和手工输入校验使用同一边界。
if(parameterIndex == 1) {
minValue = 0.0;
}
return true;
}
@ -214,10 +206,6 @@ void nmWxAutomaticFitting::setParameterRange(int parameterIndex,
automaticFittingData.getPorosityMin().setValue(minValue);
automaticFittingData.getPorosityMax().setValue(maxValue);
break;
case 4:
automaticFittingData.getSwiMin().setValue(minValue);
automaticFittingData.getSwiMax().setValue(maxValue);
break;
case 5:
automaticFittingData.getFractureConductivityMin().setValue(minValue);
automaticFittingData.getFractureConductivityMax().setValue(maxValue);
@ -233,8 +221,8 @@ void nmWxAutomaticFitting::setParameterRange(int parameterIndex,
m_updatingParameterRanges = wasUpdatingRanges;
}
// 根据当前初值生成建议搜索范围,并始终截断在系统物理边界内。skin 使用
// 加减固定宽度,其余正值参数使用倍率范围;该规则在首次加载和拟合完成后复用。
// 根据当前初值生成建议搜索范围,并始终截断在拟合边界内。skin 下界为 0,
// 上界按初值加固定宽度,其余正值参数使用倍率范围;每次打开窗口和拟合完成后复用。
void nmWxAutomaticFitting::updateRangeForParameter(int parameterIndex,
double centerValue)
{
@ -271,19 +259,14 @@ void nmWxAutomaticFitting::updateRangeForParameter(int parameterIndex,
double newMax = physicalMax;
if(parameterIndex == 1) {
const double skinHalfRange = 10.0;
newMin = qMax(physicalMin, reference - skinHalfRange);
newMin = physicalMin;
newMax = qMin(physicalMax, reference + skinHalfRange);
} else if(reference > 0.0
&& !(parameterIndex == 4 && centerValue <= 0.0)
&& !(parameterIndex == 5 && centerValue <= 0.0)) {
const double lowerFactor = 0.1;
const double upperFactor = 10.0;
newMin = qMax(physicalMin, reference * lowerFactor);
newMax = qMin(physicalMax, reference * upperFactor);
} else if(parameterIndex == 4) {
// 没有可靠 Swi 初值时,不把搜索范围压缩到零附近。
newMin = physicalMin;
newMax = physicalMax;
} else if(parameterIndex == 5) {
// Dfc=0 表示无限导流,不存在以零为中心的连续倍率范围。
newMin = physicalMin;
@ -301,7 +284,7 @@ void nmWxAutomaticFitting::updateRangeForParameter(int parameterIndex,
}
}
// 首次进入自动范围模式时,按当前表格中的初值为所有参数建立建议范围。
// 按当前表格中的初值为所有参数建立建议范围,每次打开窗口和拟合完成后共用。
void nmWxAutomaticFitting::initializeSuggestedParameterRanges()
{
if(!m_parameterTable) {
@ -328,57 +311,6 @@ void nmWxAutomaticFitting::initializeSuggestedParameterRanges()
}
}
// 校正已保存的范围:保留物理边界内的用户区间,无交集时按当前初值生成兜底区间。
void nmWxAutomaticFitting::normalizeSavedParameterRanges()
{
if(!m_parameterTable) {
return;
}
for(int parameterIndex = 0; parameterIndex < 7; ++parameterIndex) {
QTableWidgetItem* minItem = m_parameterTable->item(parameterIndex, 2);
QTableWidgetItem* maxItem = m_parameterTable->item(parameterIndex, 4);
QTableWidgetItem* initialItem = m_parameterTable->item(parameterIndex, 3);
if(!minItem || !maxItem || !initialItem) {
continue;
}
double physicalMin = 0.0;
double physicalMax = 0.0;
if(!getPhysicalParameterRange(parameterIndex, physicalMin, physicalMax)) {
continue;
}
bool savedMinOk = false;
bool savedMaxOk = false;
const double savedMin = minItem->text().toDouble(&savedMinOk);
const double savedMax = maxItem->text().toDouble(&savedMaxOk);
// 旧项目没有 Dfc 字段时,nmDataAttribute 会表现为 0~0。Dfc=0 在求解器中
// 表示无限导流,不能作为连续拟合区间,因此按当前压裂井初值重新建范围。
const bool savedRangeValid = savedMinOk && savedMaxOk
&& nmAutoFitUiIsFinite(savedMin) && nmAutoFitUiIsFinite(savedMax)
&& savedMax >= savedMin
&& !(parameterIndex == 5 && savedMax <= 1.0e-10);
if(savedRangeValid && physicalMax >= physicalMin) {
const double clippedMin = qMax(savedMin, physicalMin);
const double clippedMax = qMin(savedMax, physicalMax);
if(clippedMax >= clippedMin) {
setParameterRange(parameterIndex, clippedMin, clippedMax);
continue;
}
}
// 已保存范围无效或与物理边界无交集时,按数据对象初值重新生成。
bool initialOk = false;
const double initialValue = initialItem->text().toDouble(&initialOk);
if(initialOk && nmAutoFitUiIsFinite(initialValue)) {
updateRangeForParameter(parameterIndex, initialValue);
} else if(physicalMax >= physicalMin) {
setParameterRange(parameterIndex, physicalMin, physicalMax);
}
}
}
// 校验当前表格中的参数范围;parameterIndex 为 -1 时检查所有可见参数行。
bool nmWxAutomaticFitting::validateParameterTable(QString& errorMessage, int parameterIndex)
{
@ -393,7 +325,7 @@ bool nmWxAutomaticFitting::validateParameterTable(QString& errorMessage, int par
static const char* parameterNames[] = {
"Permeability", "Skin", "Wellbore storage", "Porosity",
"Swi", "Fracture conductivity",
"", "Fracture conductivity",
"Fracture half length"
};
@ -484,30 +416,10 @@ nmWxAutomaticFitting::nmWxAutomaticFitting(QWidget *parent)
{
DEBUG_UI(QString("AutoFitting Constructor: this=0x%1").arg((quintptr)this, 0, 16));
// 加载已有井数据
QVector<nmDataWellBase*> listWellData = nmDataAnalyzeManager::getCurrentInstance()->getWellDataList();
// 遍历并分类井数据
foreach (nmDataWellBase* well, listWellData) {
if (auto vfWell = dynamic_cast<nmDataVerticalFracturedWell*>(well)) {
m_verticalFracturedWells.append(*vfWell);
}
else if (auto hfWell = dynamic_cast<nmDataHorizontalFracturedWell*>(well)) {
m_horizontalFracturedWells.append(*hfWell);
}
else if (auto vWell = dynamic_cast<nmDataVerticalWell*>(well)) {
m_verticalWells.append(*vWell);
}
else if (auto hWell = dynamic_cast<nmDataHorizontalWell*>(well)) {
m_horizontalWells.append(*hWell);
}
}
// 获取数据
nmDataAnalyzeManager* pManager = nmDataAnalyzeManager::getCurrentInstance();
reservoirData = pManager->getReservoirDataCopy();
automaticFittingData = pManager->getAutomaticFittingDataCopy();
const bool hasSavedFittingData = pManager && pManager->getAutomaticFittingData() != nullptr;
// 自动范围始终开启;用户在表格中修改上下限后,itemChanged 会临时切换为手工范围。
m_autoParameterRanges = true;
@ -519,11 +431,8 @@ nmWxAutomaticFitting::nmWxAutomaticFitting(QWidget *parent)
if(m_targetWellCombo->count() > 0) {
onWellSelected(0); // 默认选中第一口井
}
if(!hasSavedFittingData) {
initializeSuggestedParameterRanges();
} else {
normalizeSavedParameterRanges();
}
// 每次打开都按当前模型参数生成范围,不沿用上次的上下限。
initializeSuggestedParameterRanges();
m_updatingParameterRanges = false;
DEBUG_UI("AutoFitting Constructor completed");
@ -650,15 +559,7 @@ void nmWxAutomaticFitting::setupParameterTable()
m_parameterTable->setItem(3, 4, new QTableWidgetItem(QString::number(automaticFittingData.getPorosityMax().getValue().toDouble())));
m_parameterTable->setItem(3, 5, new QTableWidgetItem(""));
// 初始含水饱和度 (Swi)
m_parameterTable->setItem(4, 0, new QTableWidgetItem("5"));
m_swiCheckBox = new QCheckBox(tr("Swi"));
m_swiCheckBox->setChecked(automaticFittingData.getSwiSelected());
m_parameterTable->setCellWidget(4, 1, m_swiCheckBox);
m_parameterTable->setItem(4, 2, new QTableWidgetItem(QString::number(automaticFittingData.getSwiMin().getValue().toDouble())));
m_parameterTable->setItem(4, 3, new QTableWidgetItem(QString::number(reservoirData.getSwi().getValue().toDouble())));
m_parameterTable->setItem(4, 4, new QTableWidgetItem(QString::number(automaticFittingData.getSwiMax().getValue().toDouble())));
m_parameterTable->setItem(4, 5, new QTableWidgetItem(""));
// 索引 4 留空,保持裂缝参数与优化器的现有索引一致。
// 裂缝导流能力 (Dfc)。该行只对压裂井显示,初值在 onWellSelected() 中
// 从当前目标井读取,其他裂缝几何参数保持固定,不进入自动拟合。
@ -891,7 +792,6 @@ void nmWxAutomaticFitting::onReverseSelection()
if(!m_parameterTable->isRowHidden(1)) m_sCheckBox->setChecked(!m_sCheckBox->isChecked());
if(!m_parameterTable->isRowHidden(2)) m_cCheckBox->setChecked(!m_cCheckBox->isChecked());
if(!m_parameterTable->isRowHidden(3)) m_phiCheckBox->setChecked(!m_phiCheckBox->isChecked());
if(!m_parameterTable->isRowHidden(4)) m_swiCheckBox->setChecked(!m_swiCheckBox->isChecked());
if(!m_parameterTable->isRowHidden(5)) m_dfcCheckBox->setChecked(!m_dfcCheckBox->isChecked());
if(!m_parameterTable->isRowHidden(6)) m_fractureHalfLengthCheckBox->setChecked(!m_fractureHalfLengthCheckBox->isChecked());
}
@ -902,7 +802,7 @@ void nmWxAutomaticFitting::onParameterTableItemChanged(QTableWidgetItem* item)
return;
}
// 用户改动范围后,切井和拟合结果不再自动覆盖这组手工范围。
// 手工范围用于本轮拟合,切井时保留;拟合完成后再按最新参数自动生成范围。
if(item->column() == 2 || item->column() == 4) {
m_autoParameterRanges = false;
}
@ -1001,7 +901,7 @@ void nmWxAutomaticFitting::onAccept()
// 检查是否有参数被选中
bool hasSelectedParams = m_kCheckBox->isChecked() || m_sCheckBox->isChecked() ||
m_cCheckBox->isChecked() || m_phiCheckBox->isChecked() ||
m_swiCheckBox->isChecked() || m_dfcCheckBox->isChecked() ||
m_dfcCheckBox->isChecked() ||
m_fractureHalfLengthCheckBox->isChecked();
if(!hasSelectedParams) {
@ -1016,7 +916,6 @@ void nmWxAutomaticFitting::onAccept()
if(m_sCheckBox->isChecked()) selectedParameterNames << tr("Skin");
if(m_cCheckBox->isChecked()) selectedParameterNames << tr("Wellbore storage");
if(m_phiCheckBox->isChecked()) selectedParameterNames << tr("Porosity");
if(m_swiCheckBox->isChecked()) selectedParameterNames << tr("Swi");
if(m_dfcCheckBox->isChecked()) selectedParameterNames << tr("Fracture conductivity");
if(m_fractureHalfLengthCheckBox->isChecked()) selectedParameterNames << tr("Fracture half length");
@ -1030,105 +929,55 @@ void nmWxAutomaticFitting::onReject()
void nmWxAutomaticFitting::onWellSelected(int index)
{
// 获取选中的井名
QString selectedWellName = m_targetWellCombo->itemText(index);
// 在分类的井数据中查找匹配的井
bool found = false;
bool fracturedWell = false;
double skinValue = 0.0;
double wellboreStorageValue = 0.0;
double fractureConductivityValue = 0.0;
double fractureHalfLengthValue = 0.0;
// 查找垂直井
for(int i = 0; i < m_verticalWells.size(); ++i) {
if(m_verticalWells[i].getWellName() == selectedWellName) {
skinValue = m_verticalWells[i].getPerforation(0)->getSkin().getValue().toDouble();
wellboreStorageValue = m_verticalWells[i].getWellboreStorage().getValue().toDouble();
found = true;
break;
}
// 每次都读取模型实际保留的井参数,避免拟合结束后切井又恢复到打开窗口时的旧副本。
nmDataAnalyzeManager* manager = nmDataAnalyzeManager::getCurrentInstance();
if(!manager) {
return;
}
// 查找水平井
if (!found) {
for(int i = 0; i < m_horizontalWells.size(); ++i) {
if(m_horizontalWells[i].getWellName() == selectedWellName) {
skinValue = m_horizontalWells[i].getPerforation(0)->getSkin().getValue().toDouble();
wellboreStorageValue = m_horizontalWells[i].getWellboreStorage().getValue().toDouble();
found = true;
break;
}
}
nmDataWellBase* targetWell = manager->findWellByName(m_targetWellCombo->itemText(index));
if(!targetWell) {
return;
}
// 查找垂直压裂井
if (!found) {
for(int i = 0; i < m_verticalFracturedWells.size(); ++i) {
if(m_verticalFracturedWells[i].getWellName() == selectedWellName) {
skinValue = m_verticalFracturedWells[i].getPerforation(0)->getSkin().getValue().toDouble();
wellboreStorageValue = m_verticalFracturedWells[i].getWellboreStorage().getValue().toDouble();
fractureConductivityValue = m_verticalFracturedWells[i].getDfc().getValue().toDouble();
fractureHalfLengthValue = m_verticalFracturedWells[i].getFractureHalfLength().getValue().toDouble();
fracturedWell = true;
found = true;
break;
}
}
}
const bool wasUpdatingRanges = m_updatingParameterRanges;
m_updatingParameterRanges = true;
updateParameterVisibility(m_parameterTable, manager->getSolverModelType());
// 查找水平压裂井
if (!found) {
for(int i = 0; i < m_horizontalFracturedWells.size(); ++i) {
if(m_horizontalFracturedWells[i].getWellName() == selectedWellName) {
skinValue = m_horizontalFracturedWells[i].getPerforation(0)->getSkin().getValue().toDouble();
wellboreStorageValue = m_horizontalFracturedWells[i].getWellboreStorage().getValue().toDouble();
fractureConductivityValue = m_horizontalFracturedWells[i].getDfc().getValue().toDouble();
fractureHalfLengthValue = m_horizontalFracturedWells[i].getFractureHalfLength().getValue().toDouble();
fracturedWell = true;
found = true;
break;
}
nmDataPerforation* perforation = targetWell->getPerforation(0);
if(perforation) {
const double skin = perforation->getSkin().getValue().toDouble();
m_parameterTable->item(1, 3)->setText(QString::number(skin, 'g', 10));
if(m_autoParameterRanges) {
updateRangeForParameter(1, skin);
}
}
const double storage = targetWell->getWellboreStorage().getValue().toDouble();
m_parameterTable->item(2, 3)->setText(QString::number(storage, 'g', 10));
if(m_autoParameterRanges) {
updateRangeForParameter(2, storage);
}
if (found) {
// 先按目标井类型刷新可见行,再写入当前井的井级初值。
nmDataAnalyzeManager* manager = nmDataAnalyzeManager::getCurrentInstance();
if(manager) {
updateParameterVisibility(m_parameterTable, manager->getSolverModelType());
}
// 更新表格数据
// 确保表格项存在
if(!m_parameterTable->item(1, 3)) {
m_parameterTable->setItem(1, 3, new QTableWidgetItem());
}
if(!m_parameterTable->item(2, 3)) {
m_parameterTable->setItem(2, 3, new QTableWidgetItem());
}
// 设置皮肤系数(Skin)
m_parameterTable->item(1, 3)->setText(QString::number(skinValue));
// 设置井筒储集系数(Wellbore storage)
m_parameterTable->item(2, 3)->setText(QString::number(wellboreStorageValue));
if(fracturedWell && m_parameterTable->item(5, 3)) {
m_parameterTable->item(5, 3)->setText(QString::number(fractureConductivityValue));
}
if(fracturedWell && m_parameterTable->item(6, 3)) {
m_parameterTable->item(6, 3)->setText(QString::number(fractureHalfLengthValue));
}
bool fracturedWell = false;
double conductivity = 0.0;
double halfLength = 0.0;
if(nmDataVerticalFracturedWell* well = dynamic_cast<nmDataVerticalFracturedWell*>(targetWell)) {
conductivity = well->getDfc().getValue().toDouble();
halfLength = well->getFractureHalfLength().getValue().toDouble();
fracturedWell = true;
} else if(nmDataHorizontalFracturedWell* well = dynamic_cast<nmDataHorizontalFracturedWell*>(targetWell)) {
conductivity = well->getDfc().getValue().toDouble();
halfLength = well->getFractureHalfLength().getValue().toDouble();
fracturedWell = true;
}
if(fracturedWell) {
m_parameterTable->item(5, 3)->setText(QString::number(conductivity, 'g', 10));
m_parameterTable->item(6, 3)->setText(QString::number(halfLength, 'g', 10));
if(m_autoParameterRanges) {
updateRangeForParameter(1, skinValue);
updateRangeForParameter(2, wellboreStorageValue);
if(fracturedWell) {
updateRangeForParameter(5, fractureConductivityValue);
updateRangeForParameter(6, fractureHalfLengthValue);
}
updateRangeForParameter(5, conductivity);
updateRangeForParameter(6, halfLength);
}
}
m_updatingParameterRanges = wasUpdatingRanges;
}
void nmWxAutomaticFitting::setAutomaticFittingValue()
@ -1138,7 +987,6 @@ void nmWxAutomaticFitting::setAutomaticFittingValue()
automaticFittingData.setSkinSelected(m_sCheckBox->isChecked());
automaticFittingData.setWellboreStorageSelected(m_cCheckBox->isChecked());
automaticFittingData.setPorositySelected(m_phiCheckBox->isChecked());
automaticFittingData.setSwiSelected(m_swiCheckBox->isChecked());
automaticFittingData.setFractureConductivitySelected(m_dfcCheckBox->isChecked());
automaticFittingData.setFractureHalfLengthSelected(m_fractureHalfLengthCheckBox->isChecked());
automaticFittingData.setSurrogateScreeningEnabled(m_surrogateCombo && m_surrogateCombo->currentIndex() == 1);
@ -1159,10 +1007,6 @@ void nmWxAutomaticFitting::setAutomaticFittingValue()
automaticFittingData.getPorosityMin().setValue(m_parameterTable->item(3, 2)->text().toDouble());
automaticFittingData.getPorosityMax().setValue(m_parameterTable->item(3, 4)->text().toDouble());
// 保存初始含水饱和度的最小值和最大值
automaticFittingData.getSwiMin().setValue(m_parameterTable->item(4, 2)->text().toDouble());
automaticFittingData.getSwiMax().setValue(m_parameterTable->item(4, 4)->text().toDouble());
// 保存裂缝导流能力的最小值和最大值
automaticFittingData.getFractureConductivityMin().setValue(m_parameterTable->item(5, 2)->text().toDouble());
automaticFittingData.getFractureConductivityMax().setValue(m_parameterTable->item(5, 4)->text().toDouble());
@ -1175,10 +1019,9 @@ void nmWxAutomaticFitting::setAutomaticFittingValue()
automaticFittingData.getIterationCount().setValue(m_iterationEdit->text().toInt());
automaticFittingData.getErrorTolerance().setValue(m_errorLimitEdit->text().toDouble());
// 只写回仍可编辑的储层初值,厚度和压缩系数保留原始精度和值。
// 只写回可拟合的渗透率、孔隙度,含水饱和度沿用模型当前值。
reservoirData.getPermeability().setValue(m_parameterTable->item(0, 3)->text().toDouble()); // 渗透率
reservoirData.getPorosity().setValue(m_parameterTable->item(3, 3)->text().toDouble()); // 孔隙度
reservoirData.getSwi().setValue(m_parameterTable->item(4, 3)->text().toDouble()); // 初始含水饱和度
// 更新储层数据(全局)
nmDataAnalyzeManager::getCurrentInstance()->updateReservoirData(reservoirData);
@ -1361,10 +1204,9 @@ void nmWxAutomaticFitting::onFittingFinished(bool success, const QString& messag
if(manager && manager->getAttrRegistry()) {
manager->getAttrRegistry()->refreshAll();
}
// 失败状态不代表参数未写回;初值与范围始终以模型实际保留的数据为准。
refreshParametersFromModel();
if(success) {
// 只有成功拟合的结果才用于生成下一轮范围,失败结果不污染当前配置。
updateBestParametersToTable();
QString resultInfo;
if(m_autoFitterPSO || m_autoFitterLM) {
@ -1479,47 +1321,30 @@ void nmWxAutomaticFitting::cleanupFitting()
DEBUG_UI("=== CLEANUP FITTING END ===");
}
void nmWxAutomaticFitting::updateBestParametersToTable()
void nmWxAutomaticFitting::refreshParametersFromModel()
{
QVector<double> bestSolution;
// 获取最佳解决方案
if (m_autoFitterPSO) {
bestSolution = m_autoFitterPSO->getBestSolution();
} else if(m_autoFitterLM) {
bestSolution = m_autoFitterLM->getBestSolution();
}
if (bestSolution.isEmpty()) return;
// 获取启用的参数索引
QVector<int> enabledParams;
if(m_kCheckBox->isChecked()) enabledParams.append(0); // 渗透率
if(m_sCheckBox->isChecked()) enabledParams.append(1); // 表皮系数
if(m_cCheckBox->isChecked()) enabledParams.append(2); // 井筒储集系数
if(m_phiCheckBox->isChecked()) enabledParams.append(3); // 孔隙度
if(m_swiCheckBox->isChecked()) enabledParams.append(4); // 初始含水饱和度
if(m_dfcCheckBox->isChecked()) enabledParams.append(5); // 裂缝导流能力
if(m_fractureHalfLengthCheckBox->isChecked()) enabledParams.append(6); // 裂缝半长
// 更新参数值和范围
for (int i = 0; i < bestSolution.size() && i < enabledParams.size(); ++i) {
int paramIndex = enabledParams[i];
double bestValue = bestSolution[i];
if(!nmAutoFitUiIsFinite(bestValue)) {
continue;
}
// 更新初始值
m_parameterTable->item(paramIndex, 3)->setText(QString::number(bestValue, 'g', 4));
// 自动范围模式下,以拟合结果为中心复用首次建范围的规则;手工范围由用户保留。
if(m_autoParameterRanges) {
updateRangeForParameter(paramIndex, bestValue);
}
nmDataAnalyzeManager* manager = nmDataAnalyzeManager::getCurrentInstance();
if(!manager) {
return;
}
// 保存更新
nmDataAnalyzeManager::getCurrentInstance()->updateAutomaticFittingData(automaticFittingData);
// 更新完成后,通知参数界面刷新
// 同步储层副本,避免下一次保存设置时把未拟合的储层属性覆盖成旧值。
reservoirData = manager->getReservoirDataCopy();
const bool wasUpdatingRanges = m_updatingParameterRanges;
m_updatingParameterRanges = true;
m_parameterTable->item(0, 3)->setText(QString::number(
reservoirData.getPermeability().getValue().toDouble(), 'g', 10));
m_parameterTable->item(3, 3)->setText(QString::number(
reservoirData.getPorosity().getValue().toDouble(), 'g', 10));
// 成功或失败都从当前目标井读取已落地的参数,不采用优化器尚未写回的最佳候选。
// 先刷新全部初值,再统一重建范围,避免即时校验读到新旧值混合的表格。
m_autoParameterRanges = false;
onWellSelected(m_targetWellCombo->currentIndex());
m_autoParameterRanges = true;
initializeSuggestedParameterRanges();
m_updatingParameterRanges = wasUpdatingRanges;
manager->updateAutomaticFittingData(automaticFittingData);
nmWxParameterProperty::notifyUpdateTable();
}

@ -689,16 +689,19 @@ void nmWxAutomaticfittingStart::setPseudoPressureMode(bool enabled)
void nmWxAutomaticfittingStart::onFittingProgress(int iteration, double fitness)
{
// 确保iteration在合理范围内
int displayIteration = qMax(1, qMin(iteration, m_maxIterations));
// 更新进度条
progressBar->setValue(displayIteration);
double progress = (double)displayIteration / m_maxIterations * 100;
progressBar->setFormat(QString("%1%").arg(progress, 0, 'f', 1));
// LM 预调整没有固定总步数,单独显示阶段;整体阶段从局部迭代 0 开始计进度。
if (m_autoFitterLM && iteration < 0) {
progressBar->setValue(0);
progressBar->setFormat(tr("Pre-adjustment"));
currentIterationValue->setText(tr("Pre-adjustment"));
} else {
int displayIteration = qMax(m_autoFitterLM ? 0 : 1, qMin(iteration, m_maxIterations));
progressBar->setValue(displayIteration);
double progress = (double)displayIteration / m_maxIterations * 100;
progressBar->setFormat(QString("%1%").arg(progress, 0, 'f', 1));
currentIterationValue->setText(QString::number(displayIteration));
}
// 更新参数显示
currentIterationValue->setText(QString::number(displayIteration));
currentComfortValue->setText(formatScientific(fitness));
// 更新最佳适应度

@ -5,8 +5,10 @@
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <QPushButton>
#include <QVBoxLayout>
#include <qnumeric.h>
nmWxGridDlg::nmWxGridDlg(double *pPebiGridControl):
m_pPebiGridControl(pPebiGridControl),
@ -20,6 +22,13 @@ nmWxGridDlg::nmWxGridDlg(double *pPebiGridControl):
void nmWxGridDlg::on_save()
{
// 无效文本转换为零;保存前统一拦截非正数和非有限值,保留原有网格设置。
if (!qIsFinite(m_dPebiGridControlTmp) || m_dPebiGridControlTmp <= 0.0) {
QMessageBox::warning(this, tr("Input Error"),
tr("Grid size must be a finite number greater than zero."));
return;
}
// 保存PEBI GridControl, GridType固定为PEBI, 仅在界面展示.
if (m_pPebiGridControl != NULL) {
*m_pPebiGridControl = m_dPebiGridControlTmp;

Loading…
Cancel
Save