Skip to content

Commit 9dd5a80

Browse files
committed
Address reviewer comments
by CodeRabiit
1 parent a613b54 commit 9dd5a80

3 files changed

Lines changed: 43 additions & 12 deletions

File tree

pyglotaran_extras/inspect/kinetic_scheme/_layout.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class LayoutAlgorithm(str, Enum):
3333
Force-directed Fruchterman-Reingold layout. For complex cyclic
3434
schemes.
3535
MANUAL
36-
User-supplied positions passed through unchanged.
36+
User-supplied compartment positions with derived GS positioning.
3737
"""
3838

3939
HIERARCHICAL = "hierarchical"
@@ -94,9 +94,8 @@ def compute_layout(
9494
horizontal_spacing = 3.0 * DEFAULT_NODE_WIDTH
9595

9696
if algorithm == LayoutAlgorithm.MANUAL:
97-
return _manual_layout(graph, manual_positions)
98-
99-
if algorithm == LayoutAlgorithm.SPRING:
97+
positions = _manual_layout(graph, manual_positions)
98+
elif algorithm == LayoutAlgorithm.SPRING:
10099
positions = _spring_layout(
101100
graph,
102101
horizontal_spacing=horizontal_spacing,

tests/inspect/kinetic_scheme/test_k_matrix_parser.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from __future__ import annotations
44

55
from copy import deepcopy
6+
from types import SimpleNamespace
67

78
import pytest
89
from glotaran.testing.simulated_data.parallel_spectral_decay import SCHEME as SCHEME_PAR
@@ -119,16 +120,23 @@ def test_invalid_megacomplex_raises_value_error(self) -> None:
119120
with pytest.raises(ValueError, match="not found"):
120121
extract_transitions("nonexistent_mc", SCHEME_SEQ.model, SCHEME_SEQ.parameters)
121122

122-
def test_non_decay_megacomplex_raises_type_error(self) -> None:
123+
def test_non_decay_megacomplex_raises_type_error(
124+
self, monkeypatch: pytest.MonkeyPatch
125+
) -> None:
123126
"""Non-decay megacomplex without get_k_matrix raises TypeError."""
124-
# Create a model that has a coherent-artifact megacomplex
125-
# For now, we test with a mock approach
126-
# The sequential model only has decay megacomplexes, so we test
127-
# that valid ones don't raise
128-
transitions = extract_transitions(
129-
"megacomplex_sequential_decay", SCHEME_SEQ.model, SCHEME_SEQ.parameters
127+
model = deepcopy(SCHEME_SEQ.model)
128+
mc_label = "megacomplex_coherent_artifact"
129+
model.megacomplex[mc_label] = SimpleNamespace(type="coherent-artifact")
130+
131+
# Simulate filling a non-decay megacomplex object that does not expose
132+
# get_k_matrix, which should trigger the TypeError path.
133+
monkeypatch.setattr(
134+
"pyglotaran_extras.inspect.kinetic_scheme._k_matrix_parser.fill_item",
135+
lambda *_args, **_kwargs: SimpleNamespace(),
130136
)
131-
assert len(transitions) > 0
137+
138+
with pytest.raises(TypeError, match="does not support k-matrix extraction"):
139+
extract_transitions(mc_label, model, SCHEME_SEQ.parameters)
132140

133141

134142
class TestExtractTransitionsFiltering:

tests/inspect/kinetic_scheme/test_layout.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,30 @@ def test_passthrough(self) -> None:
233233
assert positions["B"] == (1.0, 1.0)
234234
assert positions["C"] == (2.0, 0.0)
235235

236+
def test_ground_state_below_parent(self) -> None:
237+
"""Manual layout should also position ground state nodes below parents."""
238+
graph = _make_sequential_graph()
239+
manual = {"A": (0.0, 2.0), "B": (1.0, 1.0), "C": (2.0, 0.0)}
240+
positions = compute_layout(graph, LayoutAlgorithm.MANUAL, manual_positions=manual)
241+
242+
assert "GS1" in positions
243+
assert positions["GS1"][0] == positions["C"][0]
244+
assert positions["GS1"][1] < positions["C"][1]
245+
246+
def test_avoids_ground_state_arrow_overlap(self) -> None:
247+
"""Manual layout should nudge a node below a GS-decaying parent."""
248+
graph = KineticGraph.from_transitions(
249+
[
250+
Transition("A", "B", 0.5, "rates.k_AB", False, "mc1"),
251+
Transition("A", "GS1", 0.1, "rates.k_A", True, "mc1"),
252+
]
253+
)
254+
manual = {"A": (0.0, 1.0), "B": (0.0, 0.0)}
255+
positions = compute_layout(graph, LayoutAlgorithm.MANUAL, manual_positions=manual)
256+
257+
assert positions["B"][0] > 0.0
258+
assert positions["B"][1] == 0.0
259+
236260
def test_missing_positions_raises(self) -> None:
237261
"""Missing node positions should raise ValueError."""
238262
graph = _make_sequential_graph()

0 commit comments

Comments
 (0)