
| ホームページ | 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]で始めてください。