HTML5による物理シミュレーション環境の構築
水素原子の波動関数ビューア
HTML5の新要素 canvas 要素に WebGL(Three.js)+Javascript を利用して構築する物理シミュレータの第3弾として、「水素原子の波動関数ビューア」を公開します。
水素原子は、陽子1つと電子1つで構成される最も単純な構造の原子です。
電子はクーロン相互作用の引力により、電子よりも2000倍重たい陽子の周りに雲のように分布します(電子雲)。
電子状態は、主量子数, 方位量子数
, 磁気量子数
3つのパラメータで指定され、それぞれ特徴的な形状を成します。
本稿では HTML5 を水素原子の電子状態を表す波動関数の3次元空間分布を可視化するためのビューアとして利用します。
以下の図はデモ画面です → 実行ページこちら
計算アルゴリズム
水素原子の電子状態は、電子のように非常に小さな粒子が従うシュレーディンガー方程式を解くことで理解することができます。
電子状態を表す波動関数に対するシュレーディンガー方程式は、その他の原子の場合と異なり、解析的に解くことができます。
解析解は、主量子数, 方位量子数
, 磁気量子数
の3つのパラメータを用いて、
と表されます。
と
は、元のシュレーディンガー方程式を変数分離した際に得られる動径方向と方位角方向の解で
と
となります。式(2),(3)にある はラゲール多項式,
はルジャンドル陪関数を表し、
で定義されます。ラゲール多項式は階乗を計算できれば簡単に計算することができますが、
ルジャンドル陪関数は、複数回の微分を演算する必要があるため、簡単に計算することできません。
しかしながら、漸化式を用いることで比較的簡単に計算することができます。
※ルジャンドル陪関数については「ルジャンドル陪関数の漸化式と規格化」を参考にして下さい。
式中のは長さの表す無次元量です。
本ビューアは、
を単位として、1辺の長さ
の立方体を描画しています。
ただし、
は
で定義され、はボーア半径と呼ばれ
と定義されます。
スクリーンショット
プログラムソース
Javascript
本ビューアは「WebWorker」と呼ばれる Javascript にて並列化計算を行うための技術を利用しています。 WebWorker も WeBGL と同様に、HTML5 における Javascript の標準化技術です。
本体
var L = 80; //系のサイズ
var n_max = 4; //主量子数の最大値
var p_max = 40000;//点の数
var p_size = 0.5; //点のサイズ
var times, timesL;
var scatterPlots = new Array(n_max+1);
var workers = new Array(n_max+1);
var $id = function(id) { return document.getElementById(id); }
function init(){
//各状態のオブジェクトとワーカーの多重配列を宣言
for (var n = 1; n <= n_max; n++) {
scatterPlots[n] = new Array(n);
workers[n] = new Array(n);
for (var l = 0; l < n; l++) {
scatterPlots[n][l] = new Array(l+1);
workers[n][l] = new Array(l+1);
for(m=0; m<=l; m++) scatterPlots[n][l][m] = new Array(3);
}
}
//ワーカーの設定
for (var n = 1; n <= n_max; n++){
for (var l = 0; l < n; l++){
for (var m = 0; m <= l; m++){
workers[n][l][m] = new Worker("worker_wave.js");
for(var i=0; i<=2; i++) scatterPlots[n][l][m][i] = new THREE.Object3D();
workers[n][l][m].onmessage = function(event) {//ワーカーから受け取り
var Ps = event.data;
var n = Ps.n; var l =Ps.l; var m =Ps.m;
var Xs = Ps.x;
var Ys = Ps.y;
var Zs = Ps.z;
var mat = new THREE.ParticleBasicMaterial({vertexColors:true, size: p_size});
var pointGeos = new Array(3);
for(var i=0; i<=2; i++) pointGeos[i] = new THREE.Geometry();
if(m==0){
for (var i=0; i< Zs.length; i++) {
pointGeos[0].vertices.push(new THREE.Vertex( new THREE.Vector3(Zs[i].x, Zs[i].y, Zs[i].z) ));
if(Zs[i].a >0 ) pointGeos[0].colors.push(new THREE.Color(0xff0000));
else pointGeos[0].colors.push(new THREE.Color(0x0000ff));
}
}else{
for (var i=0; i< Xs.length; i++) {
pointGeos[1].vertices.push(new THREE.Vertex( new THREE.Vector3(Xs[i].x, Xs[i].y, Xs[i].z) ));
if(Xs[i].a >0 ) pointGeos[1].colors.push(new THREE.Color(0xff0000));
else pointGeos[1].colors.push(new THREE.Color(0x0000ff));
pointGeos[2].vertices.push(new THREE.Vertex( new THREE.Vector3(Ys[i].x, Ys[i].y, Ys[i].z) ));
if(Ys[i].a >0 ) pointGeos[2].colors.push(new THREE.Color(0xff0000));
else pointGeos[2].colors.push(new THREE.Color(0x0000ff));
}
}
if(m==0){
scatterPlots[n][l][m][0].add(new THREE.ParticleSystem(pointGeos[0], mat));
$id("s" + n + l + m ).innerHTML = "準備完了!" ;
$id("n" + n + l + m ).style.display = 'inline';
}else{
scatterPlots[n][l][m][1].add(new THREE.ParticleSystem(pointGeos[1], mat));
$id("s" + n + l + m + "x").innerHTML = "準備完了!" ;
$id("n" + n + l + m + "x").style.display = 'inline';
scatterPlots[n][l][m][2].add(new THREE.ParticleSystem(pointGeos[2], mat));
$id("s" + n + l + m + "y").innerHTML = "準備完了!" ;
$id("n" + n + l + m + "y").style.display = 'inline';
}
}
if(m==0){
$id("n" + n + l + m ).checked = false;
$id("n" + n + l + m ).style.display = 'none';
}else{
$id("n" + n + l + m + "x").checked = false;
$id("n" + n + l + m + "x").style.display = 'none';
$id("n" + n + l + m + "y").checked = false;
$id("n" + n + l + m + "y").style.display = 'none';
}
}
}
}
}
var width, height;
var renderer;
function initThree() {
width = document.getElementById('canvas-frame').clientWidth;
height = document.getElementById('canvas-frame').clientHeight;
try {renderer = new THREE.WebGLRenderer({antialias: true});} catch (e) {}
if(!renderer) document.getElementById("errer").innerHTML = '<p style="text-align:center;font-size:small; color:red">お使いの環境ではWebGLはご利用いただけません。<br />WebGLに対応していない方のためにGIFファイルを以下に用意しました。<br /><br /><img src="http://www.natural-science.or.jp/images/tutorial6.gif" alt="WEBGLデモ" /></p>';
renderer.setSize(width, height);
document.getElementById('canvas-frame').appendChild(renderer.domElement);
renderer.setClearColorHex(0x000000, 1.0);
$id("pn").value = p_max;
$id("ps").value = p_size;
}
var camera;
var cameraL = 150;
var cameraTheta = Math.PI*(1/2-0.01);
var cameraPhi = Math.PI*(3/2-0.01);
function initCamera() {
camera = new THREE.PerspectiveCamera( 45 , width / height , 1 , 10000 );
camera.up.x = 0;
camera.up.y = 0;
camera.up.z = 1;
camera.position.set(cameraL*Math.sin(cameraTheta)*Math.cos(cameraPhi),
cameraL*Math.sin(cameraTheta)*Math.sin(cameraPhi),
cameraL*Math.cos(cameraTheta));
}
var scene;
function initScene() {
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2( 0xFFFFFF, 0.0015 );
}
var light, light2;
function initLight() {
light = new THREE.DirectionalLight(0xFFFFFF, 1.0, 0);
light.position.set( 100, 100, 100 );
scene.add(light);
light2 = new THREE.AmbientLight(0x777777);
scene.add(light2);
}
var scatterPlot;
var axis;
function initObject(){
axis = new THREE.Object3D();
scene.add(axis);
scatterPlot = scatterPlots[1][0][0];
function v(x,y,z){ return new THREE.Vertex(new THREE.Vector3(x,y,z)); }
var lineGeo = new THREE.Geometry();
lineGeo.vertices.push(
v(-L/2, 0, 0), v(L/2, 0, 0),
v(0, -L/2, 0), v(0, L/2, 0),
v(0, 0, -L/2), v(0, 0, L/2),
v(-L/2, L/2, -L/2), v(L/2, L/2, -L/2),
v(-L/2, -L/2, -L/2), v(L/2, -L/2, -L/2),
v(-L/2, L/2, L/2), v(L/2, L/2, L/2),
v(-L/2, -L/2, L/2), v(L/2, -L/2, L/2),
v(-L/2, 0, L/2), v(L/2, 0, L/2),
v(-L/2, 0, -L/2), v(L/2, 0, -L/2),
v(-L/2, L/2, 0), v(L/2, L/2, 0),
v(-L/2, -L/2, 0), v(L/2, -L/2, 0),
v(L/2, -L/2, -L/2), v(L/2, L/2, -L/2),
v(-L/2, -L/2, -L/2), v(-L/2, L/2, -L/2),
v(L/2, -L/2, L/2), v(L/2, L/2, L/2),
v(-L/2, -L/2, L/2), v(-L/2, L/2, L/2),
v(0, -L/2, L/2), v(0, L/2, L/2),
v(0, -L/2, -L/2), v(0, L/2, -L/2),
v(L/2, -L/2, 0), v(L/2, L/2, 0),
v(-L/2, -L/2, 0), v(-L/2, L/2, 0),
v(L/2, L/2, -L/2), v(L/2, L/2, L/2),
v(L/2, -L/2, -L/2), v(L/2, -L/2, L/2),
v(-L/2, L/2, -L/2), v(-L/2, L/2, L/2),
v(-L/2, -L/2, -L/2), v(-L/2, -L/2, L/2),
v(-L/2, 0, -L/2), v(-L/2, 0, L/2),
v(L/2, 0, -L/2), v(L/2, 0, L/2),
v(0, L/2, -L/2), v(0, L/2, L/2),
v(0, -L/2, -L/2), v(0, -L/2, L/2)
);
var lineMat = new THREE.LineBasicMaterial({color: 0x444444, lineWidth: 1});
var line = new THREE.Line(lineGeo, lineMat);
line.type = THREE.Lines;
axis.add(line);
var titleX = createText2D('-X', 'gray');
titleX.position.x = -(L/2+5);
titleX.rotation.x = Math.PI/2;
axis.add(titleX);
var titleX = createText2D('X', 'gray');
titleX.position.x = (L/2+5);
titleX.rotation.x = Math.PI/2;
axis.add(titleX);
var titleX = createText2D('-Y', 'gray');
titleX.position.y = -(L/2+5);
titleX.rotation.x = Math.PI/2;
axis.add(titleX);
var titleX = createText2D('Y', 'gray');
titleX.position.y = (L/2+5);
titleX.rotation.x = Math.PI/2;
axis.add(titleX);
var titleX = createText2D('-Z', 'gray');
titleX.position.z = -(L/2+5);
titleX.rotation.x = Math.PI/2;
axis.add(titleX);
var titleX = createText2D('Z', 'gray');
titleX.position.z = (L/2+5);
titleX.rotation.x = Math.PI/2;
axis.add(titleX);
start(1, 0, 0);//n=1だけデフォルトで計算スタート
$id("n100").checked = true;
/*
var AA = {n:1, l:0, m:0, p_max:p_max, L: L};
workers[1][0][0].postMessage(AA); // ワーカに数値を渡す
$id("b100").innerHTML = "再計算"; ;
$id("s100").innerHTML = "計算中..." ;
*/
}
function start(n, l, m) {//計算開始ボタンを押した時に呼び出される関数
for(var i=0; i<=2; i++) {
scene.remove( scatterPlots[n][l][m][i]);
scatterPlots[n][l][m][i] = new THREE.Object3D();
}
p_max = parseInt($id("pn").value);
p_size = parseFloat($id("ps").value);
$id("b" + n + l + m ).innerHTML = "再計算";
if(m==0){
$id("s" + n + l + m).innerHTML = "計算中..." ;
}else{
$id("s" + n + l + m + "x").innerHTML = "計算中..." ;
$id("s" + n + l + m + "y").innerHTML = "計算中..." ;
}
var AA = {n:n, l:l, m:m, p_max:p_max, L: L};
workers[n][l][m].postMessage(AA); // ワーカに数値を渡す
}
function initEvent(){//イベントの登録
window.addEventListener("mousedown", onMousedown, false);
window.addEventListener("mouseup", onMouseup, false);
window.addEventListener("mousemove", onMousemove, false);
window.addEventListener("mousewheel", onMousewheel, false);
window.addEventListener('DOMMouseScroll', onMousewheel, false); //Firefox用(マウスホイール)
window.addEventListener("dragover", onCancel, false);
window.addEventListener("dragenter", onCancel, false);
window.addEventListener("drop", onDropFile, false);
for (var n = 1; n <= n_max; n++)
for (var l = 0; l < n; l++)
for (var m = 0; m <= l; m++)
//$id("b" + n + l + m).addEventListener("click", start(n,l,m), false); //これでは、start(n,l,m)が実行されてしまうのでダメ
//$id("b" + n + l + m).addEventListener("click", function(event) { start(n,l,m); }, false); //これでは、イベントの登録がfor文の後になるため、意図通り引き数を与えることができないダメ
$id("b" + n + l + m).addEventListener("click", (function (n_, l_, m_) { return function() { start(n_, l_, m_); } })(n, l, m) , false);
//参考ページ
//http://d.hatena.ne.jp/Syunpei/20070605/1181035316
//http://d.hatena.ne.jp/uriyuri/20081014/1223974862
}
function loop() {
for (var n = 1; n <= n_max; n++)
for (var l = 0; l < n; l++)
for (var m = 0; m <= l; m++){
if(m==0){
if($id("n" + n + l + m).checked) scene.add(scatterPlots[n][l][m][0]);
else scene.remove( scatterPlots[n][l][m][0]);
}else{
if($id("n" + n + l + m + "x").checked) scene.add(scatterPlots[n][l][m][1]);
else scene.remove( scatterPlots[n][l][m][1]);
if($id("n" + n + l + m + "y").checked) scene.add(scatterPlots[n][l][m][2]);
else scene.remove( scatterPlots[n][l][m][2]);
}
}
renderer.clear();
camera.lookAt( {x:0, y:0, z:0 } );
renderer.render(scene, camera);
window.requestAnimationFrame(loop);
}
function threeStart() {
initEvent();
initThree();
initCamera();
initScene();
initLight();
initObject();
loop();
}
function createTextCanvas(text, color, font, size) {
size = size || 24;
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var fontStr = (size + 'px ') + (font || 'Arial');
ctx.font = fontStr;
var w = ctx.measureText(text).width;
var h = Math.ceil(size);
canvas.width = w;
canvas.height = h;
ctx.font = fontStr;
ctx.fillStyle = color || 'black';
ctx.fillText(text, 0, Math.ceil(size*0.8));
return canvas;
}
function createText2D(text, color, font, size, segW, segH) {
var canvas = createTextCanvas(text, color, font, size);
var plane = new THREE.PlaneGeometry(canvas.width, canvas.height, segW, segH);
var tex = new THREE.Texture(canvas);
tex.needsUpdate = true;
var planeMat = new THREE.MeshBasicMaterial({
map: tex, color: 0xffffff, transparent: true
});
var mesh = new THREE.Mesh(plane, planeMat);
mesh.scale.set(0.15, 0.15, 0.15);
mesh.doubleSided = true;
return mesh;
}
function QuaternionRotation(theta, u, v){ //(回転角, 回転軸, 任意のベクトル)
var P = new quat4.create([v[0],v[1],v[2],0]);
var Q = new quat4.create([-u[0]*Math.sin(theta/2), -u[1]*Math.sin(theta/2), -u[2]*Math.sin(theta/2), Math.cos(theta/2)]);
var R = new quat4.create([u[0]*Math.sin(theta/2), u[1]*Math.sin(theta/2), u[2]*Math.sin(theta/2), Math.cos(theta/2)]);
var S0 = quat4.multiply(P,Q, quat4.create());
var S = quat4.multiply(R,S0,quat4.create());
return S;
}
////マウスイベント////////////////////////////////////////////////////
var down = false;
var v, u, w, theta;
function onMousedown (ev){ //マウスダウン時イベント
if (ev.target == renderer.domElement) {
down = true;
sx = ev.clientX; sz = ev.clientY;
v = new vec3.create([camera.position.x,camera.position.y,camera.position.z]); //任意の点
u = new vec3.create([-v[0]*v[2],-v[1]*v[2], (v[0]*v[0]+v[1]*v[1])]);
u = vec3.normalize(u, vec3.create());
w = new vec3.create([v[1],-v[0],0]);
w = vec3.normalize(w, vec3.create());
}
};
function onMouseup (){ //マウスアップ時イベント
down = false;
};
function onMousemove (ev) { //マウスムーブ時イベント
if (down) {
if (ev.target == renderer.domElement) {
var dx = -(ev.clientX - sx);
var dz = -(ev.clientY - sz);
theta = dx/ 100;
var S = QuaternionRotation(theta, u, [camera.position.x,camera.position.y,camera.position.z]);
theta = -dz/ 100;
S = QuaternionRotation(theta, w, [S[0],S[1],S[2]]);
camera.position.set(S[0],S[1],S[2]);
sx -= dx;
sz -= dz;
}
}
}
function onMousewheel(ev){//マウスホイール時イベント
var x = camera.position.x;
var y = camera.position.y;
var z = camera.position.z;
cameraL = Math.sqrt(Math.pow(x,2)+Math.pow(y,2)+Math.pow(z,2));
cameraTheta = Math.acos(z/cameraL);
if( y >= 0 ) cameraPhi = Math.acos(x/(cameraL*Math.sin(cameraTheta)));
else cameraPhi = 2.0*Math.PI - Math.acos(x/(cameraL*Math.sin(cameraTheta)));
var delta;//回転量
if (ev.wheelDelta) {
delta = ev.wheelDelta; //Chrome用
} else if (ev.detail) {
delta = -ev.detail*20; //Firefox用
}
cameraL += delta * 0.2;
if(cameraL < 0) cameraL = 0.1;
camera.position.set(cameraL*Math.sin(cameraTheta)*Math.cos(cameraPhi),
cameraL*Math.sin(cameraTheta)*Math.sin(cameraPhi),
cameraL*Math.cos(cameraTheta));
}
var onDropFile = function(ev){ // ファイルドロップ時イベント
ev.preventDefault();
var file = ev.dataTransfer.files[0]; // File オブジェクトを取得
readFile(file); // ファイル読み込み
};
var onCancel = function(ev){ // デフォル処理をキャンセル
if(ev.preventDefault) { ev.preventDefault(); }
return false;
};
function f_write(){
var str = "";
for (var i = 0; i < N_EC; i++) {
if(!ECs[i].flag) continue;
for (var j = 0; j < n_line; j++) {
var geometry = new THREE.Geometry();
for (var k = 0; k < n_step; k++) {
if(Lines[i][j][k] != undefined && Lines[i][j][k] != undefined && Lines[i][j][k] != undefined)
str += Lines[i][j][k].x + " " + Lines[i][j][k].y + " " + Lines[i][j][k].z + "\n";
else break;
}
str += "\n";
}
}
var blobBuilder;
if ("MozBlobBuilder" in window) {
blobBuilder = new MozBlobBuilder();
} else if ("WebKitBlobBuilder" in window) {
blobBuilder = new WebKitBlobBuilder();
}
blobBuilder.append(str);
//var disp = document.getElementById("download");
if (window.URL) {
window.open(window.URL.createObjectURL(blobBuilder.getBlob()) , "New Window", "");
} else if (window.webkitURL) {
window.open(window.webkitURL.createObjectURL(blobBuilder.getBlob()), "New Window", "");
}
//disp.innerHTML = 'ファイルがダウンロードされました (t='+ t +')';
}
///////////////////////////////////////////////////////////////////////
window.onload = function(){
init();
threeStart();
}
WebWorker(worker_wave.js)
// WebWorker
onmessage = function(event) {
var AA = event.data;
var results = wavefunction(AA);
// 生成元に結果を返す
results.n = AA.n; //主量子数
results.l = AA.l; //方位量子数
results.m = AA.m; //磁気量子数 m = 0 ~ l
postMessage(results);
}
function wavefunction(AA){
var n = AA.n; //主量子数
var l = AA.l; //方位量子数
var m = AA.m; //磁気量子数 m = 0 ~ l
var L = AA.L; //計算する領域のサイズ
var p_max = AA.p_max; //描画する点の数
var results = {};
var points = new Array();
points[0] = new Array();
if(m>0){
points[1] = new Array();
points[2] = new Array();
}
if(n==1){
times = 5;
timesL = L/3;
}
else if(n==2){
times = 20;
timesL = L/2;
}
else if (n==3){
times = 50;
timesL = L/1.5;
}
else if (n==4){
times =70;
timesL = L;
}
//var mat = new THREE.ParticleBasicMaterial({vertexColors:true, size: p_size});
var pointCount = p_max*100;
//var pointGeo = new THREE.Geometry();
var p=0;
for (var i=0; i<pointCount; i++) {
var x = timesL/2* (Math.random() - Math.random());
var y = timesL/2* (Math.random() - Math.random());
var z = timesL/2* (Math.random() - Math.random());
var r = Math.sqrt(Math.pow(x,2)+Math.pow(y,2)+Math.pow(z,2));
var theta = Math.acos(z/r);
var phi;
if(y>=0) phi = Math.acos(x/(r*Math.sin(theta)));
else phi = 2.0*Math.PI - Math.acos(x/(r*Math.sin(theta)));
if(r==0){
theta = 0;
phi = 0;
}
var R = 2.0/Math.pow(n,2) * Math.sqrt(Factorial(n-l-1)/Factorial(n+l)) * Math.exp(-r/n) * Math.pow( 2.0*r/n,l) *Laguerre(2*l+1, n-l-1, 2.0*r/n) ;
var Y = Math.pow(-1.0, m) * Math.sqrt(l+1.0/2.0) * Factorial(l-m)/Factorial(l+m) * Legendre(m,l,Math.cos(theta));
var Psi, Psi_x, Psi_y;
Psi = R * Y;
if(Math.abs(Psi) * times <Math.random()) continue;
if(m==0) {
if(Psi>0) points[0].push({x:x,y:y,z:z,a:1});
else points[0].push({x:x,y:y,z:z,a:-1});
}else{
var Yx = Y * Math.cos(m*phi);
var Yy = Y * Math.sin(m*phi);
Psi_x = R * Yx;
Psi_y = R * Yy;
if(Psi_x>0) points[1].push({x:x,y:y,z:z,a:1});
else points[1].push({x:x,y:y,z:z,a:-1});
if(Psi_y>0) points[2].push({x:x,y:y,z:z,a:1});
else points[2].push({x:x,y:y,z:z,a:-1});
}
p++;
if(p > p_max) break;
}
if(m==0) {
results.z = points[0];
}else{
results.x = points[1];
results.y = points[2];
}
return results;
}
function Factorial(n, n1) //
{
var m = n1 || 1;
if( n<=0 ) return (1.0);
var F = 1.0;
for(var i=n; i>=2; i-=m ){
F *= i;
}
return (F);
}
function Legendre (m, l, x ) {//ルジャンドル陪関数
var mm = Math.abs(m);
if( mm>l ) return (0);
var r0, r1, r2;
r0 = 0.0;
r1 = Math.pow(1.0-x*x, mm/2.0) * Factorial(2*mm-1,2);
if( mm==l && m>=0) return (r1);
if( mm==l && m<0) return (r1 * Math.pow(-1.0,mm) * Factorial(l-mm)/Factorial(l+mm)) ;
for(var ll = mm+1; ll<=l; ll++ ){
r2 = ((2.0*ll-1.0)*x*r1 - (ll+mm-1.0)*r0)/(ll-mm);
r0 = r1;
r1 = r2;
}
if(m>=0) return (r2);
else return (r2 * Math.pow(-1.0,mm) * Factorial(l-mm)/Factorial(l+mm)) ;
}
function Laguerre(k, n, x){
var sum=0;
for(var m=0; m<=n; m++){
sum += Math.pow(-x,m)*Factorial(n+k)/Factorial(n-m)/Factorial(k+m)/Factorial(m);
}
return sum;
}
C言語
図を描画用にデータ出力用のプログラムです。
/*
水素原子の波動関数(厳密解)
(2012.03.29 公開)
*/
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <fstream>
#include <sstream>
#include <iostream>
#include <string>
#include <cstdio>
#include <iomanip>
#include <stdio.h>
#include <complex>
#if defined(_OPENMP)
#include <omp.h>
#endif
#if defined(_MSC_VER)
#include <direct.h> // Windowsフォルダ作成用
#elif defined(__GNUC__)
#include <sys/stat.h> // UNIX系ディレクトリ作成用
#endif
using namespace std;
double PI = acos(-1.0);
double Factorial(int n);
double Factorial(int n, int m);
const int n_max = 4;
const int kizami = 200;
const double L = 80.0;
double Laguerre( int k, int n, double x); //ラゲール陪関数
double Legendre( int m, int l, double x); //ルジャンドル陪関数
string folder = "Hydrogen";//作成するフォルダ名
int main(){
#if defined(_MSC_VER)
_mkdir(folder.c_str()); // Windowsフォルダ作成
#elif defined(__GNUC__)
mkdir(folder.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); // UNIX系のディレクトリ作成
#endif
#if defined(_OPENMP)
omp_set_num_threads(8);
cout << "OpenMPを利用します(最大スレッド数:"<< omp_get_max_threads() << ")" << endl;
#endif
double r_min = 0.0;
double r_max = 40.0;
const int Z = 1;
/*// 動径分布関数
for(int n=1; n<=n_max; n++)
{
for(int l=0; l<n; l++)
{
for(int m=-l; m<=l; m++)
{
char str[200];
string str1;
ofstream fout_s;
sprintf(str, "/R%d_%d_%d.data", n, l, m); str1 = folder + str; //動径方向
fout_s.open(str1.c_str());
for(int i=0; i<=kizami*100; i++)
{
double x = r_min + (r_max-r_min)/double(kizami*100)*double(i);
double R = 2.0/pow(double(n),2) * sqrt(Factorial(n-l-1)/Factorial(n+l)) * exp(-x/double(n)) * pow( 2.0*x/double(n),l) *Laguerre(2*l+1, n-l-1, 2.0*x/double(n)) ;
fout_s << x << " " << R << endl;
}
fout_s.close();
}
}
}
*/
for(int n=1; n<=n_max; n++)
{
for(int l=0; l<n; l++)
{
for(int m=0; m<=l; m++)
{
char str[200];
string str1;
ofstream fout_s;
sprintf(str, "/Psi%d_%d_%d.data", n, l, m); str1 = folder + str; //
fout_s.open(str1.c_str());
for(int ix=0; ix<=kizami; ix++ )
{
for(int iy=0; iy<=kizami; iy++ )
{
double x = - L/2.0 + L * double(ix)/double(kizami);
double y = - L/2.0 + L * double(iy)/double(kizami);
double z =0;
double r = sqrt(pow(x,2)+pow(y,2)+pow(z,2));
double theta = acos(z/r);
double phi;
if(y>=0) phi = acos(x/r);
else phi = 2.0*PI - acos(x/r);
if(r==0){
theta = 0;
phi = 0;
}
double R = 2.0/pow(double(n),2) * sqrt(Factorial(n-l-1)/Factorial(n+l)) * exp(-r/double(n)) * pow( 2.0*r/double(n),l) *Laguerre(2*l+1, n-l-1, 2.0*r/double(n)) ;
double Y = pow(-1.0, m) * sqrt(double(l)+1.0/2.0) * Factorial(l-m)/Factorial(l+m) * Legendre(m,l,cos(theta));
if(m==0) {
fout_s << x << " " << y << " " << R * Y << endl;
}else{
double Yx = Y * cos(double(m)*phi);
double Yy = Y * sin(double(m)*phi);
fout_s << x << " " << y << " " << R * Yx << " " << R * Yy << endl ;
}
}
fout_s << "" << endl;
}
fout_s.close();
}
}
}
}
double Laguerre( int k, int n, double x)
{
double sum=0;
for(int m=0; m<=n; m++){
sum += pow(-x,m)*Factorial(n+k)/Factorial(n-m)/Factorial(k+m)/Factorial(m);
}
return sum;
}
double Legendre( int m, int l, double x)
{
int mm = abs(m);
if( mm>l ) return 0;
double r0, r1, r2;
r0 = 0.0;
r1 = pow(1.0-x*x, double(mm)/2.0) * Factorial(2*mm-1, 2);
if( mm==l && m>=0) return r1;
if( mm==l && m<0) return r1 * pow(-1.0,mm) * Factorial(l-mm)/Factorial(l+mm) ;
for(int ll = mm+1; ll<=l; ll++ ){
r2 = ((2.0*ll-1.0)*x*r1 - (ll+mm-1.0)*r0)/(ll-mm);
r0 = r1;
r1 = r2;
}
if(m>=0) return r2;
else return r2 * pow(-1.0,mm) * Factorial(l-mm)/Factorial(l+mm) ;
}
double Factorial(int n)
{
if( n<=0 ) return 1.0;
double F = 1.0;
for(int i=n; i>=2; i--){
F *= double(i);
}
return F;
}
double Factorial(int n, int m)
{
if( n<=0 ) return 1.0;
double F = 1.0;
for(int i=n; i>=2; i=i-m){
F *= double(i);
}
return F;
}
gnuplotテンプレート
上のプログラムで出力したデータを用いて、gif描画用のgnuplotテンプレートです。
set pm3d ## 3次元カラー表示 set pm3d map ## カラーマップ表示 set pm3d interpolate 5, 5 ## 補間 set ticslevel 10 set nokey set tics font 'Times,14' set size square ######################################################## ## gif ファイル出力の場合 ############################## ######################################################## set terminal gif optimize size 600, 480 cb = 1 ##<---------カラーバーの指定 set cbrange[-cb:cb] set palette defined ( -cb "blue" , 0 "black", cb "red") L=5.2 set xr[-L:L] ##<---------描画x軸の範囲 set yr[-L:L] ##<---------描画y軸の範囲 set output 'Psi1_0_0.gif' splot "Psi1_0_0.data" u 1:2:3 with pm3d L=10 set xr[-L:L] ##<---------描画x軸の範囲 set yr[-L:L] ##<---------描画y軸の範囲 cb = 0.2 ##<---------カラーバーの指定 set cbrange[-cb:cb] set palette defined ( -cb "blue" , 0 "black", cb "red") set output 'Psi2_0_0.gif' splot "Psi2_0_0.data" u 1:2:3 with pm3d cb = 0.1 ##<---------カラーバーの指定 set cbrange[-cb:cb] set palette defined ( -cb "blue" , 0 "black", cb "red") set output 'Psi2_1_0.gif' splot "Psi2_1_0.data" u 1:2:3 with pm3d set output 'Psi2_1_x.gif' splot "Psi2_1_1.data" u 1:2:3 with pm3d set output 'Psi2_1_y.gif' splot "Psi2_1_1.data" u 1:2:4 with pm3d cb = 0.03 ##<---------カラーバーの指定 set cbrange[-cb:cb] set palette defined ( -cb "blue" , 0 "black", cb "red") L=20 set xr[-L:L] ##<---------描画x軸の範囲 set yr[-L:L] ##<---------描画y軸の範囲 set output 'Psi3_0_0.gif' splot "Psi3_0_0.data" u 1:2:3 with pm3d set output 'Psi3_1_0.gif' splot "Psi3_1_0.data" u 1:2:3 with pm3d set output 'Psi3_1_x.gif' splot "Psi3_1_1.data" u 1:2:3 with pm3d set output 'Psi3_1_y.gif' splot "Psi3_1_1.data" u 1:2:4 with pm3d set output 'Psi3_2_0.gif' splot "Psi3_2_0.data" u 1:2:3 with pm3d cb = 0.01 ##<---------カラーバーの指定 set cbrange[-cb:cb] set palette defined ( -cb "blue" , 0 "black", cb "red") set output 'Psi3_2_x.gif' splot "Psi3_2_1.data" u 1:2:3 with pm3d set output 'Psi3_2_y.gif' splot "Psi3_2_1.data" u 1:2:4 with pm3d set output 'Psi3_2_2x.gif' splot "Psi3_2_2.data" u 1:2:3 with pm3d set output 'Psi3_2_2y.gif' splot "Psi3_2_2.data" u 1:2:4 with pm3d L=30 set xr[-L:L] ##<---------描画x軸の範囲 set yr[-L:L] ##<---------描画y軸の範囲 cb = 0.02 ##<---------カラーバーの指定 set cbrange[-cb:cb] set palette defined ( -cb "blue" , 0 "black", cb "red") set output 'Psi4_0_0.gif' splot "Psi4_0_0.data" u 1:2:3 with pm3d set output 'Psi4_1_0.gif' splot "Psi4_1_0.data" u 1:2:3 with pm3d set output 'Psi4_1_x.gif' splot "Psi4_1_1.data" u 1:2:3 with pm3d set output 'Psi4_1_y.gif' splot "Psi4_1_1.data" u 1:2:4 with pm3d set output 'Psi4_2_0.gif' splot "Psi4_2_0.data" u 1:2:3 with pm3d cb = 0.005 set cbrange[-cb:cb] set palette defined ( -cb "blue" , 0 "black", cb "red") set output 'Psi4_2_x.gif' splot "Psi4_2_1.data" u 1:2:3 with pm3d set output 'Psi4_2_y.gif' splot "Psi4_2_1.data" u 1:2:4 with pm3d set output 'Psi4_2_2x.gif' splot "Psi4_2_2.data" u 1:2:3 with pm3d set output 'Psi4_2_2y.gif' splot "Psi4_2_2.data" u 1:2:4 with pm3d L=40 set xr[-L:L] ##<---------描画x軸の範囲 set yr[-L:L] ##<---------描画y軸の範囲 set output 'Psi4_3_0.gif' splot "Psi4_3_0.data" u 1:2:3 with pm3d set output 'Psi4_3_x.gif' splot "Psi4_3_1.data" u 1:2:3 with pm3d set output 'Psi4_3_y.gif' splot "Psi4_3_1.data" u 1:2:4 with pm3d cb = 0.001 set cbrange[-cb:cb] set palette defined ( -cb "blue" , 0 "black", cb "red") set output 'Psi4_3_2x.gif' splot "Psi4_3_2.data" u 1:2:3 with pm3d set output 'Psi4_3_2y.gif' splot "Psi4_3_2.data" u 1:2:4 with pm3d set output 'Psi4_3_3x.gif' splot "Psi4_3_3.data" u 1:2:3 with pm3d set output 'Psi4_3_3y.gif' splot "Psi4_3_3.data" u 1:2:4 with pm3d
・C++言語
・gnuplotテンプレート
・イラストレータ
計算結果
上のgnuplot テンプレートで出力した gif データです。
の平面での波動関数の振幅に比例する確率分布による点描画です。
主量子数:
(K殻)
主量子数:
(L殻)
主量子数:
(M殻)
主量子数:
(N殻)




