Теплопроводность · Плоскопараллельная
Ток кабеля при ограничении температуры и обратной связи сопротивления
Медная жила радиуса 5 мм, изоляция до радиуса 8 мм, длина 1 м. Охлаждение воздухом 20 °C с h=10 Вт/(м² К). Учебное ограничение температуры жилы 90 °C; сопротивление зависит от температуры.
Работа связывает полевое распределение с измеряемой электротехнической величиной. Все исходные данные заданы в программе; принятые идеализации сформулированы до расчёта. Основной результат проверяется независимой формулой или балансом, а чувствительность к геометрии исследуется отдельными вариантами.
Самостоятельная учебная модель FEMM 5: от физических соотношений к полевому решению и проверке результата.
Допущения и устройство учебной модели
Все размеры, свойства материалов и условия возбуждения заданы ниже. Численные результаты относятся к опубликованной версии модели FEMM 5.
Физическая и математическая постановка
Теория и физическая постановка
Продольный ток в круглой жиле считается равномерным, поэтому J=I/A. Его тепловое действие передаётся поперечной модели как qv=J²/σ. Это аналитический электрический подрасчёт, а не поле осевого тока, ошибочно решённое в поперечной плоскости. При постоянных тепловых коэффициентах радиальное тепловое сопротивление имеет точную формулу.
Удельное электрическое сопротивление растёт по закону 1+αΔT. Для заданного тока это положительная обратная связь: больший нагрев увеличивает сопротивление и джоулевы потери. В учебной модели проводимость всей жилы определяется её центральной температурой. При выбранной высокой теплопроводности меди внутренний перепад мал, но принятое приближение явно сохраняется в интерпретации.
Сначала нативный тепловой расчёт с единичной погонной мощностью определяет коэффициент температура/мощность. Из него вычисляется ток, достигающий 90 °C с учётом изменения сопротивления. Дополнительный расчёт проверяет эту температуру. Затем для 80 процентов найденного тока выполняются явные итерации J²-нагрева с релаксацией и сравнением с независимым нелинейным аналитическим решением.
Как читать расчёт
Программа строит три сетки, сохраняет каждую модель, закрывает её и повторно открывает перед решением. Поэтому проверка относится к точному записанному файлу с русскими именами материалов, проводников и граничных условий. Результаты каждой серии находятся в новой папке calculated-XX рядом с программой; предыдущая серия не перезаписывается.
В result.json массив records содержит расчётные величины и параметры сетки, checks — фактические отклонения, допустимые пределы и логический результат. Проверяйте все критерии одновременно. Совпадение одной точки с ориентиром не заменяет проверки баланса и изменения результата по сетке. Для итерационной обратной связи отдельно контролируется сходимость итераций; она не является оценкой пространственной погрешности.
Практическое исследование
Отключите температурный коэффициент и сравните найденный ток с расчётом постоянного сопротивления. Объясните, почему обратная связь снижает ток при том же температурном ограничении.
Измените толщину изоляции и коэффициент теплоотдачи отдельно. Определите, какой тепловой участок ограничивает результат. Сравните чувствительность тока и перегрева: зависимость через квадрат тока не является линейной.
Исследуйте приближение к условию I²R20′Rth′α=1 в аналитической модели. Объясните, почему потеря стационарного решения этой упрощённой модели не является готовым прогнозом разрушения реального кабеля.
Исходные данные
Медная жила радиуса 5 мм, изоляция до радиуса 8 мм, длина 1 м. Охлаждение воздухом 20 °C с h=10 Вт/(м² К). Учебное ограничение температуры жилы 90 °C; сопротивление зависит от температуры.
| Параметр | Значение | Единица | Происхождение |
|---|---|---|---|
| Радиус жилы | 5 | мм | Принято в учебной постановке |
| Радиус изоляции | 8 | мм | Принято в учебной постановке |
| α меди | 0,00393 | 1/К | Принято в учебной постановке |
| k изоляции | 0,2 | Вт/(м К) | Принято в учебной постановке |
| Температурное ограничение | 90 | °C | Принято в учебной постановке |
Порядок воспроизведения
- Скопируйте reproduce.py в отдельную рабочую папку и откройте встроенным редактором Python FEMM 5. Проверьте заданные параметры и единицы, затем выполните программу целиком. Внешний модуль pyFEMM для этой программы не используется.
- Откройте модель и решение окончательной сетки. Сопоставьте материалы, проводники и границы с теорией. Для осесимметричной задачи найдите ось вращения и объясните, какие поверхности образуются при вращении каждой линии поперечного рисунка.
- Составьте таблицу трёх сеток и приведите независимый расчёт по формуле. Для серии из нескольких геометрий сравнивайте только результаты одной и той же сетки, затем отдельно исследуйте её уточнение. Укажите абсолютные и относительные ошибки с единицами.
- Выполните предложенные варианты по одному. До запуска предскажите знак и примерный масштаб эффекта. В отчёте объясните, какие величины действительно определяет модель, какие параметры остаются предположениями и что пришлось бы уточнить для реального изделия.
Скачайте нужные файлы по отдельным ссылкам ниже в одну рабочую папку, затем откройте reproduce.py во встроенном Python FEMM 5 и выполните его. Сохраните имена файлов и структуру подпапок. Условия конкретного запуска и путь новых результатов указаны в комментариях программы.
Программа встроенного Python FEMM 5
"""Самостоятельная лабораторная работа курса FEMM 5.
Запуск: встроенный MicroPython FEMM 5, команды FEMM доступны непосредственно.
Геометрические координаты и глубина — мм; результаты интегралов — единицы СИ.
Данные, допущения, аналитические ориентиры и ход исследования заданы в README.md.
Каждая серия записывается в новую calculated-XX; прежние результаты сохраняются.
Сохранённая модель закрывается и открывается до расчёта, включая русские имена.
"""
import os
import sys
import json
import math
script = globals().get("__file__", "") or (sys.argv[0] if sys.argv else "")
base = os.path.dirname(os.path.abspath(script)) if script else os.getcwd()
index = 1
while "calculated-%02d" % index in os.listdir(base):
index += 1
directory = base + "/calculated-%02d" % index
os.mkdir(directory)
records = []
checks = []
def real(value):
if isinstance(value, (tuple, list)):
return real(value[0])
return complex(value).real
def check(name, value, limit):
checks.append(
{
"name": name,
"value": value,
"limit": limit,
"passed": math.isfinite(value) and value <= limit,
}
)
def polygon(prefix, points):
for x, y in points:
globals()[prefix + "_addnode"](x, y)
for i, (x, y) in enumerate(points):
x2, y2 = points[(i + 1) % len(points)]
globals()[prefix + "_addsegment"](x, y, x2, y2)
def geometry(prefix, job, mesh):
if job.get("polygon"):
polygon(prefix, job["polygon"])
for x1, y1, x2, y2 in job.get("interfaces", []) + job.get("segments", []):
globals()[prefix + "_addnode"](x1, y1)
globals()[prefix + "_addnode"](x2, y2)
globals()[prefix + "_addsegment"](x1, y1, x2, y2)
for x1, y1, x2, y2, angle, name in job.get("arcs", []):
globals()[prefix + "_addnode"](x1, y1)
globals()[prefix + "_addnode"](x2, y2)
globals()[prefix + "_addarc"](x1, y1, x2, y2, angle, 1)
cx, cy = job.get("arc_center", (0, 0))
mx = (x1 + x2) / 2 - cx
my = (y1 + y2) / 2 - cy
radius = math.sqrt((x1 - cx) ** 2 + (y1 - cy) ** 2)
factor = radius / math.sqrt(mx * mx + my * my)
globals()[prefix + "_selectarcsegment"](cx + mx * factor, cy + my * factor)
if prefix == "ci":
ci_setarcsegmentprop(1, "<None>", 0, 0, name or "<None>")
else:
hi_setarcsegmentprop(1, name or "<None>", 0, 0, "<None>")
globals()[prefix + "_clearselected"]()
for x, y, radius, name in job.get("circles", []):
globals()[prefix + "_addnode"](x - radius, y)
globals()[prefix + "_addnode"](x + radius, y)
globals()[prefix + "_addarc"](x - radius, y, x + radius, y, 180, 1)
globals()[prefix + "_addarc"](x + radius, y, x - radius, y, 180, 1)
globals()[prefix + "_selectarcsegment"](x, y + radius)
globals()[prefix + "_selectarcsegment"](x, y - radius)
if prefix == "ci":
ci_setarcsegmentprop(1, "<None>", 0, 0, name or "<None>")
else:
hi_setarcsegmentprop(1, name or "<None>", 0, 0, "<None>")
globals()[prefix + "_clearselected"]()
for x, y, name, group in job["blocks"]:
globals()[prefix + "_addblocklabel"](x, y)
globals()[prefix + "_selectlabel"](x, y)
globals()[prefix + "_setblockprop"](
name,
0,
mesh
* job.get("block_mesh_multipliers", {}).get(
str(group), job.get("mesh_multipliers", {}).get(name, 1)
),
group,
)
globals()[prefix + "_clearselected"]()
def current(job, mesh, stem):
newdocument(3)
ci_probdef("millimeters", job.get("type", "planar"), 0, 1e-10, job["depth"], 30)
for name, sx, sy in job["materials"]:
ci_addmaterial(name, sx, sy, 1, 1, 0, 0)
for name, voltage, amperes, kind in job["terminals"]:
ci_addconductorprop(name, voltage, amperes, kind)
geometry("ci", job, mesh)
for x, y, name in job.get("electrode_segments", []):
ci_selectsegment(x, y)
ci_setsegmentprop("<None>", mesh, 0, 0, 0, name)
ci_clearselected()
path = directory + "/" + stem + ".fec"
ci_saveas(path)
ci_close()
opendocument(path)
ci_analyze(1)
ci_loadsolution()
co_smooth("off")
terminals = {}
terminal_power = 0
balance = 0
current_scale = 0
for name, voltage, amperes, kind in job["terminals"]:
u, i = co_getconductorproperties(name)
terminals[name] = {"U_V": real(u), "I_A": real(i)}
terminal_power += real(complex(u) * complex(i).conjugate())
balance += complex(i)
current_scale = max(current_scale, abs(complex(i)))
powers = {}
power = 0
for x, y, name, group in job["blocks"]:
if name == "<No Mesh>":
continue
co_groupselectblock(group)
powers[str(group)] = real(co_blockintegral(0))
power += powers[str(group)]
co_clearblock()
samples = []
for x, y in job.get("samples", []):
values = co_getpointvalues(x, y)
samples.append(
{
"x_mm": x,
"y_mm": y,
"U_V": real(values[0]),
"J_A_m2": math.sqrt(
abs(complex(values[1])) ** 2 + abs(complex(values[2])) ** 2
),
}
)
row = {
"model": stem + ".fec",
"mesh_mm": mesh,
"nodes": co_numnodes(),
"elements": co_numelements(),
"terminals": terminals,
"P_W": power,
"group_power_W": powers,
"samples": samples,
"current_balance_relative": abs(balance) / current_scale,
"power_balance_relative": abs(power - terminal_power) / max(power, 1e-30),
}
check(stem + ": баланс токов", row["current_balance_relative"], 1e-6)
check(stem + ": баланс мощности", row["power_balance_relative"], 1e-6)
co_close()
ci_close()
return row
def heat(job, mesh, stem):
newdocument(2)
hi_probdef("millimeters", job.get("type", "planar"), 1e-10, job["depth"], 30)
for name, kx, ky, q in job["materials"]:
hi_addmaterial(name, kx, ky, q, 0)
for boundary in job["boundaries"]:
name, kind, temp, ambient, h = boundary[:5]
beta = boundary[5] if len(boundary) > 5 else 0
hi_addboundprop(name, kind, temp, 0, ambient, h, beta)
geometry("hi", job, mesh)
for x, y, name in job.get("boundary_segments", []):
hi_selectsegment(x, y)
hi_setsegmentprop(name, job.get("boundary_mesh", mesh), 0, 0, 0, "<None>")
hi_clearselected()
path = directory + "/" + stem + ".feh"
hi_saveas(path)
hi_close()
opendocument(path)
hi_analyze(1)
hi_loadsolution()
ho_smooth("off")
averages = {}
generated = 0
for x, y, name, group in job["blocks"]:
ho_groupselectblock(group)
averages[str(group)] = real(ho_blockintegral(0))
volume = real(ho_blockintegral(2))
q = [m[3] for m in job["materials"] if m[0] == name][0]
generated += q * volume
ho_clearblock()
samples = []
for x, y in job.get("samples", []):
samples.append({"x_mm": x, "y_mm": y, "T_K": real(ho_getpointvalues(x, y)[0])})
convection = 0
for x1, y1, x2, y2, ambient, h in job.get("convection_edges", []):
count = 200
total = 0
for n in range(count + 1):
t = real(
ho_getpointvalues(
x1 + (x2 - x1) * n / count, y1 + (y2 - y1) * n / count
)[0]
)
total += (0.5 if n in (0, count) else 1) * (t - ambient)
length = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) * 0.001
convection += total / count * length * job["depth"] * 0.001 * h
row = {
"model": stem + ".feh",
"mesh_mm": mesh,
"nodes": ho_numnodes(),
"elements": ho_numelements(),
"samples": samples,
"group_average_K": averages,
"generated_W": generated,
"convection_W": convection,
}
if job.get("convection_edges") and not job.get("skip_convection_balance"):
check(
stem + ": баланс конвективного теплоотвода",
abs(convection - generated) / max(generated, 1e-30),
0.005,
)
ho_close()
hi_close()
return row
JOB = {
"depth": 1000,
"materials": [],
"blocks": [[0, 0, "Медная жила", 1], [7, 0, "Изоляция", 2]],
"circles": [[0, 0, 5, ""], [0, 0, 8, "Конвекция"]],
"boundaries": [["Конвекция", 2, 0, 293.15, 10]],
"samples": [[0, 0]],
}
area = math.pi * 0.005**2
resistance20 = 1 / (5.8e7 * area)
alpha = 0.00393
analytic_rth = (
1 / (4 * math.pi * 380)
+ math.log(8 / 5) / (2 * math.pi * 0.2)
+ 1 / (2 * math.pi * 0.008 * 10)
)
limit_temperature = 363.15
reference_current = math.sqrt(70 / (resistance20 * analytic_rth * (1 + alpha * 70)))
for level, mesh in enumerate((0.4, 0.2, 0.1)):
job = json.loads(json.dumps(JOB))
job["materials"] = [["Медная жила", 380, 380, 1 / area], ["Изоляция", 0.2, 0.2, 0]]
unit = heat(job, mesh, "mesh-%d-unit" % (level + 1))
rth = unit["samples"][0]["T_K"] - 293.15
maximum_current = math.sqrt(70 / (resistance20 * rth * (1 + alpha * 70)))
operating_current = 0.8 * maximum_current
temperature = 293.15
history = []
for iteration in range(1, 61):
conductivity = 5.8e7 / (1 + alpha * (temperature - 293.15))
q = (operating_current / area) ** 2 / conductivity
job["materials"] = [["Медная жила", 380, 380, q], ["Изоляция", 0.2, 0.2, 0]]
result = heat(job, mesh, "mesh-%d-feedback-%02d" % (level + 1, iteration))
new_temperature = result["samples"][0]["T_K"]
residual = abs(new_temperature - temperature)
history.append(
{"iteration": iteration, "T_K": new_temperature, "residual_K": residual}
)
if residual < 0.001:
break
temperature = (temperature + new_temperature) / 2
check("Итерационная сходимость, сетка %d" % (level + 1), residual, 0.001)
check(
"Ток температурного ограничения против аналитики",
abs(maximum_current / reference_current - 1),
0.002,
)
analytic_delta = (
operating_current**2
* resistance20
* analytic_rth
/ (1 - operating_current**2 * resistance20 * analytic_rth * alpha)
)
check(
"Итерационная температура против аналитики, К",
abs(new_temperature - 293.15 - analytic_delta),
0.1,
)
q = (maximum_current / area) ** 2 / (5.8e7 / (1 + alpha * 70))
job["materials"] = [["Медная жила", 380, 380, q], ["Изоляция", 0.2, 0.2, 0]]
limit_result = heat(job, mesh, "mesh-%d-limit" % (level + 1))
check(
"Достижение заданной температуры, К",
abs(limit_result["samples"][0]["T_K"] - limit_temperature),
0.01,
)
records.append(
{
"mesh_mm": mesh,
"model": limit_result["model"],
"I_limit_A": maximum_current,
"I_operating_A": operating_current,
"operating_model": result["model"],
"unit_model": unit["model"],
"T_operating_K": new_temperature,
"iterations": iteration,
"history": history,
}
)
check(
"Сходимость предельного тока",
abs(records[-1]["I_limit_A"] / records[-2]["I_limit_A"] - 1),
0.002,
)
result = {
"runtime": "FEMM 5; embedded MicroPython",
"records": records,
"checks": checks,
"passed": all(item["passed"] for item in checks),
}
with open(directory + "/result.json", "w") as output:
json.dump(result, output)
print(json.dumps(result))
assert result["passed"]
Картина рассчитанного поля
Окна FEMM 5
Снимки интерфейса FEMM показывают геометрию, настройки и работу с результатами в FEMM 5. Откройте изображение, чтобы рассмотреть подписи и значения в полном размере.
Результаты и численные проверки
Сохранено решений FEMM: 9. Выполнено проверок: 13. Полная точность чисел и условия сравнения доступны в таблицах результатов JSON.
| Рабочий ток, А | Модель рабочего режима | Модель единичного возбуждения | Модель | Рабочая температура, К | Итераций | История итераций | Масштаб сетки, мм | Предельный ток по температуре, А |
|---|---|---|---|---|---|---|---|---|
| 260,2 | mesh-1-feedback-20.feh | mesh-1-unit.feh | mesh-1-limit.feh | 333,9 | 20 | 20 точек; полные данные JSON | 0,4 | 325,3 |
| 260,2 | mesh-2-feedback-20.feh | mesh-2-unit.feh | mesh-2-limit.feh | 333,9 | 20 | 20 точек; полные данные JSON | 0,2 | 325,3 |
| 260,2 | mesh-3-feedback-20.feh | mesh-3-unit.feh | mesh-3-limit.feh | 333,9 | 20 | 20 точек; полные данные JSON | 0,1 | 325,3 |
| Критерий | Получено | Допуск или ожидаемое значение | Итог |
|---|---|---|---|
| Итерационная сходимость, сетка 1 | 782,4 × 10⁻⁶ | 0,001 | Выполнен |
| Ток температурного ограничения против аналитики | 44,26 × 10⁻⁶ | 0,002 | Выполнен |
| Итерационная температура против аналитики, К | 0,004313 | 0,1 | Выполнен |
| Достижение заданной температуры, К | 313,8 × 10⁻⁹ | 0,01 | Выполнен |
| Итерационная сходимость, сетка 2 | 782,4 × 10⁻⁶ | 0,001 | Выполнен |
| Ток температурного ограничения против аналитики | 35,83 × 10⁻⁶ | 0,002 | Выполнен |
| Итерационная температура против аналитики, К | 0,003514 | 0,1 | Выполнен |
| Достижение заданной температуры, К | 240,3 × 10⁻⁹ | 0,01 | Выполнен |
| Итерационная сходимость, сетка 3 | 782,4 × 10⁻⁶ | 0,001 | Выполнен |
| Ток температурного ограничения против аналитики | 24,41 × 10⁻⁶ | 0,002 | Выполнен |
| Итерационная температура против аналитики, К | 0,002435 | 0,1 | Выполнен |
| Достижение заданной температуры, К | 364,1 × 10⁻⁹ | 0,01 | Выполнен |
| Сходимость предельного тока | 11,41 × 10⁻⁶ | 0,002 | Выполнен |
Допуски относятся к указанной учебной задаче и способу сравнения. Число показанных цифр не означает такой же физической точности.
Модели, решения и программа
Для нового расчёта сохраните программу, модели и необходимые данные в одну рабочую папку, сохраняя структуру подпапок.
- Файл результатов: README.md8728 байт
- Тепловое решение: mesh-1-feedback-20.anh467727 байт
- Тепловая модель: mesh-1-feedback-20.feh1240 байт
- Тепловое решение: mesh-1-limit.anh467689 байт
- Тепловая модель: mesh-1-limit.feh1240 байт
- Тепловое решение: mesh-1-unit.anh467705 байт
- Тепловая модель: mesh-1-unit.feh1240 байт
- Тепловое решение: mesh-2-feedback-20.anh660442 байт
- Тепловая модель: mesh-2-feedback-20.feh1240 байт
- Тепловое решение: mesh-2-limit.anh660393 байт
- Тепловая модель: mesh-2-limit.feh1240 байт
- Тепловое решение: mesh-2-unit.anh660393 байт
- Тепловая модель: mesh-2-unit.feh1240 байт
- Тепловое решение: mesh-3-feedback-20.anh2395634 байт
- Тепловая модель: mesh-3-feedback-20.feh1240 байт
- Тепловое решение: mesh-3-limit.anh2395630 байт
- Тепловая модель: mesh-3-limit.feh1239 байт
- Тепловое решение: mesh-3-unit.anh2395549 байт
- Тепловая модель: mesh-3-unit.feh1240 байт
- Программа встроенного Python: reproduce.py12787 байт
- Файл результатов: verification.json913 байт
Границы применимости
- Получен ток только для заданных учебных условий охлаждения и свойств. Не рассчитываются допустимый ток по нормативу, старение изоляции, скин-эффект, диэлектрические потери и реальная прокладка кабеля.
- Проверка относится к данной учебной постановке и решателю FEMM 5.0 06Oct2026. Снимки интерфейса выполняются отдельным согласованным проходом.