# Use the existing connected PyFluent variable solver.
import math

task_directory = r"C:\Users\13977\AppData\Local\FluentAGI\data\runs\20261018-142547-90d0a4"
s = solver.settings

def checked(label, result):
    print(label, result)
    if isinstance(result, dict):
        if result.get("status") in ("error", "failed", "needs_review"):
            raise RuntimeError(str(result))
        if result.get("error") or result.get("errors"):
            raise RuntimeError(str(result))
    return result

def numbers(value):
    if isinstance(value, bool):
        return []
    if isinstance(value, (int, float)):
        return [float(value)]
    if isinstance(value, dict):
        result = []
        for child in value.values():
            result.extend(numbers(child))
        return result
    if isinstance(value, (list, tuple)):
        result = []
        for child in value:
            result.extend(numbers(child))
        return result
    return []

def report_value(result, name):
    if isinstance(result, dict):
        if name in result:
            values = numbers(result[name])
            if values and math.isfinite(values[0]):
                return values[0]
        for child in result.values():
            try:
                return report_value(child, name)
            except ValueError:
                pass
    elif isinstance(result, (list, tuple)):
        for child in result:
            try:
                return report_value(child, name)
            except ValueError:
                pass
    raise ValueError("无法提取报告值：" + name + "；实际返回：" + str(result))

def scalar(setting, value):
    state = setting.get_state()
    if isinstance(state, dict):
        if "value" not in state:
            raise RuntimeError("无法识别数值字段：" + str(state))
        update = {"value": value}
        if "option" in state:
            update["option"] = "value"
        setting.set_state(update)
    else:
        setting.set_state(value)

def vector(setting, values):
    state = setting.get_state()
    if isinstance(state, (list, tuple)):
        setting.set_state(values)
    elif isinstance(state, dict):
        keys = list(state)
        groups = [
            ["x", "y", "z"],
            ["x_component", "y_component", "z_component"],
        ]
        matched = False
        for group in groups:
            if all(key in keys for key in group):
                for key, value in zip(group, values):
                    scalar(getattr(setting, key), value)
                matched = True
                break
        if not matched:
            raise RuntimeError("无法识别矢量字段：" + str(state))
    else:
        raise RuntimeError("无法识别矢量字段：" + str(state))

if not s.setup.models.is_active():
    raise RuntimeError("当前网格未激活。")

bc = s.setup.boundary_conditions
wall_names = bc.wall.get_object_names()
print("CURRENT_WALL_ZONES", wall_names)
print("CURRENT_SOLVER", s.setup.general.solver.get_state())
iteration_start = s.solution.run_calculation.iter_count.get_state()
print("INITIAL_ITERATION_COUNT", iteration_start)

s.file.write_case(
    file_name=task_directory + r"\before_front_wing_coordinate_setup.cas.h5"
)

surface_reports = s.solution.report_definitions.surface
probe_names = []
for axis in ["x", "y", "z"]:
    for suffix, report_type in [
        ("min", "surface-vertexmin"),
        ("max", "surface-vertexmax"),
    ]:
        name = "fw_bound_" + axis + "_" + suffix
        if name not in surface_reports.get_object_names():
            surface_reports[name] = {}
        report = surface_reports[name]
        report.report_type = report_type
        allowed = report.field.allowed_values()
        field = axis + "-coordinate"
        if field not in allowed:
            raise RuntimeError("坐标字段不可用：" + field)
        report.field = field
        probe_names.append(name)

bounds = {}
for zone in wall_names:
    for name in probe_names:
        surface_reports[name].surface_names = [zone]
    result = s.solution.report_definitions.compute(report_defs=probe_names)
    values = [report_value(result, name) for name in probe_names]
    bounds[zone] = values
    print("WALL_BOUNDS", zone, values)

wing = []
ground = []
farfield = []
for zone, b in bounds.items():
    xmin, xmax, ymin, ymax, zmin, zmax = b
    if max(abs(ymin), abs(ymax)) < 1e-5:
        ground.append(zone)
    elif (
        xmin > -0.031 and xmax < 0.491
        and ymin > 0.089 and ymax < 0.351
        and zmin > -0.619 and zmax < 0.619
    ):
        wing.append(zone)
    else:
        farfield.append(zone)

print("BOUNDARY_CLASSIFICATION", {
    "wing": wing,
    "ground": ground,
    "farfield": farfield,
})
if not wing or len(ground) != 1 or len(farfield) != 3:
    raise RuntimeError("边界分类与设计不符，停止求解并保留网格。")

for zone in wing + ground:
    bc.wall[zone].momentum.shear_condition = "No Slip"
    bc.wall[zone].momentum.wall_motion = "Stationary Wall"

momentum = bc.wall[ground[0]].momentum
momentum.wall_motion = "Moving Wall"
for name, value in [
    ("relative", False),
    ("rotating", False),
    ("components", False),
]:
    child = getattr(momentum, name)
    if child.is_active():
        child.set_state(value)
