Skip to content

Commit

Permalink
FINITO
Browse files Browse the repository at this point in the history
  • Loading branch information
faneshala committed May 29, 2024
1 parent 4bfb782 commit b611d56
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 14 deletions.
53 changes: 39 additions & 14 deletions src/chembalancer/chembalancer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from collections import defaultdict
from rdkit import Chem
import requests

import base64


def get_smiles_from_name(name):
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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"<img src='data:image/svg+xml;base64,{b64}'/>"
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)


17 changes: 17 additions & 0 deletions tests/test_compound_state.py
Original file line number Diff line number Diff line change
@@ -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
47 changes: 47 additions & 0 deletions tests/test_create_reaction_string.py
Original file line number Diff line number Diff line change
@@ -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"
32 changes: 32 additions & 0 deletions tests/test_display_svg.py
Original file line number Diff line number Diff line change
@@ -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 = """
<svg height="100" width="100">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
</svg>
"""
expected_html_1 = f'<img src=\'data:image/svg+xml;base64,{base64.b64encode(svg_content_1.encode("utf-8")).decode("utf-8")}\'/>'
assert display_svg(svg_content_1) == expected_html_1

# Test case 2: Display SVG with a square
svg_content_2 = """
<svg height="100" width="100">
<rect width="100" height="100" style="fill:rgb(0,0,255);stroke-width:1;stroke:rgb(0,0,0)" />
</svg>
"""
expected_html_2 = f'<img src=\'data:image/svg+xml;base64,{base64.b64encode(svg_content_2.encode("utf-8")).decode("utf-8")}\'/>'
assert display_svg(svg_content_2) == expected_html_2

# Test case 3: Display SVG with a line
svg_content_3 = """
<svg height="100" width="100">
<line x1="0" y1="0" x2="100" y2="100" style="stroke:rgb(255,0,0);stroke-width:2" />
</svg>
"""
expected_html_3 = f'<img src=\'data:image/svg+xml;base64,{base64.b64encode(svg_content_3.encode("utf-8")).decode("utf-8")}\'/>'
assert display_svg(svg_content_3) == expected_html_3
34 changes: 34 additions & 0 deletions tests/test_enthalpy.py
Original file line number Diff line number Diff line change
@@ -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)
44 changes: 44 additions & 0 deletions tests/test_entropy.py
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit b611d56

Please sign in to comment.