Разбор реальных кодов

Восемь программ физики ускорителей: задача, устройство, ключевое решение, что сделано хорошо и что требует доработки

Вячеслав Федоров

Лекция 15

Разработка и применение ПО

в физических исследованиях

diff

Содержание

Восемь программ и общие уроки

01

Огибающая

02

Динамика частиц

03

Коррекция орбиты

04

Установка и двойник

05

ИИ в пультовой

06

Уроки

Цель лекции

Читать чужой рабочий код критически и проверять его результат независимым способом

Курс давал приёмы по отдельности: структура проекта, тесты, CI, типизация, производительность, упаковка, документация, воспроизводимость. В реальной программе они встречаются вместе и в разной степени. Разбор показывает, какие из них окупились, каких не хватило и как недостающее обнаруживается простой проверкой: формулой, сравнением с независимым кодом, повторным запуском.

Восемь

программ

ПрограммаЗадачаФормаРегистрация
KENVогибающая пучкапакет PyPI№ 2024611244
ГА на DEAPподбор полей, обратная задачакод главы—
REDPICдинамика макрочастицпакет PyPI№ 2023688768
Коррекция орбитыSVD и сеть на остаткахкод главы—
CadQueryсхема установки кодомкод главы—
ACCUMULATOR, SCAUTдвойник, оркестрациясервис, библиотека№ 2026668488, № 2025619977
SAVAпомощник оператора33 сервисане оформлена
AI Scientistавтономный исследовательфорк с шаблоном—

01

Задача

физика и постановка

02

Устройство

модули и границы

03

Решение

ключевой алгоритм

04

Курс в коде

тесты, CI, пакет, типы

05

Проверка

что улучшить

Огибающая

KENV: уравнение Капчинского—Владимирского, пакет и проверка результата; генетический алгоритм и обратная задача

01

Огибающая

Динамика частиц

Коррекция орбиты

Установка и двойник

ИИ в пультовой

Уроки

Уравнение

огибающей

P = 2 * self.beam.current / \
    (consts.alfven_current * (g * beta)**3)
dxdz = xp
dxpdz = 2 * P / (x + y) + emitt_x * emitt_x / x**3 \
    - K_x * x - dgdz * xp / (beta * beta * g) \
    - d2gdz2 * x / (2 * beta * beta * g)

Для физика

ВКР: тракт 15 м, 1,5 кА — ASTRA около 4 ч на 10⁶ частиц, UltraSAM трое суток, KENV — секунды при совпадении огибающих.

01

2P/(x + y)

пространственный заряд расталкивает пучок

02

ε²/x³

эмиттанс не даёт сжать пучок в точку

03

K·x

соленоиды и квадруполи фокусируют

04

γ′, γ″

ускорение: адиабатическое затухание

Устройство

KENV 0.3.0.5

Beam

энергия, ток, r, r′, εn

Accelerator

Element × N, compile()
Bz(z), Ez(z), Gz(z)

Equations

envelope_prime(z, X)

Simulation.track()

solve_ivp, RK23 → envelope_x(z)

01

Пакет

PyPI, сайт документации, свидетельство

02

Интерфейс

параметры Beam только по имени, единицы в docstring

03

Скорость

десятки миллисекунд на огибающую

Для физика

Скорость расчёта позволила построить интерфейс настройки проводки пучка в реальном времени: меньше тестовых импульсов на настройку.

Демонстрация:

огибающая KENV

01

Тракт главы: 9 соленоидов, пучок 2 МэВ, 2 кА

02

Все поля 0,02 Тл: огибающая волной

03

0,1 Тл: перетяжка до долей миллиметра

04

0,2 Тл: тот же вызов без исключений

05

pip show kenv: объявленные зависимости

Что наблюдается

0,02 Тл: 54 мс, r max 131,2 мм. При 0,2 Тл r min = −11 895 891,7 мм без исключений; Requires пуст.

root@lab — запись со стенда

KENV:

что улучшить

beam_envelope = solve_ivp(
    equations.envelope_prime,
    t_span=[z_start, z_finish], y0=X0_beam,
    max_step=self.accelerator.dz,
    t_eval=self.accelerator.parameter,
    method=method, rtol=rtol, atol=atol
).y          # статус и r > 0 не проверяются
 
dz = (z_data[-1] - z_data[0]) / len(z_data)
             # шаг профиля: делитель len - 1

01

Результат

