# properties derived from sim_parameters
def make_sim(sim_params: SimulationParameters) -> td.Simulation:
"""Generate a simulation from given parameters."""
medium_spheres, medium_out = get_mediums(sim_params)
Lx = sim_params.Lx
Ly = sim_params.Ly
Lz = sim_params.Lz
radius = sim_params.radius
space = sim_params.space
run_time = sim_params.run_time
Lz_tot = 2 * space + Lz
sim_size = [Lx, Ly, Lz_tot]
# number of spheres to place = slab volume * nominal_density
expanded_volume = (Lx + 2 * radius) * (Ly + 2 * radius) * (Lz + 2 * radius)
nominal_density = sim_params.ff0 / (4 * np.pi / 3 * radius**3)
num_spheres = int(expanded_volume * nominal_density)
# Randomly position spheres
np.random.seed(random_seed)
sphere_geometries = []
print(f"inserting {num_spheres:2e} spheres")
for i in range(num_spheres):
position_x = np.random.uniform(-Lx / 2 - radius, Lx / 2 + radius)
position_y = np.random.uniform(-Ly / 2 - radius, Ly / 2 + radius)
position_z = np.random.uniform(-Lz / 2 - radius, Lz / 2 + radius)
sphere_i = td.Sphere(center=[position_x, position_y, position_z], radius=radius)
sphere_geometries.append(sphere_i)
spheres = td.Structure(
geometry=td.GeometryGroup(geometries=sphere_geometries), medium=medium_spheres
)
# Define effective medium around the slab
box_in = td.Box(center=[0, 0, -Lz / 2 - space], size=[td.inf, td.inf, 2 * space])
box_out = td.Box(center=[0, 0, Lz / 2 + space], size=[td.inf, td.inf, 2 * space])
struct_in = td.Structure(geometry=box_in, medium=medium_out)
struct_out = td.Structure(geometry=box_out, medium=medium_out)
structures = [spheres, struct_in, struct_out]
# Define incident plane wave
gaussian = td.GaussianPulse(
freq0=freq0, fwidth=sim_params.fwidth, offset=sim_params.offset, phase=0
)
if sim_params.sim_mode == "transmission":
source_size = (td.inf, td.inf, 0)
elif sim_params.sim_mode == "beam_spreading":
source_size = (0.25, 0.25, 0.0)
else:
raise ValueError(f"sim_mode of {sim_params.sim_mode} not recognized.")
# angle of polarization w.r.t. to the x axis (x-polarized)
source = td.PlaneWave(
size=source_size,
center=(0, 0, -Lz_tot / 2.0 + 0.1),
source_time=gaussian,
direction="+",
pol_angle=0,
)
freqs_fft = (freq0 * np.array(sim_params.f0a)).tolist()
# Records CW (via FFT) transmitted flux through the slab
freq_monitorT = td.FluxMonitor(
center=[0.0, 0.0, Lz / 2.0 + space / 2.0],
size=[td.inf, td.inf, 0],
freqs=freqs_fft,
name="freq_monitorT",
)
# Records time-dependent transmitted flux through the slab
time_monitorT = td.FluxTimeMonitor(
center=[0.0, 0.0, Lz / 2.0 + space / 2.0],
size=[td.inf, td.inf, 0],
name="time_monitorT",
)
# Records E-fields throughout simulation volume at t=run_time/2
time_monitorZH = td.FieldTimeMonitor(
center=[0.0, 0.0, 0.0],
size=[td.inf, td.inf, Lz + wavelength],
start=run_time / 2.0,
stop=run_time / 2.0,
fields=["Ex", "Ey", "Ez"],
name="time_monitorZH",
)
# Records E-fields throughout simulation volume at t=run_time
time_monitorZ = td.FieldTimeMonitor(
center=[0.0, 0.0, 0.0],
size=[td.inf, td.inf, Lz + wavelength],
start=run_time,
stop=run_time,
fields=["Ex", "Ey", "Ez"],
name="time_monitorZ",
)
N_run_time = int(sim_params.run_time / (0.99 * wavelength / (grids_pw * td.C_0 * np.sqrt(3))))
# Records E-fields at the output surface at Nt equally spaced times from 0 to run_time
spread_monitor = td.FieldTimeMonitor(
center=[0.0, 0.0, Lz / 2.0 + 2 * wavelength / grids_pw],
size=[td.inf, td.inf, 0.0],
start=0.4 * run_time,
stop=0.9 * run_time,
interval=int(N_run_time / sim_params.Nt),
fields=["Ex", "Ey", "Ez"],
name="spread_monitor",
)
# Records permittivity throughout simulation volume
eps_monitor = td.PermittivityMonitor(
center=[0.0, 0.0, 0.0],
size=[td.inf, td.inf, Lz + wavelength],
freqs=[freq0],
name="eps_monitor",
)
monitors = [freq_monitorT, time_monitorT, eps_monitor]
if sim_params.sim_mode == "transmission":
monitors.append(time_monitorZH)
monitors.append(time_monitorZ)
elif sim_params.sim_mode == "beam_spreading":
monitors.append(spread_monitor)
else:
raise ValueError(f"sim_mode of {sim_params.sim_mode} not recognized.")
# Define simulation parameters
sim = td.Simulation(
size=sim_size,
grid_spec=grid_spec,
structures=structures,
sources=[source],
monitors=monitors,
run_time=run_time,
boundary_spec=boundary_spec,
shutoff=1e-15,
subpixel=sim_params.subpixel,
)
return sim