Skip to content

Commit e404a1e

Browse files
Tests and validation for IndexSimulation/Data
1 parent d14430d commit e404a1e

File tree

5 files changed

+182
-63
lines changed

5 files changed

+182
-63
lines changed

tests/test_components/test_index.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
from __future__ import annotations
2+
3+
import pydantic.v1 as pydantic
4+
import pytest
5+
6+
from tidy3d import IndexSimulation, Simulation
7+
8+
from ..utils import SAMPLE_SIMULATIONS
9+
10+
# Reusable test constants
11+
SIM_INDEX = ("sim_A", "sim_B")
12+
DATA_INDEX = ("data_A", "data_B")
13+
14+
15+
def make_simulations() -> tuple[Simulation, ...]:
16+
"""Creates a tuple of simple, distinct Simulation objects for testing."""
17+
sim1 = SAMPLE_SIMULATIONS["full_fdtd"]
18+
sim2 = SAMPLE_SIMULATIONS["full_fdtd"]
19+
return (sim1, sim2)
20+
21+
22+
def make_index_simulation(**kwargs) -> IndexSimulation:
23+
"""Factory function to create a standard IndexSimulation instance."""
24+
return IndexSimulation(index=SIM_INDEX, simulation=make_simulations(), **kwargs)
25+
26+
27+
def test_index_simulation_creation():
28+
"""Tests successful creation of an IndexSimulation instance."""
29+
sims = make_simulations()
30+
container = make_index_simulation()
31+
assert container.index == SIM_INDEX
32+
assert container.simulation == sims
33+
assert len(container.index) == len(container.simulation)
34+
35+
36+
def test_index_simulation_mismatched_length_raises_error():
37+
"""Tests that a ValueError is raised for mismatched index and simulation lengths."""
38+
sims = make_simulations()
39+
with pytest.raises(pydantic.ValidationError, match="Length of 'index' and 'simulation'"):
40+
IndexSimulation(index=("only_one_index",), simulation=sims)
41+
42+
43+
def test_index_simulation_getitem_success():
44+
"""Tests successful retrieval of a simulation by its string index."""
45+
container = make_index_simulation()
46+
sims = make_simulations()
47+
assert container[SIM_INDEX[0]] == sims[0]
48+
assert container[SIM_INDEX[1]] == sims[1]
49+
50+
51+
def test_index_simulation_getitem_key_error():
52+
"""Tests that a KeyError is raised when retrieving a non-existent index."""
53+
container = make_index_simulation()
54+
with pytest.raises(KeyError, match="Index 'not_a_real_key' not found."):
55+
_ = container["not_a_real_key"]

