@@ -82,24 +82,28 @@ def from_file(cls, file_path: str) -> WorkflowType:
8282
8383 data = json .loads (json_str )
8484 type_ = data ["type" ]
85- if type_ == "Simulation" :
86- sim = Simulation .from_file (file_path )
87- elif type_ == "ModeSolver" :
88- sim = ModeSolver .from_file (file_path )
89- elif type_ == "HeatSimulation" :
90- sim = HeatSimulation .from_file (file_path )
91- elif type_ == "HeatChargeSimulation" :
92- sim = HeatChargeSimulation .from_file (file_path )
93- elif type_ == "EMESimulation" :
94- sim = EMESimulation .from_file (file_path )
95- elif type_ == "ModeSimulation" :
96- sim = ModeSimulation .from_file (file_path )
97- elif type_ == "VolumeMesher" :
98- sim = VolumeMesher .from_file (file_path )
99- elif type_ == "ModalComponentModeler" :
100- sim = ModalComponentModeler .from_file (file_path )
101- elif type_ == "TerminalComponentModeler" :
102- sim = TerminalComponentModeler .from_file (file_path )
85+
86+ supported_classes = [
87+ Simulation ,
88+ ModeSolver ,
89+ HeatSimulation ,
90+ HeatChargeSimulation ,
91+ EMESimulation ,
92+ ModeSimulation ,
93+ VolumeMesher ,
94+ ModalComponentModeler ,
95+ TerminalComponentModeler ,
96+ ]
97+
98+ class_map = {cls .__name__ : cls for cls in supported_classes }
99+
100+ if type_ not in class_map :
101+ raise ValueError (
102+ f"Unsupported type '{ type_ } '. Supported types: { list (class_map .keys ())} "
103+ )
104+
105+ sim_class = class_map [type_ ]
106+ sim = sim_class .from_file (file_path )
103107
104108 return sim
105109
@@ -202,7 +206,9 @@ class Tidy3dStubData(BaseModel, TaskStubData):
202206 data : WorkflowDataType
203207
204208 @classmethod
205- def from_file (cls , file_path : str ) -> WorkflowDataType :
209+ def from_file (
210+ cls , file_path : str , lazy : bool = False , on_load : Optional [Callable ] = None
211+ ) -> WorkflowDataType :
206212 """Loads a Union[:class:`.SimulationData`, :class:`.HeatSimulationData`, :class:`.EMESimulationData`]
207213 from .yaml, .json, or .hdf5 file.
208214
@@ -211,6 +217,14 @@ def from_file(cls, file_path: str) -> WorkflowDataType:
211217 file_path : str
212218 Full path to the .yaml or .json or .hdf5 file to load the
213219 Union[:class:`.SimulationData`, :class:`.HeatSimulationData`, :class:`.EMESimulationData`] from.
220+ lazy : bool = False
221+ Whether to load the actual data (``lazy=False``) or return a proxy that loads
222+ the data when accessed (``lazy=True``).
223+ on_load : Callable | None = None
224+ Callback function executed once the model is fully materialized.
225+ Only used if ``lazy=True``. The callback is invoked with the loaded
226+ instance as its sole argument, enabling post-processing such as
227+ validation, logging, or warnings checks.
214228
215229 Returns
216230 -------
@@ -227,24 +241,28 @@ def from_file(cls, file_path: str) -> WorkflowDataType:
227241
228242 data = json .loads (json_str )
229243 type_ = data ["type" ]
230- if type_ == "SimulationData" :
231- sim_data = SimulationData .from_file (file_path )
232- elif type_ == "ModeSolverData" :
233- sim_data = ModeSolverData .from_file (file_path )
234- elif type_ == "HeatSimulationData" :
235- sim_data = HeatSimulationData .from_file (file_path )
236- elif type_ == "HeatChargeSimulationData" :
237- sim_data = HeatChargeSimulationData .from_file (file_path )
238- elif type_ == "EMESimulationData" :
239- sim_data = EMESimulationData .from_file (file_path )
240- elif type_ == "ModeSimulationData" :
241- sim_data = ModeSimulationData .from_file (file_path )
242- elif type_ == "VolumeMesherData" :
243- sim_data = VolumeMesherData .from_file (file_path )
244- elif type_ == "ModalComponentModelerData" :
245- sim_data = ModalComponentModelerData .from_file (file_path )
246- elif type_ == "TerminalComponentModelerData" :
247- sim_data = TerminalComponentModelerData .from_file (file_path )
244+
245+ supported_data_classes = [
246+ SimulationData ,
247+ ModeSolverData ,
248+ HeatSimulationData ,
249+ HeatChargeSimulationData ,
250+ EMESimulationData ,
251+ ModeSimulationData ,
252+ VolumeMesherData ,
253+ ModalComponentModelerData ,
254+ TerminalComponentModelerData ,
255+ ]
256+
257+ data_class_map = {cls .__name__ : cls for cls in supported_data_classes }
258+
259+ if type_ not in data_class_map :
260+ raise ValueError (
261+ f"Unsupported data type '{ type_ } '. Supported types: { list (data_class_map .keys ())} "
262+ )
263+
264+ data_class = data_class_map [type_ ]
265+ sim_data = data_class .from_file (file_path , lazy = lazy , on_load = on_load )
248266
249267 return sim_data
250268
@@ -265,7 +283,7 @@ def to_file(self, file_path: str):
265283 self .data .to_file (file_path )
266284
267285 @classmethod
268- def postprocess (cls , file_path : str ) -> WorkflowDataType :
286+ def postprocess (cls , file_path : str , lazy : bool = True ) -> WorkflowDataType :
269287 """Load .yaml, .json, or .hdf5 file to
270288 Union[:class:`.SimulationData`, :class:`.HeatSimulationData`, :class:`.EMESimulationData`] instance.
271289
@@ -274,16 +292,28 @@ def postprocess(cls, file_path: str) -> WorkflowDataType:
274292 file_path : str
275293 Full path to the .yaml or .json or .hdf5 file to save the
276294 Union[:class:`.SimulationData`, :class:`.HeatSimulationData`, :class:`.EMESimulationData`] to.
295+ lazy : bool = False
296+ Whether to load the actual data (``lazy=False``) or return a proxy that loads
297+ the data when accessed (``lazy=True``).
277298
278299 Returns
279300 -------
280301 Union[:class:`.SimulationData`, :class:`.HeatSimulationData`, :class:`.EMESimulationData`]
281302 An instance of the component class calling ``load``.
282303 """
283- stub_data = Tidy3dStubData .from_file (file_path )
304+ stub_data = Tidy3dStubData .from_file (
305+ file_path , lazy = lazy , on_load = cls ._check_convergence_and_warnings
306+ )
307+ if not lazy :
308+ cls ._check_convergence_and_warnings (stub_data )
309+ return stub_data
284310
285- check_log_msg = "For more information, check 'SimulationData.log' or use "
286- check_log_msg += "'web.download_log(task_id)'."
311+ @staticmethod
312+ def _check_convergence_and_warnings (stub_data : WorkflowDataType ) -> None :
313+ """Check convergence, divergence, and warnings in the solver log and emit log messages."""
314+ check_log_msg = (
315+ "For more information, check 'SimulationData.log' or use 'web.download_log(task_id)'."
316+ )
287317 warned_about_warnings = False
288318
289319 if isinstance (stub_data , SimulationData ):
@@ -313,5 +343,3 @@ def postprocess(cls, file_path: str) -> WorkflowDataType:
313343 and not warned_about_warnings
314344 ):
315345 log .warning ("Warning messages were found in the solver log. " + check_log_msg )
316-
317- return stub_data
0 commit comments