【零基础学智能仿真-58】Foam-Agent协作与知识检索

课程摘要

本节围绕Foam-Agent协作与知识检索,学习如何将CFD任务拆分为规划、网格、配置、执行、审核与可视化职责。通过边界名称不一致和Courant数偏高两个案例,掌握版本筛选、证据记录、配置依赖与有限纠错。配套Python练习可离线运行,展示八种场景的决策及时间步建议,帮助学习者区分"提出修正""执行通过"与"物理可信",为下一节CFD基准测试奠定基础。


一、从"按顺序运行",到"有依据地协作"

上一节,我们建立了受控仿真工作流:

网格生成、网格检查、求解、后处理依次执行;前一步失败,后面的步骤不能假装完成。

但执行器只能忠实执行命令。它不能仅凭返回码判断:

  • 入口边界是否写错;
  • 材料参数是否缺失;
  • 检索到的配置是否适用于当前软件版本;
  • 某次修改究竟修复了错误,还是改变了原来的问题。

因此,本节要把"执行流程"向前推进一步:

谁负责理解问题,谁负责寻找依据,谁负责提出配置,谁有权修改,以及什么时候必须停止?

完成本节后,你应能够设计一套简单、可追溯的协作流程,而不仅是让多个角色轮流生成文字。

建议学习安排:阅读约35分钟,运行与核对约20分钟,练习约15分钟。本节不需要安装OpenFOAM,也不需要大模型API密钥。

二、一个入口名称,为什么能让协作失败?

继续考虑前几节使用的平行板流动问题。

网格工具输出的入口名称是:

复制代码
inlet

配置生成工具却在速度场文件中写成:

复制代码
inlet_main

这两个名称分别看起来都合理,组合起来却可能不一致。

假设这是一个仅使用精确边界名称、不使用分组或通配匹配的教学算例:

复制代码
网格边界:
inlet, outlet, walls, frontAndBack

速度场配置:
inlet_main, outlet, walls, frontAndBack

进行集合比较,可以发现:

复制代码
未覆盖的网格边界:inlet
无对应网格边界的配置:inlet_main

以上是教学一致性检查,不是OpenFOAM原生日志。

这个问题的关键不是"再增加一个智能体",而是:

所有角色是否引用了同一份经过确认的边界清单?

可以把它想成多人合作做菜。有人写采购单,有人准备食材,有人负责烹饪。如果采购单和厨房标签不一致,即使每个人都认真工作,合作仍然可能失败。

仿真协作同样需要一份"共同事实",至少包括:

  • 软件发行分支与版本;
  • 几何尺寸与单位;
  • 材料参数与物理模型;
  • 边界集合与驱动方式;
  • 计算终点与验收标准。

后续角色可以提出修改,但不能悄悄改写这些事实。

三、Foam-Agent的角色:交接的是可检查的产物

本节借鉴Foam-Agent 2.0中的六类角色:规划、网格、输入文件生成、执行、审核和可视化。知识检索是为这些角色提供依据的能力,不一定需要单独设置一个智能体。Foam-Agent 2.0论文,第3节

下面是面向课堂实践的职责约定,不是对论文代码接口的逐项复刻。

角色 应交接的产物 不应自行决定的事情
规划角色 Architect 问题规格、缺失信息、任务依赖 猜测未提供的材料参数
网格角色 Meshing 网格、边界清单、检查结果 为方便划分而改变关键几何
配置角色 Input Writer 配置建议、引用依据、修改差异 混用不同版本的设置
执行角色 Runner 命令、日志、返回码、超时记录 无审核地执行任意命令
审核角色 Reviewer 错误证据、修正建议、停止条件 为了通过而降低验收标准
可视化角色 Visualization 带单位、来源和状态说明的图表 把示意图当作计算结果

插图一:协作与有限纠错的整体关系

图中展示的是教学职责,不是本节程序的实际调用轨迹。

实际工程系统可以用普通函数、固定规则与少量智能体共同实现这些职责,不必每个框都启动一个大模型。

