********* Cookbook ********* This section gives some short examples to give an idea of things that can be done with the Stoner python module in just a few lines. The Util module =============== .. currentmodule:: Stoner.Util The **Stoner** package comes with an extra :py:mod:`Stoner.Util` module that includes some handy utility functions. The :py:class:`Data` Class -------------------------- In practice, one often requires both the analysis functions of :py:class:`Stoner.Analysis.AnalysisMixin` and the plotting functions of :py:class:`Stoner.plot.PlotMixin`. This can be done easily by creating a subclass that inherits from both, but for convenience, the :py:mod:`Stoner.Util` module provides the :py:class:`Data` class that does this for you.:: from Stoner.Core import Data d=Data("File-of-data.txt") Splitting Data into rising and falling values --------------------------------------------- So far the module just contains one function that will take a single :py:class:`Stoner.Core.DataFile` object and split it into a series of :py:class:`Stoner.Core.DataFile` objects where one column is either rising or falling. This is designed to help deal with analysis problems involving hysteretic data.:: from Stoner.Util import split_up_down folder=split_up_down(data,column) In this example *folder* is a :py:class:`Stoner.Folders.DataFolder` instance with two groups, one for rising values of the column and one for falling values of the column. The :py:func:`split\_up_down` will take an optional third parameter which is an existing :py:class:`Stoner.Core.DataFolder` instance to which the new groups (if they don't already exist) and files will be added. Analysis of Hysteresis Loops ---------------------------- Since much of our group's work is concerned with measuring magnetic hystersis loops, the :py:func:`hysteresis_correct` function provides a handy way to correct some instrumental artifacts and measure properties of hysteresis loops.:: from Stoner.Util import hysteresis_correct d=hysteresis_correct('QD-SQUID-VSM.dat',correct_background=False,correct_H=False) e=hysteresis_correct(d) print "Saturation Magnetisation = {}+/-{}".format(e["Ms"],e["Ms Error"]) print "Coercive Field = {}".format(e["Hc"]) print "Remanance = {}".format(e["Remenance"]) The keyword arguments provide options to turn on or off corrections to remove diamagnetic background (and offset in M), and any offset in H (e.g. due to trapped flux in the magnetometer). The latter option is slightly dangerous aas it will also remove the effect of any eexhange bias that moves the coercive field. As well as performing the corrections, the code will add metadata items for: * Background susceptibility (from fitting straight lines to the out part of the data) * Saturation magnetisation and uncertainty (also from fitting lines to the out part of the data) * Coervice Fields (H for zero M) * Remenance (M for zero H) * Saturation Fields (H where M deviates by the standard error from saturation) * Maximum BH product (the point where -H * M is maximum) * Loop Area (from integrating the area inside the hysteresis loop - only valid for complete loops) Some of these parameters are determined by fitting a straight line to the outer portions of the data (i.e. at the extrema in H). The keyword parameter *saturation_fraction* controls the extent of the data assumed to be saturated. Formatting Error Values ----------------------- In experimental physics, the usual practice (unless one has good reason to do otherwise) is to quote uncertainties in a measurement to one significant figure, and then quote the value to the same number of decimal places. Whilst doing this might sound simple, actually doing it seems something that many students find difficult. To hep with this task, the :py:mod:`Stoner.Util` module provides the :py:func:`Stoner.Util.format_error` function.:: from Stoner.Util import format_error from scipy.constants import hbar,m_e,m_u print format_error(value,error) print format_error(m_e,hbar,latex=True) print format_error(hbar/m_e,hbar/m_u,latex=True,mode="eng") print format_error(hbar/m_e,hbar/m_u,latex=True,mode="eng",units="Js rads^{-1}kg^{-1}) The simplest form just outputs value+/- error with suitable rounding. Adding the *latex* keyword argument wraps the output in $...$ and replaces +/- with the equivalent latex math symbol. The *mode* argument can be *eng*,*sci* or *float*(default). The first uses SI prefixes in presenting the number, the second uses mantissa-exponent notation (or x10^{exponent} in latex mode). The third, default, option does no particular scaling. Finally the units keyword allows the inclusion of the units for the quantity in the string - particularly useful in combination with the *eng* mode. .. _fitting_tricks: Fitting Tricks ============== Fitting 3D Data --------------- :py:meth:`Stoner.Analysis.AnalysisMixin.curve_fit` can also be used to fit 3D (or higher order) data - i.e. where there are two independent variables. In order to do this, the *xcol* parameter needs to be an iterable (e.g. list or tuple or array), and the function to be fitted needs to take a tuple of scalars or arrays as the first argument. The following example illustrates this by fitting a plane to a collection of points in 3D space. .. plot:: samples/curve_fit_plane.py :include-source: :outname: curvefit_plane2 Fitting to Minimize a Function ------------------------------ If *ycol* is a numpy array of the same length as the data then the values of ycol are assumed to be the points to fit rather than index of a column. This is useful if the function you want to fit can be written as :math:`f(x_1,x_2,\cdots ,x_n)=0`. In thus case pass *xcol* a list or tuple of columns that make up :math:`x_1,x_2,\cdots ,x_n` and make your function take a tuple of 1D-arrays and pass *ycol* as an array of zeros. For example: .. plot:: samples/sphere_fit.py :include-source: :outname: curvefit_sphere_2 Other Recipes ============== Extract X-Y(Z) from X-Y-Z data ------------------------------ In a number of measurement systems the data is returned as 3 parameters X, Y and Z and one wishes to extract X-Y as a function of constant Z. For example, *I-V* sweeps as a function of gate voltage *V:sub:G*. Assuming we have a data file with columns *Current*, *Voltage*,*Gate*:: d=DataFile('data.txt') t=d for gate in d.unique('Gate'): t.data=d.search('Gate',gate) t.save('Data Gate='+str(gate)+'.txt') The first line opens the data file containing the *I-V(V_G)* data. The second creates a temporary copy of the :py:class:`Stoner.Core.DataFile` object - ensuring that we get a copy of all metadata and column headers. The **for** loop iterates over all unique values of the data in the gate column and then inside the for loop, searches for the corresponding *I-V* data, sets it as the data of the temporary DataFile and then saves it. Mapping X-Y-Z data to Z(X,Y) data ---------------------------------- In a similar fashion to the previous section, where data has been recorded with fixed values of *X* and *Y* eg *I* measured for fixed *V* and *V_*, it can be useful to map the data to a matrix.:: d=DataFile('Data,.txt') t=d for gate in d.unique('Gate'): t=t+d.search('Gate',gate)[:,d.find_col('Current')] t.column_headers=['Bias='+str(x) for x in d.unique('Voltage')] t.add_column(d.unique('Gate'),'Gate Voltage',0) The start of the script follows the previous section, however this time in the for loop the addition operator is used to add a single row to the temporary :py:class:`Stoner.Core.DataFile` *t*. In this case we are using the utility method :py:meth:`Stoner.Core.DataFile.find_col` to find the index of the column with the current data. After the **for** loop we set the column headers in *t* and then insert an additional column at the start with the gate voltage values. The matrix generated by this code is suitable for feeding directly into :py:meth:`Stoner.plot.PlotMixin.plot_matrix`, however, the same plot could be generated directly from the :py:meth:`Stoner.plot.PlotMixin.plot_xyz` method too. Sampling a A 3D Surface function -------------------------------- Suppose you have a function Z(x,y) that is defined over a range -X0..+X0 and -Y0..y0 and want to quickly get a view of the surface. This problem can be easily done by creating a random distribution of (x,y) points, evaluating the function and then using the :py:class:`PlotFile` methods to generate the surface.:: from Stoner import Data from numpy import uniform samples=5000 surf=Data() surf&=uniform(-X0,X0,samples) surf&=uniform(-Y0,Y0,samples) surf.setas="xy" surf&=Z(surf.x,surf.y) surf.setas="xyz" surf.plot() # or surf.plot(cstride=1,rstride=1,linewidth=0) for finer detail. Quickly Sectioning a 3D dataset ------------------------------- We often model the magnetic state in our experiments using a variety of micromagnetic modelling codes, such OOMMF or MuMax. When modelling a 3D system, it is often useful to be able to examine a cross-section of the simulation. The Stoner package provides tools to quickly examine the output data:: from Stoner import Data p=Data('my_simulation.ovf') p.setas="xyzuvw" p=p.section(z=10.5) # Take a slice in the xy plane where z is 10.5 nm p.plot() # A 3D plot with cones p.setas="xy.uvw" p.plot() # a 2D colour wheel plot with triangular glyphs showing vector direction.