"""Независимая проверка силы через коэнергию при I=6 А, FEMM 5.

Геометрические определения читаются из точной reproduce.py без выполнения
её основной серии. Это другой метод извлечения силы той же геометрии,
а не независимая реконструкция геометрии или материалов. Новые решения
никогда не перезаписывают исходную серию или прежние проверки.
"""
import os
import sys
import json

audit_file = globals().get('__file__','') or (sys.argv[0] if sys.argv else '')
audit_base = os.path.dirname(os.path.abspath(audit_file)) if audit_file else os.getcwd()
exact_source_path = globals().get('BUL_SOURCE_PATH',audit_base+'/reproduce.py')
exact_source = open(exact_source_path).read()
marker = "\nfor family in ['u-core','pot-core']:\n"
if exact_source.count(marker)!=1:
    raise ValueError('Не найден однозначный конец frozen definitions в reproduce.py')
BUL_MODE = 'nonlinear-force-audit'
BUL_OUTPUT_ROOT = globals().get('BUL_AUDIT_OUTPUT_ROOT',audit_base)
exec(exact_source.split(marker)[0],globals())
FORCE_DERIVATIVES = []
for family in ['u-core','pot-core']:
    baseline = solve(family,'нелинейный-контроль',current=6,nonlinear=True,mesh_factor=.5)
    estimates = []
    for step in [.1,.05]:
        minus = solve(family,'нелинейная-коэнергия-минус-%g'%step,gap=1-step,current=6,nonlinear=True,mesh_factor=.5)
        plus = solve(family,'нелинейная-коэнергия-плюс-%g'%step,gap=1+step,current=6,nonlinear=True,mesh_factor=.5)
        force = (minus['coenergy_J']-plus['coenergy_J'])/(2*step*.001)
        estimates.append({'step_mm':step,'attraction_from_coenergy_N':force,
            'weighted_Maxwell_N':baseline['attraction_N'],
            'relative_difference':relative(force,baseline['attraction_N']),
            'minus_model':minus['model'],'plus_model':plus['model']})
    error = estimates[-1]['relative_difference']
    check(family+'-nonlinear-force-methods',error<.04,error,.04,'I=6 А, условная B(H), два физических зазора открываются одновременно; фиксированный ток.')
    error = relative(estimates[0]['attraction_from_coenergy_N'],estimates[1]['attraction_from_coenergy_N'])
    check(family+'-nonlinear-derivative-step',error<.02,error,.02,'Два центральных шага коэнергии: h=0.1 и0.05 мм.')
    FORCE_DERIVATIVES.append({'family':family,'baseline_model':baseline['model'],
        'current_A':6,'gap_mm':1,'mesh_factor':.5,'estimates':estimates})
for row in RECORDS:
    check(row['model']+'-roundtrip',row['solution_reopened_matches'],row['solution_reopened_linkage_Wb'],row['linkage_Wb'],'Сохранение и повторное открытие точного решения с русской цепью.')
    check(row['model']+'-energy',row['energy_identity_relative_error']<.003,row['energy_identity_relative_error'],.003,'W+Wprime=I*Psi для нелинейной безгистерезисной модели.')
    check(row['model']+'-volume',row['armature_volume_relative_error']<1e-8,row['armature_volume_relative_error'],1e-8,'Объём выбранного якоря.')
    check(row['model']+'-attraction',row['attraction_N']>0,row['attraction_N'],0,'Правильное направление силы.')
result = {'schema':'bul-apparatus-nonlinear-force-native-v1','implementation':repr(sys.implementation),
    'records':RECORDS,'force_derivatives':FORCE_DERIVATIVES,'checks':CHECKS,
    'passed':all(row['passed'] for row in CHECKS),
    'geometry_revision':CONTRACT['revision'],'definition_source':exact_source_path,
    'scope':'Отдельная проверка Maxwell / производная коэнергии только I=6А,delta1мм,mesh0.5. Геометрия и B(H) взяты из exact source; весь нелинейный диапазон не аттестуется.'}
with open(OUT+'/result.json','w') as handle:
    json.dump(result,handle)
print('BUL_NONLINEAR_FORCE_PASSED',result['passed'],len(RECORDS),OUT)
if not result['passed']:
    raise RuntimeError('Нелинейная сила: критерии не выполнены; результаты сохранены')
