Магнитное поле и электромеханика · Плоскопараллельная
Вентильно-индукторная машина: положение, потокосцепление и коэнергия
У вентильно-индукторной машины зубчатый стальной ротор стремится занять положение высокой магнитной проводимости. Положение меняет фазное потокосцепление даже при постоянном токе.
Самостоятельная учебная модель FEMM 5: от физических соотношений к полевому решению и проверке результата.
Допущения и устройство учебной модели
| Учебная модель FEMM 5 | Причина |
|---|---|
| Параметры и диапазон исследования выбраны для последовательного изучения в нашем курсе. | Учебная постановка FEMM 5 |
| Геометрия, источники, русские свойства, управление серией и проверки объединены в самостоятельную программу встроенного Python. | Учебная постановка FEMM 5 |
| Сеточная чувствительность и физические контроли показаны отдельно от основной параметрической характеристики. | Учебная постановка FEMM 5 |
Все размеры, свойства материалов и условия возбуждения заданы ниже. Численные результаты относятся к опубликованной версии модели FEMM 5.
Физическая и математическая постановка
Принцип действия и расчётные соотношения
Возбуждается только одна фаза, остальные токи заданы нулём. Для линейной стали потокосцепление пропорционально току, а энергия и коэнергия пропорциональны квадрату тока. Эти два независимых масштаба проверяются отдельным расчётом удвоенного тока. Угловая карта показывает, в какой области возрастание индуктивности создаёт положительный момент; реальная коммутация должна согласовываться с этой областью и динамикой напряжения.
Численная проверка и новая серия расчётов
Сначала выполняются три независимых расчёта одной постановки с масштабами размеров элементов 1,5; 1; 0,6 относительно учебного входа. Фактический размер в узких местах задаётся программой отдельно и может быть меньше этого масштаба. Сравниваются наблюдаемая величина и число элементов, а не только вид поля. После этого на мелкой сетке выполняется параметрическая серия и независимые физические контроли. Каждый файл модели перед расчётом закрывается и открывается повторно; этим проверяется сохранение русских свойств в точной решаемой копии.
Для нового опыта скачайте файлы со страницы работы в отдельный каталог и откройте reproduce.py в редакторе встроенного Python FEMM 5. Программа использует непосредственные команды mi/ mo и не требует внешнего управления Python. Она создаёт новую папку calculated-NN рядом с программой. Прежние решения не используются для получения новых чисел. Результат и все входы сохраняются вместе, поэтому найденное число всегда можно связать с параметрами, сеткой и точным файлом.
Исходные данные
Плоскопараллельная
| Параметр | Значение | Единица | Происхождение |
|---|---|---|---|
| Геометрия и источники | Отдельная русская учебная копия геометрии нашего курса. Все координаты, витки, материалы и глубина заданы в input.fem; рабочие токи и угловая серия задаются программой. Сталь линейная, μr=1000. | СИ и мм | Выбранные учебные параметры |
| Рабочая координата | Механический угол, град | см. подписи результатов | Параметрическая серия |
Порядок воспроизведения
- Разберите физическую модель и соотношения до запуска программы. Найдите каждое число из таблицы в CONFIG и геометрических командах.
- Откройте reproduce.py во встроенном Python FEMM 5. Сохраните пользовательские документы и запустите программу.
- Сопоставьте три сетки одной постановки и независимые физические проверки. Не подменяйте сравнение сеток разными рабочими режимами.
- Изучите параметрическую серию: объясните знак, размерность и тенденцию каждой величины. Измените один параметр и повторите опыт в новой папке.
- Оцените, какие выводы требуют трёхмерного, теплового или динамического расчёта. Сохраните модель и результат новой серии вместе.
Скачайте нужные файлы по отдельным ссылкам ниже в одну рабочую папку, затем откройте reproduce.py во встроенном Python FEMM 5 и выполните его. Сохраните имена файлов и структуру подпапок. Условия конкретного запуска и путь новых результатов указаны в комментариях программы.
Программа встроенного Python FEMM 5
"""Самостоятельное полевое исследование во встроенном 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": "srm-position-flux",
"kind": "course",
"study": "srm-map",
"title": "Вентильно-индукторная машина: положение, потокосцепление и коэнергия",
"source_model": "srm",
"baseline": 15,
"angle": 15,
"metric": "Фаза A_Psi_Wb",
"sweep": [0, 15, 30, 45, 60, 75, 90],
"metric_label": "Потокосцепление фазы A, Вб",
"parameter_name": "Механический угол, град",
"circuits": ["Фаза A", "Фаза B", "Фаза C"],
"group": 0,
"probe": [58.5, 0],
"intro": "У вентильно-индукторной машины зубчатый стальной ротор стремится занять положение высокой магнитной проводимости. Положение меняет фазное потокосцепление даже при постоянном токе.",
"theory": "Возбуждается только одна фаза, остальные токи заданы нулём. Для линейной стали потокосцепление пропорционально току, а энергия и коэнергия пропорциональны квадрату тока. Эти два независимых масштаба проверяются отдельным расчётом удвоенного тока. Угловая карта показывает, в какой области возрастание индуктивности создаёт положительный момент; реальная коммутация должна согласовываться с этой областью и динамикой напряжения.",
"formula": "\\Psi(i,\\theta)=L(\\theta)i,\\quad W^\\prime=\\tfrac12L(\\theta)i^2,\\quad T=\\tfrac12i^2dL/d\\theta",
"scope": "Двумерная учебная геометрия; точные входы и русские свойства включены в комплект. Торцевые поля, механическая динамика, температурная зависимость и реальные потери стали требуют отдельных моделей.",
"related_lessons": ["I11.1", "I11.2", "II08.1"],
"dimensions": "Отдельная русская учебная копия геометрии нашего курса. Все координаты, витки, материалы и глубина заданы в input.fem; рабочие токи и угловая серия задаются программой. Сталь линейная, μr=1000.",
"mesh_scales": [1.5, 1, 0.6],
"magnet_hc": 909456.8176679733,
"depth": 80.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"]),
}
)
Картина рассчитанного поля
Окна FEMM 5
Снимки интерфейса FEMM показывают геометрию, настройки и работу с результатами в FEMM 5. Откройте изображение, чтобы рассмотреть подписи и значения в полном размере.
Результаты и численные проверки
Сохранено решений FEMM: 4. Выполнено проверок: 4. Полная точность чисел и условия сравнения доступны в таблицах результатов JSON.
| Элементов | Напряжение, В: Фаза C | Мнимая часть потокосцепления, Вб: Фаза B | Напряжение, В: Фаза A | Индукция Bx, Тл | Коэнергия, Дж | Мнимая часть потокосцепления, Вб: Фаза A | Состояние | Ток, А: Фаза B | Мнимая часть потокосцепления, Вб: Фаза C | Мнимая часть напряжения, В: Фаза C | Потокосцепление, Вб: Фаза B | Напряжение, В: Фаза B | Потери, Вт | Напряжённость Hy, А/м | Мнимая часть напряжения, В: Фаза B | Потокосцепление, Вб: Фаза C | Ток, А: Фаза C | Индукция By, Тл | Ток, А: Фаза A | Мнимая часть напряжения, В: Фаза A | Потокосцепление, Вб: Фаза A | Энергия, Дж | Напряжённость Hx, А/м | Относительный масштаб сетки | Параметр серии |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 398922 | 0 | 0 | 0 | 0,3327 | 0,1919 | 0 | input | 0 | 0 | 0 | -720,4 × 10⁻⁶ | 0 | 0 | 0,1859 | 0 | 720,4 × 10⁻⁶ | 0 | 233,6 × 10⁻⁶ | 3 | 0 | 0,128 | 0,1919 | 264,8 | — | — |
| 185152 | 0 | 0 | 0 | 0,02043 | 0,01152 | 0 | сетка-1 | 0 | 0 | 0 | -105,6 × 10⁻⁶ | 0 | 0 | -6829 | 0 | 473,6 × 10⁻⁶ | 0 | -0,008582 | 1 | 0 | 0,02303 | 0,01152 | 16260 | 1,5 | 15 |
| 398126 | 0 | 0 | 0 | 0,02021 | 0,01154 | 0 | сетка-2 | 0 | 0 | 0 | -105,8 × 10⁻⁶ | 0 | 0 | -6669 | 0 | 474,6 × 10⁻⁶ | 0 | -0,00838 | 1 | 0 | 0,02309 | 0,01154 | 16080 | 1 | 15 |
| 1075745 | 0 | 0 | 0 | 0,02014 | 0,01157 | 0 | сетка-3 | 0 | 0 | 0 | -105,9 × 10⁻⁶ | 0 | 0 | -6751 | 0 | 475,7 × 10⁻⁶ | 0 | -0,008484 | 1 | 0 | 0,02314 | 0,01157 | 16020 | 0,6 | 15 |
| 1074828 | 0 | 0 | 0 | 0,111 | 0,02135 | 0 | угол-0 | 0 | 0 | 0 | -240,2 × 10⁻⁶ | 0 | 0 | 0,1296 | 0 | 240,2 × 10⁻⁶ | 0 | 162,8 × 10⁻⁶ | 1 | 0 | 0,04271 | 0,02135 | 88,32 | 0,6 | 0 |
| 1075745 | 0 | 0 | 0 | 0,02014 | 0,01157 | 0 | угол-15 | 0 | 0 | 0 | -105,9 × 10⁻⁶ | 0 | 0 | -6751 | 0 | 475,7 × 10⁻⁶ | 0 | -0,008484 | 1 | 0 | 0,02314 | 0,01157 | 16020 | 0,6 | 15 |
| 1075534 | 0 | 0 | 0 | 0,006551 | 0,003023 | 0 | угол-30 | 0 | 0 | 0 | -40,07 × 10⁻⁶ | 0 | 0 | -141,9 | 0 | 240,2 × 10⁻⁶ | 0 | -178,4 × 10⁻⁶ | 1 | 0 | 0,006045 | 0,003023 | 5213 | 0,6 | 30 |
| 1075230 | 0 | 0 | 0 | 0,005836 | 0,002496 | 0 | угол-45 | 0 | 0 | 0 | -106,4 × 10⁻⁶ | 0 | 0 | 0,1072 | 0 | 105,8 × 10⁻⁶ | 0 | 134,7 × 10⁻⁹ | 1 | 0 | 0,004993 | 0,002496 | 4644 | 0,6 | 45 |
| 1075467 | 0 | 0 | 0 | 0,006548 | 0,003022 | 0 | угол-60 | 0 | 0 | 0 | -240,9 × 10⁻⁶ | 0 | 0 | 142,1 | 0 | 40,08 × 10⁻⁶ | 0 | 178,6 × 10⁻⁶ | 1 | 0 | 0,006044 | 0,003022 | 5211 | 0,6 | 60 |
| 1075406 | 0 | 0 | 0 | 0,02012 | 0,0115 | 0 | угол-75 | 0 | 0 | 0 | -475,8 × 10⁻⁶ | 0 | 0 | 6642 | 0 | 105,4 × 10⁻⁶ | 0 | 0,008346 | 1 | 0 | 0,023 | 0,0115 | 16010 | 0,6 | 75 |
| 1075647 | 0 | 0 | 0 | 0,1109 | 0,02122 | 0 | угол-90 | 0 | 0 | 0 | -239,5 × 10⁻⁶ | 0 | 0 | 0,2417 | 0 | 239,5 × 10⁻⁶ | 0 | 303,8 × 10⁻⁶ | 1 | 0 | 0,04245 | 0,02122 | 88,24 | 0,6 | 90 |
| 1075745 | 0 | 0 | 0 | 0,04027 | 0,04628 | 0 | удвоенный-ток | 0 | 0 | 0 | -211,9 × 10⁻⁶ | 0 | 0 | -13500 | 0 | 951,3 × 10⁻⁶ | 0 | -0,01697 | 2 | 0 | 0,04628 | 0,04628 | 32050 | — | — |
| Критерий | Получено | Допуск или ожидаемое значение | Итог |
|---|---|---|---|
| Стабилизация наблюдаемой величины при сгущении сетки | 0,002262 | 0,025 | Выполнен |
| Фактическое увеличение числа элементов | 5,81 | 1,2 | Выполнен |
| Линейное масштабирование потокосцепления | 0 | 0,001 | Выполнен |
| Квадратичное масштабирование коэнергии | 0 | 0,001 | Выполнен |
Допуски относятся к указанной учебной задаче и способу сравнения. Число показанных цифр не означает такой же физической точности.
Модели, решения и программа
Для нового расчёта сохраните программу, модели и необходимые данные в одну рабочую папку, сохраняя структуру подпапок.
- Файл результатов: README.md4259 байт
- Магнитное решение: input.ans26596544 байт
- Магнитная модель: input.fem22482 байт
- Программа встроенного Python: reproduce.py27212 байт
- Файл результатов: verification.json1002 байт
- Магнитное решение: сетка-1.ans12094567 байт
- Магнитная модель: сетка-1.fem22849 байт
- Магнитное решение: сетка-2.ans26654217 байт
- Магнитная модель: сетка-2.fem22800 байт
- Магнитное решение: сетка-3.ans73107531 байт
- Магнитная модель: сетка-3.fem23247 байт
Границы применимости
- Двумерная учебная геометрия; точные входы и русские свойства включены в комплект. Торцевые поля, механическая динамика, температурная зависимость и реальные потери стали требуют отдельных моделей.
- Это результат численного исследования выбранной модели; физический макет и заявленное изготовителем изделие не проверялись.