"""Русская копия прежнего учебного П-магнита I05: встроенный Python FEMM5.

Сохранены точная геометрия, N=100,I=5A,глубина20мм,два зазора1мм,
линейная стальmu=2000 и исходная сеточная настройка. Это отдельная
учебная копия; старые electromagnet.fem/.ans не изменяются.
Для standalone запуска рядом нужны template.fem и parent-baseline.json.
Новый запуск создаёт новый calculated-NN. Расчёт4.8млн элементов
может занимать около минуты и требует памяти/места для крупного .ans.
"""
import os
import sys
import json

script_path=globals().get('__file__','') or (sys.argv[0] if sys.argv else '')
BASE=globals().get('LEGACY_U_ROOT',os.path.dirname(os.path.abspath(script_path))if script_path else os.getcwd())
OUTPUT=globals().get('LEGACY_U_OUTPUT_ROOT',BASE)
index=1
while os.path.exists(OUTPUT+'/calculated-%02d'%index):
    index+=1
OUT=OUTPUT+'/calculated-%02d'%index
os.mkdir(OUT)
baseline=json.load(open(BASE+'/parent-baseline.json'))
CIRCUIT='Катушка возбуждения'
CHECKS=[]


def real(value):
    return float(complex(value).real)


def relative(a,b):
    return abs(a-b)/max(abs(b),1e-20)


def check(name,passed,observed,limit,scope):
    CHECKS.append({'id':name,'passed':bool(passed),'observed':observed,'limit':limit,'scope':scope})


opendocument(BASE+'/template.fem')
path=OUT+'/П-электромагнит-I05-русская-копия.fem'
mi_saveas(path)
mi_close()
opendocument(path)
mi_analyze(1)
mi_loadsolution()
current,voltage,linkage=mo_getcircuitproperties(CIRCUIT)
mo_clearblock()
mo_groupselectblock(2)
force=[real(mo_blockintegral(18)),real(mo_blockintegral(19))]
volume=real(mo_blockintegral(10))
mo_clearblock()
mo_groupselectblock()
energy=real(mo_blockintegral(2))
coenergy=real(mo_blockintegral(17))
mo_clearblock()
point=mo_getpointvalues(20,20.5)
row={'tag':'I05-русская-копия','model':'П-электромагнит-I05-русская-копия.fem',
    'solution':'П-электромагнит-I05-русская-копия.ans','current_A':real(current),
    'linkage_Wb':real(linkage),'force_xy_N':force,'attraction_N':-force[1],
    'energy_J':energy,'coenergy_J':coenergy,'armature_volume_m3':volume,
    'armature_volume_exact_m3':60*20*20*1e-9,'point_mm':[20,20.5],
    'point_B_T':[real(point[1]),real(point[2])],'elements':mo_numelements(),'nodes':mo_numnodes(),
    'saved_reopened_solved':True,'problem_type':'planar','depth_mm':20,'turns':100,
    'gap_each_mm':1,'pole_width_mm':20,'steel_relative_permeability':2000}
mo_close()
mi_close()
opendocument(path)
mi_loadsolution()
reopened=real(mo_getcircuitproperties(CIRCUIT)[2])
row['solution_reopened_linkage_Wb']=reopened
row['solution_reopened_matches']=abs(reopened-real(linkage))<1e-12
mo_close()
mi_close()
expected={'force_x_N':baseline['force_x_N']['real'],'force_y_N':baseline['force_y_N']['real'],
    'linkage_Wb':baseline['circuits']['Coil']['linkage_Wb']['real'],
    'energy_J':baseline['energy_J']['real'],'coenergy_J':baseline['coenergy_J']['real']}
observed={'force_x_N':force[0],'force_y_N':force[1],'linkage_Wb':real(linkage),
    'energy_J':energy,'coenergy_J':coenergy}
comparisons={}
for key,value in observed.items():
    error=relative(value,expected[key])
    comparisons[key]={'new_copy':value,'parent_baseline':expected[key],'relative_difference':error}
    check(key+'-matches-parent',error<1e-7,error,1e-7,'Сравнение заново решённой русской копии с точным ранее проверенным baseline; исходники неизменны.')
check('identical-element-count',row['elements']==baseline['mesh']['elements'],row['elements'],baseline['mesh']['elements'],'Сеточные параметры не менялись; итоговое число элементов совпало.')
check('identical-node-count',row['nodes']==baseline['mesh']['nodes'],row['nodes'],baseline['mesh']['nodes'],'Число узлов совпало с сохранённым baseline.')
check('russian-roundtrip',row['solution_reopened_matches'],reopened,real(linkage),'Русское имя цепи после сохранения, повторного открытия и решения.')
error=relative(volume,row['armature_volume_exact_m3'])
check('correct-armature-volume',error<1e-8,error,1e-8,'Выбран весь якорь60x20мм,depth20мм.')
error=relative(energy+coenergy,real(current)*real(linkage))
check('energy-identity',error<.003,error,.003,'Линейная магнитостатика: W+Wprime=I*Psi.')
check('current-preserved',real(current)==5,real(current),5,'Исходный токI05 равен5А.')
result={'schema':'bul-legacy-u-russian-native-v1','implementation':repr(sys.implementation),
    'records':[row],'comparisons':comparisons,'checks':CHECKS,'passed':all(c['passed']for c in CHECKS),
    'groups':{'1':'Неподвижный магнитопровод','2':'Подвижный якорь','3':'Катушка возбуждения','4':'Воздух'},
    'scope':'Новая русская учебная копия прежнего I05, точная геометрия и сетка сохранены, решение заново выполнено FEMM5. Не новая полевая серия по зазорам и не модель нового семействаБуля.'}
with open(OUT+'/result.json','w')as output:
    json.dump(result,output)
print('LEGACY_U_RUSSIAN_PASSED',result['passed'],row['elements'],force,real(linkage),energy)
if not result['passed']:
    raise RuntimeError('Русская копияI05 не совпала с baseline; результаты сохранены')
