FEMM 5

Магнитное поле и электромеханика · Плоскопараллельная

Асинхронная машина: частота токов ротора, импеданс и потери

В асинхронной машине частота токов ротора определяется скольжением. Серия неподвижных гармонических задач позволяет исследовать вытеснение тока и эквивалентный импеданс без ложного представления о прямом моделировании вращающегося ротора во времени.

Самостоятельная учебная модель FEMM 5: от физических соотношений к полевому решению и проверке результата.

Допущения и устройство учебной модели

Принятые допущения и их назначение
Учебная модель FEMM 5Причина
Параметры и диапазон исследования выбраны для последовательного изучения в нашем курсе.Учебная постановка FEMM 5
Геометрия, источники, русские свойства, управление серией и проверки объединены в самостоятельную программу встроенного Python.Учебная постановка FEMM 5
Сеточная чувствительность и физические контроли показаны отдельно от основной параметрической характеристики.Учебная постановка FEMM 5

Все размеры, свойства материалов и условия возбуждения заданы ниже. Численные результаты относятся к опубликованной версии модели FEMM 5.

Физическая и математическая постановка

Принцип действия и расчётные соотношения

В выбранном секторе сохраняются геометрия, противопериодические границы, обмотки и проводящие стержни. Меняется частота гармонического поля при одинаковом симметричном токовом возбуждении. Из напряжений выводов получается комплексная мощность, которая сравнивается с интегралом потерь поля. На малых частотах ток проникает глубже в стержень, а рост частоты усиливает его вытеснение. Полученные частотные параметры ещё нужно включить в согласованную схему замещения для определения нагрузки и скорости реального двигателя.

\widehat I_B=\widehat I_Ae^{-j2\pi/3},\quad\widehat I_C=\widehat I_Ae^{j2\pi/3},\quad P=\tfrac12\Re\sum_k\widehat V_k\widehat I_k^*

Численная проверка и новая серия расчётов

Сначала выполняются три независимых расчёта одной постановки с масштабами размеров элементов 1; 0,7; 0,5 относительно учебного входа. Фактический размер в узких местах задаётся программой отдельно и может быть меньше этого масштаба. Сравниваются наблюдаемая величина и число элементов, а не только вид поля. После этого на мелкой сетке выполняется параметрическая серия и независимые физические контроли. Каждый файл модели перед расчётом закрывается и открывается повторно; этим проверяется сохранение русских свойств в точной решаемой копии.

Для нового опыта скачайте файлы со страницы работы в отдельный каталог и откройте reproduce.py в редакторе встроенного Python FEMM 5. Программа использует непосредственные команды mi/ mo и не требует внешнего управления Python. Она создаёт новую папку calculated-NN рядом с программой. Прежние решения не используются для получения новых чисел. Результат и все входы сохраняются вместе, поэтому найденное число всегда можно связать с параметрами, сеткой и точным файлом.

Исходные данные

Плоскопараллельная

Величины, единицы и происхождение параметров
ПараметрЗначениеЕдиницаПроисхождение
Геометрия и источникиСектор асинхронной машины с проводящими стержнями ротора и противопериодическими границами. Точные размеры, кривые магнитных материалов, проводимости и витки включены в input.fem; частота 0,5–50 Гц, амплитуда фазного тока 1 А.СИ и ммВыбранные учебные параметры
Рабочая координатаЧастота токов ротора, Гцсм. подписи результатовПараметрическая серия

Порядок воспроизведения

  1. Разберите физическую модель и соотношения до запуска программы. Найдите каждое число из таблицы в CONFIG и геометрических командах.
  2. Откройте reproduce.py во встроенном Python FEMM 5. Сохраните пользовательские документы и запустите программу.
  3. Сопоставьте три сетки одной постановки и независимые физические проверки. Не подменяйте сравнение сеток разными рабочими режимами.
  4. Изучите параметрическую серию: объясните знак, размерность и тенденцию каждой величины. Измените один параметр и повторите опыт в новой папке.
  5. Оцените, какие выводы требуют трёхмерного, теплового или динамического расчёта. Сохраните модель и результат новой серии вместе.

Скачайте нужные файлы по отдельным ссылкам ниже в одну рабочую папку, затем откройте reproduce.py во встроенном Python FEMM 5 и выполните его. Сохраните имена файлов и структуру подпапок. Условия конкретного запуска и путь новых результатов указаны в комментариях программы.

Программа встроенного Python FEMM 5
Python · reproduce.py
"""Самостоятельное полевое исследование во встроенном Python FEMM 5.

Все размеры геометрии указаны в миллиметрах, результаты — в СИ. Значения
гармонических токов являются амплитудами. Материалы и условия имеют русские
имена; номера групп описаны в README. Программа создаёт новую calculated-NN
рядом с собой, не заменяя предшествующих расчётов. Каждая входная модель
сохраняется, закрывается, открывается повторно и только затем решается.
Открытые пользовательские документы не изменяются. Параметры — в CONFIG.
"""

