|
| 1 | +{ |
| 2 | + "cells": [ |
| 3 | + { |
| 4 | + "cell_type": "markdown", |
| 5 | + "metadata": {}, |
| 6 | + "source": [ |
| 7 | + "# Friedel Pair Mapping notebook\n", |
| 8 | + "\n", |
| 9 | + "The idea behind this is similar to the idea in DCT and here https://doi.org/10.1107/S1600576724009634, where Friedel pairs are used to locate where diffraction spots come from in space. In those cases we use peaks that are 180 degrees apart. This notebook is looking for peaks that are separated by twotheta. These are the peaks we use in the friedel_rois macro at ID11 that aligns grains on the centre of rotation.\n", |
| 10 | + "\n", |
| 11 | + "The pairs we use will have:\n", |
| 12 | + "- eta -> -eta\n", |
| 13 | + "- tth -> tth\n", |
| 14 | + "- gve -> -gve\n", |
| 15 | + "\n", |
| 16 | + "Jon Wright. March 2025.\n" |
| 17 | + ] |
| 18 | + }, |
| 19 | + { |
| 20 | + "cell_type": "code", |
| 21 | + "execution_count": null, |
| 22 | + "metadata": { |
| 23 | + "tags": [] |
| 24 | + }, |
| 25 | + "outputs": [], |
| 26 | + "source": [ |
| 27 | + "dset_file =\"/path/to/dataset\"\n", |
| 28 | + "phase_name = 'phase' # or None\n", |
| 29 | + "y0 = 0.0\n", |
| 30 | + "gvtol = 0.002 # value is often OK\n", |
| 31 | + "# xpos, ypos and ytol erance for a position in space.\n", |
| 32 | + "# update these after you see your plot\n", |
| 33 | + " \n", |
| 34 | + "ytol = 1.0 # For selecting peaks in space\n", |
| 35 | + "px = 0.0 \n", |
| 36 | + "py = 0.0\n", |
| 37 | + "\n", |
| 38 | + "\n", |
| 39 | + "# test dataset:\n", |
| 40 | + "if False:\n", |
| 41 | + " dset_file = \"/data/id11/inhouse2/test_data_3DXRD/S3DXRD/FeAu/PROCESSED_DATA/20250303_JW/tomo_route/FeAu_0p5_tR_nscope/FeAu_0p5_tR_nscope_top_200um/FeAu_0p5_tR_nscope_top_200um_dataset.h5\"\n", |
| 42 | + " y0 = -15.9 # matches your dataset, if known.\n", |
| 43 | + " phase_name = 'Fe' \n", |
| 44 | + " # xpos, ypos and tolerance for a position in space.\n", |
| 45 | + " # update these after you see your plot\n", |
| 46 | + " px = -108.\n", |
| 47 | + " py = -164.\n", |
| 48 | + " ytol = 2." |
| 49 | + ] |
| 50 | + }, |
| 51 | + { |
| 52 | + "cell_type": "code", |
| 53 | + "execution_count": null, |
| 54 | + "metadata": { |
| 55 | + "tags": [] |
| 56 | + }, |
| 57 | + "outputs": [], |
| 58 | + "source": [ |
| 59 | + "import os, sys, time\n", |
| 60 | + "start = time.time()\n", |
| 61 | + "# USER: You can change this location if you want\n", |
| 62 | + "exec(open('/data/id11/nanoscope/install_ImageD11_from_git.py').read())\n", |
| 63 | + "PYTHONPATH = setup_ImageD11_from_git()" |
| 64 | + ] |
| 65 | + }, |
| 66 | + { |
| 67 | + "cell_type": "code", |
| 68 | + "execution_count": null, |
| 69 | + "metadata": { |
| 70 | + "tags": [] |
| 71 | + }, |
| 72 | + "outputs": [], |
| 73 | + "source": [ |
| 74 | + "%matplotlib ipympl\n", |
| 75 | + "import numpy as np\n", |
| 76 | + "import matplotlib.pyplot as plt\n", |
| 77 | + "import ImageD11.sinograms.dataset\n", |
| 78 | + "import ImageD11.sinograms.geometry\n", |
| 79 | + "import ImageD11.transformer\n", |
| 80 | + "import ImageD11.indexing\n", |
| 81 | + "import ImageD11.sinograms.roi_iradon\n", |
| 82 | + "import scipy.spatial" |
| 83 | + ] |
| 84 | + }, |
| 85 | + { |
| 86 | + "cell_type": "code", |
| 87 | + "execution_count": null, |
| 88 | + "metadata": { |
| 89 | + "tags": [] |
| 90 | + }, |
| 91 | + "outputs": [], |
| 92 | + "source": [ |
| 93 | + "ds = ImageD11.sinograms.dataset.load(dset_file)\n", |
| 94 | + "print(ds)" |
| 95 | + ] |
| 96 | + }, |
| 97 | + { |
| 98 | + "cell_type": "code", |
| 99 | + "execution_count": null, |
| 100 | + "metadata": { |
| 101 | + "tags": [] |
| 102 | + }, |
| 103 | + "outputs": [], |
| 104 | + "source": [ |
| 105 | + "cf_4d = ds.get_cf_4d()\n", |
| 106 | + "ds.update_colfile_pars(cf_4d, phase_name)\n", |
| 107 | + "print(cf_4d.nrows/1e6, \"million peaks read in\")" |
| 108 | + ] |
| 109 | + }, |
| 110 | + { |
| 111 | + "cell_type": "code", |
| 112 | + "execution_count": null, |
| 113 | + "metadata": { |
| 114 | + "tags": [] |
| 115 | + }, |
| 116 | + "outputs": [], |
| 117 | + "source": [ |
| 118 | + "def find_pairs_minus_eta( cf, gvtol = 0.002 ):\n", |
| 119 | + " \"\"\"\n", |
| 120 | + " Locate Friedel pairs with eta -> -eta and g -> -g\n", |
| 121 | + " returns ip, im == indices for eta+ and eta- pairs\n", |
| 122 | + " \"\"\"\n", |
| 123 | + " # select peaks from left or right of detector\n", |
| 124 | + " fp = np.mgrid[0:cf.nrows][cf_4d.eta > 0 ]\n", |
| 125 | + " fm = np.mgrid[0:cf.nrows][cf_4d.eta < 0 ]\n", |
| 126 | + " # gvector arrays of these peaks, make into KD trees\n", |
| 127 | + " kdp = scipy.spatial.cKDTree( np.transpose( (cf.gx[fp], cf.gy[fp], cf.gz[fp]) ) )\n", |
| 128 | + " kdm = scipy.spatial.cKDTree( -np.transpose( (cf.gx[fm], cf.gy[fm], cf.gz[fm]) ) )\n", |
| 129 | + " # Find the pairs\n", |
| 130 | + " coo = kdp.sparse_distance_matrix( kdm, gvtol, output_type = 'coo_matrix' )\n", |
| 131 | + " # Return the indices in the original cf_4d\n", |
| 132 | + " return fp[coo.row], fm[coo.col]\n", |
| 133 | + "\n", |
| 134 | + "def locate_pairs( cf, pairs, y0 = 0. ):\n", |
| 135 | + " \"\"\"\n", |
| 136 | + " Fit the centre of mass position of the pairs\n", |
| 137 | + " cf = colfile\n", |
| 138 | + " pairs = (ip, im) = indices of low, high pair in cf\n", |
| 139 | + " \n", |
| 140 | + " Returns sx, sy == sample x and y co-ordinates of the peak-pair\n", |
| 141 | + " \"\"\"\n", |
| 142 | + " ip, im = pairs\n", |
| 143 | + " r = np.radians(cf.omega )\n", |
| 144 | + " so = np.sin(r)\n", |
| 145 | + " co = np.cos(r)\n", |
| 146 | + " # For each paired peak take dty - y0 == observed dty value\n", |
| 147 | + " y = np.transpose((cf.dty[ip]-y0, cf.dty[im]-y0 ))\n", |
| 148 | + " # Find the 2x2 matrix for fitting the dty position (-,-) in geometry notebook\n", |
| 149 | + " R = np.transpose( (( -so[ip], -co[ip] ),\n", |
| 150 | + " ( -so[im], -co[im] )), axes=(2,0,1))\n", |
| 151 | + " # Solve for x,y in the sample co-ordinates\n", |
| 152 | + " return np.linalg.solve( R, y ).T" |
| 153 | + ] |
| 154 | + }, |
| 155 | + { |
| 156 | + "cell_type": "markdown", |
| 157 | + "metadata": {}, |
| 158 | + "source": [ |
| 159 | + "The next cell is locating the Friedel pairs. It seems to need about 1 second per million peaks." |
| 160 | + ] |
| 161 | + }, |
| 162 | + { |
| 163 | + "cell_type": "code", |
| 164 | + "execution_count": null, |
| 165 | + "metadata": { |
| 166 | + "tags": [] |
| 167 | + }, |
| 168 | + "outputs": [], |
| 169 | + "source": [ |
| 170 | + "ip, im = find_pairs_minus_eta( cf_4d, gvtol=gvtol )\n", |
| 171 | + "print('Got',len(ip),'pairs from',cf_4d.nrows,'peaks, fraction paired =',len(ip)*2/cf_4d.nrows )" |
| 172 | + ] |
| 173 | + }, |
| 174 | + { |
| 175 | + "cell_type": "markdown", |
| 176 | + "metadata": {}, |
| 177 | + "source": [ |
| 178 | + "Now fit the positions. Should be faster than finding the pairs." |
| 179 | + ] |
| 180 | + }, |
| 181 | + { |
| 182 | + "cell_type": "code", |
| 183 | + "execution_count": null, |
| 184 | + "metadata": { |
| 185 | + "tags": [] |
| 186 | + }, |
| 187 | + "outputs": [], |
| 188 | + "source": [ |
| 189 | + "sx, sy = locate_pairs( cf_4d, (ip,im), y0 = y0 )" |
| 190 | + ] |
| 191 | + }, |
| 192 | + { |
| 193 | + "cell_type": "markdown", |
| 194 | + "metadata": {}, |
| 195 | + "source": [ |
| 196 | + "For the plots, we have selected a position in space to extract a grain:" |
| 197 | + ] |
| 198 | + }, |
| 199 | + { |
| 200 | + "cell_type": "code", |
| 201 | + "execution_count": null, |
| 202 | + "metadata": { |
| 203 | + "tags": [] |
| 204 | + }, |
| 205 | + "outputs": [], |
| 206 | + "source": [ |
| 207 | + "# Mask for this position in space\n", |
| 208 | + "m = ((abs(sx-px) < ytol) & (abs(sy-py) < ytol))\n", |
| 209 | + "idxpt = np.concatenate( (ip[m], im[m]))\n", |
| 210 | + "# gvectors from the point px,py in the sample\n", |
| 211 | + "gvp = (cf_4d.gx[idxpt],cf_4d.gy[idxpt],cf_4d.gz[idxpt]) \n", |
| 212 | + "\n", |
| 213 | + "# verify whether we got the geometry right\n", |
| 214 | + "xfit,yfit,y0fit = ImageD11.sinograms.geometry.fit_sine_wave(cf_4d.omega[idxpt], cf_4d.dty[idxpt], [0.1,0.1,y0])\n", |
| 215 | + "calcy = ImageD11.sinograms.geometry.dtycalc(ds.obincens, xfit, yfit, y0fit )" |
| 216 | + ] |
| 217 | + }, |
| 218 | + { |
| 219 | + "cell_type": "code", |
| 220 | + "execution_count": null, |
| 221 | + "metadata": { |
| 222 | + "tags": [] |
| 223 | + }, |
| 224 | + "outputs": [], |
| 225 | + "source": [ |
| 226 | + "f = plt.figure(figsize=(10,4),constrained_layout=True)\n", |
| 227 | + "a = [f.add_subplot(131),f.add_subplot(132),f.add_subplot(133, projection='3d', proj_type='ortho') ]\n", |
| 228 | + "f.colorbar( a[0].hist2d( sx, sy, bins=ds.ybinedges, norm='log', zorder=1)[-1] )\n", |
| 229 | + "a[0].scatter(px,py,s=10,color='k',fc='none',ec='k')\n", |
| 230 | + "a[0].set(aspect='equal', xlabel='x sample' , ylabel='ysample',title='Pair locations')\n", |
| 231 | + "a[1].plot( cf_4d.omega[idxpt], cf_4d.dty[idxpt], '.')\n", |
| 232 | + "a[1].plot( ds.obincens, calcy, '-', label='fitted' )\n", |
| 233 | + "a[1].set(title='Fit: x %.3f, y %.3f y0 %.3f'%( xfit, yfit, y0fit ), xlabel='omega', ylabel='dty');\n", |
| 234 | + "# Select peaks from some position in space\n", |
| 235 | + "a[2].scatter( *gvp,',',s=1)\n", |
| 236 | + "a[2].set(title=f\"Selected gve\",\n", |
| 237 | + " xlabel='gx', ylabel='gy', zlabel='gz');" |
| 238 | + ] |
| 239 | + }, |
| 240 | + { |
| 241 | + "cell_type": "markdown", |
| 242 | + "metadata": {}, |
| 243 | + "source": [ |
| 244 | + "## Check: run some indexing" |
| 245 | + ] |
| 246 | + }, |
| 247 | + { |
| 248 | + "cell_type": "code", |
| 249 | + "execution_count": null, |
| 250 | + "metadata": { |
| 251 | + "tags": [] |
| 252 | + }, |
| 253 | + "outputs": [], |
| 254 | + "source": [ |
| 255 | + "def run_index_unknown(gid, cf, frac=0.2, tol=0.05, sigma=5):\n", |
| 256 | + " \"\"\"\n", |
| 257 | + " gid = string to name files\n", |
| 258 | + " cf = colfile to index\n", |
| 259 | + " frac = fraction of peaks you want to index\n", |
| 260 | + " tol = hkl tolerance\n", |
| 261 | + " \"\"\"\n", |
| 262 | + " tr = ImageD11.transformer.transformer()\n", |
| 263 | + " tr.colfile = cf\n", |
| 264 | + " tr.parameterobj = cf.parameters\n", |
| 265 | + " # need to have cell params to save gves\n", |
| 266 | + " tr.parameterobj.set('cell_lattice_[P,A,B,C,I,F,R]','P')# integer not backwards compatible\n", |
| 267 | + " tr.savegv( f'gr{gid}.gve' )\n", |
| 268 | + " !index_unknown.py -g gr{gid}.gve -m 40 --fft -t {tol} -f {frac} -o {gid}.ubi -k 1 -s {sigma}\n", |
| 269 | + " if os.path.exists(f'{gid}.ubi'):\n", |
| 270 | + " fixhandedness( f'{gid}.ubi' ) # the script on path might not be the one in git\n", |
| 271 | + " \n", |
| 272 | + "def fixhandedness( ubifile ):\n", |
| 273 | + " ubis = ImageD11.indexing.readubis( ubifile )\n", |
| 274 | + " for i in range(len(ubis)):\n", |
| 275 | + " if np.linalg.det( ubis[i] ) < 0:\n", |
| 276 | + " ubis[i][-1] = -ubis[i][-1]\n", |
| 277 | + " assert np.linalg.det( ubis[i] ) > 0\n", |
| 278 | + " ImageD11.indexing.write_ubi_file( ubifile, ubis )" |
| 279 | + ] |
| 280 | + }, |
| 281 | + { |
| 282 | + "cell_type": "code", |
| 283 | + "execution_count": null, |
| 284 | + "metadata": { |
| 285 | + "tags": [] |
| 286 | + }, |
| 287 | + "outputs": [], |
| 288 | + "source": [ |
| 289 | + "run_index_unknown( '0', cf_4d.copyrows( idxpt ), frac=0.2, tol=0.1, sigma=10)" |
| 290 | + ] |
| 291 | + }, |
| 292 | + { |
| 293 | + "cell_type": "markdown", |
| 294 | + "metadata": {}, |
| 295 | + "source": [ |
| 296 | + "You can check for higher symmetry at https://www.cryst.ehu.es/cryst/lattice.html" |
| 297 | + ] |
| 298 | + }, |
| 299 | + { |
| 300 | + "cell_type": "code", |
| 301 | + "execution_count": null, |
| 302 | + "metadata": { |
| 303 | + "tags": [] |
| 304 | + }, |
| 305 | + "outputs": [], |
| 306 | + "source": [ |
| 307 | + "print('Total runtime', time.time()-start)" |
| 308 | + ] |
| 309 | + } |
| 310 | + ], |
| 311 | + "metadata": { |
| 312 | + "kernelspec": { |
| 313 | + "display_name": "Python 3 (main)", |
| 314 | + "language": "python", |
| 315 | + "name": "python3" |
| 316 | + }, |
| 317 | + "language_info": { |
| 318 | + "codemirror_mode": { |
| 319 | + "name": "ipython", |
| 320 | + "version": 3 |
| 321 | + }, |
| 322 | + "file_extension": ".py", |
| 323 | + "mimetype": "text/x-python", |
| 324 | + "name": "python", |
| 325 | + "nbconvert_exporter": "python", |
| 326 | + "pygments_lexer": "ipython3", |
| 327 | + "version": "3.11.6" |
| 328 | + }, |
| 329 | + "widgets": { |
| 330 | + "application/vnd.jupyter.widget-state+json": { |
| 331 | + "state": {}, |
| 332 | + "version_major": 2, |
| 333 | + "version_minor": 0 |
| 334 | + } |
| 335 | + } |
| 336 | + }, |
| 337 | + "nbformat": 4, |
| 338 | + "nbformat_minor": 4 |
| 339 | +} |
0 commit comments