Set Up Simulation#
Functions for converting GDS layouts to simulation-ready 3D structures with sources, monitors, and absorbing boundaries.
Structure#
- hyperwave_community.component_to_theta(component, resolution=0.05, layer=None, waveguide_value=1.0, background_value=0.0)#
Convert gdsfactory component to theta array for hyperwave simulations.
This function takes a gdsfactory component and converts it to a 2D theta array suitable for use in hyperwave simulations. It rasterizes the component geometry at the specified resolution.
- Parameters:
component – A gf.Component instance to convert.
resolution (
float) – Grid resolution in micrometers per pixel in the final STRUCTURE. Default is 0.05 (50nm). Note: theta array will be generated at 2x this resolution since theta is downsampled by 2 during structure creation.layer (
Union[int,Tuple[int,int],None]) – Specific layer to extract. If None, uses first available layer. Can be an integer or tuple (layer, datatype).waveguide_value (
float) – Value to assign to waveguide regions (default 1.0).background_value (
float) – Value to assign to background regions (default 0.0).
- Returns:
- 2D JAX array with shape (ny, nx) containing the theta values.
Values are waveguide_value where component exists, background_value elsewhere. Note: This array will be at 2x finer resolution than the final structure.
- info: Dictionary containing metadata:
’component_name’: Name of the component
’shape’: Shape of the theta array
’theta_resolution_um’: Resolution of theta array in micrometers per pixel
’structure_resolution_um’: Resolution of final structure after downsampling
’physical_size_um’: Physical size (width, height) in micrometers
’bounding_box_um’: Bounding box coordinates in micrometers
’layer’: Layer that was extracted
- Return type:
Example:
theta, device_info = hwc.component_to_theta( component=gf_extended, resolution=RESOLUTION_UM, )
Note
Requires gdsfactory to be installed: pip install gdsfactory
- hyperwave_community.gds_to_theta(gds_filepath, resolution=0.05, layer=None, waveguide_value=1.0, background_value=0.0)#
Convert GDS file to theta array for photonic simulations.
Reads a GDS II format file and converts it to a 2D density array suitable for use in photonic simulations. The function rasterizes polygons from the GDS file into a pixelated array at the specified resolution.
- Parameters:
gds_filepath (
str) – Path to the GDS file to convert.resolution (
float) – Grid resolution in micrometers per pixel in the final STRUCTURE. Default 0.05 (50nm). Note: theta array will be generated at 2x this resolution since theta is downsampled by 2 during structure creation.layer (
Union[int,Tuple[int,int],None]) – Specific layer to extract. If None, uses most common layer. Can be integer or tuple (layer, datatype).waveguide_value (
float) – Value for waveguide regions. Default 1.0.background_value (
float) – Value for background regions. Default 0.0.
- Returns:
- theta: JAX array with shape (ny, nx) containing waveguide and
background values.
- info: Dictionary with metadata including ‘gds_file’, ‘cell_name’,
’shape’, ‘theta_resolution_um’, ‘structure_resolution_um’, ‘physical_size_um’ (width, height), ‘bounding_box_um’ (x_min, y_min, x_max, y_max), ‘layer’, ‘num_polygons’, ‘waveguide_value’, ‘background_value’.
- Return type:
Tuple of (theta, info) where
- Raises:
ValueError – If no top-level cells or polygons found in GDS file.
ValueError – If specified layer not found in GDS file.
Note
For large GDS files or fine resolution, this can be memory intensive. The rasterization uses matplotlib.path for polygon filling which is accurate but can be slow for complex polygons.
- hyperwave_community.density(theta, pad_width=0, alpha=0.0, radius=8.0, c=1.0, eta=0.5, eta_lo=0.25, eta_hi=0.75, return_loss=False)#
Convert raw optimization variables to density field with filtering.
This function takes raw optimization variables and converts them into a proper photonic device structure with density filtering for minimum feature size constraints. Implements the “three-field” scheme detailed in topology optimization literature for a final density that is binary (with the exception of boundary values) and conforms to minimum feature size requirements.
The process includes: 1. Adding padding around the structure 2. Applying density filtering for minimum feature size constraints 3. Controlling binarization levels
This function is general-purpose and can be used for any photonic device including grating couplers, metalenses, waveguides, etc.
- Parameters:
theta (
Array) – Raw optimization variables defining the basic structure. Shape should be (nx, ny) where nx and ny are the dimensions. For backward compatibility, if theta is already the filtered density u, set radius=0 to skip filtering.pad_width (
Union[int,Tuple[int,int,int,int]]) – Padding to add around the structure. Can be: - int: Same padding on all sides - tuple of 4 ints: (left, right, top, bottom) padding Default: 0 (no padding).alpha (
float) – Binarization control parameter in range [0, 1]. - 0: No binarization, density varies freely in [0, 1] - 1: Complete binarization (except boundary values) Default: 0.0 (no binarization).radius (
float) – Radius for density filtering (controls minimum feature size). Set to 0 to skip filtering. Default: 8.0.c (
float) – Controls the detection of inflection points for fabrication loss. Default: 1.0.eta (
float) – Threshold value used to binarize the density. Default: 0.5.eta_lo (
float) – Controls minimum feature size of void-phase features. Default: 0.25.eta_hi (
float) – Controls minimum feature size of density=1 features. Default: 0.75.return_loss (
bool) – If True, also return the per-pixel fabrication loss array. Default: False.
- Returns:
density field array of shape (nx + 2*pad_width, ny + 2*pad_width). If return_loss is True: tuple of (density, loss) where both arrays have the same shape. The loss quantifies minimum feature size violations.
This function enforces even spatial dimensions by trimming the last row/column of theta if needed so downstream Yee-grid operations work without errors.
- Return type:
If return_loss is False (default)
- Raises:
ValueError – If parameters are outside valid ranges or if output contains NaN/Inf.
TypeError – If input types are incorrect.
Example:
density_core = hwc.density(theta=theta, pad_width=PADDING, radius=3) density_clad = hwc.density(theta=jnp.zeros_like(theta), pad_width=PADDING, radius=5)
Note
This function replaces the separate density_pjz function, incorporating all its functionality with additional validation and preprocessing.
References
Zhou, Mingdong, et al. “Minimum length scale in topology optimization by geometric constraints.” Computer Methods in Applied Mechanics and Engineering 293 (2015): 266-282.
- class hyperwave_community.Layer(density_pattern, permittivity_values, layer_thickness, conductivity_values=0.0)#
Represents a single layer in a photonic structure.
This class encapsulates all the material properties and geometry for a single layer in a multi-layer photonic structure.
- Parameters:
density_pattern (
Array) – 2D density pattern with shape (nx, ny) where nx and ny are the X and Y dimensions. Values should be in [0, 1].permittivity_values (
Union[Tuple[float,float],float]) – Either a tuple (low, high) for interpolation or a single float value.layer_thickness (
float) – Physical thickness of the layer in grid units. Can be a float to allow precise control over layer positioning (e.g., 10.5). The final structure will have an integer number of grid points (rounded up via ceil), with fractional positions handled via subpixel averaging.conductivity_values (
Union[Tuple[float,float],float]) – Either a tuple (low, high) for interpolation or a single float value. Defaults to 0.0 (no conductivity).
Notes
The density pattern must have even dimensions. If you generate densities using density(…) they will be auto-trimmed to even sizes. If you manually modify densities afterwards and make them odd-sized, instantiating a Layer will raise a clear ValueError.
Example:
hwc.Layer(density_pattern=density_clad, permittivity_values=eps_clad, layer_thickness=clad_cells) hwc.Layer(density_pattern=density_core, permittivity_values=(eps_clad, eps_core), layer_thickness=wg_cells)
- density_pattern: Array#
- get_permittivity_values()#
Get the permittivity values for this layer based on density pattern.
- Return type:
Array- Returns:
2D array of permittivity values with same shape as density_pattern.
- get_conductivity_values()#
Get the conductivity values for this layer based on density pattern.
- Return type:
Array- Returns:
2D array of conductivity values with same shape as density_pattern.
- hyperwave_community.create_structure(layers, vertical_radius=5.0)#
Create enhanced structure with permittivity/conductivity arrays and construction metadata.
This function takes a list of Layer objects and converts them into layered permittivity and conductivity structures suitable for FDTD simulations, while preserving all construction metadata for efficient Modal serialization.
Layer thicknesses can be floats (e.g., 10.5), allowing precise control over interface positions. The total Z-dimension will be rounded up (via ceil) to the nearest integer number of grid points. Floating point rounding errors (within 1e-6 of an integer) are handled gracefully. Subpixel averaging handles fractional overlaps at interfaces.
- Parameters:
- Returns:
permittivity: (components, nx, ny, nz) permittivity distribution
conductivity: (components, nx, ny, nz) conductivity distribution
layers_info: Original Layer objects for reconstruction
construction_params: Parameters used in construction
metadata: Additional reconstruction information
- Return type:
Structure object containing
Example:
structure = hwc.create_structure( layers=[ hwc.Layer(density_pattern=density_clad, permittivity_values=eps_clad, layer_thickness=clad_cells), hwc.Layer(density_pattern=density_core, permittivity_values=(eps_clad, eps_core), layer_thickness=wg_cells), hwc.Layer(density_pattern=density_clad, permittivity_values=eps_clad, layer_thickness=clad_cells), ], vertical_radius=2, )
Absorption#
- hyperwave_community.get_optimized_absorber_params(resolution_nm=20.0, wavelength_um=1.55, structure_dimensions=None)#
Deprecated. Use
absorber_params(device_type="flat")instead.
- hyperwave_community.create_absorption_mask(grid_shape, absorption_widths, absorption_coeff)#
Create 3D adiabatic absorption mask with inward padding logic.
This function creates a 3D absorption mask that: 1. Uses inward padding: absorption occurs at grid boundaries 2. Creates zero absorption in the center region (preserves device physics) 3. Increases quadratically toward all boundaries (prevents reflections) 4. Handles all three field components (Ex, Ey, Ez) with proper Yee grid offsets
- Parameters:
- Returns:
(3, xx, yy, zz) absorption array
- Return type:
absorption_mask
- Physics:
Center region stays at zero absorption (perfect device simulation)
Boundary regions gradually absorb waves (no artificial reflections)
Quadratic profile ensures adiabatic (slowly-varying) transitions
Works in full 3D with proper field component offsets
Example:
absorber = hwc.create_absorption_mask( grid_shape=(Lx, Ly, Lz), absorption_widths=abs_widths, absorption_coeff=abs_coeff, ) structure.conductivity = jnp.zeros_like(structure.conductivity) + absorber
Source#
- hyperwave_community.create_mode_source(structure, freq_band, mode_num=0, propagation_axis='x', source_position=10, perpendicular_bounds=None, z_bounds=None, visualize=False, visualize_permittivity=False, debug=False)#
Create a modal source for simulation.
- Parameters:
structure – Structure object with permittivity.
freq_band (
Tuple[float,float,int]) – Frequency band (min, max, num_points).mode_num (
int) – Mode number to solve for.propagation_axis (
str) – Direction of mode propagation (‘x’ or ‘y’).source_position (
int) – Position along propagation axis for mode solving.perpendicular_bounds (
Optional[Tuple[int,int]]) – Optional (min, max) bounds for the axis perpendicular to propagation. For x-propagation: Y bounds (y_min, y_max). For y-propagation: X bounds (x_min, x_max). None means use full extent.z_bounds (
Optional[Tuple[int,int]]) – Optional (min, max) bounds for the Z axis. Useful for limiting mode solver to specific layers (e.g., silicon layer in SOI). None means use full Z extent.visualize (
bool) – Whether to visualize the mode field.visualize_permittivity (
bool) – Whether to visualize the permittivity at source plane.debug (
bool) – Whether to print debug information.
- Returns:
source_field: Array of shape (num_freqs, 6, 1, Ny_crop, Nz_crop). When perpendicular_bounds or z_bounds are provided, the source is returned already cropped to those bounds. Do NOT crop again. The hyperwave core version returns the full domain instead.
source_offset: (x, y, z) CORNER position for source placement
mode_info: Dict with ‘field’, ‘beta’, and ‘error’
- Return type:
Tuple of (source_field, source_offset, mode_info) where
Example:
source_field, source_offset, mode_info = hwc.create_mode_source( structure=structure, freq_band=freq_band, mode_num=0, propagation_axis="x", source_position=abs_widths[0], )
Monitors#
- hyperwave_community.create_port_monitors(component, structure, device_info, padding, absorption_widths, monitor_thickness=5, monitor_half_extent=35, z_wg_center=None, input_label_prefix='Input_', output_label_prefix='Output_')#
Create monitors at all ports of a gdsfactory component.
Converts gdsfactory port positions to structure pixel coordinates and creates appropriately sized monitors. Automatically detects Input vs Output based on port orientation. Detects and resolves monitor collisions by shrinking overlapping monitors.
Always includes an xy_mid visualization plane monitor.
- Parameters:
component – gdsfactory component with .ports attribute.
structure – Structure object with permittivity attribute.
device_info (
dict) – Dict from component_to_theta() containing ‘bounding_box_um’ and ‘theta_resolution_um’.padding (
tuple) – (left, right, top, bottom) padding used in density().absorption_widths (
tuple) – Tuple from absorber_params() for grid dimensions.monitor_thickness (
int) – Thickness along propagation axis (pixels). Default 5.monitor_half_extent (
int) – Half-size in y and z (pixels). Default 35.z_wg_center (
Optional[int]) – Z position of waveguide center. If None, auto-detects from structure by finding z with highest permittivity contrast.input_label_prefix (
str) – Prefix for input port monitors. Default"Input_".output_label_prefix (
str) – Prefix for output port monitors. Default"Output_".
- Return type:
- Returns:
MonitorSet with monitors at each port plus an xy_mid plane.
Example:
monitors = hwc.create_port_monitors( component=gf_device, structure=structure, device_info=device_info, padding=PADDING, absorption_widths=abs_widths, )
- class hyperwave_community.Monitor(shape, offset)#
Monitor configuration for field extraction during FDTD simulation.
- Parameters:
- class hyperwave_community.MonitorSet(monitors=None, mapping=None)#
Container for managing multiple monitors with add/remove/replace operations.
This class provides a clean interface for managing monitors in FDTD simulations, with support for named monitors and automatic indexing.
- monitors#
List of Monitor objects
- mapping#
Dictionary mapping names to monitor indices
- __init__(monitors=None, mapping=None)#
Initialize MonitorSet with optional existing monitors.
- add(monitor, name=None)#
Add a new monitor to the set.
- Parameters:
- Return type:
- Returns:
Index of the added monitor.
- Raises:
ValueError – If the name already exists in the mapping.
TypeError – If monitor is not a Monitor instance.
- remove(identifier)#
Remove a monitor from the set.
- Parameters:
identifier (
Union[str,int,Monitor]) – Can be: - str: Name of the monitor to remove - int: Index of the monitor to remove - Monitor: The monitor object to remove- Return type:
- Returns:
The removed Monitor object.
- Raises:
ValueError – If monitor not found or invalid identifier.
TypeError – If identifier type is not supported.
Note
Removing a monitor will update all indices in the mapping to maintain consistency.
- get(identifier)#
Get a monitor by name or index.
- Parameters:
identifier (
Union[str,int]) – Name (str) or index (int) of the monitor.- Return type:
- Returns:
The requested Monitor object.
- Raises:
ValueError – If monitor not found.
TypeError – If identifier type is not supported.
- list_monitors()#
Get list of all monitor names.
- to_tuple()#
Convert to tuple format for backward compatibility.
- view(**kwargs)#
Deprecated. Use hyperwave_community.visualization.plot_monitor_layout instead.
- Return type:
- add_monitors_at_position(structure, axis, position, monitor_thickness=5, width_factor=2.5, height_factor=None, min_width_factor=1.5, label=None, verbose=False, x_ranges=None, y_ranges=None, z_ranges=None)#
Add monitors automatically along a specific axis at a specific position.
Detects waveguides or other high-permittivity features along the specified axis at the given position and automatically creates appropriately sized monitors for each detected feature. Monitors are added to this MonitorSet with automatic naming based on feature count.
WARNING: This function is designed for structures with waveguides or other high-permittivity features. It will not work correctly for freespace regions or uniform permittivity regions as it relies on permittivity contrast to detect features. For such cases, use the add() method directly to place monitors manually.
- Parameters:
structure – Structure object with permittivity attribute.
axis (
str) – Axis perpendicular to the monitors (‘x’, ‘y’, or ‘z’). - ‘x’: Creates YZ plane monitors at specified X position - ‘y’: Creates XZ plane monitors at specified Y position - ‘z’: Creates XY plane monitors at specified Z positionposition (
int) – Position along the axis where monitors should be placed (in pixels).monitor_thickness (
int) – Thickness of monitors in the direction of the axis (in pixels). Default is 5 pixels. This is absolute, not relative.width_factor (
float) – Multiplier for waveguide width to determine monitor lateral extent. Default is 2.5 (monitor will be 2.5x the waveguide width).height_factor (
Optional[float]) – Multiplier for monitor height (Z dimension). If None, uses the same value as width_factor. For Z axis monitors, this parameter is ignored. Default is None (same as width_factor).min_width_factor (
float) – Minimum multiplier for waveguide width. Default is 1.5. Ensures monitors are at least 1.5x waveguide width.label (
Optional[str]) – Label for monitor names. If None, uses ‘mon_axisXXX’ format where XXX is the position value. If provided, smart suffixes are added: - Single monitor: uses label as-is (e.g., ‘input’) - Two monitors: adds ‘_top’/’_bottom’ or ‘_left’/’_right’ (e.g., ‘input_top’) - Multiple monitors: adds index (e.g., ‘input_0’, ‘input_1’)verbose (
bool) – Whether to print detection information. Default is False.x_ranges (
Optional[Tuple[int,int]]) – Tuple (start, end) to restrict X range for waveguide detection. Used when axis=’y’ or axis=’z’.y_ranges (
Optional[Tuple[int,int]]) – Tuple (start, end) to restrict Y range for waveguide detection. Used when axis=’x’ or axis=’z’.z_ranges (
Optional[Tuple[int,int]]) – Tuple (start, end) to restrict Z range for center-of-mass calculation. Used when axis=’x’ or axis=’y’.
- Return type:
- Returns:
List of names of the added monitors.
- Raises:
ValueError – If axis is not ‘x’, ‘y’, or ‘z’.
Note
Monitor naming follows these conventions: * Single waveguide: Uses prefix directly (e.g., ‘input’) * Two waveguides: Adds ‘_top’/’_bottom’ for X axis, ‘_left’/’_right’ for Y * Multiple waveguides: Adds index suffix (e.g., ‘coupling_0’, ‘coupling_1’)
This function is not intended for freespace or uniform permittivity regions. Use the add() method instead for those use cases.
Example
>>> # Add monitors using method syntax >>> monitor_set = MonitorSet() >>> names = monitor_set.add_monitors_at_position( ... structure, axis='x', position=100, height_factor=2.0 ... ) >>> print(f"Added {len(names)} monitors: {names}")