import os
import sys
import json
import math

MU0 = 4e-7 * math.pi
script = globals().get("__file__", "") or (sys.argv[0] if sys.argv else "")
HERE = os.path.dirname(os.path.abspath(script)) if script else os.getcwd()
number = 1
while True:
    OUT = HERE + "/calculated-%02d" % number
    try:
        os.stat(OUT)
        number += 1
    except OSError:
        os.mkdir(OUT)
        break


def scalar(value):
    if isinstance(value, (list, tuple)):
        return scalar(value[0])
    return complex(value).real


def complex_parts(value):
    value = complex(value)
    return [value.real, value.imag]


def point(radius, angle):
    angle = angle * math.pi / 180
    return (radius * math.cos(angle), radius * math.sin(angle))


def line(a, b, group=0, size=0):
    mi_addnode(a[0], a[1])
    mi_addnode(b[0], b[1])
    mi_addsegment(a[0], a[1], b[0], b[1])
    mi_selectsegment((a[0] + b[0]) / 2, (a[1] + b[1]) / 2)
    boundary = (
        "Внешняя граница A=0"
        if globals().get("AXIS", False) and abs(a[0]) + abs(b[0]) < 1e-9
        else "<None>"
    )
    mi_setsegmentprop(boundary, size, 0 if size else 1, 0, group)
    mi_clearselected()


def polygon(points, group=0, size=0):
    for index in range(len(points)):
        line(points[index], points[(index + 1) % len(points)], group, size)


def rectangle(x1, y1, x2, y2, group=0, size=0):
    polygon([(x1, y1), (x2, y1), (x2, y2), (x1, y2)], group, size)


def arc(radius, start, end, group=0, step=2):
    a, b = point(radius, start), point(radius, end)
    mi_addnode(a[0], a[1])
    mi_addnode(b[0], b[1])
    mi_addarc(a[0], a[1], b[0], b[1], end - start, step)
    middle = point(radius, (start + end) / 2)
    mi_selectarcsegment(middle[0], middle[1])
    mi_setarcsegmentprop(step, "<None>", 0, group)
    mi_clearselected()


def circle(radius, group=0, step=2):
    for start in (0, 90, 180, 270):
        arc(radius, start, start + 90, group, step)


def label(x, y, material, mesh, group=0, circuit="<None>", turns=1, angle=0):
    mi_addblocklabel(x, y)
    mi_selectlabel(x, y)
    mi_setblockprop(material, 0, mesh, circuit, angle, group, turns)
    mi_clearselected()


def begin(mesh, kind="planar", frequency=0, depth=40, radius=100, br=1.1, mur=1.05):
    global AXIS
    AXIS = kind == "axi"
    newdocument(0)
    mi_probdef(frequency, "millimeters", kind, 1e-9, depth, 25)
    mi_addmaterial("Воздух", 1, 1, 0, 0, 0)
    mi_addmaterial("Обмотка без вытеснения тока", 1, 1, 0, 0, 0)
    mi_addmaterial("Медь 58 МСм на метр", 1, 1, 0, 0, 58)
    mi_addmaterial("Сталь с линейной проницаемостью", 1000, 1000, 0, 0, 0)
    mi_addmaterial("Сталь с учебной кривой насыщения", 1, 1, 0, 0, 0)
    for b, h in (
        (0, 0),
        (0.3, 100),
        (0.6, 200),
        (1, 350),
        (1.3, 700),
        (1.5, 1800),
        (1.65, 5000),
        (1.8, 15000),
        (2, 60000),
        (2.2, 200000),
    ):
        mi_addbhpoint("Сталь с учебной кривой насыщения", b, h)
    mi_addmaterial("Постоянный магнит учебный NdFeB", mur, mur, br / (MU0 * mur), 0, 0)
    mi_addboundprop("Внешняя граница A=0", 0, 0, 0, 0, 0, 0, 0, 0, 0)
    if kind == "axi":
        rectangle(0, -radius, radius, radius)
        for x, y in ((0, 0), (radius, 0), (radius / 2, -radius), (radius / 2, radius)):
            mi_selectsegment(x, y)
            mi_setsegmentprop("Внешняя граница A=0", 0, 1, 0, 0)
            mi_clearselected()
    else:
        circle(radius, step=4)
        for angle in (45, 135, 225, 315):
            x, y = point(radius, angle)
            mi_selectarcsegment(x, y)
            mi_setarcsegmentprop(4, "Внешняя граница A=0", 0, 0)
            mi_clearselected()


