ホームページ | Pirikaで化学 | ブログ | 業務案内 | お問い合わせ |
Pirikaで化学トップ | 情報化学+教育 | HSP | 化学全般 |
情報化学+教育トップ | 情報化学 | MAGICIAN | MOOC | プログラミング |
プログラミング・トップ | 親子で | JavaScript演習 | 化学系 | HSPiP用 |
2011.3.11
最も簡単な逐次反応は,
A→B, r1=k1Ca
B→C, r2=k2Cb
と書くことができます。
各成分の濃度を表わす微分方程式は次のようになります。
dCa/dt = -k1Ca
dCb/dt =k1Ca - k2Cb
dCc/dt = k2Cb
プログラムの要素には、反応速度k1とk2の値が入るテキスト・フィールドが2つと計算を実行するボタン、さらに計算結果を描画するCanvsを持ったformが定義されています。
<form>
<p>k1の値:
<input type="text" value="0.05" name="num1" />
<br />
k2の値:
<input type="text" value="0.005" name="num2" />
<br />
<input type="button" value="計算する" onclick="calc(this.form)" />
<br />
<canvas id="imgCanvas" width="340" height="200"></canvas>
<br />
</p>
</form>
キャンバスのサイズは、幅が340、高さが200に設定されています。
このぐらいのサイズであれば、どのようなデバイスであっても十分表示できるでしょう。
しかし、どのようなデバイスであっても対応できるようにするためには、ウインドウサイズをとって、それを元に幅と高さを定める方が好ましいです。
それを回避するには、
<canvas id="imgCanvas" ></canvas>
のように入れ物だけ定義して、サイズはプログラムを実行するときに定めます。
function calc(fObj)
{
var sW, sH;
sW = window.innerWidth;
sH = window.innerHeight;
sW=sW*2/3;
if(sW>500){
sW=500;
}
if(sW/2 < sH){
sH=sW/2;
}
最初に呼ばれる関数の中で、windowの中身の幅と高さを求めます。
window一杯に書いてしまうと格好悪いので描画の幅はその2/3にすることにします。
そして、非常に大きい画面を使っていたりした時に、そのpix数が500以上だったら500を最大にします。
そして高さは、幅の半分にします。
canvasObj = document.getElementById("imgCanvas");
conObj = canvasObj.getContext("2d");
conObj.canvas.width =sW;
conObj.canvas.height =sH;
そして、formで定義したimgCanvasのオブジェクトを取り出して、キャンバスの幅と高さを設定します。
(本来はユーザーがブラウザーの幅や高さを変更したら、この部分を呼び出すように設定します。)
キャンバスを扱う時には、キャンバスオブジェクトを取り出し、2dコンテキストのオブジェクトを作成します。
これはおまじないのように覚えた方が良いでしょう。
今回の場合は描きたいのは、各成分の濃度の時間変化です。
各時間に対する濃度を配列で持たせることにします。
そのような、値を格納する変数を配列と呼びます。
時間がaの時の濃度をCaObj[a]などとします。
それを、時間を0から150まで保存する変数を用意します。
その定義の仕方もおまじないとして覚えた方が楽です。
CaObj= new Array(151);
CrObj= new Array(151);
CsObj= new Array(151);
この変数の中身に何を入れるのかは、ここではオイラー法で計算した中身を入れます。
そして実際に行うのは、
「時間0の時の値と時間1の時の値の線を引く」
を繰り返すだけです。
まず描画の下準備です。
キャンバスの中身を消去(clearRect)して描く線のスタイル(strokeStyle)を設定して外枠の四角形を描き(strokeRect)ます。
var sWs=sW-40;
var sHs=sH-40;
conObj.clearRect(0,0,sW,sH)
conObj.strokeStyle = "rgba(0,0,0,1)";
conObj.strokeRect(20,20,sWs,sHs)
ウインドウのサイズが変わっても対応できるように、相対値で指定します。
JavaScriptのCanvaへの描画は、beginPath()
で始まり、closePath()
で終わります。
その間で記録されていたものが、stroke
命令で実際に描かれます。
ここで使う命令は、moveToとlineToです。
細かく分けた部分部分を線で繋ぐと、曲線に見えます。
conObj.beginPath();
conObj.strokeStyle = "rgba(255,0,0,1)";
for(i=1;i<150;i++){
conObj.moveTo((i-1)*sWs/150+20,CaObj[i-1]+20);
conObj.lineTo(i*sWs/150+20,CaObj[i]+20);
}
conObj.closePath();
conObj.stroke();
プログラムの説明(▶︎をクリックして開く)
これをオイラー法で解くJavaScriptを書きましょう。
このプログラムは2008年2月にDashCodeを使って書いたものです。
この時のDashCodeはマックのダッシュボードというウイジットとしてしか動きませんでしたが、ウイジットの中身はHTML5+CSS+JavaScriptなので、簡単に取り出す事ができます。
そして作り直したのがこのページです。
赤い線はA成分が時間と共に減少していく様子を示しています。
A成分が減少した分、B成分(緑の線)ができますが、BはさらにC成分(青い線)に変わるので、途中から減少します。
もし、欲しい成分がCならどんどん反応させればいいのですが、B成分が欲しいなら、緑色の山の頂上で反応を止めなくてはなりません。
さもなければ、反応速度定数に差が出るような、実験条件を探さなくてはなりません。
例えば、温度を変える、圧力を変える、pHを変えるなどして、B->Cの反応速度定数を小さく出来れば、2つ目の図のようにBの収率を上げることができます。
K1、K2の値をいろいろ変えて試してみていただきたい。
K1=K2=0.05
K1=0.05, k2=0.005
K1=0.05, K2=0.5
最終的なJavaScriptプログラム(▶︎をクリックして開く)
<script type="text/javascript"><!--
CaObj= new Array(151);
CrObj= new Array(151);
CsObj= new Array(151);
function calc(fObj)
{
var sW, sH;
sW = window.innerWidth;
sH = window.innerHeight;
sW=sW*2/3;
if(sW>500){
sW=500;
}
if(sW/2 < sH){
sH=sW/2;
}
k1 = parseFloat(fObj.num1.value);
k2 = parseFloat(fObj.num2.value);
A0 = 4.0;
R0 = 0.0;
S0 = 0.0;
DT=1;
TN=150;
canvasObj = document.getElementById("imgCanvas");
conObj = canvasObj.getContext("2d");
conObj.canvas.width =sW;
conObj.canvas.height =sH;
var sWs=sW-40;
var sHs=sH-40;
conObj.clearRect(0,0,sW,sH)
conObj.strokeStyle = "rgba(0,0,0,1)";
conObj.strokeRect(20,20,sWs,sHs)
//オイラー法
CA=A0;
CR=R0;
CS=S0;
CaObj[0]=sHs-Math.round(CA*sHs/4);
CrObj[0]=sHs-Math.round(CR*sHs/4);
CsObj[0]=sHs-Math.round(CS*sHs/4);
for(i=1;i<150;i++){
DA=-DT*CA*k1;
DR=DT*(k1*CA-k2*CR);
DS=DT*k2*CR;
CA=CA+DA;
CR=CR+DR;
CS=CS+DS;
CaObj[i]=sHs-Math.round(CA*sHs/4);
CrObj[i]=sHs-Math.round(CR*sHs/4);
CsObj[i]=sHs-Math.round(CS*sHs/4);
}
conObj.beginPath();
conObj.strokeStyle = "rgba(255,0,0,1)";
for(i=1;i<150;i++){
conObj.moveTo((i-1)*sWs/150+20,CaObj[i-1]+20);
conObj.lineTo(i*sWs/150+20,CaObj[i]+20);
}
conObj.closePath();
conObj.stroke();
conObj.beginPath();
conObj.strokeStyle = "rgba(0,255,0,1)";
for(i=1;i<150;i++){
conObj.moveTo((i-1)*sWs/150+20,CrObj[i-1]+20);
conObj.lineTo(i*sWs/150+20,CrObj[i]+20);
}
conObj.closePath();
conObj.stroke();
conObj.beginPath();
conObj.strokeStyle = "rgba(0,0,255,1)";
for(i=1;i<150;i++){
conObj.moveTo((i-1)*sWs/150+20,CsObj[i-1]+20);
conObj.lineTo(i*sWs/150+20,CsObj[i]+20);
conObj.stroke();
}
conObj.closePath();
conObj.stroke();
}
// --></script>
各曲線の値を読み取って表示できるように改造してください。
キャンバスの大きさが変わるので、読み取ったマウスの位置を横軸(時間)縦軸(濃度)に合わせて変換しなければなりません。
次のプログラムが参考になるでしょう。
Copyright pirika.com since 1999-
Mail: yamahiroXpirika.com (Xを@に置き換えてください) メールの件名は[pirika]で始めてください。