2011년 1월 14일 금요일

GMES 이야기를 시작하며...

내 전공은 정보통신공학인데, 국내의 여느 과와 달리 포토닉스 관련 분야를 다룬다. 그중에서도 컴퓨터 모의실험을 주로 하는데, 그 결과로 개발하고 있는 프로그램이 GMES이다. 모의실험에 사용하는 수치해석 프로그램을 개발하다 보면, 수많은 버그와 만나게 되는데 단순한 자판 입력 실수에서부터 프로그램 언어나 알고리듬의 구현 오류와 물리적 이해의 부족에서 발생하는 오류까지, 유형이 참 다양하다. 앞의 두 이유는 다른 분야의 프로그래밍에도 흔하니, 나는 물리적 이해의 부족으로 발생하는 버그에 대해 다뤄보고자 한다.

GMES는 맥스웰 방정식을 수치해석적으로 푸는 FDTD라는 방법을 구현한 프로그램이다. 맥스웰 방정식을 푼다는 점은 전자기 현상을 다룬다는 의미와 이를 양자역학적 해석보다는 고전역학적 해석으로 다룬다는 의미가 있다. FDTD는 우리가 생활하는 공간과 같은 시공간을 그대로 사용하는 방식으로 주파수 공간이나 파수 공간을 사용하는 방식과는 달리, 실제 실험과 유사한 방식으로 계산 공간을 설정할 수 있는 장점이 있다. 즉, 실험 모형과 입력 광원 등을 모두 머릿속에 쉽게 그려볼 수 있다.

GMES의 검증도 겸할까 해서, 학부 물리학과 2학년에 전자기학에서 배우는 쉬운 실험 모형을 하나 골랐다. 바로 두 유전체의 경계면에 입사하는 평면파 모형이다. 이중에서 입력 광원에 대해 살펴보자. 먼저, 이 모의실험에 사용할, 제대로 된 평면파의 모습은 아래와 같다.
정상적인 평면파의 모습
대략 y축의 0.8부터 -1.2까지 사선 방향으로, 완벽하진 않지만, 일정한 위상을 갖는 평면파의 모습을 관찰할 수 있다. 그림의 위와 아랫부분에 평면파가 없는 것은 흡수경계조건과 입력 광원의 위치 때문이다. x축의 -1.0부터 -2.0까지와 1.0부터 2.0까지는 CPML이라는 흡수경계조건을 사용해서 반사 없이 전자기파를 흡수하고, x축의 0.8에 위치한 입력 평면에서 오른쪽 위를 향해 평면파를 입력한다. y축의 왼쪽과 오른쪽 끝은 주기적경계조건(Periodic Boundary Conditions, PBC)이 설정되어 있어서 오른쪽 끝에 다다른 전자기파는 다시 왼쪽으로 입력된다. 그런데 이렇게 나와야 하는 녀석이 아래 그림처럼 나왔다.
허수부 위상을 맞추지 못해 변형된 평면파
사실 연구실에서 직접 만든 모의실험 프로그램은 뭔가 새로운 것을 시도할 때마다 한번에 제대로 된 결과를 내놓는 경우가 거의 없다. 앞에서 말한 세 가지 버그의 유형 중의 하나 이상이 꼭 말썽을 부린다. 이번에는 내 세심함이 부족해서 생긴 문제인데, 세 가지 유형 중의 하나로 따지자면, 물리적 이해의 깊이 부족이지 않을까 한다.

첫 그림의 왼쪽과 오른쪽 끝을 비교해보자. 오른쪽 경계에 다다른 전자기파는 다시 왼쪽 경계로 다시 입력된다고 했는데, 뭔가 이상하지 않은가? 왼쪽과 오른쪽의 경계를 맞춰보면 일치하지 않고 약간 틀어져 있는 것을 알 수 있다. 즉, 어느 정도의 위상차가 있다. 평면파의 위상을 맞추려면, 오른쪽 끝의 전자기파와 왼쪽 끝의 전자기파에 일정 위상차를 주어야 하는데, 이런 위상차를 주려면 전자기파를 복소수로 나타내야 한다. 복소수로 나타낸 전자기파는 단순한 곱셈으로 진폭의 변화 없이 일정 위상만큼 변화를 줄 수 있다.

