"""Ток утечки коаксиальной линии.
Среда: встроенный 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 = (0.4, 0.2, 0.1)
DEPTH_MM = 10e6
MATERIAL = "Слабопроводящая изоляция"
SIGMA = 1.4e-9
VOLTAGE = 1
LABEL_X = 3
LABEL_Y = 0


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)
    circle(2, "Входной электрод", 4 / (2**level))
    circle(5, "Выходной электрод", 4 / (2**level))
    ci_addblocklabel(0, 0)
    ci_selectlabel(0, 0)
    ci_setblockprop("<No Mesh>", 0, mesh, 0)
    ci_clearselected()
    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,
    }

    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,
)
reference = math.log(5 / 2) / (2 * math.pi * 10000 * SIGMA)
check(
    "Логарифмическая формула сопротивления изоляции",
    abs(records[-1]["R_ohm"] / reference - 1),
    0.001,
)

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"]
