1
1
"""
2
2
This module contains all the classes for the chemicals in the PyChemicals package.
3
- Database handling is decoupled; chemical classes expect chemical data to be provided externally.
3
+ Database handling is decoupled; if the user does not supply chemical properties explicitly,
4
+ the classes will retrieve them based on the chemical name from the set data loader.
4
5
"""
5
6
6
7
from typing import Dict , Any
7
8
import numpy as np
9
+ from .chemicals_db import get_valid_acids , get_valid_bases , get_valid_gases
8
10
9
11
10
12
class Chemical :
@@ -17,7 +19,7 @@ def __init__(self, name: str, **kwargs: Dict[str, Any]):
17
19
Args:
18
20
name (str): Name of the chemical.
19
21
**kwargs: Arbitrary keyword arguments including:
20
- concentration, volume, mass, properties
22
+ concentration, volume, mass
21
23
"""
22
24
self .name = name
23
25
self .concentration = kwargs .get ("concentration" )
@@ -52,19 +54,24 @@ def calculate_moles(self) -> float:
52
54
class Acid (Chemical ):
53
55
"""Class representing an acid chemical."""
54
56
55
- def __init__ (self , name : str , properties : dict , ** kwargs : Dict [str , Any ]):
57
+ def __init__ (self , name : str , ** kwargs : Dict [str , Any ]):
56
58
"""
57
59
Initialize an Acid instance.
58
60
59
61
Args:
60
62
name (str): Name of the acid.
61
- properties (dict): A dictionary containing acid properties
62
- (e.g. proticity, Ka, molar_mass, etc.).
63
63
**kwargs: Arbitrary keyword arguments including:
64
- concentration, volume, mass
64
+ concentration, volume, mass, properties
65
65
"""
66
- # properties should include keys: proticity, Ka, molar_mass, optionally ka1 and ka2.
66
+ # If the user did not supply a properties dict, automatically load it.
67
+ properties = kwargs .pop ("properties" , None )
68
+ if properties is None :
69
+ acids_data = get_valid_acids ()
70
+ if name not in acids_data :
71
+ raise ValueError (f"Unknown acid: { name } in the current data source." )
72
+ properties = acids_data [name ]
67
73
self .properties = properties
74
+
68
75
super ().__init__ (name , ** kwargs )
69
76
self .proticity = properties ["proticity" ]
70
77
self .ka = properties ["Ka" ]
@@ -109,7 +116,7 @@ def calculate_temp_dependence(
109
116
) -> float :
110
117
"""
111
118
Calculate the temperature dependence of the equilibrium constant
112
- using the van 't Hoff equation.
119
+ using the van't Hoff equation.
113
120
"""
114
121
t_initial_k = t_initial + 273.15
115
122
t_final_k = t_final + 273.15
@@ -127,9 +134,9 @@ def validate_acid(self) -> None:
127
134
expected_mass = self .concentration * self .volume * self .molar_mass
128
135
if not np .isclose (self .mass , expected_mass , rtol = 1e-3 ):
129
136
raise ValueError (
130
- f""" { self .volume } L of
131
- { self . concentration } M { self . name } (expected mass { expected_mass :.2f} g
132
- does not match provided mass { self .mass } g."" "
137
+ f"{ self .volume } L of { self . concentration } M { self . name } "
138
+ " (expected mass {expected_mass:.2f}g) "
139
+ f" does not match provided mass { self .mass } g."
133
140
)
134
141
135
142
def verify_acid_type (self ) -> str :
@@ -168,18 +175,23 @@ def __repr__(self) -> str:
168
175
class Base (Chemical ):
169
176
"""Class representing a base chemical."""
170
177
171
- def __init__ (self , name : str , properties : dict , ** kwargs : Dict [str , Any ]):
178
+ def __init__ (self , name : str , ** kwargs : Dict [str , Any ]):
172
179
"""
173
180
Initialize a Base instance.
174
181
175
182
Args:
176
183
name (str): Name of the base.
177
- properties (dict): A dictionary containing base properties
178
- (e.g. proticity, Kb, molar_mass, etc.).
179
184
**kwargs: Arbitrary keyword arguments including:
180
- concentration, volume, mass
185
+ concentration, volume, mass, properties
181
186
"""
187
+ properties = kwargs .pop ("properties" , None )
188
+ if properties is None :
189
+ bases_data = get_valid_bases ()
190
+ if name not in bases_data :
191
+ raise ValueError (f"Unknown base: { name } in the current data source." )
192
+ properties = bases_data [name ]
182
193
self .properties = properties
194
+
183
195
super ().__init__ (name , ** kwargs )
184
196
self .proticity = properties ["proticity" ]
185
197
self .kb = properties ["Kb" ]
@@ -198,9 +210,9 @@ def validate_base(self) -> None:
198
210
expected_mass = self .concentration * self .volume * self .molar_mass
199
211
if not np .isclose (self .mass , expected_mass , rtol = 1e-3 ):
200
212
raise ValueError (
201
- f""" { self .volume } L of { self .concentration } M { self .name }
202
- (expected mass { expected_mass :.2f} g)
203
- does not match provided mass { self .mass } g."" "
213
+ f"{ self .volume } L of { self .concentration } M { self .name } "
214
+ " (expected mass {expected_mass:.2f}g) "
215
+ f" does not match provided mass { self .mass } g."
204
216
)
205
217
206
218
def verify_base_type (self ) -> str :
@@ -286,19 +298,23 @@ class Gas(Chemical):
286
298
287
299
R = 0.0821 # Ideal gas constant in L·atm/(K·mol)
288
300
289
- def __init__ (self , name : str , properties : dict , ** kwargs : Dict [str , Any ]):
301
+ def __init__ (self , name : str , ** kwargs : Dict [str , Any ]):
290
302
"""
291
303
Initialize a Gas instance.
292
304
293
305
Args:
294
306
name (str): Name of the gas.
295
- properties (dict): Dictionary containing gas properties (e.g., molar_mass, density).
296
307
**kwargs: Arbitrary keyword arguments including:
297
- pressure, temperature, volume, mass
308
+ pressure, temperature, volume, mass, properties
298
309
"""
310
+ properties = kwargs .pop ("properties" , None )
311
+ if properties is None :
312
+ gases_data = get_valid_gases ()
313
+ if name not in gases_data :
314
+ raise ValueError (f"Unknown gas: { name } in the current data source." )
315
+ properties = gases_data [name ]
299
316
self .properties = properties
300
- if "molar_mass" not in properties :
301
- raise ValueError ("Gas properties must include 'molar_mass'." )
317
+
302
318
super ().__init__ (name , ** kwargs )
303
319
self .pressure = kwargs .get ("pressure" )
304
320
self .temperature = kwargs .get ("temperature" )
@@ -369,21 +385,3 @@ def __repr__(self) -> str:
369
385
return (
370
386
f"{ self .name } : { self .pressure } atm, { self .temperature } K, { self .volume } L"
371
387
)
372
-
373
-
374
- if __name__ == "__main__" :
375
- try :
376
- from .chemicals_db import get_valid_acids
377
-
378
- valid_acids = get_valid_acids ()
379
- hcl_properties = valid_acids ["Hydrogen chloride" ]
380
- hcl = Acid (
381
- name = "Hydrogen chloride" ,
382
- properties = hcl_properties ,
383
- concentration = 0.3 ,
384
- volume = 1.0 ,
385
- mass = 0.3 * 1.0 * hcl_properties ["molar_mass" ],
386
- )
387
- print ("HCl pH:" , hcl .ph ())
388
- except ImportError as err :
389
- print ("Error importing chemicals_db:" , err )
0 commit comments