tests/test_data/test_index.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
from __future__ import annotations
2+
3+
import pydantic.v1 as pydantic
4+
import pytest
5+
6+
from tidy3d import IndexSimulation, IndexSimulationData, Simulation, SimulationData
7+
8+
from ..utils import SAMPLE_SIMULATIONS, run_emulated
9+
10+
# Reusable test constants
11+
SIM_INDEX = ("sim_A", "sim_B")
12+
DATA_INDEX = ("data_A", "data_B")
13+
14+
15+
def make_simulations() -> tuple[Simulation, ...]:
16+
"""Creates a tuple of simple, distinct Simulation objects for testing."""
17+
sim1 = SAMPLE_SIMULATIONS["full_fdtd"]
18+
sim2 = SAMPLE_SIMULATIONS["full_fdtd"].updated_copy(run_time=2e-12)
19+
return (sim1, sim2)
20+
21+
22+
def make_simulation_data() -> tuple[SimulationData, ...]:
23+
"""Creates a tuple of simple SimulationData objects for testing."""
24+
sims = make_simulations()
25+
data1 = run_emulated(sims[0])
26+
data2 = run_emulated(sims[1])
27+
return (data1, data2)
28+
29+
30+
def make_index_simulation(**kwargs) -> IndexSimulation:
31+
"""Factory function to create a standard IndexSimulation instance."""
32+
return IndexSimulation(index=SIM_INDEX, simulation=make_simulations(), **kwargs)
33+
34+
35+
def make_index_simulation_data(**kwargs) -> IndexSimulationData:
36+
"""Factory function to create a standard IndexSimulationData instance."""
37+
return IndexSimulationData(index=DATA_INDEX, data=make_simulation_data(), **kwargs)
38+
39+
40+
def test_index_simulation_data_creation():
41+
"""Tests successful creation of an IndexSimulationData instance."""
42+
sim_data = make_simulation_data()
43+
container = make_index_simulation_data()
44+
assert container.index == DATA_INDEX
45+
assert container.data == sim_data
46+
assert len(container.index) == len(container.data)
47+
48+
49+
def test_index_simulation_data_mismatched_length_raises_error():
50+
"""Tests that a ValueError is raised for mismatched index and data lengths."""
51+
sim_data = make_simulation_data()
52+
with pytest.raises(pydantic.ValidationError, match="Length of 'index' and 'data'"):
53+
IndexSimulationData(index=("only_one_index",), data=sim_data)
54+
55+
56+
def test_index_simulation_data_getitem_success():
57+
"""Tests successful retrieval of simulation data by its string index."""
58+
container = make_index_simulation_data()
59+
sim_data = make_simulation_data()
60+
assert container[DATA_INDEX[0]] == sim_data[0]
61+
assert container[DATA_INDEX[1]] == sim_data[1]
62+
63+
64+
def test_index_simulation_data_getitem_key_error():
65+
"""Tests that a KeyError is raised when retrieving a non-existent index."""
66+
container = make_index_simulation_data()
67+
with pytest.raises(KeyError, match="Index 'not_a_real_key' not found."):
68+
_ = container["not_a_real_key"]

tests/test_plugins/smatrix/test_component_modeler.py

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -412,47 +412,3 @@ def test_mapping_with_run_only():
412412
run_only.remove(EXCLUDE_INDEX)
413413
with pytest.raises(pydantic.ValidationError):
414414
_ = make_component_modeler(element_mappings=element_mappings, run_only=run_only)
415-
416-
417-
# def test_batch_filename(tmp_path):
418-
# modeler = make_component_modeler()
419-
# path = modeler._batch_path
420-
# assert path
421-
# def test_import_smatrix_smatrix():
422-
# from tidy3d.plugins.smatrix.smatrix import ComponentModeler, Port
423-
424-
# def test_to_from_file_empty_batch(tmp_path):
425-
# modeler = make_component_modeler()
426-
#
427-
# fname = str(tmp_path) + "/modeler.json"
428-
#
429-
# modeler.to_file(fname)
430-
# modeler2 = modeler.from_file(fname)
431-
#
432-
# assert modeler2.batch_cached is None
433-
#
434-
#
435-
# def test_to_from_file_batch(tmp_path, monkeypatch):
436-
# modeler = make_component_modeler()
437-
# _ = run_component_modeler(monkeypatch, modeler)
438-
#
439-
# batch = td.web.Batch(simulations={})
440-
#
441-
# modeler._cached_properties["batch"] = batch
442-
#
443-
# fname = str(tmp_path) + "/modeler.json"
444-
#
445-
# modeler.to_file(fname)
446-
# modeler2 = modeler.from_file(fname)
447-
#
448-
# # BREAK this test because it introduces mutability which shouldn't exist
449-
# assert modeler2.batch_cached == modeler2.batch == batch
450-
#
451-
#
452-
# def test_non_default_path_dir(monkeypatch):
453-
# modeler = make_component_modeler(path_dir="not_default")
454-
# monkeypatch.setattr(ComponentModeler, "_construct_smatrix", lambda self: None)
455-
# modeler.run()
456-
# modeler.run(path_dir="not_default")
457-
# with pytest.raises(ValueError):
458-
# modeler.run(path_dir="a_new_path")

