"""Общие геометрические и измерительные операции встроенного Python FEMM 5."""
import math, json, os
MU0=4e-7*math.pi
nodes={}
records=[]
def scalar(v):return complex(v).real
def pair(v):z=complex(v);return [z.real,z.imag]
def polar(r,a):t=math.radians(a);return r*math.cos(t),r*math.sin(t)
def node(r,a,group=0):
    x,y=polar(r,a);key=(round(x,9),round(y,9))
    if key not in nodes:
        mi_addnode(x,y);nodes[key]=True
        mi_selectnode(x,y);mi_setnodeprop('<None>',group);mi_clearselected()
    return x,y
def line(r1,a1,r2,a2,group=0):
    p=node(r1,a1,group);q=node(r2,a2,group)
    mi_addsegment(p[0],p[1],q[0],q[1])
    mi_selectsegment((p[0]+q[0])/2,(p[1]+q[1])/2);mi_setsegmentprop('<None>',0,1,0,group);mi_clearselected()
def arc(r,a,b,group=0,step=1,boundary='<None>'):
    if b<a:a,b=b,a
    p=node(r,a,group);q=node(r,b,group)
    mi_addarc(p[0],p[1],q[0],q[1],b-a,step)
    x,y=polar(r,(a+b)/2);mi_selectarcsegment(x,y);mi_setarcsegmentprop(step,boundary,0,group);mi_clearselected()
def circle(r,group=0,step=1,boundary='<None>'):
    arc(r,0,180,group,step,boundary);arc(r,180,360,group,step,boundary)
def sector(r1,r2,a,b,group=0,step=1):
    arc(r1,a,b,group,step);arc(r2,a,b,group,step);line(r1,a,r2,a,group);line(r1,b,r2,b,group)
def material(name,mu=1,sigma=0,j=0,hc=0):
    mi_addmaterial(name,mu,mu,hc,j,sigma)
def label(r,a,name,mesh=1,group=0,circuit='<None>',turns=1):
    x,y=polar(r,a);mi_addblocklabel(x,y);mi_selectlabel(x,y);mi_setblockprop(name,0,mesh,circuit,0,group,turns);mi_clearselected()
def setup(frequency=0,depth=100):
    global nodes
    nodes={};newdocument(0);mi_probdef(frequency,'millimeters','planar',1e-9,depth,30)
    material('Воздух');mi_addboundprop('Внешний потенциал A=0',0,0,0,0,0,0,0,0,0)
def outer(r,mesh=3):
    circle(r,boundary='Внешний потенциал A=0');label(r-2,17,'Воздух',mesh)
def gauge(r):
    mi_addpointprop('Опорный потенциал',0,0);x,y=node(r,0);mi_selectnode(x,y);mi_setnodeprop('Опорный потенциал',0);mi_clearselected()
def steel(name='Сталь линейная',mu=10000):material(name,mu)
def nonlinear_steel():
    mi_getmaterial('M-43 Steel');mi_modifymaterial('M-43 Steel',0,'Сталь M-43');mi_modifymaterial('Сталь M-43',5,0);mi_modifymaterial('Сталь M-43',6,0)
def close():
    for f in [mo_close,mi_close]:
        try:f()
        except Exception:pass
def save_result():
    with open('result.json','w') as f:json.dump({'records':records},f)
def field_ring(radius,count=512):
    out=[];mo_smooth('off')
    for k in range(count):
        t=2*math.pi*k/count;c,s=math.cos(t),math.sin(t);v=mo_getpointvalues(radius*c,radius*s)
        br=complex(v[1])*c+complex(v[2])*s;bt=-complex(v[1])*s+complex(v[2])*c
        out.append([pair(br),pair(bt)])
    return out
def spatial(rows,order,column=0):
    n=len(rows);re=0j;im=0j
    for k,row in enumerate(rows):
        z=complex(*row[column]);a=2*math.pi*order*k/n
        re+=z*math.cos(a);im-=z*math.sin(a)
    return 2*re/n,2*im/n
def real_spatial_amp(rows,order):
    a,b=spatial(rows,order)
    return math.sqrt(a.real*a.real+b.real*b.real)
def ring_torque(rows,radius_mm,depth_mm,ac=False):
    value=sum((complex(*r[0])*complex(*r[1]).conjugate()).real for r in rows)/len(rows)
    return 2*math.pi*(radius_mm*.001)**2*(depth_mm*.001)*value/MU0*(.5 if ac else 1)
def solve(name,meta,radius=None,rotor_group=None,body_groups=None):
    mi_setcomment(meta['conditions']);mi_saveas(name+'.fem');mi_close();opendocument(name+'.fem');mi_analyze(1);mi_loadsolution()
    mo_clearblock();mo_groupselectblock();integrals={}
    for i,key in [(0,'AJ_J'),(2,'W_J'),(17,'Wprime_J'),(4,'Pjoule_W')]:integrals[key]=pair(mo_blockintegral(i))
    mo_clearblock();r=dict(meta);r.update({'id':name,'model':name+'.fem','solution':name+'.ans','nodes':mo_numnodes(),'elements':mo_numelements(),'problem_info':[scalar(v) for v in mo_getprobleminfo()],'integrals':integrals})
    if radius is not None:r['radius_mm']=radius;r['ring']=field_ring(radius)
    if rotor_group is not None:
        mo_groupselectblock(rotor_group);r['T_weighted_Nm']=pair(mo_blockintegral(22));r['P_rotor_W']=scalar(mo_blockintegral(4));mo_clearblock()
    if body_groups:
        r['bodies']={}
        for g in body_groups:
            mo_groupselectblock(g);r['bodies'][str(g)]={'P_W':scalar(mo_blockintegral(4)),'A_integral':pair(mo_blockintegral(1)),'area_m2':scalar(mo_blockintegral(5))};mo_clearblock()
    mo_close();mi_close();opendocument(name+'.fem');mi_loadsolution();mo_groupselectblock();check=complex(mo_blockintegral(2));mo_clearblock();mo_close();mi_close()
    before=complex(*integrals['W_J']);r['roundtrip_relative_error']=abs(check-before)/max(abs(before),1e-20)
    records.append(r);save_result();print('SOLVED',name,r['nodes'])
    return r