这里最重要的一条原则是:

下一角色应当能够检查上一步交来的事实,而不只是接受上一步的结论。

例如,Reviewer认为"Courant数偏高",就应给出观测值、对应时间步、日志位置和适用条件,而不是只写一句"建议减小时间步"。

四、知识检索:先问"能不能用",再问"像不像"

4.1 检索相关,不等于配置适用

RAG,即检索增强生成,基本思路是先查找资料,再让模型结合资料回答或生成内容。

但是,检索到了相似文字,并不意味着找到适用配置。

例如,以下资料可能都包含deltaT、pressure和boundary:

  1. 同发行分支、同版本的官方文档;
  2. 旧版本的相似算例;
  3. 同版本但采用不同物理模型的三维湍流案例。

如果只按关键词或向量相似度排序,后两者也可能排在前面。

因此,本节采用更保守的流程:

复制代码
候选资料
    ↓
发行分支与版本筛选
    ↓
物理条件与使用范围筛选
    ↓
相关性排序
    ↓
保留来源,生成建议

这不表示跨版本内容绝对不能复用,而是表示:

跨版本复用需要额外的兼容性核对,不能自动视为已经适用。

4.2 一条依据,至少要留下什么?

本节配套程序中的一条知识摘要如下:

复制代码
{
  "id": "TIME13",
  "family": "Foundation",
  "version": "13",
  "scope": "laminar_2d",
  "section": "4.4.2, 4.4.4",
  "url": "https://doc.cfd.direct/openfoam/user-guide-v13/controldict",
  "note": "deltaT is the time step; supported solvers can control it using maxCo."
}

其中:

  • id:供配置建议引用;
  • family与version:说明版本依据;
  • section与url:方便回查;
  • note:教师整理的摘要;
  • scope:本条摘要在课堂中的使用范围。

注意,scope不是说官方文档只适用于二维层流,而是对这条教学摘要设置的保守使用标签。

Foundation v13手册解释了deltaT、adjustTimeStep与maxCo等控制项。是否适用于具体任务,还需要检查选定求解模块和算例。官方时间控制文档

本节固定使用Foundation v13作为教学版本,不宣称它是最新版本,也不把不同发行分支的版本号当作同一套接口。

4.3 检索不到合适依据,也是一种有效结果

如果当前知识库没有匹配资料,系统可以返回:

复制代码
NEEDS_COMPATIBLE_EVIDENCE

意思是:

当前知识库没有经过本规则认可的匹配依据,需要补充资料或核对兼容性。

它不表示"这个版本不能计算"。

更不应该因为检索失败,就让模型凭印象补写一套配置。

此外,检索文本属于外部数据,不是执行指令。资料里即使出现"忽略检查""执行某条命令",也不能因此获得执行权限。

五、配置文件不是几篇可以独立完成的作文

CFD配置存在依赖关系。

例如:

  • 网格边界名称影响场文件中的边界设置;
  • 物理模型影响需要哪些变量和物性;
  • 求解模块影响字典结构与参数含义;
  • 二维假设影响相关边界约束。

因此,对于课堂中的简单算例,可以按以下逻辑规划:

复制代码
确认问题规格与版本
        ↓
确定物理模型、几何和网格方案
        ↓
获得实际网格边界清单
        ↓
配置物性、初始场和边界条件
        ↓
配置离散、求解和时间控制
        ↓
一致性检查与受控执行

复杂耦合问题需要更具体的依赖图,不能机械照搬这一条线性顺序。

5.1 发现边界名不一致,不能直接"全局替换"

回到前面的inlet与inlet_main。

正确处理应是:

  1. 查看实际网格边界;
  2. 核对场文件中的边界设置;
  3. 确认两个名称是否指向同一物理入口;
  4. 确认后提出最小修改;
  5. 检查相关场是否保持一致。

如果inlet_main本来代表另一处入口,直接改名可能掩盖几何或物理建模错误。