def band(r1, r2, count, fraction, group, mesh, phase=0, mode="radial"):
    """Кольцевая сборка: заданные магнитные секторы и воздушные промежутки."""
    pitch = 360 / count
    for index in range(count):
        start = index * pitch + phase
        stop = start + pitch * fraction
        for radius in (r1, r2):
            arc(radius, start, stop, group, step=2)
            if fraction < 0.999999:
                arc(radius, stop, start + pitch, group, step=2)
        line(point(r1, start), point(r2, start), group)
        line(point(r1, stop), point(r2, stop), group)
        angle = (start + stop) / 2
        x, y = point((r1 + r2) / 2, angle)
        if mode == "steel":
            label(x, y, "Сталь с линейной проницаемостью", mesh, group)
        else:
            direction = 2 * angle if mode == "halbach" else angle + 180 * (index % 2)
            label(x, y, "Постоянный магнит учебный NdFeB", mesh, group, angle=direction)
        if fraction < 0.999999:
            x, y = point((r1 + r2) / 2, (stop + start + pitch) / 2)
            label(x, y, "Воздух", mesh, group)


def energy():
    mo_clearblock()
    mo_groupselectblock()
    values = [scalar(mo_blockintegral(k)) for k in (2, 17, 4)]
    mo_clearblock()
    return values


def solve(tag, group=2, probe=(1, 0), circuits=()):
    path = OUT + "/" + tag + ".fem"
    mi_saveas(path)
    mi_close()
    opendocument(path)
    mi_analyze(1)
    mi_loadsolution()
    e, w, p = energy()
    row = {
        "state": tag,
        "elements": mo_numelements(),
        "energy_J": e,
        "coenergy_J": w,
        "loss_W": p,
    }
    if group:
        mo_groupselectblock(group)
        if CONFIG.get("kind") != "course":
            row["Fx_N"] = scalar(mo_blockintegral(18))
            row["Fy_N"] = scalar(mo_blockintegral(19))
        row["torque_Nm"] = scalar(mo_blockintegral(22))
        if CONFIG.get("kind") != "course":
            row["lorentz_x_N"] = scalar(mo_blockintegral(11))
            row["lorentz_y_N"] = scalar(mo_blockintegral(12))
        mo_clearblock()
    if CONFIG.get("kind") == "gear":
        for part in (2, 3, 4):
            mo_groupselectblock(part)
            row["T%d_Nm" % part] = scalar(mo_blockintegral(22))
            mo_clearblock()
    values = mo_getpointvalues(probe[0], probe[1])
    row["Bx_T"] = scalar(values[1])
    row["By_T"] = scalar(values[2])
    row["Hx_Am"] = scalar(values[5])
    row["Hy_Am"] = scalar(values[6])
    for circuit in circuits:
        i, v, psi = mo_getcircuitproperties(circuit)
        row[circuit + "_I_A"] = scalar(i)
        row[circuit + "_Psi_Wb"] = scalar(psi)
        row[circuit + "_Psi_im_Wb"] = complex(psi).imag
        row[circuit + "_V_V"] = scalar(v)
        row[circuit + "_V_im_V"] = complex(v).imag
    mo_close()
    mi_close()
    return row


def check(name, value, limit, passed):
    CHECKS.append(
        {"name": name, "value": value, "limit": limit, "passed": bool(passed)}
    )


def relative(a, b, minimum=1e-15):
    return abs(a - b) / max(abs(a), abs(b), minimum)


def finish(plot=None):
    result = {
        "title": CONFIG["title"],
        "records": ROWS,
        "checks": CHECKS,
        "units": "Геометрия мм; магнитное поле Тл, А/м; энергия Дж; сила Н; момент Н·м; потокосцепление Вб.",
        "passed": bool(CHECKS) and all(row["passed"] for row in CHECKS),
    }
    if plot:
        result["plot"] = plot
    with open(OUT + "/result.json", "w") as stream:
        json.dump(result, stream)
    print(CONFIG["id"], result["passed"], len(ROWS), "решений", OUT)
    assert result["passed"], "Не пройдена независимая проверка физической модели."


ROWS = []
CHECKS = []