그럼, 위에서 열거한 것 중에서 뭐가 문제일까? 개별 요소들은 각기 다른 환경에서 검증을 진행해 온 상태이다. 아직 검증하지 못한 부분에 버그가 존재할 수도 있고, 이들의 조합에서 문제가 발생할 수도 있다. 아니면, 내가 뭔가 잘 못 생각하고 있을 수도 있다. 내가 뭘 놓치고 있을까? 모든 가능성을 염두에 두고 print를 이용한 디버깅 정보 수집에 나섰다. 그러던 중 우연히 프로그램 일부가 실수 전자기파를 사용하고 있음을 알 수 있었다. 그리고 그 부분을 찾아보니 입력 광원 구현부였다. 입력 평면을 따라 전자기파의 실수부는 계속 변하는데, 허수부는 일정 위상(0)을 유지하고 있으니, 당연히 문제가 되었던 것이다. 이를 수정하니, 첫 번째 그림과 같은 정상적인 평면파를 볼 수 있었다.

2010년 12월 29일 수요일

파이어폭스 검색을 더욱 은밀하게

구글은 검색 정보를 암호화해서 전송할 수 있는 Google search over SSL을 제공한다. 이 사이트에 직접 들어가서 검색을 하면 검색어, 검색결과 등이 암호화되어 네트워크에서 여러분의 검색정보를 영탐할 수 없게 할 수 있다. 하지만, 파이어폭스의 우측 상단에 있는 검색바를 이용한다면, 검색정보가 네트워크에 그대로 드러나게 된다.

Google search over SSL

이 검색창도 Google search over SSL 서비스를 이용하도록 하면, 여러분의 검색정보를 보호할 수 있다. 검색바의 왼쪽에 있는 화살표를 아이콘을 누르면 제일 아래에 '검색 사이트 관리...'를 볼 수 있다. 여기서 '추가할 검색 사이트 찾기...'를 클릭하면, 파이어폭스 애드온 페이지로 연결된다. 여기서 Google search over SSL 관련 애드온을 찾을 수 있는데, 페이지 오른쪽 위편의 '부가 기능 검색하기' 서식에 'Google SSL'이라고 검색하면 관련 SSL을 찾을 수 있다. 몇몇 애드온을 찾을 수 있는데, SSL Search Bar가 제작한 Google (SSL)을 설치해보자. 오른쪽의 녹색 'Firefox에 추가' 버튼을 누르면, 설치가 진행된다. 이제 브라우저의 검색창 아이콘을 다시 클릭해보면, 'Google (SSL)'이 설치된 것을 알 수 있다.

Google (SSL)이 제대로 작동하는 것은 몇 가지 사항을 통해서 확인할 수 있다. 먼저, 브라우저의 검색바에 Google 대신에 Google (SSL)이라고 쓰여 있는 것을 확인할 수 있다. 한번 검색을 해보자. 검색 결과 페이지의 구글 마크에 조가만 SSL 문구를 볼 수 있다. 또, 주소창을 보면 프로토콜이 http가 아니라 https인 것을 알 수 있다. 이제 좀 더 안전한 검색을 할 수 있는 환경이 구축되었다.

Google (SSL)을 사용하도록 설정한 검색바

참고로, SSL 검색에서는 지원되지 않는 기능이 많이 있다. 가장 눈에 띄는 것은 특정일에 따라 변하는 구글 로고를 볼 수 없다는 것! 점차 많은 서비스를 SSL로 제공한다고 하니 일단 기다려보자.

2010년 12월 6일 월요일

리눅스와 윈도의 줄바꿈 차이

글을 입력하면서 커서가 화면의 오른쪽 끝에 이르면 '줄바꿈'을 해야 한다. 리눅스와 윈도 모두 '줄바꿈'을 할 때 누르는 키는 '엔터(Enter)'이고 화면에서 나타나는 모양새는 같은데, 내부적인 처리에는 차이가 있다.

