Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
8138697
ignoring few things
ShantanuKodgirwar Apr 8, 2026
7547fd7
an initial skeleton for documentation
ShantanuKodgirwar Apr 8, 2026
0cb8d7c
add GitHub Actions workflow for deploying documentation
ShantanuKodgirwar Apr 8, 2026
c80ec4b
adding the site URL
ShantanuKodgirwar Apr 8, 2026
c1416cd
adding a warning message
ShantanuKodgirwar Apr 8, 2026
69f51e6
version bump to 0.2.5
ShantanuKodgirwar Apr 8, 2026
af35bee
add shortcut links to README
ShantanuKodgirwar Apr 8, 2026
7240b3d
adding a warmer bg light color when selecting light theme
ShantanuKodgirwar Apr 10, 2026
f0dc26c
add generate_simu_hdf5 function to create synthetic CPM simulation da…
ShantanuKodgirwar Apr 10, 2026
90ea6d3
updating instructions on downloading example data
ShantanuKodgirwar Apr 10, 2026
c1aee81
initial fixes of these notebooks
ShantanuKodgirwar Apr 10, 2026
e4702eb
add OPRP attribute to Params class
ShantanuKodgirwar Apr 10, 2026
f82cc86
fix getOrientation function to return orientation as is instead of co…
ShantanuKodgirwar Apr 10, 2026
b218034
warning message updated
ShantanuKodgirwar Apr 10, 2026
1942497
Merge branch 'main' into docs
ShantanuKodgirwar Apr 10, 2026
860889e
removing the legacy config submodule
ShantanuKodgirwar Apr 10, 2026
7f1b2d6
adding imports under __all__
ShantanuKodgirwar Apr 10, 2026
ba5e2a5
minor changes to the tutorials
ShantanuKodgirwar Apr 10, 2026
36cc69e
an important fix to only allow cuda12 and now a general flag as `gpu`…
ShantanuKodgirwar Apr 10, 2026
6317508
updated documentation to reflect this
ShantanuKodgirwar Apr 10, 2026
a10f3e2
fixing a small bug
ShantanuKodgirwar Apr 10, 2026
8d1e65c
fix: convert orientation to integer directly in getOrientation function
ShantanuKodgirwar Apr 10, 2026
20c43fc
these notebooks now use gpu properly
ShantanuKodgirwar Apr 10, 2026
95d2885
show3Dslider detects a jupyter cell and executes a different inline i…
ShantanuKodgirwar Apr 10, 2026
023fc9b
all tutorials now work
ShantanuKodgirwar Apr 10, 2026
c3e91a4
update getOrientation to use scalify for orientation value conversion
ShantanuKodgirwar Apr 10, 2026
150de1c
include ipywidgets dependency in project requirements
ShantanuKodgirwar Apr 10, 2026
657a53d
minor readme
ShantanuKodgirwar Apr 10, 2026
8d857e0
updating badge for version
ShantanuKodgirwar Apr 10, 2026
e949df8
minor readme change
ShantanuKodgirwar Apr 11, 2026
a07958e
minor readme update
ShantanuKodgirwar Apr 11, 2026
ae22521
minor changes on GPU instructions under documentation
ShantanuKodgirwar Apr 11, 2026
549db68
creating a link for each section for easy navigation in documentation
ShantanuKodgirwar Apr 11, 2026
a92d275
minor readme change
ShantanuKodgirwar Apr 11, 2026
6faf332
lighter bg shade
ShantanuKodgirwar Apr 13, 2026
2bae2f4
minor correction in docs
ShantanuKodgirwar Apr 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Deploy Docs

on:
push:
branches:
- main

permissions:
contents: write

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: astral-sh/setup-uv@v5

- name: Install docs dependencies
run: uv sync --extra docs

- name: Deploy to GitHub Pages
run: uv run mkdocs gh-deploy --force
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,10 @@ logs_tensorboard
*.lock
.python-version
.DS_Store
.idea
.idea
.claude
.cache

# mkdocs
site/
.cache/mkdocs-jupyter/
16 changes: 16 additions & 0 deletions PtyLab/Engines/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,22 @@

# # for other Engines (like one you are developing but which is too specific) you can always import PtyLab.Engines.<your_engine_filename>.<your_class>
from .BaseEngine import BaseEngine

__all__ = [
"aPIE",
"BaseEngine",
"e3PIE",
"ePIE",
"ePIE_TV",
"mPIE",
"mPIE_tv",
"mqNewton",
"multiPIE",
"OPR",
"pcPIE",
"qNewton",
"zPIE",
]
from .e3PIE import e3PIE
from .ePIE import ePIE
from .ePIE_TV import ePIE_TV
Expand Down
4 changes: 3 additions & 1 deletion PtyLab/Operators/_propagation_kernels.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import cupy as cp
except ImportError:
# print("Cupy unavailable")
import numpy as np
pass