CONFIG = {
    "id": "induction-machine-slip-frequency",
    "kind": "course",
    "study": "induction-slip",
    "title": "Асинхронная машина: частота токов ротора, импеданс и потери",
    "source_model": "induction",
    "baseline": 0,
    "angle": 0,
    "metric": "loss_W",
    "sweep": [0.5, 1, 2, 5, 10, 20, 50],
    "metric_label": "Потери в секторе, Вт",
    "parameter_name": "Частота токов ротора, Гц",
    "circuits": ["Фаза A", "Фаза B", "Фаза C"],
    "group": 0,
    "probe": [20, 20],
    "intro": "В асинхронной машине частота токов ротора определяется скольжением. Серия неподвижных гармонических задач позволяет исследовать вытеснение тока и эквивалентный импеданс без ложного представления о прямом моделировании вращающегося ротора во времени.",
    "theory": "В выбранном секторе сохраняются геометрия, противопериодические границы, обмотки и проводящие стержни. Меняется частота гармонического поля при одинаковом симметричном токовом возбуждении. Из напряжений выводов получается комплексная мощность, которая сравнивается с интегралом потерь поля. На малых частотах ток проникает глубже в стержень, а рост частоты усиливает его вытеснение. Полученные частотные параметры ещё нужно включить в согласованную схему замещения для определения нагрузки и скорости реального двигателя.",
    "formula": "\\widehat I_B=\\widehat I_Ae^{-j2\\pi/3},\\quad\\widehat I_C=\\widehat I_Ae^{j2\\pi/3},\\quad P=\\tfrac12\\Re\\sum_k\\widehat V_k\\widehat I_k^*",
    "scope": "Двумерная учебная геометрия; точные входы и русские свойства включены в комплект. Торцевые поля, механическая динамика, температурная зависимость и реальные потери стали требуют отдельных моделей.",
    "related_lessons": ["I08.2", "I08.3", "II05.1"],
    "dimensions": "Сектор асинхронной машины с проводящими стержнями ротора и противопериодическими границами. Точные размеры, кривые магнитных материалов, проводимости и витки включены в input.fem; частота 0,5–50 Гц, амплитуда фазного тока 1 А.",
    "frequency": 2,
    "mesh_scales": [1, 0.7, 0.5],
    "magnet_hc": 0.0,
    "depth": 100.0,
}

with open(HERE + "/input.fem", "r") as stream:
    BASE_TEXT = stream.read()


def prepare(scale=1, angle=0, currents=None, frequency=0, defect=False):
    lines = BASE_TEXT.split("\n")
    start = next(
        i for i in range(len(lines)) if lines[i].startswith("[NumBlockLabels]")
    )
    count = int(lines[start].split("=")[1])
    for i in range(start + 1, start + 1 + count):
        fields = lines[i].split("\t")
        fields[3] = str(float(fields[3]) * scale)
        lines[i] = "\t".join(fields)
    path = OUT + "/текущая-настройка.fem"
    with open(path, "w") as stream:
        stream.write("\n".join(lines) + "\n")
    opendocument(path)
    mi_probdef(frequency, "millimeters", "planar", 1e-9, CONFIG.get("depth", 80), 25)
    for name, value in (currents or {}).items():
        mi_modifycircprop(name, 1, value)
    if defect:
        mi_addmaterial(
            "NdFeB с ослабленной намагниченностью",
            1.05,
            1.05,
            CONFIG["magnet_hc"] * 0.6,
            0,
            0,
        )
        mi_selectlabel(52, 0)
        mi_setblockprop(
            "NdFeB с ослабленной намагниченностью",
            0,
            1.5 * scale,
            "<None>",
            "theta",
            2,
            1,
        )
        mi_clearselected()
    if angle:
        mi_selectgroup(2)
        mi_moverotate(0, 0, angle)
        mi_clearselected()


def phase_currents(angle=0, amplitude=1, delta=0):
    return {
        name: amplitude * math.cos((2 * angle + delta - offset) * math.pi / 180)
        for name, offset in (("Фаза A", 0), ("Фаза B", 120), ("Фаза C", 240))
    }


study = CONFIG["study"]
names = CONFIG["circuits"]
opendocument(HERE + "/input.fem")
ROWS.append(solve("input", CONFIG.get("group", 0), CONFIG.get("probe", [1, 0]), names))
mesh_rows = []
mesh_scales = CONFIG.get("mesh_scales", (0.8, 0.5, 0.3))
fine_scale = mesh_scales[-1]
for index, scale in enumerate(mesh_scales):
    angle = CONFIG.get("angle", 0)
    if study in ("pm-emf", "pm-defect"):
        currents = {name: 0 for name in names}
    elif study == "pm-skew":
        currents = phase_currents(angle, 3, 60)
    elif study == "dc-reaction":
        currents = {names[0]: 1, names[1]: 2}
    elif study == "ct-burden":
        currents = {names[0]: 1, names[1]: 0}
    elif study == "three-phase":
        currents = {names[0]: 1, names[1]: 0, names[2]: 0}
    elif study == "induction-slip":
        currents = {
            names[0]: 1,
            names[1]: complex(-0.5, -math.sqrt(3) / 2),
            names[2]: complex(-0.5, math.sqrt(3) / 2),
        }
    else:
        currents = {
            names[0]: 1,
            names[1]: -0.5 if study == "synrm-angle" else 0,
            names[2]: -0.5 if study == "synrm-angle" else 0,
        }
    prepare(
        scale,
        angle,
        currents,
        frequency=CONFIG.get("frequency", 0),
        defect=study == "pm-defect",
    )
    row = solve(
        "сетка-%d" % (index + 1),
        CONFIG.get("group", 0),
        CONFIG.get("probe", [1, 0]),
        names,
    )
    row["mesh_scale"] = scale
    row["parameter"] = (
        2
        if study == "dc-reaction"
        else CONFIG.get("frequency", 0)
        if study == "induction-slip"
        else angle
    )
    ROWS.append(row)
    mesh_rows.append(row)
