# Use the existing connected PyFluent variable solver.
import math
import json

task_directory = r"C:\Users\13977\AppData\Local\FluentAGI\data\runs\20261020-092509-6d9cb4"
case_directory = r"C:\Users\13977\AppData\Local\FluentAGI\data\runs\20261020-084829-f1d49a"
prefix = "diag-a2c32574"
s = solver.settings
version = str(solver.get_fluent_version())
print("FLUENT_VERSION", version)
if not any(v in version for v in ["242", "24.2", "2024 R2"]):
    raise RuntimeError("当前版本与本任务已核验接口不同，停止。")
if not s.setup.is_active() or not s.setup.models.is_active():
    raise RuntimeError("当前未加载模型，停止并保留会话。")
if not solver.fields.field_data.is_data_valid():
    raise RuntimeError("当前有效解不可读，停止；不初始化或自动加载文件。")

fluid_names = s.setup.cell_zone_conditions.fluid.get_object_names()
zones = solver.fields.solution_variable_info.get_zones_info()
cells = sum(zones[n].count for n in fluid_names)
bc = s.setup.boundary_conditions
walls = ["wall-reactor", "wall-pipe"]
if fluid_names != ["fluid"] or cells != 45582:
    raise RuntimeError("实时网格与已完成计算的模型不同，停止。")
for container, names in [(bc.velocity_inlet, ["inlet"]), (bc.pressure_outlet, ["outlet"]), (bc.wall, walls), (bc.symmetry, ["symmetry"])]:
    actual = container.get_object_names()
    print("BOUNDARY_NAMES", actual)
    if not all(n in actual for n in names):
        raise RuntimeError("边界名称与原任务不一致，停止。")
print("LIVE_STATE", {"cells": cells, "valid_solution": True, "case_directory": case_directory})
backup = task_directory + "/" + prefix + "-live-start.cas.h5"
s.file.write_case_data(file_name=backup)
models, methods = s.setup.models, s.solution.methods
print("CURRENT_PHYSICS", {"time": s.setup.general.solver.time.get_state(), "multiphase": models.multiphase.models.get_state(), "viscous": models.viscous.get_state(), "inlet": bc.velocity_inlet["inlet"].get_state(), "outlet": bc.pressure_outlet["outlet"].get_state()})
print("CURRENT_NUMERICS", {"methods": methods.get_state(), "controls": s.solution.controls.p_v_controls.get_state()})
if s.setup.general.solver.time.get_state() != "steady" or models.multiphase.models.get_state() != "none":
    raise RuntimeError("实时模型已改变，保留备份并停止本稳态空气场诊断。")
print("DPM_INSPECTION", fluentagi.inspect_dpm())

required = ["continuity", "x-velocity", "y-velocity", "z-velocity", "k", "epsilon"]
residual = s.solution.monitor.residual
print("RESIDUAL_OPTIONS", residual.options.get_state())
scaled = bool(residual.options.residual_values.scale_residuals.get_state())
normalized = bool(residual.options.normalize.get_state())
if "residual" not in solver.monitors.get_monitor_set_names():
    raise RuntimeError("当前残差历史不可读，保留解，不重置监视器。")
indices, data = solver.monitors.get_monitor_set_data(monitor_set_name="residual")
if any(n not in data for n in required):
    raise RuntimeError("残差历史缺少必须方程，停止诊断。")
rows = {}
for j, index in enumerate(indices):
    if all(j < len(data[n]) for n in required):
        rows[int(round(float(index)))] = {n: float(data[n][j]) for n in required}
keys = sorted(rows)
if not keys or keys[-1] < 1500:
    raise RuntimeError("实时残差进度不足以核对已完成的1500次，停止且不重算。")
