"""FEMM 5, встроенный MicroPython. Аналитика и обработка ранее решённой FE-карты.
Рабочая папка: распакованный комплект audit-machines. Новые поля не решаются.
"""
import math,json,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)))

def abc(d,q,theta):
 return [d*math.cos(theta-k*2*math.pi/3)-q*math.sin(theta-k*2*math.pi/3) for k in range(3)]
def dq(v,theta):
 return [2/3*sum(v[k]*math.cos(theta-k*2*math.pi/3) for k in range(3)),-2/3*sum(v[k]*math.sin(theta-k*2*math.pi/3) for k in range(3))]
checks=[]
for theta in [0,.3,1.7,6.28]:
 v=abc(-3,4,theta);back=dq(v,theta)
 checks.append(max(abs(back[0]+3),abs(back[1]-4),abs(sum(v)),abs(sum(x*x for x in v)-37.5))<1e-12)
# Геометрическая сумма векторов ЭДС q соседних пазов.
def kw(q,alpha,pitch,harmonic):
 z=sum(complex(math.cos(harmonic*k*alpha),math.sin(harmonic*k*alpha)) for k in range(q))/q
 return abs(z)*abs(math.sin(harmonic*pitch/2))
wind=[]
for q,alpha,pitch,label in [(1,math.pi/3,math.pi,'Q12_2p4_full'),(1,math.pi/3,2*math.pi/3,'Q12_2p4_short'),(3,math.pi/9,math.pi,'Q36_2p4_distributed')]:
 wind.append({'case':label,'harmonics':[{'nu':h,'kw':kw(q,alpha,pitch,h)} for h in [1,3,5,7]]})
with open('ipm-map-compact.json') as f:data=json.load(f)
speeds=[];R=.1;U=4.;Imax=10.;p=data['p']
for n in [100,200,300,500,750,1000,1500,2000,3000]:
 omega=2*math.pi*n/60*p;admissible=[]
 for point in data['points']:
  if point['iq_A']<0 or point['I_A']>Imax+1e-9:continue
  ud=R*point['id_A']-omega*point['psiq_Wb'];uq=R*point['iq_A']+omega*point['psid_Wb'];u=math.sqrt(ud*ud+uq*uq)
  if u<=U:admissible.append((point['Tmean_Nm'],point,u))
 if admissible:
  T,point,u=max(admissible,key=lambda v:v[0]);speeds.append({'n_rpm':n,'T_Nm':T,'Pmechanical_W':T*2*math.pi*n/60,'id_A':point['id_A'],'iq_A':point['iq_A'],'Upeak_V':u,'beta_deg':point['beta_deg'],'sampled_feasible':True})
 else:speeds.append({'n_rpm':n,'sampled_feasible':False,'meaning':'Ни одна из исследованных точек двух окружностей не допустима; отсутствие всех физических режимов не доказано.'})
# Независимая линейная учебная машина: сеточный максимум в токовом диске.
linear=[]
for n in [0,500,1000,1500,2000,3000]:
 omega=4*2*math.pi*n/60;best=None
 for j in range(201):
  id=-10+j*.05
  for k in range(201):
   iq=k*.05
   if id*id+iq*iq>100.000001:continue
   psid=.08+.005*id;psiq=.008*iq
   ud=.4*id-omega*psiq;uq=.4*iq+omega*psid
   if ud*ud+uq*uq>48*48:continue
   T=6*(psid*iq-psiq*id)
   if best is None or T>best['T_Nm']:best={'n_rpm':n,'T_Nm':T,'id_A':id,'iq_A':iq,'Upeak_V':math.sqrt(ud*ud+uq*uq),'Pmechanical_W':T*2*math.pi*n/60}
  
 linear.append(best or {'n_rpm':n,'sampled_feasible':False})
result={'scope':'Аналитические тождества и постобработка существующих FE-данных, не новое решение FEMM. Ограничения R=0.1 Ом, Uамп=4 В заданы для учебного сравнения и не являются паспортом исходной машины. Линейная машина — отдельная аналитическая постановка.','checks_passed':all(checks),'winding_factors':wind,'native_map_operating_envelope':speeds,'linear_reference':linear,'dq_roundtrip_3_4_theta_0':abc(-3,4,0)}
assert all(checks)
with open('dq-windings-voltage-result.json','w') as f:json.dump(result,f)
print(json.dumps(result))
