در بازبینیِ تابعِ محاسباتِ برق، تغییرِ یک علامت می‌تواند به‌اندازهٔ تغییرِ یک فرمول مهم باشد. برای دو سلفِ کوپله، جملهٔ متقابل به جهتِ مرجعِ جریان وابسته است؛ حذفِ علامتِ آن با قدرمطلق، اصلاحِ بی‌خطری نیست. این درس یک مدلِ خطیِ ایدئال را با پاسخِ دستی و مرجعِ کسری می‌سنجد. عددها ساختگی‌اند؛ سیم‌پیچ، ترانسفورماتور، میدانِ واقعی یا گذرای مدار اندازه‌گیری و شبیه‌سازی نشده است.

۱. جریانِ لحظه‌ای، جهتِ مرجع و واحدها

ترمینال‌های مرجع دو سیم‌پیچ و قرارداد جهت جریان
تصویر مفهومی: ترمینال‌های مرجع دو سیم‌پیچ و قرارداد جهت جریان؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

دو اندوکتانسِ خودیِ مثبت L1 و L2 و اندوکتانسِ متقابلِ علامت‌دارِ M داریم. هندسه ثابت، رابطهٔ شار و جریان خطی و دستگاه متقابل فرض می‌شود. جریان‌های i1 و i2 مقدارهای لحظه‌ایِ علامت‌دارند؛ فازور یا RMS نیستند. مرجعِ هر جریان و علامتِ M باید با هم سازگار باشند. این تعریف، دستورِ اتصالِ یک قطعهٔ واقعی یا استخراجِ M از شکلِ سیم‌پیچ نیست.

درسِ اندوکتانسِ متقابلِ OpenStax، واحدِ هانری و برابریِ ضرایبِ متقابل را توضیح می‌دهد. در این قرارداد، هر اندوکتانس بر حسبِ H و جریان بر حسبِ A وارد می‌شود؛ حاصلِ H ضرب‌در A²، ژول است. میلی‌هانری یا میلی‌آمپر خودکار تبدیل نمی‌شوند. عددِ درست با برچسبِ واحدِ اشتباه، ورودیِ معناییِ درستی نمی‌سازد.

فرمولِ انرژیِ مدل، نصفِ L1i1² به‌علاوهٔ نصفِ L2i2² به‌علاوهٔ Mi1i2 است. جملهٔ متقابل یک بار می‌آید؛ نه نصف و نه دوبرابرِ آن. اگر جهتِ مرجعِ سیم‌پیچِ دوم را برگردانیم، i2 و M هر دو علامت عوض می‌کنند و نمایشِ همان وضعیت، انرژیِ یکسان دارد. عوض‌کردنِ تنها M، با ثابت‌ماندنِ مرجعِ جریان، مدلِ دیگری می‌سازد و لزوماً همان پاسخ را ندارد.

۲. انرژی و هم‌انرژی را بی‌قید برابر نگیریم

سهم‌های جدا و متقابل در مدل انرژی دو سیم‌پیچ
تصویر مفهومی: سهم‌های جدا و متقابل در مدل انرژی دو سیم‌پیچ؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

بخشِ تقابل و هم‌انرژیِ MIT، رابطهٔ شارهای پیوندی با جریان و تعریفِ هم‌انرژی را جدا می‌نویسد. هم‌انرژی برابر مجموعِ جریان ضرب‌در شارِ پیوندی، منهای انرژی است. در مدلِ خطیِ متقابلِ حاضر، هر دو به همان فرمِ درجه‌دو می‌رسند؛ این برابری را به هستهٔ غیرخطی، حرکتِ مکانیکی یا همهٔ سامانه‌های مغناطیسی تعمیم نمی‌دهیم. معادله‌های تصویریِ منبع نیز جدا دیده شده‌اند.

با L1 مثبت، کامل‌کردنِ مربع، انرژی را به دو جمله تبدیل می‌کند: نصفِ L1 ضرب‌در مربعِ i1+(M/L1)i2 و نصفِ (L1L2−M²)/L1 ضرب‌در i2². برای نامنفی‌بودنِ انرژی در همهٔ جریان‌های این مدل، M² نباید از L1L2 بیشتر باشد. شرط روی پارامترهاست؛ مثبت‌بودنِ انرژیِ تنها یک جفتِ جریان، جایِ آن را نمی‌گیرد. پارامترِ نامجاز با بریدنِ مقدارِ منفیِ انرژی اصلاح نمی‌شود.