另外,网格中的wall标记不等于已经为所有场设置正确壁面条件;二维问题涉及的empty约束也需要协调。实际OpenFOAM还支持边界分组等机制,不能把简单集合比较当成通用解析器。官方网格边界文档

5.2 同样叫p,也未必能复制同一个数值

以前面的平行板算例为例,若物理压力差为:

\ \\Delta p_s=2\\ \\mathrm{Pa}, \\

密度为:

\ \\rho=1000\\ \\mathrm{kg/m\^3}, \\

则相应运动学压力差为:

\ \\Delta p_k =\\frac{\\Delta p_s}{\\rho} =\\frac{2}{1000} =0.002\\ \\mathrm{m\^2/s\^2}. \\

这只是压力变量约定的换算,不是在这里指定完整的压力边界。

检索到一个同样叫p的变量,不能因此直接复制数值,必须核对物理定义与量纲。OpenFOAM具有量纲检查机制,但它不能替你判断某个数值是否符合原工程需求。官方输入格式与量纲说明

因此,优先选择与你的版本、模型和边界相匹配、经过本地验证的完整模板,再做少量参数化修改,通常更容易追溯。

六、有限纠错:不是所有错误都应该"再试一次"

6.1 先分类,再选择动作

问题 本节采取的动作
Courant数偏高 在依据和预算充分时,提出较小时间步建议
缺少黏度等材料参数 请求补充,不猜数值
边界名称缺失或不一致 核对边界映射,不擅自改变几何
没有匹配版本依据 补充资料或核对兼容性
无有效观测值 先补齐或核查数据
已用完提案预算 停止并保留记录
无法识别的错误 停止,交由人工诊断

"缺少黏度"不能靠增加迭代次数解决。

"边界名不存在"也不能靠减小时间步解决。

纠错的第一步,是确认措施是否对准了错误类型。

6.2 具体计算:从Co=1.2得到时间步建议

假设一个待诊断的瞬态任务给出:

\ \\Delta t_{\\mathrm{old}}=0.002\\ \\mathrm{s}, \\\ \\mathrm{Co}_{\\mathrm{obs}}=1.2, \\

教学目标为:

\ \\mathrm{Co}_{\\mathrm{target}}=0.5. \\

这些是本节人为设置的诊断输入,不是从前几节真实OpenFOAM运行中读取的结果。0.5也不是所有CFD任务通用的阈值。

当网格和速度场近似不变时,可以按局部比例估计:

\ \\Delta t_{\\mathrm{proposal}} = \\Delta t_{\\mathrm{old}} \\frac{\\mathrm{Co}_{\\mathrm{target}}} {\\mathrm{Co}_{\\mathrm{obs}}}. \\

代入数值:

\ \\Delta t_{\\mathrm{proposal}} = 0.002\\times\\frac{0.5}{1.2} \\approx0.000833333\\ \\mathrm{s}. \\

这个结果表示:

可以提出将时间步从0.002秒减小到约0.000833333秒的建议。

它不保证重跑后的Co恰好达到0.5,更不保证求解收敛。

速度变化、网格变化和其他稳定性限制都可能影响结果。

6.3 什么可以改,什么不能偷偷改?

本次建议只针对deltaT。

原来的物性、几何、边界驱动、物理终点与验收要求应保持不变。

不能为了让任务"通过"而:

  • 提前结束计算;
  • 改变载荷或流量;
  • 放宽残差要求;
  • 删除失败记录;
  • 将未完成的步骤标记为成功。

本节设置一次提案预算:

复制代码
attempt = 0:尚未消耗提案预算
attempt = 1:预算已经用完,停止继续提案

实际系统应由可信执行层保存次数,不能让模型自行把attempt重置为0。

最重要的是区分:

\ \\text{提出修正} \\neq \\text{已应用修改} \\neq \\text{已重新求解} \\neq \\text{结果物理可信}. \\

七、离线实战:运行八种场景

7.1 下载与运行

完整程序已经独立整理,无需前几节的代码文件,另存以下代码为:lesson58_collaboration.py:

复制代码
"""Offline teaching model: curated-note filtering and bounded proposals, NOT Foam-Agent."""
import argparse
import copy
import json
import math
from pathlib import Path


# These are instructor-written summaries, not downloaded document chunks.
# scope describes this note's teaching use, not all uses of its source document.
KNOWLEDGE = [
    dict(id="TIME13", family="Foundation", version="13", scope="laminar_2d",
         tags=["time", "courant"], section="4.4.2, 4.4.4",
         url="https://doc.cfd.direct/openfoam/user-guide-v13/controldict",
         note="deltaT is the time step; supported solvers can control it using maxCo."),
    dict(id="PATCH13", family="Foundation", version="13", scope="laminar_2d",
         tags=["boundary"], section="5.3, 5.3.2",
         url="https://doc.cfd.direct/openfoam/user-guide-v13/boundaries",
         note="Mesh patches and field boundary conditions must be consistent; review empty faces for 2D."),
    dict(id="OLD12", family="Foundation", version="12", scope="laminar_2d",
         tags=["time", "courant", "boundary"], section="TEACHING_DISTRACTOR",
         url=None, note="Synthetic old-version candidate: deliberately not usable evidence."),
    dict(id="OTHER3D", family="Foundation", version="13", scope="turbulent_3d",
         tags=["time", "courant", "boundary"], section="TEACHING_DISTRACTOR",
         url=None, note="Synthetic other-physics candidate: deliberately not usable evidence."),
]
SPEC = dict(family="Foundation", version="13", scope="laminar_2d",
            H_m=0.01, L_m=1.0, rho_kg_m3=1000.0, mu_Pa_s=0.001,
            deltaT_s=0.002, endTime_s=1.0, target_Co=0.5,
            pressure="kinematic", patches=["inlet", "outlet", "walls", "frontAndBack"])
QUERY = {"time", "courant", "boundary"}


def positive(value):
    return (type(value) in (int, float) and math.isfinite(value) and value > 0)


def retrieve(spec):
    """Exact metadata filter BEFORE transparent tag-overlap ranking; not semantic RAG."""
    accepted, rejected = [], []
    for item in KNOWLEDGE:
        if any(item[k] != spec.get(k) for k in ("family", "version", "scope")):
            rejected.append(item["id"])
        elif QUERY.intersection(item["tags"]):
            accepted.append(dict(item, score=len(QUERY.intersection(item["tags"]))))
    accepted.sort(key=lambda x: (-x["score"], x["id"]))
    return accepted, rejected


def decide(spec, kind, attempt=0, observed_Co=None):
    if type(attempt) is not int or attempt < 0:
        raise ValueError("attempt must be a non-negative integer")
    result = dict(status="NEEDS_INPUT", proposal=None, evidence=[], rejected=[],
                  source="OFFLINE_TEACHING", attempt=attempt,
                  OpenFOAM_run=False, LLM_used=False, physical_verified=False)
    fields = ("H_m", "L_m", "rho_kg_m3", "mu_Pa_s", "deltaT_s", "endTime_s", "target_Co")
    if (kind == "missing_material" or any(not positive(spec.get(k)) for k in fields)
            or spec.get("pressure") != "kinematic" or not spec.get("patches")):
        return result
    evidence, rejected = retrieve(spec)
    result.update(evidence=evidence, rejected=rejected)
    if {e["id"] for e in evidence} != {"TIME13", "PATCH13"}:
        result["status"] = "NEEDS_COMPATIBLE_EVIDENCE"
    elif attempt >= 1:
        result["status"] = "STOP_BUDGET"
    elif kind == "missing_patch":
        result["status"] = "REVIEW_PATCH_MAPPING"
    elif kind == "high_courant":
        if not positive(observed_Co):
            result["status"] = "STOP_INVALID_OBSERVATION"
        elif observed_Co <= spec["target_Co"]:
            result["status"] = "NO_CHANGE_REQUIRED"
        else:
            new_dt = spec["deltaT_s"] * (spec["target_Co"] / observed_Co)
            if not positive(new_dt):
                result["status"] = "STOP_INVALID_OBSERVATION"
            else:
                result["status"] = "PROPOSE_SMALLER_DT"
                result["proposal"] = dict(field="deltaT", units="s",
                    before=spec["deltaT_s"], after=new_dt, observed_Co=observed_Co,
                    target_Co=spec["target_Co"], approval="PENDING_REVIEW",
                    evidence_ids=["TIME13"], applied=False)
    else:
        result["status"] = "STOP_UNKNOWN"
    return result


