FEMM 5

Электростатика · Осесимметричная

Ёмкостный уровнемер: жидкость и воздух в одном датчике

Измените уровень жидкости в коаксиальном датчике и получите его градуировочную характеристику C(h). Покажите различие между движением электродов и движением границы диэлектриков.

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

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

Принятые допущения и их назначение
Учебная модель FEMM 5Причина
Интерфейс делит диэлектрик по высотеИзменяется заполнение датчика при неизменных электродах.
Полная осесимметричная постановкаЗаряд и энергия относятся ко всему круговому датчику.

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

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

Две параллельные части цилиндрического конденсатора

Оба участка датчика имеют одну и ту же разность потенциалов между внутренним и наружным электродами. Поэтому часть, заполненная жидкостью, и часть с воздухом соединены электрически параллельно: их заряды и ёмкости складываются.

Плоская граница жидкости перпендикулярна оси z, а идеальное поле направлено радиально и лежит в плоскости интерфейса. Поэтому тангенциальное E непрерывно; различие ε меняет заряд на соответствующей части электродов.

C(h)=\frac{2\pi\varepsilon_0}{\ln(b/a)}\left[\varepsilon_{\mathrm{ж}}h+\varepsilon_{\mathrm{в}}(L-h)\right]
\frac{dC}{dh}=\frac{2\pi\varepsilon_0(\varepsilon_{\mathrm{ж}}-\varepsilon_{\mathrm{в}})}{\ln(b/a)}

Девять полевых состояний

Для уровней 10, 20 и 30 мм выполняются три сетки. Каждая ёмкость сравнивается с точным суммированием участков; чувствительность определяется разностью результатов крайних уровней самой мелкой сетки.

Электроды на всей высоте идеальны, мениск отсутствует, торцы имеют естественные условия Dn=0. Такой эталон проверяет сетку, осесимметричный объёмный множитель и механизм изменения ёмкости. Для реального датчика отдельно добавляют мениск, проводимость жидкости и паразитные ёмкости.

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

Осесимметричная

Величины, единицы и происхождение параметров
ПараметрЗначениеЕдиницаПроисхождение
Радиусы электродов5; 10ммУсловие учебной задачи
Полная высота L40ммУсловие учебной задачи
Уровни h10; 20; 30ммУсловие учебной задачи
Проницаемость жидкости41Условие учебной задачи
Проницаемость воздуха11Условие учебной задачи
Напряжение1ВУсловие учебной задачи

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

  1. Изучите размеры, материалы, возбуждение и выбранную двумерную постановку. Сначала вычислите аналитический ориентир в единицах СИ.
  2. Скачайте файлы по отдельным ссылкам в отдельную рабочую папку. Откройте reproduce.py во встроенном редакторе Python FEMM 5 и выполните сохранённый файл.
  3. Программа строит русифицированные модели, сохраняет их, закрывает и открывает заново. Затем выполняет расчёт и читает свойства проводников по точному русскому имени.
  4. Сравните три уровня пространственной сетки. В таблицах сохранены число узлов и элементов, интегральные величины и независимые критерии. Дополнительные режимы указаны в постановке.
  5. Откройте самый подробный сохранённый вход и соответствующее решение. Проверьте смысл единиц, знаков и объёмного множителя; новый запуск сохранит результаты в новой папке calculated-NN.

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

Программа встроенного Python FEMM 5
Python · reproduce.py
"""Коаксиальный уровнемер: движение границы диэлектриков.
Среда: встроенный MicroPython FEMM 5; команды FEMM доступны глобально.
Геометрические размеры: миллиметры; поля и интегралы возвращаются в СИ.
Все материалы и проводники имеют русские имена. Каждая модель сохраняется,
закрывается и повторно открывается до нового расчёта, включая кириллицу.
Запуск создаёт новую папку calculated-NN рядом с программой; опубликованные
модели и предыдущие расчёты не перезаписываются. Все необходимые
исходные данные включены. Условия задачи и ограничения описаны в README.md.
"""
import os
import sys
import math
import json

