"""Новый опыт Frozen Permeability в FEMM 5. Запускать встроенным Python.
Модель nonlinear-ipm.fem рядом. Исходник не перезаписывается.
"""
import math,json,os,sys
assert sys.implementation.name=='micropython'

# FEMM CLI и команда запуска сохранённого файла передают его путь в sys.argv[0].
# Локальные модели/таблицы ищутся возле программы, а не в случайной рабочей папке.
import os
_saved_file=globals().get('__file__') or (sys.argv[0] if sys.argv else '')
if not _saved_file or not os.path.isfile(_saved_file):
 raise RuntimeError('Запустите сохранённый файл из полного распакованного комплекта командой FEMM Run Script; не вставляйте его частями в консоль.')
os.chdir(os.path.dirname(os.path.abspath(_saved_file)))

try:os.mkdir('frozen-pm-output')
except OSError:pass
phase=['Фаза А','Фаза В','Фаза С']
# Физическая d-ось данной модели имеет электрическое смещение 90 градусов.
id,iq=-3.,8.;theta=math.pi/2
curr=[id*math.cos(theta-k*2*math.pi/3)-iq*math.sin(theta-k*2*math.pi/3) for k in range(3)]
def close():
 for name in ['mo_close','mi_close']:
  try:globals()[name]()
  except Exception:pass
def one(name,previous,only):
 close();opendocument('nonlinear-ipm.fem');mi_setprevious(previous,2 if previous else 0)
 for k,n in enumerate(phase):mi_modifycircprop(n,1,0 if only=='pm' else curr[k])
 if only=='i':mi_modifymaterial('Постоянный магнит',3,0)
 path='frozen-pm-output/'+name+'.fem';mi_saveas(os.path.abspath(path))
 saved_cwd=os.getcwd();os.chdir('frozen-pm-output')
 try:
  mi_analyze(1);mi_loadsolution();mo_smooth('off');samples=[]
  for k in range(180):
   a=2*math.pi*k/180;x=59*math.cos(a);y=59*math.sin(a);v=mo_getpointvalues(x,y)
   samples.append([float(v[1]),float(v[2])])
  mo_clearblock();mo_groupselectblock(2);T=float(mo_blockintegral(22));mo_clearblock()
  result={'model':path,'Bxy_T':samples,'T_WST_Nm':T,'elements':mo_numelements(),'nodes':mo_numnodes()}
 finally:os.chdir(saved_cwd)
 return result
out={'scope':'Нелинейная рабочая точка, затем два линейных замороженных решения с одной сеткой. Сравниваются компоненты B. Складывать отдельные моменты без перекрёстного члена нельзя.','id_A':id,'iq_A':iq,'phase_currents_A':curr}
out['whole']=one('whole','',None)
base='whole.ans'  # относительный путь от папки FEM-файла; solver запускается в ней
out['current_only']=one('current-only',base,'i');out['pm_only']=one('pm-only',base,'pm')
errors=[];peak=0
for whole,a,b in zip(out['whole']['Bxy_T'],out['current_only']['Bxy_T'],out['pm_only']['Bxy_T']):
 errors.append(math.sqrt((whole[0]-a[0]-b[0])**2+(whole[1]-a[1]-b[1])**2));peak=max(peak,math.sqrt(whole[0]**2+whole[1]**2))
out['max_vector_residual_T']=max(errors);out['relative_to_peak_B']=max(errors)/peak
out['torque_cross_residual_Nm']=out['whole']['T_WST_Nm']-out['current_only']['T_WST_Nm']-out['pm_only']['T_WST_Nm']
out['passed']=out['relative_to_peak_B']<.002
close()
with open('frozen-pm-result.json','w') as f:json.dump(out,f)
print('Frozen PM',out['max_vector_residual_T'],out['relative_to_peak_B'],out['passed'])
assert out['passed']
