Skip to content

Commit 45493d6

Browse files
weiQingengWei
authored andcommitted
add batch id
1 parent 76f1387 commit 45493d6

File tree

8 files changed

+277
-17
lines changed

8 files changed

+277
-17
lines changed

tests/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ Inside `dir/test_code.py` pytest will run any code in the main file and then cal
1717

1818
# gets evaluated first
1919
my_two = 2.0
20-
my_three = add_one(my_two)
20+
my_three = add_one(my_two)
21+
2122

2223
def add_one(x):
2324
# calls this function

tidy3d/plugins/adjoint/web.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ def start(self) -> None:
281281
To monitor progress of the :class:`Job`, call :meth:`Job.monitor` after started.
282282
"""
283283
if self.jax_info is not None:
284-
upload_jax_info(task_id=self.task_id, jax_info=self.jax_info, verbose=self.verbose)
284+
upload_jax_info(task_id=self.task_id(), jax_info=self.jax_info, verbose=self.verbose)
285285
super().start()
286286

287287

tidy3d/plugins/invdes/test.py

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import numpy as np
2+
3+
import tidy3d as td
4+
import tidy3d.plugins.invdes as tdi
5+
6+
from .web import Job
7+
8+
# source info
9+
wavelength = 1.0
10+
11+
# waveguide parameters
12+
num_output_waveguides = 3
13+
ly_wg = 0.5 * wavelength
14+
buffer_wg = 0.9 * wavelength
15+
16+
# buffer between design region, pml, and sources
17+
buffer = 1 * wavelength
18+
19+
# relative permittivity of material
20+
eps_mat = 4.0
21+
22+
# resolution (for both the FDTD simulation and for the design region)
23+
min_steps_per_wvl = 30
24+
pixel_size = wavelength / min_steps_per_wvl / np.sqrt(eps_mat)
25+
26+
# spectral information
27+
freq0 = td.C_0 / wavelength
28+
fwidth = freq0 / 10
29+
run_time = 50 / fwidth
30+
31+
# design region size in y
32+
ly_des = num_output_waveguides * (ly_wg + buffer_wg)
33+
lx_des = 4 * wavelength
34+
35+
# simulation size
36+
Lx = 2 * buffer + lx_des + 2 * buffer
37+
Ly = buffer + ly_des + buffer
38+
39+
# source and monitor locations
40+
x_src = -lx_des / 2 - buffer
41+
x_mnt = -x_src
42+
43+
# material Medium
44+
medium = td.Medium(permittivity=eps_mat)
45+
46+
# grid spec
47+
grid_spec = td.GridSpec.auto(wavelength=wavelength, min_steps_per_wvl=min_steps_per_wvl)
48+
49+
50+
# monitor names
51+
def output_monitor_name(i: int) -> str:
52+
return f"MNT_{i}"
53+
54+
55+
field_mnt_name = "field"
56+
57+
# mode spec
58+
mode_spec = td.ModeSpec(num_modes=1)
59+
60+
61+
if __name__ == "__main__":
62+
# radius (um) of the conic filter that is convolved with th parameter array.
63+
# Larger values tend to help create larger feature sizes in the final device.
64+
projection_radius = 0.120
65+
66+
# projection strength, larger values lead to more binarization and tend to push intermediate parameters towards (0,1) density
67+
beta = 10.0
68+
69+
# transformations on the parameters that lead to the material density array (0,1)
70+
filter_project = tdi.FilterProject(radius=projection_radius, beta=beta)
71+
72+
# length scale (um) of the erosion dilation penalty.
73+
# features smaller than this scale will be penalized
74+
length_scale = 0.120
75+
76+
# penalty weight, the penalty contributes its raw value (max of 1) times this weight to the objective function
77+
weight = 0.8
78+
79+
# penalties applied to the state of the material density, after these transformations are applied
80+
penalty = tdi.ErosionDilationPenalty(weight=weight, length_scale=length_scale)
81+
82+
design_region = tdi.TopologyDesignRegion(
83+
size=(lx_des, ly_des, td.inf),
84+
center=(0, 0, 0),
85+
eps_bounds=(1.0, eps_mat), # the minimum and maximum permittivity values in the final grid
86+
transformations=[filter_project],
87+
penalties=[penalty],
88+
pixel_size=pixel_size,
89+
)
90+
91+
waveguide_in = td.Structure(
92+
geometry=td.Box(
93+
size=(Lx, ly_wg, td.inf),
94+
center=(-Lx + 2 * buffer, 0, 0),
95+
),
96+
medium=medium,
97+
)
98+
99+
y_max_wg_centers = ly_des / 2 - buffer_wg / 2 - ly_wg / 2
100+
wg_y_centers_out = np.linspace(-y_max_wg_centers, y_max_wg_centers, num_output_waveguides)
101+
102+
# put a waveguide and mode monitor at each of the outputs
103+
waveguides_out = []
104+
monitors_out = []
105+
for i, wg_y_center in enumerate(wg_y_centers_out):
106+
wg_out = td.Structure(
107+
geometry=td.Box(
108+
size=(Lx, ly_wg, td.inf),
109+
center=(Lx - 2 * buffer, wg_y_center, 0),
110+
),
111+
medium=medium,
112+
)
113+
114+
waveguides_out.append(wg_out)
115+
116+
mnt_out = td.ModeMonitor(
117+
size=(0, ly_wg + 1.8 * buffer_wg, td.inf),
118+
center=(x_mnt, wg_y_center, 0),
119+
freqs=[freq0],
120+
name=output_monitor_name(i),
121+
mode_spec=mode_spec,
122+
)
123+
124+
monitors_out.append(mnt_out)
125+
126+
source = td.ModeSource(
127+
size=(0, ly_wg + 1.8 * buffer_wg, td.inf),
128+
center=(x_src, 0, 0),
129+
source_time=td.GaussianPulse(freq0=freq0, fwidth=fwidth),
130+
mode_index=0,
131+
direction="+",
132+
)
133+
134+
# used to visualize fields in the plane, not for optimization
135+
fld_mnt = td.FieldMonitor(
136+
center=(0, 0, 0),
137+
size=(td.inf, td.inf, 0),
138+
freqs=[freq0],
139+
name=field_mnt_name,
140+
)
141+
142+
simulation = td.Simulation(
143+
size=(Lx, Ly, 0),
144+
grid_spec=grid_spec,
145+
boundary_spec=td.BoundarySpec.pml(x=True, y=True, z=False),
146+
run_time=run_time,
147+
structures=[waveguide_in] + waveguides_out,
148+
sources=[source],
149+
monitors=[fld_mnt] + monitors_out,
150+
)
151+
152+
design = tdi.InverseDesign(
153+
simulation=simulation,
154+
design_region=design_region,
155+
task_name="invdes",
156+
output_monitor_names=[mnt.name for mnt in monitors_out],
157+
)
158+
159+
optimizer = tdi.AdamOptimizer(
160+
design=design,
161+
num_steps=10,
162+
learning_rate=0.1,
163+
results_cache_fname="data/invdes_history.hdf5",
164+
)
165+
166+
optimizer.to_hdf5("data/invdes_history.hdf5")
167+
job = Job(optimizer=optimizer)

tidy3d/web/api/autograd/autograd.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1167,7 +1167,6 @@ def _run_async_tidy3d_bwd(
11671167
_ = run_kwargs.pop("path_dir", None)
11681168
batch = Batch(simulations=simulations, **batch_init_kwargs)
11691169
td.log.info(f"running {batch.simulation_type} batch with '_run_async_tidy3d_bwd()'")
1170-
11711170
batch.start()
11721171
batch.monitor()
11731172

tidy3d/web/api/container.py

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ def _check_path_dir(path: str) -> None:
4444
"""Make sure local output directory exists and create it if not."""
4545

4646
@staticmethod
47-
def _check_folder(folder_name: str) -> None:
47+
def _check_folder(folder_name: str) -> Folder:
4848
"""Make sure ``folder_name`` exists on the web UI and create it if not."""
49-
Folder.get(folder_name, create=True)
49+
return Folder.get(folder_name, create=True)
5050

5151

5252
class Job(WebContainer):
@@ -235,24 +235,24 @@ def run(self, path: str = DEFAULT_DATA_PATH) -> SimulationDataType:
235235
self.monitor()
236236
return self.load(path=path)
237237

238-
@cached_property
239-
def task_id(self) -> TaskId:
238+
def task_id(self, batch_id: str = None) -> TaskId:
240239
"""The task ID for this ``Job``. Uploads the ``Job`` if it hasn't already been uploaded."""
241240
if self.task_id_cached:
242241
return self.task_id_cached
243242
self._check_folder(self.folder_name)
244-
return self._upload()
243+
return self._upload(batch_id)
245244

