Programming Documentation

UML Diagram

../../_images/ana_uml.svg

Fig. 39 UML diagram of inheritance and created objects

High Level Interface

Plot

class Plot[source]

Bases: object

A small class used to plot figures for a presentation.

compare_sweeprates(filename='compare-sweeprates', show_inset=False)[source]

Compare different Sweeprates, fits the data and

Returns

Return type

None.

compare_voltages(filename='compare-voltages', show_inset=False)[source]

Compare different applied Voltages.

Returns

Return type

compare-voltages.pdf

fast_sweeprate(show_numbers=[320, 325, 323, 329], xlim=None, filename='FastSR-1', pos2=False, title='\\Huge \\textbf{a) Large Field Sweeps}', show_inset=False)[source]

anas Fast Sweeps over large sweep ranges (1T)

Parameters
  • show_numbers

  • xlim

  • filename (TYPE, optional) – DESCRIPTION. The default is ‘FastSR-1’.

  • pos2

  • title

Returns

Return type

None.

first_sweeps(add=False, filename='vacant-sweeps', show_inset=False)[source]

First field sweeps (plus minus 50/75/100/etc.)

Parameters
  • add – adding more data

  • filename – saving as pdf (default: vacant-sweeps)

Returns

Return type

vacant-sweeps.pdf

get_plot(**kwargs)[source]
measplan12()[source]
measplan12_2()[source]
measplan12_full(name='measplan12-full')[source]

measurement Plan #12 All 14 measurements in one Graph.

Parameters

name

multiple_fspans(filename='multiple-fspans')[source]
negative_sweeps()[source]

Testing without file output.

Returns

Return type

None.

plot_cubes_trees()[source]

Plots difference between RAW data from Cubes and Trees (1st Generation)

Returns

Return type

None.

plot_hloop_gradient(limit=50, filename='hloop-gradient')[source]

Plotting the gradient along the

Parameters
  • limit

  • filename

Returns

Return type

None.

plot_hloop_gradient2(limit=50, filename='hloop-gradient')[source]

Plotting the gradient along the

Parameters
  • limit

  • filename

Returns

Return type

None.

plot_hloop_gradient3(ax, to_show=[139, 140], limit=50, column='Vx8')[source]

Plotting the gradient along the

Parameters
  • ax

  • to_show

  • limit

  • column

Returns

Return type

None.

plot_hloop_temp()[source]

Plots the Temperature of a Hloop measurement.

Returns

Return type

None.

plot_hloops(to_show=[54, 55], filename='hloop-compare-1', **kwargs)[source]

Compares Plusses and Crosses between two angles (4 Subplots). Default: 0 and 45 Writes file hloop-compare-1.pdf

Parameters
  • to_show (list, optional) – List of 4 measurement numbers, defaults to [54, 55]

  • filename (str, optional) – DESCRIPTION, defaults to ‘hloop-compare-1’

  • **kwargs

Returns

None

plot_hloops2(to_show=[58, 59, 61, 62], filename='hloop-compare-3', **kwargs)[source]

Outputs hloop-compare-3.pdf (not used anymore)

Parameters
  • to_show (TYPE, optional) – DESCRIPTION. The default is a[85]+a[100].

  • filename (TYPE, optional) – DESCRIPTION. The default is ‘hloop-compare-3’.

  • **kwargs (TYPE) – DESCRIPTION.

Returns

Return type

None.

plot_hloops3(to_show=[139, 140], filename='hloop-repeat', xlim=(- 350, 350), ylim=None, inset_xlim=(- 30, 30), inset_ylim=(0.15, 1.25))[source]

Compares two measurements at the same Angle (repeated measurements).

Parameters
  • to_show (TYPE, optional) – DESCRIPTION. The default is [139,140].

  • filename (TYPE, optional) – DESCRIPTION. The default is ‘hloop-repeat’.

  • xlim (TYPE, optional) – DESCRIPTION. The default is (-350, 350).

  • ylim

  • inset_xlim (TYPE, optional) – DESCRIPTION. The default is (-30, 30).

  • inset_ylim (TYPE, optional) – DESCRIPTION. The default is (.15, 1.25).

Returns

Return type

None.

plot_hloops4(filename='hloop-parallel', figtitle='M57: $90^\\circ$ Parallel measurement', **kwargs)[source]

Plots a single measurement (default: 90deg parallel)

Parameters
  • filename (TYPE, optional) – DESCRIPTION. The default is ‘hloop-parallel’.

  • figtitle (TYPE, optional) – DESCRIPTION. The default is “M57: $90^circ$ Parallel measurement”.

  • **kwargs (TYPE) – DESCRIPTION.

Returns

Return type

None.

plot_hloops_90(to_show=[57, 155], filename='hloop-compare-90', **kwargs)[source]

Compares +90deg with -90deg for (180deg Comparison)

Parameters
  • to_show (TYPE, optional) – DESCRIPTION. The default is [57,155].

  • filename (TYPE, optional) – DESCRIPTION. The default is ‘hloop-compare-90’.

  • **kwargs (TYPE) – mirror: bool, optional should we mirror the hloop? The default is False

Returns

Return type

None.

plot_hloops_90_nofit(to_show=[57, 155], filename='hloop-compare-90', **kwargs)[source]

Compares +90deg with -90deg for (180deg Comparison) Using not fitted data

Parameters
  • to_show (TYPE, optional) – DESCRIPTION. The default is [57,155].

  • filename (TYPE, optional) – DESCRIPTION. The default is ‘hloop-compare-90’.

  • **kwargs (TYPE) – mirror: bool, optional should we mirror the hloop? The default is False

Returns

Return type

None.

plot_hloops_95(to_show=[42, 43, 32, 34], filename='hloop-compare-95', **kwargs)[source]

Compares +95/100/105/110deg with -85/80/75/70deg for (180deg Comparison)

:param filename:Filename to save. The default is ‘hloop-compare-95’. :type filename: str, optional

Parameters
  • to_show (list, optional) – Measurement numbers to plot. The default is a[95]+a[-85].

  • filename

  • **kwargs

Returns

{filename}.pdf

plot_hloops_compare_90(to_show=[414, 415], structure='plusses', filename='hloop-repeat-414', xlim=(- 750, 750), ylim=(- 0.36, 0.76), inset_xlim=(- 225, 225), inset_ylim=(- 0.35, 0.75), insets=[((0.05, 0.05, 0.35, 0.35), ((- 225, 225), (- 0.35, 0.75), 'blue', 0.1))], legend_loc='upper right', nograd=False, nodiff=False, figsize=(16, 12))[source]

Compares multiple measurements at 90 deg (repeated measurements).

Parameters
  • to_show (TYPE, optional) – DESCRIPTION. The default is [414,415].

  • structure

  • filename (TYPE, optional) – DESCRIPTION. The default is ‘hloop-repeat-414’.

  • xlim (TYPE, optional) – DESCRIPTION. The default is (-750, 750).

  • ylim

  • inset_xlim (TYPE, optional) – DESCRIPTION. The default is (-100, 100).

  • inset_ylim (TYPE, optional) – DESCRIPTION. The default is (-1.25, .75).

  • legend_loc

  • nograd

Returns

Return type

None.

plot_single_hloop(nr=54, filename='hloop-single-1', **kwargs)[source]

Plots a single measurement. Default: 0deg

Parameters
  • nr

  • filename (str, optional) – File to write. The default is ‘hloop-single-1’.

  • ylim (xlim /) – limit the plots axes.

Returns

{filename}.pdf

plot_static_fields(filename='static-field', show_inset=False)[source]

Plot the Signal Analyzer (SA) Data for static fields (not sweeping)

Returns

Return type

None.

set_plot_style(m=None, **kwargs)[source]
set_size(width_pt, fraction=1, subplots=(1, 1))[source]

Set figure dimensions to sit nicely in our document.

Source: https://jwalton.info/Matplotlib-latex-PGF/

Parameters
  • width_pt (float) – Document width in points

  • fraction (float, optional) – Fraction of the width which you wish the figure to occupy

  • subplots (array-like, optional) – The number of rows and columns of subplots.

Returns

fig_dim – Dimensions of figure in inches

Return type

tuple

sv_temp_effect(filename='sv-temp-effect', show_inset=False)[source]

HandleM

class HandleM(*args, **kwargs)[source]

Bases: ana.multiple.MultipleM

Parameters
  • args

  • kwargs

Note

Measurement Handler: This class represents all other measurements and is the interface to other measurement objets.

get_custom_header(kind='RAW', instruments=None)[source]

Help Function to get column names for EVE Measurements.

Parameters
  • kind (str, optional) – Kind of measurement, default: ‘RAW’

  • instruments (list, optional) – Used instruments in order of appearance, default: [‘t’, ‘LS’, ‘IPS’, ‘SR830’]

Returns

(list header, int skiprows)

Return type

tuple

get_info()[source]

Returns information about all measurements and splits it into groups (SA/RAV/MFN/VH).

load_files(directory, **kwargs)[source]

Loads all files from a specific directory and creates objects for found measurements.

Parameters
  • directory (str) – directory to search in

  • **kwargs

Returns

None

load_folder(file_list, **kwargs)[source]
Parameters
  • file_list

  • **kwargs

parse_data(data, **kwargs)[source]

imports data and sets info variable.

h.parse_data({'data': pd.DataFrame,
    'info': dict(Nr=0, ** kw) })
Parameters
  • data (dict) – Data to parse

  • **kwargs

Returns

None

plot(*args, **kwargs)[source]

Plotting a Measurement

Parameters
  • lofm – list of measurements

  • figsize – tuple size of figure

  • show_fit – bool

  • fit_range – tuple/list

  • plot_settings – list

  • grid_options – list

  • f_settings – 1/f line

  • f2_settings – 1/f^2 line

Returns

Figure, Axis

Return type

tuple

Raises
  • warning – lofm not available.

  • KeyError – Measurement does not exist.

MFN

class MFN(files, **kwargs)[source]

Bases: ana.multiple.MultipleM

Magnetic Flux Noise Measurements. Class to handle and plot MFN Measurements taken with the SR830 DAQ.

if a measurement number is given the files are loaded from folder:

