Skip to main content

Wafer-level Zernike polynomial decomposition and fitting

Project description

wlzpoly — Wafer-Level Zernike Polynomials

Decompose 13-point wafer thickness measurements into 9 Zernike coefficients (LSQ / Ridge), with a reproducible demo workflow that generates synthetic data, fits it, and verifies the recovered coefficients against ground truth.

Install

pip install wlzpoly

Requires Python 3.9+. Dependencies: numpy >= 1.22, pandas >= 1.5, matplotlib >= 3.5, tqdm >= 4.60.

Quick start

import numpy as np
from wlzpoly import ZernikePolynomials, WaferLevelZernikePolynomials

# 1) Build a wavefront from known coefficients (Noll j -> a_j)
z = ZernikePolynomials(coeffs={1: 500.0, 4: -12.0, 6: 0.5}, n_terms=9)
field = z.evaluate(rho=np.array([0.0, 0.5, 1.0]),
                   theta=np.array([0.0, 0.0, 0.0]))

# 2) Fit Zernike coefficients from measurements at known coordinates
#    coords_df   : DataFrame indexed by point_id, columns ['x','y'] (mm),
#                  attrs['wafer_radius_mm']
#    df_measured : DataFrame indexed by MultiIndex(wafer_id, point_id),
#                  column ['T']
wlz = WaferLevelZernikePolynomials(
    coords_df=coords_df, coordinate="cartesian", n_terms=9,
)
fit_results = wlz.fit_coefficients(mesured_df=df_measured, solver="lsq")
# fit_results : list of {"id": <wafer_id>, "coeffs": np.ndarray}

# 3) Render a fitted wafer field
fig = wlz.draw_field(coeffs=fit_results[0]["coeffs"])
fig.savefig("W_01_fit.png", dpi=130, bbox_inches="tight")

ZernikePolynomials follows the Noll convention and supports any radial order — the j -> (n, m) mapping is computed dynamically.

Public API

from wlzpoly import (
    ZernikePolynomials,           # pure-math + per-wavefront instance
    WaferLevelZernikePolynomials, # wafer-aware (coords + measurements -> fit)
    fit_lsq, fit_ridge,           # general-purpose linear solvers
)
Function / class Purpose
ZernikePolynomials.basis(j, rho, theta) Single Zernike basis Z_j(rho, theta)
ZernikePolynomials.basis_matrix(rho, theta, n_terms=...) Design matrix A for fitting
ZernikePolynomials.pyramid_image(n_max=..., names=..., return_type=...) Zernike pyramid PNG / Figure
ZernikePolynomials(coeffs=...).evaluate(rho, theta) Evaluate a specific wavefront
WaferLevelZernikePolynomials(coords_df, coordinate, n_terms) Pre-compute A from measurement layout
wlz.fit_coefficients(mesured_df, solver, lam) Per-wafer LSQ / Ridge fit
wlz.draw_field(coeffs) Heatmap with measurement-point overlay
fit_lsq(A, T) a_hat = (A^T A)^-1 A^T T
fit_ridge(A, T, lam) a_hat = (A^T A + lam I)^-1 A^T T
loocv_lambda(A, T, lambdas) LOOCV-driven lambda selection
wlzpoly.decompose.load_wafer_coordinates(wafer_points_file, coordinate) Read points JSON into a DataFrame
wlzpoly.decompose.load_measured_data(target_file) Read target CSV into long-format DataFrame

Three-stage demo (after development install)

git clone https://github.com/ykim2718/WaferLevelZernikePolynomials.git
cd WaferLevelZernikePolynomials
pip install -e .

cd examples
.\run_demo.ps1                                   # runs all three stages

run_demo.ps1 invokes four steps in order:

python generate_samples.py ...                   # Stage 1: synthesize wafers
                                                 #   -> 1_samples/target_file.csv,
                                                 #      1_samples/ground_truth.csv, ...
python -m wlzpoly.decompose --solver lsq   ...   # Stage 2a: LSQ fit
                                                 #   -> 2_decomposition/decomposed_targets_lsq.csv
python -m wlzpoly.decompose --solver ridge --auto_lam ...
                                                 # Stage 2b: Ridge fit with LOOCV
                                                 #   -> 2_decomposition/decomposed_targets_ridge.csv
python -m wlzpoly.verify ...                     # Stage 3: read both CSVs + ground_truth,
                                                 #   compare, plot (no fitting)

Every parameter is a CLI flag — --working_folder, --wafer_points, --input_file, --output_file, --auto_lam, --col_wafer_id, --col_points, --coeff_prefix, --decomposed_lsq_file, --decomposed_ridge_file, --ground_truth_file, --n_terms, --solver, etc. (wlzpoly.reconstruct uses --input_folder, --wafer_point_json, --decomposed_file instead — see its section below.) See python -m wlzpoly.decompose -h / verify -h / reconstruct -h for the full list. Demo outputs land in examples/{1_samples, 2_decomposition, 3_verification, 4_reconstruction}/; pre-generated copies are visible on the GitHub repo.

Optional — wlzpoly.reconstruct

Inverse of decompose: pushes fitted coefficients back through the basis matrix to rebuild the N-point measurement profile (T = A·a). No R² / no ground-truth comparison — production / inference use case where the true T is unknown.

python -m wlzpoly.reconstruct `
    --decomposed_file decomposed_targets.csv `
    --wafer_point_json wafer_points.json --n_terms 9 `
    --output_folder ./4_reconstruction

CLI options are split into Input (--input_folder, --wafer_point_json, --decomposed_file, --n_terms, --coordinate, --col_wafer_id, --col_points, --coeff_prefix, --coeff_suffix) and Output (--output_folder, --output_file) argparse groups. --coeff_suffix (default "") lets you read ML-pipeline CSVs whose coefficient columns carry trailing tags like _pred / _true (columns are read as <prefix><j><suffix> and stripped internally).

Python API: wlzpoly.reconstruct.reconstruct(...) returns a pd.DataFrame (index = wafer_id, columns = P1..PN); the CLI handles CSV writing.

Documentation

Full documentation — folder layout, CLI options, configuration files, output schemas, scenario reference, recipes, algorithm summary — lives on the GitHub README:

https://github.com/ykim2718/WaferLevelZernikePolynomials

Links

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

wlzpoly-0.0.2.tar.gz (34.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

wlzpoly-0.0.2-py3-none-any.whl (31.9 kB view details)

Uploaded Python 3

File details

Details for the file wlzpoly-0.0.2.tar.gz.

File metadata

  • Download URL: wlzpoly-0.0.2.tar.gz
  • Upload date:
  • Size: 34.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.14

File hashes

Hashes for wlzpoly-0.0.2.tar.gz
Algorithm Hash digest
SHA256 0c8d555af52331019a42ebe4916dfe844c26464b6709651fceb1fa6b3e12e10d
MD5 3251ba934962d4e1b7b087865261900d
BLAKE2b-256 0d93ab9afa90806991e2e89b800215d5aac872efd156ffb91be51855986d289f

See more details on using hashes here.

File details

Details for the file wlzpoly-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: wlzpoly-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 31.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.14

File hashes

Hashes for wlzpoly-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 d8c152bdd3ec1c59e7936a53a83573067ff945fc5f1b81cc8466efb0c71f3549
MD5 40213c156187fa0d6a01527604afa1be
BLAKE2b-256 81c215c96d40e3a92ff10aef806543a6d078351729d74f831301efbc0823d092

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page