246-
def _upload(self) -> TaskId:
245+
def _upload(self, batch_id: str) -> TaskId:
247246
"""Upload this job and return the task ID for handling."""
248247
# upload kwargs with all fields except task_id
249248
upload_kwargs = {key: getattr(self, key) for key in self._upload_fields}
249+
upload_kwargs["batch_id"] = batch_id
250250
task_id = web.upload(**upload_kwargs)
251251
return task_id
252252

253-
def upload(self) -> None:
253+
def upload(self, batch_id: str = None) -> None:
254254
"""Upload this ``Job``."""
255-
_ = self.task_id
255+
_ = self.task_id(batch_id)
256256

257257
def get_info(self) -> TaskInfo:
258258
"""Return information about a :class:`Job`.
@@ -370,7 +370,9 @@ def estimate_cost(self, verbose: bool = True) -> float:
370370
Cost is calculated assuming the simulation runs for
371371
the full ``run_time``. If early shut-off is triggered, the cost is adjusted proportionately.
372372
"""
373-
return web.estimate_cost(self.task_id, verbose=verbose, solver_version=self.solver_version)
373+
return web.estimate_cost(
374+
self.task_id(), verbose=verbose, solver_version=self.solver_version
375+
)
374376

375377
@staticmethod
376378
def _check_path_dir(path: str) -> None:
@@ -511,6 +513,18 @@ class Batch(WebContainer):
511513
description="Name of folder to store member of each batch on web UI.",
512514
)
513515