윈도는 옛 타자기 시절의 습관을 고스란히 옮겨 놓은 낭만적인 방식을 택한다. 타자기를 사용하던 시절엔 입력하는 글의 줄을 바꾸려면 두 가지 동작이 필요했다. 먼저, 사용자가 글자를 입력함에 따라 종이를 왼쪽으로 옮겨주는 캐리지(carriage)라는 부품을 오른쪽 끝으로 밀어야 한다. 그런 다음 종이를 조금 위로 올려서 다음 줄의 첫 부분에 활자가 닿을 수 있도록 조정해야 한다. 캐리지를 오른쪽 끝으로 되돌려 놓는 동작은 컴퓨터에서 커서의 위치를 왼쪽 끝으로 되돌려 놓는 동작에 해당하고, 이를 복귀(carriage return)이라고 한다. 문자열 표기(string notation)로는 '\r'로 나타낸다. 종이를 위로 올리는 동작은 현재 커서가 있는 줄의 아래에 입력 가능한 줄을 삽입하고 커서를 한 칸 아래로 내리는 동작에 해당하며 이를 새줄(new line)이라고 한다. 문자열 표기로는 '\n'으로 나타낸다. 실제로 글을 입력할 때 줄을 바꾸려면 엔터 키만 누르지만, 문서의 각 줄 끝에는 '\r\n'이 들어간다.

리눅스(유닉스 계열)는 컴퓨터에 적합한 실용적인 방식을 택한다. 타자기는 기계적인 한계 때문에 '복귀'와 '새줄'이라는 동작을 차례로 해야 했지만, 컴퓨터에서는 굳이 그럴 필요가 없다. 그래서 '줄바꿈'이라는 표시로 '\n'만을 사용한다.

모든 비표준이 그렇듯이, 이러한 차이는 리눅스와 윈도를 오가며 문서를 작성하다 보면 불편한 점을 만든다. 윈도에서 작성한 문서를 리눅스에서 보면 모든 줄 끝에 '^M'이라는 문자가 나타난다. 리눅스는  '\n'만을 '줄바꿈'으로 문자로 인식하고, '\r'은 해당 꺽쇠 표기(caret notation)인 '^M'로 화면에 표시하는 것이다.

윈도 줄바꿈 형식을 사용한 문서를 유닉스 계열의 줄바꿈 형식으로 열면 ^M 문자를 볼 수 있다.

이건 그나마 나은 편이다. 리눅스에서 작성한 문서를 윈도에서 보면 문서의 모든 내용이 한 줄에 표시된다. 윈도에서 줄바꿈으로 인식하는 '\r\n'이라는 제어 문자가 없으니 한 줄에 모든 내용을 표시해버린다.

유닉스 계열의 줄바꿈 형식을 사용한 문서를 윈도에서 열면 줄바꿈이 되지 않는다.

요즘은 편집기가 똑똑해져서 '줄바꿈' 제어 문자가 뭐로 되어 있든지 알아서 잘 처리해주지만, 때로 문제가 되기도 한다. 한 문서에 여러 가지 '줄바꿈' 제어 문자가 들어 있으면, 편집기가 이를 잘 처리하지 못하기도 하고, 시스템 환경과 다른 '줄바꿈' 문자 때문에 스크립트 프로그램이 이상한 오작동을 하기도 한다. 그렇다면, 윈도 방식의 줄바꿈으로 된 문서를 리눅스 방식의 줄바꿈으로 바꾸려면 어떻게 해야 할까?

