"""Последовательный проект: статическая муфта и тяговая характеристика левитрона.
Это новые учебные геометрии по мотивам методической последовательности НГТУ,
не численное воспроизведение размеров пособия. FEMM5, без внешнего контроллера.
Муфта плоская 20мм глубины; левитрон осесимметричный, сталь mu_r=500 линейная.
Серия силы даёт равновесие и локальную производную, а не устойчивый регулятор.
"""

# Каталог сценария определяется явно: запуск не зависит от чужого CWD.
import os
import sys

os.chdir(os.path.dirname(os.path.abspath(sys.argv[0])))
exec(open("common.py").read())
folder = result_folder("results-coupling-levitron")
data = {"coupling": [], "levitron": []}
for angle in [0, 30, 60, 90, 120, 150, 180]:
    newdocument(0)
    mi_probdef(0, "millimeters", "planar", 1e-9, 20, 30)
    material("Air")
    material("PM", hc=800000)
    mi_drawrectangle(-2, -5, 2, 5)
    mi_selectrectangle(-2.1, -5.1, 2.1, 5.1, 4)
    mi_setgroup(1)
    mi_clearselected()
    label(0, 0, "PM", 0.4, group=1, angle=90)
    for x in [-12, 12]:
        mi_drawrectangle(x - 2, -10, x + 2, 10)
        label(x, 0, "PM", 0.6, group=2, angle=90)
    label(7, 0, "Air", 1)
    mi_makeABC(7, 60, 0, 0, 0)
    mi_selectgroup(1)
    mi_moverotate(0, 0, angle)
    mi_clearselected()
    solve(folder, "coupling-%d" % angle)
    mo_groupselectblock(1)
    data["coupling"].append({"angle_deg": angle, "torque_Nm": mo_blockintegral(22)})
    close_magnetic()
    save_json(folder, data)
for gap in [4, 6, 8, 10, 12]:
    newdocument(0)
    mi_probdef(0, "millimeters", "axi", 1e-9, 1, 30)
    material("Air")
    material("Steel", mu=500)
    mi_addcircprop("Coil", 5, 1)
    mi_drawrectangle(10, 0, 20, 20)
    label(15, 10, "Air", 0.6, "Coil", 200, 1)
    mi_drawrectangle(0, -gap - 6, 4, -gap)
    label(2, -gap - 3, "Steel", 0.25, group=2)
    label(5, 0, "Air", 0.8)
    mi_makeABC(7, 80, 0, 0, 0)
    solve(folder, "levitron-gap%d" % gap)
    mo_groupselectblock(2)
    force = mo_blockintegral(19)
    data["levitron"].append(
        {"gap_mm": gap, "force_up_N": force, "equilibrium_mass_kg": force / 9.80665}
    )
    close_magnetic()
    save_json(folder, data)
f6, f10 = data["levitron"][1]["force_up_N"], data["levitron"][3]["force_up_N"]
data["dF_dgap_N_per_m"] = (f10 - f6) / 0.004
data["dF_dz_up_N_per_m"] = -data["dF_dgap_N_per_m"]
mass = 7800 * math.pi * 0.004**2 * 0.006
current_eq = 5 * math.sqrt(mass * 9.80665 / data["levitron"][2]["force_up_N"])
data["equilibrium"] = {"steel_mass_kg": mass, "current_A": current_eq, "points": []}
for gap in [6, 8, 10]:
    opendocument(os.path.join(folder, "levitron-gap%d.fem" % gap))
    mi_modifycircprop("Coil", 1, current_eq)
    solve(folder, "equilibrium-gap%d" % gap)
    mo_groupselectblock(2)
    data["equilibrium"]["points"].append(
        {"gap_mm": gap, "force_N": mo_blockintegral(19)}
    )
    close_magnetic()
# Ограниченный перебор двух размеров; оцениваем реальную массу каждого цилиндра.
data["design_grid"] = []
for radius in [3, 4, 5]:
    for gap in [6, 8, 10]:
        newdocument(0)
        mi_probdef(0, "millimeters", "axi", 1e-9, 1, 30)
        material("Air")
        material("Steel", mu=500)
        mi_addcircprop("Coil", 5, 1)
        mi_drawrectangle(10, 0, 20, 20)
        label(15, 10, "Air", 0.6, "Coil", 200, 1)
        mi_drawrectangle(0, -gap - 6, radius, -gap)
        label(radius / 2, -gap - 3, "Steel", 0.25, group=2)
        label(7, 0, "Air", 0.8)
        mi_makeABC(7, 80, 0, 0, 0)
        solve(folder, "grid-radius%d-gap%d" % (radius, gap))
        mo_groupselectblock(2)
        force = mo_blockintegral(19)
        model_mass = 7800 * math.pi * (radius / 1000) ** 2 * 0.006
        required_current = 5 * math.sqrt(model_mass * 9.80665 / force)
        data["design_grid"].append(
            {
                "radius_mm": radius,
                "gap_mm": gap,
                "mass_kg": model_mass,
                "force_at_5A_N": force,
                "current_required_A": required_current,
                "feasible": gap >= 8 and model_mass <= 0.003 and required_current <= 12,
            }
        )
        close_magnetic()
        save_json(folder, data)
feasible = [row for row in data["design_grid"] if row["feasible"]]
data["selected_design"] = min(feasible, key=lambda row: row["current_required_A"])
save_json(folder, data)
assert all(row["force_up_N"] > 0 for row in data["levitron"])
print(data)
