{"cells": [{"cell_type": "markdown", "id": "413a3048-7624-4fa6-a0e5-a2d92e55f9d0", "metadata": {}, "source": ["# Nearfield Ptychography\n", "**Learning how to reconstruct a nearfield ptychography dataset with PtyPy.**\n", "\n", "All examples so far were based on farfield ptychography, but some users might be interested to reconstruct data from a nearfield ptychography experiment. In this example, we are going to use data that was published in this article about [Phase-Vortex Removal for Quantitative X-Ray Nanotomography with Near-Field Ptychography](http://dx.doi.org/10.1103/PhysRevApplied.14.064078) and has kindly been provided by the authors to be used in this tutorial. "]}, {"cell_type": "markdown", "id": "63e10ba2-bced-48da-b372-4b410b438ee5", "metadata": {}, "source": ["## The data\n", "**Thanks to Pierre Thibault for sharing this data set for the purpose of this workshop.**\n", "\n", "\n", "The sample used for this nearfield ptychography experiment is made of Al and Ni particles inside a glass capillary and the data was collected at the ID16 instrument at the ESRF. The raw data is saved in an HDF5 file with the following entries\n", "\n", "```\n", "\t* data shape = (17, 2048, 2048)\n", "\t* posx shape = (17,)\n", "\t* posy shape = (17,)\n", "```\n", "\n", "showing that we have $17$ nearfield diffraction images of size $2048x2048$."]}, {"cell_type": "markdown", "id": "d8815084-434a-4264-8e73-5d4a1f8b68b6", "metadata": {}, "source": ["## Loading the data\n", "\n", "We can use the ```Hdf5Loader```\n", "\n", "```python\n", "p.scans.scan_00.data = u.Param()\n", "p.scans.scan_00.data.name = 'Hdf5Loader'\n", "p.scans.scan_00.data.orientation = 3\n", "```\n", "\n", "to load the intensities\n", "\n", "```python\n", "p.scans.scan_00.data.intensities = u.Param()\n", "p.scans.scan_00.data.intensities.file = path_to_data\n", "p.scans.scan_00.data.intensities.key = 'data'\n", "```\n", "\n", "and scan positions\n", "\n", "```python\n", "p.scans.scan_00.data.positions = u.Param()\n", "p.scans.scan_00.data.positions.file = path_to_data\n", "p.scans.scan_00.data.positions.slow_key = 'posy'\n", "p.scans.scan_00.data.positions.slow_multiplier = 1\n", "p.scans.scan_00.data.positions.fast_key = 'posx'\n", "p.scans.scan_00.data.positions.fast_multiplier = 1\n", "```\n", "\n", "We also need to specify some meta information\n", "\n", "```python\n", "p.scans.scan_00.data.auto_center = False \n", "p.scans.scan_00.data.psize = 50.705e-9\n", "p.scans.scan_00.data.distance = 0.019 \n", "p.scans.scan_00.data.energy = 17.05\n", "```"]}, {"cell_type": "markdown", "id": "c7b8dac1-ec91-42b8-b609-66e8860fcb70", "metadata": {}, "source": ["`````{admonition} Note \n", ":class: note\n", "Nearfield ptychography experiments are usually performed in cone-beam geometry and therefore the estimate of the sample-to-detector distance needs to be adjusted such that it can be used within parallel geometry models, see this article for more information.\n", "`````"]}, {"cell_type": "markdown", "id": "c4a480bb-2fda-4477-9fb7-8379f432031e", "metadata": {}, "source": ["## The scan model\n", "Since we are only a few frames, it is reasonable to use the ```\"Full\"``` scan model, but we need to switch ```propagation``` to ```nearfield```\n", "\n", "```python\n", "p.scans = u.Param()\n", "p.scans.scan_00 = u.Param()\n", "p.scans.scan_00.name = 'Full'\n", "p.scans.scan_00.propagation = \"nearfield\"\n", "```"]}, {"cell_type": "markdown", "id": "9c985550-8869-44f0-8c86-0ad9aa166ac4", "metadata": {}, "source": ["## Initial probe\n", "\n", "For a nearfield ptychography reconstruction, the best starting guess for the illumination would be a flatfield (beam on but without sample) back-propagated into the sample plane. If there is no flatfield available, it is also reasonable to simply average over all raw data frames and back-propagate the result into the sample plane. This can be achieved by choosing the ```\"stxm\"``` option the ```illumination.model```\n", "\n", "```python\n", "p.scans.scan_00.illumination = u.Param()\n", "p.scans.scan_00.illumination.model = \"stxm\"\n", "p.scans.scan_00.illumination.aperture = None\n", "```"]}, {"cell_type": "markdown", "id": "1d962f7c-dde5-4aa2-a92d-9fe28fde9c5b", "metadata": {}, "source": ["## Reconstruction engine\n", "\n", "For the reconstruction, we have chosen a combination of DM followed by ML refinement with the following set of parameters\n", "\n", "```python\n", "p.engines = u.Param()\n", "p.engines.DM = u.Param()\n", "p.engines.DM.name = 'DM_pycuda'\n", "p.engines.DM.numiter = 500\n", "p.engines.DM.numiter_contiguous = 10\n", "p.engines.DM.probe_support = None\n", "p.engines.DM.clip_object = (.2, 1.1)\n", "p.engines.DM.fourier_power_bound = 0.0\n", "p.engines.DM.probe_update_start = 0\n", "\n", "p.engines.ML = u.Param()\n", "p.engines.ML.name = 'ML_pycuda'\n", "p.engines.ML.numiter = 500\n", "p.engines.ML.numiter_contiguous = 10\n", "p.engines.ML.ML_type = 'Gaussian'\n", "p.engines.ML.floating_intensities = False\n", "p.engines.ML.probe_support = None\n", "p.engines.ML.reg_del2 = True\n", "p.engines.ML.reg_del2_amplitude = 0.005\n", "p.engines.ML.scale_precond = False\n", "p.engines.ML.probe_update_start = 0\n", "```\n", "\n", "resulting in the following reconstruction of the AlNi sample\n", "\n", "![](./_assets/nearfield_recons_dm_ml.png)"]}, {"cell_type": "markdown", "id": "bc8f7e85-7830-406d-813f-89aae9f47459", "metadata": {}, "source": ["---"]}, {"cell_type": "code", "execution_count": null, "id": "ee3cbe0e-7393-4e07-8e6c-a0063eb55dbe", "metadata": {"tags": []}, "outputs": [], "source": ["import ptypy, h5py, os\n", "import ptypy.utils as u\n", "\n", "# This will import the HDF5Loader class\n", "ptypy.load_ptyscan_module(\"hdf5_loader\")\n", "\n", "# This will import the GPU engines\n", "ptypy.load_gpu_engines(\"cupy\") \n", "\n", "# Root directory of tutorial data\n", "tutorial_data_home = \"../../data/\"\n", "\n", "# Path to HDF5 file with raw data\n", "dataset = \"esrf_id16_AlNi_nearfield/S00084_data_bin1_newpos_2048x2048.h5\"\n", "\n", "# Absolute path to HDF5 file with raw data\n", "path_to_data = os.path.join(tutorial_data_home, dataset)\n", "\n", "# Create parameter tree\n", "p = u.Param()\n", "\n", "# Set verbose level to interactive\n", "p.verbose_level = \"interactive\"\n", "\n", "# Scan label\n", "p.run = \"esrf_id16_AlNi_nearfield\"\n", "\n", "# Set io settings (no files saved)\n", "p.io = u.Param()\n", "p.io.rfile = None\n", "p.io.autosave = u.Param(active=False)\n", "p.io.interaction = u.Param(active=False)\n", "\n", "# Live-plotting during the reconstruction\n", "p.io.autoplot = u.Param()\n", "p.io.autoplot.active=True\n", "p.io.autoplot.threaded = False\n", "p.io.autoplot.layout = \"jupyter\"\n", "p.io.autoplot.interval = 10\n", "\n", "# Define the scan model\n", "p.scans = u.Param()\n", "p.scans.scan_00 = u.Param()\n", "p.scans.scan_00.name = 'Full'\n", "\n", "# Switch propagation to nearfield\n", "p.scans.scan_00.propagation = \"nearfield\"\n", "\n", "# Initial illumination (based on flatfield)\n", "p.scans.scan_00.illumination = u.Param()\n", "p.scans.scan_00.illumination.model = \"stxm\"\n", "p.scans.scan_00.illumination.aperture = None\n", "\n", "# Initial object\n", "p.scans.scan_00.sample = u.Param()\n", "p.scans.scan_00.sample.model = None\n", "p.scans.scan_00.sample.diversity = None\n", "p.scans.scan_00.sample.process = None\n", "\n", "# Coherence parameters (modes)\n", "p.scans.scan_00.coherence = u.Param()\n", "p.scans.scan_00.coherence.num_probe_modes = 1\n", "p.scans.scan_00.coherence.num_object_modes = 1\n", "\n", "# Data loader\n", "p.scans.scan_00.data = u.Param()\n", "p.scans.scan_00.data.name = 'Hdf5Loader'\n", "p.scans.scan_00.data.orientation = 3\n", "\n", "p.scans.scan_00.data.intensities = u.Param()\n", "p.scans.scan_00.data.intensities.file = path_to_data\n", "p.scans.scan_00.data.intensities.key = 'data'\n", "\n", "p.scans.scan_00.data.positions = u.Param()\n", "p.scans.scan_00.data.positions.file = path_to_data\n", "p.scans.scan_00.data.positions.slow_key = 'posy'\n", "p.scans.scan_00.data.positions.slow_multiplier = 1\n", "p.scans.scan_00.data.positions.fast_key = 'posx'\n", "p.scans.scan_00.data.positions.fast_multiplier = 1\n", "\n", "# Read meta information\n", "p.scans.scan_00.data.auto_center = False \n", "p.scans.scan_00.data.psize = 50.705e-9\n", "p.scans.scan_00.data.distance = 0.019 \n", "p.scans.scan_00.data.energy = 17.05\n", "\n", "# Reconstruct using GPU-accelerated DM/ML engines\n", "p.engines = u.Param()\n", "p.engines.DM = u.Param()\n", "p.engines.DM.name = 'DM_cupy'\n", "p.engines.DM.numiter = 500\n", "p.engines.DM.numiter_contiguous = 10\n", "p.engines.DM.probe_support = None\n", "p.engines.DM.clip_object = (.2, 1.1)\n", "p.engines.DM.fourier_power_bound = 0.0\n", "p.engines.DM.probe_update_start = 0\n", "\n", "p.engines.ML = u.Param()\n", "p.engines.ML.name = 'ML_cupy'\n", "p.engines.ML.numiter = 500\n", "p.engines.ML.numiter_contiguous = 10\n", "p.engines.ML.ML_type = 'Gaussian'\n", "p.engines.ML.floating_intensities = False\n", "p.engines.ML.probe_support = None\n", "p.engines.ML.reg_del2 = True\n", "p.engines.ML.reg_del2_amplitude = 0.005\n", "p.engines.ML.scale_precond = False\n", "p.engines.ML.probe_update_start = 0\n", "\n", "# Run reconstruction\n", "P = ptypy.core.Ptycho(p,level=5)"]}], "metadata": {"kernelspec": {"display_name": "PtyPy", "language": "python", "name": "ptypy_env"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7"}}, "nbformat": 4, "nbformat_minor": 5}