"""Обработка полевых серий; только стандартная библиотека встроенного Python."""
import math
from physics import harmonics, induced_emf, relative_change


def integrals(row):
    return dict(lambda_d_Wb=row['lambda_dq0_Wb'][0], lambda_q_Wb=row['lambda_dq0_Wb'][1],
                lambda_f_Wb=row['lambda_f_Wb'], energy_J=row['energy_J'],
                coenergy_J=row['coenergy_J'], torque_Nm=row['torque_weighted_stress_Nm'])


def comparison(reference, candidate, targets):
    """Знаменатель — опорное решение; абсолютные шкалы заданы ДО расчётов."""
    base, other = integrals(reference), integrals(candidate)
    result = {}
    for key in base:
        family = 'torque' if key.endswith('_Nm') else 'energy' if key.endswith('_J') else 'flux'
        unit = {'torque':'Nm', 'energy':'J', 'flux':'Wb'}[family]
        scale = targets[family+'_absolute_scale_'+unit]
        error = relative_change(base[key], other[key], scale)
        limit = targets['torque_relative' if family == 'torque' else 'integrals_relative']
        result[key] = {'reference':base[key], 'candidate':other[key], 'relative_change':error,
                       'signed_change_over_reference_scale':(other[key]-base[key])/max(abs(base[key]),scale),
                       'limit':limit, 'passed':error <= limit}
    return result


def identities(row, limits):
    circuit_sum = sum(v[0]*v[2] for v in row['circuits'].values())
    flux_error = max(abs(v[2]-row['independent_circuit_flux_Wb'][name]) /
                     max(abs(v[2]),limits['flux_absolute_scale_Wb'])
                     for name,v in row['circuits'].items())
    energy_error = abs(row['energy_J']+row['coenergy_J']-circuit_sum) / max(abs(circuit_sum),limits['energy_absolute_scale_J'])
    return {'flux_relative_error':flux_error, 'energy_relative_error':energy_error,
            'passed':flux_error <= limits['flux_identity_relative'] and energy_error <= limits['energy_identity_relative']}


def angular(rows, frequency_Hz=50.0):
    """Полный механический оборот, p=1, токи статора равны нулю."""
    rows = sorted(rows, key=lambda row:row['theta_deg'])
    n = len(rows)
    if n < 12 or n % 4:raise ValueError('Нужна полная серия с N, кратным четырём')
    for k,row in enumerate(rows):
        if abs(row['theta_deg']-360*k/n)>1e-8:raise ValueError('Пропущено или повторено угловое положение')
        if row['currents_dqf_A'] != rows[0]['currents_dqf_A'] or row['currents_dqf_A'][:2] != [0,0]:
            raise ValueError('Это обработка холостого хода с постоянным возбуждением')
    levels=[]
    for stride in (4,2,1):
        selected=rows[::stride]; count=len(selected); phases=[]
        for phase in range(3):
            spectrum=harmonics([r['lambda_abc_Wb'][phase] for r in selected],count//2-1)
            phases.append({'flux_spectrum':spectrum,
                           'emf_spectrum':[induced_emf(h,2*math.pi*frequency_Hz) for h in spectrum],
                           'fundamental_rms_V':induced_emf(spectrum[1],2*math.pi*frequency_Hz)['rms']})
        line=harmonics([r['lambda_abc_Wb'][0]-r['lambda_abc_Wb'][1] for r in selected],count//2-1)
        levels.append({'samples':count,'step_deg':360/count,'phases':phases,
                       'line_AB_emf_spectrum':[induced_emf(h,2*math.pi*frequency_Hz) for h in line],
                       'mean_torque_Nm':sum(r['torque_weighted_stress_Nm'] for r in selected)/count,
                       'mean_lambda_f_Wb':sum(r['lambda_f_Wb'] for r in selected)/count})
    changes=[]
    for coarse,fine in zip(levels,levels[1:]):
        errors=[relative_change(fine['phases'][p]['fundamental_rms_V'],coarse['phases'][p]['fundamental_rms_V'],1.0) for p in range(3)]
        changes.append({'coarse_N':coarse['samples'],'fine_N':fine['samples'],
                        'fundamental_relative_changes':errors,'passed_1_percent':max(errors)<=.01})
    half=n//2
    periodicity=max(abs(rows[k]['lambda_abc_Wb'][p]+rows[k+half]['lambda_abc_Wb'][p])
                    for k in range(half) for p in range(3))
    return {'levels':levels,'convergence':changes,'half_period_antisymmetry_max_residual_Wb':periodicity,
            'scope':'Fundamental at no load only. No claim of convergence of all harmonics or force ripple.'}