data/MFN/m%s/*.dat

else: files must be a list of filenames:

Listing 4 Example
1arg = glob('data/MFN/mXXX/m[0-9.]*.dat')
2mfn = MFN(arg)
Parameters
  • files – list of files or measurement number.

  • kwargs – Different additional parameters.

  • nr (int) – Measurement number (default: None).

  • calc_first (bool) – Calculating first spectrum (default: True).

  • calc_second (bool) – Calculating second spectrum (default: False).

  • downsample (bool) – downsample timesignal for first spectrum (default: False).

  • num_first_spectra (int) – Number of spectra to average (default: 64).

  • timeconstant (float) – Lock-In timeconstant to cut first spectrum at corresponding frequency (see cut_fmax).

  • cut_fmax (float) – Cut first spectrum at this frequency (default: \(f_{max} = \frac{\tau}{2\pi}\)).

  • default_cm (matplotlib.cm) – matplotlib color map for contour plots (default: style[‘default_cm’]).

cut_fmax(fmax)[source]

Cut frequencies higher than maximum allowed frequencies.

Parameters

fmax

load_meas(files, **kwargs)[source]

Loading a single magnetic flux noise DAQ measurement.

Files in measrument folder contain multiple measurements with one measurement per field.

Parameters

files – files to load

Returns

DataFrame Data, dict Measurement Objects

Return type

tuple

load_voltages(nr=508, figsize=(16, 12))[source]
plot_alpha(field, s, ax=None, **kwargs)[source]

Fits a spectrum and plots the slope alpha. :param s: pandas.DataFrame of the spectrum :param ax: axis where to plot the data. :return: linear regression fit

plot_compare_timesignals(fields, **kwargs)[source]

Plots a grid of 2x2 subplots to compare timesignals and histogram. :param fields: list :param factor: float Default: 1e3 (mV) :param nrows: int Default: len(fields) :param ncols: int Default: 2 :param sharey: bool Default: True :param figsize: tuple Default: (11.69, 8.27) in :param plot_range: int Default: -1

plot_field_contour(ax31, show_field=0, **kwargs)[source]
plot_first_and_second(**kwargs)[source]
Parameters

**kwargs

plot_info(show_field=0, **kwargs)[source]

Plots basic mfn infos

Parameters
  • show_field

  • kwargs

plot_info2(**kwargs)[source]
Parameters

**kwargs

plot_multiple_histograms(steps=4, fields=Series([], dtype: float64), factor=1000.0, xlim=(-1.3, 1.3), **kwargs)[source]

Plots multiple histograms using seaborn sns.FacetGrid with kdeplot.

default kwargs for subplots:

kde1_kwargs = dict(clip_on=True, shade=True, alpha=1, lw=1.5, bw=.2) kde2_kwargs = kde1_kwargs.update(dict(lw=2, color=’w’) dist_kwargs = dict(hist=True, norm_hist=True, hist_kws={‘alpha’: .5}) ax_kwargs = dict(y=0, lw=2, clip_on=True).set(xlim=(-1.3,1.3))

plot_noise_chararcteristics(ax=None, fields=None, **kwargs)[source]
Parameters
  • ax

  • fields

  • **kwargs

plot_various_noise_fits(**kwargs)[source]
Parameters

**kwargs

plot_various_noise_repr()[source]
plot_various_timesignals(fields, **kwargs)[source]

Plots a grid of 9 subplots with different timesignals :param fields: list :param nrows: int Default: len(fields) :param ncols: int Default: 2 :param sharey: bool Default: True :param figsize: tuple Default: (11.69, 8.27) in :param plot_range: int Default: -1

read(file)[source]

Write data to a file.

Parameters

file (str) –

spectra_field_contour(ax=None, **kwargs)[source]
Parameters
  • ax

  • **kwargs

subtract_mean(inplace=False, **kwargs)[source]

Subtracts mean value from timesignal.

Parameters
  • inplace (bool) – Change variable inside this object?

  • fields – list of fields to fit

Returns

normalized timesignal

write(file)[source]

Write data to a file.

Parameters

file (str) –

Low Level Python Backend

MeasurementClass

class MeasurementClass[source]

Bases: object

The main measurement class for all measurements.

Note

MeasurementClass: This Class is the parent of all other measurement classes.

All measurements have the following variables:

name: str/None

The name of the Measurement Class (e.g. SA, RAW, etc.)

info: dict
Contains informations about the measurement. Typical keys are:

Nr: float, the number of the measurement,

calc_log(df, keys=['f', 'Vx', 'Vy'])[source]
get_info_from_name(name, **kwargs)[source]

Extracting information from filenames. Using a default regex first or trying to extract ‘key-value’ pairs separated by ‘_’.

Parameters
  • name – filename that contains informations.

  • kwargs

    str regex

    Regular expression for the filename

Returns

dict(key=value)

i(*key)[source]

Get a specific measurement number information.

Parameters

*key

Will be passed to info.get

Returns

info.get(*key)

SingleM

class SingleM[source]

Bases: ana.measurement.MeasurementClass

The main constructor for all single measurements.

calc_log(df, keys=['f', 'Vx', 'Vy'])
fit_variable(df, x, y)[source]

Applys a linear regression and adds result to DataFrame

Parameters
  • df (pd.DataFrame) – Fit data

  • x (np.ndarray) – Fit variable.

  • y (np.ndarray) – Fit variable.

get_info_from_name(name, **kwargs)

Extracting information from filenames. Using a default regex first or trying to extract ‘key-value’ pairs separated by ‘_’.

Parameters
  • name – filename that contains informations.

  • kwargs

    str regex

    Regular expression for the filename

Returns

dict(key=value)

i(*key)

Get a specific measurement number information.

Parameters

*key

Will be passed to info.get

Returns

info.get(*key)

linear_regression(x, y)

RAW

class RAW(data, **kwargs)[source]

Bases: ana.single.SingleM

RAW Measurement:

Measurements with time signal only.

Listing 5 Example
1data = np.array(timesignal, shape=(n,))
2data = {
3     'data': np.array(timesignal, shape=(n,))
4     'info': {'Nr': 123, 'Add Info': 'Important Informations'}
5     }
6raw = ana.RAW(data)
Parameters
  • data – Different possibilities.

  • rate (float, default: 2e-4) – At which rate was the timesignal measured (samplingrate) [Hz]

  • nof_first_spectra (int, default: 64) – Cut timesignal into # pieces and average.

  • first_spectra_highest_frequency (float, default: rate/8) – Cut higher frequencies in final spectrum than this.

  • downsample – Downsample the timesignal before calculation.

  • calc_first (bool, default: False) – Triggers calculation of first spectrum.

  • highest_octave (int, default: 64) – The highest octave starting point [Hz] of the second spectrum.

  • minimum_points_in_octave (int, default: 10) – If octave would contain less points, stop.

  • calc_second (bool, default: False) – Triggers calculation of second spectrum.

Info

First Spectrum parameters

Info

Second Spectrum parameters

calc_first_spectrum()[source]

Sets the variable spectrum to a spectrumanalyzer.SpectrumAnalyzer object.

Important

Calculates the first spectrum.

Raises

NameError – spectrumanalyzer is not installed

Returns

None

calc_log(df, keys=['f', 'Vx', 'Vy'])
calc_second_spectrum(**kwargs)[source]

Calculating the second spectrum.

calc_time_signal_second_spectrum()[source]
check_timesignal(timesignal)[source]

converts pd.DataFrame[‘Vx’] and Series into numpy array.

Parameters

timesignal – input timesignal

Returns

converted timesignal.

Return type

numpy.ndarray

fit_variable(df, x, y)

Applys a linear regression and adds result to DataFrame

Parameters
  • df (pd.DataFrame) – Fit data

  • x (np.ndarray) – Fit variable.

  • y (np.ndarray) – Fit variable.

get_info_from_name(name, **kwargs)

Extracting information from filenames. Using a default regex first or trying to extract ‘key-value’ pairs separated by ‘_’.

Parameters
  • name – filename that contains informations.

  • kwargs

    str regex

    Regular expression for the filename

Returns

dict(key=value)

i(*key)

Get a specific measurement number information.

Parameters

*key

Will be passed to info.get

Returns

info.get(*key)

linear_regression(x, y)
plot_spectrum(ax)[source]
plot_time(ax)[source]
read(file)[source]

Reading data from file.

Parameters
  • file (str) –

  • from. (Filename base to read data) –

Returns

Return type

self

subtract_mean(inplace=False)[source]

Subtracts mean value from timesignal.

Parameters

inplace (bool) – Change variable inside this object?

Returns

normalized timesignal

write(file)[source]

Writing data to a file.

Parameters

file (str) –

SA

class SA(*args, **kwargs)[source]

Bases: ana.single.SingleM

Loads and analyzes datafiles from :instrument:`SR785`

Parameters
  • *args

  • **kwargs

Returns

None

Raises

* ValueError

calc_log(df, keys=['f', 'Vx', 'Vy'])
fit(fit_range=(0.01, 0.7))[source]

Fits a linear regression

Parameters
  • (tuple (fit_range) – (1e-2, 1e0).):

  • optional – (1e-2, 1e0).):

  • default – (1e-2, 1e0).):

fit_variable(df, x, y)

Applys a linear regression and adds result to DataFrame

Parameters
  • df (pd.DataFrame) – Fit data

  • x (np.ndarray) – Fit variable.

  • y (np.ndarray) – Fit variable.

get_info_from_name(name, **kwargs)

Extracting information from filenames. Using a default regex first or trying to extract ‘key-value’ pairs separated by ‘_’.

Parameters
  • name – filename that contains informations.

  • kwargs

    str regex

    Regular expression for the filename

Returns

dict(key=value)

i(*key)

Get a specific measurement number information.

Parameters

*key

Will be passed to info.get

Returns

info.get(*key)

linear_regression(x, y)
plot(**kwargs)[source]

Plotting the Data on given Axis or creating a new Plot for plotting.

Listing 6 Signature
1plot(ax, label=None, color=None, linestyle=None,
2    plot_x='f', plot_y='Vx', dont_set_plot_settings=False,
3
4        xscale='log', yscale='log', xlim=(None, None), ylim=(None,
5        None), legend_location='best', grid=None, title=None,
6        xlabel='f [Hz]', ylabel='S_VH [V2/Hz]',
7
8)
Parameters

**kwargs

Note

If dont_set_plot_settings is True, all kwargs below are not used.

Hloop

class Hloop(measurement_number, **kwargs)[source]

Bases: ana.single.SingleM

This class represents the measurement of a hysteresis loop. It evaluates and plots the data.

It collects all files that match the following regex:

files = glob("data/Hloop/[mM]%s_*.dat" % measurement_number)

The files should contain a file that contains the word “up” or “down” indicating the direction of the field sweep.

If one file contains the word ‘Gradio’ it expects a gradiometry measurement. If the filename contains the word ‘Parallel’ it expects a parallel measurement. If the filename contains neither of these words the variables need to be set manually.

Parameters
  • measurement_number

  • **kwargs

calc_B(V)[source]
Parameters

V

calc_log(df, keys=['f', 'Vx', 'Vy'])
calculate_fitted_strayfield()[source]

Calculates the strayfield of the fitted signal and stores it in up and down sweeps.

calculate_strayfield()[source]

Calculates the strayfield of the signal and stores it in up and down sweeps.

fit(cond=Series([], dtype: float64))[source]

Fitting the Voltage Signal.

For Parallel measurement:

the empty cross Vx13 is subtracted from the signal Vx8/Vx9

For gradiometry:

A condition can be added to determine the amount of datapoints to fit (default: B < B_min + 150mT).

Listing 7 Example
 cond = (self.up.B < self.up.B.min() + 150)
 m.fit(cond)
Parameters

cond

fit_variable(df, x, y)

Applys a linear regression and adds result to DataFrame

Parameters
  • df (pd.DataFrame) – Fit data

  • x (np.ndarray) – Fit variable.

  • y (np.ndarray) – Fit variable.

get_bhminmax()[source]

Returns the minimum and maximum of the measured hall voltage converted to strayfield.

Listing 8 Example
bhmin, bhmax = meas.get_bhminmax()
get_coercive_field()[source]

Returns the mean and coercive fields calculated.

Listing 9 Example
mean, coer1, coer2 = meas.get_coercive_field()
print('Coercive Field (up sweep): (%.3f, %.3f)' % (coer1, mean))
print('Coercive Field (down sweep): (%.3f, %.3f)' % (coer2, mean))
get_default_title()[source]
get_downminusup(n=100000.0)[source]

Returns the magnetic field and difference between down and up sweep.

Listing 10 Example
 B, Vx = meas.get_downminusup() plt.plot(B, Vx)
Parameters

n

get_downminusup_strayfield(n=100000.0, fitted_data=False)[source]

Returns the magnetic field and difference between down and up sweep.

Listing 11 Example
B_ext, Bx = meas.get_downminusup_strayfield() plt.plot(B_ext, Bx)
Parameters
  • n

  • fitted_data

get_info_from_name(name, **kwargs)

Extracting information from filenames. Using a default regex first or trying to extract ‘key-value’ pairs separated by ‘_’.

Parameters
  • name – filename that contains informations.

  • kwargs

    str regex

    Regular expression for the filename

Returns

dict(key=value)

get_minmax()[source]

Returns the minimum and maximum of the field and voltage.

Listing 12 Example
bmin, bmax, vmin, vmax = meas.get_minmax()
get_remanence()[source]

Returns the remanent voltage calculated.

Listing 13 Example
rem1, rem2 = meas.get_remanence()
print('Remanent Voltage (up sweep): (%d, %.3f)' % (0, rem1))
print('Remanent Voltage (down sweep): (%d, %.3f)' % (0, rem2))
i(*key)

Get a specific measurement number information.

Parameters

*key

Will be passed to info.get

Returns

info.get(*key)

linear_regression(x, y)
plot_downminusup(ax, figtitle='')[source]

Plotting the difference between the down and up sweep. ax: Matplotlib Axis to plot on. figtitle: can be set to a manual figure title.

Parameters
  • ax

  • figtitle

plot_hloop(ax, figtitle='', show_fitted=True, show_rem=False, show_coer=False, **kwargs)[source]

Plotting the hysteresis loop.

Parameters
  • ax – Matplotlib Axis to plot on.

  • figtitle – can be set to a manual figure title.

  • show_fitted – plot the fitted curve.

  • show_rem – show the remanent voltage.

  • show_coer – show the coercive field.

  • show_original – plots the RAW data.

  • show_linear_fit – plots the linear fitting line used for the fit

(only gradiometry).

plot_hysteresis(y='B', **kwargs)[source]
plot_strayfield(ax, figtitle='', **kwargs)[source]

Plots the strayfield of the data. Strayfield is calculated using the electron concentration at 0 degree measured before (hardcoded in __init__).

Parameters
  • ax (matplotlib.pyplot.axis) – where should we plot.

  • figtitle (str, optional) – Choose your own title above the figure.

  • **kwargs

Returns

Return type

None.

remove_zero(columns=None)[source]

Removes the values 0.0 from up and down sweep where the lock-in didn’t return any values (read error will be saved as 0.0).

Parameters

columns

set_factor(factor)[source]

Multiplying the Voltage by a Factor

Parameters

factor

MultipleM

class MultipleM[source]

Bases: ana.measurement.MeasurementClass

MultipleM:

Main parent class for all multiple measurements. These Measurements are represent by one data variable.

All multiple measurements have a variable called multi_info which contains a pandas.DataFrame with parameter settings to single measurements.

All multiple measurements can be represented by

>>> m = ana.MultipleM()
>>> m
Nr. 0: Hloop
Nr. 0: RAW
calc_log(df, keys=['f', 'Vx', 'Vy'])
get_info_from_name(name, **kwargs)

Extracting information from filenames. Using a default regex first or trying to extract ‘key-value’ pairs separated by ‘_’.

Parameters
  • name – filename that contains informations.

  • kwargs

    str regex

    Regular expression for the filename

Returns

dict(key=value)

get_lofm(to_show, label, options={})[source]
Returns the List of Measurements (lofm) for plot functions

(see ana.HandleM.plot()).

Parameters
  • to_show (dict) – Measurement Numbers pointing to label format variables: e.g. {nr: [(-1, 1), 'Plusses', '5 mT/min']}

  • label (str) – Label to show in plots: e.g. "Show %s -> %s (%s) %s " -> will be string formated using to_show

  • (dict (options) – empty): Additional plotting parameters

  • optional – empty): Additional plotting parameters

  • default – empty): Additional plotting parameters

Returns

List of Measurements with description: {nr: [labels]}

Return type

dict

get_lofm_sweeps(to_show, label, options={})[source]

Workaround to get list of measurements for sweeps.

Parameters
  • to_show (dict) – Measurement Numbers pointing to label format variables: e.g. `` {nr: [-1, 1, ‘Plusses’, ‘2 mT/min’]}

  • label (str) – Label to show in plots: e.g. “Show %s -> %s (%s) %s ” -> will be formated using to_show

  • (dict (options) – empty): Additional plotting options

  • default – empty): Additional plotting options

Returns

List of Measurements with description

Return type

dict

i(*key)

Get a specific measurement number information.

Parameters

*key

Will be passed to info.get

Returns

info.get(*key)

query(request)
read_csv(*args, **kwargs)[source]

Updates multi_info DataFrame. Reads csv and adds information to self.multi_info

Parameters
  • *args

  • **kwargs

Returns

None

PlottingClass

class PlottingClass(**style)[source]

Bases: object

Defines the basic plotting style.

Parameters

**style

draw_oneoverf(ax, **kwargs)[source]
Parameters
  • ax

  • **kwargs

get(*key)
get_style(custom=None)[source]
Parameters

custom

save_plot(fname, fext='png')[source]

Saves the current plot as file.

Parameters
  • fname (str) – Filename

  • fext (str) – Extension

Returns

None

set_plot_settings(**kwargs)[source]
Parameters

**kwargs

set_style(**style)[source]

Sets the Style for plots

Parameters
  • size (dict, None, or one of {paper, notebook, talk, poster}) – A dictionary of parameters or the name of a preconfigured set.

  • style (dict, None, or one of {darkgrid, whitegrid, dark, white, ticks}) –

  • default (bool) – if True it sets default size=talk and style=whitegrid

  • notebook (bool) – if True it sets default size=notebook and style=ticks

Returns

Return type

None.

update(other)[source]
Parameters

other

Functions

Helps to handle measurements and style plots.

getpath(*arg)[source]

Alias for glob.

Parameters

*arg

timing(f)[source]

Wraps a function to measure the time it takes.

Parameters

f

Source Code

Plot

   1class Plot(object):
   2    def __init__(self):
   3        """A small class used to plot figures for a presentation."""
   4
   5        super().__init__()
   6
   7        self.meas = {}
   8        self.m = Hloop(57)
   9        self.eva = HandleM(directory='data/SR785')
  10
  11    def get_plot(self, **kwargs):
  12        fig, ax = plt.subplots(nrows=kwargs.get('nrows', 1),
  13                               ncols=kwargs.get('ncols', 1),
  14                               figsize=kwargs.get('figsize', (16, 12)),
  15                               **kwargs.get('plot_args',dict())
  16                               )
  17        return fig, ax
  18
  19    def set_plot_style(self, m=None, **kwargs):
  20        if m is None:
  21            m = self.m
  22
  23        m.style.set_style(size=kwargs.get('size', 'talk'),
  24                          style=kwargs.get('style', 'ticks'),
  25                          palette=kwargs.get('palette', 'deep'),
  26                          grid=kwargs.get('grid', True),
  27                          latex=kwargs.get('latex', True),
  28                          figsize=kwargs.get('figsize', (16, 12))
  29                          )
  30
  31    def plot_single_hloop(self, nr=54, filename='hloop-single-1',
  32                          **kwargs):
  33        """Plots a single measurement. Default: 0deg
  34
  35        Args:
  36            nr:
  37            filename (str, optional): File to write. The default is
  38                'hloop-single-1'.
  39            xlim / ylim: limit the plots axes.
  40
  41        Returns:
  42            {filename}.pdf
  43        """
  44        fig, ax = self.get_plot(**kwargs)
  45
  46        if nr not in self.meas:
  47            self.meas[nr] = Hloop(nr)
  48
  49        self.set_plot_style(self.meas[nr])
  50        self.meas[nr].plot_strayfield(ax)
  51        ax.set_xlim(*kwargs.get('xlim', (-250, 250)))
  52        if kwargs.get('ylim'):
  53            ax.set_ylim(*kwargs.get('ylim'))
  54
  55        with sns.color_palette('deep'):
  56            self.set_plot_style(self.m)
  57            inset = inset_axes(ax, width='100%', height='90%',
  58                               bbox_to_anchor=(.64, .06, .35, .35),
  59                               bbox_transform=ax.transAxes)
  60            max_b = self.meas[nr].up.B.max()
  61            inset.plot([-max_b, max_b], [0, 0], 'r--', linewidth=.75)
  62            B_ext, B_stray = self.meas[nr].get_downminusup_strayfield()
  63            inset.plot(B_ext, B_stray)
  64            inset.set_ylabel("$\\Delta B_z\\;[\\mathrm{mT}]$")
  65            inset.set_title("Difference")
  66            inset.set_xlim(*kwargs.get('xlim', (-250, 250)))
  67
  68        plt.savefig("%s.pdf" % filename)
  69
  70    def plot_hloops(self, to_show=[54, 55], filename='hloop-compare-1',
  71                    **kwargs):
  72        """Compares Plusses and Crosses between two angles (4 Subplots).
  73        Default: 0 and 45 Writes file hloop-compare-1.pdf
  74
  75        Args:
  76            to_show (list, optional): List of 4 measurement numbers, 
  77                defaults to [54, 55]
  78            filename (str, optional): DESCRIPTION, defaults to 'hloop-compare-1'
  79            **kwargs:
  80
  81        Returns:
  82            None
  83        """
  84        self.m.style.set_style(default=True, grid=True,
  85                               size='talk', style='ticks',
  86                               palette='deep', latex=True)
  87        fig, axes = self.get_plot(nrows=2, ncols=2,
  88                                  plot_args=kwargs.get('plot_args',dict()))
  89
  90        for i, nr in enumerate(to_show):
  91            ax = axes[i // 2][i % 2]
  92            if nr not in self.meas:
  93                self.meas[nr] = Hloop(nr)
  94
  95            self.set_plot_style(self.meas[nr])
  96            self.meas[nr].plot_strayfield(ax)
  97            if nr in [54]:
  98                ax.set_ylim(0, 4.5)
  99            elif nr in [55]:
 100                ax.set_xlim(-300, 300)
 101                ax.set_ylim(-0.8, 4.45)
 102            if nr in [22, 23]:
 103                ax.set_xlim(-150, 150)
 104                ax.set_ylim(-0.25, 3.5)
 105            elif kwargs.get('xlim'):
 106                ax.set_xlim(*kwargs.get('xlim'))
 107            else:
 108                ax.set_xlim(-250, 250)
 109            ax.set_title('')
 110
 111            with sns.color_palette('deep'):
 112                self.set_plot_style(self.m)
 113                inset = inset_axes(ax, width='100%', height='90%',
 114                                   bbox_to_anchor=(.65, .09, .35, .35),
 115                                   bbox_transform=ax.transAxes)
 116                max_b = self.meas[nr].up.B.max()
 117                inset.plot([-max_b, max_b], [0, 0], '--', color='orange', linewidth=.75)
 118                B_ext, B_stray = self.meas[nr].get_downminusup_strayfield()
 119                inset.plot(B_ext, B_stray, color='black')
 120                inset.set_ylabel("$\\Delta B_z\\;[\\mathrm{mT}]$")
 121                inset.set_title("Difference")
 122                inset.set_xlim(-250, 250)
 123                if nr in [55]:
 124                    inset.set_xlim(-300, 300)
 125                if nr in [22, 23]:
 126                    inset.set_xlim(-150, 150)
 127                if kwargs.get('xlim'):
 128                    inset.set_xlim(*kwargs.get('xlim'))
 129                inset.set_yticks(kwargs.get('diff_yticks', [0, 0.5, 1]))
 130
 131        title_style = '\\bfseries \\Huge '
 132        for ax, struct in zip(axes[0],
 133                              ['Plusses', 'Crosses']):
 134            ax.set_title(title_style + struct)
 135            ax.set_xlabel('')
 136
 137        for ax in axes[:, 1]:
 138            ax.set_ylabel('')
 139            ax.set_yticklabels([])
 140
 141        for ax, a in zip(axes[:, 0],
 142         [r'{%s $\mathbf{\theta = +45^{\circ}}$}' % title_style,
 143          r'{%s $\mathbf{\theta = -45^{\circ}}$}' % title_style]):
 144            ax.set_ylabel("%s\n$\\langle B_z \\rangle [\\mathrm{mT}]$" % a)
 145
 146        axes[0, 0].set_ylim(0,4.8)
 147        axes[0, 1].set_ylim(0,4.8)
 148        axes[1, 0].set_ylim(-.1,3.45)
 149        axes[1, 1].set_ylim(-.1,3.45)
 150
 151        plt.savefig("%s.pdf" % filename)
 152
 153    def plot_hloops_95(self,
 154                       to_show=[42, 43, 32, 34],
 155                       filename='hloop-compare-95',
 156                       **kwargs):
 157        """Compares +95/100/105/110deg with -85/80/75/70deg for (180deg
 158        Comparison)
 159
 160        :param filename:Filename to save. The default is 'hloop-compare-95'.
 161        :type filename: str, optional
 162
 163        Args:
 164            to_show (list, optional): Measurement numbers to plot. The default
 165                is a[95]+a[-85].
 166            filename:
 167            **kwargs:
 168
 169        Returns:
 170            {filename}.pdf
 171        """
 172        fig, axes = plt.subplots(2, 2, figsize=(16, 12))
 173        for i, nr in enumerate(to_show):
 174            ax = axes[i // 2][i % 2]
 175            if nr not in self.meas:
 176                self.meas[nr] = Hloop(nr)
 177            self.set_plot_style(self.meas[nr])
 178            self.meas[nr].plot_strayfield(ax)
 179            if kwargs.get('xlim'):
 180                ax.set_xlim(*kwargs.get('xlim'))
 181            else:
 182                ax.set_xlim(-750, 750)
 183            if self.meas[nr].info['Structure'] == 'Crosses' and kwargs.get(
 184                    'crosses_xlim'):
 185                ax.set_xlim(*kwargs.get('crosses_xlim'))
 186
 187            with sns.color_palette('deep'):
 188                self.m.style.set_style(size='talk', style='ticks',
 189                                       grid=True, latex=True)
 190                inset = inset_axes(ax, width='100%', height='90%',
 191                                   bbox_to_anchor=(.65, .09, .35, .35),
 192                                   bbox_transform=ax.transAxes)
 193                max_b = self.meas[nr].up.B.max()
 194                inset.plot([-max_b, max_b], [0, 0], 'r--', linewidth=.75)
 195                B_ext, B_stray = self.meas[nr].get_downminusup_strayfield()
 196                inset.plot(B_ext, B_stray)
 197                # inset.set_ylabel("$\Delta B_z\\;[\\mathrm{mT}]$")
 198                inset.set_title("Difference")
 199                if kwargs.get('xlim'):
 200                    inset.set_xlim(*kwargs.get('xlim'))
 201                else:
 202                    inset.set_xlim(-750, 750)
 203                if self.meas[nr].info['Structure'] == 'Crosses' and kwargs.get(
 204                        'crosses_xlim'):
 205                    inset.set_xlim(*kwargs.get('crosses_xlim'))
 206
 207        plt.savefig("%s.pdf" % filename)
 208
 209    def plot_hloops_90(self,
 210                       to_show=[57, 155],
 211                       filename='hloop-compare-90',
 212                       **kwargs):
 213        """Compares +90deg with -90deg for (180deg Comparison)
 214
 215        Args:
 216            to_show (TYPE, optional): DESCRIPTION. The default is [57,155].
 217            filename (TYPE, optional): DESCRIPTION. The default is
 218                'hloop-compare-90'.
 219            **kwargs (TYPE): mirror: bool, optional
 220                    should we mirror the hloop? The default is False
 221
 222        Returns:
 223            None.:
 224        """
 225        self.m.style.set_style(default=True, grid=True,
 226                               size='talk', style='ticks',
 227                               palette='deep', latex=True)
 228        sns.set_palette(sns.color_palette("deep"))
 229
 230        fig, axes = plt.subplots(2, 2, figsize=(16, 12),
 231                                 **kwargs.get('plot_args', dict()))
 232        for i, nr in enumerate(to_show):
 233            cur_ax = axes[i]
 234            self.m.style.set_style(size='talk', style='ticks',
 235                                   palette='Paired',
 236                                   grid=True, latex=True)
 237
 238            self.meas[nr] = Hloop(nr)
 239            self.set_plot_style(self.meas[nr])
 240
 241            if not (kwargs.get('mirror')) and nr == 57:
 242                self.meas[nr].set_factor(-1)
 243                self.meas[nr].factor = 1
 244
 245            # Plusses
 246            ax = cur_ax[0]
 247            self.meas[nr].plot_strayfield(ax, nolegend=True,
 248                                          show_plusses=False,
 249                                          show_crosses=False)
 250
 251            ax.plot(self.meas[nr].up_fitted.B, self.meas[nr].up_fitted.Bx8,
 252                    label='Up (fitted)')
 253            ax.plot(self.meas[nr].down_fitted.B, self.meas[nr].down_fitted.Bx8,
 254                    label='Down (fitted)')
 255
 256            # Set Limits (x and y Axis)
 257            if kwargs.get('xlim'):
 258                ax.set_xlim(*kwargs.get('xlim'))
 259            else:
 260                ax.set_xlim(-750, 750)
 261
 262            ax.set_ylim(np.min([self.meas[nr].up_fitted.Bx8.min(),
 263                                self.meas[nr].down_fitted.Bx8.min()]) - .05,
 264                        np.max([self.meas[nr].up_fitted.Bx8.max(),
 265                                self.meas[nr].down_fitted.Bx8.max()]) + .05)
 266
 267            ax.set_title('')
 268            """if nr == 57:
 269                ax.set_title('m57: Plusses ($90^\circ$)')
 270            else:
 271                ax.set_title('m155: Plusses ($-90^\circ$)')"""
 272
 273            # Inset with Difference
 274            with sns.color_palette('bright'):
 275                if nr == 57 and not (kwargs.get('mirror')):
 276                    bbox = (.11, .59, .34, .35)
 277                else:
 278                    bbox = (.12, .12, .34, .35)
 279                inset = inset_axes(ax, width='100%', height='100%',
 280                                   bbox_to_anchor=bbox,
 281                                   bbox_transform=ax.transAxes)
 282                max_b = self.meas[nr].up.B.max()
 283                B_ext, B_stray = self.meas[nr].get_downminusup_strayfield(
 284                    fitted_data=True)
 285                inset.plot([-max_b, max_b], [0, 0], '--', color='orange',
 286                           linewidth=.75)
 287                inset.plot(B_ext, B_stray, color='black')
 288                inset.set_xlim(-750, 750)
 289                inset.set_title("Difference (fitted)")
 290                inset.set_yticks(kwargs.get('diff_yticks', [0, 0.5]))
 291
 292            ax.legend(loc='upper right')  # , ncol=2)
 293
 294            # Crosses
 295            ax = cur_ax[1]
 296            self.meas[nr].plot_strayfield(ax, nolegend=True,
 297                                          show_plusses=False,
 298                                          show_crosses=False)
 299            ax.plot(self.meas[nr].up_fitted.B,
 300                    self.meas[nr].up_fitted.Bx9, label='Up (fitted)')
 301            ax.plot(self.meas[nr].down_fitted.B,
 302                    self.meas[nr].down_fitted.Bx9, label='Down (fitted)')
 303
 304            # Set Limits (x and y Axis)
 305            if kwargs.get('xlim'):
 306                ax.set_xlim(*kwargs.get('xlim'))
 307            else:
 308                ax.set_xlim(-750, 750)
 309
 310            ax.set_ylim(np.min([self.meas[nr].up_fitted.Bx9.min(),
 311                                self.meas[nr].down_fitted.Bx9.min()]) - .05,
 312                        np.max([self.meas[nr].up_fitted.Bx9.max(),
 313                                self.meas[nr].down_fitted.Bx9.max()]) + .05)
 314
 315            ax.set_title('')
 316            """if nr == 57:
 317                ax.set_title('M57: Crosses ($90^\circ$)')
 318            else:
 319                ax.set_title('M155: Crosses ($-90^\circ$)')"""
 320
 321            # Inset with Difference
 322            with sns.color_palette('bright'):
 323                if nr == 57 and kwargs.get('mirror'):
 324                    bbox = (.12, .12, .34, .35)
 325                else:
 326                    bbox = (.12, .59, .34, .35)
 327                inset2 = inset_axes(ax, width='100%', height='100%',
 328                                    bbox_to_anchor=bbox,
 329                                    bbox_transform=ax.transAxes)
 330                f_up = scipy.interpolate.interp1d(self.meas[nr].up_fitted.B,
 331                                                  self.meas[nr].up_fitted.Bx9)
 332                f_down = scipy.interpolate.interp1d(
 333                    self.meas[nr].down_fitted.B,
 334                    self.meas[nr].down_fitted.Bx9)
 335                B = np.linspace(self.meas[nr].up.B.min(),
 336                                self.meas[nr].up.B.max(), int(1e5))
 337                downminusup = f_down(B) - f_up(B)
 338                inset2.plot([-max_b, max_b], [0, 0], '--',
 339                            color='orange', linewidth=.75)
 340                inset2.plot(B, downminusup, color='black')
 341                inset2.set_xlim(-750, 750)
 342                inset2.set_title("Difference (fitted)")
 343                if nr == 57:
 344                    inset.set_yticks([0, .25, 0.5])
 345                    inset2.set_yticks([0, .25, 0.5])
 346
 347            ax.legend(loc='upper right')  # , ncol=2)
 348
 349        title_style = '\\bfseries \\Huge '
 350        for ax, struct in zip(axes[0],
 351                                  ['Plusses',
 352                                   'Crosses']):
 353            ax.set_title(title_style + struct)
 354            ax.set_xlabel('')
 355
 356        for ax in axes[:, 1]:
 357            ax.set_ylabel('')
 358
 359        for ax, a in zip(axes[:, 0],
 360                         [r'{%s $\mathbf{\theta = +90^{\circ}}$}' % title_style,
 361                          r'{%s $\mathbf{\theta = -90^{\circ}}$}' % title_style]):
 362            ax.set_ylabel("%s\n$\\langle B_z \\rangle [\\mathrm{mT}]$" % a)
 363
 364        plt.savefig("%s.pdf" % filename)
 365
 366    def plot_hloops_90_nofit(self, to_show=[57, 155],
 367                             filename='hloop-compare-90', **kwargs):
 368        """Compares +90deg with -90deg for (180deg Comparison) Using not fitted
 369        data
 370
 371        Args:
 372            to_show (TYPE, optional): DESCRIPTION. The default is [57,155].
 373            filename (TYPE, optional): DESCRIPTION. The default is
 374                'hloop-compare-90'.
 375            **kwargs (TYPE): mirror: bool, optional
 376                    should we mirror the hloop? The default is False
 377
 378        Returns:
 379            None.:
 380        """
 381        self.m.style.set_style(default=True, grid=True,
 382                               size='talk', style='ticks',
 383                               palette='deep', latex=True)
 384        sns.set_palette(sns.color_palette("deep"))
 385
 386        fig, axes = plt.subplots(2, 2, figsize=(16, 12))
 387        for i, nr in enumerate(to_show):
 388            cur_ax = axes[i]
 389            self.m.style.set_style(size='talk', style='ticks',
 390                                   palette='Paired',
 391                                   grid=True, latex=True)
 392
 393            self.meas[nr] = Hloop(nr)
 394
 395            if not (kwargs.get('mirror')) and nr == 57:
 396                self.meas[nr].set_factor(-1)
 397                self.meas[nr].factor = 1
 398
 399            # Plusses
 400            ax = cur_ax[0]
 401            self.meas[nr].plot_strayfield(ax, nolegend=True,
 402                                          show_plusses=False,
 403                                          show_crosses=False)
 404
 405            ax.plot(self.meas[nr].up.B, self.meas[nr].up.Bx8, label='Up')
 406            ax.plot(self.meas[nr].down.B, self.meas[nr].down.Bx8, label='Down')
 407            ax.plot(self.meas[nr].up.B, self.meas[nr].up.Bx13,
 408                    label='Up (Empty)')
 409            ax.plot(self.meas[nr].down.B, self.meas[nr].down.Bx13,
 410                    label='Down (Empty)')
 411
 412            # Set Limits (x and y Axis)
 413            if kwargs.get('xlim'):
 414                ax.set_xlim(*kwargs.get('xlim'))
 415            else:
 416                ax.set_xlim(-1000, 1000)
 417
 418            ax.set_ylim(np.min([self.meas[nr].up.Bx8.min(),
 419                                self.meas[nr].down.Bx8.min()]) - .05,
 420                        np.max([self.meas[nr].up.Bx8.max(),
 421                                self.meas[nr].down.Bx8.max()]) + .05)
 422
 423            if nr == 57:
 424                ax.set_title('M57: Plusses ($90^\circ$)')
 425            else:
 426                ax.set_title('M155: Plusses ($-90^\circ$)')
 427
 428            # Inset with Difference
 429            with sns.color_palette('bright'):
 430                if nr == 57 and not (kwargs.get('mirror')):
 431                    bbox = (.11, .59, .34, .35)
 432                else:
 433                    bbox = (.59, .12, .34, .35)
 434                inset = inset_axes(ax, width='100%', height='100%',
 435                                   bbox_to_anchor=bbox,
 436                                   bbox_transform=ax.transAxes)
 437                max_b = self.meas[nr].up.B.max()
 438                B_ext, B_stray = self.meas[nr].get_downminusup_strayfield()
 439                inset.plot([-max_b, max_b], [0, 0], '--', color='orange',
 440                           linewidth=.75)
 441                inset.plot(B_ext, B_stray)
 442                inset.set_xlim(-1000, 1000)
 443                inset.set_title("Difference")
 444
 445            # Crosses
 446            ax = cur_ax[1]
 447            self.meas[nr].plot_strayfield(ax, nolegend=True,
 448                                          show_plusses=False,
 449                                          show_crosses=False)
 450            ax.plot(self.meas[nr].up.B, self.meas[nr].up.Bx9, label='Up')
 451            ax.plot(self.meas[nr].down.B, self.meas[nr].down.Bx9, label='Down')
 452            ax.plot(self.meas[nr].up.B, self.meas[nr].up.Bx13,
 453                    label='Up (Empty)')
 454            ax.plot(self.meas[nr].down.B, self.meas[nr].down.Bx13,
 455                    label='Down (Empty)')
 456
 457            # Set Limits (x and y Axis)
 458            if kwargs.get('xlim'):
 459                ax.set_xlim(*kwargs.get('xlim'))
 460            else:
 461                ax.set_xlim(-1000, 1000)
 462
 463            ax.set_ylim(np.min([self.meas[nr].up.Bx9.min(),
 464                                self.meas[nr].down.Bx9.min()]) - .05,
 465                        np.max([self.meas[nr].up.Bx9.max(),
 466                                self.meas[nr].down.Bx9.max()]) + .05)
 467
 468            if nr == 57:
 469                ax.set_title('M57: Crosses ($90^\circ$)')
 470            else:
 471                ax.set_title('M155: Crosses ($-90^\circ$)')
 472
 473            # Inset with Difference
 474            with sns.color_palette('bright'):
 475                if nr == 57 and kwargs.get('mirror'):
 476                    bbox = (.11, .12, .34, .35)
 477                else:
 478                    bbox = (.11, .59, .34, .35)
 479                inset2 = inset_axes(ax, width='100%', height='100%',
 480                                    bbox_to_anchor=bbox,
 481                                    bbox_transform=ax.transAxes)
 482                f_up = scipy.interpolate.interp1d(self.meas[nr].up.B,
 483                                                  self.meas[nr].up.Bx9)
 484                f_down = scipy.interpolate.interp1d(self.meas[nr].down.B,
 485                                                    self.meas[nr].down.Bx9)
 486                B = np.linspace(self.meas[nr].up.B.min(),
 487                                self.meas[nr].up.B.max(), int(1e5))
 488                downminusup = f_down(B) - f_up(B)
 489                inset2.plot([-max_b, max_b], [0, 0], '--',
 490                            color='orange', linewidth=.75)
 491                inset2.plot(B, downminusup, color='green')
 492                inset2.set_xlim(-1000, 1000)
 493                inset2.set_title("Difference")
 494                if nr == 57:
 495                    inset.set_yticklabels(['', '$0.0$', '', '$0.5$'])
 496                    inset2.set_yticklabels(['', '$0.0$', '', '$0.5$'])
 497
 498            # if nr == 57 and not kwargs.get('mirror'):
 499            #    ax.legend(loc='upper left')#, ncol=2)
 500            if not (nr == 57):
 501                ax.legend(loc='upper right')  # , ncol=2)
 502
 503        plt.savefig("%s.pdf" % filename)
 504
 505    def plot_hloops2(self, to_show=[58, 59, 61, 62],
 506                     filename='hloop-compare-3', **kwargs):
 507        """Outputs hloop-compare-3.pdf (not used anymore)
 508
 509        Args:
 510            to_show (TYPE, optional): DESCRIPTION. The default is a[85]+a[100].
 511            filename (TYPE, optional): DESCRIPTION. The default is
 512                'hloop-compare-3'.
 513            **kwargs (TYPE): DESCRIPTION.
 514
 515        Returns:
 516            None.:
 517        """
 518        fig, axes = plt.subplots(2, 2, figsize=(16, 12))
 519        for i, nr in enumerate(to_show):
 520            ax = axes[i // 2][i % 2]
 521            self.m.style.set_style(size='talk', style='ticks',
 522                                   grid=True, latex=True)
 523            self.meas[nr] = Hloop(nr)
 524            self.meas[nr].plot_strayfield(ax)
 525            ax.set_xlim(-750, 750)
 526            if nr == 58:
 527                ax.set_ylim(self.meas[nr].down_fitted.Bx8.max(),
 528                            self.meas[nr].up_fitted.Bx8.min())
 529            with sns.color_palette('deep'):
 530                self.m.style.set_style(size='talk', style='ticks',
 531                                       grid=True, latex=True)
 532                if nr in [58, 59]:
 533                    bbox = (.13, .02, .35, .35)
 534                else:
 535                    bbox = (.65, .02, .35, .35)
 536                inset = inset_axes(ax, width='100%', height='90%',
 537                                   bbox_to_anchor=bbox,
 538                                   bbox_transform=ax.transAxes)
 539                max_b = self.meas[nr].up.B.max()
 540                inset.plot([-max_b, max_b], [0, 0], 'r--', linewidth=.75)
 541                B_ext, B_stray = self.meas[nr].get_downminusup_strayfield()
 542                inset.plot(B_ext, B_stray)
 543                inset.set_xlim(-750, 750)
 544                if not (nr in [58, 59]):
 545                    inset.set_ylabel("$\Delta B_z\\;[\\mathrm{mT}]$")
 546                    inset.set_title("Difference")
 547                inset.set_xticklabels([])
 548                inset.set_xticks([])
 549
 550        plt.savefig("%s.pdf" % filename)
 551
 552    def plot_hloops3(self, to_show=[139, 140],
 553                     filename='hloop-repeat',
 554                     xlim=(-350, 350),
 555                     ylim=None,
 556                     inset_xlim=(-30, 30),
 557                     inset_ylim=(0.15, 1.25)
 558                     ):
 559        """
 560            Compares two measurements at the same Angle (repeated
 561            measurements).
 562
 563        Args:
 564            to_show (TYPE, optional): DESCRIPTION. The default is [139,140].
 565            filename (TYPE, optional): DESCRIPTION. The default is
 566                'hloop-repeat'.
 567            xlim (TYPE, optional): DESCRIPTION. The default is (-350, 350).
 568            ylim:
 569            inset_xlim (TYPE, optional): DESCRIPTION. The default is (-30, 30).
 570            inset_ylim (TYPE, optional): DESCRIPTION. The default is (.15,
 571                1.25).
 572
 573        Returns:
 574            None.:
 575        """
 576        self.m.style.set_style(size='talk', style='ticks', palette='Paired',
 577                               grid=True, latex=True)
 578        fig, ax = plt.subplots(1, 1, figsize=(16, 12))
 579        bmin, bmax = [], []
 580        for i, nr in enumerate(to_show):
 581            # ax = axes[i//2][i%2]
 582            self.meas[nr] = Hloop(nr)
 583            self.meas[nr].plot_strayfield(ax, nolegend=True)
 584            ax.set_xlim(*xlim)
 585            bmin1, bmax1 = self.meas[nr].get_bhminmax()
 586            bmin.append(bmin1)
 587            bmax.append(bmax1)
 588
 589        if ylim:
 590            ax.set_ylim(*ylim)
 591        else:
 592            ax.set_ylim(np.min(bmin) - .05, np.max(bmax) + .05)
 593        ax.set_title("M%s/%s: %s ($%s^\\circ$)" % (to_show[0], to_show[1],
 594                                                   self.meas[nr].info[
 595                                                       'Structure'].replace(
 596                                                       '_', r'\_'),
 597                                                   self.meas[nr].info[
 598                                                       'Angle']))
 599        ax.legend(["M%d: Up" % to_show[0], "M%d: Down" % to_show[0],
 600                   "M%d: Up" % to_show[1], "M%d: Down" % to_show[1], ])
 601        y1, y2 = inset_ylim[0], inset_ylim[1]
 602        x1, x2 = inset_xlim[0], inset_xlim[1]
 603        ax.fill([x1, x1, x2, x2], [y1, y2, y2, y1], 'blue', alpha=.1)
 604
 605        with sns.color_palette('bright'):
 606            bbox = (.65, .055, .35, .3)
 607            inset = inset_axes(ax, width='100%', height='100%',
 608                               bbox_to_anchor=bbox,
 609                               bbox_transform=ax.transAxes)
 610            max_b = self.meas[nr].up.B.max()
 611            for nr in to_show:
 612                B_ext, B_stray = self.meas[nr].get_downminusup_strayfield()
 613                inset.plot(B_ext, B_stray)
 614                inset.plot([-max_b, max_b], [0, 0], '--', linewidth=.75)
 615            inset.set_xlim(*xlim)
 616            inset.set_ylabel("$\Delta B_z\\;[\\mathrm{mT}]$")
 617            inset.set_title("Difference")
 618
 619        with sns.color_palette('Paired'):
 620            bbox = (.07, .45, .35, .35)
 621            inset2 = inset_axes(ax, width='100%', height='100%',
 622                                bbox_to_anchor=bbox,
 623                                bbox_transform=ax.transAxes)
 624            for nr in to_show:
 625                self.meas[nr].plot_strayfield(inset2, nolegend=True)
 626            inset2.set_xlim(*inset_xlim)
 627            inset2.set_ylim(*inset_ylim)
 628            inset2.set_title("")
 629            inset2.set_ylabel('')
 630
 631        with sns.color_palette('Paired'):
 632            bbox = (.65, .43, .35, .3)
 633            inset3 = inset_axes(ax, width='100%', height='100%',
 634                                bbox_to_anchor=bbox,
 635                                bbox_transform=ax.transAxes)
 636            self.plot_hloop_gradient3(inset3, nr=to_show,
 637                                      limit=inset_xlim[1], )
 638            # inset3.set_xticklabels([''])
 639            inset3.set_xlim(*inset_xlim)
 640            inset3.set_xlabel('')
 641            inset3.legend_.remove()
 642
 643        plt.savefig("%s.pdf" % filename)
 644
 645    def plot_hloops_compare_90(self, to_show=[414, 415],
 646                               structure='plusses',
 647                               filename='hloop-repeat-414',
 648                               xlim=(-750, 750),
 649                               ylim=(-.36, .76),
 650                               inset_xlim=(-225, 225),
 651                               inset_ylim=(-.35, .75),
 652                               insets=[((.05, .05, .35, .35), ((-225, 225), (-.35, .75), 'blue', .1))],
 653                               legend_loc='upper right',
 654                               nograd=False,
 655                               nodiff=False,
 656                               figsize=(16, 12),
 657                               ):
 658        """
 659            Compares multiple measurements at 90 deg (repeated
 660            measurements).
 661
 662        Args:
 663            to_show (TYPE, optional): DESCRIPTION. The default is [414,415].
 664            structure:
 665            filename (TYPE, optional): DESCRIPTION. The default is
 666                'hloop-repeat-414'.
 667            xlim (TYPE, optional): DESCRIPTION. The default is (-750, 750).
 668            ylim:
 669            inset_xlim (TYPE, optional): DESCRIPTION. The default is (-100,
 670                100).
 671            inset_ylim (TYPE, optional): DESCRIPTION. The default is (-1.25,
 672                .75).
 673            legend_loc:
 674            nograd:
 675
 676        Returns:
 677            None.:
 678        """
 679        self.m.style.set_style(size='talk', style='ticks',
 680                               palette='Paired',
 681                               grid=True, latex=True)
 682        fig, ax = plt.subplots(1, 1, figsize=figsize)
 683        legend = []
 684        if structure == 'plusses':
 685            column = 'Bx8'
 686        elif structure == 'crosses':
 687            column = 'Bx9'
 688
 689        for i, nr in enumerate(to_show):
 690            # ax = axes[i//2][i%2]
 691            self.meas[nr] = Hloop(nr)  # Load measurement
 692            self.meas[nr].remove_zero()  # Remove measurement Errors
 693
 694            self.meas[nr].plot_strayfield(ax, nolegend=True,
 695                                          show_plusses=(
 696                                                  structure == 'plusses'),
 697                                          show_crosses=(
 698                                                  structure == 'crosses'))
 699            ax.set_xlim(*xlim)
 700
 701            legend += ["m%d: Up" % nr, "m%d: Down" % nr]
 702
 703        m_numbers = '/'.join(map(str, to_show))
 704
 705        if ylim:
 706            ax.set_ylim(*ylim)
 707
 708        self.m.style.set_style(size='talk', style='ticks', palette='Paired',
 709                               grid=True, latex=True)
 710        ax.set_title("\\textbf{%s} ($%s^{\\circ}$)" % (
 711                                                structure.capitalize(),
 712                                                self.meas[nr].info['Angle']))
 713
 714        self.m.style.set_style(size='notebook', style='ticks',
 715                               palette='Paired',
 716                               grid=True, latex=True)
 717        all_insets = []
 718        if not nodiff:
 719            with sns.color_palette('bright'):
 720                bbox = (.7, .06, .3, .3)
 721                inset = inset_axes(ax, width='100%', height='100%',
 722                                   bbox_to_anchor=bbox,
 723                                   bbox_transform=ax.transAxes)
 724                all_insets.append(inset)
 725                max_b = self.meas[nr].up.B.max()
 726                for i, nr in enumerate(to_show):
 727                    if structure == 'plusses':
 728                        B_ext, B_stray = self.meas[nr].get_downminusup_strayfield(
 729                            fitted_data=True)
 730                    else:
 731                        up = self.meas[nr].up_fitted
 732                        down = self.meas[nr].down_fitted
 733                        f_up = scipy.interpolate.interp1d(up.B, up.Bx9)
 734                        f_down = scipy.interpolate.interp1d(down.B, down.Bx9)
 735                        B_ext = np.linspace(up.B.min(), up.B.max(), int(1e5))
 736                        B_stray = f_down(B_ext) - f_up(B_ext)
 737                    if i == 3:
 738                        inset.plot(B_ext, B_stray, color='orange')
 739                    else:
 740                        inset.plot(B_ext, B_stray)
 741                    if i == 0:
 742                        inset.plot([-max_b, max_b], [0, 0], '--', linewidth=.75)
 743
 744                inset.set_xlim(*xlim)
 745                inset.set_ylabel("$\Delta B_z\\;[\\mathrm{mT}]$")
 746                inset.set_title("Difference")
 747
 748        subfig = ['d', 'b', 'a', 'c']
 749        for bbox, (inset_xlim, inset_ylim, color, alpha) in insets:
 750            with sns.color_palette('Paired'):
 751                inset2 = inset_axes(ax, width='100%', height='100%',
 752                                    bbox_to_anchor=bbox,
 753                                    bbox_transform=ax.transAxes)
 754                all_insets.append(inset2)
 755                for i, nr in enumerate(to_show):
 756                    inset2.plot(self.meas[nr].up_fitted.B,
 757                                self.meas[nr].up_fitted[column])
 758                    inset2.plot(self.meas[nr].down_fitted.B,
 759                                self.meas[nr].down_fitted[column])
 760                inset2.set_xlim(*inset_xlim)
 761                inset2.set_ylim(*inset_ylim)
 762
 763            (ann_x, ann_xx), (ann_yy, ann_y) = inset2.get_xlim(), inset2.get_ylim()
 764            ann_x += (ann_xx - ann_x) * .05
 765            ann_y -= (ann_y - ann_yy) * .20
 766            inset2.text(x=ann_x, y=ann_y, s=subfig.pop(),
 767                          fontdict=dict(fontweight='bold', fontsize=24),
 768                          bbox=dict(boxstyle="round,pad=0.1", fc='#ccf', ec="b", lw=1))
 769
 770            # Draw Area into big plot
 771            y1, y2 = inset_ylim[0], inset_ylim[1]
 772            x1, x2 = inset_xlim[0], inset_xlim[1]
 773            coords = [x1, x1, x2, x2], [y1, y2, y2, y1]
 774            inset2.fill(*coords, color, alpha=alpha)
 775            ax.fill(*coords, color, alpha=alpha)
 776
 777        if not nograd:
 778            with sns.color_palette('Paired'):
 779                bbox = (.08, .67, .27, .3)
 780                inset3 = inset_axes(ax, width='100%', height='100%',
 781                                    bbox_to_anchor=bbox,
 782                                    bbox_transform=ax.transAxes)
 783                all_insets.append(inset3)
 784                c = column.replace('B', 'V')
 785                self.plot_hloop_gradient3(inset3, to_show, limit=inset_xlim[1],
 786                                          column=c)
 787                inset3.set_xlim(*inset_xlim)
 788                inset3.set_xlabel('')
 789                inset3.legend_.remove()
 790
 791        sns.set('notebook')
 792        ax.legend(legend, loc=legend_loc, ncol=int(len(to_show) / 2))
 793
 794        plt.savefig("%s.png" % filename)
 795
 796        return fig, (ax, all_insets)
 797
 798    def plot_hloops4(self, filename='hloop-parallel',
 799                     figtitle="M57: $90^\\circ$ Parallel measurement",
 800                     **kwargs):
 801        """Plots a single measurement (default: 90deg parallel)
 802
 803        Args:
 804            filename (TYPE, optional): DESCRIPTION. The default is
 805                'hloop-parallel'.
 806            figtitle (TYPE, optional): DESCRIPTION. The default is "M57:
 807                $90^\circ$ Parallel measurement".
 808            **kwargs (TYPE): DESCRIPTION.
 809
 810        Returns:
 811            None.:
 812        """
 813        fig, ax = plt.subplots(1, 1, figsize=(16, 12))
 814        self.m.style.set_style(size='talk', style='ticks', palette='Paired',
 815                               grid=True, latex=True)
 816        # self.mset_factor(-1)
 817        # self.mfactor = 1
 818
 819        self.m.plot_strayfield(ax, figtitle=figtitle,
 820                               show_crosses=False)
 821        self.m.up_fitted.Bx9 += .25
 822        self.m.down_fitted.Bx9 += .25
 823        ax.plot(self.m.up_fitted.B, self.m.up_fitted.Bx9,
 824                label='Crosses: Up (fitted)')
 825        ax.plot(self.m.down_fitted.B, self.m.down_fitted.Bx9,
 826                label='Crosses: Down (fitted)')
 827        ax.set_xlim(-750, 750)
 828        if kwargs.get('ylim'):
 829            ax.set_ylim(*kwargs.get('ylim'))
 830        else:
 831            ax.set_ylim(-.8, 1.8)
 832        ax.legend(loc='best')
 833
 834        with sns.color_palette('bright'):
 835            bbox = (.06, .46, .35, .23)
 836            inset = inset_axes(ax, width='100%', height='100%',
 837                               bbox_to_anchor=bbox,
 838                               bbox_transform=ax.transAxes)
 839            max_b = self.m.up.B.max()
 840            B_ext, B_stray = self.m.get_downminusup_strayfield()
 841            inset.plot([-max_b, max_b], [0, 0], '--', color='orange',
 842                       linewidth=.75)
 843            inset.plot(B_ext, B_stray)
 844            inset.set_xlim(-750, 750)
 845            inset.set_title("Difference Plusses")
 846
 847            bbox = (.06, .06, .35, .22)
 848            inset2 = inset_axes(ax, width='100%', height='100%',
 849                                bbox_to_anchor=bbox,
 850                                bbox_transform=ax.transAxes)
 851            f_up = scipy.interpolate.interp1d(self.m.up.B, self.m.up.Bx9)
 852            f_down = scipy.interpolate.interp1d(self.m.down.B, self.m.down.Bx9)
 853            B = np.linspace(self.m.up.B.min(), self.m.up.B.max(), int(1e5))
 854            downminusup = f_down(B) - f_up(B)
 855            inset2.plot([-max_b, max_b], [0, 0], '--', color='orange',
 856                        linewidth=.75)
 857            inset2.plot(B, downminusup, color='green')
 858            inset2.set_xlim(-750, 750)
 859            inset2.set_title("Difference Crosses")
 860
 861        plt.savefig("%s.pdf" % filename)
 862
 863    def plot_cubes_trees(self):
 864        """Plots difference between RAW data from Cubes and Trees (1st
 865        Generation)
 866
 867        Returns:
 868            None.:
 869        """
 870        self.m.style.set_style(size='talk', style='ticks', palette='deep',
 871                               grid=True, latex=True)
 872        file_list = ['data/Data/%sdeg_%s' % (angle, struct) for
 873                     angle in [90, -90] for
 874                     struct in ['cubes', 'Trees']
 875                     ]
 876
 877        fig, axes = plt.subplots(2, 2, figsize=(16, 12))
 878        comp = {}
 879        for i, f in enumerate(file_list):
 880            ax = axes[i // 2][i % 2]
 881            load_files = ['%s_%s.dat' % (f, direction) for
 882                          direction in ['a', 'b']]
 883            comp[i] = Hloop(0, file_list=load_files)
 884            comp[i].set_factor(1e6)
 885
 886            ax.plot(comp[i].up.B, comp[i].up.Vx8, label='Up')
 887            ax.plot(comp[i].down.B, comp[i].down.Vx8, label='Down')
 888
 889            ax.set_ylabel("$V_x [\\mathrm{\\mu V}]$")
 890            ax.set_xlabel("$\\mu_0 H_{ext}$ $[\\mathrm{mT}]$")
 891            bmin, bmax, vmin, vmax = comp[i].get_minmax()
 892            ax.set_xlim(bmin, bmax)
 893
 894            if f.find('cubes') > 0:
 895                struct = 'Cubes'
 896            else:
 897                struct = "Trees"
 898
 899            ax.set_title("%s ($%s^\\circ$)" % (struct, f[10:13].strip('d')))
 900
 901            with sns.color_palette('bright'):
 902                if i % 2 == 1:
 903                    bbox = (.69, .7, .3, .3)
 904                else:
 905                    bbox = (.12, .7, .29, .3)
 906                inset = inset_axes(ax, width='100%', height='100%',
 907                                   bbox_to_anchor=bbox,
 908                                   bbox_transform=ax.transAxes)
 909                max_b = self.m.up.B.max()
 910                B_ext, B_stray = comp[i].get_downminusup()
 911                inset.plot([-max_b, max_b], [0, 0], '--', color='orange',
 912                           linewidth=.75)
 913                inset.plot(B_ext, B_stray)
 914                inset.set_xlim(bmin, bmax)
 915                # inset.set_title("Difference")
 916
 917        ax.legend(loc='lower left')
 918
 919        plt.savefig("compare-cubes-trees.pdf")
 920
 921    def plot_hloop_gradient(self, limit=50, filename='hloop-gradient'):
 922        """Plotting the gradient along the
 923
 924        Args:
 925            limit:
 926            filename:
 927
 928        Returns:
 929            None.:
 930        """
 931        self.m.style.set_style(default=True, grid=True,
 932                               style='ticks',
 933                               size='talk',
 934                               palette='deep',
 935                               latex=True, )
 936
 937        grad1 = [np.divide(np.diff(self.m.up.Vx8), np.diff(self.m.up.Time)),
 938                 np.divide(np.diff(self.m.up.Vx9), np.diff(self.m.up.Time))]
 939        grad12 = [
 940            np.divide(np.diff(self.m.down.Vx8), np.diff(self.m.down.Time)),
 941            np.divide(np.diff(self.m.down.Vx9), np.diff(self.m.down.Time))]
 942
 943        fig, axes = plt.subplots(2, 1, sharex=True)
 944        for i in range(2):
 945            ax = axes[i]
 946            g = grad1[i]
 947            g2 = grad12[i]
 948
 949            if i == 0:
 950                add = 'Plusses'
 951            else:
 952                add = 'Crosses'
 953
 954            ax.set_title('M57: Gradient (%s)' % add)
 955            x = self.m.up[:-1]
 956            ax.plot(x.B[x.B.abs() < limit],
 957                    g[x.B.abs() < limit] * 1e6,
 958                    label='Up (%s)' % add)
 959            x = self.m.down[:-1]
 960            ax.plot(x.B[x.B.abs() < limit],
 961                    g2[x.B.abs() < limit] * 1e6,
 962                    label='Down (%s)' % add)
 963            ax.set_ylabel('$dV_H/dt\; [\\mu\\mathrm{V/s}]$')
 964        ax.set_xlabel('$\\mu_0 H_{ext} [\\mathrm{mT}]$')
 965
 966        plt.savefig('%s.pdf' % filename)
 967
 968    def plot_hloop_gradient2(self, limit=50, filename='hloop-gradient'):
 969        """Plotting the gradient along the
 970
 971        Args:
 972            limit:
 973            filename:
 974
 975        Returns:
 976            None.:
 977        """
 978        self.m.style.set_style(default=True, grid=True,
 979                               style='ticks',
 980                               size='talk',
 981                               palette='deep',
 982                               latex=True, )
 983
 984        grad = [np.gradient(self.m.up.Vx8, np.diff(self.m.up.Time).mean()),
 985                np.gradient(self.m.down.Vx8, np.diff(self.m.down.Time).mean())]
 986
 987        fig, ax = plt.subplots()
 988        ax.set_title('M%s: Gradient (%s)' % (self.m.self.measurement_number,
 989                                             self.m.info['Structure']))
 990        for i in range(2):
 991            g = grad[i]
 992
 993            if i == 0:
 994                x = self.m.up
 995                add = 'Up'
 996            else:
 997                x = self.m.down
 998                add = 'Down'
 999
1000            ax.plot(x.B[x.B.abs() < limit],
1001                    g[x.B.abs() < limit] * 1e6,
1002                    label='%s' % add)
1003        ax.set_ylabel('$dV_H/dt\; [\\mu\\mathrm{V/s}]$')
1004        ax.legend(loc='best')
1005        ax.set_xlabel('$\\mu_0 H_{ext} [\\mathrm{mT}]$')
1006
1007        plt.savefig('%s.pdf' % filename)
1008
1009    def plot_hloop_gradient3(self, ax, to_show=[139, 140], limit=50,
1010                             column='Vx8'):
1011        """Plotting the gradient along the
1012
1013        Args:
1014            ax:
1015            to_show:
1016            limit:
1017            column:
1018
1019        Returns:
1020            None.:
1021        """
1022        # self.m.style.set_style(default=True, grid=True,
1023        #        style='ticks',
1024        #        size='talk',
1025        #        palette='Paired',
1026        #        latex=True,)
1027
1028        limit = np.abs(limit)
1029
1030        ax.set_title('Gradient')
1031        for nr in to_show:
1032            self.m = self.meas[nr]
1033            c = column
1034            grad = [np.divide(np.diff(self.m.up[c]), np.diff(self.m.up.Time)),
1035                    np.divide(np.diff(self.m.down[c]),
1036                              np.diff(self.m.down.Time))]
1037            for i in range(2):
1038                g = grad[i]
1039
1040                if i == 0:
1041                    x = self.m.up[:-1]
1042                    direction = 'Up'
1043                else:
1044                    x = self.m.down[:-1]
1045                    direction = 'Down'
1046
1047                ax.plot(x.B[x.B.abs() < limit],
1048                        g[x.B.abs() < limit] * 1e6,
1049                        label='M%s: %s' % (nr, direction))
1050
1051        # ax.set_xlim(-limit, limit)
1052        ax.set_ylabel('$dV_H/dt\; [\\mu\\mathrm{V/s}]$')
1053        ax.legend(loc='best')
1054        ax.set_xlabel('$\\mu_0 H_{ext} [\\mathrm{mT}]$')
1055
1056    def plot_hloop_temp(self):
1057        """Plots the Temperature of a Hloop measurement.
1058
1059        Returns:
1060            None.:
1061        """
1062
1063        self.m.style.set_style(default=True, grid=True,
1064                               style='ticks',
1065                               size='talk',
1066                               palette='deep',
1067                               latex=True, )
1068
1069        fig, ax = plt.subplots()
1070        ax.set_title(
1071            'M%s: Sample Temperature' % self.m[self.measurement_number])
1072
1073        ax.plot(self.m.up.B, (self.m.up['Sample Temp']), '-', label='Up')
1074        ax.plot(self.m.down.B, (self.m.down['Sample Temp']), '-', label='Down')
1075
1076        ax.set_xlabel('$\\mu_0 H_{ext} [\\mathrm{mT}]$')
1077        ax.set_ylabel('Temperature $[\\mathrm{mK}]$')
1078        ax.legend(loc='best')
1079
1080        plt.savefig('hloop-90-temp.pdf')
1081
1082    #### Plot Static Fields
1083    def plot_static_fields(self, filename='static-field',
1084                           show_inset=False):
1085        """Plot the Signal Analyzer (SA) Data for static fields (not sweeping)
1086
1087        Returns:
1088            None.:
1089        """
1090        tmp = {
1091            'Plusses (25 mT)': 360,
1092            'Plusses (-25 mT)': 361,
1093            'Plusses (-9.4 mT)': 282,
1094            'Crosses (-9.4 mT)': 283,
1095            'Plusses (0 T)': 281,
1096            'Crosses (0 T)': 280,
1097            'Empty (0 T)': 310,
1098            # 'Crosses (250 mT)': 315,
1099        }
1100        lofm = {}
1101        for i, j in tmp.items():
1102            lofm.update({j: ['%s' % i, {}]})
1103
1104        self.eva.style.set_style(default=True, grid=True,
1105                                 style='ticks',
1106                                 size='talk',
1107                                 palette='Paired',
1108                                 latex=True, )
1109
1110        fig, ax = self.eva.plot(lofm,
1111                                plot_settings=dict(
1112                                    title='($90^\\circ$) Static Field',
1113                                    xlim=(2e-2, 5e0),
1114                                    ylim=(1e-7, 1e-3),
1115                                ),
1116                                f_settings=dict(
1117                                    ymin=5e-5),
1118                                f2_settings=dict(disable=True),
1119                                )
1120
1121        # ax = plt.gca()
1122        if show_inset:
1123            with sns.color_palette('deep'):
1124                inset = inset_axes(ax, width='100%', height='100%',
1125                                   bbox_to_anchor=(.25, .58, .45, .4),
1126                                   bbox_transform=ax.transAxes)
1127                self.m.style.set_style(default=True, grid=True,
1128                                       style='ticks',
1129                                       size='talk',
1130                                       palette='Paired',
1131                                       latex=True)
1132                self.m.plot_strayfield(inset, 'Strayfield Plusses', nolegend=True)
1133                inset.legend(['Up',  # ' ($-M_S \\rightarrow +M_S$)',
1134                              'Down'])  # ' ($+M_S \\rightarrow -M_S$)'])
1135                inset.grid(b=True, alpha=.4)
1136                inset.set_xlim(-50, 50)
1137                inset.set_ylim(-.65, .45)
1138
1139                def a(x):
1140                    return self.m.down.iloc[
1141                        ((self.m.down.B - x) ** 2).idxmin()]['Bx8']
1142
1143                def b(x):
1144                    return self.m.up.iloc[((self.m.up.B - x) ** 2).idxmin()]['Bx8']
1145
1146                inset.plot((-25, -25), (a(-25), a(-25)), 'o', color='#000099',
1147                           alpha=.6)
1148                inset.plot([25, 25], [b(25), b(25)], 'o', color='#9999FF',
1149                           alpha=.6)
1150                inset.plot([-9.4, -9.4], [a(-9.4), a(-9.4)], 'o', color='green',
1151                           alpha=.6)
1152                inset.plot([0], [a(0)], 'o', color='#FF3333', alpha=.8)
1153
1154                # ana.set_relative_yticks(inset)
1155                inset.set_xticks(np.arange(-50, 51, 25))
1156
1157        plt.savefig('{}.pdf'.format(filename))
1158
1159    #### Sweeping Field
1160    def first_sweeps(self, add=False, filename='vacant-sweeps',
1161                     show_inset=False):
1162        """First field sweeps (plus minus 50/75/100/etc.)
1163
1164        Args:
1165            add: adding more data
1166            filename: saving as pdf (default: vacant-sweeps)
1167
1168        Returns:
1169            vacant-sweeps.pdf:
1170        """
1171        self.m.style.set_style(default=True, grid=True,
1172                               size='talk', style='ticks', latex=True,
1173                               palette='deep')
1174
1175        lofm = {}
1176        to_show = {
1177            # 382: [(-25,25), 'Plusses', 5, {}],
1178            362: [(-50, 50), 'Plusses', 2, {}],
1179            363: [(-75, 75), 'Plusses', 2, {}],
1180            364: [(-100, 100), 'Plusses', 2, {}],
1181            366: [(-125, 125), 'Plusses', 2, {}],
1182            365: [(-150, 150), 'Plusses', 2, {}],
1183            # 352: [("-M_s \\rightarrow -25",25), 'Plusses', .1],
1184        }
1185        if add:
1186            to_show.update({
1187                336: [(-500, 500), 'Plusses', 9.4, {}],
1188                331: [('\\mathrm{C}\\,{%s}' % -100, 100), 'Crosses', 9.4,
1189                      {'linestyle': '--'}],
1190                332: [('\\mathrm{C}\\,{%s}' % -300, 300), 'Crosses', 9.4,
1191                      {'linestyle': '--'}],
1192                333: [('\\mathrm{C}\\,{%s}' % -750, 750), 'Crosses', 9.4,
1193                      {'linestyle': '--'}],
1194            })
1195
1196        for nr, content in to_show.items():
1197            lofm[nr] = ["$%s \\rightarrow %s\\;\\mathrm{mT}$" % (
1198                content[0][0],
1199                content[0][1],
1200            ), content[3]]
1201
1202        fig, ax = self.eva.plot(lofm,
1203                                # fit_range=(2e-2, 9e-1),
1204                                # show_fit=True,
1205                                plot_settings=dict(
1206                                    title='\\Huge \\textbf{b) Sweeping Inside the Hysteresis (Plusses)}',
1207                                    xlim=(1e-2, 1e0),
1208                                    ylim=(4e-7, 7e-2)),
1209                                f_settings=dict(
1210                                    disable=True,
1211                                    xmin=5e-2,
1212                                    ymin=1e-5),
1213                                f2_settings=dict(
1214                                    xmin=2e-2,
1215                                    ymin=1e-2,
1216                                ),
1217                                )
1218
1219        if show_inset and not add: # Inset with Strayfield
1220            with sns.color_palette('deep'):
1221                inset = inset_axes(ax, width='100%', height='100%',
1222                                   bbox_to_anchor=(.74, .45, .25, .25),
1223                                   bbox_transform=ax.transAxes)
1224                # Plotting Lines for each measurement
1225                y1, y2 = -1, 1
1226                for nr, content in to_show.items():
1227                    x1 = content[0][1]
1228                    inset.plot([x1, x1, -x1, -x1], [y1, y2, y2, y1])
1229
1230                self.m.plot_strayfield(inset, 'Strayfield Plusses',
1231                                       show_plusses=False,
1232                                       show_crosses=False,
1233                                       nolegend=True)
1234
1235                inset.plot(self.m.up_fitted.B, self.m.up_fitted.Bx8, '-',
1236                           color=(76 / 255, 114 / 255, 176 / 255),
1237                           linewidth=3, label='Up')
1238                inset.plot(self.m.down_fitted.B, self.m.down_fitted.Bx8, 'r-',
1239                           color=(221 / 255, 132 / 255, 82 / 255),
1240                           linewidth=1.5, label='Down')
1241
1242                inset.legend(['Up',  # ($-M_S \\rightarrow +M_S$)',
1243                              'Down'])  # ($+M_S \\rightarrow -M_S$)'])
1244                inset.grid(b=True, alpha=.4)
1245                inset.set_xlim(-200, 200)
1246                inset.set_ylim(-.65, .45)
1247                inset.set_xticks([-200 + 50 * _ for _ in range(9)])
1248                inset.set_xticks([-175 + 50 * _ for _ in range(8)], minor=True)
1249                inset.set_yticks([-0.25 + .5 * _ for _ in range(2)],
1250                                 minor=True)
1251                inset.grid(b=True, which='minor', color='#cccccc',
1252                           linestyle='-.', alpha=.3)
1253
1254                # X-Ticks (labels)
1255                xt_labels = []
1256                for i in [-200 + 50 * _ for _ in range(9)]:
1257                    if i % 100 == 0:
1258                        xt_labels += ['$%s$' % i]
1259                    else:
1260                        xt_labels += ['']
1261                inset.set_xticklabels(xt_labels)
1262
1263        # Inset showing fitted data
1264        with sns.color_palette("deep"):
1265            inset2 = inset_axes(ax, width='100%', height='100%',
1266                                bbox_to_anchor=(.44, .75, .3, .25),
1267                                bbox_transform=ax.transAxes)
1268            inset3 = inset_axes(ax, width='100%', height='100%',
1269                                bbox_to_anchor=(.09, .09, .3, .25),
1270                                bbox_transform=ax.transAxes)
1271
1272            i2data = {}
1273            for nr, content in to_show.items():
1274                intercept, slope = self.eva[nr].fit(fit_range=(1e-2, 2e-1))
1275                voltage = content[0][1]
1276                i2data[nr] = {'end field': voltage,
1277                              'alpha': -slope,
1278                              'amplitude': 10 ** intercept}
1279
1280                inset2.plot(voltage, 10 ** intercept, 'o')
1281                inset3.plot(voltage, -slope, 'o')
1282
1283            inset2.set_xlabel('End field [$\mu_0 \mathrm{H_{ext}}$]')
1284            inset2.set_ylabel('$S_{V_H} (f=1\\;$Hz$)$')
1285            inset2.set_yscale('log')
1286            inset2.set_yticks([8e-7, 9e-7, 1e-6, 2e-6])
1287
1288            inset3.set_xlabel('End field [$\mu_0 \mathrm{H_{ext}}$]')
1289            inset3.set_ylabel('$\\alpha$')
1290
1291        plt.savefig('%s.pdf' % filename)
1292        return i2data
1293
1294    def compare_voltages(self, filename='compare-voltages',
1295                         show_inset=False):
1296        """Compare different applied Voltages.
1297
1298        Returns:
1299            compare-voltages.pdf:
1300        """
1301        self.m.style.set_style(default=True, grid=True,
1302                               size='talk', style='ticks', latex=True,
1303                               palette='deep')
1304
1305        lofm = {}
1306        to_show = {}
1307        for i in range(1, 6):
1308            to_show[i + 377] = i
1309
1310        for nr, content in to_show.items():
1311            lofm[nr] = ["$%s\\;\\mu\\mathrm{A}$" % (
1312                content
1313            ), {}]
1314
1315        fig, ax = self.eva.plot(lofm,
1316                                # fit_range=(2e-2, 7e-1),
1317                                # show_fit=True,
1318                                plot_settings=dict(
1319                                    title='Current Amplitudes (SR785)',
1320                                    xlim=(1e-2, 1e0),
1321                                    ylim=(4e-7, 7e-2)),
1322                                f_settings=dict(disable=True,
1323                                                xmin=5e-2,
1324                                                ymin=1e-5),
1325                                f2_settings=dict(
1326                                    xmin=1.5e-1,
1327                                    ymin=2e-3,
1328                                ),
1329                                )
1330
1331        if show_inset:
1332            # Inset with Strayfield
1333            with sns.color_palette('deep'):
1334                inset = inset_axes(ax, width='100%', height='100%',
1335                                   bbox_to_anchor=(.07, .38, .26, .26),
1336                                   bbox_transform=ax.transAxes)
1337                self.m.plot_strayfield(inset, 'Strayfield Plusses',
1338                                       nolegend=True, )
1339                inset.legend(['Up',  # ($-M_S \\rightarrow +M_S$)',
1340                              'Down'])  # ($+M_S \\rightarrow -M_S$)'])
1341                inset.grid(b=True, alpha=.4)
1342                inset.set_xlim(-50, 50)
1343                inset.set_ylim(-.65, .45)
1344                inset.set_xticks([-50 + 25 * _ for _ in range(5)])
1345                # inset.set_xticks([-45+10*_ for _ in range(10)], minor=True)
1346                inset.grid(b=True, which='minor', color='#cccccc',
1347                           linestyle='-.', alpha=.3)
1348                # inset.set_xlabel('')
1349                inset.set_ylabel('')
1350
1351                y1, y2 = -1, 2
1352                inset.fill([-25, -25, 25, 25], [y1, y2, y2, y1], 'blue', alpha=.1)
1353                inset.annotate("", xy=(25, -.35), xytext=(-25, -.2),
1354                               arrowprops=dict(arrowstyle="->", color='blue'))
1355
1356        # Inset showing fitted data
1357        with sns.color_palette("deep"):
1358            inset2 = inset_axes(ax, width='100%', height='100%',
1359                                bbox_to_anchor=(.54, .75, .3, .25),
1360                                bbox_transform=ax.transAxes)
1361            inset3 = inset_axes(ax, width='100%', height='100%',
1362                                bbox_to_anchor=(.3, .09, .3, .25),
1363                                bbox_transform=ax.transAxes)
1364
1365            i2data = {}
1366            for nr, content in to_show.items():
1367                intercept, slope = self.eva[nr].fit(fit_range=(2e-2, 7e-1))
1368                voltage = content
1369                i2data[nr] = {'voltage': voltage,
1370                              'alpha': -slope,
1371                              'amplitude': 10**intercept}
1372
1373                inset2.plot(voltage, 10 ** intercept, 'o')
1374                inset3.plot(voltage, -slope, 'o')
1375
1376            inset2.set_xlabel('Current [$\\mu\\mathrm{A}$]')
1377            inset2.set_ylabel('$S_{V_H} (f=1\\;$Hz$)$')
1378            inset2.set_yscale('log')
1379
1380            inset3.set_xlabel('Current [$\\mu\\mathrm{A}$]')
1381            inset3.set_ylabel('$\\alpha$')
1382
1383        plt.savefig('{}.pdf'.format(filename))
1384        return i2data
1385
1386    def compare_sweeprates(self, filename='compare-sweeprates',
1387                           show_inset = False):
1388        """Compare different Sweeprates, fits the data and
1389
1390        Returns:
1391            None.:
1392        """
1393        self.m.style.set_style(default=True, grid=True,
1394                               size='talk', style='ticks', latex=True,
1395                               palette='deep')
1396
1397        lofm = {}
1398        to_show = {
1399            382: [("-M_s \\rightarrow -25", 25), 'Plusses', 5],
1400            354: [("-M_s \\rightarrow -25", 25), 'Plusses', 2],
1401            351: [("-M_s \\rightarrow -25", 25), 'Plusses', 1],
1402            355: [("-M_s \\rightarrow -25", 25), 'Plusses', .5],
1403            353: [("-M_s \\rightarrow -25", 25), 'Plusses', .25],
1404            352: [("-M_s \\rightarrow -25", 25), 'Plusses', .1],
1405        }
1406
1407        for nr, content in to_show.items():
1408            lofm[nr] = ["$%s\\;\\frac{\\mathrm{mT}}{\\mathrm{min}}$" % (
1409                content[2],
1410            ), {}]
1411
1412        fig, ax = self.eva.plot(lofm,
1413                                # fit_range=(2e-2, 5e-1),
1414                                # show_fit=True,
1415                                plot_settings=dict(
1416                                    title='Sweeprates (SR785)',
1417                                    xlim=(1e-2, 1.6e0),
1418                                    ylim=(4e-7, 5e-2)),
1419                                f_settings=dict(
1420                                    xmin=5e-2,
1421                                    ymin=1e-5,
1422                                    disable=True),
1423                                f2_settings=dict(
1424                                    xmin=1.5e-1,),
1425                                )
1426
1427        ax = plt.gca()
1428        if show_inset:
1429            # Inset with Strayfield
1430            with sns.color_palette('deep'):
1431                inset = inset_axes(ax, width='100%', height='90%',
1432                                   bbox_to_anchor=(.1, .06, .3, .33),
1433                                   bbox_transform=ax.transAxes)
1434                self.m.plot_strayfield(inset, 'Strayfield Plusses',
1435                                       nolegend=True, )
1436                inset.legend(['Up',  # ($-M_S \\rightarrow +M_S$)',
1437                              'Down'])  # ($+M_S \\rightarrow -M_S$)'])
1438                inset.grid(b=True, alpha=.4)
1439                inset.set_xlim(-50, 50)
1440                inset.set_ylim(-.65, .45)
1441                inset.set_xticks([-50 + 25 * _ for _ in range(5)])
1442
1443                y1, y2 = -1, 2
1444                inset.fill([-25, -25, 25, 25], [y1, y2, y2, y1], 'blue', alpha=.1)
1445                inset.annotate("", xy=(25, -.35), xytext=(-25, -.2),
1446                               arrowprops=dict(arrowstyle="->", color='blue'))
1447
1448        # Inset showing fitted data
1449        with sns.color_palette("deep"):
1450            inset2 = inset_axes(ax, width='100%', height='100%',
1451                                bbox_to_anchor=(.5, .75, .3, .25),
1452                                bbox_transform=ax.transAxes)
1453
1454            i2data = {}
1455            for nr, content in to_show.items():
1456                intercept, slope = self.eva[nr].fit(fit_range=(2e-2, 7e-1))
1457                sweep_rate = content[2]
1458                i2data[nr] = {'sweeprate': sweep_rate,
1459                              'alpha': -slope,
1460                              'amplitude': 10**intercept}
1461
1462                inset2.plot(sweep_rate, 10 ** intercept, 'o')
1463
1464            inset2.set_xlabel(
1465                'Sweeprate $\\frac{d}{dt} \mu_0 H_{ext} \\left[\\frac{\\mathrm{mT}}{\\mathrm{min}}\\right]$')
1466            inset2.set_ylabel('$S_{V_H} (f=1\\;$Hz$)$')
1467            inset2.set_yscale('log')
1468
1469        # Only save if necessary
1470        plt.savefig('{}.pdf'.format(filename))
1471        return i2data
1472
1473    def fast_sweeprate(self, show_numbers=[320, 325, 323, 329, ], xlim=None,
1474                       filename='FastSR-1', pos2=False,
1475                       title='\\Huge \\textbf{a) Large Field Sweeps}',
1476                       show_inset=False):
1477        """anas Fast Sweeps over large sweep ranges (1T)
1478
1479        Args:
1480            show_numbers:
1481            xlim:
1482            filename (TYPE, optional): DESCRIPTION. The default is 'FastSR-1'.
1483            pos2:
1484            title:
1485
1486        Returns:
1487            None.:
1488        """
1489        to_show = {
1490            320: [(2, 1), 'Empty', 9.4],
1491            321: [(2, 1), 'Empty', 4.7],
1492            325: [(2, 1), 'Crosses', 9.4],
1493            326: [(2, 1), 'Crosses', 4.7],
1494            323: [(1, -1), 'Empty', 9.4],
1495            324: [(1, -1), 'Empty', 4.7],
1496            329: [(1, -1), 'Crosses', 9.4],
1497            322: [(.5, -.5), 'Empty', 9.4],
1498            327: [(.5, -.5), 'Crosses', 9.4],
1499            328: [(.5, -.5), 'Crosses', 4.7],
1500            336: [(.5, -.5), 'Plusses', 9.4],
1501            333: [(.75, -.75), 'Crosses', 9.4],
1502            332: [(.3, -.3), 'Crosses', 9.4],
1503            331: [(.1, -.1), 'Crosses', 9.4],
1504        }
1505
1506        lofm = {}
1507        for nr, content in to_show.items():
1508            if content[2] == 4.7:
1509                continue
1510            if not (nr in show_numbers):
1511                continue
1512            leg_str = '\\textbf{%s} $\\;\\mathbf{%s \\rightarrow %s\\,\\mathrm{T}}$'
1513            lofm[nr] = leg_str % (
1514                content[1],   # Structure
1515                content[0][0],# Field from
1516                content[0][1],# Field to
1517                # content[2], # Sweeprate
1518            )
1519
1520        self.m.style.set_style(default=True, grid=True, size='talk',
1521                               style='ticks',
1522                               latex=True, palette='Paired')
1523        fig, ax = plt.subplots(figsize=(16, 12))
1524        for i, j in lofm.items():
1525            label = 'm%s: %s' % (i, j)
1526            if False:
1527                self.eva[i].plot(label=label, ax=ax, color='orange',
1528                                 dont_set_plot_settings=True)
1529            else:
1530                self.eva[i].plot(label=label, ax=ax,
1531                                 dont_set_plot_settings=True)
1532
1533        self.eva[i]._set_plot_settings(xlim=(1e-2, 1.6e0), ylim=(1e-7, 5e-2),
1534                                       title=title,
1535                                       grid=dict(which='minor',
1536                                                 color='#ffff99',
1537                                                 linestyle='--', alpha=.5))
1538        self.eva[i]._draw_oneoverf(ax, ymin=1e-2, xmin=3e-2, alpha=2,
1539                                   plot_style='b--', an_color='blue')
1540        # self.eva[i]._draw_oneoverf(ax, xmin=1e-2, ymin=1e-6)
1541        plt.grid(b=True, which='minor', color='#cccccc', linestyle='-.',
1542                 alpha=.3)
1543
1544        if show_inset:
1545            # Inset with Strayfield
1546            with sns.color_palette('deep'):
1547                if pos2:
1548                    bbox = (.1, .1, .3, .25)
1549                else:
1550                    bbox = (.32, .72, .3, .25)
1551                inset = inset_axes(ax, width='100%', height='100%',
1552                                   bbox_to_anchor=bbox,
1553                                   bbox_transform=ax.transAxes)
1554
1555                self.m.plot_strayfield(inset, 'Strayfield Crosses',
1556                                       nolegend=True,
1557                                       show_plusses=False)
1558                inset.grid(b=True, alpha=.4)
1559                if xlim:
1560                    inset.set_xlim(*xlim)
1561                else:
1562                    inset.set_xlim(-1000, 1000)
1563                inset.set_ylim(-.8, -1.45)
1564
1565                if pos2:
1566                    inset.set_xticks([-300 + 200 * _ for _ in range(4)],
1567                                     minor=True)
1568                    for color, x1 in [((202 / 255, 178 / 255, 214 / 255), 300),
1569                                      ((106 / 255, 61 / 255, 154 / 255), 100)]:
1570                        y1, y2 = -2, 2
1571                        inset.plot([x1, x1, -x1, -x1], [y1, y2, y2, y1],
1572                                   color=color)
1573                # inset.fill([-500, -500, 500, 500], [y1, y2, y2, y1], 'blue', alpha=.1)
1574                # inset.annotate("", xy=(25, .35), xytext=(-25, .2),
1575                #             arrowprops=dict(arrowstyle="->", color='blue'))
1576
1577        plt.savefig('%s.pdf' % filename)
1578
1579    def sv_temp_effect(self, filename='sv-temp-effect',
1580                       show_inset=False):
1581        self.m.style.set_style(default=True, grid=True,
1582                               size='talk', style='ticks', latex=True,
1583                               palette='deep')
1584
1585        lofm = {}
1586        to_show = {
1587            351: [("-M_s \\rightarrow -25", 25), 'Plusses', 1, 30],
1588            355: [("-M_s \\rightarrow -25", 25), 'Plusses', 0.5, 25],
1589            356: [("-M_s \\rightarrow -25", 25), 'Plusses', 0.5, 20],
1590            357: [("-M_s \\rightarrow -25", 25), 'Plusses', 0.5, 15],
1591            358: [("-M_s \\rightarrow -25", 25), 'Plusses', 0.5, 10],
1592            359: [("-M_s \\rightarrow -25", 25), 'Plusses', 0.5, 5],
1593        }
1594
1595        for nr, content in to_show.items():
1596            lofm[nr] = ['$%s\\;\mathrm{K}$' % (
1597                content[3],
1598            ), {}]
1599            # self.eva[nr].Vx *= grad
1600
1601        fig, ax = self.eva.plot(lofm,
1602                                # fit_range=(2e-2, 7e-1),
1603                                # show_fit=True,
1604                                plot_settings=dict(
1605                                    title='',#Temperature Effect',
1606                                    xlim=(1e-2, 1.6e0),
1607                                    ylim=(6e-7, 5e-2)
1608                                ),
1609                                f_settings=dict(disable=True),
1610                                f2_settings=dict(
1611                                    xmin=7e-2
1612                                )
1613                                )
1614
1615        if show_inset:
1616            # Inset with Strayfield
1617            with sns.color_palette('deep'):
1618                inset = inset_axes(ax, width='100%', height='90%',
1619                                   bbox_to_anchor=(.75, .4, .25, .25),
1620                                   bbox_transform=ax.transAxes)
1621                self.m.plot_strayfield(inset, 'Strayfield Plusses',
1622                                       nolegend=True, )
1623                inset.legend(['Up', 'Down'],
1624                             loc='upper right')
1625                inset.grid(b=True, alpha=.4)
1626                inset.set_xlim(-50, 50)
1627                inset.set_ylim(-.65, .45)
1628                inset.set_ylabel('')
1629
1630                y1, y2 = -1, 2
1631                inset.fill([-25, -25, 25, 25], [y1, y2, y2, y1], 'blue', alpha=.1)
1632                inset.annotate("", xy=(25, -.35), xytext=(-25, -.2),
1633                               arrowprops=dict(arrowstyle="->", color='blue'))
1634
1635        # Inset showing fitted data
1636        with sns.color_palette("deep"):
1637            inset2 = inset_axes(ax, width='100%', height='100%',
1638                                bbox_to_anchor=(.5, .75, .3, .25),
1639                                bbox_transform=ax.transAxes)
1640            inset3 = inset_axes(ax, width='100%', height='100%',
1641                                bbox_to_anchor=(.1, .09, .3, .3),
1642                                bbox_transform=ax.transAxes)
1643
1644            i2data = {}
1645            for nr, content in to_show.items():
1646                intercept, slope = self.eva[nr].fit(fit_range=(2e-2, 7e-1))
1647                temp = content[3]
1648                i2data[nr] = {'temp': temp,
1649                              'alpha': -slope,
1650                              'amplitude': 10 ** intercept}
1651
1652                inset2.plot(temp, 10 ** intercept, 'o')
1653                inset3.plot(temp, -slope, 'o')
1654
1655            inset2.set_xlabel('Temperature [$\mathrm{K}$]')
1656            inset2.set_ylabel('$S_{V_H} (f=1\\;$Hz$)$')
1657            inset2.set_yscale('log')
1658            inset2.set_xticks([5 + 10 * _ for _ in range(3)], minor=True)
1659
1660            inset3.set_xlabel('Temperature [$\mathrm{K}$]')
1661            inset3.set_ylabel('$\\alpha$')
1662            inset3.set_xticks([5 + 10 * _ for _ in range(3)], minor=True)
1663
1664        # Only save if necessary
1665        plt.savefig('{}.pdf'.format(filename))
1666        return i2data
1667
1668    def multiple_fspans(self, filename='multiple-fspans'):
1669        self.m.style.set_style(default=True, grid=True,
1670                               size='talk', style='ticks', latex=True,
1671                               palette='Paired')
1672
1673        lofm = {}
1674        to_show = {
1675            340: [(25, -25), 'Empty', 0.46, {}],
1676            349: [("-25", 25), 'Empty', 0.5, {}],
1677            338: [(25, -25), 'Plusses', 0.46, {}],
1678            342: [("+M_s \\rightarrow 25 \\rightarrow 8.3", -25), 'Plusses',
1679                  +0.11, {}],
1680            343: [("-M_s \\rightarrow -25 \\rightarrow -8.3", 25), 'Plusses',
1681                  0.11, {'color': 'red'}],
1682        }
1683
1684        for nr, content in to_show.items():
1685            if content[2] == 9.4:
1686                continue
1687            lofm[nr] = ['$%s \\rightarrow %s$ mT %s ($%s \\frac{mT}{min}$)' % (
1688                content[0][0],
1689                content[0][1],
1690                content[1],
1691                content[2],
1692            ), content[3]]
1693
1694        self.eva.plot(lofm,
1695                      plot_settings=dict(
1696                          title='($90^\\circ$) Field Sweeps ($B = \\pm 25 mT$)',
1697                          xlim=(6e-3, 1.6e0),
1698                          # ylim=()
1699                      ),
1700                      f_settings=dict(disable=True,
1701                                      xmin=5e-2,
1702                                      ymin=1e-5),
1703                      f2_settings=dict(
1704                          xmin=1e-2, )
1705                      )
1706
1707        ax = plt.gca()
1708        with sns.color_palette('Dark2'):
1709            inset = inset_axes(ax, width='100%', height='90%',
1710                               bbox_to_anchor=(.7, .44, .3, .3),
1711                               bbox_transform=ax.transAxes)
1712            self.m.plot_strayfield(inset, 'Strayfield Plusses',
1713                                   show_crosses=False,
1714                                   nolegend=True)
1715            inset.legend(['Up', 'Down'])
1716            inset.grid(b=True, alpha=.4)
1717            s = 30
1718            inset.set_xlim(-s, s)
1719            inset.set_ylim(-.65, .45)
1720            s = 20
1721            inset.set_xticks(np.linspace(-s, s, 5))
1722            inset.set_xticks([-s - 5 + 10 * _ for _ in range(6)], minor=True)
1723
1724            y1, y2 = -1, 2
1725            inset.fill([-25, -25, -8.3, -8.3], [y1, y2, y2, y1], 'red',
1726                       alpha=.2)
1727            inset.fill([25, 25, 8.3, 8.3], [y1, y2, y2, y1], 'green', alpha=.2)
1728            inset.fill([8.3, 8.3, -8.3, -8.3], [y1, y2, y2, y1], 'blue',
1729                       alpha=.05)
1730            # inset.plot([8.3, 8.3], [y1, y2], 'b-.', alpha=.8)
1731
1732        # Only save if necessary
1733        plt.savefig('{}.pdf'.format(filename))
1734
1735    def negative_sweeps(self):
1736        """Testing without file output.
1737
1738        Returns:
1739            None.:
1740        """
1741        self.m.style.set_style(default=True, grid=True,
1742                               size='talk', style='ticks', latex=True,
1743                               palette='Paired')
1744        lofm = {}
1745        to_show = {
1746            340: [(25, -25), 'Empty', 0.46, {}],
1747            338: [(25, -25), 'Plusses', 0.46, {}],
1748            348: [("-M_s \\rightarrow 36.56", -291), 'Empty', 0.5, {}],
1749            347: [("-M_s \\rightarrow 36.56", -291), 'Plusses', 0.5, {}],
1750        }
1751
1752        for nr, content in to_show.items():
1753            if content[2] == 9.4:
1754                continue
1755            options = content[3]
1756            lofm[nr] = ['$%s \\rightarrow %s$ mT %s ($%s \\frac{mT}{min}$)' % (
1757                content[0][0],
1758                content[0][1],
1759                content[1],
1760                content[2],
1761            ), options]
1762
1763        fig, ax = self.eva.plot(lofm,
1764                                plot_settings=dict(
1765                                    title='($90^\\circ$) Field Sweeps (measurement Plan \#2)',
1766                                    xlim=(6e-3, 1.6e0),
1767                                    # ylim=()
1768                                ),
1769                                f_settings=dict(
1770                                    xmin=6e-3,
1771                                    ymin=1e-5))
1772
1773        with sns.color_palette('muted'):
1774            inset = inset_axes(ax, width='100%', height='90%',
1775                               bbox_to_anchor=(.28, .73, .29, .24),
1776                               bbox_transform=ax.transAxes)
1777            self.m.plot_strayfield(inset, 'Strayfield', nolegend=True)
1778            inset.legend(['Up ($-M_S \\rightarrow +M_S$)',
1779                          'Down ($+M_S \\rightarrow -M_S$)'])
1780            inset.grid(b=True, alpha=.4)
1781            inset.set_xlim(-650, 50)
1782            inset.set_ylim(-.65, .45)
1783
1784            y1, y2 = -1, 2
1785            inset.fill([-25, -25, 25, 25], [y1, y2, y2, y1], 'blue', alpha=.1)
1786            inset.fill([-291.13, -291.13, 36.56, 36.56], [y1, y2, y2, y1],
1787                       'green', alpha=.1)
1788            inset.fill([-611, -611, -443, -443], [y1, y2, y2, y1], 'orange',
1789                       alpha=.1)
1790            inset.fill([-291, -291, -443, -443], [y1, y2, y2, y1], 'darkred',
1791                       alpha=.1)
1792
1793    def measplan12(self):
1794        c1 = sns.color_palette("hls", 6)
1795        # sns.color_palette("Reds_r", 14)
1796        self.m.style.set_style(default=True, grid=True,
1797                               size='talk', style='ticks', latex=True,
1798                               palette='deep')
1799        sns.set_palette(c1)
1800        lofm = {}
1801        to_show = {
1802            383: [("+M_s \\rightarrow 25", -25), 'Plusses', +0.5, {}],
1803            384: [("-25", -75), 'Plusses', +0.5,
1804                  {'color': 'orange', 'linestyle': '--'}],
1805        }
1806
1807        for i in range(6):
1808            to_show.update(
1809                {
1810                    (385 + i): [("+M_s \\rightarrow %d" % (-(i * 50 + 25)),
1811                                 (-(i * 50 + 25) - 50)), 'Plusses', +0.5, {}],
1812                }
1813            )
1814
1815        for nr, content in to_show.items():
1816            if content[2] == 9.4:
1817                continue
1818            options = content[3]
1819            lofm[nr] = ['$%s \\rightarrow %s\;\mathrm{mT}$' % (
1820                content[0][0],
1821                content[0][1],
1822            ), options]
1823
1824        fig, ax = self.eva.plot(lofm,
1825                                plot_settings=dict(
1826                                    title='($90^\\circ$) Field Sweeps (Plusses $0.5\;\mathrm{mT}/\mathrm{min}$)',
1827                                    xlim=(1e-2, 1.6e0),
1828                                    ylim=(3e-3, 6e-7)
1829                                ),
1830                                f_settings=dict(
1831                                    xmin=1e-2,
1832                                    ymin=3e-5),
1833                                f2_settings=dict(  # disable=True
1834                                    xmin=2e-2,
1835                                    plot_style='k-.',
1836                                    an_color='k'
1837                                ),
1838                                )
1839
1840        ax = plt.gca()
1841        with sns.color_palette(c1):
1842            inset = inset_axes(ax, width='100%', height='90%',
1843                               bbox_to_anchor=(.4, .73, .29, .24),
1844                               bbox_transform=ax.transAxes)
1845            self.m.plot_strayfield(inset, 'Strayfield', nolegend=True)
1846            inset.legend(['Up ($-M_S \\rightarrow +M_S$)',
1847                          'Down ($+M_S \\rightarrow -M_S$)'])
1848            inset.grid(b=True, alpha=.4)
1849            inset.set_xlim(-650, 50)
1850            inset.set_ylim(-.65, .45)
1851
1852            y1, y2 = -1, 2
1853            for j in [25] + [-25 - 50 * i for i in range(
1854                    5)]:  # + [-300-50*i for i in range(5)] + [-575]:
1855                inset.fill([j, j, j - 50, j - 50], [y1, y2, y2, y1], alpha=.4)
1856            # for j in [-300-50*i for i in range(5)] + [-575]:
1857            #    inset.fill([j, j, j-50, j-50], [y1, y2, y2, y1], alpha=.4)
1858        ax.annotate('Without Saturation', (1.2e-2, 1e-3), color='orange',
1859                    rotation=-55)
1860        ax.annotate('With Saturation', (1.01e-2, .8e-3), color='yellow',
1861                    rotation=-37)
1862
1863        plt.savefig('self.measplan12-1.pdf')
1864
1865    def measplan12_2(self):
1866        c1 = sns.color_palette("hls", 7)
1867        # sns.color_palette("Reds_r", 14)
1868        self.m.style.set_style(default=True, grid=True,
1869                               size='talk', style='ticks', latex=True,
1870                               palette='deep')
1871        sns.set_palette(c1)
1872        lofm = {}
1873        to_show = {}
1874        for i, j in enumerate([-(_ * 50 + 300) for _ in range(5)] + [-575]):
1875            to_show.update(
1876                {
1877                    (391 + i): [("+M_s \\rightarrow %d" % (j), (j - 50)),
1878                                'Plusses', +0.5, {}],
1879                }
1880            )
1881
1882        j = -625
1883        to_show[397] = [("-M_s \\rightarrow %d" % (j), (j + 50)), 'Plusses',
1884                        +0.5, {}]
1885
1886        for nr, content in to_show.items():
1887            if content[2] == 9.4:
1888                continue
1889            options = content[3]
1890            lofm[nr] = ['$%s \\rightarrow %s\;\mathrm{mT}$' % (
1891                content[0][0],
1892                content[0][1],
1893            ), options]
1894
1895        fig, ax = self.eva.plot(lofm,
1896                                plot_settings=dict(
1897                                    title='($90^\\circ$) Field Sweeps (Plusses $0.5\;\mathrm{mT}/\mathrm{min}$)',
1898                                    xlim=(1e-2, 1.6e0),
1899                                    ylim=(2e-4, 2e-7)
1900                                ),
1901                                f_settings=dict(
1902                                    xmin=1e-2,
1903                                    ymin=2e-5),
1904                                f2_settings=dict(  # disable=True
1905                                    xmin=1e-2,
1906                                    plot_style='k-.',
1907                                    an_color='k',
1908                                    disable=True,
1909                                ),
1910                                )
1911
1912        with sns.color_palette(c1):
1913            inset = inset_axes(ax, width='100%', height='90%',
1914                               bbox_to_anchor=(.4, .73, .29, .24),
1915                               bbox_transform=ax.transAxes)
1916            self.m.plot_strayfield(inset, 'Strayfield Plusses', nolegend=True)
1917            inset.legend(['Up ($-M_S \\rightarrow +M_S$)',
1918                          'Down ($+M_S \\rightarrow -M_S$)'])
1919            inset.grid(b=True, alpha=.4)
1920            inset.set_xlim(-650, 50)
1921            inset.set_ylim(-.65, .45)
1922
1923            y1, y2 = -1, 2
1924            for j in [-300 - 50 * i for i in range(5)] + [-575]:
1925                inset.fill([j, j, j - 50, j - 50], [y1, y2, y2, y1], alpha=.4)
1926
1927        plt.savefig('self.measplan12-2.pdf')
1928
1929    def measplan12_full(self, name='measplan12-full'):
1930        """measurement Plan #12 All 14 measurements in one Graph.
1931
1932        Args:
1933            name:
1934        """
1935        # c1 = sns.color_palette("twilight", 14)
1936        c1 = sns.color_palette("hls", 14)
1937        self.m.style.set_style(default=True, grid=True,
1938                               size='talk', style='ticks', latex=True)
1939        sns.set_palette(c1)
1940
1941        # Choosing measurement Numbers to plot
1942        lofm = {}
1943        to_show = {
1944            383: [("+M_s \\rightarrow 25", -25), 'Plusses', +0.5, {}],
1945            384: [("-25", -75), 'Plusses', +0.5,
1946                  {'color': 'orange', 'linestyle': '--'}],
1947        }
1948
1949        for i in range(6):
1950            to_show.update(
1951                {
1952                    (385 + i): [("+M_s \\rightarrow %d" % (-(i * 50 + 25)),
1953                                 (-(i * 50 + 25) - 50)), 'Plusses', +0.5, {}],
1954                }
1955            )
1956
1957        for i, j in enumerate([-(_ * 50 + 300) for _ in range(5)] + [-575]):
1958            to_show.update(
1959                {
1960                    (391 + i): [("+M_s \\rightarrow %d" % (j), (j - 50)),
1961                                'Plusses', +0.5, {}],
1962                }
1963            )
1964
1965        j = -625
1966        to_show[397] = [("-M_s \\rightarrow %d" % (j), (j + 50)), 'Plusses',
1967                        +0.5, {}]
1968
1969        # Converting them to a list of measurements (lofm)
1970        for nr, content in to_show.items():
1971            if content[2] == 9.4:
1972                continue
1973            options = content[3]
1974            lofm[nr] = ['$%s \\rightarrow %s\;\mathrm{mT}$' % (
1975                content[0][0],
1976                content[0][1],
1977            ), options]
1978
1979        fig, ax = self.eva.plot(lofm,
1980                                plot_settings=dict(
1981                                    title='($90^\\circ$) Field Sweeps (Plusses;' + \
1982                                          ' $\\Delta B = 50\\mathrm{mT}$;' + \
1983                                          ' $0.5\\;\\mathrm{mT}/\\mathrm{min}$)',
1984                                    xlim=(1e-2, 1.6e0),
1985                                    ylim=(5e-2, 5e-8),
1986                                    legend_settings=dict(loc='best', ncol=2)
1987                                ),
1988                                f_settings=dict(
1989                                    xmin=1e-2,
1990                                    ymin=3e-5),
1991                                f2_settings=dict(  # disable=True
1992                                    xmin=2e-2,
1993                                    plot_style='k-.',
1994                                    an_color='k'
1995                                ),
1996                                )
1997
1998        with sns.color_palette(c1):
1999            inset = inset_axes(ax, width='100%', height='100%',
2000                               bbox_to_anchor=(.07, .06, .34, .26),
2001                               bbox_transform=ax.transAxes)
2002            self.m.plot_strayfield(inset,
2003                                   'Strayfield Plusses',
2004                                   show_plusses=False,
2005                                   show_crosses=False,
2006                                   nolegend=True)
2007
2008            y1, y2 = -1, 2
2009            for j in [25 - 50 * i for i in range(6)] + \
2010                     [-300 - 50 * i for i in range(5)] + [-575]:
2011                inset.fill([j, j, j - 50, j - 50], [y1, y2, y2, y1], alpha=.8)
2012
2013            inset.plot(self.m.up_fitted.B, self.m.up_fitted.Bx8, 'b-',
2014                       linewidth=1.5, label='Up')
2015            inset.plot(self.m.down_fitted.B, self.m.down_fitted.Bx8, 'r-',
2016                       linewidth=3, label='Down')
2017
2018            inset.legend(loc='best')
2019
2020            inset.grid(b=True, alpha=.4)
2021            inset.set_xlim(-650, 50)
2022            inset.set_ylim(-.65, .45)
2023            inset.set_xlabel('')
2024            inset.set_ylabel('')
2025
2026        ax.annotate('Without Saturation', (1.2e-2, 5e-4), color='orange',
2027                    rotation=-55)
2028        ax.annotate('With Saturation', (1.01e-2, 4e-4), color='orange',
2029                    rotation=-35)
2030
2031        plt.savefig('%s.pdf' % name)
2032
2033    def set_size(self, width_pt, fraction=1, subplots=(1, 1)):
2034        """Set figure dimensions to sit nicely in our document.
2035        
2036        Source: https://jwalton.info/Matplotlib-latex-PGF/
2037
2038        Parameters
2039        ----------
2040        width_pt: float
2041                Document width in points
2042        fraction: float, optional
2043                Fraction of the width which you wish the figure to occupy
2044        subplots: array-like, optional
2045                The number of rows and columns of subplots.
2046        Returns
2047        -------
2048        fig_dim: tuple
2049                Dimensions of figure in inches
2050        """
2051        # Width of figure (in pts)
2052        fig_width_pt = width_pt * fraction
2053        # Convert from pt to inches
2054        inches_per_pt = 1 / 72.27
2055
2056        # Golden ratio to set aesthetic figure height
2057        golden_ratio = (5 ** .5 - 1) / 2
2058
2059        # Figure width in inches
2060        fig_width_in = fig_width_pt * inches_per_pt
2061        # Figure height in inches
2062        fig_height_in = fig_width_in * golden_ratio * (subplots[0] / subplots[1])
2063
2064        return (fig_width_in, fig_height_in)

HandleM

  1class HandleM(MultipleM):
  2    def __init__(self, *args, **kwargs):
  3        """
  4        Args:
  5            args:
  6            kwargs:
  7
  8        Note:
  9            **Measurement Handler:** This class represents all other
 10            measurements and is the interface to other measurement objets.
 11        """
 12        super().__init__()
 13        self.logger = logging.getLogger('Handle')
 14        self.data = {}
 15        self.info = pd.DataFrame()
 16        self.info_dict = {}
 17        self.sa_dict = {}
 18        self.hloop_dict = {}
 19        if kwargs.get('data'):
 20            data = kwargs.get('data')
 21            for nr, d in data:
 22                data.update(
 23                    self._process_data(nr, d['data'], d['info']))
 24            self.parse_data(data, **kwargs)
 25        elif kwargs.get('directory'):
 26            self.load_files(kwargs.pop('directory'), **kwargs)
 27
 28    def __getitem__(self, key):
 29        """
 30        Args:
 31            key:
 32        """
 33        if isinstance(key, str):
 34            if key[1] == 'm':
 35                d = self.data.get(float(key[1:]))
 36                if not d:
 37                    raise AttributeError("Measurement Nr. %s not found" % key)
 38                else:
 39                    return d
 40        else:
 41            d = self.data.get(float(key))
 42            if not d:
 43                raise AttributeError("Measurement Nr. %s not found" % key)
 44            else:
 45                return d
 46        return self.query('Nr == %f' % key)
 47
 48    def __setitem__(self, key, val):
 49        """
 50        Args:
 51            key:
 52            val:
 53        """
 54        self.logger.warning("Setting Items is not allowed.")
 55        return
 56
 57    def __contains__(self, key):
 58        """
 59        Args:
 60            key:
 61        """
 62        return (key in self.data)
 63
 64    def load_files(self, directory, **kwargs):
 65        """Loads all files from a specific directory and creates objects for
 66        found measurements.
 67
 68        Args:
 69            directory (str): directory to search in
 70            **kwargs:
 71
 72        Returns:
 73            None
 74        """
 75        file_list = glob(os.sep.join([directory, '**/*.dat']), recursive=True)
 76        if file_list:
 77            self.load_folder(file_list, **kwargs)
 78        else:
 79            self.logger.error('No Files found: %s' % file_list)
 80
 81    def get_custom_header(self, kind='RAW',
 82                          instruments=None):
 83        """Help Function to get column names for EVE Measurements.
 84
 85        Args:
 86            kind (str, optional): Kind of measurement, default: 'RAW'
 87            instruments (list, optional): Used instruments in order of
 88                appearance, default: ['t', 'LS', 'IPS', 'SR830']
 89
 90        Returns:
 91            tuple: (list header, int skiprows)
 92        """
 93        if not instruments:
 94            instruments = ['t', 'LS', 'IPS', 'SR830']
 95
 96        if kind == 'RAW':
 97            return ['Time', "Vx", "Vy"], 0
 98        elif kind == 'SA':
 99            return ['f', 'Vx', 'Vy'], 6
