Skip to content

Commit e619552

Browse files
Creating custom web flow
1 parent 9b7182d commit e619552

File tree

3 files changed

+1486
-0
lines changed

3 files changed

+1486
-0
lines changed

tidy3d/plugins/smatrix/web/__init__.py

Whitespace-only changes.
Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
"""Stub for webapi"""
2+
3+
from __future__ import annotations
4+
5+
import json
6+
from typing import Callable, Optional, Union
7+
8+
import pydantic.v1 as pd
9+
from pydantic.v1 import BaseModel
10+
11+
from tidy3d import log
12+
from tidy3d.components.base import _get_valid_extension
13+
from tidy3d.components.data.monitor_data import ModeSolverData
14+
from tidy3d.components.data.sim_data import SimulationData
15+
from tidy3d.components.eme.data.sim_data import EMESimulationData
16+
from tidy3d.components.eme.simulation import EMESimulation
17+
from tidy3d.components.mode.data.sim_data import ModeSimulationData
18+
from tidy3d.components.mode.simulation import ModeSimulation
19+
from tidy3d.components.simulation import Simulation
20+
from tidy3d.components.tcad.data.sim_data import (
21+
HeatChargeSimulationData,
22+
HeatSimulationData,
23+
VolumeMesherData,
24+
)
25+
from tidy3d.components.tcad.mesher import VolumeMesher
26+
from tidy3d.components.tcad.simulation.heat import HeatSimulation
27+
from tidy3d.components.tcad.simulation.heat_charge import HeatChargeSimulation
28+
from tidy3d.plugins.mode.mode_solver import ModeSolver
29+
from tidy3d.plugins.smatrix import (
30+
ComponentModeler,
31+
ComponentModelerData,
32+
TerminalComponentModeler,
33+
TerminalComponentModelerData,
34+
)
35+
from tidy3d.web.core.file_util import (
36+
read_simulation_from_hdf5,
37+
read_simulation_from_hdf5_gz,
38+
read_simulation_from_json,
39+
)
40+
from tidy3d.web.core.stub import TaskStub, TaskStubData
41+
from tidy3d.web.core.types import TaskType
42+
43+
SimulationType = Union[
44+
Simulation,
45+
HeatChargeSimulation,
46+
HeatSimulation,
47+
EMESimulation,
48+
ModeSolver,
49+
ModeSimulation,
50+
VolumeMesher,
51+
ComponentModeler,
52+
TerminalComponentModeler,
53+
]
54+
SimulationDataType = Union[
55+
SimulationData,
56+
HeatChargeSimulationData,
57+
HeatSimulationData,
58+
EMESimulationData,
59+
ModeSolverData,
60+
ModeSimulationData,
61+
ComponentModelerData,
62+
TerminalComponentModelerData,
63+
]
64+
65+
66+
class Tidy3dStub(BaseModel, TaskStub):
67+
simulation: SimulationType = pd.Field(discriminator="type")
68+
69+
@classmethod
70+
def from_file(cls, file_path: str) -> SimulationType:
71+
"""Loads a Union[:class:`.Simulation`, :class:`.HeatSimulation`, :class:`.EMESimulation`]
72+
from .yaml, .json, or .hdf5 file.
73+
74+
Parameters
75+
----------
76+
file_path : str
77+
Full path to the .yaml or .json or .hdf5 file to load the
78+
Union[:class:`.Simulation`, :class:`.HeatSimulation`, :class:`.EMESimulation`] from.
79+
80+
Returns
81+
-------
82+
Union[:class:`.Simulation`, :class:`.HeatSimulation`, :class:`.EMESimulation`]
83+
An instance of the component class calling ``load``.
84+
85+
Example
86+
-------
87+
>>> simulation = Simulation.from_file(fname='folder/sim.json') # doctest: +SKIP
88+
"""
89+
extension = _get_valid_extension(file_path)
90+
if extension == ".json":
91+
json_str = read_simulation_from_json(file_path)
92+
elif extension == ".hdf5":
93+
json_str = read_simulation_from_hdf5(file_path)
94+
elif extension == ".hdf5.gz":
95+
json_str = read_simulation_from_hdf5_gz(file_path)
96+
97+
data = json.loads(json_str)
98+
type_ = data["type"]
99+
if type_ == "Simulation":
100+
sim = Simulation.from_file(file_path)
101+
elif type_ == "ModeSolver":
102+
sim = ModeSolver.from_file(file_path)
103+
elif type_ == "HeatSimulation":
104+
sim = HeatSimulation.from_file(file_path)
105+
elif type_ == "HeatChargeSimulation":
106+
sim = HeatChargeSimulation.from_file(file_path)
107+
elif type_ == "EMESimulation":
108+
sim = EMESimulation.from_file(file_path)
109+
elif type_ == "ModeSimulation":
110+
sim = ModeSimulation.from_file(file_path)
111+
elif type_ == "VolumeMesher":
112+
sim = VolumeMesher.from_file(file_path)
113+
elif type_ == "ComponentModeler":
114+
sim = ComponentModeler.from_file(file_path)
115+
elif type_ == "TerminalComponentModeler":
116+
sim = TerminalComponentModeler.from_file(file_path)
117+
118+
return sim
119+
120+
def to_file(
121+
self,
122+
file_path: str,
123+
):
124+
"""Exports Union[:class:`.Simulation`, :class:`.HeatSimulation`, :class:`.EMESimulation`] instance to .yaml, .json,
125+
or .hdf5 file
126+
127+
Parameters
128+
----------
129+
file_path : str
130+
Full path to the .yaml or .json or .hdf5 file to save the :class:`Stub` to.
131+
132+
Example
133+
-------
134+
>>> simulation.to_file(fname='folder/sim.json') # doctest: +SKIP
135+
"""
136+
self.simulation.to_file(file_path)
137+
138+
def to_hdf5_gz(self, fname: str, custom_encoders: Optional[list[Callable]] = None) -> None:
139+
"""Exports Union[:class:`.Simulation`, :class:`.HeatSimulation`, :class:`.EMESimulation`] instance to .hdf5.gz file.
140+
141+
Parameters
142+
----------
143+
fname : str
144+
Full path to the .hdf5.gz file to save
145+
the Union[:class:`.Simulation`, :class:`.HeatSimulation`, :class:`.EMESimulation`] to.
146+
custom_encoders : List[Callable]
147+
List of functions accepting (fname: str, group_path: str, value: Any) that take
148+
the ``value`` supplied and write it to the hdf5 ``fname`` at ``group_path``.
149+
150+
Example
151+
-------
152+
>>> simulation.to_hdf5_gz(fname='folder/sim.hdf5.gz') # doctest: +SKIP
153+
"""
154+
155+
self.simulation.to_hdf5_gz(fname)
156+
157+
def get_type(self) -> str:
158+
"""Get simulation instance type.
159+
160+
Returns
161+
-------
162+
:class:`TaskType`
163+
An instance Type of the component class calling ``load``.
164+
"""
165+
if isinstance(self.simulation, Simulation):
166+
return TaskType.FDTD.name
167+
if isinstance(self.simulation, ModeSolver):
168+
return TaskType.MODE_SOLVER.name
169+
if isinstance(self.simulation, HeatSimulation):
170+
return TaskType.HEAT.name
171+
if isinstance(self.simulation, HeatChargeSimulation):
172+
return TaskType.HEAT_CHARGE.name
173+
if isinstance(self.simulation, EMESimulation):
174+
return TaskType.EME.name
175+
if isinstance(self.simulation, ModeSimulation):
176+
return TaskType.MODE.name
177+
elif isinstance(self.simulation, VolumeMesher):
178+
return TaskType.VOLUME_MESH.name
179+
elif isinstance(self.simulation, ComponentModeler):
180+
return TaskType.COMPONENT_MODELER.name
181+
elif isinstance(self.simulation, TerminalComponentModeler):
182+
return TaskType.TERMINAL_COMPONENT_MODELER.name
183+
184+
def validate_pre_upload(self, source_required) -> None:
185+
"""Perform some pre-checks on instances of component"""
186+
if isinstance(self.simulation, Simulation):
187+
self.simulation.validate_pre_upload(source_required)
188+
elif isinstance(self.simulation, EMESimulation):
189+
self.simulation.validate_pre_upload()
190+
191+
192+
class Tidy3dStubData(BaseModel, TaskStubData):
193+
""""""
194+
195+
data: SimulationDataType
196+
197+
@classmethod
198+
def from_file(cls, file_path: str) -> SimulationDataType:
199+
"""Loads a Union[:class:`.SimulationData`, :class:`.HeatSimulationData`, :class:`.EMESimulationData`]
200+
from .yaml, .json, or .hdf5 file.
201+
202+
Parameters
203+
----------
204+
file_path : str
205+
Full path to the .yaml or .json or .hdf5 file to load the
206+
Union[:class:`.SimulationData`, :class:`.HeatSimulationData`, :class:`.EMESimulationData`] from.
207+
208+
Returns
209+
-------
210+
Union[:class:`.SimulationData`, :class:`.HeatSimulationData`, :class:`.EMESimulationData`]
211+
An instance of the component class calling ``load``.
212+
"""
213+
extension = _get_valid_extension(file_path)
214+
if extension == ".json":
215+
json_str = read_simulation_from_json(file_path)
216+
elif extension == ".hdf5":
217+
json_str = read_simulation_from_hdf5(file_path)
218+
elif extension == ".hdf5.gz":
219+
json_str = read_simulation_from_hdf5_gz(file_path)
220+
221+
data = json.loads(json_str)
222+
type_ = data["type"]
223+
if type_ == "SimulationData":
224+
sim_data = SimulationData.from_file(file_path)
225+
elif type_ == "ModeSolverData":
226+
sim_data = ModeSolverData.from_file(file_path)
227+
elif type_ == "HeatSimulationData":
228+
sim_data = HeatSimulationData.from_file(file_path)
229+
elif type_ == "HeatChargeSimulationData":
230+
sim_data = HeatChargeSimulationData.from_file(file_path)
231+
elif type_ == "EMESimulationData":
232+
sim_data = EMESimulationData.from_file(file_path)
233+
elif type_ == "ModeSimulationData":
234+
sim_data = ModeSimulationData.from_file(file_path)
235+
elif type_ == "VolumeMesherData":
236+
sim_data = VolumeMesherData.from_file(file_path)
237+
elif type_ == "ComponentModelerData":
238+
sim_data = ComponentModelerData.from_file(file_path)
239+
elif type_ == "TerminalComponentModelerData":
240+
sim_data = TerminalComponentModelerData.from_file(file_path)
241+
242+
return sim_data
243+
244+
def to_file(self, file_path: str):
245+
"""Exports Union[:class:`.SimulationData`, :class:`.HeatSimulationData`, :class:`.EMESimulationData`] instance
246+
to .yaml, .json, or .hdf5 file
247+
248+
Parameters
249+
----------
250+
file_path : str
251+
Full path to the .yaml or .json or .hdf5 file to save the
252+
Union[:class:`.SimulationData`, :class:`.HeatSimulationData`, :class:`.EMESimulationData`] to.
253+
254+
Example
255+
-------
256+
>>> simulation.to_file(fname='folder/sim.json') # doctest: +SKIP
257+
"""
258+
self.data.to_file(file_path)
259+
260+
@classmethod
261+
def postprocess(cls, file_path: str) -> SimulationDataType:
262+
"""Load .yaml, .json, or .hdf5 file to
263+
Union[:class:`.SimulationData`, :class:`.HeatSimulationData`, :class:`.EMESimulationData`] instance.
264+
265+
Parameters
266+
----------
267+
file_path : str
268+
Full path to the .yaml or .json or .hdf5 file to save the
269+
Union[:class:`.SimulationData`, :class:`.HeatSimulationData`, :class:`.EMESimulationData`] to.
270+
271+
Returns
272+
-------
273+
Union[:class:`.SimulationData`, :class:`.HeatSimulationData`, :class:`.EMESimulationData`]
274+
An instance of the component class calling ``load``.
275+
"""
276+
stub_data = Tidy3dStubData.from_file(file_path)
277+
278+
check_log_msg = "For more information, check 'SimulationData.log' or use "
279+
check_log_msg += "'web.download_log(task_id)'."
280+
warned_about_warnings = False
281+
282+
if isinstance(stub_data, SimulationData):
283+
final_decay_value = stub_data.final_decay_value
284+
shutoff_value = stub_data.simulation.shutoff
285+
if stub_data.diverged:
286+
log.warning("The simulation has diverged! " + check_log_msg)
287+
warned_about_warnings = True
288+
elif (shutoff_value != 0) and (final_decay_value > shutoff_value):
289+
log.warning(
290+
f"Simulation final field decay value of {final_decay_value} is greater than "
291+
f"the simulation shutoff threshold of {shutoff_value}. Consider running the "
292+
"simulation again with a larger 'run_time' duration for more accurate results."
293+
)
294+
295+
if (
296+
not isinstance(stub_data, (ModeSolverData, ModeSimulationData))
297+
and "WARNING" in stub_data.log
298+
and not warned_about_warnings
299+
):
300+
log.warning("Warning messages were found in the solver log. " + check_log_msg)
301+
302+
return stub_data

0 commit comments

Comments
 (0)