JavaProjectRepo/异步推理并保存结果.md

49 lines
3.9 KiB
Markdown
Raw Permalink Normal View History

2026-03-20 19:00:16 +08:00
# 异步推理并保存结果 (SimInferService 流程解析)
在仿真计算完成后,系统需要将每一步的计算结果发送给 Python 推理服务,以预测关键指标(如 Keff。这个过程由 `SimInferService``DeviceInferService` 协同完成。
## 1. 推理数据不是一次性全部发送
**核心结论:** 并不是把所有结果封装为一个大 Request 调用一次推理接口,而是**进行了精细的多级分组后,分别调用推理接口**。
因为不同的设备、不同的材料、以及用户为设备配置的不同算法,都对应着不同的**模型文件Pipeline/Model**。一次推理请求只能加载一个模型文件,所以必须将必须使用同一模型的数据分在同一批次发送。
## 2. 数据转换与分组流程
整个流程分为两部分:在 `SimInferService` 中进行一级分组,然后在 `DeviceInferService` 中进行二级和三级分组。
### 2.1 第一级分组:按设备类型 (DeviceType) 分组
**位置:** `SimInferService.asyncInferAndSave`
1. **展平时间轴 (Flatten Timeline)**:仿真引擎返回的 `SimContext` 是按时间步Step组织的。代码首先遍历每个时间步将散落的属性如液位、浓度`unitId` 聚合。
2. **注入元数据**:通过 `SimUnit`,将静态属性(如设备固有的直径、高度)以及新加入的 `materialType`(如 U, Pu合并到设备的属性集合中生成 `DeviceStepInfo` 对象。
3. **一级分组**:将所有时间步的 `DeviceStepInfo` 根据 **设备类型 (`deviceType`)** 放入 `Map<String, List<DeviceStepInfo>> groupedDevices` 中。
- *例如:所有的 CylindricalTank 数据在一组AnnularTank 在另一组。*
### 2.2 第二级分组:按算法类型 (AlgorithmType) 分组
**位置:** `DeviceInferService.processDeviceInference`
1. **读取配置**:获取全局配置的算法(如 GPR以及针对特定设备独立配置的算法`deviceAlgoConfig`)。
2. **二级分组**:在某一个设备类型(如 CylindricalTank的数据列表中进一步按 **算法类型 (`algoType`)** 分组。
- *例如CylindricalTank 组被拆分为 -> 使用 GPR 算法的批次、使用 MLP 算法的批次。*
### 2.3 第三级分组:按材料类型 (MaterialType) 分组
**位置:** `DeviceInferService.processDeviceInference`
1. **三级分组**:在特定的算法批次中,进一步按 **材料类型 (`materialType`)** 分组。
- *例如:使用 GPR 的 CylindricalTank 批次被拆分为 -> 材料为 U 的批次、材料为 Pu 的批次。*
## 3. 发起推理请求
经过上述三级分组后,我们得到了一个**高度同质化**的数据批次。对于每一个这样的最终批次:
1. **查询模型**:调用 `algorithmModelService.getCurrentModelPath(algoType, deviceType, materialType)`。因为这三个维度相同,所以它们一定使用同一个物理模型文件(`.pkl`)。
2. **构建 Request**:将这批数据(包含所有相关设备在所有时间步的状态)组装成一个 `InferRequest`
3. **发起调用**:向 Python 服务发送 HTTP POST 请求 `/v1/infer`
4. **入库保存**Python 返回每个设备在每个时间步的预测结果(如 `keff`Java 端解析这些结果并批量保存到 `scenario_result` 表中。
## 4. 异常与状态管理
- 整个过程是加上了 `@Async` 异步执行的,不会阻塞前端获取基础仿真数据。
- 如果某一个批次因为找不到模型(例如上述终端日志中 `GPR + CylindricalTank + U` 的模型未配置),代码会记录错误并标记 `hasAnyError = true`,然后跳过该批次(`continue`)。
- **兜底机制**:如果所有的批次都失败或找不到模型(`!hasAnySuccess && hasAnyError`),会抛出 `RuntimeException`。这会被外层 `SimInferService` 捕获,并将该情景的状态更新为 **"3" (失败)**。