Skip to content

Commit 93d0fcc

Browse files
alec-flexcomputetylerflex
authored andcommitted
added lsf converter
1 parent 202cea4 commit 93d0fcc

File tree

3 files changed

+307
-10
lines changed

3 files changed

+307
-10
lines changed

tests/data/monitors.lsf

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# https://optics.ansys.com/hc/en-us/articles/360034923553-Lumerical-scripting-language-Alphabetical-list
2+
# https://readthedocs.org/projects/lumopt/downloads/pdf/latest/
3+
4+
switchtolayout;
5+
selectall;
6+
delete;
7+
8+
## SIM PARAMS
9+
size_x=16e-6;
10+
size_y=18e-6;
11+
size_z=0;
12+
mesh_x=20e-9;
13+
mesh_y=20e-9;
14+
finer_mesh_size=0.5e-6;
15+
mesh_accuracy=2;
16+
17+
addsphere;
18+
set("name","s");
19+
set("x",1e-6);
20+
set("y",2e-6);
21+
set("z",0);
22+
set("radius",0.5e-6);
23+
24+
25+
26+
## FDTD
27+
addfdtd;
28+
set('dimension','2D');
29+
set('background index',1.44);
30+
set('mesh accuracy',mesh_accuracy);
31+
set('x',0.0);
32+
set('x span',size_x);
33+
set('y',0.0);
34+
set('y span',size_y);
35+
set('z span',size_z);
36+
set('force symmetric y mesh',true);
37+
set('y min bc','Anti-Symmetric');
38+
set('pml layers',12);
39+
40+
41+
## OPTIMIZATION FIELDS MONITOR IN OPTIMIZABLE REGION
42+
addpower;
43+
set('name','opt_fields');
44+
set('monitor type','2D Z-normal');
45+
set('x',-6.5e-6);
46+
set('x span',finer_mesh_size);
47+
set('y',7.5e-6);
48+
set('y span',finer_mesh_size);
49+
50+
51+
#adddipole;
52+
#set("x",0);
53+
#set("y",1e-6);
54+
#set("z",0e-6);
55+
56+
addefieldmonitor;
57+
set("name","E_field");
58+
set("monitor type",6); # 2D y-normal
59+
set("x",-4.5e-6);
60+
set("x span",0.5e-6);
61+
set("y",7.5e-6);
62+
set("z",0);
63+
set("y span",0.5e-6);
64+
set("record electrostatic potential",1);
65+
set("save data",1);
66+
filename = "electric_field.mat";
67+
set("filename",filename);
68+
69+
addtestUnavailableCommand;
70+
71+
addmodeexpansion;
72+
set("name","mode monitor");
73+
set("x",-3.5e-6);
74+
set("x span",5e-6);
75+
set("y",7.5e-6);
76+
set("z",0);
77+
set("y span",1e-6);
78+
79+
addindex;
80+
set("name","index_monitor");
81+
set("x",0);
82+
set("x span",5e-6);
83+
set("z",0);
84+
set("y",8e-6);
85+
set("y span",2.5e-6);
86+
87+
addeffectiveindex;
88+
set("name","neff");
89+
set("x",1e-6);
90+
set("x span",0.7e-6);
91+
set("y",5e-6);
92+
set("y span",0.7e-6);
93+
94+
addtime;
95+
96+
set("name","time_1");
97+
set("monitor type",1); # point
98+
set("x",2.5e-6);
99+
set("z",0);
100+
set("y",5e-6);
101+
102+
addmovie;
103+
set("name","movie_1");
104+
set("monitor type",3); # 1 = 2D x-normal, 2 = 2D y-normal, 3 = 2D z-normal
105+
set("x",4e-6);
106+
set("x span",0.5e-6);
107+
set("y",5e-6);
108+
set("y span",5e-6);
109+
set("z",0);
110+
set("lock aspect ratio",1);
111+
set("horizontal resolution",240);
112+
113+
addprofile;
114+
set("name","field_profile");
115+
set("monitor type",7); # 2D z-normal
116+
set("x",6e-6);
117+
set("x span",1e-6);
118+
set("y",5);
119+
set("y span",1e-6);
120+
set("z",0);
121+
122+
setactivesolver("EME");
123+
addemeindex;
124+
125+
setactivesolver("EME");
126+
addemeprofile;
127+
128+
addemfieldmonitor;
129+
set("name","T");
130+
set("use source limits",1);
131+
set("reference source","plane_wave");
132+
set("surface type","solid");
133+
set("solid","2D rectangle");
134+
135+
addemfieldtimemonitor;
136+
set("name","time");
137+
set("geometry type","point");
138+
set("x",-2.5e-6);
139+
set("y",2.5e-6);
140+
set("z",0);