fine = mesh_rows[-1]
metric = CONFIG["metric"]
err = relative(mesh_rows[-2][metric], fine[metric])
check("Стабилизация наблюдаемой величины при сгущении сетки", err, 0.025, err < 0.025)
check(
    "Фактическое увеличение числа элементов",
    fine["elements"] / mesh_rows[0]["elements"],
    1.2,
    fine["elements"] > 1.2 * mesh_rows[0]["elements"],
)

if study in ("pm-emf", "pm-skew", "pm-defect", "synrm-angle", "srm-map"):
    for angle in CONFIG["sweep"]:
        if study in ("pm-emf", "pm-defect"):
            currents = {name: 0 for name in names}
        elif study == "pm-skew":
            currents = phase_currents(angle, 3, 60)
        else:
            currents = {
                names[0]: 1,
                names[1]: -0.5 if study == "synrm-angle" else 0,
                names[2]: -0.5 if study == "synrm-angle" else 0,
            }
        prepare(fine_scale, angle, currents, defect=study == "pm-defect")
        row = solve(
            "угол-" + str(angle),
            CONFIG.get("group", 0),
            CONFIG.get("probe", [1, 0]),
            names,
        )
        row["parameter"] = angle
        row["mesh_scale"] = fine_scale
        ROWS.append(row)
    data = ROWS[4:]
    if study in ("pm-emf", "pm-skew", "pm-defect"):
        # Первый и последний узлы — одинаковое положение после периода.
        key = names[0] + "_Psi_Wb" if study != "pm-skew" else "torque_Nm"
        amplitude = max(abs(r[key]) for r in data)
        err = abs(data[-1][key] - data[0][key]) / max(amplitude, 1e-12)
        check("Замыкание углового периода", err, 0.025, err < 0.025)
        if study in ("pm-emf", "pm-defect"):
            omega = 1000 * 2 * math.pi / 60
            step = (data[1]["parameter"] - data[0]["parameter"]) * math.pi / 180
            values = [r[names[0] + "_Psi_Wb"] for r in data[:-1]]

            def derivative_emf(values, step):
                size = len(values)
                return [
                    -omega
                    * (
                        -values[(i + 2) % size]
                        + 8 * values[(i + 1) % size]
                        - 8 * values[(i - 1) % size]
                        + values[(i - 2) % size]
                    )
                    / (12 * step)
                    for i in range(size)
                ]

            emf = derivative_emf(values, step)
            coarse_emf = derivative_emf(values[::2], 2 * step)
            order = int(round((CONFIG["sweep"][-1] - CONFIG["sweep"][0]) * 2 / 360))

            def amplitude(samples):
                size = len(samples)
                coefficient = 0j
                for i, value in enumerate(samples):
                    phase = 2 * math.pi * order * i / size
                    coefficient += value * complex(math.cos(phase), -math.sin(phase))
                return 2 * abs(coefficient) / size

            fine_amplitude = amplitude(emf)
            coarse_amplitude = amplitude(coarse_emf)
            angular_error = relative(fine_amplitude, coarse_amplitude)
            check(
                "Сходимость основной гармоники ЭДС по угловому шагу 15 и 7,5 град",
                angular_error,
                0.01,
                angular_error < 0.01,
            )
            for i, value in enumerate(emf):
                data[i]["emf_A_V"] = value
            check(
                "Ненулевая ЭДС при вращении",
                max(abs(v) for v in emf),
                0,
                max(abs(v) for v in emf) > 0,
            )
        else:
            values = []
            averaged = []
            for center in data:
                angle = center["parameter"]
                currents = phase_currents(angle, 3, 60)
                torques = [center["torque_Nm"]]
                for offset in (-5, 5):
                    # У всех сечений одного положения ОДИН И ТОТ ЖЕ ток статора.
                    prepare(fine_scale, angle + offset, currents)
                    row = solve(
                        "сечение-%03d-%s" % (angle, str(offset)),
                        2,
                        CONFIG.get("probe", [1, 0]),
                        names,
                    )
                    row.update(global_angle_deg=angle, slice_offset_deg=offset)
                    ROWS.append(row)
                    torques.append(row["torque_Nm"])
                center["skew_averaged_torque_Nm"] = sum(torques) / 3
                if angle != CONFIG["sweep"][-1]:
                    values.append(center["torque_Nm"])
                    averaged.append(sum(torques) / 3)
            factor = (1 + 2 * math.cos(2 * 5 * math.pi / 180)) / 3
            err = relative(sum(averaged), factor * sum(values))
            check(
                "Средний момент и гармонический коэффициент трёх фиксированных токовых сечений",
                err,
                0.03,
                err < 0.03,
            )
    elif study == "srm-map":
        angle = CONFIG.get("angle", 15)
        prepare(fine_scale, angle, {names[0]: 2, names[1]: 0, names[2]: 0})
        double = solve("удвоенный-ток", 0, CONFIG.get("probe", [1, 0]), names)
        ROWS.append(double)
        err = relative(double[names[0] + "_Psi_Wb"], 2 * fine[names[0] + "_Psi_Wb"])
        check("Линейное масштабирование потокосцепления", err, 0.001, err < 0.001)
        err = relative(double["coenergy_J"], 4 * fine["coenergy_J"])
        check("Квадратичное масштабирование коэнергии", err, 0.001, err < 0.001)
    else:
        controls = []
        for angle in (CONFIG["angle"] - 0.5, CONFIG["angle"] + 0.5):
            prepare(0.3, angle, {names[0]: 1, names[1]: -0.5, names[2]: -0.5})
            row = solve(
                "виртуальное-смещение-%d" % len(ROWS),
                2,
                CONFIG.get("probe", [1, 0]),
                names,
            )
            ROWS.append(row)
            controls.append(row)
        derivative = (controls[1]["coenergy_J"] - controls[0]["coenergy_J"]) / (
            math.pi / 180
        )
        err = relative(derivative, fine["torque_Nm"])
        check(
            "Момент и производная коэнергии при фиксированных токах",
            err,
            0.07,
            err < 0.07,
        )