статус решателя и r > 0 не проверяются

02

Пакет

setup.py импортирует kenv, зависимостей нет

03

Повторы

15 вызовов interp1d с одними параметрами

04

Состояние

атрибут: список → массив → функция

05

Копия

тот же read_fields в REDPIC

Генетический

алгоритм

creator.create("FitnessMin", base.Fitness, weights=(-1.0,))
creator.create("Individual", list, fitness=creator.FitnessMin)
toolbox.register("attr_float", random.gauss, Sol_B,
                 (Sol_B_max - Sol_B_min) / 2)
toolbox.register("evaluate", evalution_envelope)
toolbox.register("mate", tools.cxUniform, indpb=CX_INDPB)
toolbox.register("mutate", tools.mutGaussian, mu=0,
                 sigma=(Sol_B_max - Sol_B_min) / 2,
                 indpb=MUTPB)
toolbox.register("select", tools.selTournament,
                 tournsize=TOURN)
toolbox.decorate("mate", clip)
toolbox.decorate("mutate", clip)

01

Особь

9 полей соленоидов, 5–50 мТл

02

Приспособленность

средний |r − r_зад| по тракту

03

Отбор

турнир из 3; скрещивание 0,4; мутация 0,6

04

Цена

не больше 100 × 100 = 10⁴ огибающих

Для физика

Градиента нет, расчёт дёшев: 54 мс на стенде, до 10⁴ огибающих — до 9 мин в одном процессе.

Демонстрация:

генетический поиск

01

Код главы: популяция 50, 15 поколений

02

Расчёт особей в двух процессах: Pool.map

03

Два запуска: seed 1 и seed 2

04

Рисунок: огибающие и поля двух запусков

Что наблюдается

Вне коридора 30–37 % начальных генов. Лучшая в последнем поколении 17,1 мм, за прогон — 13,0 мм; поля расходятся до 39,3 мТл.

root@lab — запись со стенда

Результат:

два запуска ГА

Огибающие пучка двух запусков генетического алгоритма, заданная кривая и поля девяти соленоидов

Что наблюдается

От 2,5 до 4,4 м обе огибающие идут по заданной, у выхода первая уходит к 56 мм; невязка 13,0 и 13,9 мм; часть полей прижата к границам 5 и 50 мТл.

Обратная

задача

Начальная популяцияμσ
поля соленоидов0,04 Тл0,02 Тл
радиус r48 мм24 мм
разлёт r′38 мрад20 мрад
эмиттанс εn1150 мм·мрад500 мм·мрад

Для физика

Ток 1,5 кА: r = 42–49 мм, r′ = 35–38 мрад, εn = 1125–1215 мм·мрад — область решений, а не точка.

01

Особь

12 полей и 3 начальных условия

02

Популяция

1500 особей, 100 поколений

03

Код главы

начальная популяция мимо clip, итог — последнее поколение

04

Исправление

границы в генераторе особи, HallOfFame, seed

Важно

Оптимизатор возвращает одну точку; единственность проверяется запусками из разных начальных условий.

Динамика частиц

REDPIC: релятивистская разностная схема на Numba, устройство пакета, окружение и поведение библиотеки

02

Огибающая

Динамика частиц

Коррекция орбиты

Установка и двойник

ИИ в пультовой

Уроки

Разностная

схема

# F = H/γ в безразмерных единицах
b2 = 1 + Fx[i] ** 2 + Fy[i] ** 2 + Fz[i] ** 2
b1 = 2 - b2
b3 = 2 * (vx * Fx[i] + vy * Fy[i] + vz * Fz[i])
fx = 2 * (vy * Fz[i] - vz * Fy[i])
fy = 2 * (vz * Fx[i] - vx * Fz[i])
fz = 2 * (vx * Fy[i] - vy * Fx[i])
vx = (vx * b1 + fx + Fx[i] * b3) / b2

Пояснение

Поле пучка — прямая сумма по парам частиц: O(N²), при 10 000 частиц около 10⁸ пар на каждом шаге.

01

Поле E

в точках частиц: p += 2E за 2δt

02

Сдвиг

v = p/γ, r += v за δt

03

Поле H

в новых точках: поворот v, |v| сохраняется

04

Сдвиг

r += v, затем p = vγ

Устройство

REDPIC 0.11.8

BaseBeam

DataFrame частиц

Генераторы

RadialUniform,
Uniform, Astra