516+
batch_id: str = pd.Field(
517+
None,
518+
title="Batch Id",
519+
description="ID of batch to store member of each batch on web UI.",
520+
)
521+
522+
batch_name: str = pd.Field(
523+
"batch",
524+
title="Batch Name",
525+
description="Name of batch to store member of each batch on web UI.",
526+
)
527+
514528
verbose: bool = pd.Field(
515529
True, title="Verbose", description="Whether to print info messages and progressbars."
516530
)
@@ -674,8 +688,15 @@ def num_jobs(self) -> int:
674688
def upload(self) -> None:
675689
"""Upload a series of tasks associated with this ``Batch`` using multi-threading."""
676690
self._check_folder(self.folder_name)
691+
batch_id = self.create_batch()
677692
with ThreadPoolExecutor(max_workers=self.num_workers) as executor:
678-
futures = [executor.submit(job.upload) for _, job in self.jobs.items()]
693+
futures = [
694+
executor.submit(
695+
job.upload,
696+
batch_id,
697+
)
698+
for _, job in self.jobs.items()
699+
]
679700

680701
# progressbar (number of tasks uploaded)
681702
if self.verbose:
@@ -711,6 +732,10 @@ def get_info(self) -> dict[TaskName, TaskInfo]:
711732
def start(self) -> None:
712733
"""Start running all tasks in the :class:`Batch`.
713734
735+
Parameters:
736+
batch_id: str
737+
Batch ID to start the tasks in.
738+
714739
Note
715740
----
716741
To monitor the running simulations, can call :meth:`Batch.monitor`.
@@ -1068,3 +1093,8 @@ def _check_path_dir(path_dir: str) -> None:
10681093
"""
10691094
if len(path_dir) > 0 and not os.path.exists(path_dir):
10701095
os.makedirs(path_dir, exist_ok=True)
1096+
1097+
def create_batch(self) -> str:
1098+
"""Create batch."""
1099+
folder = self._check_folder(self.folder_name)
1100+
return OptimizationBatch.create(self.batch_name, folder.folder_id)

tidy3d/web/api/webapi.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ def upload(
201201
source_required: bool = True,
202202
solver_version: Optional[str] = None,
203203
reduce_simulation: Literal["auto", True, False] = "auto",
204+
batch_id: str = None,
204205
) -> TaskId:
205206
"""
206207
Upload simulation to server, but do not start running :class:`.Simulation`.
@@ -230,7 +231,8 @@ def upload(
230231
target solver version.
231232
reduce_simulation: Literal["auto", True, False] = "auto"
232233
Whether to reduce structures in the simulation to the simulation domain only. Note: currently only implemented for the mode solver.
233-
234+
batch_id: str = None
235+
Batch id to upload tasks to.
234236
Returns
235237
-------
236238
str
@@ -260,7 +262,14 @@ def upload(
260262
task_type = stub.get_type()
261263

262264
task = SimulationTask.create(
263-
task_type, task_name, folder_name, callback_url, simulation_type, parent_tasks, "Gz"
265+
task_type,
266+
task_name,
267+
folder_name,
268+
callback_url,
269+
simulation_type,
270+
parent_tasks,
271+
"Gz",
272+
batch_id,
264273
)
265274
if verbose:
266275
console = get_logging_console()
@@ -385,6 +394,8 @@ def start(
385394
worker group
386395
pay_type: Union[PayType, str] = PayType.AUTO
387396
Which method to pay the simulation
397+
batch_id: str
398+
batch id
388399
Note
389400
----
390401
To monitor progress, can call :meth:`monitor` after starting simulation.

0 commit comments

Comments
 (0)