elif study == "dc-reaction":
    separate = []
    for field, armature in ((1, 0), (0, 2)):
        prepare(fine_scale, 0, {names[0]: field, names[1]: armature})
        row = solve("раздельное-возбуждение-%d" % len(ROWS), 0, CONFIG["probe"], names)
        ROWS.append(row)
        separate.append(row)
    err = max(
        relative(fine[name + "_Psi_Wb"], sum(r[name + "_Psi_Wb"] for r in separate))
        for name in names
    )
    check(
        "Суперпозиция потокосцеплений двух систем возбуждения", err, 0.002, err < 0.002
    )
    for armature in (0, 1, 2, 3, 4):
        prepare(fine_scale, 0, {names[0]: 1, names[1]: armature})
        row = solve("реакция-якоря-%d" % armature, 0, CONFIG["probe"], names)
        row["parameter"] = armature
        ROWS.append(row)
elif study == "ct-burden":
    prepare(fine_scale, 0, {names[0]: 0, names[1]: 1})
    secondary = solve("вторичный-столбец", 0, CONFIG["probe"], names)
    ROWS.append(secondary)
    l1 = fine[names[0] + "_Psi_Wb"]
    m1 = fine[names[1] + "_Psi_Wb"]
    m2 = secondary[names[0] + "_Psi_Wb"]
    l2 = secondary[names[1] + "_Psi_Wb"]
    err = relative(m1, m2)
    check("Взаимность связи обмоток", err, 0.001, err < 0.001)
    for resistance in (0.01, 0.03, 0.1, 0.3, 1):
        omega = 2 * math.pi * 50
        ip = 100
        secondary_current = -1j * omega * m1 * ip / (resistance + 1j * omega * l2)
        prepare(fine_scale, 0, {names[0]: ip, names[1]: secondary_current}, 50)
        row = solve("нагрузка-%d" % len(ROWS), 0, CONFIG["probe"], names)
        i2 = secondary_current
        v2 = complex(row[names[1] + "_V_V"], row[names[1] + "_V_im_V"])
        v1 = complex(row[names[0] + "_V_V"], row[names[0] + "_V_im_V"])
        residual = abs(v2 + resistance * i2) / max(abs(v2), 1e-12)
        supplied = 0.5 * (v1 * ip).real
        load = 0.5 * resistance * abs(i2) ** 2
        err = relative(supplied, load)
        row.update(
            parameter=resistance,
            secondary_amplitude_A=abs(i2),
            load_power_W=load,
            supplied_power_W=supplied,
            kvl_residual=residual,
            power_error=err,
        )
        ROWS.append(row)
        check(
            "Невязка уравнения вторичной цепи R=" + str(resistance),
            residual,
            0.002,
            residual < 0.002,
        )
        check("Баланс активной мощности R=" + str(resistance), err, 0.002, err < 0.002)
elif study == "three-phase":
    matrix = []
    for i in range(3):
        prepare(
            fine_scale, 0, {name: 1 if j == i else 0 for j, name in enumerate(names)}
        )
        row = solve("столбец-%d" % i, 0, CONFIG["probe"], names)
        ROWS.append(row)
        row["parameter"] = i
        matrix.append([row[name + "_Psi_Wb"] for name in names])
    asym = max(relative(matrix[i][j], matrix[j][i]) for i in range(3) for j in range(3))
    check("Взаимность трёхфазной матрицы", asym, 0.002, asym < 0.002)
    for label_, currents in (
        ("нулевая-последовательность", [1, 1, 1]),
        ("несимметрия", [1, 2, -1]),
        ("баланс", [1, -0.5, -0.5]),
    ):
        prepare(fine_scale, 0, {name: currents[i] for i, name in enumerate(names)})
        row = solve(label_, 0, CONFIG["probe"], names)
        ROWS.append(row)
        expected = 0.5 * sum(
            currents[i] * matrix[j][i] * currents[j] for i in range(3) for j in range(3)
        )
        err = relative(expected, row["energy_J"])
        check("Энергия поля и матрица: " + label_, err, 0.002, err < 0.002)
