Skip to content

Commit 0310e6e

Browse files
coupled database to provide simpler API
1 parent c153394 commit 0310e6e

File tree

1 file changed

+39
-41
lines changed

1 file changed

+39
-41
lines changed

PyChemicals/chemicals.py

+39-41
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
"""
22
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.
45
"""
56

67
from typing import Dict, Any
78
import numpy as np
9+
from .chemicals_db import get_valid_acids, get_valid_bases, get_valid_gases
810

911

1012
class Chemical:
@@ -17,7 +19,7 @@ def __init__(self, name: str, **kwargs: Dict[str, Any]):
1719
Args:
1820
name (str): Name of the chemical.
1921
**kwargs: Arbitrary keyword arguments including:
20-
concentration, volume, mass, properties
22+
concentration, volume, mass
2123
"""
2224
self.name = name
2325
self.concentration = kwargs.get("concentration")
@@ -52,19 +54,24 @@ def calculate_moles(self) -> float:
5254
class Acid(Chemical):
5355
"""Class representing an acid chemical."""
5456

55-
def __init__(self, name: str, properties: dict, **kwargs: Dict[str, Any]):
57+
def __init__(self, name: str, **kwargs: Dict[str, Any]):
5658
"""
5759
Initialize an Acid instance.
5860
5961
Args:
6062
name (str): Name of the acid.
61-
properties (dict): A dictionary containing acid properties
62-
(e.g. proticity, Ka, molar_mass, etc.).
6363
**kwargs: Arbitrary keyword arguments including:
64-
concentration, volume, mass
64+
concentration, volume, mass, properties
6565
"""
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]
6773
self.properties = properties
74+
6875
super().__init__(name, **kwargs)
6976
self.proticity = properties["proticity"]
7077
self.ka = properties["Ka"]
@@ -109,7 +116,7 @@ def calculate_temp_dependence(
109116
) -> float:
110117
"""
111118
Calculate the temperature dependence of the equilibrium constant
112-
using the van 't Hoff equation.
119+
using the van't Hoff equation.
113120
"""
114121
t_initial_k = t_initial + 273.15
115122
t_final_k = t_final + 273.15
@@ -127,9 +134,9 @@ def validate_acid(self) -> None:
127134
expected_mass = self.concentration * self.volume * self.molar_mass
128135
if not np.isclose(self.mass, expected_mass, rtol=1e-3):
129136
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."
133140
)
134141

135142
def verify_acid_type(self) -> str:
@@ -168,18 +175,23 @@ def __repr__(self) -> str:
168175
class Base(Chemical):
169176
"""Class representing a base chemical."""
170177

171-
def __init__(self, name: str, properties: dict, **kwargs: Dict[str, Any]):
178+
def __init__(self, name: str, **kwargs: Dict[str, Any]):
172179
"""
173180
Initialize a Base instance.
174181
175182
Args:
176183
name (str): Name of the base.
177-
properties (dict): A dictionary containing base properties
178-
(e.g. proticity, Kb, molar_mass, etc.).
179184
**kwargs: Arbitrary keyword arguments including:
180-
concentration, volume, mass
185+
concentration, volume, mass, properties
181186
"""
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]
182193
self.properties = properties
194+
183195
super().__init__(name, **kwargs)
184196
self.proticity = properties["proticity"]
185197
self.kb = properties["Kb"]
@@ -198,9 +210,9 @@ def validate_base(self) -> None:
198210
expected_mass = self.concentration * self.volume * self.molar_mass
199211
if not np.isclose(self.mass, expected_mass, rtol=1e-3):
200212
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."
204216
)
205217

206218
def verify_base_type(self) -> str:
@@ -286,19 +298,23 @@ class Gas(Chemical):
286298

287299
R = 0.0821 # Ideal gas constant in L·atm/(K·mol)
288300

289-
def __init__(self, name: str, properties: dict, **kwargs: Dict[str, Any]):
301+
def __init__(self, name: str, **kwargs: Dict[str, Any]):
290302
"""
291303
Initialize a Gas instance.
292304
293305
Args:
294306
name (str): Name of the gas.
295-
properties (dict): Dictionary containing gas properties (e.g., molar_mass, density).
296307
**kwargs: Arbitrary keyword arguments including:
297-
pressure, temperature, volume, mass
308+
pressure, temperature, volume, mass, properties
298309
"""
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]
299316
self.properties = properties
300-
if "molar_mass" not in properties:
301-
raise ValueError("Gas properties must include 'molar_mass'.")
317+
302318
super().__init__(name, **kwargs)
303319
self.pressure = kwargs.get("pressure")
304320
self.temperature = kwargs.get("temperature")
@@ -369,21 +385,3 @@ def __repr__(self) -> str:
369385
return (
370386
f"{self.name}: {self.pressure} atm, {self.temperature} K, {self.volume} L"
371387
)
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

Comments
 (0)