# Radigen 🧪⚛️ **Radigen** is a compact Python kernel for simulating radical oxidation mechanisms in complex mixtures such as **edible oils**, **biofuels**, and **polymers**. It supports the **generative construction**, **parameterization**, and **numerical resolution** of complex chemical reaction networks, with full support for **dynamic thermal and oxygenation conditions**. > Radigen is part of the **Generative Simulation Initiative**, and is designed for **prompt-based modeling and LLM-driven scientific reasoning**. ![Radigen Illustration](docs/assets/radigen_banner.png) 🎨 Credits: Olivier Vitrac ------ ## 📚 Table of Contents - [📚🔬 1 | Scientific Foundation](#scientific-foundation) - [🧠🛠️ 2 | Core Capabilities](#core-capabilities) - [🤖🧠 3 | LLM Integration](#llm-integration) - [🚀📦 4 | Quick Start](#quick-start) - [⚗️ 5 | Example Scenarios](#example-scenarios) - [♾️⚗️ 6 | Combinatorial Radical Chemistry](#combinatorial-radical-chemistry) - [✚⚗️ 7 | Extending Radical Chemistry](#%e2%9c%9a-7-extending-radical-chemistry) - [📦📁 8 | Project Structure](#project-structure) - [🔧 9 | Installation](#installation) - [🧩📘 10 | Core Classes](#core-classes) - [🧺🧫 11 | Lumped Species](#lumped-species) - [🧬 12 | Chemistry Assumptions](#chemistry-assumptions) - [📜 License](#license) - [📣 Contact](#contact) ------ ## 📚🔬 1 | Scientific Foundation Radigen is based on two peer-reviewed frameworks for oxidation kinetics in unsaturated systems: 1. **Touffet M., Smith P., Vitrac O.**, *A comprehensive two-scale model for predicting the oxidizability of fatty acid methyl ester mixtures*, *Food Research International*, 173, 2023, 113289. https://doi.org/10.1016/j.foodres.2023.113289 2. **Touffet M., Vitrac O.**, *Temperature-dependent kinetics of unsaturated fatty acid methyl esters: Modeling autoxidation mechanisms*, *Food Chemistry*, 481, 2025, 143952. https://doi.org/10.1016/j.foodchem.2025.143952 ------ ## 🧠🛠️ 2 | Core Capabilities - ✅ Declarative **species and reaction network construction** - ✅ **Combinatorial generation** of mono/bimolecular reactions - ✅ **Cross-reaction inference** from self-reactions - ✅ **Rate constant assignment** from structured fingerprint database - ✅ **Temperature + viscosity effects** (Arrhenius & Smoluchowski) - ✅ **Cage ↔ free** hydroperoxide decomposition equilibrium - ✅ **O₂ transport** coupling via Henry's law and dissolution kinetics - ✅ Dynamic **temperature & kO₂ cycles** with `TKO2cycle` - ✅ **Chained simulations**, physical mixing of partially oxidized systems - ✅ High-level **plotting**, **DataFrame output**, and **lumped species** - ✅ Built-in support for **LLM prompts**, aiding scientific simulation ------ ## 🤖🧠 3 | LLM Integration Radigen was designed for **prompt-based simulation** and **LLM-assisted reasoning**. Its modular architecture allows: - Easy mapping from LLM prompt to chemical simulations - Access to symbolic names (e.g., `L1OOH`, `L2•`) and grouped observables - High-quality examples showcasing realistic oxidation scenarios - Support for mixing, renewal, dynamic profiles, and chemical interrogation > 💡 **Example Prompts**: > > - “*Simulate oxidation of linolenic acid at 180°C with oxygen limitation*.” > - “*What happens when abused oil is mixed with fresh oil?*” > - “*Generate a kinetic profile under cooling from 160°C to 40°C.*” > > ------ > ## 🚀📦 4 | Quick Start ```python from radigen3.oxidation import mixture, mixtureKinetics oil = mixture() oil.add("L1H", concentration=3000) oil.add("L1OOH", concentration=100) oil.add("O2", concentration=10) oil.addProducts() oil.addReactions() oil.populateReactionRates() oilmodel = mixtureKinetics(oil) oilmodel.solve((10, "days"), T=60) oilmodel.plot(["L1H", "L1OOH", "O2"]) ``` ------ ## ⚗️ 5 | Example Scenarios Radigen includes worked-out examples such as `simulate\_oil.py` and `test\_oxidation.py`, including: - **Scenario 1**: Oxidation at 140°C for 1 hour - **Scenario 2**: Long-term oxidation at 80°C for 100 hours - **Scenario 3**: Dynamic cycle (180°C → 40°C over days) - **Scenario 4**: Partial renewal (abused + pristine oil, then storage) - **Unit tests**: Oxidation under **anoxia** and with **oxygen excess** 🖼️ Each simulation includes ready-to-use `plot()` methods, with export support via `fig.print(filename, folder)`. ------ ## ♾️⚗️ 6 | Combinatorial Radical Chemistry ### Overview Radigen generates all reactions and paramaterized them according to combinatorial rules. The following script spans a reaction scheme involving **27 species** and **60 reactions**. ``` oil = mixture() oil.add("L1H",concentration=3000) oil.add("L2H",concentration=1000) oil.add("L3H",concentration=500) oil.add("L1OOH",concentration=100) oil.add("O2",concentration=10) oil.addProducts() oil.addReactions() oil.populateReactionRates() print("Considered reaction Scheme", "-"*30, oil.reactionScheme, sep="\n") oilmodel = mixtureKinetics(oil) # kinetic model oilmodel.solve((10,"days"),60) oilmodel.plot() df = oilmodel.results_as_dataframe(["L1H","L2H","L3H","L1OOH","L2OOH","L3OOH"]) print(df) ```
Click here to see results ⏱ **60 reactions** involving **27 species** were generated, including: ``` *R0: L1H + HO• → L1• + H2O, *R1: L1H + L1O• → L1• + L1OH, *R2: L1H + L1OO• → L1• + L1OOH, *R3: L1H + L2OO• → L1• + L2OOH, *R4: L1H + L3OO• → L1• + L3OOH, *R5: L1H + L2O• → L1• + L2OH, *R6: L1H + L3O• → L1• + L3OH, *R7: L2H + HO• → L2• + H2O, *R8: L2H + L1O• → L2• + L1OH, *R9: L2H + L1OO• → L2• + L1OOH, *R10: L2H + L2OO• → L2• + L2OOH, *R11: L2H + L3OO• → L2• + L3OOH, *R12: L2H + L2O• → L2• + L2OH, *R13: L2H + L3O• → L2• + L3OH, *R14: L3H + HO• → L3• + H2O, *R15: L3H + L1O• → L3• + L1OH, *R16: L3H + L1OO• → L3• + L1OOH, *R17: L3H + L2OO• → L3• + L2OOH, *R18: L3H + L3OO• → L3• + L3OOH, *R19: L3H + L2O• → L3• + L2OH, *R20: L3H + L3O• → L3• + L3OH, *R21: L1OOH → L1O• + HO•, *R22: L1OOH + L1OOH → L1O• + L1OO•, *R23: L1OOH + L2OOH → L1O• + L2OO•, *R24: L1OOH + L2OOH → L2O• + L1OO•, *R25: L1OOH + L3OOH → L1O• + L3OO•, *R26: L1OOH + L3OOH → L3O• + L1OO•, *R27: L1• + O2 → L1OO•, *R28: L1• + L1• → L1-polymer + L1-polymer, *R29: L1• + L2• → L1-polymer + L2-polymer, *R30: L1• + L3• → L1-polymer + L3-polymer, *R31: L1• + L1OO• → L1-polymer + L1-polymer, *R32: L1• + L2OO• → L1-polymer + L2-polymer, *R33: L1• + L3OO• → L1-polymer + L3-polymer, *R34: L2• + O2 → L2OO•, *R35: L2• + L2• → L2-polymer + L2-polymer, *R36: L2• + L3• → L2-polymer + L3-polymer, *R37: L2• + L1OO• → L2-polymer + L1-polymer, *R38: L2• + L2OO• → L2-polymer + L2-polymer, *R39: L2• + L3OO• → L2-polymer + L3-polymer, *R40: L3• + O2 → L3OO•, *R41: L3• + L3• → L3-polymer + L3-polymer, *R42: L3• + L1OO• → L3-polymer + L1-polymer, *R43: L3• + L2OO• → L3-polymer + L2-polymer, *R44: L3• + L3OO• → L3-polymer + L3-polymer, *R45: L1O• → L1=O, *R46: L1OO• + L1OO• → L1-polymer + L1-polymer, *R47: L1OO• + L2OO• → L1-polymer + L2-polymer, *R48: L1OO• + L3OO• → L1-polymer + L3-polymer, *R49: L2OO• + L2OO• → L2-polymer + L2-polymer, *R50: L2OO• + L3OO• → L2-polymer + L3-polymer, *R51: L3OO• + L3OO• → L3-polymer + L3-polymer, *R52: L2OOH → L2O• + HO•, *R53: L2OOH + L2OOH → L2O• + L2OO•, *R54: L2OOH + L3OOH → L2O• + L3OO•, *R55: L2OOH + L3OOH → L3O• + L2OO•, *R56: L3OOH → L3O• + HO•, *R57: L3OOH + L3OOH → L3O• + L3OO•, *R58: L2O• → L2=O, *R59: L3O• → L3=O ``` *A star indicates that the reaction has been correctly parameterized*.
## ✚⚗️ 7 | Extending Radical Chemistry Radigen **automatically generates all chemically valid reactions** by combining species according to their **reactive functions**, including hydrogen abstraction, radical recombination, and hydroperoxide decomposition. Each species class defines: * Its **reactive function** (e.g., `"CH"` for allylic hydrogen) * Which **functions it reacts with** (e.g., `"HO•"`, `"COO•"`) * The **product class** to form (e.g., `monoAllylicC`) * A **default name and root**, which are used to infer product names * An **optional color and linestyle** for visualization ### 🧬 7.1 | Example: Hydrogen Abstraction from Oleic Acid (L1H) The species `L1H` is defined using a class that encodes its reactivity: ```python @species.register class monoAllylicCH(species): """Aliphatic (CH) on monoallylic site""" classAliases = ['C1H', 'R1H', 'L1H', 'P1H'] defaultName = "L1H" defaultRoot = "L1" suffix = "H" allylic = 1 reactiveFunction = "CH" reactWith = ['HO•', 'CO•', 'COO•'] product = ['monoAllylicC'] ``` This declares that: * `L1H` can lose a hydrogen (H abstraction) * It reacts with radicals such as `HO•`, `COO•`, etc. * The product is a `monoAllylicC` radical (usually named `L1•`) ### 🔢 7.2 | Reaction Generation When you run: ```python oil.addProducts() oil.addReactions() ``` Radigen: 1. Constructs new species like `L1•` and `L1OH` using registered rules. 2. Creates all valid reactions like: ```text L1H + L1O• → L1• + L1OH L1H + HO• → L1• + H2O ``` 3. Assigns reaction rate constants by matching **reaction fingerprints**. ### 🧾 7.3 | Rate Constant Assignment Reaction rates are assigned from a curated database (`reactionRateDB`) using string-based **fingerprints** such as: ```python reactionRateDB( fingerprint="L1H + L1O• -> L1• + L1OH", T0=30, # reference temperature (°C) k0=3.80e3, # rate constant at T0 [m³·mol⁻¹·s⁻¹] Ea=14.0e3, # activation energy [J/mol] source="Touffet et al. 2023, Table 2" ) ``` * Fingerprints use canonical formatting and names. * Each fingerprint may be associated with multiple entries (e.g., confidence intervals, sources). * Cross-reactions inherit rate constants via **geometric mean rules** (from collision theory). ### 🔄 7.4 | Fingerprint Substitution Radigen supports **symbolic substitution** to simplify fingerprint management. For instance: ```python reactionRateDB.register_fingerprint_substitution(r"C([123])", r"L\1") ``` This maps `"C1H"` → `"L1H"`, allowing fingerprints like: ```text C1H + C1O• → C1• + C1OH ``` to reuse existing data for `L1H` reactions. ------ ## 📦📁 8 | Project Structure ``` radigen/ ├── radigen3/ │ ├── oxidation.py # main kernel (>4 Klines) │ └── ... ├── simulate_oil.py # main simulation scenarios ├── test_oxidation.py # validation and baseline examples ├── README.md ├── images/ # printed figures as PNG and PDF images ├── docs/ # code documentation └── literature/ # manuscripts and constant tables ``` ------ ## 🔧 9 | Installation Radigen is a **self-contained kernel** distributed as a single module file: `radigen3/oxidation.py` (∼4,000+ lines of fully documented code). No `pip install` is required. ### 📁 Recommended Project Layout Place your scripts in a folder alongside `radigen3/`: ``` radigen/ ├── radigen3/ │ └── oxidation.py # main module (kernel) ├── yourscript.py # your example or project scripts ... └── README.md ``` This allows **direct usage** of all core classes without installation: ```python from radigen3.oxidation import mixture, mixtureKinetics, TKO2cycle ``` This strategy: - Enables **version isolation** and multiple coexisting Radigen versions. - Avoids `sys.path` manipulation or environment modification. - Is ideal for **prompt-driven** or **LLM-assisted scripting** workflows. > 🚩If you use notebooks (e.g., `Jupyter`), just ensure the notebook is run from the folder containing `radigen3/`. ------ ## 🧩📘 10 | Core Classes **Module** `radigen3.oxidation` | Class | Purpose | | ----------------- | ------------------------------------------------------------ | | `species` | Represents chemical species (radicals, peroxides, stable) | | `reaction` | Canonical reaction with fingerprint and type inference | | `reactionRateDB` | Registry of rate constants (k₀, Eₐ) searchable by fingerprint | | `mixture` | Encodes physical/chemical system: species, V, A, T, kO₂, pO₂ | | `mixtureKinetics` | Numerical integrator for the chemical system (solve\_ivp) | | `lumped` | Group of species for aggregate observables | | `TKO2cycle` | Dynamic temperature and kO₂ definition for advanced scenarios | **Class Inheritance Diagram** (without species-derived classes) ```{mermaid} graph TD; PrintableFigure TKO2cycle lumped mixture mixtureKinetics reaction reactionRateDB species Figure --> PrintableFigure object --> TKO2cycle object --> mixture object --> mixtureKinetics object --> reaction object --> reactionRateDB object --> species species --> lumped ``` **Class Inheritance Diagram** (with species-derived classes) ```{mermaid} graph TD; H2O PrintableFigure TKO2cycle diAllylicC diAllylicCH diAllylicCHO diAllylicCO diAllylicCOH diAllylicCOO diAllylicCOOH diAllylicCeqO lumped mixture mixtureKinetics monoAllylicC monoAllylicCH monoAllylicCHO monoAllylicCO monoAllylicCOH monoAllylicCOO monoAllylicCOOH monoAllylicCeqO oxygen peroxyl reaction reactionRateDB species terminationPolymers triAllylicC triAllylicCH triAllylicCHO triAllylicCO triAllylicCOH triAllylicCOO triAllylicCOOH triAllylicCeqO Figure --> PrintableFigure object --> TKO2cycle object --> mixture object --> mixtureKinetics object --> reaction object --> reactionRateDB object --> species species --> H2O species --> diAllylicC species --> diAllylicCH species --> diAllylicCHO species --> diAllylicCO species --> diAllylicCOH species --> diAllylicCOO species --> diAllylicCOOH species --> diAllylicCeqO species --> lumped species --> monoAllylicC species --> monoAllylicCH species --> monoAllylicCHO species --> monoAllylicCO species --> monoAllylicCOH species --> monoAllylicCOO species --> monoAllylicCOOH species --> monoAllylicCeqO species --> oxygen species --> peroxyl species --> terminationPolymers species --> triAllylicC species --> triAllylicCH species --> triAllylicCHO species --> triAllylicCO species --> triAllylicCOH species --> triAllylicCOO species --> triAllylicCOOH species --> triAllylicCeqO ``` ------ ## 🧺🧫 11 | Lumped Species Radigen offers built-in **grouping functions** to track hydroperoxides, aldehydes, ketones, radicals and polymers: ```python oilmodel.register_lumped("LOOH", oil.lumped_hydroperoxides()) oilmodel.register_lumped("rad_C", oil.lumped_radicals_on_C()) df = oilmodel.results_as_dataframe(["L1H", "LOOH", "rad_C"]) ``` Custom groups by pattern or reactive function: ```python peroxyls = oil.get_lumped_by_pattern(r".*OO•$") cooh_group = oil.get_lumped_by_function("COOH") ``` ------ ## 🧬 12 | Chemistry Assumptions - Allylicity matters: `L1H`, `L2H`, `L3H` represent mono-, di-, and triallylic protons. - Glycerol backbone is ignored; triglycerides are **approximated by FAMEs** (fatty methyl esters). - O₂ transport modeled via gas-liquid interface (Henry's law + `kO2`) - Thermodynamic equilibrium (stability of H-bonds) governs **ROOH cage vs. free** mechanism. ------ ## 📜 License MIT License – see `LICENSE`. ------ ## 📣 Contact Developed under the **Generative Simulation Initiative** 🌱 Lead: **Olivier Vitrac** Contact: *[olivier.vitrac@gmail.com](mailto:olivier.vitrac@gmail.com)* *Last updated: 2025-05-23*