박사 과정에서 GMES(GIST Maxwell’s Equations Solver)라는 전자기 시뮬레이터를 만들었습니다. 맥스웰 방정식을 유한차분 시간영역법(FDTD)으로 푸는 프로그램입니다. Python API 아래에 C++, SWIG, Cython을 결합했고, 1·2·3차원 계산과 여러 분산 물질 모델, PML 경계 조건, MPI 병렬 실행을 지원했습니다.
GMES의 설계와 구현은 Computer Physics Communications에 발표했고(Chun et al., 2013a), Drude-Critical Point 분산 물질 모델의 PLRC와 ADE 구현은 Progress In Electromagnetics Research에 발표했다(Chun et al., 2013b). 두 논문 모두 GMES를 개발하고 검증한 학위 과정의 결과물입니다.
소스 코드는 GitHub의 ruddyscent/gmes 저장소에 공개되어 있습니다.
당시에는 눈앞의 연구 문제를 푸는 일이 우선이었습니다. GMES는 그 역할을 해냈지만, 학위를 마친 뒤 개발은 사실상 멈췄습니다. 그사이 Python 2는 수명을 다했고 빌드 도구와 NumPy C API도 크게 달라졌습니다. 이제 저장소는 최신 Python에서 바로 실행되지 않습니다.
최근 이 프로젝트를 다시 꺼냈습니다. 목표는 예전 코드를 그대로 되살리는 데 있지 않습니다. 해석해와 다른 수치기법을 기준 삼아 결과를 검증하고, 나아가 인공지능을 이용한 최적화까지 시험할 수 있는 작은 수치 실험실로 바꾸려 합니다.
계산 코어보다 경계가 먼저 낡았다
소스 전체에는 예상대로 Python 2의 흔적이 짙게 남아 있었습니다. print 문만 200개가 넘고 xrange, iteritems, has_key, 삭제된 np.int도 곳곳에 보입니다. 패키지 내부 import는 Python 2의 암시적 상대 import에 기대고 있으며, 일부 파일은 현재 Python에서 구문 분석조차 되지 않습니다.
빌드 계층은 더 오래됐습니다. setup.py는 이제 표준 라이브러리에서 사라진 Distutils를 직접 사용합니다. Python과 C++ 사이에 놓인 2009년 무렵의 NumPy SWIG typemap은 PyString_Check, PyInt_Check 같은 구형 API와 PyArrayObject 내부 필드에 접근합니다. Cython 코드도 Python 3 의미론에 맞춰 다시 확인해야 합니다.
뜻밖의 희소식도 있었습니다. 순수 C++ 계산 코어는 현대 C++ 컴파일러의 문법 검사를 통과했습니다. 모든 것을 처음부터 다시 쓸 필요는 없습니다. 가장 심하게 낡은 곳은 계산식 자체가 아니라 Python, NumPy, C++이 맞닿는 경계였습니다.
결론은 분명했습니다. GMES에 필요한 것은 여기저기 덧대는 수선이 아니라, 범위를 명확히 정한 현대화 릴리스다.
실행보다 정답을 먼저 지켜야 한다
자동 변환 도구를 쓰면 오래된 Python 코드의 표면적인 오류는 상당수 없앨 수 있습니다. 그러나 과학 계산 코드에서 ‘실행된다’는 것은 출발선에 불과합니다. 더 위험한 실패는 오류 없이 끝까지 실행되면서 답만 조금씩 달라지는 경우입니다.
대표적인 예가 나눗셈입니다. Python 2에서 두 정수의 /는 정수 나눗셈이었지만 Python 3에서는 실수를 반환합니다. GMES의 격자 크기와 MPI 영역 분할 코드에는 이 동작을 전제로 작성한 부분이 있습니다.
self.general_field_size = (
self.whole_field_size / self.cart_comm.topo[0]
)
연산자를 그대로 두면 배열 크기가 실수가 되거나 영역 경계가 예전과 다르게 나뉠 수 있습니다. 그렇다고 /를 //로 바꾸는 것으로 끝나지도 않습니다. 격자가 나누어떨어지지 않을 때 남은 셀을 어느 프로세스에 배분할지까지 테스트로 고정해야 합니다.
iterator와 map의 동작, 배열 dtype, 실수장과 복소수장의 변환, C++과 NumPy 사이의 메모리 소유권도 결과를 소리 없이 바꿀 수 있습니다. 그래서 이번 작업의 첫 원칙을 다음과 같이 정했습니다.
Python 3에서 실행되게 만드는 일과, 계산 결과가 계속 옳다는 것을 확인하는 일을 분리하지 않습니다.
가능하다면 격리된 과거 실행 환경에서 기준 결과를 추출합니다. 어렵다면 해석해나 신뢰할 만한 다른 해석 코드의 결과를 기준으로 삼습니다. 현대화 전후의 그림이 그럴듯하게 닮았다는 정도로는 부족합니다.
코드보다 먼저 수치 안전망을 세운다
기존 저장소에도 단위 테스트는 있지만 대부분 pointwise material 계열에 몰려 있습니다. 전체 FDTD 시간 진행과 좌표 변환, 파원, PML 반사 성능, 파일 입출력, MPI 통신을 직접 검증하는 테스트는 부족합니다. 난수 seed가 없거나 여러 전자기장 성분이 같은 NumPy 배열을 공유하는 테스트도 있습니다. 이런 테스트는 오류를 잡기보다 가릴 수 있습니다.
새 테스트는 작은 격자와 짧은 실행 시간을 기본으로 삼고 모든 입력을 결정론적으로 구성합니다. 필드마다 독립된 배열을 사용하고, 비트 단위의 일치 대신 물리적으로 의미 있는 허용 오차를 둡니다.
smoke test는 말 그대로 ‘연기가 나는지 보는 시험’입니다. 전자 장비에 처음 전원을 넣었을 때 연기가 나지 않으면 최소한의 첫 관문은 통과했다고 보는 데서 나온 표현입니다. 소프트웨어에서도 같은 뜻으로, 세부 기능을 검증하기 전에 프로그램이 아예 실행되지 않는 치명적인 문제부터 빠르게 찾아냅니다.
GMES의 smoke test에서는 설치된 패키지를 import할 수 있는지, 최소한의 시뮬레이션이 오류 없이 끝나는지, 결과에 NaN이나 무한대가 생기지 않는지를 확인합니다. 이 검사를 통과했다고 계산이 정확하다는 뜻은 아닙니다. 빌드나 의존성 변경으로 기본 실행 경로가 망가지지 않았음을 확인했을 뿐입니다. 따라서 smoke test는 코드가 바뀔 때마다 실행하고, 해석해와 물리량을 정밀하게 비교하는 수치 회귀 테스트는 별도로 돌립니다.
처음 마련할 기준 문제는 다음과 같습니다.
| 기준 문제 | 비교할 값 | 기준 |
|---|---|---|
| 진공의 평면파 전파 | 전파 속도, 위상 오차 | 해석해 |
| 두 물질의 평면 경계 | 반사율, 투과율 | Fresnel 식 |
| PEC 공진기 | 공진 주파수 | 고유 모드 해석해 |
| 흡수 경계 | 입사파 대비 잔류 반사 | PML 오차 기준 |
| 분산·손실 물질 | 감쇠율, 위상 지연 | 해석해 또는 주파수영역 풀이 |
| 동일한 산란 문제 | 장 분포, 산란 계수 | FEM·FDFD 등 다른 수치기법 |
| MPI 영역 분할 | 단일 프로세스 결과와의 차이 | 동일 문제의 직렬 실행 |
해석해가 있는 단순 문제는 코드가 물리 법칙을 제대로 구현했는지 가장 명확하게 알려줍니다. 다른 수치기법과의 비교는 해석해가 없는 복잡한 형상에서 유용합니다. 두 종류를 함께 써야 구현 오류와 특정 수치기법의 오차를 구분할 수 있습니다.
하나의 설정이 하나의 실험을 설명하게 한다
예전 GMES의 장점은 Python으로 형상과 물질을 구성할 수 있다는 점이었습니다. 그러나 기능이 늘면서 계산 설정과 물리 모델, 출력 코드가 서로 얽혔습니다. 새 버전에서는 거대한 API 하나를 키우는 대신, 역할이 분명한 작은 구성 요소를 조합하려 합니다.
문제 정의
├── 계산 영역과 격자
├── 물질과 형상
├── 파원과 경계 조건
└── 관측량
실행 설정
├── 시간 간격과 종료 조건
├── 계산 백엔드
├── 병렬화 방법
└── 출력과 체크포인트
사용자는 Python API와 설정 파일 가운데 편한 쪽을 선택할 수 있어야 합니다. 설정 파일은 값을 모아두는 문서에 그치지 않습니다. 실행 전에 타입과 단위를 검증하고, 실제로 사용한 최종 조건을 남기는 실험 기록이어야 합니다.
problem:
dimension: 2
cell_size: 1.0e-3
boundary: pml
source:
type: gaussian-pulse
center_frequency: 10.0e9
solver:
backend: numpy
courant_number: 0.5
output:
probes: [reflection, transmission]
checkpoint_interval: 1000
핵심 FDTD 갱신식은 NumPy·C++·향후 GPU 백엔드와 분리합니다. 같은 문제를 여러 백엔드에서 실행할 수 있으면 성능 비교뿐 아니라 교차 검증도 쉬워집니다. HDF5, MPI, 시각화처럼 모든 사용자에게 필요하지 않은 기능은 선택 의존성으로 떼어냅니다. 쓰이지 않는 오래된 Dia 유틸리티와 GUI까지 억지로 되살릴 이유는 없습니다.
마이그레이션은 다섯 단계로 나눈다
점검에서 드러난 작업을 의존 관계에 따라 다섯 단계로 나눴습니다.
1. 기존 예제 실행과 기준 결과 확보
먼저 저장소에 남아 있는 예제를 하나씩 실행해봅니다. 어떤 예제가 현재 환경에서 동작하는지, 어디서 실패하는지, 실행 결과가 물리적으로 타당한지를 살펴봅니다. 생성되는 필드와 관측값, 출력 파일도 함께 확인해 이후 마이그레이션에서 보존해야 할 동작을 정리합니다.
Python 3.14를 첫 공식 목표로 두고 지원 운영체제와 컴파일러를 정합니다. 과거 환경에서 예제를 실행할 수 있다면 회귀 비교에 쓸 기준 결과를 이때 추출합니다. 그다음 해석해가 있는 작은 기준 문제와 빠른 smoke test를 추가합니다.
2. 빌드 체계와 import 마이그레이션
pyproject.toml 기반 격리 빌드로 옮기고 런타임 의존성과 선택 기능을 명시합니다. 패키지 내부 import, Python 3 문법, 컬렉션 API와 기본 자료형의 차이를 정리합니다. 소스 배포본과 wheel을 실제로 설치해 import까지 확인하는 것이 이 단계의 완료 조건입니다.
3. 네이티브 확장 현대화
Cython 3의 의미론에 맞춰 코드를 고치고 SWIG wrapper를 다시 생성합니다. 오래된 NumPy typemap은 현재 NumPy C API에 맞는 작은 변환 계층으로 교체합니다. 실수·복소수 배열, 연속·비연속 메모리, Python과 C++ 사이의 소유권을 각각 테스트합니다.
4. 수치 기능 이식과 검증
geometry와 좌표 변환, 파원, 분산 물질, PML, 전체 시간 진행을 차례로 검증합니다. 그 뒤에 HDF5와 MPI를 붙이고 단일 프로세스와 다중 프로세스의 결과를 비교합니다. 기능을 한꺼번에 켜기보다 기준 문제를 하나씩 통과시키며 범위를 넓힙니다.
5. 지속 가능한 개발 체계 구축
Linux와 macOS에서 빌드와 테스트를 자동화하고, wheel 설치 검사와 정적 검사를 CI에 넣습니다. README, 변경 기록, 지원 범위와 릴리스 절차도 코드와 함께 갱신합니다. 오래된 프로젝트를 한 번 실행시키는 것이 아니라 이후에도 변경할 수 있는 상태로 만드는 단계입니다.
신뢰할 수 있는 시뮬레이터 위에 최적화를 올린다
현대화의 목적은 과거 기능을 보존하는 데 그치지 않습니다. GMES가 신뢰할 수 있는 순방향 시뮬레이터가 되면, 그 위에서 여러 최적화 문제를 다룰 수 있습니다.
예를 들어 원하는 주파수 응답을 만드는 물질 배치, 특정 위치로 에너지를 모으는 구조, 넓은 대역에서 반사가 작은 형상, 제한된 센서 데이터로 물질의 유전율과 전도도를 추정하는 역문제를 생각할 수 있습니다. 설계 변수와 목적 함수를 설정으로 분리해두면 GMES에 여러 탐색 방법을 붙일 수 있습니다.
처음부터 모든 것을 신경망에 맡길 생각은 없습니다. 변수가 적다면 격자 탐색이나 전통적인 최적화 기법이 훌륭한 기준선입니다. 계산 비용이 큰 블랙박스 문제에는 베이지안 최적화를, 고차원 형상 설계에는 서로게이트 모델이나 미분 가능한 계산을 검토할 수 있습니다. 강화학습은 설계를 순차적인 의사결정으로 표현할 수 있을 때 비로소 후보가 됩니다.
어떤 인공지능 기법을 쓰더라도 판단 기준은 같습니다.
- 해석해 또는 단순한 기준 문제에서 먼저 검증할 것
- 전통적인 최적화 방법보다 실제로 나아지는지 비교할 것
- 학습 데이터 밖에서도 맥스웰 방정식의 제약을 만족하는지 확인할 것
- 좋은 결과뿐 아니라 계산 비용과 실패 조건도 기록할 것
AI는 시뮬레이터의 정확성을 대신 증명하지 않습니다. 잘못된 시뮬레이터를 빠르게 반복하면 그럴듯한 오답만 더 빨리 쌓입니다. 그래서 최적화보다 수치 검증이 먼저입니다.
앞으로 할 일
처음 GMES를 만들 때는 필요한 전자기 문제를 푸는 데 집중했습니다. 이번에는 한 걸음 더 나아가 왜 이 결과를 믿을 수 있는지 설명하는 도구로 만들고 싶습니다. 문제 정의와 계산 엔진을 분리하고, 같은 설정을 여러 계산 백엔드에서 실행하며, 해석해와 다른 수치기법으로 결과를 교차 검증하는 구조입니다.
당장 할 일은 기존 예제를 실행하고 그 결과를 살펴보는 것입니다. 지금도 동작하는 기능과 이미 깨진 기능을 구분하고, 마이그레이션 전후를 비교할 기준 결과를 남깁니다. 그다음 목표는 최신 Python에서 설치된 GMES로 작은 Fresnel 문제를 풀고, 반사율이 이론값과 정해진 오차 안에서 일치하는지 확인하는 것입니다. 이 성공을 발판 삼아 공진기, 분산 물질, PML, MPI 문제로 범위를 넓혀갑니다.
10년 넘게 멈춘 프로젝트를 다시 시작하지만, 목표는 GMES를 이전 상태 그대로 되돌리는 데 있지 않습니다. 박사 과정에서 만든 이 프로그램을 새로운 수치기법과 인공지능 기반 최적화를 안전하게 시험할 수 있는 연구 도구로 만드는 것. 앞으로 그 과정을 하나씩 기록하려 합니다.