{"id":30516145,"url":"https://github.com/thomashirtz/lithox","last_synced_at":"2025-08-26T09:29:38.717Z","repository":{"id":307448796,"uuid":"1029521683","full_name":"thomashirtz/lithox","owner":"thomashirtz","description":"High performance jax-based photolithography simulation.","archived":false,"fork":false,"pushed_at":"2025-08-25T08:14:02.000Z","size":1095,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-08-25T10:53:04.175Z","etag":null,"topics":["equinox","jax","photolithography","python","simulation"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thomashirtz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-07-31T07:08:20.000Z","updated_at":"2025-08-25T08:14:05.000Z","dependencies_parsed_at":"2025-07-31T11:26:58.031Z","dependency_job_id":"1bfc1424-f164-4a80-8865-01bd0914aaf1","html_url":"https://github.com/thomashirtz/lithox","commit_stats":null,"previous_names":["thomashirtz/lithox"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/thomashirtz/lithox","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomashirtz%2Flithox","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomashirtz%2Flithox/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomashirtz%2Flithox/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomashirtz%2Flithox/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thomashirtz","download_url":"https://codeload.github.com/thomashirtz/lithox/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomashirtz%2Flithox/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272205374,"owners_count":24891744,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-26T02:00:07.904Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["equinox","jax","photolithography","python","simulation"],"created_at":"2025-08-26T09:29:34.200Z","updated_at":"2025-08-26T09:29:38.708Z","avatar_url":"https://github.com/thomashirtz.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🔬 `lithox`\n\nHigh-performance JAX-based photolithography simulation.\n\n## Installation\n\n```bash\npip install git+https://github.com/thomashirtz/lithox#egg=lithox\n```\n\n## Theory\n\n`lithox` models partially coherent imaging via the Hopkins formulation with a coherent-mode decomposition. The formula used to compute the aerial image is:\n\n$$\nI(x,y)=\\sum_{k} s_k \\left|\\big(h_k * (\\mathrm{dose}\\cdot M)\\big)(x, y)\\right|^{2},\n$$\n\nwhere $M$ is the mask, $h_k$ are coherent-mode PSFs, and $s_k\\ge 0$ are the corresponding weights. In practice this is evaluated in the Fourier domain:\n\n$$\nI=\\sum_{k} s_k \\left| \\mathcal{F}^{-1} \\left(\\mathcal{F}\\\\{\\mathrm{dose}\\cdot M\\\\}\\cdot H_k\\right)\\right|^{2},\n$$\n\nwith $H_k=\\mathcal{F}\\\\{h_k\\\\}$.\n\nA simple resist and print model maps the aerial image to binary output:\n\n$$\nR = \\sigma \\big(\\alpha (I-\\tau_{\\mathrm{resist}})\\big),\\qquad\nP = \\mathbf{1} \\left[R\u003e\\tau_{\\mathrm{print}}\\right].\n$$\n\n**Notations:**\n\n* $x,y$: image-plane spatial coordinates.\n* $M\\in[0,1]^{H\\times W}$: mask transmission.\n* $\\mathrm{dose}\\in\\mathbb{R}_+$: exposure dose scalar applied to the mask.\n* $h_k$: $k$-th coherent-mode point spread function (PSF).\n* $H_k$: Fourier transform of $h_k$.\n* $s_k\\ge 0$: nonnegative weight for mode $k$ (sums the partially coherent contributions).\n* $*$: 2D convolution; $\\mathcal{F}$, $\\mathcal{F}^{-1}$: centered FFT and IFFT used in code.\n* $I\\in\\mathbb{R}_+^{H\\times W}$: aerial image (intensity).\n* $\\sigma(\\cdot)$: logistic sigmoid; $\\alpha\u003e0$ is its steepness.\n* $\\tau_{\\mathrm{resist}}$: threshold shifting the sigmoid; $R\\in(0,1)^{H\\times W}$ is the “resist” image.\n* $\\tau_{\\mathrm{print}}$: binarization threshold; $P\\in\\\\{0,1\\\\}^{H\\times W}$ is the final printed pattern.\n\nGradients are supported via a custom VJP for the aerial step, enabling end-to-end autodiff through $I\\rightarrow R\\rightarrow P$ (with a straight-through style threshold in practice).\n\n\u003e The coherent-mode kernels and weights used by lithox are taken from the [lithobench](https://github.com/shelljane/lithobench) project and redistributed here for convenience.\n\n## Simulation\n\nGetting started:\n\n```python\nimport lithox as ltx\nimport matplotlib.pyplot as plt\n\nmask = ltx.load_image(\"./data/mask.png\", size=1024) # Update the path if necessary\n\nsimulator = ltx.LithographySimulator()\noutput = simulator(mask)\n\nplt.imshow(output.printed)\nplt.show()\n```\n\n**What does `output` contain?**\n\n* `output.aerial: jnp.Array` — continuous aerial intensity $I$ (float32, shape `[H, W]`).\n* `output.resist: jnp.Array` — sigmoid-mapped resist image $R\\in(0,1)$ (float32, `[H, W]`).\n* `output.printed: jnp.Array` — binary print $P\\in\\\\{0,1\\\\}$ (float32, `[H, W]`).\n\n`LithographySimulator` variants (identical API, different conditions):\n\n* `LithographySimulator.nominal(...)`: in-focus kernels, nominal dose.\n* `LithographySimulator.maximum(...)`: in-focus kernels, maximum dose.\n* `LithographySimulator.minimum(...)`: defocus kernels, minimum dose.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./scripts/simulation.png\" alt=\"scripts/simulation.png\" width=\"500\"/\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003cem\u003eExample of simulation output generated with the script \u003ccode\u003e\u003ca href=\"./scripts/simulation.py\"\u003e./scripts/simulation.py\u003c/a\u003e\u003c/code\u003e\u003c/em\u003e\n\u003c/p\u003e\n\n\u003cdetails\u003e\n \n\u003csummary\u003eMore detailed example\u003c/summary\u003e\n \n```python\nimport lithox as ltx\nimport matplotlib.pyplot as plt\n\nmask = ltx.load_image(\"./data/mask.png\", size=1024) # Update the path if necessary\n\nsimulator = ltx.LithographySimulator()\noutput = simulator(mask)\n\ntitle_to_data = {\n    \"Mask\": mask,\n    \"Aerial image\": output.aerial,\n    \"Resist image\": output.resist,\n    \"Printed image\": output.printed,\n}\n\nfig, axes = plt.subplots(2, 2, constrained_layout=True)\nfor ax, (title, data) in zip(axes.flat, title_to_data.items()):\n    ax.imshow(data, cmap=\"gray\")\n    ax.set_title(title, pad=2)\n    ax.axis(\"off\")\n\nplt.show()\n```\n \n\u003c/details\u003e\n\n## Process variation\n\n`ProcessVariationSimulator` bundles three simulators to emulate process corners:\n\n* **nominal** — in-focus, nominal dose\n* **max** — in-focus, maximum dose\n* **min** — defocus, minimum dose\n\nCalling it returns all three results in a structured output so you can compare aerial/resist/printed across corners.\n\n```python\nfrom lithox.variation import ProcessVariationSimulator\n\npvs = ProcessVariationSimulator()\npv_output = pvs(mask)\n\n# Access by field:\nI_nom, I_max, I_min = pv_output.aerial.nominal, pv_output.aerial.max, pv_output.aerial.min\nR_nom, R_max, R_min = pv_output.resist.nominal, pv_output.resist.max, pv_output.resist.min\nP_nom, P_max, P_min = pv_output.printed.nominal, pv_output.printed.max, pv_output.printed.min\n```\n\n**Process-variation band (PVB)**\n\nA simple stability indicator is the fraction of pixels that flip across corners. Two convenience methods are available:\n\n```python\n# Per-pixel PVB map in [0,1], shape [H, W]\npvb_map = pvs.get_pvb_map(mask)\n\n# Mean PVB value in [0,1], scalar\npvb_mean = pvs.get_pvb_mean(mask)\n```\n\nMathematically:\n\n$$\n\\mathrm{PVB\\_map}(x,y) = P_{\\max}(x,y) - P_{\\min}(x,y), \\quad \\mathrm{PVB\\_mean} = \\frac{1}{HW} \\sum_{x,y} \\mathrm{PVB\\_map}(x,y)\n$$\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./scripts/variation.png\" alt=\"scripts/variation.png\" width=\"500\"/\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003cem\u003eExample of process variation band computed using the script \u003ccode\u003e\u003ca href=\"./scripts/variation.py\"\u003e./scripts/variation.py\u003c/a\u003e\u003c/code\u003e\u003c/em\u003e\n\u003c/p\u003e\n\n## Citation\n\nIf you use lithox in your work—whether for research, publications, or projects—please cite it as follows:\n\n```bibtex\n@misc{hirtz2025lithox,\n  author       = {Thomas Hirtz},\n  title        = {lithox: A JAX-based lithography simulation library},\n  year         = {2025},\n  howpublished = {\\url{https://github.com/thomashirtz/lithox}},\n  publisher    = {GitHub},\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthomashirtz%2Flithox","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthomashirtz%2Flithox","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthomashirtz%2Flithox/lists"}