pyreactlab-core 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,114 @@
1
+ # import libs
2
+ import logging
3
+ from typing import List, Optional
4
+ # locals
5
+ from ..models.reaction import Reaction
6
+
7
+ # NOTE: configure logger
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ def build_stoichiometry_matrix(reactions: List[Reaction]):
12
+ '''
13
+ Build stoichiometry matrix for reactions
14
+
15
+ Parameters
16
+ ----------
17
+ reactions : List[Reaction]
18
+ List of Reaction instances
19
+
20
+ Returns
21
+ -------
22
+ component_list: list
23
+ component list
24
+ component_dict: dict
25
+ component dict
26
+ comp_list: list
27
+ component list
28
+ comp_coeff: list
29
+ component coefficient
30
+ component_state_list: list
31
+ component state list
32
+ '''
33
+ try:
34
+ # SECTION: extract reaction results
35
+ # NOTE: reaction num
36
+ reaction_num = len(reactions)
37
+
38
+ # NOTE: component list
39
+ component_list = []
40
+
41
+ # NOTE: component state list
42
+ component_state_list = []
43
+
44
+ # SECTION: Iterate over reactions and extract reactants and products
45
+ for item in reactions:
46
+ # get components
47
+ _components = item.all_components
48
+ # store
49
+ component_list.extend(_components)
50
+
51
+ # remove duplicate
52
+ component_list = list(set(component_list))
53
+
54
+ # component id: key, value
55
+ component_dict = {}
56
+
57
+ # loop over component list
58
+ for i, item in enumerate(component_list):
59
+ component_dict[item] = i
60
+
61
+ # SECTION: Initialize the component list
62
+ comp_list = [
63
+ {i: 0.0 for i in component_dict.keys()} for _ in range(reaction_num)
64
+ ]
65
+
66
+ # SECTION: Iterate over reactions and components
67
+ for j, reaction in enumerate(reactions):
68
+ for item in component_dict.keys():
69
+ # NOTE: Check reactants
70
+ for reactant in reactions[j].reactants:
71
+ # matching state
72
+ if reactant['molecule_state'] == item:
73
+ comp_list[j][item] = -1 * \
74
+ float(reactant['coefficient'])
75
+
76
+ # >> component state list
77
+ component_state_list.append(
78
+ (
79
+ reactant['molecule'],
80
+ reactant['state'],
81
+ reactant['molecule_state']
82
+ )
83
+ )
84
+
85
+ # NOTE: Check products
86
+ for product in reactions[j].products:
87
+ # matching state
88
+ if product['molecule_state'] == item:
89
+ comp_list[j][item] = float(product['coefficient'])
90
+
91
+ # >> component state list
92
+ component_state_list.append(
93
+ (
94
+ product['molecule'],
95
+ product['state'],
96
+ product['molecule_state']
97
+ )
98
+ )
99
+
100
+ # Convert comp_list to comp_matrix
101
+ comp_coeff = [
102
+ [comp_list[j][item] for item in component_dict.keys()] for j in range(reaction_num)
103
+ ]
104
+
105
+ # res
106
+ return {
107
+ "component_list": component_list,
108
+ "component_dict": component_dict,
109
+ "component_state_list": component_state_list,
110
+ "comp_list": comp_list,
111
+ "comp_coeff": comp_coeff,
112
+ }
113
+ except Exception as e:
114
+ raise Exception(f"Error defining component ID: {e}")
File without changes
@@ -0,0 +1,174 @@
1
+ # import libs
2
+ from __future__ import annotations
3
+
4
+ from typing import Any, Dict, Optional, List
5
+ from pydantic import BaseModel, Field, computed_field, model_validator
6
+ # local imports
7
+ from ..core.chem_react import (
8
+ ChemReact,
9
+ ReactionMode,
10
+ PhaseRule
11
+ )
12
+
13
+
14
+ class Reaction(BaseModel):
15
+ """
16
+ A class representing a chemical reaction, including its analysis and properties.
17
+
18
+ Attributes
19
+ ----------
20
+ name : str
21
+ The name of the reaction.
22
+ reaction : str
23
+ The chemical reaction equation as a string.
24
+ reaction_mode_symbol : Optional[ReactionMode]
25
+ The symbol used to separate reactants and products in a reaction equation.
26
+ analysis : Dict[str, Any]
27
+ A dictionary containing the analysis results of the reaction.
28
+
29
+ Properties
30
+ ----------
31
+ symbolic_reaction : str
32
+ The symbolic representation of the balanced reaction.
33
+ symbolic_unbalanced_reaction : str
34
+ The symbolic representation of the unbalanced reaction.
35
+ reactants_names : list[str]
36
+ A list of names of the reactants in the reaction.
37
+ products_names : list[str]
38
+ A list of names of the products in the reaction.
39
+ products : List[Dict[str, Any]]
40
+ A list of dictionaries representing the products of the reaction.
41
+ reactants : List[Dict[str, Any]]
42
+ A list of dictionaries representing the reactants of the reaction.
43
+ reaction_coefficients : Dict[str, float]
44
+ A dictionary of reaction coefficients for each component.
45
+ reaction_stoichiometry : Dict[str, float]
46
+ A dictionary representing the stoichiometry of the reaction.
47
+ reaction_stoichiometry_matrix : list[float]
48
+ A list representing the stoichiometry matrix of the reaction.
49
+ carbon_count : int
50
+ The total number of carbon atoms in the reaction.
51
+ reaction_state : str
52
+ The state of the reaction (e.g., "balanced", "unbalanced").
53
+ reaction_phase : Optional[PhaseRule]
54
+ The phase rule of the reaction, if applicable.
55
+ state_count : Dict[str, int]
56
+ A dictionary counting the states of components in the reaction.
57
+ component_ids : Dict[str, int]
58
+ A dictionary mapping component names to their IDs.
59
+ all_components : list[str]
60
+ A list of all component names involved in the reaction.
61
+
62
+ Methods
63
+ -------
64
+ _run_existing_analysis(self) -> Reaction
65
+ Validates and analyzes the reaction after initialization.
66
+ """
67
+ name: str
68
+ reaction: str
69
+ reaction_mode_symbol: Optional[ReactionMode] = Field(
70
+ default=None,
71
+ description="The symbol used to separate reactants and products in a reaction equation."
72
+ )
73
+ analysis: Dict[str, Any] = Field(default_factory=dict)
74
+
75
+ @model_validator(mode="after")
76
+ def _run_existing_analysis(self):
77
+ # NOTE: check reaction mode symbol
78
+ if "<=>" in self.reaction:
79
+ self.reaction_mode_symbol = "<=>"
80
+ elif "=>" in self.reaction:
81
+ self.reaction_mode_symbol = "=>"
82
+ elif "=" in self.reaction:
83
+ self.reaction_mode_symbol = "="
84
+ else:
85
+ raise ValueError(
86
+ f"Invalid reaction format in reaction: {self.reaction}"
87
+ )
88
+
89
+ # NOTE: analyze reaction
90
+ util = ChemReact(reaction_mode_symbol=self.reaction_mode_symbol)
91
+
92
+ # NOTE: perform analysis
93
+ self.analysis = util.analyze_reaction(
94
+ reaction_pack={
95
+ "name": self.name,
96
+ "reaction": self.reaction
97
+ },
98
+ )
99
+ return self
100
+
101
+ @computed_field
102
+ @property
103
+ def symbolic_reaction(self) -> str:
104
+ return self.analysis.get("symbolic_reaction", "")
105
+
106
+ @computed_field
107
+ @property
108
+ def symbolic_unbalanced_reaction(self) -> str:
109
+ return self.analysis.get("symbolic_unbalanced_reaction", "")
110
+
111
+ @computed_field
112
+ @property
113
+ def reactants_names(self) -> list[str]:
114
+ return self.analysis.get("reactants_names", [])
115
+
116
+ @computed_field
117
+ @property
118
+ def products_names(self) -> list[str]:
119
+ return self.analysis.get("products_names", [])
120
+
121
+ @computed_field
122
+ @property
123
+ def products(self) -> List[Dict[str, Any]]:
124
+ return self.analysis.get("products", [])
125
+
126
+ @computed_field
127
+ @property
128
+ def reactants(self) -> List[Dict[str, Any]]:
129
+ return self.analysis.get("reactants", [])
130
+
131
+ @computed_field
132
+ @property
133
+ def reaction_coefficients(self) -> Dict[str, float]:
134
+ return self.analysis.get("reaction_coefficients", {})
135
+
136
+ @computed_field
137
+ @property
138
+ def reaction_stoichiometry(self) -> Dict[str, float]:
139
+ return self.analysis.get("reaction_stoichiometry", {})
140
+
141
+ @computed_field
142
+ @property
143
+ def reaction_stoichiometry_matrix(self) -> list[float]:
144
+ return self.analysis.get("reaction_stoichiometry_matrix", [])
145
+
146
+ @computed_field
147
+ @property
148
+ def carbon_count(self) -> int:
149
+ return self.analysis.get("carbon_count", 0)
150
+
151
+ @computed_field
152
+ @property
153
+ def reaction_state(self) -> str:
154
+ return self.analysis.get("reaction_state", "unknown")
155
+
156
+ @computed_field
157
+ @property
158
+ def reaction_phase(self) -> Optional[PhaseRule]:
159
+ return self.analysis.get("reaction_phase", None)
160
+
161
+ @computed_field
162
+ @property
163
+ def state_count(self) -> Dict[str, int]:
164
+ return self.analysis.get("state_count", {})
165
+
166
+ @computed_field
167
+ @property
168
+ def component_ids(self) -> Dict[str, int]:
169
+ return self.analysis.get("component_ids", {})
170
+
171
+ @computed_field
172
+ @property
173
+ def all_components(self) -> list[str]:
174
+ return self.analysis.get("all_components", [])
File without changes
@@ -0,0 +1,30 @@
1
+ # import libs
2
+ import logging
3
+ from typing import Literal, Optional
4
+
5
+ # setup logger
6
+ logger = logging.getLogger(__name__)
7
+
8
+ # NOTE: check if value is number
9
+
10
+
11
+ def is_number(value: str) -> bool:
12
+ try:
13
+ float(value)
14
+ return True
15
+ except (ValueError, TypeError):
16
+ return False
17
+
18
+ # NOTE: check if value is integer
19
+
20
+
21
+ def is_integer(value: str) -> bool:
22
+ try:
23
+ num = float(value) # handles "10", "10.5", "1e-3", etc.
24
+ except (ValueError, TypeError):
25
+ return False
26
+
27
+ if num.is_integer(): # built-in float method
28
+ return True
29
+ else:
30
+ return False
@@ -0,0 +1,179 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyreactlab-core
3
+ Version: 0.1.0
4
+ Summary: pyreactlab-core is the core foundation of the PyReactLab ecosystem, offering shared data structures and algorithms for chemical reaction representation, stoichiometry, and reaction analysis.
5
+ Author-email: Sina Gilassi <sina.gilassi@gmail.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/sinagilassi/PyReactLab-Core
8
+ Project-URL: Documentation, https://pyreactlab-core.readthedocs.io/en/latest/
9
+ Project-URL: Source, https://github.com/sinagilassi/PyReactLab-Core
10
+ Project-URL: Tracker, https://github.com/sinagilassi/PyReactLab-Core/issues
11
+ Keywords: chemical-engineering,chemical-reactions,reaction-representation,reaction-stoichiometry,reaction-analysis
12
+ Classifier: Development Status :: 1 - Planning
13
+ Classifier: Intended Audience :: Education
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Operating System :: Unix
16
+ Classifier: Operating System :: MacOS :: MacOS X
17
+ Classifier: Operating System :: Microsoft :: Windows
18
+ Requires-Python: >=3.11
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: numpy>=2.4.0
22
+ Requires-Dist: pydantic>=2.12.5
23
+ Requires-Dist: pydantic-settings>=2.12.0
24
+ Requires-Dist: scipy>=1.16.3
25
+ Dynamic: license-file
26
+
27
+ # 🧪 PyReactLab-Core
28
+
29
+ [![PyPI Downloads](https://static.pepy.tech/badge/pyreactlab-core/month)](https://pepy.tech/projects/pyreactlab-core)
30
+ ![PyPI](https://img.shields.io/pypi/v/pyreactlab-core)
31
+ ![Python Version](https://img.shields.io/pypi/pyversions/pyreactlab-core.svg)
32
+ ![License](https://img.shields.io/pypi/l/pyreactlab-core)
33
+ ![Read the Docs](https://img.shields.io/readthedocs/pyreactlab-core)
34
+
35
+ **PyReactLab-Core** is the core foundation of the PyReactLab ecosystem, offering shared data structures and algorithms for chemical reaction representation, stoichiometry, and reaction analysis.
36
+
37
+ ## ✨ Features
38
+
39
+ - **⚗️ Reaction Representation**: Define and manipulate chemical reactions with ease.
40
+ - **⚖️ Stoichiometry Calculations**: Perform stoichiometric calculations for reactions.
41
+ - **🔬 Reaction Analysis**: Analyze reaction properties and behaviors.
42
+ - **🧩 Extensible Design**: Built to be extended by other PyReactLab modules.
43
+
44
+ ## 📦 Installation
45
+
46
+ You can install PyReactLab-Core via pip:
47
+
48
+ ```bash
49
+ pip install pyreactlab-core
50
+ ```
51
+
52
+ ## 🚀 Usage
53
+
54
+ ### Introduce a reaction
55
+
56
+ A typical reaction can be introduced as follows:
57
+
58
+ ```python
59
+ from pyreactlab_core import Reaction
60
+
61
+ reaction = Reaction(
62
+ name="Combustion of Methane",
63
+ reaction="CO2(g) + 3H2(g) => CH3OH(g) + H2O(g)"
64
+ )
65
+
66
+ # print analysis
67
+ print(
68
+ f"[bold underline]Reaction Analysis for: {reaction_1.name}[/bold underline]")
69
+ print(f"Reaction: {reaction_1.reaction}")
70
+ print(f"Reactants: {reaction_1.reactants}")
71
+ print(f"Products: {reaction_1.products}")
72
+ print(f"Reaction Coefficients: {reaction_1.reaction_coefficients}")
73
+ print(f"Reaction Stoichiometry: {reaction_1.reaction_stoichiometry}")
74
+ print(f"State Counts: {reaction_1.state_count}")
75
+ print(f"Reaction Phase: {reaction_1.reaction_phase}")
76
+ print(f"Reaction State: {reaction_1.reaction_state}")
77
+ print(f"Carbon Count: {reaction_1.carbon_count}")
78
+ print(f"Reactants Names: {reaction_1.reactants_names}")
79
+ print(f"Products Names: {reaction_1.products_names}")
80
+
81
+ # results:
82
+ # Reaction: CO2(g) + 3H2(g) => CH3OH(g) + H2O(g)
83
+ # Component IDs: {'CO2-g': 1, 'H2-g': 2, 'CH3OH-g': 3, 'H2O-g': 4}
84
+ # Reaction Mode Symbol: =>
85
+ # Symbolic Unbalanced Reaction: CO2 + H2 => CH3OH + H2O
86
+ # Symbolic Reaction: CO2 + 3.0H2 => CH3OH + H2O
87
+ # Reactants: [{'coefficient': 1.0, 'molecule': 'CO2', 'state': 'g', 'molecule_state': 'CO2-g'}, {'coefficient': 3.0, 'molecule': 'H2', 'state': 'g', 'molecule_state': 'H2-g'}]
88
+ # Products: [{'coefficient': 1.0, 'molecule': 'CH3OH', 'state': 'g', 'molecule_state': 'CH3OH-g'}, {'coefficient': 1.0, 'molecule': 'H2O', 'state': 'g', 'molecule_state': 'H2O-g'}]
89
+ # Reaction Coefficients: 2.0
90
+ # Reaction Stoichiometry: {'CO2-g': -1.0, 'H2-g': -3.0, 'CH3OH-g': 1.0, 'H2O-g': 1.0}
91
+ # State Counts: {'g': 4, 'l': 0, 'aq': 0, 's': 0}
92
+ # Reaction Phase: gas
93
+ # Reaction State: {'CO2-g': 'g', 'H2-g': 'g', 'CH3OH-g': 'g', 'H2O-g': 'g'}
94
+ # Carbon Count: {'CO2-g': 1.0, 'H2-g': 0.0, 'CH3OH-g': 1.0, 'H2O-g': 0.0}
95
+ # Reactants Names: ['CO2-g', 'H2-g']
96
+ # Products Names: ['CH3OH-g', 'H2O-g']
97
+ ```
98
+
99
+ ### Stoichiometric Balance
100
+
101
+ You can check if a reaction is balanced:
102
+
103
+ ```python
104
+ from pyreactlab_core import Reaction
105
+ from pyreactlab_core.core import balance
106
+
107
+ # define a reaction
108
+ reaction = Reaction(
109
+ name="Combustion of Methane",
110
+ reaction="CO2(g) + 3H2(g) => CH3OH(g) + H2O(g)"
111
+ )
112
+
113
+ # balance the reaction automatically
114
+ balanced_reaction = balance(reaction)
115
+ print(f"Balanced Reaction: {balanced_reaction.reaction}")
116
+ ```
117
+
118
+ ### Stoichiometric Matrix
119
+
120
+ You can create a stoichiometric matrix for a list of reactions:
121
+
122
+ ```python
123
+ from pyreactlab_core import Reaction
124
+ from pyreactlab_core import rxn, rxn_stoichiometry, rxns_stoichiometry
125
+
126
+ # NOTE: define reaction string
127
+ reaction_1 = "CO2(g) + 3H2(g) => CH3OH(g) + H2O(g)"
128
+ name_1 = "CO2 Hydrogenation to Methanol"
129
+
130
+ # second reaction
131
+ reaction_2 = "C2H4(g) + H2(g) => C2H6(g)"
132
+ name_2 = "Ethylene Hydrogenation to Ethane"
133
+
134
+ # NOTE: create reaction instance
135
+ rxn_1: Reaction = rxn(
136
+ reaction_str=reaction_1,
137
+ name=name_1
138
+ )
139
+
140
+ rxn_2: Reaction = rxn(
141
+ reaction_str=reaction_2,
142
+ name=name_2
143
+ )
144
+
145
+ # NOTE: Get stoichiometry matrices for multiple reactions
146
+ reactions_list = [rxn_1, rxn_2]
147
+ stoichiometry_matrices = rxns_stoichiometry(
148
+ reactions=reactions_list,
149
+ )
150
+ # log
151
+ print(stoichiometry_matrices)
152
+
153
+ # results:
154
+ # {
155
+ # 'components': ['CO2-g', 'H2O-g', 'C2H4-g', 'H2-g', 'CH3OH-g', 'C2H6-g'],
156
+ # 'component_ids': {'CO2-g': 0, 'H2O-g': 1, 'C2H4-g': 2, 'H2-g': 3, 'CH3OH-g': 4, 'C2H6-g': 5},
157
+ # 'stoichiometry_matrices_list': [[-1.0, 1.0, 0.0, -3.0, 1.0, 0.0], [0.0, 0.0, -1.0, -1.0, 0.0, 1.0]],
158
+ # 'stoichiometry_matrices_dict': [
159
+ # {'CO2-g': -1.0, 'H2O-g': 1.0, 'C2H4-g': 0.0, 'H2-g': -3.0, 'CH3OH-g': 1.0, 'C2H6-g': 0.0},
160
+ # {'CO2-g': 0.0, 'H2O-g': 0.0, 'C2H4-g': -1.0, 'H2-g': -1.0, 'CH3OH-g': 0.0, 'C2H6-g': 1.0}
161
+ # ]
162
+ # }
163
+ ```
164
+
165
+ ## 🤝 Contributing
166
+
167
+ Contributions are highly welcome — bug fixes, new calculation routines, mixture models, extended unit tests, documentation, etc.
168
+
169
+ ## 📝 License
170
+
171
+ This project is distributed under the Apache License, Version 2.0, which grants you broad freedom to use, modify, and integrate the software into your own applications or projects, provided that you comply with the conditions outlined in the license. Although Apache 2.0 does not require users to retain explicit author credit beyond standard copyright and license notices, I kindly request that if you incorporate this work into your own software, you acknowledge Sina Gilassi as the original author. Referencing the original repository or documentation is appreciated, as it helps recognize the effort invested in developing and maintaining this project.
172
+
173
+ ## ❓ FAQ
174
+
175
+ For any question, contact me on [LinkedIn](https://www.linkedin.com/in/sina-gilassi/)
176
+
177
+ ## 👨‍💻 Authors
178
+
179
+ - [@sinagilassi](https://www.github.com/sinagilassi)
@@ -0,0 +1,19 @@
1
+ pyreactlab_core/__init__.py,sha256=JGNJu9kdkUlIjNog8OXeCHCpnB9nJfClHqW-BcqoGxQ,453
2
+ pyreactlab_core/app.py,sha256=HWVXNpFHqKWqf5TUaym9SCUjAxEDRYEWCU-EPK-Yi5g,3605
3
+ pyreactlab_core/configs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ pyreactlab_core/configs/constants.py,sha256=8dpnVqnYwu5On1IH7toELWvNHp4BLgpLnhTRDobKVis,1295
5
+ pyreactlab_core/configs/info.py,sha256=uvzRSHbrnPlbzSn-rGNE11Htq6pIK-N9f0eE8NhD3Oo,448
6
+ pyreactlab_core/core/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
7
+ pyreactlab_core/core/chem_react.py,sha256=nMvAfZxw5OysSeYIhg82iaKil59Obc6dCMmq1CoKllw,31174
8
+ pyreactlab_core/docs/__init__.py,sha256=Q_nWMKjz40OCw-WsSHu-8Aenvj1_yY_N47VnCpzeMTA,262
9
+ pyreactlab_core/docs/chem_balance.py,sha256=qU035GBsFvFoEQCV_596OTGtFRqlnfMm4mXimqLdd5M,21503
10
+ pyreactlab_core/docs/chem_utils.py,sha256=jTyo-iMXs4JNqenZJ-g7Np_0SLfWGmu2trPUjWQZMzI,3603
11
+ pyreactlab_core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ pyreactlab_core/models/reaction.py,sha256=LiUKwoiKy_49WBboYPsZWyyFzVp9E1pv6JxoluQgC8U,5818
13
+ pyreactlab_core/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ pyreactlab_core/utils/tools.py,sha256=P8fwEYzXVteRb76BGT6IDtDQj2UJfEiZfyij5OzbA_k,638
15
+ pyreactlab_core-0.1.0.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
16
+ pyreactlab_core-0.1.0.dist-info/METADATA,sha256=IZyxyYBC6_ZaqrKvHtkrdG-l4Zkfrydkj1jb6KkYHog,7286
17
+ pyreactlab_core-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
18
+ pyreactlab_core-0.1.0.dist-info/top_level.txt,sha256=OTJ7aN0HVzzbIM1yAOk9UGg0v4AvpshSY86msqB7uiM,16
19
+ pyreactlab_core-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+