"""Занятия 11–15: самостоятельные ненулевые решения учебной модели."""
import sys,json,math,os
ROOT=globals().get('TVV_ROOT','')
if not ROOT:raise ValueError('Задайте TVV_ROOT')
if ROOT in sys.path:sys.path.remove(ROOT)
sys.path.insert(0,ROOT)
# Повторный запуск редактора не должен использовать старые модули другой копии.
for module in ('run_record','geometry','physics','build','field_results','material_variants'):
    sys.modules.pop(module,None)
from run_record import start,finish,save_json,sha
from build import build
from field_results import collect
from material_variants import variant
names=['run_fields.py','field_results.py','run_record.py','build.py','geometry.py','physics.py',
       'machine.json','winding.json','rotor-bh.json','stator-bh.json','stator-cross-bh.json','field-plan.json','model-scope.json','current-mapping.json','material_variants.py']
resume=globals().get('TVV_RESUME_DIR','')
selected=globals().get('TVV_CASE_ID',None);series=globals().get('TVV_SERIES','four-states')
part=globals().get('TVV_PART',0);parts=globals().get('TVV_PARTS',1)
if parts<1 or not 0<=part<parts:raise ValueError('Неверная часть серии')
if resume:
    folder=resume
    with open(folder+'/receipt.json') as f:record=json.loads(f.read())
    if (record.get('selected_case'),record.get('series'),record.get('part',0),record.get('parts',1))!=(selected,series,part,parts):
        raise ValueError('Продолжение требует того же набора заданий')
    for name,digest in record['inputs_and_programs'].items():
        if sha(ROOT+'/'+name)!=digest or sha(folder+'/snapshot/'+name)!=digest:
            raise ValueError('Изменены входы или версия программы: '+name)
    if 'case_outputs' not in record:raise ValueError('В этой квитанции нет проверяемых промежуточных результатов')
else:
    folder,record=start(ROOT,globals().get('TVV_OUTPUT_ROOT',ROOT+'/runs'),'fields',names,'nonzero teaching model; no factory accuracy claim')
    record['case_outputs']={}
def load(name):
    with open(folder+'/snapshot/'+name+'.json') as f:return json.loads(f.read())
plan=load('field-plan');scope=load('model-scope');rows=[]
if scope['factory_accuracy_claim']:raise ValueError('Необоснованное заявление о заводской точности')
record.update(selected_case=selected,series=series,part=part,parts=parts,status='running')
save_json(folder+'/receipt.json',record)
try:
    cases=[c for c in plan['cases'] if (c['id']==selected if selected else c.get('series')==series)]
    for index,case in enumerate(cases):
        if index%parts!=part:continue
        if case['id'] in record['case_outputs']:
            for name,digest in record['case_outputs'][case['id']].items():
                if sha(folder+'/'+name)!=digest:raise ValueError('Повреждён результат продолжения: '+name)
            with open(folder+'/'+case['id']+'-result.json') as f:rows.append(json.loads(f.read()))
            print('TVV_FIELD_REUSED',case['id']);continue
        machine=load('machine');winding=load('winding')['rows'];mesh=plan['mesh'].copy()
        for key in mesh:mesh[key]*=case.get('mesh_scale',1.)
        mesh.update(case.get('mesh_overrides',{}))
        for section,values in case.get('overrides',{}).items():machine[section].update(values)
        materials={'rotor':load('rotor-bh'),'stator':load(case.get('stator_curve','stator')+'-bh')}
        machine['_bh_limits']={tag:[v['points'][0][0],v['points'][-1][0]] for tag,v in materials.items()}
        for tag in materials:materials[tag]['points']=variant(materials[tag]['points'],case.get('bh_mode','native'))
        path=folder+'/'+case['id']+'.fem'
        record['active_case']=case['id'];save_json(folder+'/receipt.json',record)
        print('TVV_FIELD_BUILD',case['id'])
        build(globals(),machine,winding,materials,path,mesh,case['theta_deg'],case['currents_dqf_A'])
        mi_close();opendocument(path);mi_analyze(1);mi_loadsolution()
        print('TVV_FIELD_SOLVED',case['id'],mo_numelements())
        row=collect(globals(),machine,winding,case['theta_deg'],case['currents_dqf_A'],folder,case['id'],case.get('coverage',True),case.get('contour_samples',1440))
        row['case']=case;row['mesh']=mesh
        limits=plan['acceptance']
        errors={name:abs(values[2]-row['independent_circuit_flux_Wb'][name])/max(abs(values[2]),limits['flux_absolute_scale_Wb']) for name,values in row['circuits'].items()}
        row['flux_identity_relative_error']=max(errors.values())
        row['energy_identity_relative_error']=abs(row['energy_J']+row['coenergy_J']-row['sum_i_lambda_J'])/max(abs(row['sum_i_lambda_J']),limits['energy_absolute_scale_J'])
        rows.append(row);save_json(folder+'/results.json',rows)
        if row['max_copper_area_relative_error']>1e-8:raise ValueError('Площадь активных областей не совпадает')
        if row['flux_identity_relative_error']>limits['flux_identity_relative']:raise ValueError('Независимое потокосцепление не совпадает')
        if row['energy_identity_relative_error']>limits['energy_identity_relative']:raise ValueError('Не выполнен энергетический интеграл')
        mo_close();mi_close()
        save_json(folder+'/'+case['id']+'-result.json',row)
        record['case_outputs'][case['id']]={name:sha(folder+'/'+name) for name in os.listdir(folder) if name.startswith(case['id']+'.') or name.startswith(case['id']+'-')}
        save_json(folder+'/receipt.json',record);print('TVV_FIELD_CHECKED',case['id'])
    if not rows:raise ValueError('Не найдено задание расчёта')
    record['checks']={'cases':len(rows),'factory_validation':False,'mesh_convergence':False}
    finish(folder,record,'passed_field_identities_not_convergence')
except Exception as error:
    record['error']=str(error);save_json(folder+'/results.json',rows);finish(folder,record,'failed');raise
print('TVV_FIELDS_RESULT',folder,record['status'])