import numpy as np

# from PtyLab.Operators.Operators import cache_size
cache_size = 30
Expand Down
2 changes: 2 additions & 0 deletions PtyLab/Params/Params.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ def __init__(self):
# map a change in positions to a change in z. Experimental, do not use
self.map_position_to_z_change = False

self.OPRP = False

# Default values of all OPR parameters
# Index of the incoherent probe modes that are linked in a subspace.
self.OPR_modes = np.array([0])
Expand Down
7 changes: 0 additions & 7 deletions PtyLab/config/__init__.py

This file was deleted.

3 changes: 3 additions & 0 deletions PtyLab/io/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ def getExampleDataFolder():
"""
fracPy_folder = Path(__file__).parent.parent.parent
return fracPy_folder / "example_data"


from PtyLab.io.generateSimulationData import generate_simu_hdf5 # noqa: E402
135 changes: 135 additions & 0 deletions PtyLab/io/generateSimulationData.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
"""
Generate synthetic CPM simulation data and save as simu.hdf5.

This module contains the simulation logic from example_scripts/simulateData.py
as a callable function with no side effects (no plotting, no os.chdir).
"""

from pathlib import Path

import h5py
import numpy as np
from scipy.signal import convolve2d

from PtyLab.Operators.Operators import aspw
from PtyLab.utils.scanGrids import GenerateNonUniformFermat
from PtyLab.utils.utils import cart2pol, circ, fft2c, gaussian2D


def generate_simu_hdf5(output_path: Path) -> None:
"""
Generate a synthetic CPM ptychography dataset and save it as an HDF5 file.

The simulation produces a focused Gaussian probe and a complex spiral object,
computes diffraction patterns using Fraunhofer propagation, adds Poisson noise,
and writes the result to *output_path*.

Parameters
----------
output_path : Path
Destination file path (e.g. ``example_data/simu.hdf5``).
The parent directory must already exist.
"""
output_path = Path(output_path)

# Physical properties
wavelength = 632.8e-9
zo = 5e-2
binningFactor = 1

# Detector coordinates
Nd = 2**7
dxd = 2**11 / Nd * 4.5e-6
Ld = Nd * dxd

# Probe coordinates
dxp = wavelength * zo / Ld
Np = Nd
Lp = dxp * Np
xp = np.arange(-Np // 2, Np // 2) * dxp
Xp, Yp = np.meshgrid(xp, xp)

# Object coordinates
No = 2**10 + 2**9
dxo = dxp
Lo = dxo * No
xo = np.arange(-No // 2, No // 2) * dxo
Xo, Yo = np.meshgrid(xo, xo)

# Generate illumination: focused beam via pinhole + lens
f = 8e-3
pinhole = circ(Xp, Yp, Lp / 2)
pinhole = convolve2d(pinhole, gaussian2D(5, 1).astype(np.float32), mode="same")

probe = aspw(pinhole, 2 * f, wavelength, Lp)[0]

aperture = circ(Xp, Yp, 3 * Lp / 4)
aperture = convolve2d(aperture, gaussian2D(5, 3).astype(np.float32), mode="same")
probe = (
probe
* np.exp(-1.0j * 2 * np.pi / wavelength * (Xp**2 + Yp**2) / (2 * f))
* aperture
)
probe = aspw(probe, 2 * f, wavelength, Lp)[0]

# Generate object: complex spiral pattern
d = 1e-3
b = 33
theta, rho = cart2pol(Xo, Yo)
t = (1 + np.sign(np.sin(b * theta + 2 * np.pi * (rho / d) ** 2))) / 2
phaseFun = np.exp(1.0j * (theta + 2 * np.pi * (rho / d) ** 2))
t = t * circ(Xo, Yo, Lo) * (1 - circ(Xo, Yo, 200 * dxo)) * phaseFun + circ(
Xo, Yo, 130 * dxo
)
obj = convolve2d(t, gaussian2D(5, 3), mode="same")
object_ = obj * phaseFun

# Generate scan positions (non-uniform Fermat spiral)
numPoints = 100
radius = 150
p = 1
R, C = GenerateNonUniformFermat(numPoints, radius=radius, power=p)

encoder = np.vstack((R * dxo, C * dxo)).T
positions = np.round(encoder / dxo)
offset = np.array([50, 20])
positions = (positions + No // 2 - Np // 2 + offset).astype(int)
numFrames = len(R)

# Estimate beam size for entrancePupilDiameter
beamSize = (
np.sqrt(np.sum((Xp**2 + Yp**2) * np.abs(probe) ** 2) / np.sum(abs(probe) ** 2))
* 2.355
)

# Compute ptychogram
ptychogram = np.zeros((numFrames, Nd, Nd))
for loop in np.arange(numFrames):
row, col = positions[loop]
sy = slice(row, row + Np)
sx = slice(col, col + Np)
objectPatch = object_[..., sy, sx].copy()
esw = objectPatch * probe
ESW = fft2c(esw)
ptychogram[loop] = abs(ESW) ** 2

# Simulate Poisson noise
bitDepth = 14
maxNumCountsPerDiff = 2**bitDepth
ptychogram = ptychogram / np.max(ptychogram) * maxNumCountsPerDiff
noise = np.random.poisson(ptychogram)
ptychogram += noise
ptychogram[ptychogram < 0] = 0

# Save to HDF5
with h5py.File(output_path, "w") as hf:
hf.create_dataset("ptychogram", data=ptychogram, dtype="f")
hf.create_dataset("encoder", data=encoder, dtype="f")
hf.create_dataset("binningFactor", data=binningFactor, dtype="i")
hf.create_dataset("dxd", data=(dxd,), dtype="f")
hf.create_dataset("Nd", data=(Nd,), dtype="i")
hf.create_dataset("No", data=(No,), dtype="i")
hf.create_dataset("zo", data=(zo,), dtype="f")
hf.create_dataset("wavelength", data=(wavelength,), dtype="f")
hf.create_dataset("entrancePupilDiameter", data=(beamSize,), dtype="f")
hf.create_dataset("orientation", data=0)
32 changes: 16 additions & 16 deletions PtyLab/io/readHdf5.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import logging
from pathlib import Path
import tables

import h5py
import numpy as np
import logging
import tables
from scipy.io import loadmat
import h5py

logger = logging.getLogger("readHdf5")

Expand Down Expand Up @@ -34,7 +35,7 @@ def loadInputData(filename: Path, requiredFields, optionalFields):
"""
filename = Path(filename)
if not filename.exists():
raise FileNotFoundError(f'Could not find file {filename}.')
raise FileNotFoundError(f"Could not find file {filename}.")
logger.debug("Loading input data: %s", filename)

