Simulator backends¶
openlithohub.simulators is the vendor-neutral interface for lithography
forward simulation. The bundled HopkinsSimulator is the reference
implementation; CalibreSimulator and TachyonSimulator are commercial
adapters that require the respective vendor toolchain on PATH and a
license. Both support mock_mode (config.extra["mock_mode"] = True)
for testing and CI without the commercial binary.
The module also re-exports load_source_intensity,
load_zernike_coefficients, and zernike_phase_map so callers can drive
the Hopkins forward model with measured-source maps and Zernike-pupil
aberrations without reaching into _utils.optics.
Registry¶
openlithohub.simulators.registry
¶
Open registry of simulator backends, keyed by string name.
Resist / Lithography Backend Selection Guide¶
This registry dispatches to several fidelity tiers of lithography simulation:
Lightweight built-in (default)
:func:~openlithohub._utils.resist_model.apply_differentiable_resist
provides a fast sigmoid-threshold resist with optional Gaussian acid diffusion.
This is the default path when config.resist_backend == "ctr".
High-fidelity Dill/Mack exposure + development
Available via the diffcfd_litho backend, which uses
:class:~openlithohub.plugins.diffcfd_process.DiffCFDLithoSimulator.
Requires the [diffcfd] extra. Models Beer-Lambert exposure with PAC
bleaching (Dill) and solvent-dependent dissolution (Mack).
High-fidelity CAR/PEB resist
Available via the diffnano resist backend, which uses
:class:~openlithohub.plugins.diffnano_resist.DiffNanoResistAdapter.
Requires the [diffnano] extra. Models acid diffusion, PEB diffusion,
and sigmoid development with calibratable contrast.
Hopkins forward model
The bundled :class:~openlithohub.simulators.hopkins_sim.HopkinsSimulator
provides the core Hopkins/SOCS aerial-image simulation. DiffNano also ships
diffnano.solvers.litho.HopkinsLithoModel (a simplified PSF model that
is functionally equivalent to the Hopkins path here). For new work, prefer
the core HopkinsSimulator which supports multiple illumination types and
SOCS kernel caching.
See docs/resist_capability_matrix.md for the full capability comparison.
get_simulator(name, config=None)
¶
Construct a simulator by name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Registered backend name (e.g. |
required |
config
|
SimulatorConfig | None
|
Optional :class: |
None
|
Raises:
| Type | Description |
|---|---|
KeyError
|
If |
Source code in src/openlithohub/simulators/registry.py
list_simulators()
¶
register_simulator(name, cls)
¶
Register a simulator class under name.
Overwrites any previous registration to keep the API simple — users
that want defensiveness can guard with name in list_simulators().
Source code in src/openlithohub/simulators/registry.py
describe_simulators()
¶
Return (name, class) pairs for every registered simulator, sorted by name.
Used by the CLI to print human-readable backend listings without
reaching into _REGISTRY from the outside.
Source code in src/openlithohub/simulators/registry.py
Base interface¶
openlithohub.simulators.base
¶
Simulator base class and result/config dataclasses.
BaseSimulator
¶
Bases: ABC
Abstract simulator backend.
A simulator maps mask -> SimulatorResult. The ABC makes no
assumption about differentiability; callers that need gradients
should pick a backend that documents support (e.g.
:class:HopkinsSimulator).
Implementations should be cheap to construct — heavy state (kernel
caches, tool licenses) belongs in :meth:prepare so that callers
can decide when to pay the cost.
Source code in src/openlithohub/simulators/base.py
prepare()
¶
Eagerly initialise backend state (kernels, tool sessions).
Default no-op. Override when there is meaningful setup cost so that callers can amortise it across many simulate() calls.
with_config(config)
¶
Return a sibling simulator using config, sharing cached state where possible.
Default builds a fresh instance via type(self)(config). Subclasses
with expensive per-config setup (SOCS kernels, vendor sessions) should
override to clone cheaply when the new config only differs in fields
the cached state does not depend on (typically dose / threshold).
Source code in src/openlithohub/simulators/base.py
simulate(mask)
abstractmethod
¶
Simulate the aerial image (and resist contour, if available).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mask
|
Tensor
|
Real-valued mask. Shape |
required |
Returns:
| Name | Type | Description |
|---|---|---|
A |
SimulatorResult
|
class: |
SimulatorResult
|
|
Source code in src/openlithohub/simulators/base.py
SimulatorConfig
dataclass
¶
Vendor-neutral simulator configuration.
Backends are free to ignore fields they don't model and to read
extra options from :attr:extra. We deliberately keep the surface
small — fields here must mean the same thing across every backend.
Attributes:
| Name | Type | Description |
|---|---|---|
wavelength_nm |
float
|
Exposure wavelength. 193 = ArF, 13.5 = EUV. |
na |
float
|
Numerical aperture (image-side). |
sigma |
float
|
Outer partial-coherence factor. |
sigma_inner |
float
|
Inner sigma for annular/dipole/quasar (0 = circular). |
pixel_size_nm |
float
|
Physical size of one mask pixel. |
defocus_nm |
float
|
Defocus offset. |
dose |
float
|
Linear dose multiplier. |
threshold |
float
|
Resist intensity threshold for binarization (0–1
relative to |
resist_backend |
str
|
Select resist model. |
extra |
dict[str, Any]
|
Backend-specific options. Treated as opaque by the ABC. |
Source code in src/openlithohub/simulators/base.py
SimulatorResult
dataclass
¶
Output of a simulator forward pass.
Attributes:
| Name | Type | Description |
|---|---|---|
aerial |
Tensor
|
Aerial intensity image, same spatial shape as mask. |
resist |
Tensor | None
|
Optional binarized resist contour (0/1) at
:attr: |
backend |
str
|
Name of the simulator that produced the result. |
metadata |
dict[str, Any]
|
Free-form per-backend metadata (e.g. license info, vendor version, kernel count). Treated as opaque. |
Source code in src/openlithohub/simulators/base.py
Hopkins reference adapter¶
openlithohub.simulators.hopkins_sim
¶
Hopkins/SOCS simulator adapter — the bundled reference backend.
HopkinsSimulator
¶
Bases: BaseSimulator
Differentiable Hopkins/SOCS simulator.
Wraps :func:openlithohub._utils.hopkins.simulate_aerial_image_hopkins.
The full forward pass is auto-differentiable, so this adapter is the
right choice when used as a training-loop oracle for ILT or AI-OPC.
Backend-specific options read from config.extra:
illumination(circular|annular|dipole|quasar) — source shape, defaultcircular.num_kernels(int) — SOCS truncation order, default 24 (per Yang2023_LithoBench §3.2 / Table II — the reference SOCS decomposition for ICCAD16 evaluation).dipole_angle_deg,pole_opening_deg— pole geometry for dipole/quasar.
Source code in src/openlithohub/simulators/hopkins_sim.py
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | |
with_config(config)
¶
Clone, reusing cached SOCS kernels when only dose/threshold changed.
SOCS kernel construction depends on optical fields
(wavelength/NA/sigma/illumination/defocus/pixel_size_nm/num_kernels).
When those are unchanged, we can hand the new sibling our pre-built
:class:HopkinsParams instead of recomputing.
Source code in src/openlithohub/simulators/hopkins_sim.py
Commercial adapters¶
openlithohub.simulators.commercial
¶
Commercial simulator adapter protocol and shared utilities.
Defines :class:CommercialSimulatorAdapter, the protocol that every
vendor-specific adapter (Tachyon, Calibre, …) must satisfy. Provides
common machinery for pre-flight checks, file I/O, and mock mode so each
concrete adapter stays thin.
CommercialSimulatorAdapter
¶
Bases: Protocol
Protocol shared by all commercial simulator adapters.
Concrete adapters (Tachyon, Calibre) must provide:
- :meth:preflight — verify toolchain + license
- :meth:simulate — forward simulation
- :attr:mock_mode — whether the adapter runs without the real tool
Source code in src/openlithohub/simulators/commercial.py
PreflightStatus
dataclass
¶
openlithohub.simulators.calibre
¶
Siemens EDA Calibre nmOPC simulator adapter.
Real Calibre integration requires the Calibre toolchain on PATH plus a
valid license file. The adapter supports mock_mode for testing and CI.
CalibreSimulator
¶
Bases: BaseSimulator
Adapter for Siemens EDA Calibre nmOPC.
config.extra should carry:
calibre_home(str) -- install root containingbin/calibre.runset(str) -- path to the SVRF runset to execute.layer_map(dict[str, int], optional) -- maps OpenLithoHub layer names to Calibre layer numbers.license_server(str, optional) -- for non-default flexlm setups.mock_mode(bool, optional) -- run in mock mode without the real toolchain. Defaults toFalse.
Source code in src/openlithohub/simulators/calibre.py
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | |
preflight()
¶
Check Calibre binary and license availability.
Source code in src/openlithohub/simulators/calibre.py
simulate(mask)
¶
Run Calibre nmOPC forward simulation.
In mock mode, produces a threshold-based approximation that simulates basic OPC behaviour without the commercial tool.
Source code in src/openlithohub/simulators/calibre.py
openlithohub.simulators.tachyon
¶
ASML Brion Tachyon simulator adapter.
Real Tachyon integration requires the Tachyon toolchain on PATH plus a
license. The adapter supports a mock_mode that produces plausible
synthetic results for testing and CI.
TachyonSimulator
¶
Bases: BaseSimulator
Adapter for ASML Brion Tachyon lithography simulator.
config.extra should carry:
tachyon_home(str) -- install root containing the Tachyon binary.recipe(str) -- path to the.tclrecipe to execute.layer_map(dict[str, int], optional) -- OpenLithoHub layer to Tachyon layer mapping.mock_mode(bool, optional) -- run in mock mode without the real toolchain. Defaults toFalse.
Source code in src/openlithohub/simulators/tachyon.py
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | |
preflight()
¶
Check Tachyon binary and license availability.
Source code in src/openlithohub/simulators/tachyon.py
simulate(mask)
¶
Run Tachyon forward simulation.
In mock mode, produces a Gaussian-blurred approximation of the mask that mimics a low-fidelity aerial image without requiring the commercial tool.
Source code in src/openlithohub/simulators/tachyon.py
Measured-source / Zernike-pupil I/O¶
openlithohub._utils.optics
¶
Standardized I/O for measured sources and pupil aberrations.
Hopkins / SOCS already accepts parametric sources (circular, annular, dipole, quasar) and a clean defocus-only pupil. Real production OPC needs more:
- Measured / freeform source intensity — a TIFF or PNG dumped from
the scanner illuminator, or a custom shape designed by SMO. This is
loaded as a 2-D
[N, N]array on normalized pupil coordinates(σx, σy) ∈ [-1, 1]. - Pupil aberrations as Zernike coefficients — typically tens of Zernikes (Z2..Z37 or higher) measured on the scanner. We parse a small text/JSON/CSV file mapping Noll-indexed terms → coefficients in waves of OPD, and synthesize a phase map on the same normalized pupil grid.
Both outputs are plain torch.Tensor so they can be plugged into a
custom Hopkins / SOCS run without further conversion. These three
functions are re-exported from :mod:openlithohub.simulators for
discoverability — the bundled :class:HopkinsSimulator does not
yet consume them through SimulatorConfig.extra; users who need
measured-source or Zernike-pupil pathing currently do so by calling
:func:openlithohub._utils.hopkins.simulate_aerial_image_hopkins
directly with the loaded tensor injected into a custom params object.
Wiring through the public HopkinsSimulator is roadmap (item #65 in
the bug-triage list once the API knobs are settled).
Coordinate convention: normalized pupil coordinates run from -1 to +1
across the diameter, with the unit circle being the NA-defined edge.
The center pixel is at (σx, σy) = (0, 0). For a square N x N
grid this places the center at index (N-1) / 2.
load_source_intensity(path, *, grid_size=None, normalize=True, device='cpu')
¶
Load a measured source intensity image as a square [N, N] tensor.
Supports any format Pillow can read: TIFF (16-bit OK), PNG, BMP, etc.
The image is converted to float32 in the range [0, ∞), optionally
resized to grid_size (bilinear), and optionally normalized so the
sum is 1 (which is what Hopkins J(f) expects).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | Path
|
Image file. |
required |
grid_size
|
int | None
|
If given, resize to |
None
|
normalize
|
bool
|
If True (default), rescale so the source sums to 1. |
True
|
device
|
str | device
|
Torch device for the output tensor. |
'cpu'
|
Returns:
| Type | Description |
|---|---|
Tensor
|
Float32 tensor on |
Tensor
|
pupil-coordinate layout (origin at the geometric center). |
Source code in src/openlithohub/_utils/optics.py
load_zernike_coefficients(path)
¶
Parse a Zernike coefficient file → {noll_index: coeff_in_waves}.
Three formats are supported, dispatched by extension:
.json— flat object{"4": 0.05, "11": -0.02, ...}or a nested form{"zernikes": {"4": 0.05, ...}}..csv— header row required, columnsnollandcoeff(case-insensitive). Other columns are ignored..txt— whitespace-separatednoll coeffpairs,#comments.
Coefficients are interpreted as waves of OPD (the scanner / Synopsys convention). Z1 (piston) is silently dropped — it has no optical effect and many vendor tools include it as a sanity column.
Source code in src/openlithohub/_utils/optics.py
zernike_phase_map(coeffs, grid_size, *, device='cpu')
¶
Synthesize a pupil OPD map from Noll-indexed Zernike coefficients.
Returns a real [N, N] tensor of optical path difference in waves
(multiply by 2π to get phase in radians, or by the wavelength to
get OPD in nm). Outside the unit pupil the map is set to 0.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coeffs
|
dict[int, float]
|
|
required |
grid_size
|
int
|
Output grid edge |
required |
device
|
str | device
|
Torch device. |
'cpu'
|