برابریِ مرز نیز مجاز است: مدل نیمه‌معین می‌شود و ممکن است برای جریانِ ناصفر، انرژی صفر باشد. مثالِ دقیقِ دودویی، L1 برابر ۴/۱۰۲۴، L2 برابر ۹/۱۰۲۴ و M برابر ۶/۱۰۲۴ هانری دارد. برای جریان‌های سه و منفی دو آمپر، هر دو جملهٔ مربع‌شده صفرند. این تابع فقط انرژی می‌خواهد؛ معکوسِ ماتریس نمی‌گیرد که لازم باشد همین حالتِ تکین را برای حلِ یکتا رد کند.

۳. کدِ کامل و مرجعِ دستیِ تغییرِ علامت

مرجع مستقل حساب انرژی در برابر ماژول محاسبه
تصویر مفهومی: مرجع مستقل حساب انرژی در برابر ماژول محاسبه؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

در نمونهٔ اصلی، L1 برابر ۰٫۰۰۴، L2 برابر ۰٫۰۰۹ و M برابر ۰٫۰۰۳ هانری است؛ جریان‌ها دو و منفی یک آمپرند. سهم‌های خودی هشت و چهارونیم میلی‌ژول‌اند و سهمِ متقابل منفی شش میلی‌ژول است؛ جواب شش‌ونیم میلی‌ژول می‌شود. اگر تغییرِ کد فقط علامتِ M را برگرداند، پاسخ هجده‌ونیم میلی‌ژول خواهد شد. هر دو خروجی مثبت‌اند؛ آزمونِ صرفِ نامنفی‌بودن، آن تغییر را کشف نمی‌کند.

"""Bounded ideal reciprocal linear two-coil energy; no hardware simulation."""
from fractions import Fraction
import json
import math
import platform


def number(value, name, low, high):
    if type(value) not in (int, float) or (type(value) is float and not math.isfinite(value)):
        raise ValueError(name + "_type_finite")
    if not low <= value <= high:
        raise ValueError(name + "_range")
    return float(value)


def energy_j(l1, l2, mutual, i1, i2, *, inductance_unit="H", current_unit="A"):
    if type(inductance_unit) is not str or inductance_unit != "H" or type(current_unit) is not str or current_unit != "A":
        raise ValueError("units")
    l1 = number(l1, "l1", 1e-6, 1.0)
    l2 = number(l2, "l2", 1e-6, 1.0)
    mutual = number(mutual, "mutual", -1.0, 1.0)
    i1 = number(i1, "i1", -100.0, 100.0)
    i2 = number(i2, "i2", -100.0, 100.0)
    # The PSD test concerns exact rational values represented by these input floats.
    a, b, m = map(Fraction.from_float, (l1, l2, mutual))
    determinant = a * b - m * m
    if determinant < 0:
        raise ValueError("nonpassive_linear_parameters")
    residual_l = float(determinant / a)
    combined_current = i1 + (mutual / l1) * i2
    # Complete the square; no subtraction of three nearly cancelling energies.
    result = math.fsum((0.5 * l1 * combined_current * combined_current,
                        0.5 * residual_l * i2 * i2))
    if not math.isfinite(result) or result < 0:
        raise ValueError("energy_result")
    return result


def direct_fraction_reference(l1, l2, mutual, i1, i2):
    # Independent direct polynomial on exact represented float inputs, not square completion.
    a, b, m, x, y = (Fraction.from_float(float(v)) for v in (l1, l2, mutual, i1, i2))
    return a * x * x / 2 + b * y * y / 2 + m * x * y


def error(call):
    try:
        call()
    except ValueError as exc:
        return str(exc)
    return None


