{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "bed4637d-8e0c-42b4-9331-8a39ca2381a6",
   "metadata": {},
   "source": [
    "Copyright (c) 2025 Mitsuru Ohno  \n",
    "Use of this source code is governed by a BSD-3-style  \n",
    "license that can be found in the LICENSE file.  "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "911d53c0",
   "metadata": {},
   "source": [
    "## 当ノートブックのワークフロー  \n",
    "複数の化学種が関与し、経時変化データの一部に欠損がある場合の事例  \n",
    "1. 未知の速度定数を含む、csvに書き込んだ反応式を読み込む。  \n",
    "2. 化学種の濃度の経時変化の実験データを読み込む。実験データには欠損を含んでもよい。  \n",
    "2. RxnIVPsolv(\"sample_data.csv\")でインスタンス化し、化学種ごとの微分型の反応速度式を作成する。速度定数が未知の素反応にはシンボリックな変数が割り振られる。  \n",
    "3. 作成した微分方程式を、数値解析可能な形式にする  \n",
    "4.  scipy.optimize.minimizeを使い、化学種ごとの濃度の経時変化が、実験値と近づくように、未知の速度定数を求める： get_ode_system()で必要なオブジェクトを取得。  \n",
    "5. 経時変化を図示する： matplotlibで結果をプロット  \n",
    "\n",
    "もしエラーが発生した場合は、debug_ode_system()メソッドで詳細な情報を確認できる。  \n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3eb47a04-e039-412a-a2bc-4e9fc632e0ce",
   "metadata": {},
   "source": [
    "## 引用文献  \n",
    "4) Tutomu SUZUKI, Kazuhiro SUYAMA, Sadao SUZUKI, The Kinetics of the Formation and the Decomposition of Mono-chloro Peracetic Acid, Nippon kagaku zassi, 1964, Volume 85, Issue 11, Pages 802-805,A61, Released on J-STAGE May 30, 2011, Online ISSN 2185-0917, Print ISSN 0369-5387, https://doi.org/10.1246/nikkashi1948.85.11_802,  \n",
    "https://www.jstage.jst.go.jp/article/nikkashi1948/85/11/85_11_802/_article/-char/en  \n",
    "\n",
    "- 文献中式 (1), (2), (3) の反応式および表1のデータを用いた。  \n",
    "- 経時変化データの一部に欠損があるデータセットでのフィッティングを例示した。  "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f793b762-0cca-45f9-ad15-a0c092ac36e6",
   "metadata": {},
   "source": [
    "## 反応式を記載したcsvファイルを指定する  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "92b6caf1-fbe4-47c8-9bf7-15dc223b3b28",
   "metadata": {},
   "outputs": [],
   "source": [
    "file_path_rxn = './sample_data/ref4/sample_rxn_ref4.csv'  # CSVファイルのパスを指定"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "95324112-077e-4f27-9529-2a86aaa179f2",
   "metadata": {},
   "source": [
    "## 反応速度式をscipy.integrate.solve_ivpで処理できる連立微分方程式にする  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "75e2680b-f29a-4109-8707-97deb1943716",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "from rxnfit import RxnODEbuild, SolverConfig, RxnODEsolver\n",
    "from rxnfit.expdata_reader import expdata_read\n",
    "from rxnfit.expdata_fit_sci import ExpDataFitSci\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e182c934",
   "metadata": {},
   "source": [
    "# 反応速度式の作成"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "aeb55f6f-83d3-4689-9921-c0f9c3c7216f",
   "metadata": {},
   "outputs": [],
   "source": [
    "builded_rxnode = RxnODEbuild(file_path_rxn)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "dc5a5d88-0b5d-4f67-bd1c-81209bae4d0a",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "number of species: 5\n",
      "unique species: ['ClCH2COOH', 'H2O2', 'ClCH2COOOH', 'H2O', 'O2']\n",
      "rate constant: {'k1': k1, 'k2': k2, 'k3': k3, 'k4': k4}\n",
      "\n",
      "=== debug info ===\n",
      "Successfully created function for ClCH2COOH with args: ['t', 'ClCH2COOH', 'H2O2', 'ClCH2COOOH', 'H2O', 'O2']\n",
      "Successfully created function for H2O2 with args: ['t', 'ClCH2COOH', 'H2O2', 'ClCH2COOOH', 'H2O', 'O2']\n",
      "Successfully created function for ClCH2COOOH with args: ['t', 'ClCH2COOH', 'H2O2', 'ClCH2COOOH', 'H2O', 'O2']\n",
      "Successfully created function for H2O with args: ['t', 'ClCH2COOH', 'H2O2', 'ClCH2COOOH', 'H2O', 'O2']\n",
      "Successfully created function for O2 with args: ['t', 'ClCH2COOH', 'H2O2', 'ClCH2COOOH', 'H2O', 'O2']\n",
      "order of args: ['t', 'ClCH2COOH', 'H2O2', 'ClCH2COOOH', 'H2O', 'O2']\n",
      "system of ODE: {'ClCH2COOH': '-1/1*k1*ClCH2COOH(t)*H2O2(t)+1/1*k2*ClCH2COOOH(t)*H2O(t)+1/1*k3*ClCH2COOOH(t)', 'H2O2': '-1/1*k1*ClCH2COOH(t)*H2O2(t)+1/1*k2*ClCH2COOOH(t)*H2O(t)-1/1*k4*H2O2(t)', 'ClCH2COOOH': '1/1*k1*ClCH2COOH(t)*H2O2(t)-1/1*k2*ClCH2COOOH(t)*H2O(t)-1/1*k3*ClCH2COOOH(t)', 'H2O': '1/1*k1*ClCH2COOH(t)*H2O2(t)-1/1*k2*ClCH2COOOH(t)*H2O(t)+1/1*k4*H2O2(t)', 'O2': '0.5/1*k3*ClCH2COOOH(t)+0.5/1*k4*H2O2(t)'}\n"
     ]
    }
   ],
   "source": [
    "builded_rxnode.get_ode_info(debug_info=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "e98f2a67",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Successfully created function for ClCH2COOH with args: ['t', 'ClCH2COOH', 'H2O2', 'ClCH2COOOH', 'H2O', 'O2']\n",
      "Successfully created function for H2O2 with args: ['t', 'ClCH2COOH', 'H2O2', 'ClCH2COOOH', 'H2O', 'O2']\n",
      "Successfully created function for ClCH2COOOH with args: ['t', 'ClCH2COOH', 'H2O2', 'ClCH2COOOH', 'H2O', 'O2']\n",
      "Successfully created function for H2O with args: ['t', 'ClCH2COOH', 'H2O2', 'ClCH2COOOH', 'H2O', 'O2']\n",
      "Successfully created function for O2 with args: ['t', 'ClCH2COOH', 'H2O2', 'ClCH2COOOH', 'H2O', 'O2']\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[Eq(Derivative(ClCH2COOH(t), t), -k1*ClCH2COOH(t)*H2O2(t) + k2*ClCH2COOOH(t)*H2O(t) + k3*ClCH2COOOH(t)),\n",
       " Eq(Derivative(H2O2(t), t), -k1*ClCH2COOH(t)*H2O2(t) + k2*ClCH2COOOH(t)*H2O(t) - k4*H2O2(t)),\n",
       " Eq(Derivative(ClCH2COOOH(t), t), k1*ClCH2COOH(t)*H2O2(t) - k2*ClCH2COOOH(t)*H2O(t) - k3*ClCH2COOOH(t)),\n",
       " Eq(Derivative(H2O(t), t), k1*ClCH2COOH(t)*H2O2(t) - k2*ClCH2COOOH(t)*H2O(t) + k4*H2O2(t)),\n",
       " Eq(Derivative(O2(t), t), 0.5*k3*ClCH2COOOH(t) + 0.5*k4*H2O2(t))]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 作成した微分方程式\n",
    "builded_rxnode.get_ode_system()[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "1cfb1b8a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'k1': k1, 'k2': k2, 'k3': k3, 'k4': k4}\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[sympy.core.symbol.Symbol,\n",
       " sympy.core.symbol.Symbol,\n",
       " sympy.core.symbol.Symbol,\n",
       " sympy.core.symbol.Symbol]"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 速度定数の確認\n",
    "print(builded_rxnode.rate_consts_dict)\n",
    "\n",
    "check_type = [v for v in builded_rxnode.rate_consts_dict.values()]\n",
    "[type(e) for e in check_type]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a0f9e382",
   "metadata": {},
   "source": [
    "## 経時変化の実験データを読み込み　　\n",
    "### データフレーム化  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "6d74af85",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[([array([ 0,  1,  4,  9, 13, 18, 22, 25, 28, 31]),\n",
       "   array([ 0,  1,  9, 13, 18, 25, 31]),\n",
       "   array([ 0,  1,  4,  9, 18, 22, 25, 28, 31]),\n",
       "   array([ 0,  1,  4,  9, 13, 18, 22, 28, 31]),\n",
       "   array([ 0,  1,  4,  9, 18, 25, 28, 31])],\n",
       "  [array([2.945, 2.852, 2.477, 2.07 , 1.762, 1.356, 1.106, 0.88 , 0.707,\n",
       "          0.557]),\n",
       "   array([6.807, 6.614, 6.515, 6.569, 6.638, 6.697, 6.743]),\n",
       "   array([0.   , 0.193, 0.385, 0.292, 0.169, 0.11 , 0.086, 0.065, 0.063]),\n",
       "   array([13.99 , 14.083, 14.458, 14.867, 15.187, 15.557, 15.875, 16.205,\n",
       "          16.374]),\n",
       "   array([0.    , 0.    , 0.0415, 0.2925, 0.699 , 0.9705, 1.0645, 1.155 ])])]"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "file_path_data = './sample_data/ref4/sample_timecourse_ref4.csv' # データファイルのパス\n",
    "df1 = pd.read_csv(file_path_data)\n",
    "\n",
    "expdata_read([df1,])  # 複数データフレームのリストを渡すとまとめて読み込み可能"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6ef7e98f",
   "metadata": {},
   "source": [
    "### フィッティング  \n",
    "ExpDataFitSci でシンボリックな速度定数をフィッティング。  \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "a303dde2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Successfully created function for ClCH2COOH with args: ['t', 'ClCH2COOH', 'H2O2', 'ClCH2COOOH', 'H2O', 'O2']\n",
      "Successfully created function for H2O2 with args: ['t', 'ClCH2COOH', 'H2O2', 'ClCH2COOOH', 'H2O', 'O2']\n",
      "Successfully created function for ClCH2COOOH with args: ['t', 'ClCH2COOH', 'H2O2', 'ClCH2COOOH', 'H2O', 'O2']\n",
      "Successfully created function for H2O with args: ['t', 'ClCH2COOH', 'H2O2', 'ClCH2COOOH', 'H2O', 'O2']\n",
      "Successfully created function for O2 with args: ['t', 'ClCH2COOH', 'H2O2', 'ClCH2COOOH', 'H2O', 'O2']\n",
      "Optimization success: True\n",
      "Fitted rate constants:\n",
      "  k1 = 0.0114623\n",
      "  k2 = 0.0142805\n",
      "  k3 = 0.319184\n",
      "  k4 = 1e-10\n",
      "Residual sum of squares: 0.37345  R²: 0.975635\n"
     ]
    }
   ],
   "source": [
    "# ExpDataFitSci でフィッティング（t_range, df_list 必須）\n",
    "t_range = (float(df1.iloc[:, 0].min()), float(df1.iloc[:, 0].max()))\n",
    "fit_sci = ExpDataFitSci(builded_rxnode, [df1], t_range)\n",
    "result, param_info, fit_metrics = fit_sci.run_fit(p0={\"k1\": 0.001, \"k2\": 0.001, \"k3\": 0.001, \"k4\": 0.001})  # initial guesses for symbolic rate constants"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "4b1e4ca9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "=== Time-course plot ===\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 43544 characters]",
      "text/plain": [
       "<Figure size 600x400 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "=== Concentration at the final time point ===\n",
      "0:\n",
      "  H2O2: 0.748685\n",
      "  ClCH2COOOH: 0.113016\n",
      "  O2: 1.041649\n"
     ]
    }
   ],
   "source": [
    "# 経時変化を図示（plot_fitted_solution を使用）\n",
    "fit_sci.plot_fitted_solution(expdata_df=df1, species=['H2O2', 'ClCH2COOOH', 'O2'])"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.14.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