BaseSimulation

track(n_files)

REDSimulation

_first_step_red
_second_step_red

Accelerator

Element, compile()

utils.field

@jit, prange
cuda.jit

вверх — наследование, вправо — вызов полей

01

Пакет

PyPI, зависимости объявлены

02

CI

flake8, pylint, pytest на 3.10–3.12

03

Классы

абстрактные базы, инверсия зависимостей

04

Скорость

Numba: JIT, параллельные циклы, CUDA

Пояснение

Выпуск в GitHub Actions публикует пакет на PyPI и документацию; Dockerfile фиксирует Python 3.10, но не версии библиотек.

Демонстрация:

окружение REDPIC

01

Пример из главы на SciPy 1.18

02

Объявленные и установленные версии

03

Тот же расчёт с заменой функции

04

Рисунок: макрочастицы и край пучка

Что наблюдается

scipy.misc.derivative удалена, а scipy объявлена без верхней границы. С заменой функции 6000 частиц рассчитаны за 4,4 с.

root@lab — запись со стенда

Результат:

запуск REDPIC

Радиусы макрочастиц REDPIC вдоль тракта и край пучка 2σx по слоям

Что наблюдается

Край пучка 2σx растёт от 49 до 111 мм; голова сгустка к концу счёта у 2,43 м, поэтому слои взяты до 2,2 м.

Четыре строки

с последствиями

# redpic/utils/jit.py
def jit(func):
    def wrapper(*args, **kwargs):
        ...
        return numba_jit(...)(func)(*args, **kwargs)
    return wrapper
# redpic/solver/red.py
if bool(beam.total_charge for beam in self.beams):
print("\r%.1f %% " % progress, end="")
# redpic/core/config.py
logging_config.dictConfig(LOGGING)

01

jit

новый диспетчер Numba при каждом вызове

02

bool(генератор)

всегда True: поле пучка и при нулевом токе

03

print

прогресс в stdout без отключения

04

dictConfig

импорт настраивает корневой журнал

Важно

По отдельности строки не меняют результата; вместе они замедляют расчёт и вмешиваются в чужую программу.

Демонстрация:

скорость REDPIC

01

Исходный текст декоратора jit

02

Проверка заряда в трекинге

03

Поле пучка: обёртка против njit

04

Трекинг при нулевом токе

Что наблюдается

300 частиц: 1,36 мс на вызов через обёртку против 0,15 мс. При токе 0 А поле пучка вызвано 180 раз и заняло 1,5 с из 2,0 с.

root@lab — запись со стенда

REDPIC:

что улучшить

01

Окружение

верхние границы версий, CI по расписанию

02

Журнал

NullHandler вместо dictConfig, без print

03

Пакет

find_packages без каталога tests

04

Скорость

диспетчер один раз, any() вместо bool

05

Воспроизводимость

seed у генератора пучка

# setup.py
packages=find_packages(exclude=["tests", "tests.*"])
# redpic/__init__.py
logging.getLogger("redpic").addHandler(
    logging.NullHandler())
# redpic/utils/jit.py
def jit(func):
    return func if cfg.DISABLE_JIT else numba_jit(
        nopython=True, parallel=True, fastmath=True,
        cache=True, nogil=True)(func)
# redpic/solver/red.py
if any(beam.total_charge for beam in self.beams):

Коррекция орбиты

Матрица отклика и SVD с отсечением мод, нейросеть на остатках и повторный запуск кода главы

03

Огибающая

Динамика частиц

Коррекция орбиты

Установка и двойник

ИИ в пультовой

Уроки

Матрица отклика

и SVD

def svd_correction(R, x, n_modes):
    """Коррекция орбиты с обрезанием SVD-спектра."""
    U, s, Vt = np.linalg.svd(R, full_matrices=False)
    s_inv = np.zeros_like(s)
    s_inv[:n_modes] = 1.0 / s[:n_modes]
    return -(Vt.T * s_inv) @ (U.T @ x)

Пояснение

x = Rθ, R = UΣV⊤, θ = −VΣ⁺U⊤x; Rij = √(βiβj)·cos(|φi − φj| − πν) / (2 sin πν).

01

Отклик

R 64 × 48: мониторы на корректоры, ν = 8,42

02

Обращение

псевдообратная матрица через SVD

03

Малые σk

усиливают шум, моды отсекают

04

Компромисс

остаток орбиты против токов

Для физика