saved = globals().get('__file__', '') or (sys.argv[0] if sys.argv else '')
base = os.path.dirname(os.path.abspath(saved)) if saved else os.getcwd()
index = 1
while os.path.exists(base + '/calculated-%02d' % index):
    index += 1
directory = base + '/calculated-%02d' % index
os.mkdir(directory)
records = []
checks = []
EPS0 = 8.85418781762e-12


def real(value):
    if isinstance(value, (list, tuple)):
        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 label(x, y, material, mesh, group=1):
    ei_addblocklabel(x, y)
    ei_selectlabel(x, y)
    ei_setblockprop(material, 0, mesh, group)
    ei_clearselected()


def circle(x, radius, conductor, angle):
    ei_addnode(x - radius, 0)
    ei_addnode(x + radius, 0)
    ei_addarc(x - radius, 0, x + radius, 0, 180, angle)
    ei_addarc(x + radius, 0, x - radius, 0, 180, angle)
    ei_selectarcsegment(x, radius)
    ei_selectarcsegment(x, -radius)
    ei_setarcsegmentprop(angle, '<None>', 0, 0, conductor)
    ei_clearselected()


def solve(model):
    ei_saveas(model)
    ei_close()
    opendocument(model)
    ei_analyze(1)
    ei_loadsolution()
    eo_smooth('off')


def electric_result(mesh, model, input_name, output_name, exact_C):
    voltage_in, charge_in = eo_getconductorproperties(input_name)
    voltage_out, charge_out = eo_getconductorproperties(output_name)
    voltage = real(voltage_in) - real(voltage_out)
    charge = real(charge_in)
    eo_groupselectblock(1)
    energy = real(eo_blockintegral(0))
    eo_clearblock()
    capacitance = charge / voltage
    energy_C = 2 * energy / voltage ** 2
    row = {'mesh_mm': mesh, 'model': os.path.basename(model),
           'nodes': eo_numnodes(), 'elements': eo_numelements(),
           'voltage_V': voltage, 'charge_C': charge, 'energy_J': energy,
           'capacitance_F': capacitance, 'energy_capacitance_F': energy_C,
           'analytic_capacitance_F': exact_C,
           'analytic_C_relative_error': abs(capacitance / exact_C - 1),
           'energy_charge_relative_error': abs(energy_C / capacitance - 1)}
    records.append(row)
    check('Энергия и заряд: ' + os.path.basename(model),
          row['energy_charge_relative_error'], 1e-5)
    return row


def circle_xy(cx, cy, radius, conductor, angle):
    ei_addnode(cx - radius, cy)
    ei_addnode(cx + radius, cy)
    ei_addarc(cx - radius, cy, cx + radius, cy, 180, angle)
    ei_addarc(cx + radius, cy, cx - radius, cy, 180, angle)
    ei_selectarcsegment(cx, cy + radius)
    ei_selectarcsegment(cx, cy - radius)
    ei_setarcsegmentprop(angle, '<None>', 0, 0, conductor)
    ei_clearselected()


def polygon(points, conductor='<None>', mesh=0):
    for x, y in points:
        ei_addnode(x, y)
    for k, (x, y) in enumerate(points):
        x2, y2 = points[(k + 1) % len(points)]
        ei_addsegment(x, y, x2, y2)
        if conductor != '<None>':
            ei_selectsegment((x + x2) / 2, (y + y2) / 2)
            ei_setsegmentprop('<None>', mesh, 0, 0, 0, conductor)
            ei_clearselected()


def terminal(x, y, name, mesh):
    ei_selectsegment(x, y)
    ei_setsegmentprop('<None>', mesh, 0, 0, 0, name)
    ei_clearselected()


