"""Стационарные уравнения в единой пассивной dq-нормировке, без скрытых поправок."""
import math
from numerics import solve_linear

def electrical(current,flux,rs,omega=2*math.pi*50,end_inductance=0.):
    d,q,f=current;ld=flux[0]+end_inductance*d;lq=flux[1]+end_inductance*q
    ud=rs*d-omega*lq;uq=rs*q+omega*ld
    p=-1.5*(ud*d+uq*q);reactive=-1.5*(uq*d-ud*q)
    apparent=math.sqrt(p*p+reactive*reactive)
    return {'ud_V':ud,'uq_V':uq,'U_line_rms_V':math.sqrt(1.5*(ud*ud+uq*uq)),
            'I_phase_rms_A':math.sqrt((d*d+q*q)/2),'P_generator_W':p,'Q_generator_var':reactive,
            'power_factor':p/apparent if apparent else None,'delta_deg':math.degrees(math.atan2(ud,uq)),
            'torque_dq_Nm':1.5*(ld*q-lq*d),'stator_copper_loss_W':1.5*rs*(d*d+q*q)}

def search(evaluate,initial,bounds,tolerance=1e-3,maximum=18):
    """Ограниченный Broyden в безразмерных координатах; журнал каждой полевой оценки.

    evaluate returns already scaled residuals. No extrapolation or acceptance
    outside bounds; exhausted search returns an explicit failure.
    """
    n=len(initial);width=[b-a for a,b in bounds]
    if any(w<=0 for w in width):raise ValueError('Неверные границы поиска')
    z=[(initial[k]-bounds[k][0])/width[k] for k in range(n)];log=[]
    def physical(v):return [bounds[k][0]+width[k]*v[k] for k in range(n)]
    def call(v):
        if any(not 0<=x<=1 for x in v):raise ValueError('Выход поиска за границы')
        x=physical(v);r=evaluate(x)
        if len(r)!=n or any(not math.isfinite(x) for x in r):raise ValueError('Неверная невязка')
        log.append({'x':x,'residual':r,'norm':max(abs(x) for x in r)})
        return r
    r=call(z);jac=None
    for iteration in range(maximum):
        norm=max(abs(x) for x in r)
        if norm<=tolerance:return {'converged':True,'x':physical(z),'log':log,'iterations':iteration}
        if jac is None:
            jac=[[0.]*n for _ in range(n)]
            for j in range(n):
                h=.002 if z[j]<=.998 else -.002;trial=z[:];trial[j]+=h;rr=call(trial)
                for i in range(n):jac[i][j]=(rr[i]-r[i])/h
        try:step=solve_linear(jac,[-x for x in r])
        except ValueError:return {'converged':False,'x':physical(z),'log':log,'reason':'Вырожденный якобиан'}
        factor=min(1.,.15/max(max(abs(s) for s in step),1e-30));accepted=False
        for attempt in range(6):
            trial=[z[j]+factor*step[j] for j in range(n)]
            if all(0<=x<=1 for x in trial):
                rr=call(trial)
                if max(abs(v) for v in rr)<norm:accepted=True;break
            factor*=.5
        if not accepted:
            if iteration and jac is not None:jac=None;continue
            return {'converged':False,'x':physical(z),'log':log,'reason':'Нет уменьшающего шага внутри области'}
        dz=[trial[j]-z[j] for j in range(n)];den=sum(v*v for v in dz)
        for i in range(n):
            correction=rr[i]-r[i]-sum(jac[i][j]*dz[j] for j in range(n))
            for j in range(n):jac[i][j]+=correction*dz[j]/max(den,1e-30)
        z=trial;r=rr
    return {'converged':False,'x':physical(z),'log':log,'reason':'Исчерпано число итераций'}