Линак инжектора СКИФ: 7 датчиков положения, 6 больших и 8 малых корректоров; матрица измеряется.

Сеть

на остатках

svd_answer = orbits @ svd_matrix.T  # что предложил бы SVD
residuals = -thetas - svd_answer    # чего ему не хватило
net = make_pipeline(
    StandardScaler(),
    MLPRegressor(hidden_layer_sizes=(64, 32), max_iter=300,
                 early_stopping=True, random_state=0),
)
net.fit(orbits, residuals)
 
def hybrid_step(x):
    return svd_matrix @ x + net.predict(x[None, :])[0]

01

Линейная задача

сеть 0,151 мм против 0,028 мм у SVD

02

Насыщение

θmax·tanh(θ/θmax): отклик нелинеен

03

Гибрид

SVD плюс поправка сети на остатке

04

Итерации

решают ту же задачу: вклады разделяются

Важно

Если сеть превзошла SVD на линейной задаче, ошибка в постановке опыта.

Демонстрация:

коррекция орбиты

01

Функция svd_correction главы

02

Кольцо 64 × 48: 10, 30 и 48 мод

03

Сеть, гибрид, 100 испытаний по 6 итераций

04

Рисунок: спектр и итерации

Что наблюдается

10 и 30 мод и сеть дают числа главы; 48 мод — угол 1,1·10¹² мрад. Гибрид выигрывает 28,2 % на первой итерации и проигрывает с третьей.

root@lab — запись со стенда

Результат:

коррекция орбиты

Спектр сингулярных чисел матрицы отклика и СКО орбиты по итерациям для SVD и гибрида

Что наблюдается

Ранг 41: семь сингулярных чисел на уровне шума округления. Гибрид лучше всего на первой и второй итерациях, 0,195 и 0,192 мм; SVD к шестой — 0,112 мм.

Повторный

запуск

ВеличинаПрежняя главаСтенд
СКО до коррекции1,539 мм1,539 мм
30 мод0,028 мм0,028 мм
48 мод: угол1,34·10¹² мрад1,11·10¹² мрад
сеть0,151 мм0,151 мм
до гибрида0,306 мм0,308 мм
1-я итерация+27,1 %+28,2 %
лучшая итерация1-я, 0,191 мм2-я, 0,192 мм
шаг SVD / сети1,2 / 110 мкс0,76 / 87 мкс

Пояснение

Столбец SVD в таблице гибрида зависит только от генератора случайных чисел: 0,306 и 0,308 мм — разные состояния генератора.

Важно

Числа текста порождает напечатанный код: скрипт в CI и свой генератор у каждой функции.

Установка и двойник

CadQuery: геометрия из описания структуры; ACCUMULATOR: двойник по протоколу EPICS; SCAUT: оркестрация экспериментов и ферма двойников

04

Огибающая

Динамика частиц

Коррекция орбиты

Установка и двойник

ИИ в пультовой

Уроки

Схема установки

кодом

def build(lattice):
    assembly, pos, heading = cq.Assembly(), np.zeros(3), 0.0
    for i, (kind, length, param) in enumerate(lattice):
        assembly.add(BUILDERS[kind](length, param),
                     name=f"{kind}{i}",
                     loc=cq.Location(cq.Vector(*pos),
                                     cq.Vector(0, 1, 0),
                                     np.degrees(heading)))
        step, turn = local_step(kind, length, param)
        c, s = np.cos(heading), np.sin(heading)
        pos = pos + np.array([c * step[0] + s * step[2], 0.0,
                              -s * step[0] + c * step[2]])
        heading += turn
    return assembly, pos, heading

01

Данные

LATTICE — тот же файл, что у расчёта

02

Конструкторы

функция на тип, словарь BUILDERS

03

Порядок

поставить, сдвинуть, довернуть

04

Проверка

поворот 90°, габарит накрывает путь

Для физика

Ошибка оси вращения найдена по габариту: Z 1,83 м при конце траектории 2,002 м.

Результат:

схема установки

Сборка CadQuery: дрейфы, фокусирующий и дефокусирующий квадруполи, два поворотных магнита по 45° и ось пучка

Что наблюдается

8 элементов, габарит X 1,32 м и Z 2,10 м, конец траектории X 1,202 м и Z 2,002 м, поворот 90,0°: числа главы получены заново на CadQuery 2.8.

Цифровой

двойник

Клиент

