@@ -1811,13 +1811,17 @@ def user_defined(self, line_num: int, line: str) -> None:
18111811 Examples
18121812 --------
18131813 >>> '@rxn Reactant --> Product: define rate equation here'
1814+ >>> '@rxn A --> 0: p[Vdeg] * u[A] * u[B] / (p[Kdeg] + u[A])'
1815+ >>> '@rxn E + S --> E + P: p[Vmax] * u[E] * u[S] / (p[Km] + u[S])'
18141816
18151817 Notes
18161818 -----
18171819 * Use p[xxx] and u[xxx] for describing parameters and species, respectively.
18181820
18191821 * Use '0' or '∅' for degradation/creation to/from nothing.
18201822
1823+ * Multiple reactants and products can be described by using '+'.
1824+
18211825 * Differential equation
18221826 .. math::
18231827
@@ -1833,52 +1837,79 @@ def user_defined(self, line_num: int, line: str) -> None:
18331837 rate_equation = description [1 ].strip ()
18341838 for arrow in self .fwd_arrows :
18351839 if arrow in balance :
1836- reactant , product = balance .split (arrow )
1837- if reactant in self .nothing :
1838- self ._set_species (product .strip ())
1839- elif product in self .nothing :
1840- self ._set_species (product .strip ())
1840+ two_species = balance .split (arrow )
1841+ # reactant, product = balance.split(arrow)
1842+ if len (two_species [0 ].split (" " )) > 1 and "+" not in two_species [0 ]:
1843+ reactants = None
1844+ else :
1845+ reactants = [
1846+ reactant
1847+ for reactant in two_species [0 ].replace (" " , "" ).split ("+" )
1848+ if reactant not in self .nothing
1849+ ]
1850+ if len (two_species [1 ].split (" " )) > 1 and "+" not in two_species [1 ]:
1851+ products = None
18411852 else :
1842- self ._set_species (reactant .strip (), product .strip ())
1853+ products = [
1854+ product
1855+ for product in two_species [1 ].replace (" " , "" ).split ("+" )
1856+ if product not in self .nothing
1857+ ]
18431858 break
18441859 else :
18451860 raise ArrowError (f"line{ line_num :d} : Use one of { ', ' .join (self .fwd_arrows )} ." )
1861+ if reactants is None or products is None :
1862+ raise DetectionError (f"Unregistered words in line{ line_num :d} : { line } " )
1863+ if len (reactants ) == 0 :
1864+ self ._set_species (* products )
1865+ elif len (products ) == 0 :
1866+ self ._set_species (* reactants )
1867+ else :
1868+ self ._set_species (* reactants , * products )
18461869 rate_equation = (
18471870 rate_equation .replace ("p[" , "x[C." ).replace ("u[" , "y[V." ).replace ("^" , "**" )
18481871 )
18491872 self .reactions .append (f"v[{ line_num :d} ] = " + rate_equation .strip ())
1873+ modulators = set (reactants ) & set (products )
1874+ for modulator in modulators :
1875+ reactants .remove (modulator )
1876+ products .remove (modulator )
18501877 modulators = (
18511878 * list (
18521879 set (
18531880 [
18541881 ent
18551882 for ent in re .findall (r"(?<=\[V.)(.+?)(?=\])" , rate_equation )
1856- if ent not in [reactant , product ]
1883+ if ent not in [* reactants , * products ]
18571884 ]
18581885 )
1886+ | modulators
18591887 ),
18601888 )
18611889 self .kinetics .append (
18621890 KineticInfo (
1863- () if reactant in self . nothing else ( reactant , ),
1864- () if product in self . nothing else ( product , ),
1865- () if modulators is None else (modulators ),
1891+ tuple ( reactants ),
1892+ tuple ( products ),
1893+ tuple (modulators ),
18661894 rate_equation .replace ("x[C." , "" ).replace ("y[V." , "" ).replace ("]" , "" ),
18671895 )
18681896 )
1869- counter_reactant = 0
1870- counter_product = 0
1871- for i , eq in enumerate (self .differential_equations ):
1872- if f"dydt[V.{ reactant } ]" in eq and reactant not in self .nothing :
1873- counter_reactant += 1
1874- self .differential_equations [i ] = eq + f" - v[{ line_num :d} ]"
1875- elif f"dydt[V.{ product } ]" in eq and product not in self .nothing :
1876- counter_product += 1
1877- self .differential_equations [i ] = eq + f" + v[{ line_num :d} ]"
1878- if counter_reactant == 0 and reactant not in self .nothing :
1879- self .differential_equations .append (f"dydt[V.{ reactant } ] = - v[{ line_num :d} ]" )
1880- if counter_product == 0 and product not in self .nothing :
1881- self .differential_equations .append (f"dydt[V.{ product } ] = + v[{ line_num :d} ]" )
1897+ for reactant in reactants :
1898+ counter_reactant = 0
1899+ for i , eq in enumerate (self .differential_equations ):
1900+ if f"dydt[V.{ reactant } ]" in eq :
1901+ counter_reactant += 1
1902+ self .differential_equations [i ] = eq + f" - v[{ line_num :d} ]"
1903+ if counter_reactant == 0 :
1904+ self .differential_equations .append (f"dydt[V.{ reactant } ] = - v[{ line_num :d} ]" )
1905+ for product in products :
1906+ counter_product = 0
1907+ for i , eq in enumerate (self .differential_equations ):
1908+ if f"dydt[V.{ product } ]" in eq :
1909+ counter_product += 1
1910+ self .differential_equations [i ] = eq + f" + v[{ line_num :d} ]"
1911+ if counter_product == 0 :
1912+ self .differential_equations .append (f"dydt[V.{ product } ] = + v[{ line_num :d} ]" )
18821913
18831914 def _extract_event (self , line_num : int , line : str ):
18841915 # About biochemical event
0 commit comments