tidy3d/components/data/index.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,45 @@
11
from __future__ import annotations
22

3+
import pydantic.v1 as pd
4+
35
from tidy3d.components.base import Tidy3dBaseModel
46
from tidy3d.components.simulaton_types import SimulationDataType
57

68

79
class IndexSimulationData(Tidy3dBaseModel):
10+
"""Container for a set of simulation data, accessible by a string index."""
11+
812
index: tuple[str, ...]
13+
"""Tuple of unique string identifiers for each simulation data object."""
14+
915
data: tuple[SimulationDataType, ...]
16+
"""Tuple of :class:`.SimulationData` objects corresponding to each index."""
17+
18+
@pd.root_validator()
19+
def _validate_lengths_match(cls, values):
20+
"""Validate that index and data have the same length."""
21+
index, data = values.get("index"), values.get("data")
22+
if index is not None and data is not None and len(index) != len(data):
23+
raise ValueError("Length of 'index' and 'data' must be the same.")
24+
return values
1025

1126
def __getitem__(self, index: str) -> SimulationDataType:
12-
"""
13-
Allows retrieving simulation data by the port name.
27+
"""Allows retrieving simulation data by its index name.
1428
15-
Args:
16-
port_name: The string name of the port to look up.
29+
Parameters
30+
----------
31+
index : str
32+
The string name of the simulation data to look up.
1733
18-
Returns:
19-
The SimulationData object corresponding to the given port name.
34+
Returns
35+
-------
36+
SimulationDataType
37+
The :class:`.SimulationData` object corresponding to the given index name.
2038
21-
Raises:
22-
KeyError: If no port with the given name is found.
39+
Raises
40+
------
41+
KeyError
42+
If no simulation data with the given index name is found.
2343
"""
2444
for i, index_i in enumerate(self.index):
2545
if index_i == index:

tidy3d/components/index.py

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,46 @@
22

33
from typing import Union
44

5+
import pydantic.v1 as pd
6+
57
from tidy3d.components.base import Tidy3dBaseModel
68
from tidy3d.components.simulaton_types import SimulationType
79

810

911
class IndexSimulation(Tidy3dBaseModel):
10-
index: tuple[str, ...]
11-
simulation: tuple[SimulationType, ...]
12+
"""Container for a set of simulations, accessible by a string index."""
1213

13-
def __getitem__(self, index: Union[str, int]) -> SimulationType:
14-
"""
15-
Allows retrieving simulation data by the port name.
14+
index: tuple[str, ...]
15+
"""Tuple of unique string identifiers for each simulation."""
1616

17-
Args:
18-
port_name: The string name of the port to look up.
17+
simulation: tuple[SimulationType, ...]
18+
"""Tuple of :class:`.Simulation` objects corresponding to each index."""
1919

20-
Returns:
21-
The SimulationData object corresponding to the given port name.
20+
@pd.root_validator()
21+
def _validate_lengths_match(cls, values):
22+
"""Validate that index and simulation have the same length."""
23+
index, simulation = values.get("index"), values.get("simulation")
24+
if index is not None and simulation is not None and len(index) != len(simulation):
25+
raise ValueError("Length of 'index' and 'simulation' must be the same.")
26+
return values
2227

23-
Raises:
24-
KeyError: If no port with the given name is found.
28+
def __getitem__(self, index: Union[str, int]) -> SimulationType:
29+
"""Allows retrieving a simulation by its index name.
30+
31+
Parameters
32+
----------
33+
index : Union[str, int]
34+
The string name or integer index of the simulation to look up.
35+
36+
Returns
37+
-------
38+
SimulationType
39+
The :class:`.Simulation` object corresponding to the given index.
40+
41+
Raises
42+
------
43+
KeyError
44+
If no simulation with the given index name is found.
2545
"""
2646
for i, index_i in enumerate(self.index):
2747
if index_i == index:

0 commit comments

Comments
 (0)