скрипт, CS-Studio, SAVA

EPICS Channel Access

caget, caput по имени PV

ElegantIOC на pcaspy

write, read, масштабы

Elegant

eleput, eleget, файлы SDDS

01

Каналы

из config.lte и таблицы обработчиков

02

116 PV

7 × 8 + 5 × 4 + 5 × 4 + 4 × 2 + 12

03

caput

полный пересчёт Elegant

04

Клиент

не отличает двойник от машины

Для физика

Пучковое время — самый дефицитный ресурс: ошибка скрипта на двойнике обходится перезапуском контейнера.

Граница

единиц

def write(self, reason, value):
    element_info = self.element_map[reason]
    if element_info['read_only']:
        logger.warning(
            f"Attempt to write to read-only monitor {reason}")
        return False
    sim_name = get_sim_name(element_info['name'],
                            element_info['param'])
    scale = element_info.get('scale', 1.0)
    val_to_write = value / scale
    eleput(sim_name, val_to_write)
    self.setParam(reason, value)
    return True

01

Расчёт

K1 — градиент, нормированный на Bρ

02

Пульт

ток источника: k1 = 0,2998·G/p

03

Масштаб

0,1207: K1 = −10,115 → −1,22 А

04

Имена

PV_MAPPING: MG-LA1:QLF1-Iadd:Set

Для физика

На машине тот же канал держит −1,3 А: 6 % при пересчёте по одной точке 6 А ~ 6,3 Тл/м.

Демонстрация:

двойник на caproto

01

Модель устройства на caproto, 127.0.0.1

02

16 каналов из двух секций структуры

03

caget и caput: ток корректора CLX2

04

Пикапы после пересчёта модели

05

Запись в монитор PK3

Что наблюдается

QLF1 = −1,22104 А. После caput 0,05 А: PK3 — 0,271 мм, PK4 — 2,223 мм; запись в PK3 отклонена.

root@lab — запись со стенда

SCAUT:

скан с откатом

try:
    for step_index, combination in enumerate(
            all_combinations * repeat):
        set_motors_values(motor_names, combination, ...)
        meter_data, meter_errors = get_meters_data(...)
        data["steps"].append(step_data)
finally:
    for call in callback:
        call(data)
    if save_original_motor_values:
        set_motors_values(original_motor_values.keys(),
                          original_motor_values.values(), ...)
    if save:
        save_data(path, data)

01

Интерфейс

что сканируем и в каких пределах

02

Скан

планировщик, обработчики, аварии

03

Оборудование

мотор и датчик вместо прибора

04

Коммуникация

EPICS, Tango или расчётная модель

05

Данные

метаданные и версия кода

Важно

В finally — обработчики, возврат моторов и сохранение: и после ошибки, и после прерывания.

Ферма

двойников

INSTANCE_POOL = queue.Queue()
for _p in PREFIXES:            # ACC1:, ACC2:, ... ACC89:
    INSTANCE_POOL.put(_p)
 
def objective(trial: optuna.Trial) -> float:
    prefix = INSTANCE_POOL.get()  # ждём свободный экземпляр
    try:
        values = [trial.suggest_float(p, lo, hi)
                  for p, (lo, hi) in zip(actuator_pvs, bounds)]
        apply_actuators(prefix, actuator_pvs, values,
                        strip=REAL_PREFIX)
        ...
        return float(np.mean(step_losses))
    finally:
        INSTANCE_POOL.put(prefix)  # возвращаем при любом исходе

01

Скан машины

10 корректоров, 17 каналов, 28 шагов

02

Подгонка

26 параметров, Optuna, 89 экземпляров

03

Коррекция

бета-функции на двойнике, ±0,2 А

04

Перенос

снимок, таблица, dry_run=False

05

Проверка

новый скан машины

Демонстрация:

ферма и невязка

01

generate_compose.py --count 90

02

Порты последнего сервиса

03

Проверка диапазона портов

04

Невязка nanmean без одного пикапа

Что наблюдается

У accelerator-90 порты 94064 и 94065; за пределом 65535 — 29 сервисов, с 62-го по 90-й. Без PK5 nanmean даёт 0,053 вместо 0,302.

root@lab — запись со стенда

Двойник:

что улучшить

Сделано хорошо

01

Каналы из структуры

новый элемент — без правки кода

02

Граница единиц

масштабы и имена в конфигурации

03

Перенос на машину