actual_iteration = keys[-1]
tail = keys[-20:]
consecutive = len(tail) == 20 and tail == list(range(actual_iteration - 19, actual_iteration + 1))
residual_pass = scaled and not normalized and consecutive and all(math.isfinite(rows[k][n]) and 0 <= rows[k][n] <= 1e-5 for k in tail for n in required)
trend = {}
window = keys[-200:]
for n in required:
    values = [rows[k][n] for k in window]
    if any(not math.isfinite(v) or v < 0 for v in values):
        raise RuntimeError("残差历史存在异常数值，停止诊断。")
    sample = min(50, len(values) // 2)
    first = sum(values[:sample]) / sample if sample else None
    last = sum(values[-sample:]) / sample if sample else None
    trend[n] = {"latest": rows[actual_iteration][n], "window_min": min(values), "window_max": max(values), "first_mean": first, "last_mean": last, "last_to_first": last / first if first and last is not None else None}
print("RESIDUAL_DIAGNOSIS", json.dumps({"iteration": actual_iteration, "window": [window[0], window[-1]], "scaled": scaled, "normalized": normalized, "last20_pass": residual_pass, "equations": trend}, ensure_ascii=False))

rd = s.solution.report_definitions
def compute_reports(names):
    raw = rd.compute(report_defs=names)
    print("NATIVE_REPORTS", raw)
    merged = {}
    for block in raw if isinstance(raw, (list, tuple)) else [raw]:
        if isinstance(block, dict):
            merged.update(block)
    if any(n not in merged for n in names):
        raise RuntimeError("原生报告返回不完整，停止且不重复计算流场。")
    result = {}
    for n in names:
        value = merged[n]
        result[n] = float(value[0] if isinstance(value, (list, tuple)) else value)
    if not all(math.isfinite(v) for v in result.values()):
        raise RuntimeError("原生报告存在非有限值，停止。")
    return result

base_reports = ["air-mdot-in", "air-mdot-out", "air-pressure-in"]
for name, boundary in zip(base_reports[:2], ["inlet", "outlet"]):
    if name not in rd.flux.get_object_names():
        raise RuntimeError("缺少现有质量流率报告：" + name)
    report = rd.flux[name]
    if report.report_type.get_state() != "flux-massflow" or report.boundaries.get_state() != [boundary]:
        raise RuntimeError("现有质量流率报告定义已改变，停止。")
if base_reports[2] not in rd.surface.get_object_names():
    raise RuntimeError("缺少现有入口压力报告，停止。")
pressure_report = rd.surface[base_reports[2]]
if pressure_report.report_type.get_state() != "surface-areaavg" or pressure_report.field.get_state() != "pressure" or pressure_report.surface_names.get_state() != ["inlet"]:
    raise RuntimeError("现有入口压力报告定义已改变，停止。")
current = compute_reports(base_reports)
mi, mo = [current[n] for n in base_reports[:2]]
imbalance = abs(mi + mo) / abs(mi) if abs(mi) > 1e-12 else None
print("FLOW_BALANCE", {"inlet": mi, "outlet": mo, "relative_imbalance": imbalance, "inlet_pressure": current[base_reports[2]], "pressure_stability_retested": False})

diagnostic_names, report_map, unavailable = [], {}, []
specs = [(wall, "y-plus") for wall in walls] + [("outlet", "y-velocity"), ("outlet", "y-coordinate")]
for surface, field in specs:
    for suffix, report_type in [("min", "surface-facetmin"), ("max", "surface-facetmax"), ("areaavg", "surface-areaavg")]:
        name = prefix + "-" + surface + "-" + field + "-" + suffix
        if name in rd.surface.get_object_names():
            raise RuntimeError("诊断报告已存在，停止以避免重复执行：" + name)
        rd.surface.create(name=name)
        report = rd.surface[name]
        if report_type not in report.report_type.allowed_values():
            unavailable.append(name)
            continue
        report.report_type = report_type
        allowed = report.field.allowed_values()
        if field not in allowed:
            unavailable.append({"report": name, "field": field})
            continue
        report.field = field
        report.surface_names = [surface]
        if report.average_over.is_active():
            report.average_over = 1
        if report.per_surface.is_active():
            report.per_surface = False
        diagnostic_names.append(name)
        report_map[name] = {"surface": surface, "field": field, "statistic": suffix}
diagnostics = compute_reports(diagnostic_names) if diagnostic_names else {}
for name, value in diagnostics.items():
    print("SURFACE_DIAGNOSIS", dict(report_map[name], value=value))
print("UNAVAILABLE_DIAGNOSTICS", unavailable)

field_info = solver.fields.field_info.get_scalar_fields_info()
ymin = diagnostics.get(prefix + "-outlet-y-coordinate-min")
ymax = diagnostics.get(prefix + "-outlet-y-coordinate-max")
outlet_plane_verified = ymin is not None and ymax is not None and abs(ymin - 6) < 1e-5 and abs(ymax - 6) < 1e-5
backflow = {"verified": False, "area_fraction_verified": False}
if outlet_plane_verified and "y-velocity" in field_info:
    raw = solver.fields.field_data.get_scalar_field_data(field_name="y-velocity", surfaces=["outlet"], node_value=False, boundary_value=True)
    velocities = []
    for block in raw.values() if isinstance(raw, dict) else [raw]:
        block = block.data if hasattr(block, "data") and not hasattr(block, "shape") else block
        for v in block:
            velocities.append(float(v.scalar_data if hasattr(v, "scalar_data") else v))
    if velocities and all(math.isfinite(v) for v in velocities):
        negative = sum(v < -1e-8 for v in velocities)
        backflow.update({"verified": True, "outward_direction": "+Y", "faces": len(velocities), "negative_velocity_faces": negative, "face_count_fraction": negative / len(velocities), "weighting": "face-count", "velocity_min": min(velocities), "velocity_max": max(velocities)})
print("OUTLET_BACKFLOW", json.dumps(backflow, ensure_ascii=False))

exports = []
section = "air-section-z"
if section in s.results.surfaces.iso_surface.get_object_names():
    print("EXISTING_SECTION", s.results.surfaces.iso_surface[section].get_state())
    exports.append(fluentagi.export_results(directory=task_directory, fields=["pressure", "velocity-magnitude"], surfaces=[section]))
else:
    print("INTERIOR_EXPORT_SKIPPED", "现有剖面不存在，保留现场。")
if "y-plus" in field_info:
    exports.append(fluentagi.export_results(directory=task_directory, fields=["y-plus"], surfaces=walls))
for result in exports:
    print("EXPORT_RESULTS", result)
    if not isinstance(result, dict) or result.get("errors") or result.get("error") or result.get("status") != "ok":
        print("POSTPROCESSING_NEEDS_REVIEW", result)
saved = task_directory + "/" + prefix + "-diagnosed.cas.h5"
s.file.write_case_data(file_name=saved)
print("DIAGNOSTIC_SUMMARY", json.dumps({"status": "diagnosis_only", "actual_iteration": actual_iteration, "iterations_requested": 0, "last20_residuals_pass": residual_pass, "relative_mass_imbalance": imbalance, "backflow": backflow, "unavailable": unavailable, "case": saved, "data": saved.replace(".cas.h5", ".dat.h5"), "convergence_verified": False, "grid_independence_verified": False, "DPM_tracking_executed": False}, ensure_ascii=False))
print("NEXT_ACTION", ["结合末段残差趋势与实际出口回流，决定是否续算或调整数值设置。", "结合两类壁面y-plus判断近壁分辨率，统计值不能代替网格独立性验证。", "严格液滴教程的表面注入、Rosin-Rammler和DRW10尚无已验证适配器，继续保留待支持状态。"])