Теплопроводность · Плоскопараллельная
Взаимный нагрев кабелей и расстояние между ними
Три кабеля радиусами жилы 5 мм и изоляции 10 мм находятся на глубине 1 м в грунте k=1 Вт/(м К). Каждый выделяет 20 Вт/м. Сравниваются расстояния между осями 150, 300 и 600 мм; поверхность грунта поддерживается при 20 °C.
Работа связывает полевое распределение с измеряемой электротехнической величиной. Все исходные данные заданы в программе; принятые идеализации сформулированы до расчёта. Основной результат проверяется независимой формулой или балансом, а чувствительность к геометрии исследуется отдельными вариантами.
Самостоятельная учебная модель FEMM 5: от физических соотношений к полевому решению и проверке результата.
Допущения и устройство учебной модели
Все размеры, свойства материалов и условия возбуждения заданы ниже. Численные результаты относятся к опубликованной версии модели FEMM 5.
Физическая и математическая постановка
Теория и физическая постановка
Линейное уравнение теплопроводности допускает сложение температурных полей. Поэтому температура среднего кабеля содержит собственный вклад и два вклада соседей. Для полупространства с изотермической поверхностью независимая оценка получается методом зеркальных линейных источников противоположного знака.
Тепловой поток в изоляции создаёт собственный логарифмический перепад, который почти не зависит от расстояния между кабелями. Взаимное влияние передаётся через грунт. При увеличении расстояния снижается именно этот вклад; оценивать его по одному внутреннему сопротивлению кабеля невозможно.
Численная область ограничена удалённым прямоугольником 40 × 21 м с заданной температурой 20 °C. Это приближение полупространства проверяется сравнением с аналитикой. Разность температур между тесной и разреженной прокладкой дополнительно сравнивается с разностью аналитических оценок, что уменьшает влияние общего удалённого граничного условия.
Как читать расчёт
Программа строит три сетки, сохраняет каждую модель, закрывает её и повторно открывает перед решением. Поэтому проверка относится к точному записанному файлу с русскими именами материалов, проводников и граничных условий. Результаты каждой серии находятся в новой папке calculated-XX рядом с программой; предыдущая серия не перезаписывается.
В result.json массив records содержит расчётные величины и параметры сетки, checks — фактические отклонения, допустимые пределы и логический результат. Проверяйте все критерии одновременно. Совпадение одной точки с ориентиром не заменяет проверки баланса и изменения результата по сетке. Для итерационной обратной связи отдельно контролируется сходимость итераций; она не является оценкой пространственной погрешности.
Практическое исследование
Увеличьте удалённую область и сравните изменение абсолютных температур с изменением их разности между двумя расстояниями. Отделите ошибку внешней границы от погрешности мелкой кабельной сетки.
Отключите потери крайних кабелей при сохранённой геометрии. Разность температур среднего кабеля прямо показывает взаимный вклад. Проверьте его линейное масштабирование с потерями соседей.
Измените глубину прокладки и сопоставьте собственный и взаимный вклады. Не переносите результат изотермической поверхности на реальную сезонную границу без дополнительной модели окружающей среды.
Исходные данные
Три кабеля радиусами жилы 5 мм и изоляции 10 мм находятся на глубине 1 м в грунте k=1 Вт/(м К). Каждый выделяет 20 Вт/м. Сравниваются расстояния между осями 150, 300 и 600 мм; поверхность грунта поддерживается при 20 °C.
| Параметр | Значение | Единица | Происхождение |
|---|---|---|---|
| Глубина | 1000 | мм | Принято в учебной постановке |
| Расстояния осей | 150; 300; 600 | мм | Принято в учебной постановке |
| Потери каждого кабеля | 20 | Вт/м | Принято в учебной постановке |
| k грунта | 1 | Вт/(м К) | Принято в учебной постановке |
| Температура поверхности | 20 | °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 = {
"polygon": [[-20000, -20000], [20000, -20000], [20000, 1000], [-20000, 1000]],
"depth": 1000,
"boundaries": [["Удалённая среда", 0, 293.15, 0, 0]],
"boundary_segments": [
[-20000, -1000, "Удалённая среда"],
[20000, -1000, "Удалённая среда"],
[0, -20000, "Удалённая среда"],
[0, 1000, "Удалённая среда"],
],
"boundary_mesh": 500,
"mesh_multipliers": {"Грунт": 2000},
"samples": [[0, 0]],
}
for level, mesh in enumerate((0.8, 0.4, 0.2)):
readings = []
for pitch in (150, 300, 600):
job = json.loads(json.dumps(JOB))
job["materials"] = [
["Медь", 380, 380, 20 / (math.pi * 0.005**2)],
["Изоляция", 0.2, 0.2, 0],
["Грунт", 1, 1, 0],
]
job["circles"] = []
job["interfaces"] = [
[-1000, -1000, 1000, -1000],
[-1000, -1000, -1000, 1000],
[1000, -1000, 1000, 1000],
]
job["boundary_segments"] += [
[-10000, 1000, "Удалённая среда"],
[10000, 1000, "Удалённая среда"],
]
job["blocks"] = [[0, -500, "Грунт", 99], [5000, -5000, "Грунт", 98]]
job["block_mesh_multipliers"] = {"99": 80, "98": 2000}
for cable, x in enumerate((-pitch, 0, pitch)):
job["circles"] += [
[x, 0, 5, ""],
[x, 0, 10, ""],
[x, 0, 30, ""],
[x, 0, 60, ""],
]
job["blocks"] += [
[x, 0, "Медь", 10 * cable + 1],
[x + 7, 0, "Изоляция", 10 * cable + 2],
[x + 20, 0, "Грунт", 10 * cable + 3],
[x + 45, 0, "Грунт", 10 * cable + 4],
]
job["block_mesh_multipliers"][str(10 * cable + 3)] = 5
job["block_mesh_multipliers"][str(10 * cable + 4)] = 15
result = heat(job, mesh, "mesh-%d-pitch-%d" % (level + 1, pitch))
p = pitch * 0.001
self_heat = (
20 / (2 * math.pi) * math.log(2 / 0.01)
+ 20 / (2 * math.pi * 0.2) * math.log(10 / 5)
+ 20 / (4 * math.pi * 380)
)
neighbor = 2 * 20 / (2 * math.pi) * math.log(math.sqrt(p * p + 4) / p)
result["pitch_mm"] = pitch
result["T_center_K"] = result["samples"][0]["T_K"]
result["analytic_halfspace_K"] = 293.15 + self_heat + neighbor
check(
"Изображения линейных источников, шаг %d мм" % pitch,
abs(result["T_center_K"] - result["analytic_halfspace_K"]),
1,
)
check("Полная мощность трёх кабелей", abs(result["generated_W"] - 60), 0.02)
readings.append(result)
records.append(
{"mesh_mm": mesh, "readings": readings, "model": readings[-1]["model"]}
)
check(
"Сходимость температуры средней прокладки, К",
abs(
records[-1]["readings"][1]["T_center_K"]
- records[-2]["readings"][1]["T_center_K"]
),
0.15,
)
actual = (
records[-1]["readings"][0]["T_center_K"] - records[-1]["readings"][-1]["T_center_K"]
)
reference = (
records[-1]["readings"][0]["analytic_halfspace_K"]
- records[-1]["readings"][-1]["analytic_halfspace_K"]
)
check(
"Влияние расстояния против аналитической разности, К", abs(actual - reference), 0.15
)
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. Выполнено проверок: 20. Полная точность чисел и условия сравнения доступны в таблицах результатов JSON.
| Масштаб сетки, мм | Модель | Тепловыделение, Вт | Конвективный тепловой поток, Вт | Расстояние между осями кабелей, мм | Элементов | Контрольные точки | Температура в центре, К | Температура аналитического полупространства, К | Средняя температура области, К | Узлов | Контрольная точка: Температура, К | Контрольная точка: Координата x, мм | Контрольная точка: Координата y, мм |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0,8 | mesh-1-pitch-150.feh | 60 | 0 | 150 | 57686 | 1 точек; полные данные JSON | 337,4 | 337,6 | 14 полей; полные данные JSON | 29048 | 337,4 | 0 | 0 |
| 0,8 | mesh-1-pitch-300.feh | 60 | 0 | 300 | 57814 | 1 точек; полные данные JSON | 333 | 333,2 | 14 полей; полные данные JSON | 29113 | 333 | 0 | 0 |
| 0,8 | mesh-1-pitch-600.feh | 60 | 0 | 600 | 57822 | 1 точек; полные данные JSON | 328,8 | 329 | 14 полей; полные данные JSON | 29123 | 328,8 | 0 | 0 |
| 0,4 | mesh-2-pitch-150.feh | 60 | 0 | 150 | 72737 | 1 точек; полные данные JSON | 337,5 | 337,6 | 14 полей; полные данные JSON | 36590 | 337,5 | 0 | 0 |
| 0,4 | mesh-2-pitch-300.feh | 60 | 0 | 300 | 72594 | 1 точек; полные данные JSON | 333,1 | 333,2 | 14 полей; полные данные JSON | 36518 | 333,1 | 0 | 0 |
| 0,4 | mesh-2-pitch-600.feh | 60 | 0 | 600 | 72623 | 1 точек; полные данные JSON | 328,9 | 329 | 14 полей; полные данные JSON | 36534 | 328,9 | 0 | 0 |
| 0,2 | mesh-3-pitch-150.feh | 60 | 0 | 150 | 151940 | 1 точек; полные данные JSON | 337,5 | 337,6 | 14 полей; полные данные JSON | 76226 | 337,5 | 0 | 0 |
| 0,2 | mesh-3-pitch-300.feh | 60 | 0 | 300 | 152586 | 1 точек; полные данные JSON | 333,1 | 333,2 | 14 полей; полные данные JSON | 76550 | 333,1 | 0 | 0 |
| 0,2 | mesh-3-pitch-600.feh | 60 | 0 | 600 | 152004 | 1 точек; полные данные JSON | 328,9 | 329 | 14 полей; полные данные JSON | 76261 | 328,9 | 0 | 0 |
Итоги исходных серий
| Контрольные показания | Масштаб сетки, мм | Модель |
|---|---|---|
| 3 точек; полные данные JSON | 0,8 | mesh-1-pitch-600.feh |
| 3 точек; полные данные JSON | 0,4 | mesh-2-pitch-600.feh |
| 3 точек; полные данные JSON | 0,2 | mesh-3-pitch-600.feh |
| Критерий | Получено | Допуск или ожидаемое значение | Итог |
|---|---|---|---|
| Изображения линейных источников, шаг 150 мм | 0,1865 | 1 | Выполнен |
| Полная мощность трёх кабелей | 0,003046 | 0,02 | Выполнен |
| Изображения линейных источников, шаг 300 мм | 0,183 | 1 | Выполнен |
| Полная мощность трёх кабелей | 0,003046 | 0,02 | Выполнен |
| Изображения линейных источников, шаг 600 мм | 0,2136 | 1 | Выполнен |
| Полная мощность трёх кабелей | 0,003046 | 0,02 | Выполнен |
| Изображения линейных источников, шаг 150 мм | 0,1043 | 1 | Выполнен |
| Полная мощность трёх кабелей | 0,003046 | 0,02 | Выполнен |
| Изображения линейных источников, шаг 300 мм | 0,1263 | 1 | Выполнен |
| Полная мощность трёх кабелей | 0,003046 | 0,02 | Выполнен |
| Изображения линейных источников, шаг 600 мм | 0,1337 | 1 | Выполнен |
| Полная мощность трёх кабелей | 0,003046 | 0,02 | Выполнен |
| Изображения линейных источников, шаг 150 мм | 0,07051 | 1 | Выполнен |
| Полная мощность трёх кабелей | 0,003046 | 0,02 | Выполнен |
| Изображения линейных источников, шаг 300 мм | 0,08809 | 1 | Выполнен |
| Полная мощность трёх кабелей | 0,003046 | 0,02 | Выполнен |
| Изображения линейных источников, шаг 600 мм | 0,09305 | 1 | Выполнен |
| Полная мощность трёх кабелей | 0,003046 | 0,02 | Выполнен |
| Сходимость температуры средней прокладки, К | 0,03816 | 0,15 | Выполнен |
| Влияние расстояния против аналитической разности, К | 0,02254 | 0,15 | Выполнен |
Допуски относятся к указанной учебной задаче и способу сравнения. Число показанных цифр не означает такой же физической точности.
Модели, решения и программа
Для нового расчёта сохраните программу, модели и необходимые данные в одну рабочую папку, сохраняя структуру подпапок.
- Файл результатов: README.md8417 байт
- Тепловое решение: mesh-1-pitch-150.anh2938228 байт
- Тепловая модель: mesh-1-pitch-150.feh2752 байт
- Тепловое решение: mesh-1-pitch-300.anh2944912 байт
- Тепловая модель: mesh-1-pitch-300.feh2754 байт
- Тепловое решение: mesh-1-pitch-600.anh2945341 байт
- Тепловая модель: mesh-1-pitch-600.feh2754 байт
- Тепловое решение: mesh-2-pitch-150.anh3718054 байт
- Тепловая модель: mesh-2-pitch-150.feh2748 байт
- Тепловое решение: mesh-2-pitch-300.anh3711040 байт
- Тепловая модель: mesh-2-pitch-300.feh2750 байт
- Тепловое решение: mesh-2-pitch-600.anh3712882 байт
- Тепловая модель: mesh-2-pitch-600.feh2750 байт
- Тепловое решение: mesh-3-pitch-150.anh7838849 байт
- Тепловая модель: mesh-3-pitch-150.feh2748 байт
- Тепловое решение: mesh-3-pitch-300.anh7872595 байт
- Тепловая модель: mesh-3-pitch-300.feh2750 байт
- Тепловое решение: mesh-3-pitch-600.anh7841766 байт
- Тепловая модель: mesh-3-pitch-600.feh2750 байт
- Программа встроенного Python: reproduce.py13030 байт
- Файл результатов: verification.json913 байт
Границы применимости
- Однородный грунт и изотермическая поверхность; не учитываются конвекция над грунтом, высыхание, траншея и продольный теплоотвод. Аналитическая оценка использует линейные источники вне собственных сечений.
- Проверка относится к данной учебной постановке и решателю FEMM 5.0 06Oct2026. Снимки интерфейса выполняются отдельным согласованным проходом.