dry_run по умолчанию и снимок

04

Скан

откат моторов в finally

Требует доработки

01

Тесты и CI

в репозитории двойника их нет

02

Окружение

версии не закреплены, Elegant по wget

03

Ошибки чтения

старое значение без тревоги и статуса

04

Конфигурация

правит библиотеку: «FIX in SCAUT!»

05

Порты

генератор не проверяет 65535

ИИ в пультовой

SAVA: помощник оператора и конверт записи; AI Scientist: автономный исследователь и проверка его чисел

05

Огибающая

Динамика частиц

Коррекция орбиты

Установка и двойник

ИИ в пультовой

Уроки

SAVA:

устройство

Telegram, веб, CLI

запрос оператора

Роутер

выбор инструмента

Контроллер

GetPV, SetPV

Консультант

FileSearch, RAG

Секретарь

журнал смены

EPICS, двойник

векторный индекс

PostgreSQL

команды через Kafka

01

Реестр

ToolSpec и AgentSpec — один источник

02

Инструменты

15: десять видны модели

03

Роли

knowledge, observer, operator

04

Состав

33 сервиса: Kafka, PostgreSQL, Redis

Для физика

Сторож порогов отправляет тревогу в обход модели: факт доходит и при недоступном провайдере.

Конверт

записи

@dataclass(frozen=True)
class WriteEnvelope:
    envelope_id: str
    command_id: str
    purpose: str                # «матрица отклика орбиты»
    bounds: tuple[WriteBound, ...]
    max_writes: int
    ttl_s: float = 1800.0
 
    def check(self, pv: str, value: float) -> None:
        b = self.bound_for(pv)
        if b is None:
            raise PolicyError(f"{pv} не входит в состав …")
        if value < b.lo or value > b.hi:
            raise PolicyError(f"{pv}={value:g} вне границ …")

01

Подтверждение

операция целиком, одним «да»

02

Отказ

а не обрезка значения до границы

03

Пределы

лимит записей и срок действия

04

Журнал

каждый вердикт с sha256 политики

Для физика

На двойнике: наибольшее отклонение, 207 мкм на PK5, уменьшено до 77 мкм за три шага.

Демонстрация:

конверт записи

01

Реестр: инструменты, роли, агенты

02

Конверт: CLX2 в ±0,01 А, две записи

03

Шесть попыток записи

04

Тесты конверта без EPICS и модели

Что наблюдается

15 инструментов: 10 видны модели, 3 пишут. Из шести записей прошли две; отказы — граница, состав, чтение, лимит. Тесты прошли.

root@lab — запись со стенда

AI Scientist:

цикл без человека

Идея и план

модель по шаблону

Новизна

Semantic Scholar

Архив идей

оценки 1–10

Шаблон

код SVD, идеи

aider

правит код

Прогон run_i

числа в notes.txt

Каркас LaTeX

по заметкам

Статья

pdflatex, chktex

Рецензия

5 отзывов и мета

01

Шаблон

orbit: VEPP-5, 16 мониторов, ±7 А

02

Цикл

до 4 неудач подряд, до 10 прогонов

03

Ошибка

1500 знаков stderr — обратно модели

04

Итог

статья и рецензия: 3 из 10, Reject

Демонстрация:

артефакты прогона

01

Архив идей: затравочные и модели

02

Прогоны run_0 … run_10

03

seed в обучении и в тесте

04

Утверждения против чисел

05

Цена по журналу aider

Что наблюдается

Интересность 8–10 у всех десяти идей, у пяти идей модели — 9–10. В каждом прогоне 9 различных seed на 100 расстроек, те же в обучении. Три утверждения и рецензия неверны; статья — $0,85.

root@lab — запись со стенда

ИИ-системы:

что улучшить

SAVAAI Scientist
Сделано хорошо67 файлов тестов; ruff, mypy и сборка образов в CI; 18 ADR; политика данными с sha256каталог на прогон: код, числа и переписка рядом; Dockerfile для изоляции прогона
Требует доработки33 сервиса на одну машину; модульные тесты импортируют код агентовдевять seed на обучение и тест; заметки противоречат числам; рецензент ошибается
Урокзапись в оборудование — свойство системычисло модели проверяет скрипт

Важно

Модель пишет код и выводы быстрее человека, а проверяет их хуже: проверка остаётся на тестах и на исследователе.

Уроки

