"""Постоянный ток в проводнике с боковым вырезом.
Среда: встроенный MicroPython FEMM 5; команды FEMM доступны как глобальные функции.
Модель создаётся заново, сохраняется, закрывается и повторно открывается до расчёта.
Размеры геометрии заданы в миллиметрах; интегральные результаты имеют единицы СИ.
Каждый запуск создаёт новый calculated-XX рядом с программой и не меняет прежние
модели, результаты и квитанции. Все входные параметры заданы непосредственно в программе.
Область применимости и принятые допущения описаны в README.md.
"""

import os
import sys
import math
import json

script = globals().get("__file__", "") or (sys.argv[0] if sys.argv else "")
base = os.path.dirname(os.path.abspath(script)) if script else os.getcwd()
index = 1
while "calculated-%02d" % index in os.listdir(base):
    index += 1
directory = base + "/calculated-%02d" % index
os.mkdir(directory)
records = []
checks = []


def real(value):
    if isinstance(value, (list, tuple)):
        return real(value[0])
    return complex(value).real


def check(name, value, limit):
    checks.append(
        {
            "name": name,
            "value": value,
            "limit": limit,
            "passed": math.isfinite(value) and value <= limit,
        }
    )


MESHES = (8, 4, 2)
DEPTH_MM = 1000
MATERIAL = "Проводник переменного сечения"
SIGMA = 1 / 0.0097784
VOLTAGE = 10
LABEL_X = 100
LABEL_Y = 146


def polygon(points):
    for x, y in points:
        ci_addnode(x, y)
    for number, (x, y) in enumerate(points):
        x2, y2 = points[(number + 1) % len(points)]
        ci_addsegment(x, y, x2, y2)


def terminal(x, y, name, mesh):
    ci_selectsegment(x, y)
    ci_setsegmentprop("<None>", mesh, 0, 0, 0, name)
    ci_clearselected()


def circle(radius, conductor, angle):
    ci_addnode(-radius, 0)
    ci_addnode(radius, 0)
    ci_addarc(-radius, 0, radius, 0, 180, angle)
    ci_addarc(radius, 0, -radius, 0, 180, angle)
    ci_selectarcsegment(0, radius)
    ci_selectarcsegment(0, -radius)
    ci_setarcsegmentprop(angle, "<None>", 0, 0, conductor)
    ci_clearselected()


for level, mesh in enumerate(MESHES):
    newdocument(3)
    ci_probdef("millimeters", "planar", 0, 1e-10, DEPTH_MM, 30)
    ci_addmaterial(MATERIAL, SIGMA, SIGMA, 1, 1, 0, 0)
    ci_addconductorprop("Входной электрод", VOLTAGE, 0, 1)
    ci_addconductorprop("Выходной электрод", 0, 0, 1)
    polygon(
        [
            (0, 0),
            (121, 0),
            (121, 293),
            (0, 293),
            (0, 173),
            (71, 173),
            (71, 123),
            (0, 123),
        ]
    )
    terminal(60, 293, "Входной электрод", mesh)
    terminal(60, 0, "Выходной электрод", mesh)
    ci_addblocklabel(LABEL_X, LABEL_Y)
    ci_selectlabel(LABEL_X, LABEL_Y)
    ci_setblockprop(MATERIAL, 0, mesh, 1)
    ci_clearselected()
    model = directory + "/model-%02d.fec" % (level + 1)
    ci_saveas(model)
    ci_close()
    # Проверяется именно записанная версия с кириллическими именами.
    opendocument(model)
    ci_analyze(1)
    ci_loadsolution()
    co_smooth("off")
    potential_in, current_in = co_getconductorproperties("Входной электрод")
    potential_out, current_out = co_getconductorproperties("Выходной электрод")
    current = abs(complex(current_in))
    co_groupselectblock(1)
    power = real(co_blockintegral(0))
    co_clearblock()
    balance = abs(complex(current_in) + complex(current_out)) / current
    power_error = abs(power - VOLTAGE * current) / (VOLTAGE * current)
    row = {
        "mesh_mm": mesh,
        "model": "model-%02d.fec" % (level + 1),
        "nodes": co_numnodes(),
        "elements": co_numelements(),
        "I_A": current,
        "R_ohm": VOLTAGE / current,
        "P_W": power,
        "current_balance_relative": balance,
        "power_balance_relative": power_error,
    }
    values = co_getpointvalues(96, 148)
    row["J_neck_A_m2"] = math.sqrt(
        abs(complex(values[1])) ** 2 + abs(complex(values[2])) ** 2
    )
    records.append(row)
    check("Баланс токов, сетка %d" % (level + 1), balance, 1e-6)
    check("Баланс мощности, сетка %d" % (level + 1), power_error, 1e-6)
    co_close()
    ci_close()
check(
    "Сходимость сопротивления",
    abs(records[-1]["R_ohm"] / records[-2]["R_ohm"] - 1),
    0.005,
)
check(
    "Сходимость плотности тока вдали от входящего угла",
    abs(records[-1]["J_neck_A_m2"] / records[-2]["J_neck_A_m2"] - 1),
    0.01,
)
# Нижняя граница следует из одномерной цепи с равномерным током в каждом сечении.
lower = 0.0097784 * (0.243 / 0.121 + 0.050 / 0.050)
check(
    "Вариационная нижняя граница сопротивления",
    max(0, lower / records[-1]["R_ohm"] - 1),
    1e-6,
)

result = {
    "runtime": "FEMM 5.0; embedded MicroPython",
    "records": records,
    "checks": checks,
    "passed": all(item["passed"] for item in checks),
}
with open(directory + "/result.json", "w") as output:
    json.dump(result, output)
print(json.dumps(result))
assert result["passed"]