쉘에서 손쉽게 바꾸는 방법은 아래와 같다.
$ cat <FileName> | tr -d '\r' > out; mv out <FileName>
이맥스에서 두 가지 방법으로 할 수 있다. 첫 번째는 ^M을 공백으로 치환하는 방법이다. C-Home으로 버퍼의 첫 부분으로 간 다음 아래 명령을 실행한다.
M-% C-q C-m RET RET !
C-q는 입력 버퍼에 제어 문자를 입력하게 해주는 키이다. 두 번째로 현재 버퍼의 코딩 방식을 바꿔주는 방법이 있다.
M-x set-buffer-file-coding-system을 입력하거나 C-x RET f를 입력해서, 미니버퍼에
Coding system for saving file (default nil):
이 뜨면, unix를 입력한다.

2010년 10월 27일 수요일

오일러의 수, 전기장, 에너지

매스매티카 사용자에게 오일러의 수, 전기장, 에너지를 생각하면 떠오르는 건 뭘까? 방정식에서 모두 E로 표기된다는 점이다. 그래서 문제가 발생한다. 실수로 아래와 같이 입력하면,
E == m c^2
결과는




이 된다. 이 방정식은 좌변은 오일러의 수로 상수를 나타내므로 틀린 식이다. 매스매티카에서 대문자 E는 오일러의 수를 가리킨다. 즉, 매스매티카에서 E 또는 Esc+ee+Esc, 혹은 \[ExponentialE]를 입력하면 이건 오일러의 수를 나타낸다. 그럼, 전기장이나 에너지를 뜻하는 변수로 E를 쓸 수는 없을까? 유감스럽게도 그렇다. 다른 문자를 사용해야 한다. 하지만, 대안이 있다. 보기에 E와 비슷한 문자를 사용할 수는 있다. \[CapitalEpsilon] 즉, 그리스 문자 ε의 대문자가 E와 비슷하게 생겼는데, 이걸 사용하면 된다.
\[CapitalEpsilon] == m c^2
라고 입력하면 쓸만한 결과를 얻을 수 있다.




다행히도 단축키도 있어서 매번 \[CapitalEpsilon]을 입력할 필요도 없다. 단축키는 Esc+E+Esc이다.

x에 대하여 정리하면...

대중에게 알려진 가장 유명한 방정식은 아인슈타인의 에너지와 질량의 동등성을 나타내는
e == m c^2
일 것 같다. 여기서 m은 물체의 질량, c는 진공에서 빛의 속도, 그리고 e는 이 질량에 저장된 에너지를 뜻한다. 이 방정식을 m에 관해 정리하려면, 즉 m만 좌변에 남기고 모두 우변으로 옮기려면 어떻게 해야 할까? 방정식을 m에 관해 풀면 된다.
e == m c^2
% // Solve[#, m] &
결과는 방정식이 아닌 Rule로 주어진다. 이걸 방정식으로 바꾸려면, 아래와 같이 리스트(List)를 벗겨 내고, 헤더를 Equal로 바꿔주면 된다.
e == m c^2
% // Solve[#, m][[1, 1]] & // Equal @@ # &
그럼, c^2에 관해 정리하려면 어떻게 해야 할까? 위와 같은 방법으로 하면 c^2가 적절한 변수가 아니라는 오류 메시지를 보게 될 뿐이다. 이럴 땐, c^2를 잠시 HoldForm으로 잡아두면 문제를 해결할 수 있다.
e == m c^2
% /. c^2 -> HoldForm[c^2] // Solve[#, HoldForm[c^2]][[1, 1]] & // 
  ReleaseHold // Equal @@ # &
Solve로 항을 정리한 후에 ReleaseHold를 사용하면 간단하게 HoldForm을 제거할 수 있다.

2010년 9월 26일 일요일

주기율표에 나오는 원소기호로 만들 수 있는 가장 긴 단어는?

KAIST의 정재승 교수가 트위터에 던진 질문인데, 인터넷에서 몇몇 분이 이 문제를 풀었다. 그 중, 조승연 씨가 파이썬으로 작성한 코드는 아래와 같다.
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import re