Сводка по восьми программам, ошибки, общие для многих из них, и итоговый проект курса

06

Огибающая

Динамика частиц

Коррекция орбиты

Установка и двойник

ИИ в пультовой

Уроки

Сводка

по программам

ПрограммаТестыCIПакетТипыВоспроизводимость
KENVнетнетнет зависимостейnp.arange как типрешатель без проверки
ГА главынетнет—нетseed не задан
REDPICфункциональныеестьлишний testsnp.array как типверсии без границ
Орбитапротив SVDнет—нетseed 42, иной прогон
CadQueryassertнет—нетчисла совпали
ACCUMULATORнетнетDockerнетверсии без границ
SAVA67 файловестьComposemypyобразы по тегам релиза
AI Scientistнетнетбез версийнеткаталог на прогон

Демонстрация:

пакеты и журнал

01

Что пакеты объявляют о себе

02

Журнал до и после import redpic

03

Чей пакет tests в окружении

04

Тесты SAVA рядом с REDPIC

Что наблюдается

kenv: 0 зависимостей; redpic кладёт tests в site-packages. После import redpic чужое предупреждение — в формате REDPIC; тесты SAVA не находятся.

root@lab — запись со стенда

Общие

уроки

01

Физика проверяется частным случаем с известным ответом: поворот 90°, SVD на линейной задаче.

02

Результат решателя проверяется: статус, физическая допустимость, NaN и отказы каналов.

03

Окружение закрепляется и проверяется на новых версиях зависимостей в CI.

04

Библиотека не меняет чужого: корневой журнал, пакеты верхнего уровня, общий генератор.

05

Ограничения задаются в алгоритме: коридор полей, пределы токов; запись охраняет конверт.

06

Числа текста порождает код из репозитория, числа модели проверяет скрипт.

Итоговый

проект

ЭтапСрокРезультат
Формулировкапоследняя неделя ноябрядокумент: задача, путь решения, этапы
Базавторая неделя декабрябазовые требования
Расширениезачётная неделядополнительные требования

Пояснение

Команда из двух-трёх человек, задача связана с физикой, Python; всё — в системе контроля версий.

01

Сборка

без IDE, контейнер приветствуется

02

Тесты

формулы частных случаев и сверка

03

Документация

программного интерфейса и примеры

04

Проверка

пять вопросов лекции к своему коду

Итоги

лекции

01

Восемь программ решают реальные задачи, и каждая требует доработки.

02

Ошибки находят простые проверки: формула, вторая программа, повторный запуск.

03

KENV не проверяет результат решателя; у KENV и REDPIC окружение не закреплено.

04

Двойник и SCAUT: границы слоёв и единиц удачны, тестов и закреплённых версий нет.

05

SAVA защищает запись конвертом; числа AI Scientist проверяет человек или скрипт.

Упражнения

и задание

01

CI на новых версиях

задача по расписанию с новейшими версиями

02

Пакет REDPIC

NullHandler, find_packages, any()

03

Элита и зерно

HallOfFame и seed в ГА главы

04

Ревью

пять вопросов к программе своей лаборатории

Пояснение

Итоговый проект: те же пять вопросов к собственному коду перед каждым этапом.

Для физика

Вывод о программе подкрепляется запуском, а не только чтением кода.

Источники

1

Книга курса: восемь глав части «Разбор реальных кодов» и «Итоговый проект»

phys-dev.github.io/soft-dev-book

2

Федоров В. В. Создание программного комплекса для расчёта динамики частиц во внешних электромагнитных полях : ВКР бакалавра. – НГУ; Создание программного комплекса для оркестрации ускорительных экспериментов : ВКР магистра. – НГУ

3

Свидетельства о регистрации программ для ЭВМ: REDPIC № 2023688768, KENV № 2024611244, SCAUT № 2025619977, ACCUMULATOR № 2026668488

4

KENV и REDPIC: исходный код и пакеты

github.com/isyspac/kenv, github.com/isyspac/redpic

5

Lu C. et al. The AI Scientist: towards fully automated open-ended scientific discovery. – 2024; форк с шаблоном коррекции орбиты

arxiv.org/abs/2408.06292, github.com/fuodorov/ai-scientist

6

DEAP, caproto, CadQuery: документация

deap.readthedocs.io, caproto.github.io/caproto, cadquery.readthedocs.io

Спасибо за внимание

Вопросы

phys-dev.github.io/soft-dev-book

TODO