Электрическое поле тока · Осесимметричная
Полусферический заземлитель и потенциал поверхности грунта
Полусферический электрод радиуса 0.1 м лежит в однородном полупространстве грунта. Внешняя эквипотенциальная полусфера имеет радиус 10 м. Удельное сопротивление грунта 100 Ом м; электрод 100 В, удалённая граница 0 В. Модель осесимметричная.
Работа связывает полевое распределение с измеряемой электротехнической величиной. Все исходные данные заданы в программе; принятые идеализации сформулированы до расчёта. Основной результат проверяется независимой формулой или балансом, а чувствительность к геометрии исследуется отдельными вариантами.
Самостоятельная учебная модель FEMM 5: от физических соотношений к полевому решению и проверке результата.
Допущения и устройство учебной модели
Все размеры, свойства материалов и условия возбуждения заданы ниже. Численные результаты относятся к опубликованной версии модели FEMM 5.
Физическая и математическая постановка
Теория и физическая постановка
Изолированная поверхность грунта не выпускает постоянный ток в воздух. Поэтому ток от полусферического электрода растекается через полусферические поверхности площади 2πr². Интегрирование радиального поля даёт аналитическое сопротивление. Для конечного внешнего радиуса в формуле обязательно остаётся член 1/R.
Правая нижняя четверть рисунка при вращении вокруг вертикальной оси создаёт полусферический объём, а не бесконечно длинный цилиндр. Это позволяет избежать ошибочной интерпретации плоской модели трёх стержней как пространственного заземлителя. На оси действует естественное условие симметрии, верхний горизонтальный участок изолирован.
Программа также измеряет потенциал поверхности на расстоянии 1 и 2 м от оси. Разность между потенциалом электрода и потенциалом грунта в выбранной точке обозначена учебным напряжением прикосновения без учёта тела и растекания через стопы. Она характеризует только заданное поле грунта и не является расчётом допустимого воздействия на человека.
Как читать расчёт
Программа строит три сетки, сохраняет каждую модель, закрывает её и повторно открывает перед решением. Поэтому проверка относится к точному записанному файлу с русскими именами материалов, проводников и граничных условий. Результаты каждой серии находятся в новой папке calculated-XX рядом с программой; предыдущая серия не перезаписывается.
В result.json массив records содержит расчётные величины и параметры сетки, checks — фактические отклонения, допустимые пределы и логический результат. Проверяйте все критерии одновременно. Совпадение одной точки с ориентиром не заменяет проверки баланса и изменения результата по сетке. Для итерационной обратной связи отдельно контролируется сходимость итераций; она не является оценкой пространственной погрешности.
Практическое исследование
Увеличьте внешний радиус и сравните сопротивление с пределом ρ/(2πa). Количественно оцените ошибку усечения области и отделите её от погрешности сетки.
Измените радиус электрода и сопротивление грунта. Объясните различие между изменением сопротивления заземлителя при фиксированном токе и изменением тока при фиксированном напряжении.
Постройте профиль потенциала поверхности и разности между двумя точками с постоянным шагом. Не называйте эту разность фактическим напряжением шага человека без модели контакта с грунтом и дополнительных исходных данных.
Исходные данные
Полусферический электрод радиуса 0.1 м лежит в однородном полупространстве грунта. Внешняя эквипотенциальная полусфера имеет радиус 10 м. Удельное сопротивление грунта 100 Ом м; электрод 100 В, удалённая граница 0 В. Модель осесимметричная.
| Параметр | Значение | Единица | Происхождение |
|---|---|---|---|
| Радиус электрода | 0,1 | м | Принято в учебной постановке |
| Внешний радиус | 10 | м | Принято в учебной постановке |
| Удельное сопротивление грунта | 100 | Ом м | Принято в учебной постановке |
| Потенциал электрода | 100 | В | Принято в учебной постановке |
Порядок воспроизведения
- Скопируйте 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 = {
"type": "axi",
"depth": 1,
"materials": [["Однородный грунт", 0.01, 0.01]],
"terminals": [
["Полусферический электрод", 100, 0, 1],
["Удалённая земля", 0, 0, 1],
],
"blocks": [
[110, -110, "Однородный грунт", 1],
[230, -230, "Однородный грунт", 2],
[530, -530, "Однородный грунт", 3],
[2000, -2000, "Однородный грунт", 4],
],
"block_mesh_multipliers": {"1": 1, "2": 2, "3": 5, "4": 80},
"segments": [[100, 0, 10000, 0], [0, -100, 0, -10000]],
"arcs": [
[0, -100, 100, 0, 90, "Полусферический электрод"],
[0, -200, 200, 0, 90, ""],
[0, -500, 500, 0, 90, ""],
[0, -1000, 1000, 0, 90, ""],
[0, -10000, 10000, 0, 90, "Удалённая земля"],
],
"samples": [[1000, 0], [2000, 0]],
}
for level, mesh in enumerate((8, 4, 2)):
row = current(JOB, mesh, "model-%02d" % (level + 1))
amperes = abs(row["terminals"]["Полусферический электрод"]["I_A"])
row["R_ohm"] = 100 / amperes
row["touch_1m_V"] = 100 - row["samples"][0]["U_V"]
reference = 100 / (2 * math.pi) * (1 / 0.1 - 1 / 10)
check(
"Сопротивление ограниченной полусферической области",
abs(row["R_ohm"] / reference - 1),
0.002,
)
for sample in row["samples"]:
expected = 100 * (1 / (sample["x_mm"] * 0.001) - 1 / 10) / (1 / 0.1 - 1 / 10)
check(
"Потенциал поверхности, сетка %d" % (level + 1),
abs(sample["U_V"] - expected),
0.1,
)
records.append(row)
check(
"Сходимость сопротивления заземлителя",
abs(records[-1]["R_ohm"] / records[-2]["R_ohm"] - 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: 3. Выполнено проверок: 16. Полная точность чисел и условия сравнения доступны в таблицах результатов JSON.
| Сопротивление, Ом | Масштаб сетки, мм | Мощность области, Вт | Напряжение прикосновения на расстоянии 1 м, В | Элементов | Контрольные точки | Электроды | Относительная невязка тока | Относительная невязка мощности | Мощность, Вт | Узлов | Модель |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 157,32895 | 8 | 4 полей; полные данные JSON | 90,919776 | 12359 | 2 точек; полные данные JSON | 2 полей; полные данные JSON | 731,34282 × 10⁻¹² | 80,794227 × 10⁻¹² | 63,561094 | 6403 | model-01.fec |
| 157,46425 | 4 | 4 полей; полные данные JSON | 90,927348 | 21061 | 2 точек; полные данные JSON | 2 полей; полные данные JSON | 766,41053 × 10⁻¹² | 9,991002 × 10⁻¹² | 63,506478 | 10790 | model-02.fec |
| 157,50912 | 2 | 4 полей; полные данные JSON | 90,928142 | 59812 | 2 точек; полные данные JSON | 2 полей; полные данные JSON | 1,6414201 × 10⁻⁹ | 381,93711 × 10⁻¹² | 63,488387 | 30249 | model-03.fec |
| Критерий | Получено | Допуск или ожидаемое значение | Итог |
|---|---|---|---|
| model-01: баланс токов | 731,3 × 10⁻¹² | 1e-06 | Выполнен |
| model-01: баланс мощности | 80,79 × 10⁻¹² | 1e-06 | Выполнен |
| Сопротивление ограниченной полусферической области | 0,001488 | 0,002 | Выполнен |
| Потенциал поверхности, сетка 1 | 0,01069 | 0,1 | Выполнен |
| Потенциал поверхности, сетка 1 | 0,005504 | 0,1 | Выполнен |
| model-02: баланс токов | 766,4 × 10⁻¹² | 1e-06 | Выполнен |
| model-02: баланс мощности | 9,991 × 10⁻¹² | 1e-06 | Выполнен |
| Сопротивление ограниченной полусферической области | 629,2 × 10⁻⁶ | 0,002 | Выполнен |
| Потенциал поверхности, сетка 2 | 0,01826 | 0,1 | Выполнен |
| Потенциал поверхности, сетка 2 | 0,002088 | 0,1 | Выполнен |
| model-03: баланс токов | 1,641 × 10⁻⁹ | 1e-06 | Выполнен |
| model-03: баланс мощности | 381,9 × 10⁻¹² | 1e-06 | Выполнен |
| Сопротивление ограниченной полусферической области | 344,5 × 10⁻⁶ | 0,002 | Выполнен |
| Потенциал поверхности, сетка 3 | 0,01905 | 0,1 | Выполнен |
| Потенциал поверхности, сетка 3 | 0,004427 | 0,1 | Выполнен |
| Сходимость сопротивления заземлителя | 284,9 × 10⁻⁶ | 0,002 | Выполнен |
Допуски относятся к указанной учебной задаче и способу сравнения. Число показанных цифр не означает такой же физической точности.
Модели, решения и программа
Для нового расчёта сохраните программу, модели и необходимые данные в одну рабочую папку, сохраняя структуру подпапок.
- Файл результатов: README.md8531 байт
- Решение электрического тока: model-01.anc618772 байт
- Модель электрического тока: model-01.fec1461 байт
- Решение электрического тока: model-02.anc1056709 байт
- Модель электрического тока: model-02.fec1460 байт
- Решение электрического тока: model-03.anc3104293 байт
- Модель электрического тока: model-03.fec1460 байт
- Программа встроенного Python: reproduce.py11352 байт
- Файл результатов: verification.json913 байт
Границы применимости
- Однородный линейный грунт, идеальная полусфера, конечная внешняя граница. Не моделируются реальный стержень, слоистость, ионизация, импульсный ток, тело человека и нормативная оценка безопасности.
- Проверка относится к данной учебной постановке и решателю FEMM 5.0 06Oct2026. Снимки интерфейса выполняются отдельным согласованным проходом.