diff --git a/ImageD11/nbGui/fit_geometry.py b/ImageD11/nbGui/fit_geometry.py index 9fc90b34..0b2d57b8 100644 --- a/ImageD11/nbGui/fit_geometry.py +++ b/ImageD11/nbGui/fit_geometry.py @@ -1,114 +1,119 @@ -import ipywidgets, pylab as pl + +import ipywidgets, pylab as pl, numpy as np, time from ImageD11 import transformer -from tkinter import * -from tkinter import filedialog +from IPython.display import display class FitGeom(transformer.transformer): "Gives an IPython notebook UI for the experimental calibrations" - try: - if get_ipython().__class__.__name__ == 'ZMQInteractiveShell': - interactive = True - else: - interactive = False - except NameError: - interactive = False + def __init__(self): transformer.transformer.__init__(self) - self.vars = "y_center z_center distance tilt_y tilt_z".split() - self.steps = (1, 1, 100, 0.01, 0.01, 0) - self.nv = len(self.vars) + names = "cell__a cell__b cell__c cell_alpha cell_beta cell_gamma cell_lattice_[P,A,B,C,I,F,R]".split() +\ + "distance wavelength y_size z_size y_center z_center tilt_x tilt_y tilt_z t_x t_y t_z omegasign".split() +\ + "o11 o12 o21 o22 fit_tolerance".split() + self.names = names + self.gui = None - def __parCallBack( self, arg ): - self.parameterobj.parameters.update( { arg['owner'].description : arg['new'] } ) - self.__drawPlot() + def parCallBack( self, arg, name ): + self.parameterobj.parameters.update( { name : arg['new'] } ) + self.drawPlot() - def __fixVaryCallBack( self, arg ): + def fixVaryCallBack( self, arg, name ): name = arg['owner'].description.split(" ")[1] - vars = self.getvars() - if arg.new and not (name in self.vars): - vars.append( name ) - if name in self.vars and not arg.new: - vars.remove(name) - self.parameterobj.set_varylist(vars) + vary = arg.new + if arg.new: + if name not in self.parameterobj.varylist: + self.parameterobj.varylist.append(name) + else: + self.parameterobj.varylist.remove(name) - def __fitCallBack(self, arg): + def fitCallBack(self, arg): """ fit call back - runs fit """ lo, hi = self.ax1.get_xlim() self.fit( lo, hi ) - self.__updateGui() - - def __drawPlot(self): - tth, eta = self.compute_tth_eta() - self.pt1.set_data( tth, eta ) - - def __loadCallBack(self, arg): - rootTk = Tk() - rootTk.withdraw() - rootTk.call('wm', 'attributes', '.', '-topmost', True) - filename = filedialog.askopenfilename() - get_ipython().run_line_magic('gui', 'tk') - if not filename == '': - self.loadfileparameters(filename) - self.__updateGui() - - def __saveCallBack(self, arg): - rootTk = Tk() - rootTk.withdraw() - rootTk.call('wm', 'attributes', '.', '-topmost', True) - filename = filedialog.asksaveasfilename() - get_ipython().run_line_magic('gui', 'tk') - if not filename == '': - self.parameterobj.saveparameters(filename) - - def __updateGui(self): - for i, pname in enumerate(self.vars): - self.layout[i,0].value = self.parameterobj.get(pname) - self.__drawPlot() + self.updateGui() + + def drawPlot(self): + self.compute_tth_eta() + self.addcellpeaks() + self.pt1.set_data( self.colfile.tth, self.colfile.eta ) + self.pt2.set_data( self.theorytth, np.full_like(self.theorytth,0) ) + self.ax1.set(title='fitted') + self.fig1.canvas.draw() + self.fig1.canvas.flush_events() + + def updateGui(self): + for i, name in enumerate( self.parameterobj.varylist ): + self.valuewidgets[name].value = self.parameterobj.parameters[name] + self.drawPlot() - def __drawWidgets(self): - nv = self.nv - vars = self.vars - steps = self.steps - self.layout = ipywidgets.GridspecLayout(nv+1,3) - for i,( pname, pstep ) in enumerate( zip( vars, steps ) ) : - self.layout[i,0] = ipywidgets.FloatText( description=pname, - value = self.parameterobj.parameters.get(pname), - step=pstep) - self.layout[i,0].observe( self.__parCallBack , names='value' ) - self.layout[i,1] = ipywidgets.ToggleButton( description="Vary "+pname, - value = pname in self.getvars() ) - self.layout[i,1].observe( self.__fixVaryCallBack, names='value' ) - - self.layout[nv,0] = ipywidgets.FloatText( description='fit_tolerance', - value = self.parameterobj.parameters.get("fit_tolerance"), step=0,) - self.layout[nv,0].observe( self.__parCallBack , names='value' ) - - self.layout[nv,1] = ipywidgets.Button(description="Run Fit (blocks)") - self.layout[nv,1].on_click( self.__fitCallBack ) - - self.layout[0,2] = ipywidgets.Button(description="Load parameters") - self.layout[0,2].on_click( self.__loadCallBack ) - - self.layout[1,2] = ipywidgets.Button(description="Save to a file") - self.layout[1,2].on_click( self.__saveCallBack ) + def drawWidgets(self): + values = {} + fix = {} + for i,name in enumerate( self.names ): + value = self.parameterobj.parameters[name] + values[name] = ipywidgets.FloatText( + description="", + value = value, + layout=ipywidgets.Layout(positioning='left')) + values[name].observe( lambda a, name=name: self.parCallBack(a, name), + names='value' ) + if self.parameterobj.par_objs[name].can_vary: + fix[name] = ipywidgets.Checkbox( description="Vary "+name, + value = name in self.parameterobj.varylist, + indent=False, + layout=ipywidgets.Layout(positioning='left')) + fix[name].observe( lambda a, name=name: self.fixVaryCallBack(a, name), + names='value' ) + else: + fix[name] = ipywidgets.Label( + value=name, + layout=ipywidgets.Layout(positioning='left') ) + self.valuewidgets = values + self.fixwidgets = fix + def fitGui(self): - if self.__class__.interactive: - self.fig1 = pl.figure(1, figsize=(9,6)) + if self.gui is not None: + return self.gui + else: + with pl.ioff(): + self.fig1 = pl.figure(figsize=(9,6), constrained_layout=True) self.ax1 = self.fig1.add_subplot() - tth, eta = self.compute_tth_eta() + self.compute_tth_eta() self.addcellpeaks() - self.pt1, = self.ax1.plot( tth, eta, ",") + self.pt1, = self.ax1.plot( self.colfile.tth, self.colfile.eta, ".", + ms=2, zorder=1) self.ax1.set(xlabel="tth", ylabel="eta") - self.ax1.plot( self.theorytth, [0,]*len(self.theorytth), "r|", ms=360, alpha=0.2 ) - # Add controls - self.__drawWidgets() - display(self.layout) - else: - print('Sorry, this Gui works only in IPython notebooks!') - - - + self.pt2, = self.ax1.plot( self.theorytth, np.full_like(self.theorytth,0), + "g|", ms=360, lw= 0.1, zorder=2 ) + + self.fitbutton = ipywidgets.Button(description="Fit x plot range", + width='auto') + + self.fitbutton.on_click( self.fitCallBack ) + self.drawWidgets() + self.gui = ipywidgets.GridBox( + [ ipywidgets.VBox( list(self.fixwidgets.values()) ), + ipywidgets.VBox( list(self.valuewidgets.values()) ), + ipywidgets.VBox( [ self.fitbutton, self.fig1.canvas ], ) ], + layout=ipywidgets.Layout( width='100%', + grid_template_rows='auto auto auto', + grid_template_columns='15% 15% 70%',) ) + display(self.gui) + ping(self.fig1) # gets lost on run all cells otherwise + + + + +def ping(fig): + # https://github.com/matplotlib/ipympl/issues/290 + canvas = fig.canvas + #display(canvas) + canvas._handle_message(canvas, {'type': 'send_image_mode'}, []) + canvas._handle_message(canvas, {'type':'refresh'}, []) + canvas._handle_message(canvas,{'type': 'initialized'},[]) + canvas._handle_message(canvas,{'type': 'draw'},[]) \ No newline at end of file diff --git a/ImageD11/nbGui/silicon_calib.ipynb b/ImageD11/nbGui/silicon_calib.ipynb new file mode 100755 index 00000000..2ff05707 --- /dev/null +++ b/ImageD11/nbGui/silicon_calib.ipynb @@ -0,0 +1,351 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Silicon calibration for a single crystal on ID11 nscope\n", + "\n", + "Uses the older ImageD11 calibration fitting routines\n", + "\n", + "Last updated 24/7/2024 by @jonwright" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "exec(open('/data/id11/nanoscope/install_ImageD11_from_git.py').read())\n", + "PYTHONPATH = setup_ImageD11_from_git( os.path.join( os.environ['HOME'],'git'), 'ImageD11_clean' )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib ipympl\n", + "import pylab as pl\n", + "import numpy as np\n", + "import ImageD11.sinograms.dataset\n", + "import ImageD11.transformer\n", + "import ImageD11.columnfile\n", + "import ImageD11.indexing\n", + "import ImageD11.grain\n", + "import ImageD11.nbGui.fit_geometry" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# 'Ag': 25.514, 'Sn': 29.2001, 'Nd': 43.5689, 'Gd': 50.2391, 'Hf': 65.3508, 'W' : 69.525, 'Pt': 78.3948, 'Pb': 88.0045\n", + "a_silicon = 5.43094\n", + "econst = 12.398423\n", + "energy = 43.5689" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Load some peaks for your silicone\n", + "# colf = ImageD11.columnfile.columnfile('Silicon/Silicon_100ms/Silicon_100ms_peaks_2d.h5')\n", + "dset = ImageD11.sinograms.dataset.load(\"WAu_siliconAttrz25_dataset.h5\")\n", + "colf = dset.get_cf_2d()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Remove any weak peaks / noise (average intensity > cutoff)\n", + "cutoff = 10\n", + "colf.filter(colf.sum_intensity / colf.Number_of_pixels > cutoff)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "f, a = pl.subplots(1,2,figsize=(12,6), constrained_layout=True)\n", + "a[0].plot(colf.fc,colf.sum_intensity/colf.Number_of_pixels,'.',alpha=0.2)\n", + "a[0].set(yscale='log',xlabel='pixel',ylabel='intensity')\n", + "a[1].plot(colf.fc,colf.sc,'.')\n", + "a[1].set(xlabel='pixel', ylabel='pixel');" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "colf.writefile(\"si.flt\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "with open(\"si_start.par\",\"w\") as pars:\n", + " pars.write(f\"\"\"cell__a {a_silicon}\n", + "cell__b {a_silicon}\n", + "cell__c {a_silicon}\n", + "cell_alpha 90.0\n", + "cell_beta 90.0\n", + "cell_gamma 90.0\n", + "cell_lattice_[P,A,B,C,I,F,R] 227\n", + "chi 0.0\n", + "distance 151000.0\n", + "fit_tolerance 0.05\n", + "o11 -1\n", + "o12 0\n", + "o21 0\n", + "o22 -1\n", + "omegasign 1.0\n", + "t_x 0\n", + "t_y 0\n", + "t_z 0\n", + "tilt_x 0.0\n", + "tilt_y 0.0\n", + "tilt_z 0.0\n", + "wavelength {econst/energy}\n", + "wedge 0.0\n", + "y_center 1050.4779244096353\n", + "y_size 75.0\n", + "z_center 1111.1724398511979\n", + "z_size 75.0\"\"\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "ui = ImageD11.nbGui.fit_geometry.FitGeom( )\n", + "ui.loadfiltered( 'si.flt' )\n", + "ui.loadfileparameters( 'si_start.par' )\n", + "ui.fitGui()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "ui.valuewidgets['fit_tolerance'].value = 0.5\n", + "ui.fit(2,9)\n", + "ui.valuewidgets['fit_tolerance'].value = 0.05\n", + "ui.fit(3,21)\n", + "ui.fit(0,180)\n", + "ui.updateGui()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "ui.savegv('si.gve')\n", + "ui.saveparameters('si_powder.par')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Change log level to 1 to see what it did\n", + "idx = ImageD11.indexing.index( ui.colfile, npk_tol=[( ui.colfile.nrows//2, 0.05),], log_level=3)\n", + "idx.ubis" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "idx.saveubis('si.ubi')" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "# TODO : modernize the rest ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "!makemap.py -f si.flt -u si.ubi -U si.map -p si_powder.par -l cubic -s cubic -t 0.05 --omega_slop={dset.ostep/2}\n", + "!makemap.py -f si.flt -u si.map -U si.map -p si_powder.par -l cubic -s cubic -t 0.025 --omega_slop={dset.ostep/2}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "!refine_em.py si.flt.new si.map si_powder.par --omega_slop={dset.ostep/2} -x wedge -l cubic" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "!cp 0.par si.par\n", + "!makemap.py -f si.flt -u si.map -U si.map -p si.par -l cubic -s cubic -t 0.05 --omega_slop={dset.ostep/2}\n", + "!makemap.py -f si.flt -u si.map -U si.map -p si.par -l cubic -s cubic -t 0.025 --omega_slop={dset.ostep/2}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "!refine_em.py si.flt.new si.map si.par --omega_slop={dset.ostep/2} -x wedge -l cubic" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!cp 0.par si.par\n", + "!cat si.par" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "!makemap.py -f si.flt -u si.map -U si.map -p si.par -s cubic -t 0.025 --omega_slop={dset.ostep/2}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "!ubi2cellpars.py si.map" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "g = ImageD11.grain.read_grain_file('si.map')[0]\n", + "v = np.linalg.det(g.ubi)\n", + "a_avg = pow(v ,1/3)\n", + "\n", + "deviatoric = g.eps_grain_matrix( [a_avg, a_avg, a_avg, 90, 90, 90] )\n", + "print('deviatoric strains, should be zero, so an estimate of precision:\\n',deviatoric)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "wold = ui.parameterobj.get('wavelength')\n", + "wnew = wold*a_silicon/a_avg\n", + "print( 'Wavelength input',wold,'estimated from silicon',wnew)\n", + "print( 'Energy input',econst/wold,'estimated from silicon',econst/wnew)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (main)", + "language": "python", + "name": "python3" + }, + "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.11.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/ImageD11/sinograms/dataset.py b/ImageD11/sinograms/dataset.py index c59087bd..9ac4e6cb 100644 --- a/ImageD11/sinograms/dataset.py +++ b/ImageD11/sinograms/dataset.py @@ -126,6 +126,7 @@ def __init__( self._peaks_table = None self._pk2d = None self._pk4d = None + self.parfile = None if filename is not None: self.load(filename) @@ -611,8 +612,9 @@ def get_colfile_from_peaks_dict(self, peaks_dict=None): cf = colfile_from_dict(spat(peaks_dict)) # Update parameters for the columnfile - cf.parameters.loadparameters(self.parfile) - cf.updateGeometry() + if self.parfile is not None: + cf.parameters.loadparameters(self.parfile) + cf.updateGeometry() return cf def get_cf_2d(self): diff --git a/ImageD11/transformer.py b/ImageD11/transformer.py index 964b75e8..9aff6c3d 100644 --- a/ImageD11/transformer.py +++ b/ImageD11/transformer.py @@ -190,9 +190,9 @@ def __init__(self, parfile=None, fltfile=None): # Interesting - is this an alias for the dict? self.pars = self.parameterobj.get_parameters() self.colfile = None - self.xname = None - self.yname = None - self.omeganame = None + self.xname = 'sc' + self.yname = 'fc' + self.omeganame = 'omega' self.theoryds = None if parfile is not None: self.loadfileparameters(parfile) @@ -234,9 +234,9 @@ def setxyomcols(self, xname, yname, omeganame): self.xname = xname self.yname = yname self.omeganame = omeganame - logging.warning("titles are %s %s %s" % (self.xname, - self.yname, - self.omeganame)) + #logging.warning("titles are %s %s %s" % (self.xname, + # self.yname, + # self.omeganame)) def getcols(self): return self.colfile.titles