elif study == "induction-slip":
    for frequency in CONFIG["sweep"]:
        currents = {
            names[0]: 1,
            names[1]: complex(-0.5, -math.sqrt(3) / 2),
            names[2]: complex(-0.5, math.sqrt(3) / 2),
        }
        prepare(fine_scale, 0, currents, frequency)
        row = solve("частота-%d" % len(ROWS), 0, CONFIG["probe"], names)
        supplied = 0
        for name, current in currents.items():
            voltage = complex(row[name + "_V_V"], row[name + "_V_im_V"])
            supplied += 0.5 * (voltage * complex(current).conjugate()).real
        err = relative(supplied, row["loss_W"])
        row.update(parameter=frequency, terminal_power_W=supplied, power_error=err)
        ROWS.append(row)
        check(
            "Баланс потерь поля и выводов, f=" + str(frequency), err, 0.02, err < 0.02
        )

finish(
    {
        "x": "parameter",
        "y": CONFIG.get("plot_metric", CONFIG["metric"]),
        "x_label": CONFIG.get("parameter_name", "Параметр"),
        "y_label": CONFIG.get("plot_label", CONFIG["metric_label"]),
    }
)

Картина рассчитанного поля

Линии магнитного потока: сетка-3.ans
Визуализация сохранённого решения FEMM 5 сетка-3.ans. Плоская постановка: изолинии векторного потенциала Az. Показана действительная компонента фазора при фазе 0; это не модуль индукции. Показана расчётная область. Контуры деталей взяты из той же модели. Области вне сохранённых треугольников не заполняются. Это научный график сохранённых данных. Нажмите на карту, чтобы открыть её отдельно и увеличить.

Окна FEMM 5

Снимки интерфейса FEMM показывают геометрию, настройки и работу с результатами в FEMM 5. Откройте изображение, чтобы рассмотреть подписи и значения в полном размере.

Асинхронная машина: расчётный сектор 90° со статорными пазами, воздушным зазором и стержнями ротора. Подписи скрыты для читаемости.
Асинхронная машина: расчётный сектор 90° со статорными пазами, воздушным зазором и стержнями ротора. Подписи скрыты для читаемости. Открыть в полном размере
Асинхронная машина: поле расчётного сектора при частоте скольжения 2 Гц. Норма комплексной амплитуды магнитной индукции B, Т; контуры A скрыты.
Асинхронная машина: поле расчётного сектора при частоте скольжения 2 Гц. Норма комплексной амплитуды магнитной индукции B, Т; контуры A скрыты. Открыть в полном размере

Результаты и численные проверки

В гармонических строках компоненты Bx, By, Hx, Hy и величины цепей I, V, Ψ представлены действительными частями фазоров (Re). Мнимые части подписаны отдельно. Заданные амплитуды возбуждения указаны в исходных данных. Для энергии, мощности и силы используется смысл соответствующего интеграла, описанный в постановке.

Сохранено решений FEMM: 4. Выполнено проверок: 9. Полная точность чисел и условия сравнения доступны в таблицах результатов JSON.