def run_example():
    checks = {}
    def check(name, value):
        checks[name] = bool(value)
        if not checks[name]:
            raise AssertionError(name)
    close = lambda a, b: math.isclose(a, b, rel_tol=3e-13, abs_tol=1e-15)
    hand = energy_j(0.004, 0.009, 0.003, 2, -1)
    check("hand_6_5_millijoule_signed_cross_term", close(hand, 0.0065))
    check("uncoupled_energy_is_sum_of_two_self_terms", close(energy_j(0.004, 0.009, 0, 2, -1), 0.0125))
    check("zero_currents_and_single_active_coil", energy_j(0.004, 0.009, 0.003, 0, 0) == 0.0 and close(energy_j(0.004, 0.009, 0.003, 2, 0), 0.008))
    check("reverse_both_currents_preserves_energy", energy_j(0.004, 0.009, 0.003, -2, 1) == hand)
    check("reverse_one_reference_requires_mutual_and_current_flip", energy_j(0.004, 0.009, -0.003, 2, 1) == hand)
    wrong_sign = energy_j(0.004, 0.009, -0.003, 2, -1)
    check("mutual_sign_only_change_is_detected_18_5_not_6_5_mj", close(wrong_sign, 0.0185) and not close(wrong_sign, hand))
    check("swap_two_coil_labels_preserves_energy_with_tolerance", close(energy_j(0.009, 0.004, 0.003, -1, 2), hand))
    l1, l2, m = 4 / 1024, 9 / 1024, 6 / 1024
    check("positive_perfect_coupling_semidefinite_nonzero_null_current", energy_j(l1, l2, m, 3, -2) == 0.0)
    check("negative_perfect_coupling_null_current_reference", energy_j(l1, l2, -m, 3, 2) == 0.0)
    check("one_float_above_coupling_bound_rejected", error(lambda: energy_j(l1, l2, math.nextafter(m, math.inf), 1, 1)) == "nonpassive_linear_parameters")
    near = -1.0 + 2.0 ** -30
    stable = energy_j(1.0, 1.0, 1.0, 1.0, near)
    reference = direct_fraction_reference(1.0, 1.0, 1.0, 1.0, near)
    direct_float = math.fsum((0.5, 0.5 * near * near, near))
    check("small_null_direction_energy_matches_exact_reference", stable == float(reference) == 2.0 ** -61 and direct_float == 0.0)
    below = math.nextafter(1.0, 0.0)
    check("near_boundary_positive_determinant_not_clamped_to_zero", energy_j(1.0, 1.0, below, 1.0, -1.0) > 0 and close(energy_j(1.0, 1.0, below, 1.0, -1.0), float(direct_fraction_reference(1, 1, below, 1, -1))))
    check("current_scaling_is_quadratic_in_same_units", close(energy_j(0.004, 0.009, 0.003, 4, -2), 4 * hand))
    count = 0
    for a in (0.25, 1.0):
        for b in (0.25, 1.0):
            for coupling in (-0.25, -0.125, 0.0, 0.125, 0.25):
                for x in (-2.0, -0.5, 0.0, 1.0):
                    for y in (-2.0, -0.5, 0.0, 1.0):
                        actual = energy_j(a, b, coupling, x, y)
                        expected = direct_fraction_reference(a, b, coupling, x, y)
                        if expected < 0 or not close(actual, float(expected)):
                            raise AssertionError("independent_direct_energy_polynomial")
                        count += 1
    check("320_fraction_oracle_cases_one_group", count == 320)
    check("finite_exact_numeric_types_and_units_rejected", all(error(lambda v=v: energy_j(v, 1, 0, 1, 1)) == "l1_type_finite" for v in (True, "1", float("nan"), float("inf")))
          and error(lambda: energy_j(1, 1, 0, False, 1)) == "i1_type_finite"
          and error(lambda: energy_j(1, 1, 0, 1, 1, inductance_unit="mH")) == "units"
          and error(lambda: energy_j(1, 1, 0, 1, 1, current_unit="mA")) == "units")
    check("strict_positive_inductance_and_declared_numeric_bounds", error(lambda: energy_j(0, 1, 0, 1, 1)) == "l1_range"
          and error(lambda: energy_j(10 ** 400, 1, 0, 1, 1)) == "l1_range"
          and error(lambda: energy_j(1, 1.1, 0, 1, 1)) == "l2_range"
          and error(lambda: energy_j(1, 1, 0, 101, 1)) == "i1_range"
          and error(lambda: energy_j(1, 1, 2, 1, 1)) == "mutual_range")
    return {"python_version": platform.python_version(), "model": "fixed-geometry reciprocal linear ideal inductors; signed mutualH; energy=coenergy only under this stated model",
            "units": {"inductance": "H", "current": "A", "energy": "J"},
            "hand_energy_j": hand, "mutual_sign_only_changed_energy_j": wrong_sign,
            "perfect_coupling_nonzero_current_energy_j": 0.0,
            "near_null_stable_energy_j": stable, "near_null_direct_float_energy_j": direct_float,
            "near_null_exact_represented_input_reference": str(reference),
            "fraction_reference_cases_one_group": count,
            "comparison_tolerance": {"relative": 3e-13, "absolute_j": 1e-15, "small_exact_special_fixture": "exact equality to2**-61 J, not hidden by absolute tolerance"},
            "limitations": "Bounded float inputs and exact rational PSD validation, not universal conditioning/exactness. Complete-square rounding can lose tiny combined currents; no arbitrary geometry/nonlinear core/hysteresis/loss/transient/transformer performance or hardware measurement. Nonzero current can have zero energy in singular ideal model.",
            "checks": checks}