scalar(momentum.speed, 30.0)
vector(momentum.direction, [1.0, 0.0, 0.0])
print("MOVING_GROUND_READBACK", momentum.get_state())

for zone in farfield:
    momentum = bc.wall[zone].momentum
    momentum.wall_motion = "Stationary Wall"
    momentum.shear_condition = "Specified Shear"
    print("SLIP_OUTER_BOUNDARY", zone, momentum.get_state())

inlet = bc.velocity_inlet["inlet"]
outlet = bc.pressure_outlet["outlet"]
scalar(inlet.momentum.velocity, 30.0)
scalar(outlet.momentum.gauge_pressure, 0.0)
inlet.turbulence.turbulence_specification = "Intensity and Viscosity Ratio"
scalar(inlet.turbulence.turbulent_intensity, 0.01)
scalar(inlet.turbulence.turbulent_viscosity_ratio, 10.0)
print("INLET_TURBULENCE_READBACK", inlet.turbulence.get_state())

force_names = []
for name, direction in [
    ("front_wing_drag_interface", [1.0, 0.0, 0.0]),
    ("front_wing_lift_interface", [0.0, 1.0, 0.0]),
]:
    report = s.solution.report_definitions.force[name]
    report.force_vector = direction
    report.zones = wing
    force_names.append(name)
    print("WING_FORCE_READBACK", name, report.get_state())

for name, zone in [("fw_inlet_mass", "inlet"), ("fw_outlet_mass", "outlet")]:
    if name not in surface_reports.get_object_names():
        surface_reports[name] = {}
    report = surface_reports[name]
    report.report_type = "surface-massflowrate"
    report.surface_names = [zone]

s.file.write_case(
    file_name=task_directory + r"\front_wing_ready_to_solve.cas.h5"
)
print("RESIDUAL_CONFIGURATION", s.solution.monitor.residual.get_state())

if iteration_start != 0:
    raise RuntimeError("检测到已有迭代结果；保留结果，停止首次初始化流程。")

s.setup.general.solver.time = "steady"
s.solution.initialization.hybrid_initialize()
s.file.start_transcript(
    file_name=task_directory + r"\front_wing_flow_and_forces.trn"
)
try:
    for block in range(6):
        s.solution.run_calculation.iterate(iter_count=50)
        result = s.solution.report_definitions.compute(report_defs=force_names)
        drag = report_value(result, force_names[0])
        lift = report_value(result, force_names[1])
        dynamic_force = 0.5 * 1.225 * 30.0**2 * (1.2 * 0.46)
        print("STEADY_FORCE_HISTORY", {
            "iteration": s.solution.run_calculation.iter_count.get_state(),
            "drag": drag,
            "lift": lift,
            "downforce": -lift,
            "CD": drag / dynamic_force,
            "CL": lift / dynamic_force,
        })

    s.file.write_case_data(
        file_name=task_directory + r"\front_wing_steady_checkpoint.cas.h5"
    )
    section = checked(
        "CENTRAL_SECTION",
        fluentagi.create_section(axis="z", position=0.0),
    )
    s.setup.general.solver.time = "unsteady-2nd-order"
    s.solution.run_calculation.transient_controls.time_step_size = 0.0005
    checked(
        "VELOCITY_SECTION_ANIMATION",
        fluentagi.record_animation(
            directory=task_directory,
            field="velocity-magnitude",
            frames=41,
            steps_per_frame=5,
            max_iter_per_step=40,
            surfaces=[section["surface"]],
        ),
    )
    print(
        "FINAL_FORCES",
        s.solution.report_definitions.compute(report_defs=force_names),
    )
    print(
        "BOUNDARY_MASS_FLOW",
        s.solution.report_definitions.compute(
            report_defs=["fw_inlet_mass", "fw_outlet_mass"]
        ),
    )
    checked(
        "FINAL_SECTION_EXPORT",
        fluentagi.export_results(
            directory=task_directory,
            fields=["pressure", "velocity-magnitude"],
            surfaces=[section["surface"]],
        ),
    )
    paths = s.results.graphics.pathline
    if paths.is_active():
        path_name = "front_wing_streamlines"
        if path_name not in paths.get_object_names():
            paths[path_name] = {}
        path = paths[path_name]
        print("STREAMLINE_FIELDS", getattr(path, "child_names", []))
        print("STREAMLINE_STATE", path.get_state())
finally:
    s.file.stop_transcript()
    s.file.write_case_data(
        file_name=task_directory + r"\front_wing_flow_checkpoint.cas.h5"
    )

print("REFERENCE_AREA", 1.2 * 0.46)
print("ANIMATION_PLANNED_DURATION", 0.1)
print("VALIDATION", {
    "all_steps_convergence_verified": False,
    "mesh_independence_verified": False,
    "near_wall_resolution_verified": False,
    "streamline_export_verified": False,
})