def run_demo():
    rows = []
    scenarios = [
        ("high_courant/0", "high_courant", 0, 1.2),
        ("high_courant/1", "high_courant", 1, 1.2),
        ("missing_material/0", "missing_material", 0, None),
        ("missing_patch/0", "missing_patch", 0, None),
        ("unknown/0", "unknown", 0, None),
        ("version14/0", "high_courant", 0, 1.2),
        ("invalid_Co/0", "high_courant", 0, None),
        ("low_Co/0", "high_courant", 0, 0.2),
    ]
    for name, kind, attempt, co in scenarios:
        spec = copy.deepcopy(SPEC)
        if name.startswith("version14"):
            spec["version"] = "14"
        if kind == "missing_material":
            spec.pop("mu_Pa_s")
        before = copy.deepcopy(spec)
        record = decide(spec, kind, attempt, co)
        assert spec == before, "The proposal must not mutate the problem specification"
        rows.append(dict(case=name, request=before, **record))
        print(f"{name}: {record['status']}")
    p = rows[0]["proposal"]
    print(f"deltaT: {p['before']:.9f} -> {p['after']:.9f} s (proposal only)")
    print("MODE=OFFLINE_TEACHING; LLM_used=False; OpenFOAM_run=False")
    return rows


def self_test():
    expected = ["PROPOSE_SMALLER_DT", "STOP_BUDGET", "NEEDS_INPUT", "REVIEW_PATCH_MAPPING",
                "STOP_UNKNOWN", "NEEDS_COMPATIBLE_EVIDENCE", "STOP_INVALID_OBSERVATION", "NO_CHANGE_REQUIRED"]
    rows = run_demo()
    assert [r["status"] for r in rows] == expected
    a, rejected = retrieve(SPEC)
    assert [x["id"] for x in a] == ["TIME13", "PATCH13"]
    assert rejected == ["OLD12", "OTHER3D"]
    assert math.isclose(rows[0]["proposal"]["after"], 1 / 1200)
    for bad in (float("nan"), float("inf"), 0, -1, True):
        assert decide(SPEC, "high_courant", observed_Co=bad)["status"] == "STOP_INVALID_OBSERVATION"
    for bad in (-1, True, 1.5):
        try:
            decide(SPEC, "high_courant", bad, 1.2)
        except ValueError:
            pass
        else:
            raise AssertionError("Invalid attempt accepted")
    assert all(not r["OpenFOAM_run"] and not r["physical_verified"] for r in rows)
    assert rows[0]["proposal"]["applied"] is False
    print("SELF_TEST=PASS (8 scenarios + filtering, numerical and input guards)")


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--out", type=Path, default=Path("lesson58_run"))
    parser.add_argument("--self-test", action="store_true")
    args = parser.parse_args()
    if args.self_test:
        self_test()
        return
    if args.out.exists():
        parser.error("Output path already exists; choose a new --out directory")
    rows = run_demo()
    args.out.mkdir(parents=True, exist_ok=False)
    report = dict(mode="OFFLINE_TEACHING", candidate_count=len(KNOWLEDGE),
                  knowledge_notes=KNOWLEDGE, records=rows)
    (args.out / "report.json").write_text(
        json.dumps(report, ensure_ascii=False, indent=2, allow_nan=False), encoding="utf-8")
    print(f"SAVED={args.out / 'report.json'}")


if __name__ == "__main__":
    main()

将文件放到自己的课程文件夹,在该文件夹中打开终端,运行:

复制代码
python lesson58_collaboration.py --self-test

核心练习只使用Python标准库,不需要安装pandas、LangGraph或FAISS。完整代码也已放在下载讲义的附录中,可以直接复制保存。

7.2 程序实际上完成了什么?

程序做两件事:

  1. 从4条本地教学条目中,先筛选元数据,再按标签重合数排序;
  2. 对8种预设场景输出决策记录。

其中,TIME13与PATCH13是教师整理的官方文档摘要。

OLD12与OTHER3D是专门设置的干扰条目,并未伪装成已经读取的官方资料。

本程序没有联网检索,没有向量数据库,没有调用LLM,也没有启动OpenFOAM。错误类别由测试场景提供,不是模型自动从真实日志识别出来的。

这是协作策略的透明教学模型,不是Foam-Agent完整复现。

7.3 实际运行输出

复制代码
high_courant/0: PROPOSE_SMALLER_DT
high_courant/1: STOP_BUDGET
missing_material/0: NEEDS_INPUT
missing_patch/0: REVIEW_PATCH_MAPPING
unknown/0: STOP_UNKNOWN
version14/0: NEEDS_COMPATIBLE_EVIDENCE
invalid_Co/0: STOP_INVALID_OBSERVATION
low_Co/0: NO_CHANGE_REQUIRED
deltaT: 0.002000000 -> 0.000833333 s (proposal only)
MODE=OFFLINE_TEACHING; LLM_used=False; OpenFOAM_run=False
SELF_TEST=PASS (8 scenarios + filtering, numerical and input guards)

逐项理解:

  • PROPOSE_SMALLER_DT:提出较小时间步建议;
  • STOP_BUDGET:已用完提案预算;
  • NEEDS_INPUT:问题信息不完整;
  • REVIEW_PATCH_MAPPING:需要核对边界对应关系;
  • STOP_UNKNOWN:错误类别无法处理;
  • NEEDS_COMPATIBLE_EVIDENCE:本地库没有匹配依据;
  • STOP_INVALID_OBSERVATION:观测值无效或缺失;
  • NO_CHANGE_REQUIRED:无需执行本条"因Co过高而减小时间步"的策略。

最后一项不表示任务已经成功,只表示这条策略不需要触发。

同样,SELF_TEST=PASS只表示教学程序通过测试,不表示八个CFD任务求解成功。

7.4 保存结构化记录

运行:

复制代码
python lesson58_collaboration.py --out lesson58_run

程序会输出相同的场景结果,并将记录保存为:

复制代码
SAVED=lesson58_run\report.json

程序拒绝覆盖已有输出目录。再次运行时,可换一个目录名称:

复制代码
python lesson58_collaboration.py --out lesson58_run_02

插图二:实际决策结果与时间步建议

此图直接读取本次程序生成的report.json绘制。

左侧展示原时间步与建议值,右侧展示8种场景的实际返回状态。它不是CFD求解结果、残差曲线或预测精度比较。

7.5 为什么看起来最相关的条目反而被拒绝?

查询标签包含:

复制代码
time, courant, boundary

两个干扰条目都包含这三个标签。如果只按标签数排序,它们可能非常"相关"。

但:

复制代码
OLD12:版本不匹配
OTHER3D:教学使用范围不匹配

所以它们先被排除。

保留下来的条目为:

复制代码
TIME13: score=2
PATCH13: score=1

这里的分数只是标签重合数量,不是概率、置信度或正确率。

真实RAG可以在同样的元数据闸门之后,再使用语义检索与重排序。

7.6 怎样读懂修正提案?

第一条记录中的关键内容为:

复制代码
{
  "field": "deltaT",
  "units": "s",
  "before": 0.002,
  "after": 0.0008333333333333334,
  "observed_Co": 1.2,
  "target_Co": 0.5,
  "approval": "PENDING_REVIEW",
  "evidence_ids": ["TIME13"],
  "applied": false
}

其中:

PENDING_REVIEW表示待审核,applied=false表示没有修改任何OpenFOAM字典。