tests/test_package/test_convert.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""Test converting .lsf files to Tidy3D python files."""
2+
3+
import pytest
4+
import os
5+
from tidy3d.web.cli.converter import converter_arg
6+
7+
8+
@pytest.mark.parametrize("lsf_file", ("tests/data/example.lsf", "tests/data/monitors.lsf"))
9+
def test_tidy3d_converter(lsf_file, tmp_path):
10+
"""Generates Tidy3D python files from example lsf files in tests/data"""
11+
12+
new_file_path = str(tmp_path / "test.py")
13+
converter_arg(lsf_file, new_file_path)
14+
assert os.path.exists(new_file_path)

tidy3d/web/cli/converter.py

Lines changed: 153 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -924,7 +924,6 @@ def _addfdtd(Lines, i: int, v_dict: dict) -> str:
924924
gridspec_string += " wavelength=1.0)"
925925
sim_string += gridspec_string
926926
sim_string += ",\n)\n"
927-
928927
return sim_string, gridspec_string
929928

930929

@@ -962,10 +961,65 @@ def _addmesh(Lines, i: int, overstrct: int):
962961
override_string += "\tgeometry=td.Box(center=(" + x + "," + y + "," + z + "),"
963962
override_string += "size=(" + xl + "," + yl + "," + zl + ")),\n"
964963
override_string += "\tdl=(" + dx + "," + dy + "," + dz + "),\n)\n"
965-
966964
return override_string, name.replace(" ", "_")
967965

968966

967+
def _addindex(Lines, i: int, indexmon: int):
968+
x, y, z = "0", "0", "0"
969+
xl, yl, zl = "0", "0", "0"
970+
name = "permittivity_monitor_" + str(indexmon)
971+
freqs = " # NOTE: please specify"
972+
wave_center, wave_span = "0.5", "0.1"
973+
index_monitor_string = " = td.PermittivityMonitor(\n"
974+
while Lines[i][0] == "\n" or Lines[i][:5] == "set('" or Lines[i][0] == "#":
975+
if Lines[i][5:9] == "name":
976+
name = Lines[i][12:].split("'")[0]
977+
elif Lines[i][5:7] == "x'":
978+
x = Lines[i][8:].split(";")[0][:-1]
979+
elif Lines[i][5:7] == "y'":
980+
y = Lines[i][8:].split(";")[0][:-1]
981+
elif Lines[i][5:7] == "z'":
982+
z = Lines[i][8:].split(";")[0][:-1]
983+
elif Lines[i][5:11] == "x span":
984+
xl = Lines[i][13:].split(";")[0][:-1]
985+
elif Lines[i][5:11] == "y span":
986+
yl = Lines[i][13:].split(";")[0][:-1]
987+
elif Lines[i][5:11] == "z span":
988+
zl = Lines[i][13:].split(";")[0][:-1]
989+
elif Lines[i][5:9] == "name":
990+
name = Lines[i][12:].split("'")[0]
991+
elif Lines[i][5:22] == "wavelength center":
992+
wave_center = Lines[i][24:].split(";")[0][:-1] + "*1e6"
993+
elif Lines[i][5:20] == "wavelength span":
994+
wave_span = Lines[i][22:].split(";")[0][:-1] + "*1e6"
995+
elif Lines[i][5:21] == "frequency points":
996+
num_freqs = Lines[i][23:].split(";")[0][:-1]
997+
freqs = (
998+
"np.linspace("
999+
+ wave_center
1000+
+ "-"
1001+
+ wave_span
1002+
+ "/2,"
1003+
+ wave_center
1004+
+ "+"
1005+
+ wave_span
1006+
+ "/2,"
1007+
+ num_freqs
1008+
+ ")"
1009+
)
1010+
i += 1
1011+
if i == len(Lines):
1012+
break
1013+
x, y, z, xl, yl, zl = _to_um([x, y, z, xl, yl, zl])
1014+
index_monitor_string += "\tcenter=(" + x + "," + y + "," + z + "),\n"
1015+
index_monitor_string += "\tsize=(" + xl + "," + yl + "," + zl + "),\n"
1016+
index_monitor_string += "\tname='" + name + "',\n"
1017+
index_monitor_string += "\tinterval_space=(1,1,1),\n"
1018+
index_monitor_string += "\tfreqs=" + freqs + ",\n"
1019+
index_monitor_string += ")\n"
1020+
return index_monitor_string, name.replace(" ", "_")
1021+
1022+
9691023
def _addpower(Lines, i: int, fluxmon: int):
9701024
x, y, z = "0", "0", "0"
9711025
xl, yl, zl = "0", "0", "0"
@@ -1021,6 +1075,62 @@ def _addpower(Lines, i: int, fluxmon: int):
10211075
return flux_monitor_string, name.replace(" ", "_")
10221076

10231077

1078+
def _addmovie(Lines, i: int, fieldTime: int):
1079+
# https://optics.ansys.com/hc/en-us/articles/360034924633-addefieldmonitor
1080+
x, y, z = "0", "0", "0"
1081+
xl, yl, zl = "0", "0", "0"
1082+
x_min, x_max = "0", "0"
1083+
y_min, y_max = "0", "0"
1084+
z_min, z_max = "0", "0"
1085+
name = "field_time_monitor_" + str(fieldTime)
1086+
field_monitor_string = " = td.FieldTimeMonitor(\n"
1087+
while Lines[i][0] == "\n" or Lines[i][:5] == "set('" or Lines[i][0] == "#":
1088+
if Lines[i][5:9] == "name":
1089+
name = Lines[i][12:].split("'")[0]
1090+
elif Lines[i][5:7] == "x'":
1091+
x = Lines[i][8:].split(";")[0][:-1]
1092+
elif Lines[i][5:7] == "y'":
1093+
y = Lines[i][8:].split(";")[0][:-1]
1094+
elif Lines[i][5:7] == "z'":
1095+
z = Lines[i][8:].split(";")[0][:-1]
1096+
elif Lines[i][5:10] == "x_min":
1097+
x_min = Lines[i][12:].split(";")[0][:-1]
1098+
xl = x_max + "-" + x_min
1099+
elif Lines[i][5:10] == "x_max":
1100+
x_max = Lines[i][12:].split(";")[0][:-1]
1101+
xl = x_max + "-" + x_min
1102+
elif Lines[i][5:10] == "y_min":
1103+
y_min = Lines[i][12:].split(";")[0][:-1]
1104+
yl = y_max + "-" + y_min
1105+
elif Lines[i][5:10] == "y_max":
1106+
y_max = Lines[i][12:].split(";")[0][:-1]
1107+
yl = y_max + "-" + y_min
1108+
elif Lines[i][5:10] == "z_min":
1109+
z_min = Lines[i][12:].split(";")[0][:-1]
1110+
zl = z_max + "-" + z_min
1111+
elif Lines[i][5:10] == "z_max":
1112+
z_max = Lines[i][12:].split(";")[0][:-1]
1113+
zl = z_max + "-" + z_min
1114+
elif Lines[i][5:11] == "x span":
1115+
xl = Lines[i][13:].split(";")[0][:-1]
1116+
elif Lines[i][5:11] == "y span":
1117+
yl = Lines[i][13:].split(";")[0][:-1]
1118+
elif Lines[i][5:11] == "z span":
1119+
zl = Lines[i][13:].split(";")[0][:-1]
1120+
i += 1
1121+
if i == len(Lines):
1122+
break
1123+
x, y, z, xl, yl, zl = _to_um([x, y, z, xl, yl, zl])
1124+
field_monitor_string += "\tcenter=(" + x + "," + y + "," + z + "),\n"
1125+
field_monitor_string += "\tsize=(" + xl + "," + yl + "," + zl + "),\n"
1126+
field_monitor_string += "\tname='" + name + "',\n"
1127+
field_monitor_string += "\tinterval=1,\n"
1128+
field_monitor_string += "\tstart=0.0,\n"
1129+
field_monitor_string += "\tstop=None,\n"
1130+
field_monitor_string += ")\n"
1131+
return field_monitor_string, name.replace(" ", "_")
1132+
1133+
10241134
def _addefieldmonitor(Lines, i: int, fieldmon: int):
10251135
# https://optics.ansys.com/hc/en-us/articles/360034924633-addefieldmonitor
10261136
x, y, z = "0", "0", "0"
@@ -1196,7 +1306,7 @@ def lsf_reader(filename: str) -> None:
11961306
rect, sph, cyl, polyslab = 0, 0, 0, 0
11971307
sources, modesrc, planewv, ptdipole, gauss, tfsf = "[", 0, 0, 0, 0, 0
11981308
monitors = "["
1199-
modemon, fluxmon, fieldmon = 0, 0, 0
1309+
modemon, fluxmon, fieldmon, fieldTime, indexmon = 0, 0, 0, 0, 0
12001310
gridspec_string = "grid_spec=td.GridSpec.auto()"
12011311
override_structures, overstrct = "[", 0
12021312

@@ -1267,12 +1377,35 @@ def lsf_reader(filename: str) -> None:
12671377
override_structures += refine_box_name + ", "
12681378
tidy3d_file += refine_box_name + override_string
12691379
overstrct += 1
1380+
elif (
1381+
line[:9] == "addindex;"
1382+
or line[:18] == "addeffectiveindex;"
1383+
or line[:12] == "addemeindex;"
1384+
):
1385+
addindex_string, index_name = _addindex(Lines, i + 1, indexmon)
1386+
monitors += index_name + ", "
1387+
tidy3d_file += index_name + addindex_string
1388+
indexmon += 1
12701389
elif line[:9] == "addpower;":
12711390
addpower_string, power_name = _addpower(Lines, i + 1, fluxmon)
12721391
monitors += power_name + ", "
12731392
tidy3d_file += power_name + addpower_string
12741393
fluxmon += 1
1275-
elif line[:17] == "addefieldmonitor;":
1394+
elif (
1395+
line[:9] == "addmovie;"
1396+
or line[:22] == "addemfieldtimemonitor;"
1397+
or line[:8] == "addtime;"
1398+
):
1399+
addmovie_string, movie_name = _addmovie(Lines, i + 1, fieldTime)
1400+
monitors += movie_name + ", "
1401+
tidy3d_file += movie_name + addmovie_string
1402+
fieldTime += 1
1403+
elif (
1404+
line[:17] == "addefieldmonitor;"
1405+
or line[:11] == "addprofile;"
1406+
or line[:18] == "addemfieldmonitor;"
1407+
or line[:14] == "addemeprofile;"
1408+
):
12761409
addefieldmonitor_string, monitor_name = _addefieldmonitor(Lines, i + 1, fieldmon)
12771410
monitors += monitor_name + ", "
12781411
tidy3d_file += monitor_name + addefieldmonitor_string
@@ -1293,11 +1426,22 @@ def lsf_reader(filename: str) -> None:
12931426
pass
12941427
else:
12951428
tidy3d_file += "# " + line[:-1] + " # NOTE: does not yet parse to Tidy3D\n"
1296-
structures = structures[:-2] + "]"
1297-
sources = sources[:-2] + "]"
1298-
monitors = monitors[:-2] + "]"
1299-
override_structures = override_structures[:-2] + "]"
1300-
1429+
if len(structures) > 1:
1430+
structures = structures[:-2] + "]"
1431+
else:
1432+
structures = "[]"
1433+
if len(sources) > 1:
1434+
sources = sources[:-2] + "]"
1435+
else:
1436+
sources = "[]"
1437+
if len(monitors) > 1:
1438+
monitors = monitors[:-2] + "]"
1439+
else:
1440+
monitors = "[]"
1441+
if len(override_structures) > 1:
1442+
override_structures = override_structures[:-2] + "]"
1443+
else:
1444+
override_structures = "[]"
13011445
tidy3d_file += "\nsim = sim.copy(update=dict(structures=" + structures + ","
13021446
tidy3d_file += " # NOTE: Check order of structures for potential overlap issues\n"
13031447
tidy3d_file += "\tsources=" + sources + ",\n"
@@ -1310,7 +1454,6 @@ def lsf_reader(filename: str) -> None:
13101454
+ ")\n\t)\n"
13111455
)
13121456
tidy3d_file += ")"
1313-
13141457
return tidy3d_file
13151458

13161459

0 commit comments

Comments
 (0)