if __name__ == "__main__":
    print(json.dumps(run_example(), ensure_ascii=False, indent=2))

API آموزشی فقط int یا float دقیقِ متناهی می‌پذیرد. L1 و L2 بین ده به توان منفی شش و یک هانری، M بین منفی یک و یک هانری و جریان‌ها بین منفی صد و صد آمپرند. شرطِ کوپلینگ هم جدا لازم است. bool، NaN، بی‌نهایت و واحدِ نامناسب رد می‌شوند؛ عددِ صحیحِ بسیار بزرگ پیش از تبدیلِ شناور، با کرانِ دامنه رد می‌شود. این حدود، مشخصاتِ قطعه یا معیارِ ایمنی نیستند.

شرطِ M² با Fraction.from_float روی مقدارِ دقیقِ نمایش‌یافتهٔ ورودی بررسی می‌شود؛ نه روی اعشارِ آرمانیِ نوشته‌شده در ذهنِ ما. پس یک float بالاتر از مرزِ دقیقِ نمونهٔ دودویی، نامجاز است. ضریبِ باقی‌ماندهٔ مربع از همان دترمینانِ کسری گرفته و سپس شناور می‌شود. مسیرِ اصلی، دو جملهٔ نامنفی را جمع می‌کند؛ پارامترِ نامجاز را با تلورانس به مرز نمی‌چسباند.

۴. مرجعِ مستقل و شاهدِ انرژیِ بسیار کوچک

تغییر ارجاع یک سیم‌پیچ همراه با تغییر پارامتر متقابل
تصویر مفهومی: تغییر ارجاع یک سیم‌پیچ همراه با تغییر پارامتر متقابل؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

مرجعِ Fraction تفاوتِ مقدارِ دقیقِ float با کسرِ اعشاری را روشن می‌کند. مرجعِ آزمون، چندجمله‌ایِ مستقیمِ سه جملهٔ انرژی را با کسرها حساب می‌کند؛ شکلِ کامل‌کردنِ مربعِ تابعِ اصلی را تکرار نمی‌کند. ۳۲۰ ترکیبِ کوچک، یک گروه از شانزده گروهِ بررسی‌اند. جابه‌جاییِ نامِ دو سلف، تغییرِ هم‌زمانِ مرجع و مقیاس‌کردنِ جریان نیز کنترل شده‌اند.

{
  "python_version": "3.14.5",
  "model": "fixed-geometry reciprocal linear ideal inductors; signed mutualH; energy=coenergy only under this stated model",
  "units": {
    "inductance": "H",
    "current": "A",
    "energy": "J"
  },
  "hand_energy_j": 0.0065,
  "mutual_sign_only_changed_energy_j": 0.0185,
  "perfect_coupling_nonzero_current_energy_j": 0.0,
  "near_null_stable_energy_j": 4.336808689942018e-19,
  "near_null_direct_float_energy_j": 0.0,
  "near_null_exact_represented_input_reference": "1/2305843009213693952",
  "fraction_reference_cases_one_group": 320,
  "comparison_tolerance": {
    "relative": 3e-13,
    "absolute_j": 1e-15,
    "small_exact_special_fixture": "exact equality to2**-61 J, not hidden by absolute tolerance"
  },
  "limitations": "Bounded float inputs and exact rational PSD validation, not universal conditioning/exactness. Complete-square rounding can lose tiny combined currents; no arbitrary geometry/nonlinear core/hysteresis/loss/transient/transformer performance or hardware measurement. Nonzero current can have zero energy in singular ideal model.",
  "checks": {
    "hand_6_5_millijoule_signed_cross_term": true,
    "uncoupled_energy_is_sum_of_two_self_terms": true,
    "zero_currents_and_single_active_coil": true,
    "reverse_both_currents_preserves_energy": true,
    "reverse_one_reference_requires_mutual_and_current_flip": true,
    "mutual_sign_only_change_is_detected_18_5_not_6_5_mj": true,
    "swap_two_coil_labels_preserves_energy_with_tolerance": true,
    "positive_perfect_coupling_semidefinite_nonzero_null_current": true,
    "negative_perfect_coupling_null_current_reference": true,
    "one_float_above_coupling_bound_rejected": true,
    "small_null_direction_energy_matches_exact_reference": true,
    "near_boundary_positive_determinant_not_clamped_to_zero": true,
    "current_scaling_is_quadratic_in_same_units": true,
    "320_fraction_oracle_cases_one_group": true,
    "finite_exact_numeric_types_and_units_rejected": true,
    "strict_positive_inductance_and_declared_numeric_bounds": true
  }
}

