Entropyk/bindings/python/tests/test_solver.py

148 lines
4.6 KiB
Python

"""Entropyk — End-to-End Solver Tests.
Tests for System construction, finalization, and solving from Python.
"""
import pytest
import entropyk
class TestSystemConstruction:
"""Tests for System graph building."""
def test_empty_system(self):
system = entropyk.System()
assert system.node_count == 0
assert system.edge_count == 0
def test_add_component(self):
system = entropyk.System()
idx = system.add_component(entropyk.Condenser(ua=5000.0))
assert idx == 0
assert system.node_count == 1
def test_add_multiple_components(self):
system = entropyk.System()
i0 = system.add_component(entropyk.Compressor())
i1 = system.add_component(entropyk.Condenser())
i2 = system.add_component(entropyk.ExpansionValve())
i3 = system.add_component(entropyk.Evaporator())
assert system.node_count == 4
assert i0 != i1 != i2 != i3
def test_add_edge(self):
system = entropyk.System()
i0 = system.add_component(entropyk.Compressor())
i1 = system.add_component(entropyk.Condenser())
edge_idx = system.add_edge(i0, i1)
assert edge_idx == 0
assert system.edge_count == 1
def test_repr(self):
system = entropyk.System()
system.add_component(entropyk.Compressor())
system.add_component(entropyk.Condenser())
system.add_edge(0, 1)
r = repr(system)
assert "System" in r
assert "nodes=2" in r
assert "edges=1" in r
class TestSystemFinalize:
"""Tests for system finalization."""
def test_simple_cycle_finalize(self):
"""Build and finalize a simple 4-component cycle."""
system = entropyk.System()
comp = system.add_component(entropyk.Compressor())
cond = system.add_component(entropyk.Condenser())
exv = system.add_component(entropyk.ExpansionValve())
evap = system.add_component(entropyk.Evaporator())
system.add_edge(comp, cond)
system.add_edge(cond, exv)
system.add_edge(exv, evap)
system.add_edge(evap, comp)
system.finalize()
assert system.state_vector_len > 0
class TestSolverConfigs:
"""Tests for solver configuration objects."""
def test_newton_default(self):
config = entropyk.NewtonConfig()
assert "NewtonConfig" in repr(config)
assert "100" in repr(config)
def test_newton_custom(self):
config = entropyk.NewtonConfig(
max_iterations=200,
tolerance=1e-8,
line_search=True,
timeout_ms=5000,
)
assert "200" in repr(config)
def test_picard_default(self):
config = entropyk.PicardConfig()
assert "PicardConfig" in repr(config)
def test_picard_custom(self):
config = entropyk.PicardConfig(
max_iterations=300,
tolerance=1e-5,
relaxation=0.7,
)
assert "300" in repr(config)
def test_picard_invalid_relaxation_raises(self):
with pytest.raises(ValueError, match="between"):
entropyk.PicardConfig(relaxation=1.5)
def test_fallback_default(self):
config = entropyk.FallbackConfig()
assert "FallbackConfig" in repr(config)
def test_fallback_custom(self):
newton = entropyk.NewtonConfig(max_iterations=50)
picard = entropyk.PicardConfig(max_iterations=200)
config = entropyk.FallbackConfig(newton=newton, picard=picard)
assert "50" in repr(config)
class TestConvergedState:
"""Tests for ConvergedState and ConvergenceStatus types."""
def test_convergence_status_repr(self):
# We can't easily create a ConvergedState without solving,
# so we just verify the classes exist
assert hasattr(entropyk, "ConvergedState")
assert hasattr(entropyk, "ConvergenceStatus")
class TestAllComponentsInSystem:
"""Test that all component types can be added to a System."""
@pytest.mark.parametrize("component_factory", [
lambda: entropyk.Compressor(),
lambda: entropyk.Condenser(),
lambda: entropyk.Evaporator(),
lambda: entropyk.Economizer(),
lambda: entropyk.ExpansionValve(),
lambda: entropyk.Pipe(),
lambda: entropyk.Pump(),
lambda: entropyk.Fan(),
lambda: entropyk.FlowSplitter(),
lambda: entropyk.FlowMerger(),
lambda: entropyk.FlowSource(),
lambda: entropyk.FlowSink(),
])
def test_add_component(self, component_factory):
system = entropyk.System()
idx = system.add_component(component_factory())
assert idx >= 0
assert system.node_count == 1