def summary(mesh, model, source, ground, exact_C=None):
    v1, q1 = eo_getconductorproperties(source)
    v0, q0 = eo_getconductorproperties(ground)
    v1, v0, q1, q0 = real(v1), real(v0), real(q1), real(q0)
    voltage = v1 - v0
    eo_groupselectblock(1)
    energy = real(eo_blockintegral(0))
    eo_clearblock()
    capacitance = q1 / voltage
    row = {'mesh_mm': mesh, 'model': os.path.basename(model),
           'nodes': eo_numnodes(), 'elements': eo_numelements(),
           'voltage_V': voltage, 'charge_C': q1, 'energy_J': energy,
           'capacitance_F': capacitance,
           'energy_capacitance_F': 2 * energy / voltage ** 2}
    if exact_C is not None:
        row['analytic_capacitance_F'] = exact_C
        row['analytic_C_relative_error'] = abs(capacitance / exact_C - 1)
    records.append(row)
    check('Энергия и терминальный заряд: ' + os.path.basename(model),
          abs(2 * energy / voltage ** 2 / capacitance - 1), 1e-5)
    return row


def field(x, y):
    values = eo_getpointvalues(x, y)
    return math.sqrt(real(values[3]) ** 2 + real(values[4]) ** 2)


def rectangle_field(mesh, filename, epsx=2, epsy=2, voltage=100, width=10, height=10, depth=100):
    newdocument(1)
    ei_probdef('millimeters', 'planar', 1e-10, depth, 30)
    ei_addmaterial('Диэлектрик учебного образца', epsx, epsy, 0)
    ei_addconductorprop('Питающий электрод', voltage, 0, 1)
    ei_addconductorprop('Заземлённый электрод', 0, 0, 1)
    polygon(((0, 0), (width, 0), (width, height), (0, height)))
    terminal(0, height / 2, 'Питающий электрод', mesh)
    terminal(width, height / 2, 'Заземлённый электрод', mesh)
    label(width / 2, height / 2, 'Диэлектрик учебного образца', mesh)
    model = directory + '/' + filename + '.fee'
    solve(model)
    return model


for height in (10, 20, 30):
    for level, mesh in enumerate((1, 0.5, 0.25)):
        newdocument(1)
        ei_probdef('millimeters', 'axi', 1e-10, 1, 30)
        ei_addmaterial('Жидкость с проницаемостью 4', 4, 4, 0)
        ei_addmaterial('Воздух над жидкостью', 1, 1, 0)
        ei_addconductorprop('Внутренний электрод уровнемера', 1, 0, 1)
        ei_addconductorprop('Наружный электрод уровнемера', 0, 0, 1)
        polygon(((5, 0), (10, 0), (10, height), (10, 40), (5, 40), (5, height)))
        ei_addsegment(5, height, 10, height)
        for z in (height / 2, (height + 40) / 2):
            terminal(5, z, 'Внутренний электрод уровнемера', mesh)
            terminal(10, z, 'Наружный электрод уровнемера', mesh)
        label(7.5, height / 2, 'Жидкость с проницаемостью 4', mesh)
        label(7.5, (height + 40) / 2, 'Воздух над жидкостью', mesh)
        model = directory + '/level-%02d-%02d.fee' % (height, level + 1)
        solve(model)
        exact = 2 * math.pi * EPS0 / math.log(2) * (4 * height + 40 - height) * 1e-3
        row = summary(mesh, model, 'Внутренний электрод уровнемера', 'Наружный электрод уровнемера', exact)
        row['liquid_height_mm'] = height
        check('Ёмкость параллельных по оси участков', row['analytic_C_relative_error'], 0.005)
        eo_close()
        ei_close()
slope = (records[8]['capacitance_F'] - records[2]['capacitance_F']) / 0.02
expected = 2 * math.pi * EPS0 * (4 - 1) / math.log(2)
check('Чувствительность к уровню жидкости', abs(slope / expected - 1), 0.005)