برای شاهدِ نزدیکِ حالتِ صفر، هر دو L و M یک هانری‌اند؛ جریانِ اول یک و دومی منفیِ یک به‌علاوهٔ دو به توان منفی سی آمپر است. انرژیِ دقیقِ همان ورودی، دو به توان منفی ۶۱ ژول، حدود ۴٫۳۳۶۸ ضرب‌در ده به توان منفی نوزده است. شکلِ مربع آن را حفظ می‌کند، ولی جمعِ سه سهمِ ازقبل‌گردشده در این اجرا صفر می‌دهد. جمعِ دقیق‌تر، خطای قبلیِ ضرب‌ها را خودکار پس نمی‌گیرد.

این شاهد با برابریِ دقیقِ جوابِ کسریِ نمایش‌یافته بررسی شده است؛ تلورانسِ مطلقِ شبکهٔ معمول، آن را پنهان نمی‌کند. برای مقایسه‌های معمول، تلورانسِ نسبی سه ضرب‌در ده به توان منفی سیزده و مطلق ده به توان منفی پانزده ژول است. با این حال، کامل‌کردنِ مربع درمانِ همگانیِ حذفِ عددی نیست؛ جریانِ ترکیبی می‌تواند گرد شود و انرژیِ بسیار ریز نیز کم‌ریزی کند. کرانِ ورودی، تضمینِ دقتِ نسبیِ همهٔ جواب‌ها نیست.

انرژیِ سلفِ منفرد حدِ بدونِ کوپلینگ را پشتیبانی می‌کند. آزمونِ امپدانسِ RLC دربارهٔ فرکانس و فازور بود و اشتراکِ بارِ خازن دو حالتِ الکترواستاتیکی را مقایسه می‌کرد. اینجا هیچ اتلاف، زمانِ انتقالِ انرژی یا پاسخِ دینامیکی نداریم. اجرا Python 3.14.5، مستندات 3.14.8 و دسترسی منابع ۲۵ مهر ۱۴۰۵ است.

۵. چهار پرسش با پاسخِ کامل

مرز کوپلینگ مجاز و بررسی مدل بدون اندازه‌گیری واقعی
تصویر مفهومی: مرز کوپلینگ مجاز و بررسی مدل بدون اندازه‌گیری واقعی؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

پرسش اول: چرا جوابِ نمونه شش‌ونیم میلی‌ژول است؟ پاسخ: هشت و چهارونیم میلی‌ژولِ خودی، با منفی شش میلی‌ژولِ متقابل جمع می‌شوند. حذفِ علامتِ جملهٔ متقابل، جواب را به هجده‌ونیم می‌برد و با مرجعِ دستی ناسازگار است.

پرسش دوم: با برگشتِ مرجعِ جریانِ دوم، فقط i2 را عوض کنیم؟ پاسخ: برای نمایشِ همان وضعیت، M نیز باید علامت عوض کند؛ حاصلِ ضربِ متقابل ثابت می‌ماند. تغییرِ یک علامت به‌تنهایی، تبدیلِ سازگارِ مرجع نیست.

پرسش سوم: انرژیِ صفر با جریانِ ناصفر همیشه خطاست؟ پاسخ: خیر؛ در مرزِ نیمه‌معینِ مدلِ ایدئال، نمونهٔ سه و منفی دو آمپر چنین پاسخِ دقیقی دارد. این نتیجه، تأییدِ رفتارِ یک سیم‌پیچِ واقعیِ دارایِ اتلاف نیست.

پرسش چهارم: شاهدِ کوچک، دقتِ کاملِ همهٔ ورودی‌ها را ثابت می‌کند؟ پاسخ: خیر؛ فقط همان موردِ حذفِ عددی را با مرجعِ مستقل آشکار می‌کند. گردشدنِ جریانِ ترکیبی، کم‌ریزی و فرض‌های مدل همچنان محدودیت دارند؛ از این آزمون نتیجهٔ عملکردِ سخت‌افزار نمی‌گیریم.

منابع و مطالعهٔ بیشتر

بررسی منابع: ۲۵ مهر ۱۴۰۵. تحلیل‌ها و پیشنهادهای اجرایی، جمع‌بندی نویسنده‌اند.