diff --git a/src/chembalancer/chembalancer.py b/src/chembalancer/chembalancer.py
index f330088..e8b5270 100644
--- a/src/chembalancer/chembalancer.py
+++ b/src/chembalancer/chembalancer.py
@@ -16,7 +16,7 @@
from collections import defaultdict
from rdkit import Chem
import requests
-
+import base64
def get_smiles_from_name(name):
@@ -45,11 +45,11 @@ def count_atoms(smiles):
mol = Chem.MolFromSmiles(smiles)
atom_counts = defaultdict(int)
if mol:
+ mol = Chem.AddHs(mol)
for atom in mol.GetAtoms():
atom_counts[atom.GetSymbol()] += 1
return dict(atom_counts)
-
def solve_ilp(A):
""" Solve the integer linear programming problem to find stoichiometric coefficients. """
num_vars = A.shape[1]
@@ -169,35 +169,60 @@ def create_reaction_string(reactants, products):
products_str = '.'.join(products)
return f"{reactants_str}>>{products_str}"
+
def display_svg(svg):
"""Display SVG in Streamlit using markdown with unsafe HTML."""
b64 = base64.b64encode(svg.encode('utf-8')).decode("utf-8")
html = f""
- st.markdown(html, unsafe_allow_html=True)
+ return html
+
def compound_state(compound, temp):
CAS_compound = CAS_from_any(compound)
boiling_p = Tb(CAS_compound)
melting_p = Tm(CAS_compound)
- if temp <= melting_p:
+
+ if float(temp) <= float(melting_p):
return 'solid'
- elif temp > melting_p and temp <= boiling_p:
- return 'liquid'
- else:
+ elif float(temp) >= float(boiling_p):
return 'gas'
+ else:
+ return 'liquid'
def enthalpy(coeff, compound, state):
+ Cas_compound=CAS_from_any(compound)
if state == 'solid':
- return coeff * Hfs(CAS_from_any(compound))
+ if Hfs(Cas_compound)== None:
+ return 0
+ else:
+ return float(coeff) * Hfs(Cas_compound)
elif state == 'liquid':
- return coeff * Hfl(CAS_from_any(compound))
+ if Hfl(CAS_from_any(compound))== None:
+ return 0
+ else:
+ return float(coeff) * Hfl(Cas_compound)
else:
- return coeff * Hfg(CAS_from_any(compound))
-
+ if Hfg(CAS_from_any(compound))== None:
+ return 0
+ else:
+ return float(coeff) * Hfg(Cas_compound)
+
def entropy(coeff, compound, state):
+ Cas_compound=CAS_from_any(compound)
if state == 'solid':
- return coeff * S0s(CAS_from_any(compound))
+ if S0s(Cas_compound)== None:
+ return 0
+ else:
+ return float(coeff) * S0s(Cas_compound)
elif state == 'liquid':
- return coeff * S0l(CAS_from_any(compound))
+ if S0l(CAS_from_any(compound))== None:
+ return 0
+ else:
+ return float(coeff) * S0l(Cas_compound)
else:
- return coeff * S0g(CAS_from_any(compound))
+ if S0g(CAS_from_any(compound))== None:
+ return 0
+ else:
+ return float(coeff) * S0g(Cas_compound)
+
+
diff --git a/tests/test_compound_state.py b/tests/test_compound_state.py
new file mode 100644
index 0000000..d66a9cd
--- /dev/null
+++ b/tests/test_compound_state.py
@@ -0,0 +1,17 @@
+import pytest
+from chemicals import CAS_from_any
+from chembalancer.chembalancer import compound_state
+
+@pytest.mark.parametrize(
+ "compound, temp_kelvin, expected_state",
+ [
+ ("water", 298.15, "liquid"),
+ ("water", 383.15, "gas"),
+ ("ethanol", 298.15, "liquid"),
+ ("ethanol", 351.15, "liquid"),
+ ("iron", 298.15, "solid"),
+ ("oxygen", 73.15, "liquid"),
+ ]
+)
+def test_compound_state(compound, temp_kelvin, expected_state):
+ assert compound_state(compound, temp_kelvin) == expected_state
diff --git a/tests/test_create_reaction_string.py b/tests/test_create_reaction_string.py
new file mode 100644
index 0000000..cf2d111
--- /dev/null
+++ b/tests/test_create_reaction_string.py
@@ -0,0 +1,47 @@
+from chembalancer.chembalancer import create_reaction_string
+
+
+def test_create_reaction_string():
+ # Test case 1: Reaction with simple reactants and products
+ reactants_1 = ['CH4', 'O2']
+ products_1 = ['CO2', 'H2O']
+ expected_output_1 = "CH4.O2>>CO2.H2O"
+ assert create_reaction_string(reactants_1, products_1) == expected_output_1
+
+ # Test case 2: Reaction with more complex reactants and products
+ reactants_2 = ['C2H6', 'O2']
+ products_2 = ['CO2', 'H2O', 'C2H5OH']
+ expected_output_2 = "C2H6.O2>>CO2.H2O.C2H5OH"
+ assert create_reaction_string(reactants_2, products_2) == expected_output_2
+
+ # Test case 3: Reaction with a single reactant and a single product
+ reactants_3 = ['H2']
+ products_3 = ['H2O']
+ expected_output_3 = "H2>>H2O"
+ assert create_reaction_string(reactants_3, products_3) == expected_output_3
+
+ # Test case 4: Reaction with an empty list of reactants
+ reactants_4 = []
+ products_4 = ['H2O']
+ expected_output_4 = ">>H2O"
+ assert create_reaction_string(reactants_4, products_4) == expected_output_4
+
+ # Test case 5: Reaction with an empty list of products
+ reactants_5 = ['H2']
+ products_5 = []
+ expected_output_5 = "H2>>"
+ assert create_reaction_string(reactants_5, products_5) == expected_output_5
+
+ # Test case 6: Reaction with reactants and products as empty lists
+ reactants_6 = []
+ products_6 = []
+ expected_output_6 = ">>"
+ assert create_reaction_string(reactants_6, products_6) == expected_output_6
+
+ # Test case 7: Reaction with invalid input (integers instead of strings)
+ reactants_7 = [1, 2]
+ products_7 = [3, 4]
+ try:
+ create_reaction_string(reactants_7, products_7)
+ except TypeError as e:
+ assert str(e) == "sequence item 0: expected str instance, int found"
diff --git a/tests/test_display_svg.py b/tests/test_display_svg.py
new file mode 100644
index 0000000..44cd771
--- /dev/null
+++ b/tests/test_display_svg.py
@@ -0,0 +1,32 @@
+# tests/test_display_svg.py
+
+from chembalancer.chembalancer import display_svg
+import base64
+
+def test_display_svg():
+ # Test case 1: Display SVG with a simple circle
+ svg_content_1 = """
+
+ """
+ expected_html_1 = f''
+ assert display_svg(svg_content_1) == expected_html_1
+
+ # Test case 2: Display SVG with a square
+ svg_content_2 = """
+
+ """
+ expected_html_2 = f''
+ assert display_svg(svg_content_2) == expected_html_2
+
+ # Test case 3: Display SVG with a line
+ svg_content_3 = """
+
+ """
+ expected_html_3 = f''
+ assert display_svg(svg_content_3) == expected_html_3
diff --git a/tests/test_enthalpy.py b/tests/test_enthalpy.py
new file mode 100644
index 0000000..992f656
--- /dev/null
+++ b/tests/test_enthalpy.py
@@ -0,0 +1,34 @@
+import pytest
+from unittest.mock import patch
+from chembalancer.chembalancer import enthalpy
+from chemicals import CAS_from_any, Tb, Tm, Tc, Hfs, Hfl, Hfg, S0s, S0l, S0g
+
+@pytest.mark.parametrize(
+ "coeff, compound, state, expected_enthalpy",
+ [
+ (1.0, "water", "solid", 0.0),
+ (2.0, "water", "liquid", -571650.0),
+ (3.0, "water", "gas", -725466.0),
+ (1.0, "ethanol", "solid", 0.0),
+ (2.0, "ethanol", "liquid", -554060),
+ (3.0, "ethanol", "gas", -703710.0),
+ (1.0, "iron", "solid", 0.0),
+ (2.0, "iron", "liquid", 24800.0),
+ (3.0, "iron", "gas", 1248900.0),
+ ]
+)
+def test_enthalpy(coeff, compound, state, expected_enthalpy):
+ with patch("chemicals.CAS_from_any") as mock_CAS_from_any:
+ mock_CAS_from_any.return_value = "dummy_cas"
+
+ with patch("chemicals.Hfs") as mock_Hfs, \
+ patch("chemicals.Hfl") as mock_Hfl, \
+ patch("chemicals.Hfg") as mock_Hfg:
+
+ mock_Hfs.return_value = 0.0
+ mock_Hfl.return_value = 333.55
+ mock_Hfg.return_value = 891.8
+
+ result = enthalpy(coeff, compound, state)
+
+ assert result == pytest.approx(expected_enthalpy, rel=1e-2)
diff --git a/tests/test_entropy.py b/tests/test_entropy.py
new file mode 100644
index 0000000..92f2665
--- /dev/null
+++ b/tests/test_entropy.py
@@ -0,0 +1,44 @@
+import pytest
+from unittest.mock import patch
+from chemicals import CAS_from_any, S0s, S0l, S0g
+from chembalancer.chembalancer import entropy
+
+@pytest.mark.parametrize(
+ "coeff, compound, state, expected_entropy",
+ [
+ (1.0, "water", "solid", 0.0),
+ (2.0, "water", "liquid", 141),
+ (3.0, "water", "gas", 566.4),
+ (1.0, "ethanol", "solid", 0.0),
+ (2.0, "ethanol", "liquid", 321),
+ (3.0, "ethanol", "gas", 845),
+ (1.0, "iron", "solid", 27.2),
+ (2.0, "iron", "liquid", 69),
+ (3.0, "iron", "gas", 540),
+ ]
+)
+
+def test_entropy(coeff, compound, state, expected_entropy):
+ with patch("chemicals.CAS_from_any") as mock_CAS_from_any:
+ mock_CAS_from_any.return_value = "dummy_cas"
+
+ with patch("chemicals.S0s") as mock_S0s, \
+ patch("chemicals.S0l") as mock_S0l, \
+ patch("chemicals.S0g") as mock_S0g:
+
+ if state == "solid":
+ mock_S0s.return_value = expected_entropy
+ mock_S0l.return_value = None
+ mock_S0g.return_value = None
+ elif state == "liquid":
+ mock_S0s.return_value = None
+ mock_S0l.return_value = expected_entropy
+ mock_S0g.return_value = None
+ else:
+ mock_S0s.return_value = None
+ mock_S0l.return_value = None
+ mock_S0g.return_value = expected_entropy
+
+ result = entropy(coeff, compound, state)
+
+ assert result == pytest.approx(expected_entropy, rel=1e-2)