同时,记录中的:

复制代码
OpenFOAM_run=False
physical_verified=False

明确说明没有执行或验证真实CFD。

还应注意:官方文档解释控制项,比例估算则依赖课堂假设。两者需要分别说明,不能把自己的推导包装成官方文档原话。

八、如何理解论文中的"成功率"?

本节固定引用Foam-Agent 2.0的arXiv:2509.18178v2版本,日期为2025年9月30日。该版本摘要报告:使用Claude 3.5 Sonnet,在110项任务上的成功率为88.2%。因此,不能沿用原始目录中"110项任务100%成功"的表述。指定版本论文摘要

更不能根据论文中的数字,保证自己的软件版本、模型和任务也能达到相同水平。

课堂实践应分别记录:

  • 配置是否有效;
  • 执行是否完成;
  • 数值指标是否通过;
  • 物理基准是否通过;
  • 使用了多少次尝试;
  • 是否发生人工介入。

不要把这些结果压缩成一句含义不清的"成功了"。

九、本节练习与参考答案

练习一:缺少黏度,可以先填一个"常见值"吗?

参考答案:不能默认为用户要求。

黏度会影响雷诺数与流动响应。应请求补充;如果用户明确授权使用假设值,也必须在规格和报告中标记。

练习二:入口名称不一致,可以直接批量替换吗?

参考答案:先确认两个名称是否指向同一物理入口。

确认后提出最小修改,并检查相关场的一致性。不能通过删除入口或改成壁面来消除报错。

练习三:Co为0.2、目标为0.5,是否必须增大时间步?

参考答案:不必。

本节策略只处理"Co过高时减小时间步",不负责自动增大时间步。NO_CHANGE_REQUIRED也不是整项任务通过。

练习四:同版本的三维湍流案例,可以直接用于二维层流吗?

参考答案:不能直接整套移植。

版本一致只消除一类风险。维度、湍流模型、壁面处理和边界驱动仍需要核对。

练习五:批准减小时间步后,怎样证明修正有效?

参考答案:在新目录保存修改差异,重新运行到相同物理终点,再检查Co、日志、守恒与关键物理量,并进行独立基准比较。

只有修正提案,或者一条较低的残差,都不足以证明结果可信。

十、小结与下一节安排

本节的核心不是让系统"多试几次",而是让每一次建议都有依据、有边界、有记录。

请记住四句话:

  1. 角色协作靠共同规格与明确交接,不靠角色数量。
  2. 检索先检查兼容性,再比较相关性。
  3. 修正应针对错误,不能改变问题来换取通过。
  4. 提案、执行、数值通过和物理可信是不同状态。
相关推荐
德林恩宝1 小时前
搜索 Agent 的两条路——训练一个,还是工程搭一个
人工智能·大模型·llm·agent
cc_瀚海知行1 小时前
工具调用 6|助手什么时候该停下来?
java·服务器·前端·人工智能·python·ai
Martina_03211 小时前
AI生成3D模型UV岛太碎,重绘贴图全是接缝?用6步重整UV并保留原材质
图像处理·人工智能·3d·自然语言处理·贴图·uv·游戏策划
拜小白1 小时前
【论文解读】U2RLE:不确定性引导的两阶段房间布局估计
深度学习·计算机视觉·全景图
youdexiang1 小时前
Android录音APP微信语音导入机制详解
人工智能·语音识别
悟天特斯1 小时前
楼宇自控的“协同优化“:从设备控制到系统级节能与舒适平衡
人工智能·物联网
叫我啊腻1 小时前
Nano Banana 2.1 深度测评:升级了哪些,和 GPT Image 2.5 怎么选?
人工智能
johnsong1 小时前
AI前沿日报 2026-10-21:开源AI全面反击
人工智能·开源
漫步人生走在路上1 小时前
外贸SEO中的品牌词与产品词策略:大卖家和小卖家的不同打法
大数据·javascript·网络·数据库·人工智能·搜索引擎