f = open("/usr/share/dict/words", 'r')
maxlen = 0
maxword = ""
pattern = r"^(Ac|Al|Am|Sb|Ar|As|At|Ba|Bk|Be|Bi|Bh|B|Br|Cd\
|Ca|Cf|C|Ce|Cs|Cl|Cr|Co|Cu|Cm|Ds|Db|Dy|Es|Er|Eu|Fm|F|Fr\
|Gd|Ga|Ge|Au|Hf|Hs|He|Ho|H|In|I|Ir|Fe|Kr|La|Lr|Pb|Li|Lu|Mg\
|Mn|Mt|Md|Hg|Mo|Nd|Ne|Np|Ni|Nb|N|No|Os|O|Pd|P|Pt|Pu|Po\
|K|Pr|Pm|Pa|Ra|Rn|Re|Rh|Rg|Rb|Ru|Rf|Sm|Sc|Sg|Se|Si|Ag\
|Na|Sr|S|Ta|Tc|Te|Tb|Tl|Th|Tm|Sn|Ti|W|Uuh|Uuo|Uup|Uuq\
|Uus|Uut|U|V|Xe|Yb|Y|Zn|Zr|Cn)+$"

for line in f:
   ll = line.strip()
   if re.match(pattern, ll, flags=re.IGNORECASE) and \
len(ll) > maxlen:
      maxlen = len(ll)
      maxword = ll

print maxlen, maxword
이 페이지에 나온 답은 22글자의 nonrepresentationalism이지만, 내가 가진 사전 파일에는 이 단어가 없다. 대신 19글자의 nonrepresentational이 걸린다. 이걸 매스매티카로 해보면?
FindMaxElementWord[dictFile_] :=
 Module[{maxlen = 0, maxword = "", elements, pattern, dict, word},
  elements = Table[ElementData[n, "Abbreviation"], {n, 118}];
  pattern =
   "^(" <> Drop[Flatten[Map[{#, "|"} &, elements]], -1] <> ")+$";
  dict = OpenRead[dictFile];
  word = Read[dict, Word];
  While[word =!= EndOfFile,
   If[StringMatchQ[word, RegularExpression[pattern],
      IgnoreCase -> True] &&
     StringLength[word] > maxlen, {maxlen,
      maxword} = {StringLength[word], word}];
   word = Read[dict, Word]];
  Close[dict];
  Return[{maxlen, maxword}]]
알고리듬은 위의 파이썬 코드와 같다. 다만, 원소기호를 자동으로 생성하도록 바꾸었다. FindMaxElementWord["/usr/share/dict/words"]를 실행해보면, 역시 19글자의 nonrepresentational이 걸린다.

2010년 9월 7일 화요일

상수 모으기

매스매티카를 쓰다 보면, 간단한 수식인데 깔끔하게 정리하기 어려울 때가 있다. 예를 들어, Collect[2/x - 2/y, 2]의 결과가


이렇게 나오면 좋겠지만, 결과는 2/x - 2/y이다. 2로 묶어주려면, 어떻게 해야 할까? 이런 어려움에 봉착했을 땐 우선 풀폼을 확인한다.
FullForm[2/x - 2/y]
 

풀폼에서 알 수 있듯이, 첫 번째 항은 2가 두 번째 항은 -2가 곱해져 있어서 2로 묶지 못한다. 답답하게도 -2에서 2를 빼내지 못하는군. 그럼, -2-1*2로 바꿔주면 되겠다 싶지만, 여기에도 문제가 있다. 매스매티카는 -1*2를 입력하면 바로 -2로 바꿔버리므로, 바꾸지 못하게 해야 한다.
2/x - 2/y
% /. -2 -> HoldForm[-1] 2
% // Collect[#, 2] & // ReleaseHold
이제 됐을까? 결과를 보면 알겠지만, 이번에도 실패다. Collect가 문제인데, Collect는 변수만을 모을 수 있기 때문이다. 그럼, 2는 변수가 아닌데 어떻게 해야 할까? 2HoldForm[2]로 치환해서 변수처럼 만들어주면 된다.
2/x - 2/y
% /. -2 -> HoldForm[-1] 2 /. 2 -> HoldForm[2]
% // Collect[#, HoldForm[2]] & // ReleaseHold
이제야 원하는 결과가 나오는군. 휴~