# sanity checks
Expand All @@ -48,7 +49,6 @@ def loadInputData(filename: Path, requiredFields, optionalFields):
dataset = dict()
try:
with tables.open_file(str(filename), mode="r") as hdf5File:

# load the required fields
for key in requiredFields:
value = hdf5File.root[key].read()
Expand All @@ -66,19 +66,20 @@ def loadInputData(filename: Path, requiredFields, optionalFields):
except Exception as e:
logger.error("Error reading hdf5 file!")
raise e
if 'encoder' in dataset:
print(f'Found encoder with shape {dataset["encoder"].shape}')
if dataset['encoder'].shape[0] < dataset['encoder'].shape[1]:
dataset['encoder'] = dataset['encoder'].T
print('Warning: Automatically changing the shape of encoder. ')
dataset['encoder'] -= dataset['encoder'].mean(axis=0, keepdims=True)
print(dataset['encoder'].shape, dataset['encoder'].mean(axis=0))
if "encoder" in dataset:
print(f"Found encoder with shape {dataset['encoder'].shape}")
if dataset["encoder"].shape[0] < dataset["encoder"].shape[1]:
dataset["encoder"] = dataset["encoder"].T
print("Warning: Automatically changing the shape of encoder. ")
dataset["encoder"] -= dataset["encoder"].mean(axis=0, keepdims=True)
print(dataset["encoder"].shape, dataset["encoder"].mean(axis=0))
# dataset['encoder'] *= -1
# dirty hack for now

# upsample

from PtyLab.utils.utils import fft2c, ifft2c

# dataset['dxd'] = dataset['dxd'] / 2
# padwidth = dataset['ptychogram'].shape[-1]//2
# padwidth = [[0,0], [padwidth, padwidth], [padwidth,padwidth]]
Expand All @@ -95,8 +96,9 @@ def getOrientation(filename):
orientation = None
with h5py.File(str(filename), "r") as archive:
if "orientation" in archive.keys():
orientation = np.array(archive["orientation"]).ravel()[0].astype(int)
return int(orientation) if orientation is not None else None
raw_value = np.array(archive["orientation"]).ravel()[0]
orientation = scalify(raw_value)
return orientation


def checkDataFields(filename, requiredFields):
Expand All @@ -121,5 +123,3 @@ def checkDataFields(filename, requiredFields):
raise KeyError("hdf5 file misses key %s" % k)

return None


Loading