100
101        def_skiprows = 3
102        def_header = []
103        for k in instruments:
104            if k == 't':
105                def_header += ["Time", ]
106            if k == 'LS':
107                def_header += ["Temp%s" % i for i in range(6)]
108            if k == 'IPS':
109                def_header += ["B", ]
110            if k == 'SR830':
111                def_header += [
112                    "Vx", "Vy", "Vr", "Vth",
113                    #                   ["V%s%d" % (var, gpib) for gpib in [8, 9, 13]
114                    #                   for var in ['x', 'y', 'r', 'th',]]
115                ]
116        return def_header, def_skiprows
117
118    def load_folder(self, file_list, **kwargs):
119
120        """
121        Args:
122            file_list:
123            **kwargs:
124        """
125        self.logger.warning('Start loading folder: %s' % file_list[0])
126        d_dict = dict()
127
128        for f in file_list:
129            directory, filename = f.split(os.sep)[-2:]
130            regex = re.match(
131                '(.*)[mM]([0-9.]+)(.*)([Pp]lusses|[Cc]rosses|[Ee]mpty)(.*).dat',
132                filename)
133            if not regex:
134                regex = re.match('(.*)[mM]([0-9.]+)(.*).dat', filename)
135                if not regex:
136                    self.logger.warning("Regex doesn't match: %s" % f)
137                    continue
138                else:
139                    folder, nr, add_info = regex.groups()
140                    struct = None
141                    m_type = 'NA'
142                    struct = 'NA'
143            else:
144                folder, nr, m_type, struct, add_info = regex.groups()
145
146            try:
147                nr = float(nr)
148            except Exception:
149                self.logger.error('Not a valid Measurement Nr. (%s)\n' % nr + \
150                                  'Debug kind/struct/f (%s, %s, %s)' % (
151                                      m_type, struct, f))
152                continue
153
154            if nr == int(nr):
155                nr = int(nr)
156                def_header, def_skiprows = self.get_custom_header('SA')
157            elif 'SR830' in add_info:
158                def_header, def_skiprows = self.get_custom_header('RAW')
159            else:
160                def_header, def_skiprows = self.get_custom_header(
161                    instruments=['t', 'LS', 'IPS', 'SR830'])
162
163            data = pd.read_csv(f, sep=kwargs.get('sep', '\t'),
164                               # header=kwargs.get('header', def_header_index),
165                               names=kwargs.get('names', def_header),
166                               skiprows=kwargs.get('skiprows', def_skiprows))
167
168            if int(nr) == nr:
169                nr = int(nr)
170                subnr = False
171            else:
172                i, subnr = str(nr).split('.')
173
174            info = {'type': 'SA',
175                    'Nr': nr,
176                    'filename': f.split(os.sep)[-1],
177                    'folder': folder,
178                    'technique': m_type.strip('_'),
179                    'Structure': struct,
180                    'Additional': add_info.strip('_')}
181            if subnr:
182                info['type'] = 'RAW'
183                info['subnr'] = subnr
184
185            d_dict.update(self._process_data(nr, data, info))
186            if kwargs.get('drop_unused_columns', True):
187                if 'Temp0' in def_header:
188                    d_dict[nr]['data'] = d_dict[nr]['data'].drop(
189                        ["Temp%s" % i for i in range(6)] + \
190                        ["Vr", "Vth"],
191                        axis=1,
192                    )
193        self.parse_data(d_dict)
194
195    def parse_data(self, data, **kwargs):
196        """imports data and sets info variable.
197
198        .. code-block:: python
199
200           h.parse_data({'data': pd.DataFrame,
201               'info': dict(Nr=0, ** kw) })
202
203        Args:
204            data (dict): Data to parse
205            **kwargs:
206
207        Returns:
208            None
209        """
210        self.logger.debug('Start parsing data: %s' % data.items())
211        for nr, df in data.items():
212            if not df.get('info'):
213                continue
214            m_type = df['info'].get('type', 'Unknown')
215            self.info_dict.update({nr: df['info']})
216            if df['info'].get('Structure'):
217                kwargs['Structure'] = df['info']['Structure']
218            if df['info'].get('Angle'):
219                kwargs['Angle'] = df['info']['Angle']
220            if df['info'].get('Additional'):
221                kwargs['Additional'] = df['info']['Additional']
222
223            if m_type == 'SA':
224                self.data[nr] = SA(df)
225                self.sa_dict[nr] = self.data[nr]
226            elif m_type == 'Hloop':
227                self.data[nr] = Hloop(nr, down_only=True,
228                                      **kwargs)
229                self.hloop_dict[nr] = self.data[nr]
230            elif m_type == 'RAW':
231                self.data[nr] = RAW(df, **kwargs)
232                self.hloop_dict[nr] = self.data[nr]
233            else:
234                self.logger.error(
235                    'Measurement %s (type: %s) not processed (unknown type).' % (
236                        nr, m_type))
237
238        self.info = pd.DataFrame(self.info_dict).T
239        # self.info.set_index('Nr', inplace=True)
240        self.logger.debug('Parsing Measurement Finished\n' + \
241                          'Debug size data/info (%s, %s)' % (len(self.data),
242                                                             self.info.shape))
243
244    def _process_data(self, nr, data, info):
245        """
246        Args:
247            nr:
248            data:
249            info:
250        """
251        self.logger.info('%s\n%s' % (data, info))
252
253        # Logging statistical values for info
254        for key in data.keys():
255            # Converting Data to numeric
256            if data[key].dtype == object:
257                try:
258                    data[key] = pd.to_numeric(data[key], errors='coerce')
259                except Exception:
260                    self.logger.error('Not Numeric column: %s/%s' % (nr, key))
261
262            info['%s_count' % key] = data[key].count()
263            info['%s_mean' % key] = data[key].mean()
264            info['%s_var' % key] = data[key].var()
265            info['%s_min' % key] = data[key].min()
266            info['%s_max' % key] = data[key].max()
267
268        d_dict = {nr: {
269            'data': data,
270            'info': info
271        }}
272        return d_dict
273
274    def plot(self, *args, **kwargs):
275        """Plotting a Measurement
276
277        Args:
278            lofm: list of measurements
279            figsize: tuple size of figure
280            show_fit: bool
281            fit_range: tuple/list
282            plot_settings: list
283            grid_options: list
284            f_settings: 1/f line
285            f2_settings: 1/f^2 line
286            
287
288        Returns:
289            tuple: Figure, Axis
290
291        Raises:
292            warning: lofm not available.
293            KeyError: Measurement does not exist.
294        """
295        lofm = kwargs.get('lofm', args[0])
296        if not (lofm):
297            self.logger.warning("No list of measurements (lofm) available.")
298            return
299
300        fig, ax = plt.subplots(figsize=kwargs.get('figsize', (16, 12)))
301        for i, j in lofm.items():
302            if i not in self.data:
303                raise KeyError(i)
304
305            label = 'm%s: %s' % (i, j[0])
306            plot_options = j[1]
307
308            if kwargs.get('fit_range'):
309                intercept, slope = self.data[i].fit(kwargs.get('fit_range'))
310                x = np.linspace(*kwargs.get('fit_range'))
311                if kwargs.get('show_fit'):
312                    ax.plot(x, (10 ** intercept * np.power(x, slope)),
313                            label='Fit m%s' % i)
314                if kwargs.get('show_fit_label'):
315                    label += ' $\\alpha = %.3f, S_R(f=0) = 10^{%.3f}$' % (
316                        slope, intercept)
317
318            if kwargs.get('norm_S'):
319                self.data[i].df['norm'] = self.data[i].df['Vx'] * \
320                                          self.data[i].df['f']
321                plot_options['plot_y'] = 'norm'
322
323            self.data[i].plot(label=label, ax=ax,
324                              dont_set_plot_settings=True,
325                              **plot_options)
326
327        tmp = kwargs.get('plot_settings', dict())
328        if not (tmp):
329            tmp = {}
330        plot_settings = dict(
331            xlim=(1e-2, 1e0),
332            ylim=(1e-7, 5e-2),
333            title='($90^\circ$) Field sweeps',
334            grid=dict(
335                which='minor',
336                color='#ffff99',
337                linestyle='--',
338                alpha=.5))
339        plot_settings.update(tmp)
340        self.data[i]._set_plot_settings(**plot_settings)
341
342        grid_options = dict(
343            b=True,
344            which='minor',
345            color='#cccccc',
346            linestyle='-.',
347            alpha=.3)
348        grid_options.update(kwargs.get('grid_options', dict()))
349        plt.grid(**grid_options)
350
351        # Draw 1 over f lines
352        f_settings = dict(xmin=1e-2, ymin=1e-6)
353        f_settings.update(kwargs.get('f_settings', {}))
354        f2_settings = dict(ymin=1e-3, alpha=2,
355                           plot_style='b--', an_color='blue')
356        f2_settings.update(kwargs.get('f2_settings', {}))
357
358        self.data[i]._draw_oneoverf(ax, **f_settings)
359        self.data[i]._draw_oneoverf(ax, **f2_settings)
360
361        return fig, ax
362
363    def __repr__(self):
364        ret = 'HandleM\n'
365        if not self.data:
366            return ret + 'Empty'
367
368        for nr, info in self.info_dict.items():
369            ret += 'Nr. %s:\t Type: %s\t Structure: %s\tTechnique: %s\n' % (
370                nr,
371                info['type'],
372                info['Structure'],
373                info['technique'])
374        return ret
375
376    def get_info(self):
377        """Returns information about all measurements 
378        and splits it into groups (SA/RAV/MFN/VH)."""
379        
380        info = self.info
381        sa = info[info['type'] == 'SA']
382        raw = info[info['type'] == 'RAW']
383        mfn = info[info['technique'] == 'MFN']
384        vh = info[info['technique'] == 'VH']
385
386        groups = dict(vh=vh, mfn=mfn, sa=sa, raw=raw)
387
388        return info, groups