Результаты расчётов и производные итоги
ЭлементовДействительная часть (Re): Напряжение, В: Фаза CМнимая часть потокосцепления, Вб: Фаза BДействительная часть (Re): Напряжение, В: Фаза AДействительная часть (Re): Индукция Bx, ТлКоэнергия, ДжМнимая часть потокосцепления, Вб: Фаза AСостояниеДействительная часть (Re): Ток, А: Фаза BМнимая часть потокосцепления, Вб: Фаза CМнимая часть напряжения, В: Фаза CДействительная часть (Re): Потокосцепление, Вб: Фаза BДействительная часть (Re): Напряжение, В: Фаза BПотери, ВтДействительная часть (Re): Напряжённость Hy, А/мМнимая часть напряжения, В: Фаза BДействительная часть (Re): Потокосцепление, Вб: Фаза CДействительная часть (Re): Ток, А: Фаза CДействительная часть (Re): Индукция By, ТлДействительная часть (Re): Ток, А: Фаза AМнимая часть напряжения, В: Фаза AДействительная часть (Re): Потокосцепление, Вб: Фаза AЭнергия, ДжДействительная часть (Re): Напряжённость Hx, А/мОтносительный масштаб сеткиПараметр серииАктивная мощность на зажимах, ВтОтносительная невязка мощности
251270,0311562680,0321023840,390015820,0518637250,014730934-0,031036473input-0,5-0,0024793371-0,459706120,016779942-0,403410450,578436393,04697940,21086297-0,036582251-0,50,02680263110,239690120,0190739340,0147309345,8959773————
28519-0,41372881-998,48041 × 10⁻⁶0,38434524-0,148263050,014731694-0,030585223сетка-1-0,50,0329234920,20289419-0,0364152170,0125472750,57847346-15,824608-0,457607110,016145807-0,5-0,1392005210,243973310,0194147790,014731694-16,85485512——
45119-0,41375274-998,16588 × 10⁻⁶0,38447447-0,148197830,014730304-0,030595506сетка-2-0,50,0329253970,20301441-0,0364227920,0125433220,57863832-15,781152-0,45770230,016155374-0,5-0,1388182610,243898660,0194088390,014730304-16,8474410,72——
77328-0,41377111-994,78902 × 10⁻⁶0,38458079-0,14842180,014729021-0,030603967сетка-3-0,50,0329268590,20309462-0,0364282460,0125008880,57877109-15,783506-0,457770840,016161756-0,5-0,1388389710,243860880,0194058320,014729021-16,8729020,52——
77328-0,23208706-0,041046130,099906898-0,394401960,050013195-0,031801353частота-4-0,50,073875604-0,018949564-0,0615243890,128950220,15122693-45,43361-0,19328457-0,0060318336-0,5-0,3996549110,209699820,0667495270,050013195-44,836444—0,50,15122693740,38396 × 10⁻¹⁵
77328-0,35721588-0,016598940,24565663-0,294706870,03197725-0,039097467частота-5-0,50,0568526730,075451617-0,0554726140,104294210,36965453-32,731963-0,348544710,012008498-0,5-0,2879253910,267449930,0425659790,03197725-33,502897—10,3696545353,725396 × 10⁻¹²
77328-0,41377111-994,78902 × 10⁻⁶0,38458079-0,14842180,014729021-0,030603967частота-6-0,50,0329268590,20309462-0,0364282460,0125008880,57877109-15,783506-0,457770840,016161756-0,5-0,1388389710,243860880,0194058320,014729021-16,872902—20,578771093,4501501 × 10⁻¹²
77328-0,460041740,00125404830,45558253-0,0340408990,0057411918-0,014501642частота-7-0,50,0146435830,26298114-0,016387755-0,0393970890,68945589-3,3510619-0,514836490,0083709496-0,5-0,0294774810,231242810,00736068720,0057411918-3,869841—50,689455892,0548894 × 10⁻¹²
77328-0,58319354-515,5086 × 10⁻⁶0,4692785-0,00408911170,0041539115-0,0074687993частота-8-0,50,00928181350,21234071-0,00915855560,0323903610,713463-0,2944111-0,575449020,0033795073-0,5-0,002589775310,329326370,00524139190,0041539115-0,46485882—100,7134632,2228953 × 10⁻¹²
77328-0,86911231-0,00192608080,478752530,00205119690,0037199486-0,0038097916частота-9-0,50,0069161760,060983031-0,00564651120,242038460,729800310,23228541-0,70956152485,28754 × 10⁻⁶-0,50,002043289210,583276090,00464156360,00371994860,23318437—200,72980031599,8366 × 10⁻¹⁵
77328-1,7687262-0,00290626630,5103218997,94677 × 10⁻⁶0,0035708884-0,0016244047частота-10-0,50,0056300303-0,41695382-0,00363530550,91303050,783067130,1108067-1,1420649-0,0013272052-0,5974,70666 × 10⁻⁶11,38533190,00440964840,00357088840,11344869—500,7830671313,526972 × 10⁻¹²
Проверки опубликованного расчёта
КритерийПолученоДопуск или ожидаемое значениеИтог
Стабилизация наблюдаемой величины при сгущении сетки229,4 × 10⁻⁶0,025Выполнен
Фактическое увеличение числа элементов2,7111,2Выполнен
Баланс потерь поля и выводов, f=0.5740,4 × 10⁻¹⁵0,02Выполнен
Баланс потерь поля и выводов, f=153,73 × 10⁻¹²0,02Выполнен
Баланс потерь поля и выводов, f=23,45 × 10⁻¹²0,02Выполнен
Баланс потерь поля и выводов, f=52,055 × 10⁻¹²0,02Выполнен
Баланс потерь поля и выводов, f=102,223 × 10⁻¹²0,02Выполнен
Баланс потерь поля и выводов, f=20599,8 × 10⁻¹⁵0,02Выполнен
Баланс потерь поля и выводов, f=5013,53 × 10⁻¹²0,02Выполнен

Допуски относятся к указанной учебной задаче и способу сравнения. Число показанных цифр не означает такой же физической точности.

Модели, решения и программа

Для нового расчёта сохраните программу, модели и необходимые данные в одну рабочую папку, сохраняя структуру подпапок.

Полные численные результаты

Границы применимости