pirika logo

ホームページ Pirikaで化学 ブログ 業務案内 お問い合わせ
Pirikaで化学トップ 情報化学+教育 HSP 化学全般
情報化学+教育トップ 情報化学 MAGICIAN MOOC プログラミング
MAGICIANとは、材料ゲノム(Materials Genome)、材料情報学(Materials Informatics)、情報化学(Chemo-Informatics)とネットワーク(Networks)を結びつけて(Associate)いかれる人材です。
プログラミング・トップ 親子で JavaScript演習 化学系 HSPiP用

化学系プログラミング > Javascript化学系演習 > 逐次回分反応

2011.3.11

最も簡単な逐次反応は,
A→B,  r1=k1Ca
B→C,  r2=k2Cb
と書くことができます。

各成分の濃度を表わす微分方程式は次のようになります。
dCa/dt = -k1Ca
dCb/dt =k1Ca - k2Cb
dCc/dt = k2Cb

逐次回文反応計算プログラム

k1の値:
k2の値:


プログラムの要素

プログラムの要素には、反応速度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>

課題

各曲線の値を読み取って表示できるように改造してください。
キャンバスの大きさが変わるので、読み取ったマウスの位置を横軸(時間)縦軸(濃度)に合わせて変換しなければなりません。

次のプログラムが参考になるでしょう。

キャンバス中のマウスの位置を読み取り表示

マウスの位置をToolTipsに表示


Copyright pirika.com since 1999-
Mail: yamahiroXpirika.com (Xを@に置き換えてください) メールの件名は[pirika]で始めてください。