격자를 자른다는 것 (FDM)
3부를 시작하며
2부에서 계속 “복잡함을 아래로 내린다”고 말했다. 3부는 그 아래로 내려가는 이야기다.
내려가 보면 무엇이 있나. 격자를 자르고, 방정식을 대수적인 식으로 바꾸고, 수없이 반복 계산하고, 답이 믿을 만한지 확인하는 과정이 있다.
이 과정을 아는 것이 왜 중요한가. 무엇을 포기했는지 알기 위해서다. 우리는 폰에서 돌리기 위해 여러 타협을 했다. 그 타협이 어디에 영향을 주는지 알려면 계산의 구조를 알아야 한다.
미분방정식이 말하는 것
1편에서 열전달이 미분방정식으로 표현된다고 했다. 그 방정식이 실제로 무엇을 말하는지 보자.
수식 없이 문장으로 옮기면 이렇다.
어떤 지점의 온도가 변하는 속도는, 그 지점이 주변보다 얼마나 차가운지에 비례한다.
조금 풀어보자. 어떤 점이 주변보다 차갑다면 열이 그 점으로 흘러들어온다. 그래서 온도가 오른다. 반대로 주변보다 뜨겁다면 열이 빠져나가고 온도가 내려간다.
주변보다 얼마나 차가운가를 정확히 표현하려면 미분이 필요하다. 그런데 개념은 단순하다. 이웃과 비교하는 것이다.
여기서 자연스러운 발상이 나온다. 이웃과 비교하는 것이라면, 이웃을 정하면 계산할 수 있지 않을까.
유한차분법: 이웃을 정한다
FDM(Finite Difference Method, 유한차분법)은 정확히 그렇게 한다.
먼저 공간을 격자로 나눈다. 예를 들어 판을 가로 100칸, 세로 100칸으로 자른다. 그러면 격자점이 만 개 생긴다. 각 점에는 온도값 하나가 붙는다.
그다음 각 점에서 이렇게 계산한다.
이 점의 온도 변화 = (위 + 아래 + 왼쪽 + 오른쪽 이웃의 평균 온도 − 이 점의 온도) × 어떤 계수
이웃 넷의 평균보다 내가 차가우면 온도가 오른다. 미분방정식이 말한 것과 같은 이야기인데, 이제 사칙연산으로만 되어 있다.
계수에는 재료의 열전도율과 격자 간격과 시간 간격이 들어간다. 이 부분이 물리와 수치의 연결점이다.
시간을 앞으로 밀기
공간은 격자로 나눴다. 시간도 나눈다.
아주 짧은 시간 — 예를 들어 0.001초 — 동안 각 점의 온도가 얼마나 변하는지 위 식으로 계산한다. 그리고 모든 점의 온도를 한꺼번에 갱신한다. 그게 한 걸음이다.
그 다음 걸음에서 같은 일을 반복한다. 새로 갱신된 값을 기준으로 다시 이웃과 비교하고, 또 갱신한다.
이 걸음을 수십만 번 반복하면 시간에 따른 온도 변화가 나온다. 1편에서 “100만 × 60만”이라고 했던 그 숫자가 여기서 나온다.
그래서 이 방법은 개념적으로는 대단히 단순하다. 이웃과 비교해서 조금 고치기를 아주 많이 반복하는 것. 어려운 것은 원리가 아니라 규모다.
그런데 마음대로 자를 수 없다
여기서 중요한 제약이 하나 나온다. 격자 간격과 시간 간격을 독립적으로 정할 수 없다.
직관적으로 보면 이렇다. 한 걸음 동안 열은 대략 이웃 칸까지만 퍼져야 한다. 만약 시간 간격을 너무 크게 잡으면, 그 시간 동안 열이 두 칸 세 칸을 건너뛸 만큼 퍼져야 하는데, 계산식은 이웃 하나만 보고 있다.
그러면 계산이 터진다. 온도가 진동하며 커지다가 결국 무한대가 된다. 물리적으로 말이 안 되는 값이 나온다.
이걸 막는 조건이 있다. 격자를 절반으로 잘게 나누면, 시간 간격은 네 배로 줄여야 한다.
이 관계가 계산량 폭발의 진짜 원인이다.
격자를 절반으로 나누면 2차원에서 점의 수는 네 배가 된다. 여기에 시간 단계가 네 배 늘어난다. 곱하면 열여섯 배다.
| 격자 세밀도 | 점의 수 | 시간 단계 | 총 계산량 |
|---|---|---|---|
| 기준 | 1 | 1 | 1 |
| 2배 촘촘 | 4 | 4 | 16 |
| 4배 촘촘 | 16 | 16 | 256 |
정확도를 조금 올리려면 계산량이 훨씬 많이 늘어난다. 이게 폰에서 돌릴 때 가장 아프게 부딪히는 벽이다.
(참고로 이 제약을 우회하는 방법이 있다. 시간을 앞으로 미는 대신 연립방정식을 풀어 다음 시점을 한꺼번에 구하는 방식인데, 계산 한 걸음이 무거워지는 대신 큰 시간 간격을 쓸 수 있다. 어느 쪽이 유리한지는 문제에 따라 다르다.)
격자를 자를 때의 판단
여기서 9편의 결정 — 격자 설정을 사용자에게 노출하지 않는다 — 가 왜 나왔는지 보인다.
격자를 정하는 일에는 여러 판단이 얽혀 있다.
얼마나 촘촘해야 하나. 온도가 급하게 변하는 곳은 촘촘해야 한다. 완만한 곳은 성겨도 된다. 그런데 어디가 급한지는 계산해 보기 전에는 모른다.
균일하게 자를까. 관심 있는 부분만 촘촘하게 하면 계산량을 아낄 수 있다. 대신 격자 간격이 갑자기 변하는 곳에서 오차가 생긴다.
형상의 경계를 어떻게 맞출까. 격자는 사각형인데 형상은 곡선일 수 있다. 그러면 경계가 계단 모양이 된다.
이런 판단은 전문 지식이다. 사용자에게 맡기면 대개 틀린다. 그래서 시스템이 정한다. 대신 9편에서 말한 대로 틀릴 책임을 시스템이 진다.
그 책임을 어떻게 지는가. 답은 검증이다. 격자를 바꿔가며 답이 안정되는지 확인하는 것. 18편과 20편에서 다룬다.
우리가 여기서 포기한 것
FDM을 쓰기로 한 결정에는 대가가 있다.
형상의 자유가 없다. FDM은 격자가 규칙적일 때 가장 잘 작동한다. 사각형이나 원 같은 단순한 형상에는 좋고, 복잡한 곡면에는 불리하다.
9편에서 “임의 형상을 받지 않는다”고 한 결정이 여기서 나온 것이다. 순서가 반대로 보일 수 있지만, 실제로는 폰에서 돌릴 수 있는 방법을 먼저 정하고 그것에 맞춰 다룰 범위를 좁힌 것이다.
대신 얻은 것이 있다. FDM은 구현이 단순하다. 코드가 짧고, 메모리 사용을 예측할 수 있고, 버그가 숨을 곳이 적다. 그리고 계산이 국소적이라 — 각 점이 이웃만 보므로 — 병렬로 나누기 쉽다.
폰에서 돌리기에 이 성질들이 중요했다. 성능이 아니라 예측 가능성 때문이다. 앱이 언제 죽을지 알 수 없는 것보다, 조금 느려도 항상 끝나는 것이 낫다.
다음 편에서는 이 방법으로 실제 문제 하나를 끝까지 풀어본다. 지금까지의 설명이 실제 숫자로 어떻게 나타나는지 보게 된다.
다음 편 예고 — 열전달, 첫 번째 해석. 프라이팬 손잡이 문제를 처음부터 끝까지 따라가며, 조건 설정부터 결과 해석까지의 전 과정을 본다.