columns = {
    'mesh_mm': 'Размер сетки, мм', 'model': 'Файл модели',
    'nodes': 'Узлов', 'elements': 'Элементов',
    'voltage_V': 'Разность потенциалов, В', 'charge_C': 'Заряд, Кл',
    'energy_J': 'Энергия, Дж', 'capacitance_F': 'Ёмкость по заряду, Ф',
    'energy_capacitance_F': 'Ёмкость по энергии, Ф',
    'analytic_capacitance_F': 'Аналитическая ёмкость, Ф',
    'analytic_C_relative_error': 'Относительная ошибка ёмкости',
    'energy_charge_relative_error': 'Невязка энергии и заряда',
    'E_7p5mm_V_m': 'Поле при r=7,5 мм, В/м',
    'analytic_E_7p5mm_V_m': 'Аналитическое поле при r=7,5 мм, В/м',
    'E_150mm_V_m': 'Поле при r=150 мм, В/м',
    'analytic_E_150mm_V_m': 'Аналитическое поле при r=150 мм, В/м',
    'outer_radius_mm': 'Радиус внешней границы, мм',
    'insulation': 'Изоляция', 'analytic_Emax_kV_mm': 'Аналитический максимум поля, кВ/мм',
    'Emax_from_charge_kV_mm': 'Максимум поля по заряду, кВ/мм',
    'E_10mm_kV_mm': 'Поле при r=10 мм, кВ/мм',
    'E_25mm_kV_mm': 'Поле при r=25 мм, кВ/мм',
    'conductivity_S_m': 'Эквивалентная проводимость, См/м',
    'I_real_A': 'Активная компонента амплитуды тока, А',
    'I_imag_A': 'Квадратурная компонента амплитуды тока, А',
    'analytic_I_real_A': 'Аналитическая активная компонента, А',
    'analytic_I_imag_A': 'Аналитическая квадратурная компонента, А',
    'losses_W': 'Диэлектрические потери, Вт',
    'analytic_losses_W': 'Аналитические потери, Вт',
    'terminal_power_W': 'Средняя мощность электрода, Вт',
    'apparent_power_VA': 'Полная мощность, ВА',
    'U_RMS_V': 'Действующее напряжение, В', 'U_amplitude_V': 'Амплитуда напряжения, В'
}
columns.update({'E_tip_sample_V_m': 'Поле около закруглённого конца, В/м', 'local_mesh_mm': 'Сетка около иглы, мм', 'substrate_epsr': 'Проницаемость подложки', 'effective_epsr': 'Эффективная проницаемость', 'Z0_ohm': 'Квази-ТЕМ сопротивление, Ом', 'liquid_height_mm': 'Уровень жидкости, мм', 'offset_mm': 'Эксцентриситет, мм'})
result = {'runtime': 'FEMM 5.0; встроенный MicroPython', 'records': records,
          'checks': checks, 'columns': columns,
          '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'], 'Не выполнен численный критерий'

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

Потенциал, В: level-30-03.res
Визуализация сохранённого решения FEMM 5 level-30-03.res. Электрический потенциал по сохранённым узловым значениям. Показана расчётная область. Контуры деталей взяты из той же модели. Области вне сохранённых треугольников не заполняются. Это научный график сохранённых данных. Нажмите на карту, чтобы открыть её отдельно и увеличить.

Окна FEMM 5

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

Рабочая область модели «Ёмкостный уровнемер: жидкость и воздух в одном датчике». Нативный препроцессор FEMM 5.
Рабочая область модели «Ёмкостный уровнемер: жидкость и воздух в одном датчике». Нативный препроцессор FEMM 5. Открыть в полном размере
Сохранённое решение «Ёмкостный уровнемер: жидкость и воздух в одном датчике». Электрический потенциал V, В, в осесимметричном ёмкостном уровнемере с частичным заполнением.
Сохранённое решение «Ёмкостный уровнемер: жидкость и воздух в одном датчике». Электрический потенциал V, В, в осесимметричном ёмкостном уровнемере с частичным заполнением. Открыть в полном размере

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

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