MFN

   1class MFN(MultipleM):
   2    def __init__(self, files, **kwargs):
   3        r"""Magnetic Flux Noise Measurements.
   4        Class to handle and plot MFN Measurements
   5        taken with the SR830 DAQ.
   6
   7        if a measurement number is given the files are 
   8        loaded from folder:
   9                ``data/MFN/m%s/*.dat``
  10        
  11        else: files must be a list of filenames:
  12
  13            .. code:: python
  14               :linenos:
  15               :caption: Example
  16
  17               arg = glob('data/MFN/mXXX/m[0-9.]*.dat')
  18               mfn = MFN(arg)
  19
  20        :param files: list of files or measurement number.
  21        :param kwargs: Different additional parameters.
  22        :param int nr: Measurement number
  23           (default: None).
  24        :param bool calc_first: Calculating first spectrum
  25           (default: True).
  26        :param bool calc_second: Calculating second spectrum
  27           (default: False).
  28        :param bool downsample: downsample timesignal for first spectrum
  29           (default: False).
  30        :param int num_first_spectra: Number of spectra to average
  31           (default: 64).
  32        :param float timeconstant: Lock-In timeconstant to cut first spectrum
  33            at corresponding frequency (see ``cut_fmax``).
  34        :param float cut_fmax: Cut first spectrum at this frequency
  35           (default: :math:`f_{max} = \frac{\tau}{2\pi}`).
  36        :param default_cm: matplotlib color map for contour plots
  37           (default: style['default_cm']).
  38        :type default_cm: matplotlib.cm
  39        """
  40        super().__init__()
  41        self.name = 'Magnetic Flux Noise'
  42        self.default_cm = kwargs.get('default_cm', self.style.get('default_cm'))
  43        self.style['default_cm'] = self.default_cm
  44
  45        self.info = {
  46            'Nr': kwargs.get('nr'),
  47            'first': kwargs.get('calc_first', True),
  48            'second': kwargs.get('calc_second', False),
  49            'num': kwargs.get('num_first_spectra', 64),
  50            'downsample': kwargs.get('downsample', False),
  51            'length': kwargs.get('length'),
  52        }
  53
  54        if isinstance(files, int):
  55            self.info['Nr'] = files
  56            files = glob('data/MFN/m%s/*' % self.info['Nr'])
  57
  58        self.logger = logging.getLogger('MFNMeasurement (%s)' %
  59                                        self.info['Nr'])
  60
  61        self.data, self.measurements = self.load_meas(files, **kwargs)
  62
  63        # Stop here if we have no data
  64        if len(self.data) == 0:
  65            return
  66
  67        # Cut too long measurements
  68        if kwargs.get('equalize_length', True):
  69            self.__equalize_length()
  70
  71        self.info['timeconstant'] = kwargs.get('timeconstant')
  72
  73        # Cut higher frequencies if timeconstant is known
  74        fmax_rate = self.info.get('rate') / (2 * np.pi) if self.info.get('rate') else 1e6
  75        fmax_tc = 1 / (2 * np.pi * self.info['timeconstant']) if self.info['timeconstant'] else 1e6
  76        fmax = np.min([fmax_rate, fmax_tc])
  77        self.info['fmax'] = fmax
  78        if fmax < 1e6 or kwargs.get('cut_fmax'):
  79            self.cut_fmax(kwargs.get('cut_fmax', fmax))
  80
  81    def load_meas(self, files, **kwargs):
  82        """
  83        Loading a single magnetic flux noise DAQ measurement.
  84        
  85        Files in measrument folder contain multiple measurements
  86        with one measurement per field.
  87
  88        :param files: files to load
  89        
  90        :return: DataFrame Data, dict Measurement Objects
  91        :rtype: tuple
  92        """
  93
  94        max_len = 0
  95        meas = {}
  96        all_data = pd.DataFrame({'Field': [], 'Time': [], 'Vx': [], 'Vy': []})
  97        for f in files:
  98            info_dict = self.get_info_from_name(f)
  99
 100            field = info_dict.get('field')
 101            nr = info_dict.get('nr')
 102
 103            if not self.info.get('Nr'):
 104                self.info['Nr'] = nr
 105
 106            data_df = pd.read_csv(f, sep='\t')
 107            if data_df.empty:
 108                continue
 109
 110            if len(data_df['Vx']) % 1024:
 111                d = data_df['Vx'].iloc[:-(len(data_df['Vx']) % 1024)]
 112            else:
 113                d = data_df.Vx
 114
 115            max_len = len(d)
 116
 117            data = {
 118                'data': d,
 119                'info': {
 120                    'Nr': nr,
 121                    'field': field,
 122                    'rate': 1 / data_df.time.diff().mean(),
 123                    'length': max_len * data_df.time.diff().mean(),
 124                }
 125            }
 126
 127            if not self.info['length']:
 128                self.info['length'] = max_len * data_df.time.diff().mean()
 129            if not self.info.get('rate'):
 130                self.info['rate'] = 1 / data_df.time.diff().mean()
 131
 132            # Calculate the first and second spectrum
 133            meas[field] = self._get_raw_meas(data, **kwargs)
 134
 135            if meas[field] == None:
 136                meas.pop(field)
 137
 138            tmp_df = pd.DataFrame(
 139                {'Field': field,
 140                 'Time': data_df.time.iloc[:max_len],
 141                 'Vx': data_df.Vx.iloc[:max_len] * kwargs.get('factor', 1e3),
 142                 'Vy': data_df.Vy.iloc[:max_len] * kwargs.get('factor', 1e3),
 143                 })
 144            all_data = pd.concat([all_data, tmp_df])  # , how='outer')
 145
 146        return all_data, meas
 147
 148    def _get_raw_meas(self, data, **kwargs):
 149        """
 150        Returns the RAW Measurement.
 151        :param dict data: time signal and info
 152        :param bool calculate_second_by_hand: if we calc
 153        :return: RAWMeasurement
 154        """
 155        if len(data['data']) == 0:
 156            return None
 157
 158        ret = RAW(data,
 159                  rate=data['info']['rate'],
 160                  nof_first_spectra=self.info.get('num', 64),
 161                  calc_first=self.info['first'],
 162                  calc_second=self.info['second'],
 163                  downsample=self.info['downsample'],
 164                  **kwargs.get('raw_kwargs', dict())
 165                  )
 166
 167        if kwargs.get('calculate_second_by_hand', False):
 168            ret.calc_second_spectrum(
 169                highest_octave=kwargs.get(
 170                    'highest_octave', ret.avg_spec.freq.max()),
 171                minimum_points_in_octave=kwargs.get(
 172                    'minimum_points_in_octave', 3),
 173                peak_filter=kwargs.get(
 174                    'peak_filter', False),
 175            )
 176
 177        return ret
 178
 179    def cut_fmax(self, fmax):
 180        """Cut frequencies higher than maximum allowed frequencies.
 181
 182        Args:
 183            fmax:
 184        """
 185        for field, spec in self.measurements.items():
 186            # Drop all frequencies larger than fmax
 187            s = spec.spectrum.first_spectra_df
 188            s.drop(s.query('Frequency > %s' % fmax).index.tolist(),
 189                   axis=0, inplace=True
 190                   )
 191            freq = spec.spectrum.frequency_span_array
 192            freq = freq[freq <= fmax]
 193            spec.spectrum.frequency_span_array = freq
 194
 195            # Reset First Spectrum Variables
 196            spec.spectrum.finalize_first_spectrum(s)
 197            spec.avg_spec = pd.DataFrame({
 198                'freq': freq,
 199                'S': spec.spectrum.first_spectrum_avg.loc[:]  # copy
 200            })
 201
 202    def __equalize_length(self):
 203        """
 204        Check that data for all fields have same amount of data.
 205        :return:
 206        """
 207        # Check if all fields have same amount of data
 208        nofvalues = self.data.groupby('Field').count()
 209        if nofvalues.Vx.max() - nofvalues.Vx.min() > 0:
 210            fields_with_more_data = nofvalues[
 211                nofvalues.Vx > nofvalues.Vx.median()].index.tolist()
 212            fields_with_less_data = nofvalues[
 213                nofvalues.Vx < nofvalues.Vx.median()].index.tolist()
 214            self.logger.warning(
 215                'Not all fields have same amount of data (median = %s):\n' % (
 216                    nofvalues.Vx.quantile(.5))
 217                + 'Fields with more data: %s\n' % fields_with_more_data
 218                + 'Fields with less data: %s\n' % fields_with_less_data
 219                + '%s\n' % nofvalues.Vx.describe()
 220                + 'To avoid data loss use the option "equalize_length=False"!')
 221
 222            for field in self.data.Field.unique():
 223                condition = 'Field == %s' % field
 224                if field in fields_with_more_data:
 225                    # Cut data
 226                    cut_data = self.data.query(condition).iloc[
 227                               :int(nofvalues.Vx.median())]
 228                    self.data.drop(
 229                        self.data.query(condition).index.tolist(),
 230                        axis=0, inplace=True)
 231                    self.data = pd.merge(self.data, cut_data, how='outer')
 232
 233                    if self.data.query(condition).shape[0] > 0:
 234                        # recalculate Spectrum
 235                        rate = 1 / self.data.query(condition).Time.diff().median()
 236                        self.measurements[field] = self._get_raw_meas(
 237                            {'data': self.data.query(condition).Vx.to_numpy(),
 238                             'info': {
 239                                 'Nr': self.info['Nr'],
 240                                 'field': field,
 241                                 'rate': rate,
 242                             }
 243                             },
 244                        )
 245                    else:
 246                        # Remove field from measurements
 247                        self.measurements.pop(field)
 248                        self.data.drop(
 249                            self.data.query(condition).index.tolist(),
 250                            axis=0, inplace=True)
 251
 252                    self.logger.error(
 253                        'This field (%s) has more data '
 254                        '(%s) than median (%s): Cutting data!' % (
 255                            field,
 256                            nofvalues.Vx.loc[field],
 257                            nofvalues.Vx.median()
 258                        )
 259                    )
 260
 261                if field in fields_with_less_data:
 262                    self.logger.error(
 263                        'This field (%s) has less data '
 264                        '(%s) than median (%s): Deleting field from data!' % (
 265                            field,
 266                            nofvalues.Vx.loc[field],
 267                            nofvalues.Vx.median()
 268                        )
 269                    )
 270                    self.data.drop(
 271                        self.data.query(condition).index,
 272                        inplace=True)
 273                    self.measurements.pop(field)
 274
 275    def plot_first_and_second(self, **kwargs):
 276        """
 277        Args:
 278            **kwargs:
 279        """
 280        sns.set_palette(sns.color_palette("hls", len(self.measurements)))
 281        fig, (ax1, ax2) = plt.subplots(nrows=2, ncols=1,
 282                                       figsize=(11.69, 8.27), )
 283
 284        fields = kwargs.get('fields', self.data.sort_values(
 285            'Field')['Field'].unique())
 286        for field in fields:
 287            # Setting shortcuts
 288            try:
 289                meas = self.measurements[field]
 290            except KeyError:
 291                continue
 292
 293            s = meas.avg_spec
 294
 295            ax1.plot(field, s.S.sum(), 'o')
 296
 297            if hasattr(meas.spectrum, 'second_spectra'):
 298                marker = "+x*ds"
 299                color = "rbgyk"
 300                for octave in range(
 301                        meas.spectrum.number_of_octaves):
 302                    second = meas.spectrum.second_spectra[octave]
 303                    ax2.plot(field,
 304                             second.sum(),
 305                             "%s%s" % (color[octave], marker[octave])
 306                             )
 307
 308        ax2.set_title('$\\sum$ Second Spectrum')
 309        ax2.set_ylabel('$S$ [a.u.]')
 310
 311        # Legend for second spectrum
 312        field = fields[-1]
 313        labels = []
 314        if hasattr(self.measurements[field].spectrum, 'second_spectra'):
 315            for octave in range(
 316                    self.measurements[field].spectrum.number_of_octaves):
 317                labels.append('%s Octave' % (octave + 1))
 318        ax2.legend(labels)
 319
 320        ax1.set_title(
 321            'm%s: $\\sum$ First Spectrum Noise' % self.info['nr'])
 322        ax1.set_ylabel(
 323            '$\\langle S_V \\rangle$ [$\\mathrm{V}^2/\\mathrm{Hz}$]')
 324
 325        for ax in [ax2, ax1]:
 326            ax.set_yscale('log')
 327        plt.show()
 328
 329    def plot_alpha(self, field, s, ax=None, **kwargs):
 330        """
 331        Fits a spectrum and plots the slope alpha.
 332        :param s: pandas.DataFrame of the spectrum
 333        :param ax: axis where to plot the data.
 334        :return: linear regression fit
 335        """
 336        if not ax:
 337            fig, ax = plt.subplots()
 338
 339        # Fitting alpha
 340        s['lnf'] = np.log10(s.freq)
 341        s['lnS'] = np.log10(s.S)
 342
 343        fit_indices = s.freq < kwargs.get('fit_range', 0.7)
 344
 345        f = scipy.stats.linregress(
 346            s.lnf.loc[fit_indices],
 347            s.lnS.loc[fit_indices])
 348
 349        plt_obj, = ax.plot(field, -f.slope, kwargs.get('alpha_symbol', 'o'), label='$\\alpha$')
 350
 351        return f, plt_obj
 352
 353    def plot_info2(self, **kwargs):
 354        """
 355        Args:
 356            **kwargs:
 357        """
 358        if kwargs.get('fig'):
 359            fig = kwargs.get('fig')
 360            (ax11, ax12) = kwargs.get('axes')
 361        else:
 362            fig, (ax11, ax12) = plt.subplots(nrows=2, ncols=1,
 363                                             figsize=(5, 10), )
 364        fields = kwargs.get('fields',
 365                            self.data.sort_values('Field')['Field'].unique())
 366        self.plot_noise_chararcteristics(ax11, fields=fields)
 367        self.spectra_field_contour(ax12, fields=fields)
 368
 369    def plot_noise_chararcteristics(self, ax=None, fields=None, **kwargs):
 370        """
 371        Args:
 372            ax:
 373            fields:
 374            **kwargs:
 375        """
 376        if not ax:
 377            fig, ax = plt.subplots()
 378        fields = kwargs.get('fields', self.data.sort_values('Field')['Field'].unique())
 379
 380        par2 = ax.twinx()
 381
 382        spectra_df = pd.DataFrame()
 383        for i, field in enumerate(fields):
 384            # Setting shortcut for Average Spectrum s
 385            try:
 386                meas = self.measurements[field]
 387                if meas == None or len(meas.avg_spec) < 2:
 388                    continue
 389            except KeyError:
 390                continue
 391
 392            s = meas.avg_spec
 393
 394            spectra_df['S%s' % field] = s.S
 395
 396            f, alpha = self.plot_alpha(field, s, par2, alpha_symbol="bo",
 397                                       **kwargs)
 398            meas.info['alpha'] = -f.slope,
 399            meas.info['power'] = f.intercept
 400
 401            integral, = ax.plot(field, s.S.sum() * s.freq.diff().iloc[1],
 402                                'gd', label='Integral')
 403            intercept, = ax.plot(field, 10 ** f.intercept, 'r*',
 404                                 label='$S(f=1~\\mathrm{Hz})$')
 405        ax.set_yscale('log')
 406        plt.legend(handles=[integral, intercept, alpha], frameon=True,
 407                   fancybox=True, framealpha=.8)
 408        # ('Integral', '$S(f=1~\\mathrm{Hz})$', '$\\alpha$'))
 409
 410    def plot_info(self, show_field=0, **kwargs):
 411        """Plots basic mfn infos
 412
 413        Args:
 414            show_field:
 415            kwargs:
 416        """
 417
 418        sns.set_palette(sns.color_palette("hls", len(self.measurements)))
 419        fig, ((ax11, ax12),
 420              (ax21, ax22),
 421              (ax31, ax32)) = plt.subplots(nrows=3, ncols=2,
 422                                           figsize=self.style.get('figsize'),
 423                                           gridspec_kw=kwargs.get('gridspec_kw', dict()),
 424                                           #  wspace=.3,
 425                                           #  hspace=.45)
 426                                           )
 427        fields = kwargs.get('fields',
 428                            self.data.sort_values('Field')['Field'].unique())
 429        spectra_df = pd.DataFrame()
 430        second = pd.DataFrame()
 431        spectra_fit = dict()
 432        for i, field in enumerate(fields):
 433            # Setting shortcut for Average Spectrum s
 434            try:
 435                meas = self.measurements[field]
 436                if meas == None or len(meas.avg_spec) < 2:
 437                    continue
 438            except KeyError:
 439                continue
 440
 441            s = meas.avg_spec
 442
 443            spectra_df['S%s' % field] = s.S
 444
 445            ax11.loglog(s.freq, s.S*s.freq, label='%s T' % field)
 446
 447            f, _ = self.plot_alpha(field, s, ax21, **kwargs)
 448            meas.info['alpha'] = -f.slope,
 449            meas.info['amplitude'] = f.intercept
 450            meas.info['power'] = s.S.sum() * s.freq.diff().iloc[1]
 451
 452            ax22.plot(field, meas.info['power'], 'o')
 453            spectra_fit[field] = {
 454                'alpha': meas.info['alpha'][0],
 455                'amplitude': meas.info['amplitude'],
 456                'power': meas.info['power']}
 457
 458            if field != float(field):
 459                raise ValueError
 460
 461            time_df = self.data.query('Field == %f' % field)
 462            m = time_df['Vx'].mean()
 463            std = time_df['Vx'].std()
 464            ax32.errorbar(field, m, yerr=2 * std, elinewidth=2, capsize=4)
 465
 466            if hasattr(meas.spectrum, 'second_spectra'):
 467                if len(meas.spectrum.second_spectra) > 0:
 468                    second['S%s' % field] = meas.spectrum.second_spectra[0]
 469
 470        self.spectra_fit_df = pd.DataFrame(spectra_fit).T
 471        self.spectra_fit_df.index.name = 'Field'
 472
 473        # Plot Contour Plot of Averaged Spectra per Field
 474        if kwargs.get('plot_field_contour', True):
 475            self.spectra_field_contour(ax12, **kwargs)
 476        else:
 477            field = fields[-1]
 478            if kwargs.get('plot_second_sum', False):
 479                marker = "+x*ds"
 480                color = "rbgyk"
 481                for octave in range(
 482                        self.measurements[
 483                            field].spectrum.number_of_octaves):
 484                    ax12.plot(field,
 485                              self.measurements[
 486                                  field].spectrum.second_spectra[
 487                                  octave].sum(),
 488                              "%s%s" % (color[octave], marker[octave])
 489                              )
 490                ax12.set_ylabel('$S_2 (f)$')
 491            elif kwargs.get('plot_second_freq', False):
 492                frequencies = self.measurements[
 493                    field].spectrum.frequency_span_array_second_spectra
 494                smin, smax = second.min().min(), second.max().max()
 495                levels = np.logspace(np.log10(smin),
 496                                     np.log10(smax), 20)
 497                cs = ax12.contourf(fields,
 498                                   frequencies,
 499                                   second,
 500                                   norm=LogNorm(vmin=smin, vmax=smax),
 501                                   levels=levels,
 502                                   cmap=kwargs.get('contour_cm',
 503                                                   self.style.get('default_cm')),
 504                                   )
 505                cbar = plt.colorbar(cs, ax=ax12)
 506                cbar.set_ticklabels(['%.2e' % _ for _ in levels])
 507                ax12.set_ylabel('$f$ [$\\mathrm{Hz}$]')
 508                ax12.set_title('Second Spectrum over Field')
 509                ax12.set_xlabel('$\\mu_0 H_{ext}$ [$T$]')
 510            else:
 511                self.data.query("Field == @show_field").plot(
 512                    x='Time', y='Vx', ax=ax12)
 513
 514        # # Plot 1/f line in Spectrum
 515        # xmin, ymin = kwargs.get('xymin', (2e-1, 5e-14))
 516        # factor, alpha = (1.5, 2)
 517        # ax11.plot([xmin, xmin * factor],
 518        #           [ymin, ymin / (factor ** alpha)],
 519        #           'k--')
 520        # ax11.annotate('$1/f^{}$'.format(alpha),
 521        #               (
 522        #                   xmin * np.sqrt(factor),
 523        #                   ymin / (factor ** (alpha / 2))
 524        #                )
 525        #               )
 526
 527        if not kwargs.get('disable_PSD_grid'):
 528            #ax11.set_xticks([.2+_/10 for _ in range(9)], minor=True)
 529            ax11.grid(b=True, which='minor',
 530                      color='#cccccc',
 531                      linestyle='-.',
 532                      alpha=.3)
 533
 534        # Set nice title and axis labels
 535        ax11.set_title('\\textbf{I. PSD}')
 536        ax11.set_xlabel('$f$ [$\\mathrm{Hz}$]')
 537        ax11.set_ylabel('$S_V (f) \cdot f$')
 538
 539        ax21.set_title('\\textbf{III. Slope} $\\mathbf{S_V \sim 1/f^{\\alpha}}$')
 540        ax21.set_ylabel('$\\alpha$')
 541
 542        ax22.set_title('\\textbf{IV. PSD Integral}')
 543
 544        for ax in [ax22, ax12]:
 545            ax.set_yscale('log')
 546
 547        self.plot_field_contour(ax31, show_field, **kwargs)
 548
 549        ax32.set_title('\\textbf{VI. Hysteresis Loop}')
 550        ax32.set_ylabel('$V_H$ [$%s\\mathrm{V}$]' % (kwargs.get('unit', 'm')))
 551        ax32.set_xlabel('$\\mu_0 H_{ext}$ [$T$]')
 552
 553        if not kwargs.get('notitle'):
 554            fig.suptitle('m%d: ' % self.info.get('Nr', 431)
 555                     + 'Rate=$%d\\,\\mathrm{Hz}$, ' % self.info['rate']
 556                     + 'Length=$%s\\,\\mathrm{s}$ %s' % (self.info['length'],
 557                                                         kwargs.get(
 558                                                             'add_info', '')))
 559
 560    def plot_field_contour(self, ax31, show_field=0, **kwargs):
 561        # Show specific field
 562        field_data = self.data.query('Field == %f' % show_field)
 563        if field_data.empty:
 564            self.logger.error("Can not find timesignal for field (%s)" %
 565                              show_field)
 566        else:
 567            try:
 568                field_spectra = self.measurements[show_field]
 569                if kwargs.get('plot_timesignal', False):
 570                    field_data.plot(x='Time', y='Vx', legend=False, ax=ax31)
 571                    ax31.set_title(
 572                        'Timesignal ($\\mu_0 H_{ext} = %.2f\\,\\mathrm{mT}$)' % (
 573                                show_field * 1e3))
 574                    ax31.set_ylabel(
 575                        '$V_H$ [$%s\\mathrm{V}$]' % (kwargs.get('unit', 'm')))
 576
 577                    # show chunksize of first spectra generated
 578                    num = field_spectra.spectrum.number_of_first_spectra
 579                    len_first_spectra = len(field_data) / num
 580                    for i in range(1, num):
 581                        x = field_data.iloc[int(len_first_spectra * i)]['Time']
 582                        ax31.axvline(x, linewidth=.5)
 583                elif len(field_spectra.spectrum.first_spectra_df) > 1:
 584                    s = field_spectra.spectrum.first_spectra_df
 585                    freq = s.Frequency.to_numpy()
 586                    first_spectra = s.drop('Frequency', axis=1)
 587                    freq_table = np.tile(freq, (64,1)).T
 588                    first_spectra = first_spectra * freq_table
 589                    freq = field_spectra.avg_spec.freq
 590                    smin = kwargs.get('smin', first_spectra.min().min())
 591                    smax = kwargs.get('smax', first_spectra.max().max())
 592                    levels = np.logspace(np.log10(smin),
 593                                         np.log10(smax),
 594                                         kwargs.get('numlevels', 10))
 595                    times = np.linspace(0, field_spectra.info['Time'],
 596                                    field_spectra.num)
 597                    cs = ax31.contourf(
 598                        times,  # x = Time
 599                        freq,  # y = Frequency
 600                        first_spectra,  # z = all spectra
 601                        norm=LogNorm(vmin=smin, vmax=smax),
 602                        levels=levels,
 603                        cmap=kwargs.get('contour_cm',
 604                                        self.style.get('default_cm')),
 605                        # levels=kwargs.get('contour_levels'),
 606                    )
 607                    self.time_spectra_field_contour_df = first_spectra.copy(deep=True)
 608                    self.time_spectra_field_contour_df['freq'] = freq
 609                    self.time_spectra_field_contour_df.set_index('freq', inplace=True)
 610                    self.time_spectra_field_contour_df.rename(
 611                        {'S{}'.format(_): times[_] for _ in range(field_spectra.num)},
 612                        axis='columns', inplace=True)
 613
 614                    cbar = plt.gcf().colorbar(cs, ax=ax31)
 615                    cbar.set_label('$S_V^{(n)} (f) \cdot f$')
 616                    cbar.set_ticklabels(['%.2e' % _ for _ in levels])
 617                    ax31.set_yscale('log')
 618                    ax31.set_title(
 619                        '\\textbf{V. Time-resolved PSD} ($\\mu_0 H_{ext} = '
 620                        '%.0f\\,\\mathrm{mT}$)' % (show_field * 1e3)
 621                    )
 622                    ax31.set_ylabel('$f$ [Hz]')
 623            except Exception as e:
 624                self.logger.error("Can not plot field (H = %s) data: %s" %
 625                                  (show_field, e))
 626                raise ValueError("Can not plot field (H = %s) data: %s" %
 627                                 (show_field, e))
 628        ax31.set_xlabel('Time [s]')
 629
 630    def plot_various_noise_repr(self):
 631        sns.set_palette(sns.color_palette("hls", len(self.measurements)))
 632        fig, axes = plt.subplots(nrows=4, ncols=4,
 633                                 gridspec_kw=dict(wspace=.3, hspace=.45))
 634        for i in range(16):
 635            ax = axes[i // 4][i % 4]
 636            for field in self.data.sort_values(
 637                    'Field', ascending=False)['Field'].unique():
 638                # Setting shortcut for Average Spectrum s
 639                s = self.measurements[field].avg_spec
 640
 641                ax.plot(field, s.iloc[i]['S'], 'o')
 642                ax.set_title('$S_V (f=%.3f)$' % s.iloc[i].freq)
 643        plt.show()
 644
 645    def plot_various_noise_fits(self, **kwargs):
 646        """
 647        Args:
 648            **kwargs:
 649        """
 650        sns.set_palette(sns.color_palette("hls", len(self.measurements)))
 651        fig, axes = plt.subplots(nrows=kwargs.get('nrows', 3),
 652                                 ncols=kwargs.get('ncols', 3),
 653                                 gridspec_kw=dict(wspace=.3, hspace=.45),
 654                                 figsize=(11.69, 8.27))
 655        for i, fit_range in enumerate(kwargs.get('fit_ranges', range(3, 11))):
 656            ax = axes[i // kwargs.get('nrows', 3)][i % kwargs.get('ncols', 3)]
 657            for field in self.data.sort_values('Field')['Field'].unique():
 658                # Setting shortcut for Average Spectrum s
 659                s = self.measurements[field].avg_spec
 660                fit = scipy.stats.linregress(s.lnf.iloc[:fit_range],
 661                                             s.lnS.iloc[:fit_range])
 662
 663                if kwargs.get('value', 'intercept') == 'intercept':
 664                    ax.plot(field, 10 ** fit.intercept, 'o')
 665                    ax.set_yscale('log')
 666                else:
 667                    ax.plot(field, -1 * fit.slope, 'o')
 668
 669                ax.set_title('Fit Range = %s' % fit_range)
 670        fig.suptitle(kwargs.get('title', 'Noise Fit at 1 Hz'))
 671
 672    def plot_compare_timesignals(self, fields, **kwargs):
 673        """ Plots a grid of 2x2 subplots to compare timesignals and histogram.
 674        Args:
 675            fields: list
 676            factor: float Default: 1e3 (mV)
 677            nrows: int Default: len(fields)
 678            ncols: int Default: 2
 679            sharey: bool Default: True
 680            figsize: tuple Default: (11.69, 8.27) in
 681            plot_range: int Default: -1
 682        """
 683        fig, axes = plt.subplots(nrows=kwargs.get('nrows', 1),
 684                                 ncols=kwargs.get('ncols', len(fields)),
 685                                 sharey=kwargs.get('sharey', False),
 686                                 figsize=kwargs.get('figsize', (11.69, 8.27)),
 687                                 **kwargs.get('subplot_kw', dict()))
 688        twinax = []
 689        factor = kwargs.get('factor', 1e3)
 690        colors = kwargs.get('colors',['red',
 691                                     'blue',
 692                                     'green',
 693                                     'orange',
 694                                     'purple'])
 695        for i, (f, c) in enumerate(zip(fields, colors)):
 696            ax = axes[i]
 697            d = self.data[self.data['Field'] ==
 698                          f].iloc[:kwargs.get('plot_range', -1)]
 699            ax.plot(d['Time'], d['Vx']*factor, color=c, **kwargs.get('plot_kw', {}))
 700            twinax.append(ax.twiny())
 701            sns.distplot(d['Vx']*factor, ax=twinax[i], vertical=True, color=c, **kwargs.get('dist_kw', {}))
 702            ax.legend(['$V_H (\\mathbf{\\mu_0 H_{ext}=%s\\,mT})$' % int(float(f * 1e3))])
 703
 704        return fig, axes, twinax
 705
 706    def plot_various_timesignals(self, fields, **kwargs):
 707        """ Plots a grid of 9 subplots with different timesignals
 708        Args:
 709            fields: list
 710            nrows: int Default: len(fields)
 711            ncols: int Default: 2
 712            sharey: bool Default: True
 713            figsize: tuple Default: (11.69, 8.27) in
 714            plot_range: int Default: -1
 715        """
 716        fig, axes = plt.subplots(nrows=kwargs.get('nrows', len(fields)),
 717                                 ncols=kwargs.get('ncols', 2),
 718                                 sharey=kwargs.get('sharey', True),
 719                                 figsize=kwargs.get('figsize', (11.69, 8.27))
 720                                 )
 721        for i, f in enumerate(fields):
 722            ax = axes[i // kwargs.get('nrows', 3)][i % kwargs.get('ncols', 3)]
 723            d = self.data[self.data['Field'] ==
 724                          f].iloc[:kwargs.get('plot_range', -1)]
 725            ax.plot(d['Time'], d['Vx'])
 726            ax.legend(['$V_H (\\mu_0 H_{ext}=%.1f\\,\\mathrm{mT})$' % (f * 1e3)])
 727
 728    def spectra_field_contour(self, ax=None, **kwargs):
 729        """
 730        Args:
 731            ax:
 732            **kwargs:
 733        """
 734        if not ax:
 735            fig, ax = plt.subplots()
 736        fields = kwargs.get('fields',
 737                            self.data.sort_values('Field')['Field'].unique())
 738
 739        # Create variable for third dimension
 740        spectra_df = pd.DataFrame()
 741        for f in fields:
 742            average_spectrum = self.measurements[f].avg_spec
 743            spectra_df[f] = average_spectrum.S * \
 744                average_spectrum.freq
 745        frequencies = self.measurements[f].avg_spec['freq']
 746        smin = kwargs.get('smin', spectra_df.min().min())
 747        smax = kwargs.get('smax', spectra_df.max().max())
 748        levels = np.logspace(np.log10(smin),
 749                             np.log10(smax),
 750                             kwargs.get('numlevels', 10))
 751
 752        size_fields = len(fields)
 753        size_freq = len(frequencies)
 754        if spectra_df.shape != (size_freq, size_fields):
 755            self.logger.error('Cannot plot contour:\n' + \
 756                              'spectra_df.shape (%s) != ' + \
 757                              'size_freq (%s), size_fields (%s)', (
 758                                  spectra_df.shape, size_freq, size_fields))
 759            return
 760
 761        if size_fields < 2 or size_freq < 2:
 762            self.logger.error('Cannot plot contour: ' + \
 763                              'size_freq (%s), size_fields (%s) incorrect' % (
 764                                  size_freq, size_fields))
 765            return
 766
 767        cs = ax.contourf(fields * 1e3, frequencies, spectra_df,
 768                         norm=LogNorm(vmin=smin, vmax=smax),
 769                         levels=levels,
 770                         cmap=kwargs.get('contour_cm',
 771                                         self.style.get('default_cm')),
 772                         )
 773        self.spectra_field_contour_df = spectra_df.copy(deep=True)
 774        self.spectra_field_contour_df['freq'] = frequencies
 775        self.spectra_field_contour_df.set_index('freq', inplace=True)
 776
 777        if kwargs.get('cbar', True):
 778            cbar = plt.colorbar(cs, ax=ax)
 779            cbar.set_ticklabels(['%.2e' % _ for _ in levels])
 780            cbar.set_label('$S_{V} (f) \cdot f$')
 781        ax.set_yscale('log')
 782        ax.set_ylabel('$f$ [$\\mathrm{Hz}$]')
 783        ax.set_xlabel('$\\mu_0 H_{ext}$ [$\\mathrm{mT}$]')
 784        ax.set_title(kwargs.get('title', '\\textbf{II. PSD Contour Plot}'))
 785
 786    def write(self, file):
 787        """Write data to a file.
 788
 789        Args:
 790            file (str):
 791        """
 792        if not os.path.exists(file):
 793            os.makedirs(file)
 794
 795        self.data.to_csv("%s_fields.dat" % file, index=False)
 796        for field, m in self.measurements.items():
 797            m.write("%s_spectrum_%s" % (file, field))
 798
 799    def read(self, file):
 800        """Write data to a file.
 801
 802        Args:
 803            file (str):
 804        """
 805        self.data = pd.read_csv("%s_fields.dat" % file)
 806        for field in self.data['Field'].unique():
 807            df = self.data.query('Field == %s' % field)
 808            self.measurements[field] = \
 809                RAW({'data': df['Vx']},
 810                    rate=1 / df['Time'].diff().median(),
 811                    )
 812            if not self.measurements[field].read("%s_spectrum_%s" % (file,
 813                                                                     field)):
 814                self.measurements.pop(field)
 815
 816    def subtract_mean(self, inplace=False, **kwargs):
 817        """Subtracts mean value from timesignal.
 818
 819        :param inplace: Change variable inside this object?
 820        :type inplace: bool
 821
 822        :param fields: list of fields to fit
 823
 824        :return: normalized timesignal
 825        """
 826
 827        df_fit = pd.DataFrame()
 828        fields = kwargs.get('fields',
 829                            self.data.sort_values('Field')['Field'].unique())
 830        for field in fields:
 831            signal = self.data.query('Field == @field').copy()
 832            mean = signal.Vx.mean()
 833            signal.loc[:, 'normedVx'] = signal.Vx.copy() - mean
 834
 835            if inplace:
 836                self.data[self.data.Field == field].loc[:, 'normedVx'] = \
 837                    signal.loc[:, 'normedVx']
 838
 839            df_fit = pd.concat([df_fit, signal])
 840        return df_fit
 841
 842    def plot_multiple_histograms(self, steps=4,
 843                                 fields=pd.Series(dtype=np.float64),
 844                                 factor=1e3,
 845                                 xlim=(-1.3, 1.3),
 846                                 **kwargs):
 847        """Plots multiple histograms using seaborn ``sns.FacetGrid``
 848           with kdeplot.
 849           
 850           default kwargs for subplots:
 851            kde1_kwargs = dict(clip_on=True, shade=True, alpha=1, lw=1.5, bw=.2)
 852            kde2_kwargs = kde1_kwargs.update(dict(lw=2, color='w')
 853            dist_kwargs = dict(hist=True, norm_hist=True, hist_kws={'alpha': .5})
 854            ax_kwargs = dict(y=0, lw=2, clip_on=True).set(xlim=(-1.3,1.3))
 855        """
 856        if fields.empty:
 857            fields = self.data.Field.unique()
 858
 859        field_range = abs(fields.max() - fields.min())
 860        stepsize = (field_range / steps)
 861        for i in range(steps):
 862            start = fields.min() + i * stepsize
 863            end = start + stepsize
 864            df_tmp = self.subtract_mean().query('Field >= @start and Field < @end').copy()
 865            df_tmp.loc[:, 'normedVx'] *= factor
 866
 867            # Initialize the FacetGrid object
 868            pal = sns.dark_palette(color="blue", n_colors=10, input='huls')
 869            face_kwargs = dict(aspect=5, height=.8, palette=pal)
 870            face_kwargs.update(kwargs.get('face_kwargs', {}))
 871            kde1_kwargs = dict(clip_on=True, shade=True, alpha=1, lw=1.5, bw=.2)
 872            kde1_kwargs.update(kwargs.get('kde1_kwargs', {}))
 873            kde2_kwargs = kde1_kwargs.copy()
 874            kde2_kwargs.update(dict(lw=2, bw=.2, color='w'))
 875            kde2_kwargs.update(kwargs.get('kde2_kwargs', {}))
 876            dist_kwargs = dict(hist=True, norm_hist=True, hist_kws={'alpha': .5})
 877            dist_kwargs.update(kwargs.get('dist_kwargs', {}))
 878            ax_kwargs = dict(y=0, lw=2, clip_on=True)
 879            ax_kwargs.update(kwargs.get('ax_kwargs', {}))
 880
 881            g = sns.FacetGrid(df_tmp, row="Field", hue="Field", **face_kwargs)
 882            g.map(sns.kdeplot, "normedVx", **kde1_kwargs)
 883            g.map(sns.kdeplot, "normedVx", **kde2_kwargs)
 884            # g.map(sns.distplot, "normedVx", **dist_kwargs)
 885            g.map(plt.axhline, **ax_kwargs).set(xlim=xlim)
 886
 887            def label(x, color, label):
 888                ax = plt.gca()
 889                ax.text(0, kwargs.get('adjust_hspace', .3), label, fontweight="bold", color=color,
 890                        ha="left", va="center", fontsize=16,
 891                        transform=ax.transAxes)
 892
 893            g.map(label, "normedVx")
 894
 895            # Set the subplots to overlap
 896            g.fig.subplots_adjust(-kwargs.get('adjust_hspace', .3))
 897
 898            # Remove axes details that don't play well with overlap
 899            g.set_titles("")
 900            g.set(yticks=[])  # , xticks=[-100 + 50*_ for _ in range(5)])
 901            g.despine(bottom=True, left=True)
 902            unit = 'mV' if factor == 1e3 else '\\mu{}V' if factor == 1e6 else 'V'
 903            plt.gca().set_xlabel('$V_H$ [$\\mathrm{%s}$]' % unit)
 904
 905    def load_voltages(self, nr=508, figsize=(16, 12)):
 906        def load_data(datapath):
 907            meas_data = {}
 908            meas_info = {}
 909            all_data = {}
 910            for f in datapath:
 911                f_info = self.get_info_from_name(f)
 912                sr = f_info['Vin']
 913                nr = f_info['nr']
 914                meas_info[sr] = f_info
 915                meas_data[sr] = pd.read_csv(f, sep='\t')
 916                new_df = meas_data[sr]
 917                new_df['Vin'] = float(sr[:-2])
 918                if nr in all_data.keys():
 919                    all_data[nr] = pd.concat([all_data[nr], new_df])
 920                else:
 921                    all_data[nr] = new_df
 922            return meas_data, meas_info, all_data
 923
 924        def calc_PSD(meas_data):
 925            meas_obj = {}
 926            for sr, data_df in meas_data.items():
 927                if len(data_df['Vx']) % 1024:
 928                    avg = len(data_df['Vx']) // 1024
 929                    d = data_df['Vx'].iloc[:-(len(data_df['Vx']) % 1024)]
 930                else:
 931                    d = data_df.Vx
 932
 933                max_len = len(d)
 934
 935                data = {
 936                    'data': d,
 937                    'info': {
 938                        'Nr': meas_info[sr]['nr'],
 939                        'rate': 1 / data_df.time.diff().mean(),
 940                        'length': max_len * data_df.time.diff().mean(),
 941                    }
 942                }
 943
 944                meas_obj[sr] = RAW(data,
 945                                   rate=data['info']['rate'],
 946                                   nof_first_spectra=32,
 947                                   calc_first=True,
 948                                   downsample=False,
 949                                   )
 950            return meas_obj
 951
 952        def merge_data(meas_obj, cutoff_frequency=.9):
 953            diff_voltages = pd.DataFrame()
 954            for sr, m in meas_obj.items():
 955                s = m.avg_spec
 956                s = s[s.freq < cutoff_frequency]
 957                if len(s) < 2:
 958                    continue
 959                newdf = pd.DataFrame()
 960                newdf['freq'] = s.freq
 961                newdf['S'] = s.S
 962                newdf['Vin'] = float(sr[:-2])
 963                diff_voltages = pd.concat([diff_voltages, newdf])
 964            return diff_voltages
 965
 966        def plot_PSD_classic(diff_voltages, title, groupby_category='Vin',
 967                             num=10, style=[['science'], {'context': 'talk', 'style': 'white', 'palette': 'bright', }]):
 968            set_style(style)
 969            c1 = sns.color_palette("hls", num)
 970            sns.set_palette(c1)
 971            fig, ax = plt.subplots(figsize=(12, 8))
 972            # g = sns.relplot(x='freq', y='S', hue='Vin', data=diff_voltages, height=5, kind='line')
 973            grouped = diff_voltages.groupby(groupby_category)
 974            for group in grouped.groups.keys():
 975                grouped.get_group(group).plot(x='freq', y='S', kind='line',
 976                                              loglog=True, ax=ax,
 977                                              label=group,
 978                                              xlabel='Frequency [Hz]',
 979                                              ylabel='$S_{V_H}$ [$\\mathrm{V}^2/\\mathrm{Hz}$]',
 980                                              )
 981            ax.set_title(title)
 982            # save_plot('m506', 'png')
 983
 984        def show_PSD_classic(diff_voltages, title, ax=None, groupby_category='Vin',
 985                             num=10, style=[['science'], {'context': 'talk', 'style': 'white', 'palette': 'bright', }]):
 986            if not ax:
 987                fig, ax = plt.subplots(figsize=(12, 8))
 988            set_style(style)
 989            c1 = sns.color_palette("hls", num)
 990            sns.set_palette(c1)
 991            # g = sns.relplot(x='freq', y='S', hue='Vin', data=diff_voltages, height=5, kind='line')
 992            grouped = diff_voltages.groupby(groupby_category)
 993            for group in grouped.groups.keys():
 994                grouped.get_group(group).plot(x='freq', y='S', kind='line',
 995                                              loglog=True, ax=ax,
 996                                              label=group,
 997                                              xlabel='Frequency [Hz]',
 998                                              ylabel='$S_{V_H}$ [$\\mathrm{V}^2/\\mathrm{Hz}$]',
 999                                              )
1000            ax.set_title(title)
1001            return ax
1002
1003        datapath = glob('./data/MFN/m%s/*' % nr)
1004        meas_data, meas_info, all_data = load_data(datapath)
1005        meas_obj = calc_PSD(meas_data)
1006        diff_voltages = merge_data(meas_obj)
1007        fig, ax = plt.subplots(figsize=figsize)
1008        show_PSD_classic(diff_voltages, 'm%s: Compare different Voltages' % nr, ax=ax)
1009
1010        inset2 = inset_axes(ax, width='100%', height='100%',
1011                            bbox_to_anchor=(.54, .75, .3, .25),
1012                            bbox_transform=ax.transAxes)
1013        inset3 = inset_axes(ax, width='100%', height='100%',
1014                            bbox_to_anchor=(.1, .1, .3, .25),
1015                            bbox_transform=ax.transAxes)
1016
1017        grouped = diff_voltages.groupby('Vin')
1018        for group in grouped.groups.keys():
1019            g = grouped.get_group(group)
1020            fit_area = g.query('freq > %f and freq < %f' % (2e-2, 7e-1))
1021            fit_area['lnf'] = np.log10(fit_area.freq)
1022            fit_area['lnS'] = np.log10(fit_area.S)
1023            fit = scipy.stats.linregress(fit_area.lnf, fit_area.lnS)
1024            intercept, slope = fit.intercept, -fit.slope
1025            voltage = group
1026
1027            inset2.plot(voltage, 10 ** intercept, 'o')
1028            inset3.plot(voltage, slope, 'o')
1029
1030        inset2.set_xlabel('Voltage [$\mathrm{V}$]')
1031        inset2.set_ylabel('$S_{V_H} (f=1\\;$Hz$)$')
1032        inset2.set_yscale('log')
1033
1034        inset3.set_xlabel('Voltage [$\mathrm{V}$]')
1035        inset3.set_ylabel('$\\alpha$')

MeasurementClass

  1class MeasurementClass(object):
  2    """
  3        The main measurement class for all measurements.
  4    """
  5    def __init__(self):
  6        """
  7        .. note:: **MeasurementClass:**
  8            This Class is the parent of all other measurement classes.
  9
 10
 11        All measurements have the following variables:
 12            
 13        name: str/None
 14            The name of the Measurement Class (e.g. SA, RAW, etc.)
 15
 16        info: dict
 17            Contains informations about the measurement. Typical keys are:                
 18                Nr: float, the number of the measurement,
 19
 20        """
 21        self.name = ''
 22        self.data = {}
 23        self.info = {'Nr': 0}
 24        self.info_file = {}
 25        self.style = PlottingClass()
 26
 27    def i(self, *key):
 28        """
 29        Get  a specific measurement number information.
 30
 31        
 32        :param *key: Will be passed to info.get
 33        
 34        :return: info.get(*key)
 35
 36        """
 37        return self.info.get(*key)
 38
 39
 40    __repr__ = lambda self: '%s (Nr. %s)\n' % (self.name, self.i('Nr', 0))
 41
 42    __str__ = lambda self: '%s (Nr. %s)\n' % (self.name, self.i('Nr', 0)) + \
 43        '\n'.join(['%s:\t%s' % (key, val) for key, val in self.info.items()])
 44    
 45    
 46    def get_info_from_name(self, name, **kwargs):
 47        """
 48        Extracting information from filenames.
 49        Using a default regex first or trying to extract 'key-value' pairs
 50        separated by '_'.
 51    
 52        :param name: filename that contains informations.
 53        :param kwargs:
 54            :str regex: Regular expression for the filename
 55        :return: dict(key=value)
 56        """
 57    
 58        # Default Regex Structure
 59        # Nr,     Struct,         deg,        Type1
 60        # Type2, Field, Date, Time
 61        # I1, I2, Lock-In (Connections)
 62        # Voltage In, Resistors (R11, R12)
 63        # R13, R21
 64        # Capacitors (C11, C21)
 65        # Temp
 66        def_regex = ".*[Mm]([0-9.]*)_([A-Za-z]*)_([0-9.-]*)deg_([A-Za-z]*)_" \
 67                    "([A-Za-z]*)_*B_([0-9.-]*)T_([0-9]*)_([0-9]*)_I1-([" \
 68                    "0-9\\-]*)_I2-([0-9\\-]*)_G[PB]I[BP].-([0-9\\-]*)_Vin-([" \
 69                    "0-9.]*)V_R11-([0-9]*.)O_R12-([0-9.]*.)O_R13-([" \
 70                    "0-9.]*.)_R21-([0-9.]*.)O_C11-([0-9]*)_C21-([0-9]*)_T-([" \
 71                    "0-9]*)K.*"
 72        regex = kwargs.get('regex', def_regex)
 73        reg = re.match(regex, name)
 74    
 75        info_dict = dict()
 76        if not reg:
 77            filename = name.split(os.sep)[-1][:-4]
 78            raw_info = filename.split('_')
 79            for element in raw_info:
 80                regex_dict = {
 81                    'nr': 'm([0-9.]*)',
 82                    'deg': '((neg)?[0-9.-]*)deg',
 83                    'dir': '([Uu]p|[Dd]own)',
 84                    'date': '(20[12][90][0-9]*)',
 85                    'time': '([012][0-9][0-6]*)',
 86                    'type1': '(RAW|Hloop)',
 87                    'type2': '(Parallel|Gradio)',
 88                    'struct': '([Pp]lusses|[Cc]rosses|[Ee]mpty)',
 89                    'field': 'p?m?([0-9.-]+)m?T',
 90                }
 91                if re.match(regex_dict['field'], element):
 92                    info_dict['field'] = re.match(regex_dict['field'], element).groups()[0]
 93                    continue
 94    
 95                items = element.split('-')
 96                if len(items) == 1:
 97                    for key, single_regex in regex_dict.items():
 98                        reg = re.match(single_regex, items[0])
 99                        if reg:
100                            info_dict[key] = reg.groups()[0]
101                elif len(items) == 2:
102                    key, value = items
103                    info_dict[key] = value
104                elif len(items) > 2:
105                    value = '-'.join(items[1:])
106                    info_dict[items[0]] = value
107        else:
108            nr, struct, deg, type1, \
109                type2, field, date, time, \
110                i1, i2, lock_in, \
111                vin, r11, r12, r13, r21, \
112                c11, c21, temp = reg.groups()
113            try:
114                info_dict['nr'] = float(nr)
115                info_dict['field'] = float(field)
116            except ValueError:
117                print('Expected float field and nr, got:'
118                                  ' %s, %s' % (field, nr))
119            info_dict['srtuct'] = struct
120            info_dict['deg'] = deg
121            info_dict['type1'] = type1
122            info_dict['type2'] = type2
123            info_dict['date'] = date
124            info_dict['time'] = time
125            info_dict['i1'] = i1
126            info_dict['i2'] = i2
127            info_dict['lock_in'] = lock_in
128            info_dict['r11'] = r11
129            info_dict['r12'] = r12
130            info_dict['r13'] = r13
131            info_dict['r21'] = r21
132            info_dict['vin'] = vin
133            info_dict['c11'] = c11
134            info_dict['c21'] = c21
135            info_dict['temp'] = temp
136   
137        self.info_file = info_dict
138        
139        return info_dict
140
141    def calc_log(self, df, keys=['f', 'Vx', 'Vy']):
142        for key in keys:
143            df['ln%s' % key] = np.log10(df[key])

SingleM

 1class SingleM(MeasurementClass):
 2    def __init__(self):
 3        """The main constructor for all single measurements."""
 4        super().__init__()
 5        self.data = pd.DataFrame()
 6
 7    linear_regression = lambda self, x, y: scipy.stats.linregress(x, y)
 8    
 9    def fit_variable(self, df, x, y):
10        """Applys a linear regression and adds result to DataFrame
11
12        Args:
13            df (pd.DataFrame): Fit data
14            x (np.ndarray): Fit variable.
15            y (np.ndarray): Fit variable.
16        """
17        fit = self.linear_regression(x[1], y[1])
18        for var in [x, y]:
19            df[var[0]] = var[1]
20    
21        return df

RAW

  1class RAW(SingleM):
  2    def __init__(self, data, **kwargs):
  3        """
  4        RAW Measurement:
  5            Measurements with time signal only.
  6
  7        .. code-block:: python
  8           :linenos:
  9           :caption: Example
 10
 11           data = np.array(timesignal, shape=(n,))
 12           data = {
 13                'data': np.array(timesignal, shape=(n,))
 14                'info': {'Nr': 123, 'Add Info': 'Important Informations'}
 15                }
 16           raw = ana.RAW(data)
 17
 18        :param data: Different possibilities.
 19
 20        :param rate: At which rate was the timesignal measured (samplingrate) [Hz]
 21        :type rate: float, default: 2e-4
 22
 23        :info: First Spectrum parameters
 24        :param nof_first_spectra: Cut timesignal into # pieces and average.
 25        :type nof_first_spectra: int, default: 64
 26
 27        :param first_spectra_highest_frequency: Cut higher frequencies in final spectrum than this.
 28        :type first_spectra_highest_frequency: float, default: rate/8
 29
 30        :param downsample: Downsample the timesignal before calculation.
 31        :type downsamle: bool, default: True
 32
 33        :param calc_first: Triggers calculation of first spectrum.
 34        :type calc_first: bool, default: False
 35
 36        :info: Second Spectrum parameters
 37        :param highest_octave: The highest octave starting point [Hz] of the second spectrum.
 38        :type highest_octave: int, default: 64
 39
 40        :param minimum_points_in_octave: If octave would contain less points, stop.
 41        :type minimum_points_in_octave: int, default: 10
 42
 43        :param calc_second: Triggers calculation of second spectrum.
 44        :type calc_second: bool, default: False
 45        """
 46        super().__init__()
 47        self.logger = logging.getLogger('RAW')
 48        self.name = 'RAW'
 49        # Default Parameter for the first and second spectrum
 50        timesignal = data.get('data', 
 51                              kwargs.get('timesignal', 
 52                                         pd.Series([], dtype='float64')
 53                                         )
 54                              )
 55        self.info = data.get('info', {})
 56        self.data = data
 57        self.rate = kwargs.get('rate', 2e-4)
 58
 59        # Define dummies
 60        self.avg = 0
 61        self.spectrum = None
 62        self.spec = []
 63        self.avg_spec = pd.DataFrame()
 64        self.freq2 = np.array([])
 65        self.time_signal_second_spectrum_transposed_normalized = np.array([])
 66        self.second_spectrum_time_array = np.array([])
 67
 68        self.timesignal = self.check_timesignal(timesignal)
 69        self.n = int(1024 * self.avg)
 70        self.highest_octave = kwargs.get('highest_octave', 64)
 71        self.minimum_points_in_octave = kwargs.get('minimum_points_in_octave',
 72                                                   10)
 73        self.shifting_method = False
 74        self.short_mode = 1
 75        self.filter_type = 'lowpass'
 76        self.passband_factor = 1
 77        self.filter_order = 6
 78        self.filter_analog = False
 79        self.downsample = kwargs.get('downsample', True)
 80        self.f_max = kwargs.get(
 81            'first_spectra_highest_frequency', self.rate / 8)
 82        self.num = kwargs.get('nof_first_spectra', 64)
 83
 84        self.info.update({
 85            "Time": self.n / self.rate,
 86            "Num": self.n,
 87            "Rate": self.rate,
 88            "Avg": self.avg,
 89            "downsample": self.downsample,
 90            "f_max": self.f_max,
 91            "NofSpectra": self.num,
 92        })
 93
 94        if kwargs.get('calc_first'):
 95            self.calc_first_spectrum()
 96            
 97            if self.avg_spec.empty:
 98                return 
 99            
100            max_freq = self.avg_spec['freq'].max()
101            self.info.update({
102                # "f_min": freq[0],
103                # 'f_max': freq.max(),
104                "f_min": self.avg_spec['freq'].min(),
105                'f_max': max_freq,
106            })
107
108            if kwargs.get('calc_second'):
109                self.calc_second_spectrum(highest_octave=max_freq)
110
111    def check_timesignal(self, timesignal):
112        """ converts pd.DataFrame['Vx'] and Series into numpy array.
113
114        :param timesignal: input timesignal
115        :return: converted timesignal.
116        :rtype: numpy.ndarray
117        """
118        if isinstance(timesignal, pd.DataFrame):
119            ts = np.array(timesignal['Vx'])
120        elif isinstance(timesignal, pd.Series):
121            ts = timesignal.to_numpy()
122        else:
123            ts = timesignal
124
125        self.avg = (len(ts) / 1024.)
126        if self.avg != int(self.avg):
127            self.avg = int(len(ts) // 1024)
128            ts = ts[:int(self.avg * 1024)]
129
130        if self.rate == 0:
131            self.rate = 1
132
133        return ts
134
135    def calc_first_spectrum(self):
136        """
137        Sets the variable ``spectrum`` to a
138        :class:`spectrumanalyzer.SpectrumAnalyzer` object.
139
140        .. important::
141            Calculates the first spectrum.
142
143        
144        :raises NameError: spectrumanalyzer is not installed
145        :return: None
146
147        """
148        
149        try:
150            self.spectrum = SpectrumAnalyzer(
151                # filepath=filename,
152                timesignal=self.timesignal,
153                samplingrate=self.rate,
154                averages=self.avg,
155                shifting_method=self.shifting_method,
156                short_mode=self.short_mode,
157                filter_type=self.filter_type,
158                passband_factor=self.passband_factor,
159                filter_order=self.filter_order,
160                filter_analog=self.filter_analog,
161                first_spectra_highest_frequency=self.f_max,
162                downsample=self.downsample,
163            )
164        except NameError:
165            raise NameError("SpectrumAnalyzer module not installed.")
166
167        for spectrum_arr, freq, k in self.spectrum.cut_timesignal(self.num):
168            self.spec.append(spectrum_arr)
169
170        self.avg_spec = pd.DataFrame({
171            'freq': self.spectrum.frequency_span_array,
172            'S': self.spectrum.first_spectrum})
173
174    def calc_second_spectrum(self, **kwargs):
175        """
176        Calculating the second spectrum.
177        """
178
179        self.spectrum.calculate_second_spectrum(
180            highest_octave=kwargs.get('highest_octave', self.highest_octave),
181            minimum_points_in_octave=kwargs.get('minimum_points_in_octave',
182                                                self.minimum_points_in_octave),
183            peak_filter=kwargs.get('peak_filter', False)
184        )
185
186        self.freq2 = self.spectrum.frequency_span_array[
187            self.spectrum.frequency_span_array > 0
188            ]
189
190        octaves = [self.highest_octave]
191        for i in range(30):
192            freq = np.fft.fftfreq(
193                int(self.n / self.spectrum.number_of_first_spectra),
194                1. / self.rate)[3:]
195            freq = freq[freq > 0]
196            freq = freq[freq < self.f_max]
197            points_in_octave = len(
198                freq[freq < self.highest_octave / (2. ** (i + 1))])
199            number_of_octaves = i
200            if points_in_octave < self.minimum_points_in_octave:
201                break
202            else:
203                octaves = np.append([min(octaves) / 2.], octaves)
204
205        self.info.update({
206            'NofOct': number_of_octaves,
207        })
208
209        # Calculate Timesignal if needed
210        if not kwargs.get('skip_timesignal', False):
211            self.calc_time_signal_second_spectrum()
212
213    def calc_time_signal_second_spectrum(self):
214        time_signal_second_spectrum_transposed = \
215            self.spectrum.time_signal_second_spectrum.transpose()
216        second_spectrum_time_array = \
217            np.arange(0, len(time_signal_second_spectrum_transposed[0])) * \
218            self.spectrum.timestep_second_spectrum
219        time_signal_second_spectrum_transposed_normalized = \
220            np.zeros((len(time_signal_second_spectrum_transposed),
221                      len(time_signal_second_spectrum_transposed[0])))
222        time_signal_second_spectrum_transposed_normalized = \
223            np.fliplr(time_signal_second_spectrum_transposed_normalized)
224
225        for p in range(self.spectrum.number_of_octaves):
226            time_signal_second_spectrum_transposed_normalized[p] = \
227                time_signal_second_spectrum_transposed[p] / np.mean(
228                    time_signal_second_spectrum_transposed[p])
229
230        self.time_signal_second_spectrum_transposed_normalized = \
231            time_signal_second_spectrum_transposed_normalized
232        self.second_spectrum_time_array = second_spectrum_time_array
233
234
235    def plot_spectrum(self, ax, ):
236        self.avg_spec.plot(x='freq', y='S', loglog=True, ax=ax)
237
238    def plot_time(self, ax, ):
239        ax.plot(np.arange(len(self.timesignal)), self.timesignal,
240                  color='red', label='time', linewidth=.2)
241
242    def subtract_mean(self, inplace=False):
243        """Subtracts mean value from timesignal.
244
245        :param inplace: Change variable inside this object?
246        :type inplace: bool
247
248        :return: normalized timesignal
249        """
250        norm_ts = self.timesignal - np.mean(self.timesignal, axis=1)
251
252        if inplace:
253            self.timesignal = norm_ts
254
255        return norm_ts
256
257    def write(self, file):
258        """
259        Writing data to a file.
260
261        Parameters
262        ----------
263        file: str
264        """
265        if self.avg_spec.empty:
266            return
267
268        if not os.path.exists(file):
269            os.makedirs(file)
270
271        all_first_spectra = self.spectrum.first_spectra_df.loc[:]
272        all_first_spectra['Avg'] = self.avg_spec['S']
273
274        all_first_spectra.to_csv("%s_first.dat" % file, index=False)
275        # all_first_spectra.to_feather("%s_first.feather" % file)
276
277        if not self.spectrum.second_spectra.any():
278            return
279
280        all_second_spectra = pd.DataFrame(self.spectrum.second_spectra,
281                                          index=False)
282        all_second_spectra['Frequency'] = \
283            self.spectrum.frequency_span_array_second_spectra
284        all_second_spectra.to_csv('%s_second.dat' % file,
285                                  index=False)
286        # all_second_spectra.to_feather('%s_second.feather' % file)
287
288        all_second_spectra_time = pd.DataFrame(
289            self.spectrum.time_signal_second_spectrum)
290        all_second_spectra_time.to_csv('%s_second_time.dat' % file,
291                                       index=False)
292        # all_second_spectra_time.to_feather('%s_second_time.feather' % file)
293
294    def read(self, file):
295        """
296        Reading data from file.
297
298        Parameters
299        ----------
300        file: str
301        Filename base to read data from.
302
303        Returns
304        -------
305        self
306        """
307
308        try:
309            first_spectra = pd.read_csv("%s_first.dat" % file)
310        except FileNotFoundError:
311            self.logger.error('First Spectrum at %s_first.dat not found!',
312                              file)
313            return False
314
315        try:
316            self.spectrum = SpectrumAnalyzer(
317                timesignal=self.timesignal,
318                samplingrate=self.rate,
319                averages=self.avg,
320                shifting_method=self.shifting_method,
321                short_mode=self.short_mode,
322                filter_type=self.filter_type,
323                passband_factor=self.passband_factor,
324                filter_order=self.filter_order,
325                filter_analog=self.filter_analog,
326                first_spectra_highest_frequency=self.f_max,
327                downsample=self.downsample,
328            )
329        except Exception as e:
330            self.logger.error("Can not create SpectrumAnalyzer Object: %s", e)
331            return False
332
333        try:
334            self.avg_spec = first_spectra[['Frequency', 'Avg']].loc[:]
335            self.avg_spec.rename({'Avg': 'S',
336                                  'Frequency': 'freq'},
337                                 axis='columns', inplace=True)
338
339            self.num = first_spectra.shape[1] - 2
340            self.spectrum.number_of_first_spectra = self.num
341
342            self.spectrum.frequency_span_array = first_spectra['Frequency']
343            self.spectrum.finalize_first_spectrum(
344                first_spectra.drop('Avg', axis=1))
345        except Exception as e:
346            self.logger.error("Can not load variables: %s", e)
347            return False
348
349        try:
350            second_spectrum = pd.read_csv("%s_second.dat" % file)
351            second_spectrum_time = pd.read_csv("%s_second_time.dat" % file)
352        except FileNotFoundError:
353            self.logger.info('Second Spectrum (%s_second.dat) not found!',
354                             file)
355            return True
356
357        try:
358            self.spectrum.frequency_span_array_second_spectra = \
359                second_spectrum['Frequency']
360            self.spectrum.second_spectra = second_spectrum.drop('Frequency',
361                                                                axis=1)
362            self.spectrum.time_signal_second_spectrum = second_spectrum_time
363        except Exception as e:
364            self.logger.error("Can not load second spectrum variables: %s", e)
365            return False
366
367        self.logger.warning("Second Spectrum loaded but not all variables"
368                            "are implemented yet. Only access to \n"
369                            "- second_spectra\n"
370                            "- time_signal_second_spectrum\n"
371                            "- frequency_span_array_second_spectra")
372        return True

SA

  1class SA(SingleM):
  2    """Loads and analyzes datafiles from :instrument:`SR785`"""
  3    def __init__(self, *args, **kwargs):
  4        """
  5        Args:
  6            *args:
  7            **kwargs:
  8
  9        Returns:
 10            None
 11
 12        Raises:
 13            * ValueError
 14        """
 15        super().__init__()
 16        self.logger = logging.getLogger('SA')
 17        self.name = 'SA'
 18        try:
 19            self.data = kwargs.get('data', args[0])
 20        except Exception:
 21            error_message = "Not Able to find DataFrame. Please use SA_measurement(data)."
 22            self.logger.critical(error_message)
 23            raise ValueError(error_message)
 24
 25        if isinstance(self.data, dict):
 26            self.df = self.data.get('data')
 27            self.info = self.data.get('info')
 28        else:
 29            self.df = self.data
 30
 31        # Convert data to numeric values
 32        try:
 33            self.df.f = pd.to_numeric(self.df.f, errors='coerce')
 34            self.df.Vx = pd.to_numeric(self.df.Vx, errors='coerce')
 35            self.df.Vy = pd.to_numeric(self.df.Vy, errors='coerce')
 36        except Exception:
 37            error_message = "Unable to convert data to numeric."
 38            self.logger.critical(error_message)
 39            raise ValueError(error_message)
 40
 41        # Redirect functions
 42        self._draw_oneoverf = self.style.draw_oneoverf
 43        self._set_plot_settings = self.style.set_plot_settings
 44        self._calc_log = self.calc_log
 45
 46    def __repr__(self):
 47        ret = 'SA Measurement (Nr. %s)\n' % self.info.get('Nr', 0)
 48        for key, val in self.info.items():
 49            ret += '%s:\t%s\n' % (key, val)
 50        return ret
 51
 52
 53    def plot(self, **kwargs):
 54        """Plotting the Data on given Axis or creating a new Plot for plotting.
 55
 56        .. code-block:: python
 57           :linenos:
 58           :caption: Signature
 59
 60           plot(ax, label=None, color=None, linestyle=None,
 61               plot_x='f', plot_y='Vx', dont_set_plot_settings=False,
 62
 63                   xscale='log', yscale='log', xlim=(None, None), ylim=(None,
 64                   None), legend_location='best', grid=None, title=None,
 65                   xlabel='f [Hz]', ylabel='S_VH [V2/Hz]',
 66
 67           )
 68
 69        Args:
 70            **kwargs:
 71
 72        Note:
 73            If **dont_set_plot_settings** is True, all kwargs below are not
 74            used.
 75        """
 76        ax = kwargs.get('ax')
 77        if not ax:
 78            fig, ax = plt.subplots()
 79
 80        plotargs = {}
 81        if 'label' in kwargs:
 82            plotargs['label'] = kwargs.get('label')
 83        if 'color' in kwargs:
 84            plotargs['color'] = kwargs.get('color')
 85        if 'linestyle' in kwargs:
 86            plotargs['linestyle'] = kwargs.get('linestyle')
 87
 88        ax.plot(self.df[kwargs.get('plot_x', 'f')],
 89                self.df[kwargs.get('plot_y', 'Vx')],
 90                **plotargs)
 91
 92        if not (kwargs.get('dont_set_plot_settings', False)):
 93            self._set_plot_settings(**kwargs)
 94
 95    def fit(self, fit_range=(1e-2, 7e-1)):
 96        """Fits a linear regression
 97
 98        Args:
 99            fit_range (tuple, optional, default: (1e-2, 1e0).):
100        """
101        self.calc_log(self.df, ['f', 'Vx', 'Vy'])
102        xmin, xmax = fit_range
103
104        fit_area = self.df[self.df['f'] < xmax]
105        fit_area = fit_area[fit_area.f > xmin]
106        f = scipy.stats.linregress(fit_area.lnf, fit_area.lnVx)
107
108        self.info['Slope'] = f.slope
109        self.info['Intercept'] = f.intercept
110        return f.intercept, f.slope

Hloop

  1class Hloop(SingleM):
  2    """This class represents the measurement of a hysteresis loop. It evaluates
  3    and plots the data.
  4
  5    It collects all files that match the following regex:
  6
  7    .. code-block:: python
  8
  9       files = glob("data/Hloop/[mM]%s_*.dat" % measurement_number)
 10
 11    The files should contain a file that contains the word "up" or "down"
 12    indicating the direction of the field sweep.
 13
 14    If one file contains the word 'Gradio' it expects a gradiometry
 15    measurement. If the filename contains the word 'Parallel' it expects a
 16    parallel measurement. If the filename contains neither of these words the
 17    variables need to be set manually.
 18    """
 19
 20    def __init__(self, measurement_number, **kwargs):
 21        """
 22        Args:
 23            measurement_number:
 24            **kwargs:
 25        """
 26        super().__init__()
 27        self.logger = logging.getLogger('Hloop')
 28        self.name = 'Hloop'
 29        # Defining Constants
 30        header_gradio = ["Time", "B", "Vx8", "Vy8", "Vr8", "Vth8", "TempRO CU",
 31                         "TempRO YOKE", "TempCX CU", "SampTemp", "Temp1K Pot",
 32                         "TempCharcoal"]
 33        header_parallel = ["Time", "B", "Vx8", "Vy8", "Vr8", "Vth8", "Vx9",
 34                           "Vy9", "Vr9", "Vth9", "Vx13", "Vy13", "Vr13",
 35                           "Vth13", "TempRO CU", "TempRO YOKE", "TempCX CU",
 36                           "SampTemp", "Temp1K Pot", "TempCharcoal"]
 37        self.c_list = [0, 6.8, 10, 18, 27, 39, 56, 82, 100, 150, 180, 270,
 38                       330]  # Condensators in pF
 39        self.r_list = [0, 10, 100, 200, 300, 392, 475, 562, 619, 750, 825, 909,
 40                       1e3]  # Resistors in k Ohm
 41
 42        self.n = 1369288448319507.5  # Carrier Concentration calculated from perp. sweep
 43
 44        if isinstance(measurement_number, float) or isinstance(
 45                measurement_number, int):
 46            files = glob("data/Hloop/[mM]%s_*.dat" % measurement_number)
 47        else:
 48            files = glob(measurement_number)
 49        files = kwargs.get('file_list', files)
 50        if not (files):
 51            raise NameError(
 52                "No File for measurement Nr. %s found." % measurement_number)
 53
 54        for fname in files:
 55            file_info = None
 56            try:
 57                if fname.find('Gradio') > 0:
 58                    reg = re.match(
 59                        ".*[Mm]([0-9.]*)_([A-Za-z]*)_([0-9.-]*)deg_"
 60                        "([A-Za-z]*)_"
 61                        "([A-Za-z]*)_*([A-Za-z]*)_([0-9]*)_([0-9]*)_"
 62                        "I1-([0-9\-]*)_I2-([0-9\-]*)_G.*-([0-9\-]*)_"
 63                        "Vin-([0-9.]*)V_R11-([0-9]*.)O_R12-([0-9.]*.)O_"
 64                        "R13-([0-9.]*.)_R21-([0-9.]*.)O_"
 65                        "C11-([0-9]*)_C21-([0-9]*)_"
 66                        "T-([0-9]*)K_SR-([0-9.]*)-T-min",
 67                        fname)
 68                    if not reg:
 69                        raise NameError("%s not fitting Regex" % fname)
 70                    m_no, struct, deg, m_type, \
 71                        m_dir, m_type2, m_date, m_time, \
 72                        m_i1, m_i2, m_li1, \
 73                        m_vin, m_r11, m_r12, \
 74                        m_r13, m_r21, \
 75                        m_c11, m_c21, \
 76                        m_T, m_SR = reg.groups()
 77                    header = header_gradio
 78                    self.parallel = False
 79                elif fname.find('Parallel') > 0:
 80                    reg = re.match(
 81                        ".*[Mm]([0-9.]*)_([A-Za-z_]*)_([0-9.-]*)deg_([A-Za-z]*)_"
 82                        + "([A-Za-z]*)_*([A-Za-z]*)_([0-9]*)_([0-9]*)_" \
 83                        + "I-([0-9\-]*)_(G.*-[0-9\-]*)_" \
 84                        + "Vin-([0-9.]*)V_R11-([0-9]*.)O" \
 85                        + ".*_" \
 86                        + "T-([0-9]*)K_SR-([0-9.]*)-T-min",
 87                        fname)
 88                    if not reg:
 89                        raise NameError("%s not fitting Regex" % fname)
 90                    m_no, struct, deg, m_type, \
 91                    m_dir, m_type2, m_date, m_time, \
 92                    m_i1, m_li1, \
 93                    m_vin, m_r11, \
 94                    m_T, m_SR = reg.groups()
 95                    m_i2 = m_r12 = m_r13 = m_r21 = m_c11 = m_c21 = '0'
 96                    header = header_parallel
 97                    self.parallel = True
 98                else:
 99                    raise NameError("%s not fitting Regex" % fname)
100            except NameError:
101                file_info = self.get_info_from_name(fname)
102                
103                self.parallel = file_info.get('type2') == 'Parallel'
104                header = header_gradio if file_info.get('type2') == 'Gradio' else header_parallel
105                header = kwargs.get('header', header)
106                kwargs.update(file_info)
107
108                if isinstance(file_info.get('deg'), str) and \
109                        file_info.get('deg').find('neg') > -1:
110                    file_info['deg'] = file_info.get('deg').replace('neg', '-')
111                deg = file_info.get('deg') if file_info.get('deg') else 0.01
112                m_dir = file_info.get('dir', 'down').lower()
113
114                if fname.find('b.dat') > 0:
115                    m_dir = 'up'
116
117
118            ### Loading File
119            if (m_dir.lower() == 'up'):
120                self.fname_up = fname
121                self.up = pd.read_csv(fname, sep='\t', names=header,
122                                      skiprows=3)
123                self.up.B = self.up.B * 1e3
124            else:
125                self.fname_down = fname
126                self.down = pd.read_csv(fname, sep='\t', names=header,
127                                        skiprows=3)
128                self.down.B = self.down.B * 1e3
129
130        if (kwargs.get('down_only')):
131            self.up = self.down
132        if (kwargs.get('up_only')):
133            self.down = self.up
134
135        self.measurement_number = measurement_number
136        self.factor = 1
137
138        # Mirror all Angles bigger than 90
139        if kwargs.get('force_mirror') or \
140                (kwargs.get('auto_mirror', True) and \
141                 float(deg) >= 90):
142            self.set_factor(-1)
143            self.factor = 1
144
145        if file_info:
146            self.info = file_info
147            self.info.update({
148                'Type': "%s (%s)" % (file_info.get('type'),
149                                     file_info.get('type2')),
150                'Structure': file_info.get('struct'),
151                'Angle': file_info.get('deg'),
152            })
153        else:
154            m_datetime = "%s.%s.%s %s:%s" % (
155            m_date[6:], m_date[4:6], m_date[:4], m_time[:2], m_time[2:])
156            self.info = {
157                'Type': "%s (%s)" % (m_type, m_type2),
158                'Date': m_datetime,
159                'Structure': struct,
160                'Angle': deg,
161                'I1': m_i1,
162                'I2': m_i2,
163                'Vin': m_vin,
164                'R11': m_r11,
165                'R12': m_r12,
166                'R13': m_r13,
167                'R21': m_r21,
168                'C11': self.c_list[int(m_c11)],
169                'C21': self.c_list[int(m_c21)],
170                'T': m_T,
171                'SR': m_SR,
172                'Additional': ''}
173
174        # Update data from kwargs
175        for key, value in kwargs.items():
176            if key in self.info.keys():
177                self.info[key] = value
178
179    def set_factor(self, factor):
180        """Multiplying the Voltage by a Factor
181
182        Args:
183            factor:
184        """
185        self.up.Vx8 /= self.factor
186        self.down.Vx8 /= self.factor
187        if (self.parallel):
188            self.up.Vx9 /= self.factor
189            self.up.Vx13 /= self.factor
190            self.down.Vx9 /= self.factor
191            self.down.Vx13 /= self.factor
192
193        self.factor = factor
194
195        self.up.Vx8 *= self.factor
196        self.down.Vx8 *= self.factor
197        if (self.parallel):
198            self.up.Vx9 *= self.factor
199            self.up.Vx13 *= self.factor
200            self.down.Vx9 *= self.factor
201            self.down.Vx13 *= self.factor
202
203    def __repr__(self):
204        return self.get_default_title()
205
206    def calc_B(self, V):
207        """
208        Args:
209            V:
210        """
211        e = scipy.constants.physical_constants['electron volt'][0]
212        R = V / 2.5e-6
213        B = R * self.n * e
214        return B
215
216    def remove_zero(self, columns=None):
217        """Removes the values 0.0 from up and down sweep where the lock-in
218        didn't return any values (read error will be saved as 0.0).
219
220        Args:
221            columns:
222        """
223        # Set Columns to remove values from
224        if columns == None:
225            if self.parallel:
226                columns = ['Vx8', 'Vx9', 'Vx13']
227            else:
228                columns = ['Vx8']
229
230        # Remove zero values from columns (= measurement error)
231        for v in columns:
232            self.up = self.up[self.up[v] != 0.0]
233            self.down = self.down[self.down[v] != 0.0]
234
235    def fit(self, cond=pd.Series()):
236        """
237        Fitting the Voltage Signal.
238
239        For Parallel measurement:
240
241           the empty cross Vx13 is subtracted from the signal Vx8/Vx9
242
243        For gradiometry:
244
245           A condition can be added to determine the amount of
246           datapoints to fit (default: B < B_min + 150mT).
247
248        .. code-block:: python
249           :caption: Example
250
251            cond = (self.up.B < self.up.B.min() + 150)
252            m.fit(cond)
253
254        Args:
255            cond:
256        """
257        # Set Fitting Condition
258        if (cond.empty):
259            cond = (self.up.B < self.up.B.min() + 150)
260
261        ### Fitting Graph
262        self.up_fitted = self.up.copy()
263        self.down_fitted = self.down.copy()
264
265        if (self.parallel):
266            self.up_fitted.Vx8 -= self.up_fitted.Vx13
267            self.up_fitted.Vx9 -= self.up_fitted.Vx13
268            self.down_fitted.Vx8 -= self.down_fitted.Vx13
269            self.down_fitted.Vx9 -= self.down_fitted.Vx13
270        else:
271            fit_area = self.up[cond].copy()
272            fit = scipy.stats.linregress(fit_area.B, fit_area.Vx8)
273            self.up_fitted['fit'] = (
274                        self.up_fitted.B * fit.slope + fit.intercept)
275            self.down_fitted['fit'] = (
276                        self.down_fitted.B * fit.slope + fit.intercept)
277            fit_area['fit'] = (fit_area.loc[:,
278                               'B'].to_numpy() * fit.slope + fit.intercept)
279            self.up_fitted.Vx8 = self.up_fitted.Vx8 - self.up_fitted.fit
280            self.down_fitted.Vx8 = self.down_fitted.Vx8 - self.down_fitted.fit
281
282    def plot_hloop(self, ax, figtitle="", show_fitted=True,
283                   show_rem=False, show_coer=False,
284                   **kwargs):
285        """Plotting the hysteresis loop.
286
287        :param ax: Matplotlib Axis to plot on.
288        :param figtitle: can be set to a manual figure title.
289        :param show_fitted: plot the fitted curve.
290        :param show_rem: show the remanent voltage.
291        :param show_coer: show the coercive field.
292        :param show_original: plots the RAW data.
293        :param show_linear_fit: plots the linear fitting line used for the fit
294        (only gradiometry).
295        """
296
297        if kwargs.get('show_original'):  # Drawing RAW Data
298            ax.plot(self.up.B, self.up.Vx8, label="up")
299            ax.plot(self.down.B, self.down.Vx8, label='down')
300            if (self.parallel):
301                ax.plot(self.up.B, self.up.Vx9, label="up (LI 9)")
302                ax.plot(self.down.B, self.down.Vx9, label='down (LI 9)')
303                ax.plot(self.up.B, self.up.Vx13, label="up (LI 13)")
304                ax.plot(self.down.B, self.down.Vx13, label='down (LI 13)')
305
306        if show_fitted:  # Drawing fitted data
307            # Already fitted?
308            if not (hasattr(self, 'down_fitted')):
309                self.fit()
310            if (self.parallel):
311                ax.plot(self.up_fitted.B, self.up_fitted.Vx8,
312                        label='Plusses: Up (fitted)')
313                ax.plot(self.down_fitted.B, self.down_fitted.Vx8,
314                        label='Plusses: Down (fitted)')
315                ax.plot(self.up_fitted.B, self.up_fitted.Vx9,
316                        label='Crosses: Up (fitted)')
317                ax.plot(self.down_fitted.B, self.down_fitted.Vx9,
318                        label='Crosses: Down (fitted)')
319            else:
320                ax.plot(self.up_fitted.B, self.up_fitted.Vx8,
321                        label='Up (fitted)')
322                ax.plot(self.down_fitted.B, self.down_fitted.Vx8,
323                        label='Down (fitted)')
324                if kwargs.get('show_linear_fit'):  # Drawing linear Fit
325                    ax.plot(self.up_fitted.B, self.up_fitted.fit,
326                            label='Linear Fit')
327
328        ### Remanent Field ###
329        if show_rem:
330            rem1, rem2 = self.get_remanence()
331            ax.plot([0, 0],
332                    [self.up_fitted.Vx8.min(), self.down_fitted.Vx8.max()],
333                    'r-.', linewidth='.5')
334            ax.plot(rem1['B'], rem1['Vx8'], 'bo', markersize=12)
335            ax.plot(rem2['B'], rem2['Vx8'], 'bo', markersize=12)
336            ax.annotate("$V_{rem} = %.3f \\mu V$" % rem1['Vx8'],
337                        xy=(rem1['B'], rem1['Vx8']),
338                        xytext=(150, -100), textcoords='offset points',
339                        arrowprops={'arrowstyle': '->', 'color': 'black'})
340            ax.annotate("$V_{rem} = %.3f \\mu V$" % rem2['Vx8'],
341                        xy=(rem2['B'], rem2['Vx8']),
342                        xytext=(-150, 100), textcoords='offset points',
343                        arrowprops={'arrowstyle': '->', 'color': 'black'})
344
345        ### Coercive Field
346        if show_coer:
347            mean, coer, coer2 = self.get_coercive_field()
348            ax.plot([self.up_fitted.B.min(), self.up_fitted.B.max()],
349                    [mean, mean], 'r-.', linewidth='.5')
350            ax.plot(coer['B'], coer['Vx8'], 'go', markersize=12)
351            ax.plot(coer2['B'], coer2['Vx8'], 'go', markersize=12)
352            ax.annotate("$B_{coer} = %.4f T$" % coer.B,
353                        xy=(coer['B'], coer['Vx8']),
354                        xytext=(50, -30), textcoords='offset points',
355                        arrowprops={'arrowstyle': '->', 'color': 'black'})
356            ax.annotate("$B_{coer} = %.4f T$" % coer2.B,
357                        xy=(coer2['B'], coer2['Vx8']),
358                        xytext=(-200, 20), textcoords='offset points',
359                        arrowprops={'arrowstyle': '->', 'color': 'black'})
360
361        # Making a nice plot
362        ax.legend(loc='best')
363        bmin, bmax, vmin, vmax = self.get_minmax()
364        ax.set_xlim(bmin, bmax)
365        ax.set_xlabel("$B\\;[\\mathrm{mT}]$")
366
367        # Setting correct Y Label
368        yunit = '$V_x$'
369        if (self.factor == 1e3):
370            yunit += ' $[\\mathrm{mV}]$'
371        elif (self.factor == 1e6):
372            yunit += ' $[\\mathrm{\\mu V}]$'
373
374        ax.set_ylabel("%s" % yunit)
375
376        if (figtitle == ''):
377            figtitle = self.get_default_title()
378        ax.set_title(figtitle)
379
380    def calculate_strayfield(self):
381        """Calculates the strayfield of the signal and stores it in up and down
382        sweeps.
383        """
384        self.up['Bx8'] = self.calc_B(self.up.Vx8)
385        self.down['Bx8'] = self.calc_B(self.down.Vx8)
386        if (self.parallel):
387            self.up['Bx9'] = self.calc_B(self.up.Vx9)
388            self.down['Bx9'] = self.calc_B(self.down.Vx9)
389            self.up['Bx13'] = self.calc_B(self.up.Vx13)
390            self.down['Bx13'] = self.calc_B(self.down.Vx13)
391
392    def calculate_fitted_strayfield(self):
393        """Calculates the strayfield of the fitted signal and stores it in up
394        and down sweeps.
395        """
396        self.up_fitted['Bx8'] = self.calc_B(self.up_fitted.Vx8)
397        self.down_fitted['Bx8'] = self.calc_B(self.down_fitted.Vx8)
398        if (self.parallel):
399            self.up_fitted['Bx9'] = self.calc_B(self.up_fitted.Vx9)
400            self.down_fitted['Bx9'] = self.calc_B(self.down_fitted.Vx9)
401            self.up_fitted['Bx13'] = self.calc_B(self.up_fitted.Vx13)
402            self.down_fitted['Bx13'] = self.calc_B(self.down_fitted.Vx13)
403
404    def plot_strayfield(self, ax, figtitle="", **kwargs):
405        """Plots the strayfield of the data. Strayfield is calculated using the
406        electron concentration at 0 degree measured before
407        (hardcoded in __init__).
408
409        Args:
410            ax (matplotlib.pyplot.axis): where should we plot.
411            figtitle (str, optional): Choose your own title above the figure.
412            **kwargs:
413
414        Returns:
415            None.:
416        """
417        self.set_factor(kwargs.get('factor', 1e3))
418        self.calculate_strayfield()
419
420        if kwargs.get('show_original'):  # Drawing RAW Data
421            ax.plot(self.up.B, self.up.Bx8, label="up")
422            ax.plot(self.down.B, self.down.Bx8, label='down')
423
424        if kwargs.get('show_fitted', True):  # Drawing fitted data
425            # Already fitted?
426            if not (hasattr(self, 'down_fitted')):
427                self.fit()
428            self.calculate_fitted_strayfield()
429
430            if self.parallel:
431                if kwargs.get('show_plusses', True):
432                    ax.plot(self.up_fitted.B, self.up_fitted.Bx8,
433                            label='Plusses: Up (fitted)')
434                    ax.plot(self.down_fitted.B, self.down_fitted.Bx8,
435                            label='Plusses: Down (fitted)')
436
437                if kwargs.get('show_crosses', True):
438                    ax.plot(self.up_fitted.B, self.up_fitted.Bx9,
439                            label='Crosses: Up (fitted)')
440                    ax.plot(self.down_fitted.B, self.down_fitted.Bx9,
441                            label='Crosses: Down (fitted)')
442            else:
443                ax.plot(self.up_fitted.B, self.up_fitted.Bx8,
444                        label='Up (fitted)')
445                ax.plot(self.down_fitted.B, self.down_fitted.Bx8,
446                        label='Down (fitted)')
447
448        # Making a nice plot
449        if not (kwargs.get('nolegend')):
450            ax.legend(loc='best')
451        bmin, bmax, vmin, vmax = self.get_minmax()
452        ax.set_xlim(bmin, bmax)
453        ax.set_xlabel("$\\mu_0 H_{ext}$ $[\\mathrm{mT}]$")
454
455        # Setting correct Y Label
456        bhmin, bhmax = self.get_bhminmax()
457        ax.set_ylim(bhmin - .05, bhmax + .05)
458        ax.set_ylabel("$\\langle B_z \\rangle$ $[\\mathrm{mT}]$")
459
460        # Setting Title
461        if (figtitle == ''):
462            figtitle = self.get_default_title()
463            if (self.parallel):
464                figtitle = "M%s: $%s^\\circ$" % (self.measurement_number,
465                                                 self.info['Angle'])
466        ax.set_title(figtitle)
467
468    def plot_downminusup(self, ax, figtitle=""):
469        """Plotting the difference between the down and up sweep. ax: Matplotlib
470        Axis to plot on. figtitle: can be set to a manual figure title.
471
472        Args:
473            ax:
474            figtitle:
475        """
476        B, vx = self.get_downminusup()
477        ax.plot(B, vx)
478        ax.set_xlabel("$B [\\mathrm{mT}]$")
479        ax.set_ylabel("$V_x [\\mathrm{\\mu V}]$")
480        if (figtitle == ''):
481            figtitle = self.get_default_title()
482        ax.set_title(figtitle)
483
484    def get_downminusup(self, n=1e5):
485        """Returns the magnetic field and difference between down and up sweep.
486
487        .. code-block:: python
488           :caption: Example
489
490            B, Vx = meas.get_downminusup() plt.plot(B, Vx)
491
492        Args:
493            n:
494        """
495        f_up = scipy.interpolate.interp1d(self.up.B, self.up.Vx8)
496        f_down = scipy.interpolate.interp1d(self.down.B, self.down.Vx8)
497        B = np.linspace(self.up.B.min(), self.up.B.max(), int(n))
498        downminusup = f_down(B) - f_up(B)
499        return B, downminusup
500
501    def get_downminusup_strayfield(self, n=1e5, fitted_data=False):
502        """Returns the magnetic field and difference between down and up sweep.
503
504        .. code-block:: python
505           :caption: Example
506
507           B_ext, Bx = meas.get_downminusup_strayfield() plt.plot(B_ext, Bx)
508
509        Args:
510            n:
511            fitted_data:
512        """
513        if fitted_data:
514            up = self.up_fitted
515            down = self.down_fitted
516        else:
517            up = self.up
518            down = self.down
519        f_up = scipy.interpolate.interp1d(up.B, up.Bx8)
520        f_down = scipy.interpolate.interp1d(down.B, down.Bx8)
521        B = np.linspace(up.B.min(), up.B.max(), int(n))
522        downminusup = f_down(B) - f_up(B)
523        return B, downminusup
524
525    def get_coercive_field(self):
526        """Returns the mean and coercive fields calculated.
527
528        .. code-block:: python
529           :caption: Example
530
531           mean, coer1, coer2 = meas.get_coercive_field()
532           print('Coercive Field (up sweep): (%.3f, %.3f)' % (coer1, mean))
533           print('Coercive Field (down sweep): (%.3f, %.3f)' % (coer2, mean))
534        """
535        mean = (self.up_fitted['Vx8'].min() + self.down_fitted[
536            'Vx8'].max()) / 2
537        coer = self.up_fitted.iloc[np.abs(self.up_fitted[ \
538                                              self.up_fitted.B.abs() < 350][
539                                              'Vx8'] - mean).argmin()]
540        coer2 = self.down_fitted.iloc[np.abs(self.down_fitted[ \
541                                                 self.down_fitted.B.abs() < 350][
542                                                 'Vx8'] - mean).argmin()]
543        return mean, coer, coer2
544
545    def plot_hysteresis(self, y="B", **kwargs):
546        fig, ax = plt.subplots(figsize=self.style.get('figsize'))
547        
548        if y == 'B':
549            self.plot_strayfield(ax, **kwargs)
550        elif y == 'V':
551            self.plot_hloop(ax, **kwargs)
552        else:
553            self.plot_downminusup(ax, **kwargs)
554
555    def get_remanence(self):
556        """Returns the remanent voltage calculated.
557
558        .. code-block:: python
559           :caption: Example
560
561           rem1, rem2 = meas.get_remanence()
562           print('Remanent Voltage (up sweep): (%d, %.3f)' % (0, rem1))
563           print('Remanent Voltage (down sweep): (%d, %.3f)' % (0, rem2))
564        """
565        rem1 = self.up_fitted.iloc[self.up_fitted.B.abs().idxmin()]
566        rem2 = self.down_fitted.iloc[self.down_fitted.B.abs().idxmin()]
567        return rem1, rem2
568
569    def get_minmax(self):
570        """Returns the minimum and maximum of the field and voltage.
571
572        .. code-block:: python
573           :caption: Example
574
575           bmin, bmax, vmin, vmax = meas.get_minmax()
576        """
577        bmin = np.min([self.down.B.min(), self.up.B.min()])
578        bmax = np.max([self.down.B.max(), self.up.B.max()])
579        if (self.parallel):
580            max_8 = self.up.Vx8.max()
581            max_9 = self.up.Vx9.max()
582            max_13 = self.up.Vx13.max()
583            vmax = np.max([max_8, max_9, max_13])
584
585            min_8 = self.up.Vx8.min()
586            min_9 = self.up.Vx9.min()
587            min_13 = self.up.Vx13.min()
588            vmin = np.min([min_8, min_9, min_13])
589        else:
590            vmax = self.up.Vx8.max()
591            vmin = self.up.Vx8.min()
592
593        return bmin, bmax, vmin, vmax
594
595    def get_bhminmax(self):
596        """Returns the minimum and maximum of the measured hall voltage
597        converted to strayfield.
598
599        .. code-block:: python
600           :caption: Example
601
602           bhmin, bhmax = meas.get_bhminmax()
603        """
604        max_8 = np.max([self.up_fitted.Bx8.max(),
605                        self.down_fitted.Bx8.max()])
606        min_8 = np.min([self.up_fitted.Bx8.min(),
607                        self.down_fitted.Bx8.min()])
608        if (self.parallel):
609            max_9 = np.max([self.up_fitted.Bx9.max(),
610                            self.down_fitted.Bx9.max()])
611            vmax = np.max([max_8, max_9])
612
613            min_9 = np.min([self.up_fitted.Bx9.min(),
614                            self.down_fitted.Bx9.min()])
615            vmin = np.min([min_8, min_9])
616        else:
617            vmax = max_8
618            vmin = min_8
619
620        return vmin, vmax
621
622    def get_default_title(self):
623        if (self.parallel):
624            return "m%s: $%s^\\circ$" % (self.measurement_number,
625                                         self.info['Angle'])
626        return 'm%s: %s ($%s^\\circ$)' % (self.measurement_number,
627                                          self.info['Structure'],
628                                          self.info['Angle'])

MultipleM

 1class MultipleM(MeasurementClass):
 2    def __init__(self):
 3        """MultipleM:
 4
 5        Main parent class for all multiple measurements. These Measurements
 6        are represent by one data variable.
 7
 8        All multiple measurements have a variable called ``multi_info`` which
 9        contains a pandas.DataFrame with parameter settings to single
10        measurements.
11
12        All multiple measurements can be represented by
13
14            >>> m = ana.MultipleM()
15            >>> m
16            Nr. 0: Hloop
17            Nr. 0: RAW
18        """
19        
20        super().__init__()
21        self.multi_info = pd.DataFrame()
22        if os.path.exists('data/mfn_info.csv'):
23            self.read_csv('data/mfn_info.csv')
24    
25    
26    query = lambda self, request: self.multi_info.query(request)
27
28    def get_lofm_sweeps(self, to_show, label, options={}):
29        """Workaround to get list of measurements for sweeps.
30
31        Args:
32            to_show (dict): Measurement Numbers pointing to label format
33                variables: e.g. `` {nr: [-1, 1, 'Plusses', '2 mT/min']}
34            label (str): Label to show in plots: e.g. "Show %s -> %s (%s) %s "
35                -> will be formated using to_show
36            options (dict, default: empty): Additional plotting options
37
38        Returns:
39            dict: List of Measurements with description
40        """
41        
42        return self.get_lofm(to_show, 
43                             label, 
44                             options)
45
46    def read_csv(self, *args, **kwargs): 
47        """Updates multi_info DataFrame. Reads csv and adds information to
48        self.multi_info
49
50        Args:
51            *args:
52            **kwargs:
53
54        Returns:
55            None
56        """
57
58        self.multi_info = pd.concat([self.multi_info, pd.read_csv(*args, **kwargs)])
59
60    def get_lofm(self, to_show, label, options={}):
61        """Returns the List of Measurements (lofm) for plot functions
62            (see :meth:`ana.HandleM.plot`).
63
64        Args:
65            to_show (dict): Measurement Numbers pointing to label format
66                variables: e.g. ``{nr: [(-1, 1), 'Plusses', '5 mT/min']}``
67            label (str): Label to show in plots: e.g.
68                ``"Show %s -> %s (%s) %s "`` -> will be string formated using
69                to_show
70            options (dict, optional, default: empty): Additional plotting
71                parameters
72
73        Returns:
74            dict: List of Measurements with description: ``{nr: [labels]}``
75        """
76        lofm = {}
77        for nr, content in to_show.items():
78            if isinstance(content[0], tuple):
79                form = content[0][0], content[0][1], content[1], content[2]
80            # elif isinstance(content[0], str) or \
81            #         isinstance(content[0], int) or \
82            #         isinstance(content[0], float):
83            #    form = content[0], content[1], content[2]
84            else:
85                form = content
86                
87            lofm[nr] = [label % form, options]
88
89        return lofm

PlottingClass

  1class PlottingClass(object):
  2    """Defines the basic plotting style."""
  3
  4    def __init__(self, **style):
  5        """
  6        Args:
  7            **style:
  8        """
  9        default_style = {
 10            'dpi': 300,
 11            'figure.autolayout': False,
 12            'figsize': (8, 6),
 13            'axes.labelsize': 18,
 14            'axes.titlesize': 20,
 15            'font.size': 16,
 16            'lines.linewidth': 2.0,
 17            'lines.markersize': 8,
 18            'legend.fontsize': 14,
 19            'mpl_style': False,
 20            'latex': True,
 21            'default_cm': cm.Blues,
 22        }
 23
 24        self.name = 'Default Plotting Class'
 25        self.style = default_style
 26        self.set_style(**style)
 27
 28    get = lambda self, *key: self.style.get(*key)
 29
 30    __repr__ = lambda self: '%s\n' % self.name
 31
 32    __str__ = lambda self: '%s\n' % self.name + \
 33                           '\n'.join(['%s:\t%s' % (key, val) for key, val in self.style.items()])
 34
 35    def update(self, other):
 36
 37        """
 38        Args:
 39            other:
 40        """
 41        self.style.update(other)
 42
 43    def __setitem__(self, key, value):
 44        """
 45        Args:
 46            key:
 47            value:
 48        """
 49        self.update(dict(key=value))
 50
 51    def __add__(self, other):
 52        self.update(other)
 53
 54    # Set Plotting Style
 55    def set_style(self, **style):
 56        """
 57        Sets the Style for plots
 58    
 59        :param size: A dictionary of parameters or the name of a preconfigured set.
 60        :type size: dict, None, or one of {paper, notebook, talk, poster}
 61
 62        :param style:
 63        :type style: dict, None, or one of {darkgrid, whitegrid, dark, white, ticks}
 64    
 65        :param default: if True it sets default
 66            :code:`size=talk` and
 67            :code:`style=whitegrid`
 68        :type default: bool
 69
 70        :param notebook: if True it sets default
 71            :code:`size=notebook` and
 72            :code:`style=ticks`
 73
 74        :type notebook: bool
 75
 76    
 77        Returns
 78        -------
 79        None.
 80    
 81        """
 82        self.style.update(style)
 83        if self.get('default'):
 84            def_size = 'talk'
 85            def_style = 'whitegrid'
 86        elif self.get('notebook'):
 87            def_size = 'notebook'
 88            def_style = 'ticks'
 89        else:
 90            def_size = 'talk'
 91            def_style = 'whitegrid'
 92
 93        sns.set(self.get('size', def_size),
 94                self.get('style', def_style),
 95                self.get('palette', 'deep'),
 96                self.get('font', 'sans-serif'),
 97                self.get('font-scale', 1))
 98
 99        if style.get('grid'):
100            plt.rcParams['axes.grid'] = True
101            plt.rcParams['grid.linestyle'] = '--'
102
103        latex = 'latex' in self.style
104        plt.rcParams['text.usetex'] = latex
105        if latex:  # True activates latex output in fonts!
106            plt.rcParams[
107                'text.latex.preamble'] = ''
108
109        if self.get('set_mpl_params', False):
110            params = style.get('mpl_params', {
111                'figure.dpi': self.get('dpi', 300),
112                'savefig.dpi': self.get('dpi', 300),
113                'figure.figsize': self.get('figsize'),
114                'figure.autolayout': False,
115                'axes.labelsize': 18,
116                'axes.titlesize': 20,
117                'font.size': 16,
118                'lines.linewidth': 2.0,
119                'lines.markersize': 8,
120                'legend.fontsize': 14,
121                'figure.subplot.hspace': 0.3,
122                'figure.subplot.wspace': 0.3,
123                'savefig.transparent': False,
124                'savefig.bbox': 'tight',
125                'savefig.pad_inches': 0.1,
126            })
127            update_keys = ['figure.autolayout',
128                           'axes.labelsize',
129                           'axes.titlesize',
130                           'font.size',
131                           'lines.linewidth',
132                           'lines.markersize',
133                           'legend.fontsize']
134            params.update({
135                update_keys[key]: self.get(update_keys[key]) \
136                for key in np.argwhere([_ in style \
137                                        for _ in update_keys]).ravel()
138            })
139            matplotlib.rcParams.update(params)
140
141        if self.get('mpl_style'):
142            matplotlib.style.use(self.get('mpl_style'))
143
144    def get_style(self, custom=None):
145        """
146        Args:
147            custom:
148        """
149        if custom:
150            return plt.style.context(custom)
151        return plt.style.context(self.get('mpl_style'))
152
153    def set_plot_settings(self, **kwargs):
154        """
155        Args:
156            **kwargs:
157        """
158        plt.xscale(kwargs.get('xscale', 'log'))
159        plt.yscale(kwargs.get('yscale', 'log'))
160
161        xmin, xmax = kwargs.get('xlim', (None, None))
162        ymin, ymax = kwargs.get('ylim', (None, None))
163        if xmin:
164            plt.xlim(xmin, xmax)
165        if ymin:
166            plt.ylim(ymin, ymax)
167        if not kwargs.get('legend_settings'):
168            plt.legend(loc=kwargs.get('legend_location', 'best'))
169        else:
170            plt.legend(**kwargs.get('legend_settings'))
171
172        if kwargs.get('grid'):
173            plt.grid(b=True, **kwargs['grid'])
174
175        title = kwargs.get('title')
176        if title:
177            plt.title(title)
178
179        xlabel = kwargs.get('xlabel', '$f$ [Hz]')
180        ylabel = kwargs.get('ylabel', '$S_{V_H}$ [${V^2}$/{Hz}]')
181        plt.xlabel(xlabel)
182        plt.ylabel(ylabel)
183
184    def draw_oneoverf(self, ax, **kwargs):
185        # Draw 1/f
186        """
187        Args:
188            ax:
189            **kwargs:
190        """
191        xmin = kwargs.get('xmin', .1)
192        ymin = kwargs.get('ymin', 1e-6)
193        factor = kwargs.get('factor', 10)
194        alpha = kwargs.get('alpha', 1)
195        if kwargs.get('plot_label'):
196            label = kwargs.get('plot_label')
197        elif alpha == 1:
198            label = '$1/f$'
199        else:
200            label = '$1/f^%s$' % alpha
201
202        mean_x = kwargs.get('mean_x', (xmin * np.sqrt(factor)))
203        mean_y = kwargs.get('mean_y', (ymin / (factor ** (alpha / 2))))
204        if not (kwargs.get('disable')):
205            ax.plot([xmin, xmin * factor], [ymin, ymin / (factor ** alpha)],
206                    kwargs.get('plot_style', 'k--'), label=label, linewidth=2)
207            ax.annotate(label, (mean_x, mean_y),
208                        color=kwargs.get('an_color', 'black'),
209                        size=kwargs.get('an_size', 16))
210
211    def save_plot(self, fname, fext='png'):
212        """Saves the current plot as file.
213
214        Args:
215            fname (str): Filename
216            fext (str): Extension
217
218        Returns:
219            None
220        """
221
222        base = os.path.basename(fname)
223        if not os.path.exists(base):
224            os.makedirs(base)
225
226        if fext == 'pgf':
227            matplotlib.rcParams.update({
228                "pgf.texsystem": "pdflatex",
229                'font.family': 'serif',
230                'text.usetex': True,
231                'pgf.rcfonts': False,
232                'text.latex.preamble': r'\usepackage[utf8]{inputenc}\DeclareUnicodeCharacter{2212}{-}'
233            })
234            plt.savefig('%s.%s' % (fname, fext), backend='pgf')
235            plt.show()
236
237        else:
238            plt.savefig('%s.%s' % (fname, fext))

Functions

 1# SPDX-FileCopyrightText: 2020/2021 Jonathan Pieper <ody55eus@mailbox.org>
 2#
 3# SPDX-License-Identifier: GPL-3.0-or-later
 4
 5"""
 6Helps to handle measurements and style plots.
 7"""
 8
 9from glob import glob
10
11from functools import wraps
12from time import time
13
14from .plotting import PlottingClass
15from .measurement import MeasurementClass
16
17plot = PlottingClass()
18set_sns = plot.set_style
19set_plot_settings = plot.set_plot_settings
20
21meas = MeasurementClass()
22get_info_from_name = meas.get_info_from_name
23
24
25def getpath(*arg):
26    """Alias for ``glob``.
27
28    Args:
29        *arg:
30    """
31    return glob(*arg)
32
33
34def timing(f):
35    """Wraps a function to measure the time it takes.
36
37    Args:
38        f:
39    """
40    @wraps(f)
41    def wrap(*args, **kw):
42        ts = time()
43        result = f(*args, **kw)
44        te = time()
45        print('func:%r took: %2.4f sec' % \
46          (f.__name__, te-ts))
47        return result
48    return wrap