{
 "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",
    "6) Kinetics of Polyesterification: A Study of the Effects of Molecular Weight and Viscosity on Reaction Rate\n",
    "Paul J. Flory, Journal of the American Chemical Society 1939 61 (12), 3334-3340, DOI: 10.1021/ja01267a030\n",
    "https://doi.org/10.1021/ja01267a030  \n",
    "\n",
    "Table Iのデータに以下の処理を施した。内容はref6.xlsxを参照のこと。  \n",
    "- diethylene glycol (DEG: MW 106.12), adipic acid (AdA: MW 146.14)とした。CRUのMWは 106.12 + 146.12 - 18.02 x 2 = 216.22とした。  \n",
    "- 初期濃度t = 0は、モノマーであるDEGとAdAが、1,000 g 中に等物質量ずつ含まれているとした。ともに3.964 molずつで、 DEG (420.7 g)、AdA (579.3 g) になる。  \n",
    "- polyesterのDPnからMnを算出した。DPn=0の場合を除き、Mn = DPn x 216.22 + 18.02 とした。  \n",
    "- 触媒の重量は無視できるとした。  \n",
    "- 各反応時間でのモノマー残量は表中のρ（転嫁率に相当）から算出した。  \n",
    "- 発生した水は速やかに系から除去されるとし、その重量をρから算出した。t=0での総重量 1,000 gから、発生した水の重量を減じ、各時間での総重量を求めた。  \n",
    "- DEG, AdA以外はすべてpolyesterと考えてその重量を算出し、各時間でのMnから主査の物質量とした。これを各時間の総重量で割り、polyesterの濃度を算出した。  \n",
    "- 各時間での、各化学種の物質量を、その時点での総重量で除し、濃度とした。  \n",
    "\n",
    "解析は、Mnの成長速度を見込むことを目標にした。成長の速度定数kpは、モノマー同士の速度定数kmのと時間の関数として表現でき、ある時間でのkpは  \n",
    "kp = km x exp(-a*t)  \n",
    "で表されると仮定した。重合の初期速度はモノマー同士の反応と等しい、という考え方である。  \n",
    "よって、フィッティングでは、kmとaを求めることとなる。  \n",
    "この際、以下の対応をしている。  \n",
    "- 現行versionのrxnfitでは、出発物のみの半反応は扱うことができるが、生成物のみの半反応を扱うことができない。このためダミー中間体として \"olg\" （オリゴマー）を設定し、その濃度はt=0で0、他は全て未知（空欄）とした。  \n",
    "- Mnは1e3、AdAやDEGの濃度は1e-3  の桁でレンジが異なりすぎるため、Mnを1e-6倍してフィッティングした。  \n",
    "\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/ref6/sample_rxn_ref6.csv'  # CSVファイルのパスを指定\n",
    "file_path_k = './sample_data/ref6/sample_k_ref6.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(\n",
    "    file_path_rxn, \n",
    "    rate_const_overrides=file_path_k) # 時間の関数で表現される速度定数の定義場所を指定  "
   ]
  },
  {
   "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: 4\n",
      "unique species: ['DEG', 'AdA', 'olg', 'Mn']\n",
      "rate constant: {'km': km, 'kp': km*exp(-a*t)}\n",
      "\n",
      "=== debug info ===\n",
      "Successfully created function for DEG with args: ['t', 'DEG', 'AdA', 'olg', 'Mn']\n",
      "Successfully created function for AdA with args: ['t', 'DEG', 'AdA', 'olg', 'Mn']\n",
      "Successfully created function for olg with args: ['t', 'DEG', 'AdA', 'olg', 'Mn']\n",
      "Successfully created function for Mn with args: ['t', 'DEG', 'AdA', 'olg', 'Mn']\n",
      "order of args: ['t', 'DEG', 'AdA', 'olg', 'Mn']\n",
      "system of ODE: {'DEG': '-1/1*km*DEG(t)*AdA(t)', 'AdA': '-1/1*km*DEG(t)*AdA(t)', 'olg': '1/1*km*DEG(t)*AdA(t)-1/1*kp*olg(t)', 'Mn': '1/1*kp*olg(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 DEG with args: ['t', 'DEG', 'AdA', 'olg', 'Mn']\n",
      "Successfully created function for AdA with args: ['t', 'DEG', 'AdA', 'olg', 'Mn']\n",
      "Successfully created function for olg with args: ['t', 'DEG', 'AdA', 'olg', 'Mn']\n",
      "Successfully created function for Mn with args: ['t', 'DEG', 'AdA', 'olg', 'Mn']\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[Eq(Derivative(DEG(t), t), -km*AdA(t)*DEG(t)),\n",
       " Eq(Derivative(AdA(t), t), -km*AdA(t)*DEG(t)),\n",
       " Eq(Derivative(olg(t), t), km*AdA(t)*DEG(t) - km*olg(t)*exp(-a*t)),\n",
       " Eq(Derivative(Mn(t), t), km*olg(t)*exp(-a*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": [
      "{'km': km, 'kp': km*exp(-a*t)}\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[sympy.core.symbol.Symbol, sympy.core.mul.Mul]"
      ]
     },
     "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,    6,   12,   23,   37,   59,   88,  129,  170,  203,  235,\n",
       "           270,  321,  397,  488,  596,  690,  793,  900, 1008, 1147, 1370,\n",
       "          1606]),\n",
       "   array([   0,    6,   12,   23,   37,   59,   88,  129,  170,  203,  235,\n",
       "           270,  321,  397,  488,  596,  690,  793,  900, 1008, 1147, 1370,\n",
       "          1606]),\n",
       "   array([], dtype=int64),\n",
       "   array([   0,    6,   12,   23,   37,   59,   88,  129,  170,  203,  235,\n",
       "           270,  321,  397,  488,  596,  690,  793,  900, 1008, 1147, 1370,\n",
       "          1606])],\n",
       "  [array([0.00396416, 0.00345151, 0.00303863, 0.00257493, 0.00206539,\n",
       "          0.00162451, 0.00130685, 0.0010418 , 0.00088474, 0.00077414,\n",
       "          0.00069599, 0.00063306, 0.00056121, 0.0004921 , 0.00043458,\n",
       "          0.00038832, 0.00035504, 0.00033101, 0.00030864, 0.00029597,\n",
       "          0.00027442, 0.00025286, 0.00023508]),\n",
       "   array([0.00396416, 0.00345151, 0.00303863, 0.00257493, 0.00206539,\n",
       "          0.00162451, 0.00130685, 0.0010418 , 0.00088474, 0.00077414,\n",
       "          0.00069599, 0.00063306, 0.00056121, 0.0004921 , 0.00043458,\n",
       "          0.00038832, 0.00035504, 0.00033101, 0.00030864, 0.00029597,\n",
       "          0.00027442, 0.00025286, 0.00023508]),\n",
       "   array([], dtype=float64),\n",
       "   array([0.        , 0.00026883, 0.00030516, 0.00035986, 0.00045024,\n",
       "          0.00056938, 0.00070776, 0.00088722, 0.00104506, 0.00119426,\n",
       "          0.00132831, 0.00146021, 0.00164616, 0.00187751, 0.00212617,\n",
       "          0.00237914, 0.00259969, 0.00278996, 0.00299537, 0.00311645,\n",
       "          0.00336467, 0.00365268, 0.00392728])])]"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "file_path_data = './sample_data/ref6/sample_timecourse_ref6.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": [],
   "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"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "4a746158",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "p0の変数順 (symbolic_rate_consts): ['a', 'km']\n",
      "p0の要素数: 2\n"
     ]
    }
   ],
   "source": [
    "# p0 に入力する変数の順番を表示（run_fit の前に確認用）\n",
    "keys = builded_rxnode.get_symbolic_rate_const_keys()\n",
    "print(\"p0の変数順 (symbolic_rate_consts):\", keys)\n",
    "print(\"p0の要素数:\", len(keys))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "5f0df241",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Successfully created function for DEG with args: ['t', 'DEG', 'AdA', 'olg', 'Mn']\n",
      "Successfully created function for AdA with args: ['t', 'DEG', 'AdA', 'olg', 'Mn']\n",
      "Successfully created function for olg with args: ['t', 'DEG', 'AdA', 'olg', 'Mn']\n",
      "Successfully created function for Mn with args: ['t', 'DEG', 'AdA', 'olg', 'Mn']\n",
      "Optimization success: True\n",
      "Fitted rate constants:\n",
      "  a = 1e-10\n",
      "  km = 2.00317\n",
      "Residual sum of squares: 3.12456e-05  R²: 0.641543\n"
     ]
    }
   ],
   "source": [
    "# 未知の速度定数の初期値\n",
    "result, param_info, fit_metrics = fit_sci.run_fit(p0={\"a\": 5e-2, \"km\": 2})  # initial guesses for symbolic rate constants"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "4b1e4ca9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "=== Time-course plot ===\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 51932 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",
      "  AdA: 0.000288\n",
      "  olg: -0.000000\n",
      "  Mn: 0.003676\n"
     ]
    }
   ],
   "source": [
    "# 経時変化を図示（plot_fitted_solution を使用）\n",
    "fit_sci.plot_fitted_solution(expdata_df=df1, species=['AdA', 'olg', 'Mn'])"
   ]
  }
 ],
 "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
}