Результаты расчётов и производные итоги
Уровень жидкости, ммРазмер сетки, ммРазность потенциалов, ВЗаряд, КлЭлементовАналитическая ёмкость, ФФайл моделиЁмкость по энергии, ФЁмкость по заряду, ФОтносительная ошибка ёмкостиЭнергия, ДжУзлов
10115,6202081 × 10⁻¹²24595,6182515 × 10⁻¹²level-10-01.fee5,6202081 × 10⁻¹²5,6202081 × 10⁻¹²348,26369 × 10⁻⁶2,8101041 × 10⁻¹²1346
100,515,6200631 × 10⁻¹²26675,6182515 × 10⁻¹²level-10-02.fee5,6200631 × 10⁻¹²5,6200631 × 10⁻¹²322,45396 × 10⁻⁶2,8100316 × 10⁻¹²1475
100,2515,6188239 × 10⁻¹²80705,6182515 × 10⁻¹²level-10-03.fee5,6188239 × 10⁻¹²5,6188239 × 10⁻¹²101,87947 × 10⁻⁶2,8094119 × 10⁻¹²4283
20118,029052 × 10⁻¹²24408,0260736 × 10⁻¹²level-20-01.fee8,029052 × 10⁻¹²8,029052 × 10⁻¹²371,0892 × 10⁻⁶4,014526 × 10⁻¹²1344
200,518,0287862 × 10⁻¹²25648,0260736 × 10⁻¹²level-20-02.fee8,0287862 × 10⁻¹²8,0287862 × 10⁻¹²337,97294 × 10⁻⁶4,0143931 × 10⁻¹²1429
200,2518,0268893 × 10⁻¹²80078,0260736 × 10⁻¹²level-20-03.fee8,0268893 × 10⁻¹²8,0268893 × 10⁻¹²101,6362 × 10⁻⁶4,0134447 × 10⁻¹²4270
301110,437678 × 10⁻¹²245010,433896 × 10⁻¹²level-30-01.fee10,437678 × 10⁻¹²10,437678 × 10⁻¹²362,53846 × 10⁻⁶5,2188392 × 10⁻¹²1345
300,5110,437446 × 10⁻¹²264710,433896 × 10⁻¹²level-30-02.fee10,437446 × 10⁻¹²10,437446 × 10⁻¹²340,31358 × 10⁻⁶5,2187232 × 10⁻¹²1462
300,25110,434991 × 10⁻¹²808310,433896 × 10⁻¹²level-30-03.fee10,434991 × 10⁻¹²10,434991 × 10⁻¹²104,94537 × 10⁻⁶5,2174953 × 10⁻¹²4294
Проверки опубликованного расчёта
КритерийПолученоДопуск или ожидаемое значениеИтог
Энергия и терминальный заряд: level-10-01.fee8,244 × 10⁻¹²1e-05Выполнен
Ёмкость параллельных по оси участков348,3 × 10⁻⁶0,005Выполнен
Энергия и терминальный заряд: level-10-02.fee385,4 × 10⁻¹²1e-05Выполнен
Ёмкость параллельных по оси участков322,5 × 10⁻⁶0,005Выполнен
Энергия и терминальный заряд: level-10-03.fee134,9 × 10⁻¹²1e-05Выполнен
Ёмкость параллельных по оси участков101,9 × 10⁻⁶0,005Выполнен
Энергия и терминальный заряд: level-20-01.fee1,797 × 10⁻¹²1e-05Выполнен
Ёмкость параллельных по оси участков371,1 × 10⁻⁶0,005Выполнен
Энергия и терминальный заряд: level-20-02.fee7,789 × 10⁻¹²1e-05Выполнен
Ёмкость параллельных по оси участков338 × 10⁻⁶0,005Выполнен
Энергия и терминальный заряд: level-20-03.fee77,46 × 10⁻¹²1e-05Выполнен
Ёмкость параллельных по оси участков101,6 × 10⁻⁶0,005Выполнен
Энергия и терминальный заряд: level-30-01.fee724,1 × 10⁻¹⁵1e-05Выполнен
Ёмкость параллельных по оси участков362,5 × 10⁻⁶0,005Выполнен
Энергия и терминальный заряд: level-30-02.fee347,2 × 10⁻¹²1e-05Выполнен
Ёмкость параллельных по оси участков340,3 × 10⁻⁶0,005Выполнен
Энергия и терминальный заряд: level-30-03.fee70,16 × 10⁻¹²1e-05Выполнен
Ёмкость параллельных по оси участков104,9 × 10⁻⁶0,005Выполнен
Чувствительность к уровню жидкости108,5 × 10⁻⁶0,005Выполнен

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

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

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

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

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