diff --git a/.gitignore b/.gitignore index c317064..93750fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,17 @@ +# Python-generated files AGENTS.md +__pycache__/ +*.py[oc] +build/ +dist/ +wheels/ +*.egg-info + +# Documentation build output +docs/build/ + +# Virtual environments +.venv + +# Extracted test data (kept as ZIP archive) +tests/data/test_loading/ diff --git a/docs/build/doctrees/api.doctree b/docs/build/doctrees/api.doctree deleted file mode 100644 index 90c95b5..0000000 Binary files a/docs/build/doctrees/api.doctree and /dev/null differ diff --git a/docs/build/doctrees/contributing.doctree b/docs/build/doctrees/contributing.doctree deleted file mode 100644 index b0a1f2d..0000000 Binary files a/docs/build/doctrees/contributing.doctree and /dev/null differ diff --git a/docs/build/doctrees/development.doctree b/docs/build/doctrees/development.doctree deleted file mode 100644 index 4906f73..0000000 Binary files a/docs/build/doctrees/development.doctree and /dev/null differ diff --git a/docs/build/doctrees/environment.pickle b/docs/build/doctrees/environment.pickle deleted file mode 100644 index fe5489c..0000000 Binary files a/docs/build/doctrees/environment.pickle and /dev/null differ diff --git a/docs/build/doctrees/examples.doctree b/docs/build/doctrees/examples.doctree deleted file mode 100644 index 42cd537..0000000 Binary files a/docs/build/doctrees/examples.doctree and /dev/null differ diff --git a/docs/build/doctrees/index.doctree b/docs/build/doctrees/index.doctree deleted file mode 100644 index db567f7..0000000 Binary files a/docs/build/doctrees/index.doctree and /dev/null differ diff --git a/docs/build/doctrees/installation.doctree b/docs/build/doctrees/installation.doctree deleted file mode 100644 index 3532ca0..0000000 Binary files a/docs/build/doctrees/installation.doctree and /dev/null differ diff --git a/docs/build/doctrees/introduction.doctree b/docs/build/doctrees/introduction.doctree deleted file mode 100644 index c56e0f3..0000000 Binary files a/docs/build/doctrees/introduction.doctree and /dev/null differ diff --git a/docs/build/doctrees/license.doctree b/docs/build/doctrees/license.doctree deleted file mode 100644 index cd2d89b..0000000 Binary files a/docs/build/doctrees/license.doctree and /dev/null differ diff --git a/docs/build/html/.buildinfo b/docs/build/html/.buildinfo deleted file mode 100644 index b86aef7..0000000 --- a/docs/build/html/.buildinfo +++ /dev/null @@ -1,4 +0,0 @@ -# Sphinx build info version 1 -# This file records the configuration used when building these files. When it is not found, a full rebuild will be done. -config: a6a60257adb09a6b8793dd150e758cef -tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/build/html/.buildinfo.bak b/docs/build/html/.buildinfo.bak deleted file mode 100644 index a3aaac2..0000000 --- a/docs/build/html/.buildinfo.bak +++ /dev/null @@ -1,4 +0,0 @@ -# Sphinx build info version 1 -# This file records the configuration used when building these files. When it is not found, a full rebuild will be done. -config: 69ef75d2d28c03cf9e107b4a1f000fd6 -tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/build/html/.doctrees/api.doctree b/docs/build/html/.doctrees/api.doctree deleted file mode 100644 index 90c95b5..0000000 Binary files a/docs/build/html/.doctrees/api.doctree and /dev/null differ diff --git a/docs/build/html/.doctrees/contributing.doctree b/docs/build/html/.doctrees/contributing.doctree deleted file mode 100644 index 3008315..0000000 Binary files a/docs/build/html/.doctrees/contributing.doctree and /dev/null differ diff --git a/docs/build/html/.doctrees/development.doctree b/docs/build/html/.doctrees/development.doctree deleted file mode 100644 index 4906f73..0000000 Binary files a/docs/build/html/.doctrees/development.doctree and /dev/null differ diff --git a/docs/build/html/.doctrees/environment.pickle b/docs/build/html/.doctrees/environment.pickle deleted file mode 100644 index a6b719f..0000000 Binary files a/docs/build/html/.doctrees/environment.pickle and /dev/null differ diff --git a/docs/build/html/.doctrees/examples.doctree b/docs/build/html/.doctrees/examples.doctree deleted file mode 100644 index 42cd537..0000000 Binary files a/docs/build/html/.doctrees/examples.doctree and /dev/null differ diff --git a/docs/build/html/.doctrees/index.doctree b/docs/build/html/.doctrees/index.doctree deleted file mode 100644 index db567f7..0000000 Binary files a/docs/build/html/.doctrees/index.doctree and /dev/null differ diff --git a/docs/build/html/.doctrees/installation.doctree b/docs/build/html/.doctrees/installation.doctree deleted file mode 100644 index 3532ca0..0000000 Binary files a/docs/build/html/.doctrees/installation.doctree and /dev/null differ diff --git a/docs/build/html/.doctrees/introduction.doctree b/docs/build/html/.doctrees/introduction.doctree deleted file mode 100644 index c56e0f3..0000000 Binary files a/docs/build/html/.doctrees/introduction.doctree and /dev/null differ diff --git a/docs/build/html/.doctrees/license.doctree b/docs/build/html/.doctrees/license.doctree deleted file mode 100644 index cd2d89b..0000000 Binary files a/docs/build/html/.doctrees/license.doctree and /dev/null differ diff --git a/docs/build/html/_modules/index.html b/docs/build/html/_modules/index.html deleted file mode 100644 index ad0c9d4..0000000 --- a/docs/build/html/_modules/index.html +++ /dev/null @@ -1,122 +0,0 @@ - - - - -
- - -
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-(c) M. Sc. Florian Quintes, 2021-2026
-
-@contact: florian.quintes@pc.uni.freiburg.de
-
-@author: Florian Quintes
-"""
-
-from typing import Any, Sequence
-
-import numpy as np
-from numpy.typing import NDArray
-from static_radical_pair.radpair import do_simulation_multicore, do_simulation
-from teacups.simulations import teacups_multicore
-from teacups.simulations import teacups
-from oop_eseem.opossum import (
- oop_eseem,
- oop_eseem_distribution,
- _get_multi_gauss_distribution,
-)
-
-from PySpin.plotter import plot
-import scipy.optimize as optimize
-from copy import deepcopy
-from functools import partial
-
-
-
-[docs]
-def plot_callback(
- xk: NDArray[np.float64],
- *_,
- Sys: Any = None,
- Exp: Any = None,
- SimOpt: Any = None,
- FitOpt: Any = None,
- Var: Any = None,
- **_kwargs,
-) -> None:
- """
- Plot the current optimization state in the graphical user interface.
-
- Parameters
- ----------
- xk : np.array
- Current best guess vector.
- *_ : arbitrary
- Unused arguments passed by some optimization routines to the callback
- function.
- Sys : object
- Reference spin-system object.
- Exp : object
- Experimental data object.
- SimOpt : object
- Simulation options object.
- Var : object
- Variation object describing the fitted parameters.
- FitOpt : object
- Fitting options object.
- *_kwargs : arbitrary
- Unused keyword arguments passed by some optimization routines to the
- callback function.
-
- Raises
- ------
- ValueError
- Raised if the selected simulation routine is unknown.
-
- Returns
- -------
- None.
-
- """
- current_Sys = guess2Sys(xk, Sys, Var, SimOpt)
- SimOpt.mode = "simulation"
-
- if SimOpt.routine.lower() == "static_radpair":
- simulated_spectra = do_simulation_multicore(current_Sys, Exp, SimOpt)
- elif SimOpt.routine.lower() == "teacups":
- simulated_spectra = teacups_multicore(current_Sys, Exp, SimOpt)
- elif SimOpt.routine.lower() == "opossum":
- simulated_spectra = oop_eseem(current_Sys, Exp, SimOpt)
- elif SimOpt.routine.lower() == "didelphis":
- simulated_spectra = oop_eseem_distribution(current_Sys, Exp, SimOpt)
- else:
- raise ValueError("Can't find a routine named '{0}'!".format(SimOpt.routine))
-
- simulated_spectra /= (abs(simulated_spectra)).max()
- Exp.spec_sim = simulated_spectra
- data = np.array([Exp.spec_sim, Exp.int])
- label = ["current best Fit", "exp. Data"]
- if SimOpt.routine.lower() in ("static_radpair", "teacups"):
- plot(FitOpt.window, Exp.B_z, data, FitOpt.window.canvas_1, labels=label)
- else:
- plot(
- FitOpt.window,
- Exp.time_axis,
- data,
- FitOpt.window.canvas_1,
- labels=label,
- )
-
- plot(
- FitOpt.window,
- current_Sys.distribution[0],
- current_Sys.distribution[1],
- FitOpt.window.canvas_distribution,
- labels=["current Distribution"],
- )
-
-
-
-
-[docs]
-def spinanalysis2scipy(x: NDArray[np.float64], *objects: Any) -> float:
- """
- Objective function for the scipy.optimize interface.
-
- Used by scipy.optimize routines.
-
- Parameters
- ----------
- x : np.array
- Current parameter vector of the optimizer.
- *objects : object
- Additional objects required for the simulation and fitting interface.
-
- Raises
- ------
- ValueError
- Raised if the selected simulation routine is unknown.
-
- Returns
- -------
- error : float
- Sum of squared residuals between experimental and simulated data.
-
- """
- Sys_def, Exp, SimOpt, Var = objects
-
- Sys = guess2Sys(x, Sys_def, Var, SimOpt)
-
- SimOpt.mode = "simulation"
-
- if SimOpt.routine.lower() == "static_radpair":
- simulated_spectra = do_simulation_multicore(Sys, Exp, SimOpt)
- elif SimOpt.routine.lower() == "teacups":
- simulated_spectra = teacups_multicore(Sys, Exp, SimOpt)
- elif SimOpt.routine.lower() == "opossum":
- simulated_spectra = oop_eseem(Sys, Exp, SimOpt)
- elif SimOpt.routine.lower() == "didelphis":
- simulated_spectra = oop_eseem_distribution(Sys, Exp, SimOpt)
- else:
- raise ValueError("Can't find a routine named '{0}'!".format(SimOpt.routine))
-
- simulated_spectra /= (abs(simulated_spectra)).max()
- Exp.spec_sim = simulated_spectra
-
- error = ((Exp.int.real - Exp.spec_sim.real) ** 2).sum()
-
- return error
-
-
-
-
-[docs]
-def spinanalysis2scipy_singlecore(x: NDArray[np.float64], *objects: Any) -> float:
- """
- Objective function for the scipy.optimize interface.
-
- Used by scipy.optimize.differential_evolution.
-
- Parameters
- ----------
- x : np.array
- Current parameter vector of the optimizer.
- *objects : object
- Additional objects required for the simulation and fitting interface.
-
- Raises
- ------
- ValueError
- Raised if the selected simulation routine is unknown.
-
- Returns
- -------
- error : float
- Sum of squared residuals between experimental and simulated data.
-
- """
- Sys_def, Exp, SimOpt, Var = objects
-
- Sys = guess2Sys(x, Sys_def, Var, SimOpt)
-
- SimOpt.mode = "fitting"
-
- if SimOpt.routine.lower() == "static_radpair":
- simulated_spectra = do_simulation(Sys, Exp, SimOpt)
- elif SimOpt.routine.lower() == "teacups":
- simulated_spectra = teacups(Sys, Exp, SimOpt)
- elif SimOpt.routine.lower() == "opossum":
- simulated_spectra = oop_eseem(Sys, Exp, SimOpt)
- elif SimOpt.routine.lower() == "didelphis":
- simulated_spectra = oop_eseem_distribution(Sys, Exp, SimOpt)
- else:
- raise ValueError("Can't find a routine named '{0}'!".format(SimOpt.routine))
-
- simulated_spectra /= (abs(simulated_spectra)).max()
- Exp.spec_sim = simulated_spectra
-
- error = ((Exp.int.real - Exp.spec_sim.real) ** 2).sum()
-
- return error
-
-
-
-
-[docs]
-def spinanalysis2scipy_res(
- x: NDArray[np.float64], *objects: Any
-) -> NDArray[np.float64]:
- """
- Residual function for scipy.optimize least-squares algorithms.
-
- Returns the absolute residuals between simulation and experiment.
-
- Used by scipy.optimize routines.
-
- Parameters
- ----------
- x : np.array
- Current parameter vector of the optimizer.
- *objects : object
- Additional objects required for the simulation and fitting interface.
-
- Raises
- ------
- ValueError
- Raised if the selected simulation routine is unknown.
-
- Returns
- -------
- error : np.array
- One-dimensional array containing the absolute residuals.
-
- """
- Sys_def, Exp, SimOpt, Var = objects
-
- Sys = guess2Sys(x, Sys_def, Var, SimOpt)
-
- SimOpt.mode = "simulation"
-
- if SimOpt.routine.lower() == "static_radpair":
- simulated_spectra = do_simulation_multicore(Sys, Exp, SimOpt)
- elif SimOpt.routine.lower() == "teacups":
- simulated_spectra = teacups_multicore(Sys, Exp, SimOpt)
- elif SimOpt.routine.lower() == "opossum":
- simulated_spectra = oop_eseem(Sys, Exp, SimOpt)
- elif SimOpt.routine.lower() == "didelphis":
- simulated_spectra = oop_eseem_distribution(Sys, Exp, SimOpt)
- else:
- raise ValueError("Can't find a routine named '{0}'!".format(SimOpt.routine))
-
- simulated_spectra /= (abs(simulated_spectra)).max()
- Exp.spec_sim = simulated_spectra
-
- error = abs(Exp.int.real - Exp.spec_sim.real)
-
- return error
-
-
-
-
-[docs]
-def guess2Sys(x: NDArray[np.float64], Sys: Any, Var: Any, SimOpt: Any) -> Any:
- """
- Create a spin-system object from the current optimizer vector.
-
- Parameters
- ----------
- x : np.array
- Current parameter vector of the optimizer.
- Sys : object
- Reference spin-system object.
- Var : object
- Variation object.
- SimOpt : object
- Simulation options object.
-
- Returns
- -------
- Sys_mod : object
- Spin-system object corresponding to the current optimizer vector.
-
- """
- Sys_mod = deepcopy(Sys)
-
- n = 0
- for key in vars(Var):
- if key in Var.non_vars:
- pass
- elif key in Var.single_vars:
- if vars(Var)[key] > 0:
- vars(Sys_mod)[key] = x[n]
- n += 1
- else:
- for i, parameter in enumerate(vars(Var)[key]):
- if parameter > 0:
- vars(Sys_mod)[key][i] = x[n]
- n += 1
-
- if hasattr(Var, "isotropic"):
- for par in Var.isotropic:
- vars(Sys_mod)[par][1] = vars(Sys_mod)[par][0]
- vars(Sys_mod)[par][2] = vars(Sys_mod)[par][0]
-
- for key in vars(Sys):
- if key.startswith("frame_group"):
- for i, frame in enumerate(vars(Sys)[key]):
- if not frame.endswith("_frame"):
- frame = frame + "_frame"
- if i == 0:
- reference_frame = vars(Sys_mod)[frame]
- else:
- vars(Sys_mod)[frame] = reference_frame
-
- if Var.fit_distribution:
- pos = np.zeros(Sys.distribution_order)
- sigma = np.zeros(Sys.distribution_order)
- intens = np.zeros(Sys.distribution_order)
- for i in range(Sys.distribution_order):
- intens[i] = x[n + i * 3]
- sigma[i] = x[n + i * 3 + 1]
- pos[i] = x[n + i * 3 + 2]
-
- intens /= intens.sum()
- r_axis = np.linspace(SimOpt.min_r, SimOpt.max_r, SimOpt.r_points)
- mg_distribution = _get_multi_gauss_distribution(intens, sigma, pos, r_axis)
- Sys_mod.distribution = np.array([r_axis, mg_distribution])
- Sys._mgf_parameters = np.array([pos, intens, sigma])
-
- return Sys_mod
-
-
-
-
-[docs]
-def get_random_x0(
- boundaries: Sequence[tuple[float, float]],
-) -> NDArray[np.float64]:
- """
- Generate a random initial guess within the variation boundaries.
-
- Parameters
- ----------
- boundaries : list
- Lower and upper bounds of the fitted parameters.
-
- Returns
- -------
- x0 : np.array
- Randomly generated initial parameter vector.
-
- """
- x0 = np.empty(len(boundaries))
-
- for i, bounds in enumerate(boundaries):
- x0[i] = np.random.uniform(bounds[0], bounds[1])
-
- return x0
-
-
-
-
-[docs]
-def dualannealing(Sys: Any, Exp: Any, SimOpt: Any, FitOpt: Any, Var: Any) -> Any:
- """
- Run scipy.optimize.dual_annealing for global optimization.
-
- Parameters
- ----------
- Sys : object
- Reference spin-system object.
- Exp : object
- Experimental data object.
- SimOpt : object
- Simulation options object.
- Var : object
- Variation object describing the fitted parameters.
- FitOpt : object
- Fitting options object.
-
- Returns
- -------
- best_Sys : object
- Best spin-system object found by the optimizer.
-
- """
- Var.get_boundaries(Sys)
-
- settings = dict(args=(Sys, Exp, SimOpt, Var), bounds=Var.boundaries)
-
- if FitOpt.gui:
- settings["callback"] = partial(
- plot_callback,
- Sys=Sys,
- Exp=Exp,
- SimOpt=SimOpt,
- FitOpt=FitOpt,
- Var=Var,
- )
-
- if FitOpt.x0 is not None:
- settings["x0"] = FitOpt.x0
-
- if FitOpt.method is not None:
- settings["minimizer_kwargs"] = dict()
- settings["minimizer_kwargs"]["method"] = FitOpt.method
-
- if FitOpt.maxiter_minimizer is not None:
- settings["minimizer_kwargs"]["maxiter"] = FitOpt.maxiter_minimizer
-
- if FitOpt.maxiter is not None:
- settings["maxiter"] = FitOpt.maxiter
-
- if FitOpt.initial_temp is not None:
- settings["initial_temp"] = FitOpt.initial_temp
-
- if FitOpt.restart_temp_ratio is not None:
- settings["restart_temp_ratio"] = FitOpt.restart_temp_ratio
-
- if FitOpt.visit is not None:
- settings["visit"] = FitOpt.visit
-
- if FitOpt.accept is not None:
- settings["accept"] = FitOpt.accept
-
- if FitOpt.maxfun is not None:
- settings["maxfun"] = FitOpt.maxfun
-
- if FitOpt.no_local_search is not None:
- settings["no_local_search"] = FitOpt.no_local_search
-
- results = optimize.dual_annealing(spinanalysis2scipy, **settings)
-
- best_Sys = guess2Sys(results["x"], Sys, Var, SimOpt)
-
- if FitOpt.gui:
- return best_Sys, results
- print(results)
-
- return best_Sys
-
-
-
-
-[docs]
-def shgo(Sys: Any, Exp: Any, SimOpt: Any, FitOpt: Any, Var: Any) -> Any:
- """
- Run scipy.optimize.shgo for global optimization.
-
- Parameters
- ----------
- Sys : object
- Reference spin-system object.
- Exp : object
- Experimental data object.
- SimOpt : object
- Simulation options object.
- Var : object
- Variation object describing the fitted parameters.
- FitOpt : object
- Fitting options object.
-
- Returns
- -------
- best_Sys : object
- Best spin-system object found by the optimizer.
-
- """
- Var.get_boundaries(Sys)
-
- settings = dict(args=(Sys, Exp, SimOpt, Var), bounds=Var.boundaries)
-
- if FitOpt.gui:
- settings["callback"] = partial(
- plot_callback,
- Sys=Sys,
- Exp=Exp,
- SimOpt=SimOpt,
- FitOpt=FitOpt,
- Var=Var,
- )
-
- if FitOpt.method is not None:
- settings["minimizer_kwargs"] = dict()
- settings["minimizer_kwargs"]["method"] = FitOpt.method
-
- if FitOpt.maxiter_minimizer is not None:
- options = dict(maxiter=FitOpt.maxiter_minimizer)
- settings["minimizer_kwargs"]["options"] = options
-
- settings["options"] = dict()
-
- if FitOpt.maxiter is not None:
- settings["options"]["maxiter"] = FitOpt.maxiter
-
- if FitOpt.n is not None:
- settings["n"] = FitOpt.n
-
- if FitOpt.iters is not None:
- settings["iters"] = FitOpt.iters
-
- if FitOpt.maxfev is not None:
- settings["maxfev"] = FitOpt.maxfev
-
- if FitOpt.f_tol is not None:
- settings["options"]["f_tol"] = FitOpt.f_tol
-
- if FitOpt.maxev is not None:
- settings["options"]["maxev"] = FitOpt.maxev
-
- if FitOpt.maxtime is not None:
- settings["options"]["maxtime"] = FitOpt.maxtime
-
- if FitOpt.minimize_every_iter is not None:
- settings["options"]["minimize_every_iter"] = FitOpt.minimize_every_iter
-
- if FitOpt.local_iter is not None:
- settings["options"]["local_iter"] = FitOpt.local_iter
-
- if FitOpt.sampling_method is not None:
- settings["sampling_method"] = FitOpt.sampling_method
-
- results = optimize.shgo(spinanalysis2scipy, **settings)
- best_Sys = guess2Sys(results["x"], Sys, Var, SimOpt)
-
- if FitOpt.gui:
- return best_Sys, results
- print(results)
-
- return best_Sys
-
-
-
-
-[docs]
-def differential_evolution(
- Sys: Any, Exp: Any, SimOpt: Any, FitOpt: Any, Var: Any
-) -> Any:
- """
- Run scipy.optimize.differential_evolution for global optimization.
-
- Parameters
- ----------
- Sys : object
- Reference spin-system object.
- Exp : object
- Experimental data object.
- SimOpt : object
- Simulation options object.
- Var : object
- Variation object describing the fitted parameters.
- FitOpt : object
- Fitting options object.
-
- Returns
- -------
- best_Sys : object
- Best spin-system object found by the optimizer.
-
- """
- Var.get_boundaries(Sys)
-
- settings = dict(args=(Sys, Exp, SimOpt, Var), bounds=Var.boundaries)
-
- if FitOpt.gui:
- settings["callback"] = partial(
- plot_callback,
- Sys=Sys,
- Exp=Exp,
- SimOpt=SimOpt,
- FitOpt=FitOpt,
- Var=Var,
- )
-
- if FitOpt.strategy is not None:
- settings["strategy"] = FitOpt.strategy
-
- if FitOpt.maxiter is not None:
- settings["maxiter"] = FitOpt.maxiter
-
- if FitOpt.popsize is not None:
- settings["popsize"] = FitOpt.popsize
-
- if FitOpt.tol is not None:
- settings["tol"] = FitOpt.tol
-
- if FitOpt.mutation is not None:
- settings["mutation"] = FitOpt.mutation
-
- if FitOpt.recombination is not None:
- settings["recombination"] = FitOpt.recombination
-
- if FitOpt.seed is not None:
- settings["seed"] = FitOpt.seed
-
- if FitOpt.polish is not None:
- settings["polish"] = FitOpt.polish
-
- if FitOpt.init is not None:
- settings["init"] = FitOpt.init
-
- if FitOpt.atol is not None:
- settings["atol"] = FitOpt.atol
-
- if FitOpt.updating is not None:
- settings["updating"] = FitOpt.updating
-
- if FitOpt.x0 is not None:
- settings["x0"] = FitOpt.x0
-
- if FitOpt.cpu_cores == 0:
- settings["workers"] = -1
- else:
- settings["workers"] = FitOpt.cpu_cores
-
- results = optimize.differential_evolution(spinanalysis2scipy_singlecore, **settings)
- best_Sys = guess2Sys(results["x"], Sys, Var, SimOpt)
-
- if FitOpt.gui:
- return best_Sys, results
- print(results)
-
- return best_Sys
-
-
-
-
-[docs]
-def basinhopping(Sys: Any, Exp: Any, SimOpt: Any, FitOpt: Any, Var: Any) -> Any:
- """
- Run scipy.optimize.basinhopping for global optimization.
-
- Parameters
- ----------
- Sys : object
- Reference spin-system object.
- Exp : object
- Experimental data object.
- SimOpt : object
- Simulation options object.
- Var : object
- Variation object describing the fitted parameters.
- FitOpt : object
- Fitting options object.
-
- Returns
- -------
- best_Sys : object
- Best spin-system object found by the optimizer.
-
- """
- Var.get_boundaries(Sys)
-
- settings = dict()
-
- if FitOpt.gui:
- settings["callback"] = partial(
- plot_callback,
- Sys=Sys,
- Exp=Exp,
- SimOpt=SimOpt,
- FitOpt=FitOpt,
- Var=Var,
- )
-
- if FitOpt.x0 is not None:
- settings["x0"] = FitOpt.x0
- else:
- settings["x0"] = get_random_x0(Var.boundaries)
-
- basinhopping_bounds = BasinhoppingBounds(Var)
- settings["accept_test"] = basinhopping_bounds
-
- settings["minimizer_kwargs"] = dict()
- settings["minimizer_kwargs"]["args"] = (Sys, Exp, SimOpt, Var)
-
- if FitOpt.method is not None:
- settings["minimizer_kwargs"]["method"] = FitOpt.method
- settings["minimizer_kwargs"]["bounds"] = Var.boundaries
-
- if FitOpt.T is not None:
- settings["T"] = FitOpt.T
-
- if FitOpt.niter is not None:
- settings["niter"] = FitOpt.niter
-
- if FitOpt.stepsize is not None:
- basinhopping_steps = BasinhoppingStep(Var, FitOpt.stepsize)
- else:
- basinhopping_steps = BasinhoppingStep(Var)
- settings["take_step"] = basinhopping_steps
-
- if FitOpt.interval is not None:
- settings["interval"] = FitOpt.interval
-
- if FitOpt.disp is not None:
- settings["disp"] = FitOpt.disp
-
- basinhopping_status = BasinhoppingStatus(Sys, Var, FitOpt.disp)
- settings["callback"] = basinhopping_status
-
- if FitOpt.niter_success is not None:
- settings["niter_success"] = FitOpt.niter_success
-
- if FitOpt.seed is not None:
- settings["seed"] = FitOpt.seed
-
- if FitOpt.target_accept_rate is not None:
- settings["target_accept_rate"] = FitOpt.target_accept_rate
-
- if FitOpt.stepwise_factor is not None:
- settings["stepwise_factor"] = FitOpt.stepwise_factor
-
- results = optimize.basinhopping(spinanalysis2scipy, **settings)
- best_Sys = guess2Sys(results["x"], Sys, Var, SimOpt)
-
- if FitOpt.gui:
- return best_Sys, results
- print(results)
-
- return best_Sys
-
-
-
-
-[docs]
-def least_squares(Sys: Any, Exp: Any, SimOpt: Any, FitOpt: Any, Var: Any) -> Any:
- """
- Run scipy.optimize.least_squares for nonlinear optimization.
-
- Parameters
- ----------
- Sys : object
- Reference spin-system object.
- Exp : object
- Experimental data object.
- SimOpt : object
- Simulation options object.
- Var : object
- Variation object describing the fitted parameters.
- FitOpt : object
- Fitting options object.
-
- Returns
- -------
- best_Sys : object
- Best spin-system object found by the optimizer.
-
- """
- Var.get_boundaries(Sys)
-
- lb = []
- ub = []
- for bounds in Var.boundaries:
- lb.append(bounds[0])
- ub.append(bounds[1])
-
- lb = np.array(lb)
- ub = np.array(ub)
-
- settings = dict(args=(Sys, Exp, SimOpt, Var), bounds=(lb, ub))
-
- if FitOpt.x0 is not None:
- settings["x0"] = FitOpt.x0
- else:
- settings["x0"] = get_random_x0(Var.boundaries)
-
- if FitOpt.method is not None:
- settings["method"] = FitOpt.method
-
- if FitOpt.method == "lm":
- del settings["bounds"]
-
- if FitOpt.ftol is not None:
- settings["ftol"] = FitOpt.ftol
-
- if FitOpt.xtol is not None:
- settings["xtol"] = FitOpt.xtol
-
- if FitOpt.gtol is not None:
- settings["gtol"] = FitOpt.gtol
-
- if FitOpt.loss is not None:
- settings["loss"] = FitOpt.loss
-
- if FitOpt.f_scale is not None:
- settings["f_scale"] = FitOpt.f_scale
-
- if FitOpt.max_nfev is not None:
- settings["max_nfev"] = FitOpt.max_nfev
-
- if FitOpt.tr_solver is not None:
- settings["tr_solver"] = FitOpt.tr_solver
-
- if FitOpt.verbose is not None:
- settings["verbose"] = FitOpt.verbose
-
- results = optimize.least_squares(spinanalysis2scipy_res, **settings)
- best_Sys = guess2Sys(results["x"], Sys, Var, SimOpt)
-
- if FitOpt.gui:
- return best_Sys, results
- print(results)
-
- return best_Sys
-
-
-
-
-[docs]
-def minimize(Sys: Any, Exp: Any, SimOpt: Any, FitOpt: Any, Var: Any) -> Any:
- """
- Run scipy.optimize.minimize for local optimization.
-
- Minimize provides multiple local optimization routines such as Nelder-Mead,
- COBYLA, Powell, CG and so on.
-
- Parameters
- ----------
- Sys : object
- Reference spin-system object.
- Exp : object
- Experimental data object.
- SimOpt : object
- Simulation options object.
- Var : object
- Variation object describing the fitted parameters.
- FitOpt : object
- Fitting options object.
-
- Returns
- -------
- best_Sys : object
- Best spin-system object found by the optimizer.
- results : str, optional
- Results of the scipy optimization. Only for the GUI.
-
- """
- Var.get_boundaries(Sys)
-
- settings = dict(args=(Sys, Exp, SimOpt, Var), bounds=Var.boundaries)
- if FitOpt.gui:
- settings["callback"] = partial(
- plot_callback,
- Sys=Sys,
- Exp=Exp,
- SimOpt=SimOpt,
- FitOpt=FitOpt,
- Var=Var,
- )
-
- if FitOpt.x0 is not None:
- settings["x0"] = FitOpt.x0
- else:
- settings["x0"] = get_random_x0(Var.boundaries)
-
- if FitOpt.method is not None:
- settings["method"] = FitOpt.method
-
- if FitOpt.maxiter is not None:
- settings["options"] = dict()
- settings["options"]["maxiter"] = FitOpt.maxiter
-
- results = optimize.minimize(spinanalysis2scipy, **settings)
- best_Sys = guess2Sys(results["x"], Sys, Var, SimOpt)
-
- if FitOpt.gui:
- return best_Sys, results
- print(results)
-
- return best_Sys
-
-
-
-
-[docs]
-class BasinhoppingBounds:
- """
- Acceptance test for the scipy.optimize.basinhopping algorithm.
-
- Attributes
- ----------
- xmin : np.array
- Lower bounds for the varied parameters.
- xmax : np.array
- Upper bounds for the varied parameters.
- Var : object
- Object of class Variation from the epr_setup module.
-
- Methods
- -------
- __call__(**kwargs)
- Check if the current guess is within the bounds.
-
- """
-
- def __init__(self, Var: Any) -> None:
- self.xmin, self.xmax = np.array(Var.boundaries).T
-
- return None
-
-
-[docs]
- def __call__(self, **kwargs) -> bool:
- """
- Check if the current guess is within the bounds.
-
- Parameters
- ----------
- **kwargs : list
- Varied parameters.
-
- Returns
- -------
- bool
- True if the guess is within the bounds, False if not.
-
- """
- x = kwargs["x_new"]
- test_min = bool(np.all(x >= self.xmin))
- test_max = bool(np.all(x <= self.xmax))
-
- return test_max and test_min
-
-
-
-
-
-[docs]
-class BasinhoppingStep:
- """
- Step generator for the scipy.optimize.basinhopping algorithm.
-
- Attributes
- ----------
- stepsize : float, optional
- Relative size of the random step with respect to the variation range.
- rng : object
- NumPy random number generator.
- Var : object
- Object of class Variation from the epr_setup module.
- bounds : np.array
- Parameter boundaries as a two-dimensional array.
- lb : np.array
- Lower bounds.
- ub : np.array
- Upper bounds.
- var_range : np.array
- Half the difference between lower and upper bounds.
- dim_var : int
- Number of variables.
-
- Methods
- -------
- __call__(x)
- Generate the next random step.
-
- """
-
- def __init__(self, Var: Any, stepsize: float = 0.75) -> None:
- self.stepsize = stepsize
- self.rng = np.random.default_rng()
- self.bounds = np.array(Var.boundaries)
- self.lb = self.bounds[:, 0]
- self.ub = self.bounds[:, 1]
- self.var_range = 0.5 * (self.ub - self.lb)
- self.dim_var = len(self.var_range)
-
- return None
-
-
-[docs]
- def __call__(self, x: NDArray[np.float64]) -> NDArray[np.float64]:
- """
- Generate the next random step.
-
- Parameters
- ----------
- x : np.array
- Current guess.
-
- Returns
- -------
- x : np.array
- Current parameter vector after applying a random step.
-
- """
- x = self.check_guess(x)
-
- step_range = self.stepsize * self.var_range
-
- mask_lb = (x - self.lb) >= step_range
- mask_ub = (self.ub - x) >= step_range
-
- l_range = np.zeros(self.dim_var)
- u_range = np.zeros(self.dim_var)
-
- l_range[mask_lb] = step_range[mask_lb]
- l_range[~mask_lb] = (x - self.lb)[~mask_lb]
-
- u_range[mask_ub] = step_range[mask_ub]
- u_range[~mask_ub] = (self.ub - x)[~mask_ub]
-
- x += self.rng.uniform(-l_range, u_range)
-
- return x
-
-
-
-[docs]
- def check_guess(self, x: NDArray[np.float64]) -> NDArray[np.float64]:
- """
- Check whether the current parameter vector is within the bounds.
-
- Out-of-bound values are replaced by randomly generated values inside the bounds.
-
- Parameters
- ----------
- x : np.array
- Current guess.
-
- Returns
- -------
- x : np.array
- Current guess.
-
- """
- choose_outliers = ~((x >= self.lb) & (x <= self.ub))
-
- if choose_outliers.sum() > 0:
- x_new = self.rng.uniform(self.lb, self.ub)
- x[choose_outliers] = x_new[choose_outliers]
-
- return x
-
-
-
-
-
-[docs]
-class BasinhoppingStatus:
- """
- Status callback for the scipy.optimize.basinhopping algorithm.
-
- Attributes
- ----------
- best : float
- Best objective-function value found so far.
- def_Sys : object
- Reference spin-system object.
- Var : object
- Variation object containing the parameter ranges.
- verbose : bool, optional
- Controls whether status information is printed.
- xmin : np.array
- Lower bounds for the varied parameters.
- xmax : np.array
- Upper bounds for the varied parameters.
-
- Methods
- -------
- __call__(x, value, accepted)
- Print status information.
-
- """
-
- def __init__(self, Sys: Any, Var: Any, verbose: bool = False) -> None:
- self.best = np.inf
- self.def_Sys = deepcopy(Sys)
- self.Var = Var
- self.xmin, self.xmax = np.array(Var.boundaries).T
-
- if verbose is not None:
- self.verbose = verbose
- else:
- self.verbose = False
-
- return None
-
-
-[docs]
- def __call__(self, x: NDArray[np.float64], value: float, accepted: int) -> None:
- """
- Print status information about the basinhopping progress.
-
- Parameters
- ----------
- x : np.array
- Current guess.
- value : float
- Objective-function value of the current parameter vector.
- accepted : int
- Acceptance status returned by the basinhopping algorithm.
-
- Returns
- -------
- None
- No return value.
-
- """
- mes_1 = "The current minimum with an error of {:.4f} ".format(value)
- if self.check_bounds(x):
- mes_2 = "was accepted"
-
- if value < self.best:
- self.best = value
- self.save_best(x)
- mes_3 = " and is a new best minimum!"
- else:
- mes_3 = "."
- else:
- mes_2 = "was not accepted"
- mes_3 = "."
-
- if self.verbose:
- print(mes_1 + mes_2 + mes_3)
-
- return None
-
-
-
-[docs]
- def check_bounds(self, x: NDArray[np.float64]) -> bool:
- """
- Check if the current guess is within the bounds.
-
- Parameters
- ----------
- x : np.array
- Current guess.
-
- Returns
- -------
- bool
- True if the guess is within the bounds, False if not.
-
- """
- test_min = bool(np.all(x >= self.xmin))
- test_max = bool(np.all(x <= self.xmax))
-
- return test_min and test_max
-
-
-
-[docs]
- def save_best(self, x: NDArray[np.float64]) -> None:
- """
- Create a spin-system object from the best parameter vector and save it.
-
- Parameters
- ----------
- x : np.array
- Current guess.
-
- Returns
- -------
- None
- No return value.
-
- """
- Sys = guess2Sys(x, self.def_Sys, self.Var, self.SimOpt)
- Sys.save_spinsystem("basinhopping_current_best")
-
- return None
-
-
-
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-© M. Sc. Florian Quintes, 2021-2022
-
-@contact: florian.quintes@pc.uni.freiburg.de
-
-@author: Florian Quintes
-"""
-
-from time import time
-from copy import deepcopy
-from multiprocessing import cpu_count, Pool
-from itertools import repeat
-import numpy as np
-
-
-
-[docs]
-def timer(func: callable) -> callable:
- """
- Decorator function to measure time for one function call.
-
- Parameters
- ----------
- func : callable
- Function whose runtime will be measured.
-
- Returns
- -------
- res : any
- Result(s) of the function.
-
- """
-
- def time_wrap(*args, **kwargs):
- start = time()
- res = func(*args, **kwargs)
- runtime = time() - start
- print("The runtime of {} is {:.3f} s".format(func.__name__, runtime))
- return res
-
- return time_wrap
-
-
-
-
-[docs]
-def function_benchmark(func: callable, niter: int = 100) -> callable:
- """
- This decorateur will run the given function niter times and print the best,
- the worst and the average runtime.
-
- Parameters
- ----------
- func : callable
- Function which will be benchmarked.
- niter : int
- Number of function calls.
-
- Returns
- -------
- callable
- Function with automatic benchmark.
-
- """
-
- def benchmarked_function(*args, **kwargs):
- times = np.empty(niter)
- for i in range(times.shape[0]):
- start = time()
- func(*args, **kwargs)
- times[i] = time() - start
-
- print("Runned a benchmark of {}.".format(func.__name__))
-
- if times.min() > 1:
- unit = "s"
- elif times.min() > 1e-3:
- unit = "ms"
- times *= 1e3
- else:
- unit = "μs"
- times *= 1e6
-
- print("Average time: {:.3f} {}".format(times.mean(), unit))
- print("Best time: {:.3f} {}".format(times.min(), unit))
- print("Worst time: {:3f} {}".format(times.max(), unit))
-
- return benchmarked_function
-
-
-
-
-[docs]
-def multicore(simulation: callable) -> callable:
- """
- Using multiprocessing.Pool() with starmap() for parallel computing of
- various simulation routines using the easypairspin function interface
- simulation(Sys, Exp, SimOpt).
-
- Parameters
- ----------
- simulation : callable
- Simulation function which uses the easypairspin interface (Sys, Exp,
- SimOpt).
-
- Returns
- -------
- multicore_wrapper : callable
- The origin simulation callable as multicore version.
-
- """
-
- def simulation_with_queue(
- simulation: callable,
- Sys: object,
- Exp: object,
- SimOpt: object,
- queue: object,
- num: int,
- ) -> None:
- intensity = simulation(Sys, Exp, SimOpt)
- queue.put((num, intensity))
-
- return None
-
- def multicore_wrapper(Sys: object, Exp: object, SimOpt: object) -> np.array:
- if SimOpt.cpu_cores == 0:
- SimOpt.cpu_cores = cpu_count()
-
- whole_spectrum = 1 * Exp.magnetic_field
- whole_B_z = 1 * Exp.B_z
- n_field_points = whole_spectrum.shape[0]
-
- points_per_core = n_field_points // SimOpt.cpu_cores
-
- Exp_list = np.empty(SimOpt.cpu_cores, dtype=object)
- for core in range(SimOpt.cpu_cores):
- Experimental = deepcopy(Exp)
- start = core * points_per_core
- if core + 1 < SimOpt.cpu_cores:
- end = (core + 1) * points_per_core
- Experimental.magnetic_field = whole_spectrum[start:end]
- Experimental.B_z = whole_B_z[start:end]
- else:
- Experimental.magnetic_field = whole_spectrum[start:]
- Experimental.B_z = whole_B_z[start:]
- Exp_list[core] = Experimental
-
- # [Multi-Core Calculation]
- pool = Pool(processes=SimOpt.cpu_cores)
- single_intensities = pool.starmap(
- simulation, zip(repeat(Sys), Exp_list, repeat(SimOpt))
- )
- pool.close()
- pool.join()
-
- # queue = Queue()
-
- # processes = []
- # for i, Exp_i in enumerate(Exp_list):
- # processes.append(Process(target=simulation_with_queue,
- # args=(simulation, Sys, Exp_i, SimOpt,
- # queue, i)
- # )
- # )
-
- # for p in processes:
- # p.start()
-
- # for p in processes:
- # p.join()
-
- # intensities = [queue.get() for p in processes]
- # intensities.sort()
-
- # single_intensities = [intensity[1] for intensity in intensities]
-
- intens_arr_tuple = tuple(single_intensities)
- intensity = np.hstack(intens_arr_tuple)
-
- return intensity
-
- return multicore_wrapper
-
-
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-© M. Sc. Florian Quintes, 2021-2022
-
-@contact: florian.quintes@pc.uni.freiburg.de
-
-@author: Florian Quintes
-"""
-
-import mkl
-import spinanalysis._interface_handler as spo
-from genetic_radpair.genetic_classes import Genetic_Radpair
-from oop_eseem.opossum import (
- oop_eseem,
- oop_eseem_distance_distribution,
- oop_eseem_distribution,
-)
-from teacups.simulations import teacups
-from static_radical_pair.radpair import do_simulation_multicore
-import sys
-import os
-import datetime
-import logging
-import numpy as np
-
-version = "v0.1.0"
-
-
-mkl.set_num_threads(1)
-
-
-
-[docs]
-def start_log() -> None:
- # TODO
- """
- Diese Funktion soll mal Log-Dateien während der Nutzung anlegen.
- Aktuell nicht nutzbar! Reine Baustelle!
- """
- date = datetime.datetime.now()
-
- date = date.strftime("%d_%m_%Y__%H:%M:%S")
- session_name = "EasyPairSpin_Session_at_" + date + ".log"
- log_path = os.path.join(sys.prefix, "easypairspin", "logs")
-
- if not os.path.exists(log_path):
- os.makedirs(log_path)
-
- logfile_name = os.path.join(log_path, session_name)
-
- fmt = "{asctime} - [{levelname:8}] - {module} {funcName} - {message}"
- dfmt = "%d/%m/%Y %H:%M:%S"
-
- logging.basicConfig(
- filename=logfile_name,
- style="{",
- format=fmt,
- datefmt=dfmt,
- level=logging.DEBUG,
- )
- logging.info("Log startet")
- return None
-
-
-
-
-[docs]
-def simulate(Sys: object, Exp: object, SimOpt: object) -> np.ndarray:
- # TODO Hinschreiben, welche Simulationen möglich sind + Paper.
- """
- Do various simulations with (spinpolarized) radical pairs.
-
- Parameters
- ----------
- Sys : object
- Spinsystem object of module 'epr_setup'.
- Exp : object
- Experimental object of module 'epr_setup'. simulated_spectra will be
- saved in Exp.spec_sim.
- SimOpt : object
- SimulationOptions object of module 'epr_setup'.
-
- Raises
- ------
- ValueError
- Will be raised, if the given simulation routine in SimOpt.routine is an
- invalid string.
-
- Returns
- -------
- simulated_spectra : np.ndarray
- Simulated spectra as a numpy.ndarray.
-
- Examples
- --------
-
- Basic simulation using static_radical_pair:
-
- >>> from epr_setup import Spinsystem, Experimental, SimulationOptions
- >>> Sys = Spinsystem()
- >>> Exp = Experimental()
- >>> SimOpt = SimulationOptions()
- >>> SimOpt.routine = 'static_radpair'
- >>> simulate(Sys, Exp, SimOpt)
-
- Plot your result:
-
- >>> from plotting import plot_2D
- >>> plot_2D(Exp.B_z , Exp.spec_sim)
-
- """
- SimOpt.mode = "simulation"
-
- # TODO an match case anpassen PYTHON 3.10
- if SimOpt.routine.lower() == "static_radpair":
- simulated_spectra = do_simulation_multicore(Sys, Exp, SimOpt)
- elif SimOpt.routine.lower() == "teacups":
- if SimOpt.eigval_mode is True:
- teacups(Sys, Exp, SimOpt)
- simulated_spectra = np.ones((Exp.t_points, len(Exp.B_z)))
- else:
- SimOpt.mode = "fitting"
- simulated_spectra = teacups(Sys, Exp, SimOpt)
- elif SimOpt.routine.lower() == "opossum":
- simulated_spectra = oop_eseem(Sys, Exp, SimOpt)
- elif SimOpt.routine.lower() == "didelphis":
- simulated_spectra = oop_eseem_distribution(Sys, Exp, SimOpt)
- elif SimOpt.routine.lower() == "didelphis_tikhonov":
- simulated_spectra = oop_eseem_distance_distribution(Sys, Exp, SimOpt)
- else:
- raise ValueError("Can't find a routine named '{0}'!".format(SimOpt.routine))
-
- simulated_spectra /= (abs(simulated_spectra)).max()
- Exp.spec_sim = simulated_spectra
-
- return simulated_spectra
-
-
-
-
-[docs]
-def optimize(
- Sys: object,
- Exp: object,
- SimOpt: object,
- FitOpt: object,
- Var: object,
-) -> object:
- # Hinschreiben welche Optimierungen möglich sind + Paper.
- """
- Do various optimizations with all simulations available in
- 'easypairspin()'.
-
- Parameters
- ----------
- Sys : object
- Spinsystem object of module 'epr_setup'.
- Exp : object
- Experimental object of module 'epr_setup'.
- SimOpt : object
- SimulationOptions object of module 'epr_setup'.
- FitOpt : object
- FittingOptions object of module 'epr_setup'.
- Var : object
- Variation object of module 'epr_setup'.
-
- Raises
- ------
- ValueError
- Will be raised, if the given simulation routine in FitOpt.routine is an
- invalid string.
-
- Returns
- -------
- best_Spinsystem: object
- The best Spinsystem found during optimization. Object is of class
- Spinsystem of module 'epr_setup'.
-
- Examples
- --------
-
- Basic optimization using genetic_radpair and static_radical_pair:
-
- >>> from epr_setup import Spinsystem, Experimental, SimulationOptions,
- ... FittingOptions, Variation
- >>> Sys = Spinsystem()
- >>> Exp = Experimental()
- >>> SimOpt = SimulationOptions()
- >>> SimOpt.routine = 'static_radpair'
- >>> FitOpt = FittingOptions()
- >>> Var = Variation()
- >>> Var.g1 = np.array([0.001, 0.003, 0.002])
- >>> best_Sys = optimize(Sys, Exp, SimOpt, FitOpt, Var)
-
- Plot your result:
-
- >>> from plotting import plot_2D
- >>> simulate(best_Sys, Exp, SimOpt)
- >>> plot_2D(Exp.B_z , Exp.spec_sim)
-
-
- """
- # TODO an match case anpassen PYTHON 3.10
-
- SimOpt.mode = "fitting"
-
- if FitOpt.routine.lower() == "genetic":
- Gen_Rad = Genetic_Radpair(Sys, Exp, SimOpt, FitOpt, Var)
- best_spinsystem = Gen_Rad.best_spinsystem
- elif FitOpt.routine.lower() == "dual_annealing":
- best_spinsystem = spo.dualannealing(Sys, Exp, SimOpt, FitOpt, Var)
- elif FitOpt.routine.lower() == "shgo":
- best_spinsystem = spo.shgo(Sys, Exp, SimOpt, FitOpt, Var)
- elif FitOpt.routine.lower() == "differential_evolution":
- best_spinsystem = spo.differential_evolution(Sys, Exp, SimOpt, FitOpt, Var)
- elif FitOpt.routine.lower() == "basinhopping":
- best_spinsystem = spo.basinhopping(Sys, Exp, SimOpt, FitOpt, Var)
- elif FitOpt.routine.lower() == "least_squares":
- best_spinsystem = spo.least_squares(Sys, Exp, SimOpt, FitOpt, Var)
- elif FitOpt.routine.lower() == "minimize":
- best_spinsystem = spo.minimize(Sys, Exp, SimOpt, FitOpt, Var)
- else:
- raise ValueError(
- "Can't find an optimization routine named '{0}'".format(FitOpt.routine)
- )
- return best_spinsystem
-
-
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-© M. Sc. Florian Quintes, 2021-2022
-
-@contact: florian.quintes@pc.uni.freiburg.de
-
-@author: Florian Quintes
-"""
-
-import numpy as np
-import scipy.constants as constant
-from spinanalysis import profiles
-
-
-
-[docs]
-class EPR_Parameters:
- """
- A class containing all parameters for various radical pair simulations.
-
- Attributes
- ----------
- g1 : 1d-Array, np.float64
- g-Tensor of electron 1.
- g2 : 1d-Array, np.float64
- g-Tensor of electron 2.
- g_tri : 1d-Array, np.float64
- g-Tensor of a triplet radical.
- g : 1d-Array, np.float64
- g-Tensor of a radical.
- A1 : 1d-Array, np.float64
- A-Tensor of nuclei 1 in Megahertz.
- A2 : 1d-Array, np.float64
- A-Tensor of nuclei 2 in Megahertz.
- A3 : 1d-Array, np.float64
- A-Tensor of nuclei 3 in Megahertz.
- A4 : 1d-Array, np.float64
- A-Tensor of nuclei 4 in Megahertz.
- A5 : 1d-Array, np.float64
- A-Tensor of nuclei 5 in Megahertz.
- A_eseem : float64
- Hyperfine coupling for the nuclei in OOP-ESEEM in Megahertz.
- omega_I : float64
- Nuclei frequency in OOP-ESEEM in Megahertz.
- D : float64
- Zero field splitting parameter D in Megahertz.
- D_tri : float64
- Zero field splitting parameter D of a triplet in Megahertz.
- E : float64
- Zero field splitting parameter E in Megahertz.
- E_tri : float64
- Zero field splitting parameter E of a triplet in Megahertz.
- beta : float64
- Decay rate of the exchange coupling in Å^-1.
- J_0 : float64
- Zero distance exchange coupling constant in Megahertz.
- J_ex : float64
- Exchange coupling in Megahertz.
- g1_frame : 1d-Array, np.float64
- Orientation of electron spin 1 in radian.
- g2_frame : 1d-Array, np.float64
- Orientation of electron spin 2 in radian.
- g_tri_frame : 1d-Array, np.float64
- Orientation of g_tri in radian.
- g_frame : 1d-Array, np.float64
- Orientation of g in radian.
- A1_frame : 1d-Array, np.float64
- Orientation of nuclei spin 1 in radian.
- A2_frame : 1d-Array, np.float64
- Orientation of nuclei spin 2 in radian.
- A3_frame : 1d-Array, np.float64
- Orientation of nuclei spin 3 in radian.
- A4_frame : 1d-Array, np.float64
- Orientation of nuclei spin 4 in radian.
- A5_frame : 1d-Array, np.float64
- Orientation of nuclei spin 5 in radian.
- D_frame : 1d-Array, np.float64
- Orientation of dipol coupling in radian.
- D_tri_frame : 1d-Array, np.float64
- Orientation of triplet dipol coupling in radian.
- n1 : int
- Number of chemically equivalent atoms.
- I1 : float64
- Corresponding nuclear spin.
- n2 : int
- Number of chemically equivalent atoms.
- I2 : float64
- Corresponding nuclear spin.
- n3 : int
- Number of chemically equivalent atoms.
- I3 : float64
- Corresponding nuclear spin.
- n4 : int
- Number of chemically equivalent atoms.
- I4 : float64
- Corresponding nuclear spin.
- n5 : int
- Number of chemically equivalent atoms.
- I5 : float64
- Corresponding nuclear spin.
- width_gauss : float64
- Gaussian linewidth in mT.
- T_relax_1 : float64
- Longitudinal relaxation time in s.
- T_relax_2 : float64
- Transversal relaxation time in s.
- decay : float64
- Exponential decay time for hilbert space simulations in s.
- T_pm : float64
- Phase memory time (OOP ESEEM).
- population : 1d-Array, np.float64
- Populations of the initial density matrix of a triplet precursor in
- zero field.
- amplitude : float64
- Amplitude of the spectra for OOP-ESEEM.
-
- """
-
- def __init__(self) -> None:
- """
- Initialize object of class 'Spinsystem' for radical pair simulation.
-
- Returns
- -------
- None.
-
- """
- # [SPINSYSTEM]
- self.g1 = np.array([2.002, 2.002, 2.002])
- self.g2 = np.array([2.004, 2.004, 2.004])
- self.g_tri = np.array([2.002, 2.002, 2.002])
- self.g = np.array([2.002, 2.002, 2.002])
-
- self.A1 = np.zeros(3) # MHz
- self.A2 = np.zeros(3) # MHz
- self.A3 = np.zeros(3) # MHz
- self.A4 = np.zeros(3) # MHz
- self.A5 = np.zeros(3) # MHz
- self.A_eseem = 0.0 # MHz
- self.omega_I = 0.0 # MHz
- self.D = 1.0 # MHz
- self.D_tri = 0.0 # MHz
- self.E = 0 # MHz
- self.E_tri = 0 # MHz
- self.J_ex = 0 # MHz
- self.J_0 = 0 # MHz
- self.beta = 1.4 # Å^-1.
-
- # [ORIENTATIONS]
- self.g1_frame = np.zeros(3) # Euler angle / rad
- self.g2_frame = np.zeros(3)
- self.g_tri_frame = np.zeros(3)
- self.g_frame = np.zeros(3)
- self.A1_frame = np.zeros(3)
- self.A2_frame = np.zeros(3)
- self.A3_frame = np.zeros(3)
- self.A4_frame = np.zeros(3)
- self.A5_frame = np.zeros(3)
- self.D_frame = np.zeros(3)
- self.D_tri_frame = np.zeros(3)
-
- # [LINEWIDTHS]
- self.width_gauss = 0.5 # Gaussian linewidth / mT
-
- # [RELAXATION TIMES]
- self.T_relax_1 = 0.0 # longitudinal
- self.T_relax_2 = 0.0 # transversal
- self.T_pm = 0.0 # phase memory time
- self.decay = 0.0
-
- # [DENSITY MATRIX]
- self.population = np.array([1.0, 0.0, 0.0])
-
- # [OTHER]
- self.amplitude = 0.0
-
-
-
-
-[docs]
-class Spinsystem(EPR_Parameters):
- """
- A class containing all parameters for various radical pair simulations.
-
- Attributes
- ----------
- g1_iso : float64
- Isotropic g value of electron 1.
- g2_iso : float64
- Isotropic g value of electron 2.
- n1 : int
- Number of chemically equivalent atoms.
- I1 : float64
- Corresponding nuclear spin.
- n2 : int
- Number of chemically equivalent atoms.
- I2 : float64
- Corresponding nuclear spin.
- n3 : int
- Number of chemically equivalent atoms.
- I3 : float64
- Corresponding nuclear spin.
- n4 : int
- Number of chemically equivalent atoms.
- I4 : float64
- Corresponding nuclear spin.
- n5 : int
- Number of chemically equivalent atoms.
- I5 : float64
- Corresponding nuclear spin.
- donor_list : np.array
- Defines which atom groups are donor groups.
- acceptor_list : np.array
- Defines which atom groups are acceptor groups.
- frame_group_i : list
- Define a frame_group which will be used in optimization mode. Each
- frame group contains the names of the angle lists which always will
- have same values during optimization. i is a variable and can be
- whatever you want. You can define as many frame groups as you want. An
- example frame group would be: frame_group_1 = ['A1', 'A2', 'D']. This
- list means, that A2_frame and D_frame will always have the same values
- as A1_frame, no matter which values were given to them.
- spin_system : str
- Define the spin system by one out of: "rp" (radical pair), "doub"
- (doublet), "trip" (triplet), "tdp" (triplet-doublet pair).
- precursor : str
- State of the precursor. One out of: "zf", "eigen", "singlet",
- "triplet-zf", "triplet-eigen", "coupled", "basis".
- dynamics : np.array
- Matrix with rate constants of relaxation process in 1/s. For further
- information see the documentation.
- distribution_order : int
- Number of Gaussians used for Multi-Gauss-Fitting.
- distribution : np.array
- Distance distribution of the radical pair.
-
- Methods
- -------
- load(profile_name: str)
- Load Spinsystem values from a config file (profile_name.ini).
-
- Recommended
- save(profile_name: str)
- Save the current spinsystem values as a config file (profile_name.ini).
-
- Recommended
- _get_g_iso()
- Determine both g_iso values. Needed in simulation.
-
- Examples
- --------
- Initialize a new object of class <Spinsystem>:
-
- >>> Sys = Spinsystem()
- >>> Sys.g1
- np.array([2.002, 2.002, 2.002])
- >>> Sys.g1_iso
- 2.002
-
- Change values:
-
- >>> Sys.g1 = np.array([2.0024, 2.00381, 2.0027])
- >>> Sys.get_g_iso()
- >>> Sys.g1
- np.array([2.0024 , 2.00381, 2.0027 ])
- >>> Sys.g1_iso
- 2.00297
-
- Create a new spinsystem profile from an empty template and load it:
-
- >>> Sys_profile = profiles.new_spinsystem_profile()
- >>> Sys_profile['g_1'] = [2.0034, 2.00156, 2.00228] # use list not array!
- >>> profiles.add_profile(Sys.profile, 'spinsystem', 'Sys_prof_1')
- >>> Sys_2 = Spinsystem()
- >>> Sys_2.load_profile('Sys_prof_1')
- >>> Sys_2.g1
- np.array([2.0034 , 2.00156, 2.00228])
-
- You can also save your current spinsystem as a new profile:
-
- >>> Sys_3 = Spinsystem()
- >>> Sys_3.g1 = np.array([1, 2, 3]) # either array or list
- >>> Sys_3.save('Sys_prof_2')
- >>> Sys_4 = Spinsystem()
- >>> Sys_4.load('Sys_prof_2')
- >>> Sys_4.g1
- np.array([1., 2., 3.])
-
- """
-
- def __init__(self) -> None:
- """
- Initialize object of class 'Spinsystem' for radical pair simulation.
-
- Returns
- -------
- None.
-
- """
- super().__init__()
-
- # [SPIN SYSTEM]
- self._get_g_iso()
- self.spin_system = "rp"
- self.precursor = "singlet"
-
- # [NUCLEI]
- self.acceptor_list = np.array([1, 2, 3])
- self.donor_list = np.array([4, 5])
-
- self.n1 = 0 # Number of chemically equivalent atoms
- self.I1 = 0 # corresponding nuclear spin
- self.n2 = 0
- self.I2 = 0
- self.n3 = 0
- self.I3 = 0
- self.n4 = 0
- self.I4 = 0
- self.n5 = 0
- self.I5 = 0
-
- # [DYNAMICS]
- self.dynamics = None
-
- # [DISTRIBUTIONS]
- self.distribution_order = 3
- self.distribution = None
-
-
-[docs]
- def _get_g_iso(self) -> None:
- """Get g1_iso and g2_iso."""
- self.g1_iso = self.g1.sum() / 3
- self.g2_iso = self.g2.sum() / 3
-
-
-
-[docs]
- def load(self, profile_name: str, degree: bool = False) -> None:
- """
- Load a spinsystem from a profile.
-
- Load the settings from [sys.prefix]/easypairspin/profiles/spinsystem/
- [profile_name].ini into the Spinsystem object. Overwrites previous
- settings.
-
- Parameters
- ----------
- profile_name : str
- Name of the Spinsystem profile which will be loaded.
- degree : bool, optional
- If True, the angle values in the profile are given in degree not
- radian. Thus, they will be converted to radian. If False, the
- angles are given in radian and will therefore not be converted. The
- default is 'False'.
-
- Returns
- -------
- None.
-
- """
- spinsystem_profile = profiles.load_profile(profile_name, "spinsystem")
-
- for key in vars(self):
- if key not in ("g1_iso", "g2_iso"):
- try:
- vars(self)[key] = spinsystem_profile["main"][key]
- except KeyError:
- pass
-
- try:
- if len(vars(self)[key]) == 3:
- vars(self)[key] = np.array(vars(self)[key])
- except TypeError:
- pass
-
- if key in ("acceptor_list", "donor_list"):
- vars(self)[key] = np.array(vars(self)[key])
-
- if key == "distribution":
- if vars(self)[key] in ("None", None):
- vars(self)[key] = None
- else:
- vars(self)[key] = np.array(vars(self)[key])
- size = vars(self)[key].size
- vars(self)[key] = vars(self)[key].reshape((2, size // 2))
-
- if degree:
- if key.endswith("_frame"):
- vars(self)[key] *= np.pi / 180
-
- self._get_g_iso()
-
-
-
-[docs]
- def save(self, profile_name: str = "") -> None:
- """
- Save the spinsystem as a profile.
-
- Save the spinsystem object as a spinsystem profile using
- profile_management.add_profile(). Load the spinsystem using
- Spinsystem.load_profile(<profile_name>).
-
- Parameters
- ----------
- profile_name : str, optional
- Name of the profile. If no profile name is given, a default one
- will be generated by get_profile_name(). The default is ''.
-
- Returns
- -------
- None
- Nothing will be returned.
-
- """
- spinsys_profile = profiles.new_spinsystem_profile()
-
- for key in spinsys_profile["main"]:
- if isinstance(vars(self)[key], np.ndarray):
- spinsys_profile["main"][key] = list(vars(self)[key].flatten())
- else:
- spinsys_profile["main"][key] = vars(self)[key]
-
- profiles.add_profile(spinsys_profile, "spinsystem", pname=profile_name)
-
-
-
-
-
-[docs]
-class Experimental:
- """
- A class containing all experimental parameters and data.
-
- Attributes
- ----------
- B_z : 1d-Array, np.float64
- External magnetic field points in mT used for simulation. Conversions
- allowed.
- freq_mw : float64
- Frequency of induced microwave radiation in Gigahertz.
- magnetic_field : 1d-Array, np.float64
- Same as B_z, but will never be changed.
- int : np.array, np.complex128
- Real and imaginary part of the measured intensities. 1d or 2d.
- time_axis : 1d-Array, optional
- Contains all experimental time points.
- spec_sim : 1d-Array, np.float64
- Calculated spectrum. At initialisation empty.
-
- Methods
- -------
- get_linear_time_axis()
- Get a linear time axis using the given boundaries from self.t_scale
- with self.t_points points.
-
- """
-
- def __init__(
- self,
- magnetic_field: np.array = None,
- real_int: np.array = None,
- imag_int: np.array = None,
- cmplx_int: np.array = None,
- time_axis: np.array = None,
- rescale: bool = True,
- ):
- """
- Initialize object of class 'Experimental' for radical pair simulation.
-
- Parameters
- ----------
- magnetic_field : 1d-Array, np.float64, optional
- Contains all experimental external magnetic field points.
- real_int : np.array, np.float64, optional
- Real part of the measured intensities. 1d or 2d.
- imag_int : np.array, np.float64, optional
- Imaginary part of the measured intensities. 1d or 2d.
- cmplx_int : np.array, np.complex128, optional
- Real and imaginary part of the measured intensities. 1d or 2d. If
- this parameter is given, real_int and imag_int will be ignored.
- time_axis : 1d-Array, optional
- Contains all experimental time points in s.
- rescale : bool, optional
- If True, the experimental intensities will be scaled to a maximum
- of 1. Default is 'True'.
-
- Returns
- -------
- None.
-
- """
- self.magnetic_field = magnetic_field
- if self.magnetic_field is None:
- self.B_z = np.linspace(240, 260, 100)
- else:
- self.B_z = 1 * magnetic_field # external magnetic field / mT
- self.freq_mw = 9.7e9 # Microwave radiation / Hz
- self.B_mw = 1e-3
-
- self.t_scale = [0, 2e-6]
- self.t_points = 2
- self.time_axis = time_axis
- if time_axis is not None:
- self.t_scale[0] = time_axis.min()
- self.t_scale[1] = time_axis.max()
- self.t_points = time_axis.shape[0]
-
- self.int = None
- if cmplx_int is not None:
- self.int = cmplx_int
- else:
- if real_int is not None:
- self.int = np.zeros(real_int.shape, dtype="complex128")
- self.int.real = real_int
- if imag_int is not None:
- if self.int is not None:
- self.int.imag = imag_int
- else:
- self.int = np.zeros(imag_int.shape, dtype="complex128")
- self.int.imag = real_int
-
- if self.int is not None and rescale:
- self.int /= np.abs(self.int).max()
- self.spec_sim = np.zeros(
- self.int.shape, dtype="complex128"
- ) # simulated spectra
-
-
-[docs]
- def get_linear_time_axis(
- self, t_min: float = None, t_max: float = None, t_points: int = None
- ) -> None:
- """
- Get a linear timea axis for transient simulations.
-
- Get a linear time axis using the given boundaries from self.t_scale
- with self.t_points points.
-
- Parameters
- ----------
- t_min : float, optional
- Left boundary of the time axis. If None is given, the current value
- of self.t_scale[0] will be used. Else, the value of self.t_scale[0]
- will be replaced. The default is None.
- t_max : float, optional
- Right boundary of the time axis. If None is given, the current
- value of self.t_scale[1] will be used. Else, the value of
- self.t_scale[1] will be replaced. The default is None.
- t_points : int, optional
- Number of time points. If None is given, the current value of
- self.t_points will be used. Else, the value of self.t_points will
- be replaced. The default is None.
-
- Returns
- -------
- None
- Nothing will be returned.
-
- """
- if t_min is not None:
- self.t_scale[0] = t_min
- if t_max is not None:
- self.t_scale[1] = t_max
- if t_points is not None:
- self.t_points = t_points
-
- self.time_axis = np.linspace(self.t_scale[0], self.t_scale[1], self.t_points)
-
-
-
-
-
-[docs]
-class Variation(EPR_Parameters):
- """
- A class containing variation ranges for all possible parameters.
-
- Attributes
- ----------
- needed_digits : int
- Number of needed digits for chromosomes.
- number_of_genes : int
- Number of varied parameters. Used for fp representation.
- variation_array : 1d-Array, np.float64
- Array with all variation ranges greater 0.
- boundaries : list
- Sequence of tuples containing upper and lower bounds for all varied
- parameters. Used for scipy.optimize.
- freq_mw : float64
- Frequency of induced microwave radiation in Hertz.
- bohr_magneton : float64
- Bohr magneton in Hertz/Tesla.
-
- Methods
- -------
- load(profile_name: str)
- Load Spinsystem values from a config file (profile_name.ini).
-
- Recommended
- save(profile_name: str)
- Save the current variation values as a config file (profile_name.ini).
-
- Recommended
- get_digits_for_one_par(Par, digits_per_True, one_par=True)
- Get the number of needed digits for one varied parameter.
- get_needed_digits()
- Get the total number of needed digits in binary mode.
- get_number_of_genes()
- Get the total number of genes. Is equal to the number of varied
- parameters.
- get_variation_array()
- Create an array with all used variation ranges. Only used in floating
- point representation.
- update_digits()
- Determine number of needed digits. Just calls get_needed_digits()
- get_boundaries(Sys)
- Get a sequence of tuples containing the boundaries for the varied
- parameters.
-
- Examples
- --------
- Initialize a new object of class <Variation>:
-
- >>> Var = Variation()
- >>> Var.g1
- np.array([0., 0., 0.])
-
- Change values:
-
- >>> Var.g1 = np.array([0.003, 0.004, 0.003])
- >>> Var.g1
- np.array([0.003, 0.004, 0.003])
-
- Create a new variation profile from an empty template and load it:
-
- >>> Var_profile = profiles.new_variation_profile()
- >>> Var_profile['g_1'] = [0.001, 0.007, 0.003] # use list not array!
- >>> profiles.add_profile(Var.profile, 'variation', 'Var_prof_1')
- >>> Var_2 = Variation()
- >>> Var_2.load_profile('Var_prof_1')
- >>> Var_2.g1
- np.array([0.001, 0.007, 0.003])
-
- You can also save your current variation object as a new profile:
-
- >>> Var_3 = Variation()
- >>> Var_3.g1 = np.array([1, 2, 3]) # either array or list
- >>> Var_3.save('Var_prof_2')
- >>> Var_4 = Variation()
- >>> Var_4.load('Var_prof_2')
- >>> Var_4.g1
- np.array([1., 2., 3.])
-
- """
-
- def __init__(self):
- """
- Initialize object of class 'Variation' for radical pairs in EPR.
-
- Returns
- -------
- None.
-
- """
- super().__init__()
- for key in vars(self):
- if isinstance(vars(self)[key], float):
- vars(self)[key] = 0.0
- elif isinstance(vars(self)[key], int):
- vars(self)[key] = 0
- else:
- for i in range(len(vars(self)[key])):
- vars(self)[key][i] = 0.0
-
- self.bohr_magneton = constant.value("Bohr magneton in Hz/T")
-
- self.fit_distribution = False
-
- # [EXPERIMENTAL SETUP]
- self.freq_mw = 0.0 # Microwave radiation / Hz
-
- # [Variable Lists]
- self.non_vars = (
- "bohr_magneton",
- "needed_digits",
- "number_of_genes",
- "variation_array",
- "boundaries",
- "isotropic",
- "fit_distribution",
- "non_vars",
- "single_vars",
- )
- self.single_vars = (
- "A_eseem",
- "omega_I",
- "D",
- "D_tri",
- "E",
- "E_tri",
- "beta",
- "J_0",
- "J_ex",
- "T_relax_1",
- "T_relax_2",
- "T_pm",
- "decay",
- "freq_mw",
- "amplitude",
- "width_gauss",
- )
-
-
-[docs]
- def load(self, profile_name: str, degree: bool = False) -> None:
- """
- Laod a variation object from a profile.
-
- Load the settings from [sys.prefix]/easypairspin/profiles/variation/
- [profile_name].ini into the Variation object. Overwrites previous
- settings.
-
- Parameters
- ----------
- profile_name : str
- Name of the Variation profile which will be loaded.
- degree : bool, optional
- If True, the angle values in the profile are given in degree not
- radian. Thus, they will be converted to radian. If False, the
- angles are given in radian and will therefore not be converted. The
- default is 'False'.
-
- Returns
- -------
- None.
-
- """
- variation_profile = profiles.load_profile(profile_name, "variation")
-
- for key in vars(self):
- if key not in ("g1_iso", "g2_iso", "bohr_magneton"):
- try:
- vars(self)[key] = variation_profile["main"][key]
- except KeyError:
- pass
-
- try:
- if len(vars(self)[key]) == 3:
- vars(self)[key] = np.array(vars(self)[key])
- except TypeError:
- pass
-
- if degree:
- if key.endswith("_frame"):
- vars(self)[key] *= np.pi / 180
-
-
-
-[docs]
- def save(self, profile_name: str = "") -> None:
- """
- Save a variation object as a profile.
-
- Save the variation object as a variation profile using
- profile_management.add_profile(). Load the variation object using
- Variation.load_profile(<profile_name>).
-
- Parameters
- ----------
- profile_name : str, optional
- Name of the profile. If no profile name is given, a default one
- will be generated by get_profile_name(). The default is ''.
-
- Returns
- -------
- None
- Nothing will be returned.
-
- """
- var_profile = profiles.new_variation_profile()
-
- for key in var_profile["main"]:
- if isinstance(vars(self)[key], np.ndarray):
- var_profile["main"][key] = list(vars(self)[key])
- else:
- var_profile["main"][key] = vars(self)[key]
-
- profiles.add_profile(var_profile, "variation", pname=profile_name)
-
-
-
-[docs]
- def get_needed_digits(self) -> None:
- """
- Get sum of needed digits for all varied parameters.
-
- Returns
- -------
- None.
-
- """
- self.get_number_of_genes()
- self.needed_digits = self.number_of_genes * 12
-
- return None
-
-
-
-[docs]
- def get_number_of_genes(self) -> None:
- """Determine number of parameters which get varied."""
- self.number_of_genes = 0
- for key in vars(self):
- if key in self.non_vars:
- pass
- elif key in self.single_vars:
- if vars(self)[key] > 0.0:
- self.number_of_genes += 1
- else:
- for parameter in vars(self)[key]:
- if parameter > 0.0:
- self.number_of_genes += 1
-
- return None
-
-
-
-[docs]
- def get_variation_array(self) -> None:
- """Put all variation ranges in one 1d-Array."""
- self.get_number_of_genes()
- self.variation_array = np.zeros(self.number_of_genes)
- i = 0
- for key in vars(self):
- if key in self.non_vars:
- pass
- elif key in self.single_vars:
- if vars(self)[key] > 0:
- self.variation_array[i] = vars(self)[key]
- i += 1
- else:
- for parameter in vars(self)[key]:
- if parameter > 0:
- self.variation_array[i] = parameter
- i += 1
-
- return None
-
-
-
-[docs]
- def update_digits(self) -> None:
- """Update number of needed_digits."""
- self.get_needed_digits()
-
- return None
-
-
-
-[docs]
- def get_boundaries(self, Sys: object) -> None:
- """
- Create a sequence of pairs with all bounds for the varied parameters.
-
- Used for the scipy optimization routines.
-
- Parameters
- ----------
- Sys : object
- Spinsystem object.
-
- Returns
- -------
- None
- Nothing will be returned.
-
- """
- self.boundaries = []
-
- for key in vars(self):
- if key in self.non_vars:
- pass
- elif key in self.single_vars:
- if vars(self)[key] > 0:
- mid = vars(Sys)[key]
- var = vars(self)[key]
- lb = mid - var
- ub = mid + var
-
- if lb > ub:
- lb = mid + var
- ub = mid - var
-
- bounds = (lb, ub)
- self.boundaries.append(bounds)
- else:
- for i, parameter in enumerate(vars(self)[key]):
- if parameter > 0:
- mid = vars(Sys)[key][i]
- var = parameter
- lb = mid - var
- ub = mid + var
-
- bounds = (lb, ub)
- self.boundaries.append(bounds)
-
- if self.fit_distribution:
- for i in range(Sys.distribution_order):
- b_int = (0, 1)
- b_pos = (Sys.distribution[0].min(), Sys.distribution[0].max())
- b_sigma = (0.01, 0.7)
- self.boundaries.append(b_int)
- self.boundaries.append(b_sigma)
- self.boundaries.append(b_pos)
-
- return None
-
-
-
-
-
-[docs]
-class SimulationOptions:
- """
- A class containing all simulation options.
-
- Attributes
- ----------
- routine: str
- Name of the simulation routine which will be used by easypairspin() and
- easypairspin_optimize().
- grid_points: int
- Number of points used for spherical grid.
- space: str
- Name of the mathematical space used for some calculations.
- pop_evolution : boolean
- If set to True, the population evolution in calculated using teacups.
- eigval_mode : boolean
- If set to True, only the eigenvalues of the system are calculated using
- teacups.
- force_cpu : boolean
- If True, the simulation will be executed on the CPU, even if GPU is
- available. Default is False.
- regularization_mode : int
- Choose the regularization matrix used for the Tikhonov-Regularization.
- 0 : Unitary matrix
- 1 : First order derivative matrix
- 2 : Second order derivative matrix (default)
-
- Methods
- -------
- load(profile_name: str)
- Load SimulationOptions values from a config file (profile_name.ini).
-
- Recommended
- save(profile_name: str)
- Save the current simulation options as a config file
- (profile_name.ini).
-
- Examples
- --------
- Initialize an object of class <SimulationOptions>:
-
- >>> SimOpt = SimulationOptions()
- >>> SimOpt.grid_points
- 500
-
- Change values:
-
- >>> SimOpt.grid_points = 1000
- >>> SimOpt.grid_points
- 1000
-
- Save your current values as a new profile:
-
- >>> SimOpt.save('SimOpt_prof_1')
- >>> SimOpt_2 = SimulationOptions()
- >>> SimOpt_2.grid_points
- 500
- >>> SimOpt.load('SimOpt_prof_1')
- >>> SimOpt_2.grid_points
- 1000
-
- You can also create a simulation options profile from an empty template:
-
- >>> simopt_prof = profiles.new_simulation_profile()
- >>> simopt_prof['static_radpair']['grid_points'] = 1100
- >>> profiles.add_profile(simopt_prof, 'simulation', 'SimOpt_prof_2')
- >>> SimOpt_3 = SimulationOptions()
- >>> SimOpt_3.grid_points
- 500
- >>> SimOpt.load('SimOpt_prof_2')
- >>> SimOpt_3.grid_points
- 1100
-
-
- """
-
- def __init__(self):
- """
- Initialize object of class 'Simulation_Options' used for simulations.
-
- Returns
- -------
- None.
-
- """
- # [MAIN]
- self.routine = ""
- self.cpu_cores = 0
-
- # [STATIC_RADICAL_PAIR]
- self.grid_points = 500
- self.refinement = 1
-
- # [TEACUPS]
- self.space = "hilbert"
- self.pop_evolution = True
- self.eigval_mode = False
-
- # [OPOSSUM]
-
- # [DIDELPHIS]
- self.min_r = 10
- self.max_r = 50
- self.r_points = 401
- self.fast_mode = False
- self.GCV = False
- self.force_cpu = False
- self.regularization_mode = 2
-
- # [DIDELPHIS_MGF]
-
-
-[docs]
- def load(self, profile_name: str) -> None:
- """
- Load simulation options from a profile.
-
- Load the settings from [sys.prefix]/easypairspin/profiles/simulation/
- [profile_name].ini into the SimulationOptions object. Overwrites
- previous settings.
-
- Parameters
- ----------
- profile_name : str
- Name of the simulation profile which will be loaded.
-
- Returns
- -------
- None.
-
- """
- simulation_profile = profiles.load_profile(profile_name, "simulation")
-
- self.routine = simulation_profile["main"]["routine"]
- self.cpu_cores = simulation_profile["main"]["cpu_cores"]
- for key in simulation_profile[self.routine].keys():
- vars(self)[key] = simulation_profile[self.routine][key]
-
-
-
-[docs]
- def save(self, profile_name: str = "") -> None:
- """
- Save the simulation optionas as a profile.
-
- Save the simulation options as a simulation options profile using
- profile_management.add_profile(). Load the simulation options using
- SimulationOptions.load_profile(<profile_name>).
-
- Parameters
- ----------
- profile_name : str, optional
- Name of the profile. If no profile name is given, a default one
- will be generated by get_profile_name(). The default is ''.
-
- Returns
- -------
- None
- Nothing will be returned.
-
- """
- simopt_profile = profiles.new_simulation_profile()
-
- for section in ["main", "static_radpair", "teacups", "opossum"]:
- for key in simopt_profile[section]:
- if isinstance(vars(self)[key], np.ndarray):
- simopt_profile[section][key] = list(vars(self)[key])
- else:
- simopt_profile[section][key] = vars(self)[key]
-
- profiles.add_profile(simopt_profile, "simulation", pname=profile_name)
-
-
-
-
-
-[docs]
-class FittingOptions:
- """
- A class containing all optimization options.
-
- Attributes
- ----------
- routine : str
- Name of the optimization routine which will be used by
- easypairspin_optimize().
- method : str
- Name of the optimization method used in the scipy.optimize routines.
- x0 : numpy.array, np.float64
- Array containing the initial guess for the optimization routine for
- the parameters which will be varied.
- cpu_cores : int
- Number of cores used for the optimization.
- gui : bool
- Set to True if in GUI mode (PySpin). The default is False.
- window : object
- Plot canvas. Only needed in GUI mode.
-
- Methods
- -------
- load(profile_name: str)
- Load FittingOptions values from a config file (profile_name.ini).
-
- Recommended
- save(profile_name: str)
- Save the current fitting options as a config file (profile_name.ini).
-
- Recommended
-
- Examples
- --------
- Initialize an object of class <FittingOptions>:
-
- >>> FitOpt = FittingOptions()
- >>> FitOpt.GAVaPS
- True
-
- Change values:
-
- >>> FitOpt.GAVaPS = False
- >>> FitOpt.GAVaPS
- False
-
- Save your current values as a new profile:
-
- >>> FitOpt.save_simulationoptions('FitOpt_prof_1')
- >>> FitOpt_2 = FittingOptions()
- >>> FitOpt_2.GAVaPS
- True
- >>> FitOpt.load_profile('FitOpt_prof_1')
- >>> FitOpt_2.GAVaPS
- False
-
- You can also create a fitting options profile from an empty template:
-
- >>> fitopt_prof = profiles.new_optimization_profile()
- >>> fitopt_prof['genetic']['GAVaPS'] = False
- >>> profiles.add_profile(fitopt_prof, 'simulation', 'FitOpt_prof_2')
- >>> FitOpt_3 = FittingOptions()
- >>> FitOpt_3.GAVaPS
- True
- >>> FitOpt.load_profile('FitOpt_prof_2')
- >>> FitOpt_3.GAVaPS
- False
-
- """
-
- def __init__(self):
- """
- Initialize object of class 'FittingOptions' used for optimization.
-
- Returns
- -------
- None.
-
- """
- # [MAIN]
- self.routine = None
- self.method = None
- self.x0 = None
- self.cpu_cores = 0
- self.gui = False
- self.window = None
-
- # [GENETIC]
- self.GAVaPS = True
- self.representation = None
- self.lifetime_mode = None
- self.crossover_type = None
- self.mutation_type = None
- self.min_lifetime = None
- self.max_lifetime = None
- self.reproduction_ratio = None
- self.p_c = None
- self.p_m = None
- self.pop_size = None
- self.min_pop_size = None
- self.max_pop_size = None
- self.convergence = None
- self.peak_prominence = None
- self.error_weight = None
- self.max_generation = None
- self.show_status = None
-
- # [MINIMIZE]
- self.maxiter = None
-
- # [DUAL_ANNEALING]
- self.maxiter = None
- self.maxiter_minimizer = None
- self.initial_temp = None
- self.restart_temp_ratio = None
- self.visit = None
- self.accept = None
- self.maxfun = None
- self.no_local_search = None
-
- # [SHGO]
- self.n = None
- self.iters = None
- self.maxiter_minimizer = None
- self.maxfev = None
- self.f_tol = None
- self.maxiter = None
- self.maxev = None
- self.maxtime = None
- self.minimize_every_iter = None
- self.local_iter = None
- self.sampling_method = None
-
- # [DIFFERENTIAL_EVOLUTION]
- self.strategy = None
- self.maxiter = None
- self.popsize = None
- self.tol = None
- self.mutation = None
- self.recombination = None
- self.seed = None
- self.disp = None
- self.polish = None
- self.init = None
- self.atol = None
- self.updating = None
-
- # [BASINHOPPING]
- self.T = None
- self.niter = None
- self.stepsize = None
- self.maxiter_minimizer = None
- self.interval = None
- self.disp = None
- self.niter_success = None
- self.seed = None
- self.target_accept_rate = None
- self.stepwise_factor = None
-
- # [LEAST_SQUARES]
- self.ftol = None
- self.xtol = None
- self.gtol = None
- self.loss = None
- self.f_scale = None
- self.max_nfev = None
- self.tr_solver = None
- self.verbose = None
-
-
-[docs]
- def load(self, profile_name: str) -> None:
- """
- Load fitting options from a profile.
-
- Load the settings from [sys.prefix]/easypairspin/profiles/optimization/
- [profile_name].ini into the FittingOptions object. Overwrites previous
- settings. Only loads the section given in ['main']['routine'].
-
- Parameters
- ----------
- profile_name : str
- Name of the optimization profile which will be loaded.
-
- Returns
- -------
- None.
-
- """
- fitting_profile = profiles.load_profile(profile_name, "optimization")
-
- self.routine = fitting_profile["main"]["routine"]
- self.cpu_cores = fitting_profile["main"]["cpu_cores"]
- for key in fitting_profile[self.routine].keys():
- vars(self)[key] = fitting_profile[self.routine][key]
-
-
-
-[docs]
- def save(self, profile_name: str = "") -> None:
- """
- Save the fitting options as a profile.
-
- Save the fitting options as a fitting options profile using
- profile_management.add_profile(). Load the fitting options using
- FittingOptions.load_profile(<profile_name>).
-
- Parameters
- ----------
- profile_name : str, optional
- Name of the profile. If no profile name is given, a default one
- will be generated by get_profile_name(). The default is ''.
-
- Returns
- -------
- None
- Nothing will be returned.
-
- """
- fitopt_profile = profiles.new_optimization_profile()
-
- for section in [
- "main",
- "genetic",
- "minimize",
- "dual_annealing",
- "shgo",
- "differential_evolution",
- "basinhopping",
- "least_squares",
- ]:
- for key in fitopt_profile[section]:
- if isinstance(vars(self)[key], np.ndarray):
- fitopt_profile[section][key] = list(vars(self)[key])
- else:
- fitopt_profile[section][key] = vars(self)[key]
-
- profiles.add_profile(fitopt_profile, "optimization", pname=profile_name)
-
-
-
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-© M. Sc. Florian Quintes, 2021-2022
-
-@contact: florian.quintes@pc.uni.freiburg.de
-
-@author: Florian Quintes
-"""
-
-import os
-import glob
-from typing import Tuple
-import numpy as np
-from scipy.io import loadmat
-from spinanalysis._utils import strtobool
-
-
-
-[docs]
-def get_full_path(directory_name: str, start_directory: str = None) -> str:
- """
- Get the full path of a given directory. Search starts at home directory.
-
- Parameters
- ----------
- directory_name : str
- Name of the directory whose path is to be found.
- start_directory : str, optional
- Directory at which the search starts. If given, the perfomance
- increases very sharply. The default is None.
-
- Recommended.
-
- Returns
- -------
- full_path : str
- The full path of the directory.
-
- """
- if start_directory is None:
- start_directory = os.path.join(os.path.expanduser("~"), "**")
- else:
- start_directory = os.path.join(os.path.expanduser("~"), start_directory, "**")
-
- all_subdirectories = glob.glob(start_directory, recursive=True)
-
- full_path = None
- for path in all_subdirectories:
- if path.endswith(directory_name):
- full_path = path
-
- return full_path
-
-
-
-
-[docs]
-def get_DSC_parameters(path_to_folder: str) -> dict:
- """
- Extract all parameters from the DSC File.
-
- Parameters
- ----------
- path_to_folder : str
- Full path to the folder with .DSC and .DTA file. Files and path must
- have the same basename (BRUKER STANDARD).
-
- Returns
- -------
- DSC_parameters : dict
- Dictionary with all parameters. Key is the same as in .DSC.
-
- """
- DSC_parameters = {}
-
- basename = os.path.basename(path_to_folder) # Foldername == Filename
- DSC_file = os.path.join(path_to_folder, basename + ".DSC")
-
- with open(DSC_file, "r") as file:
- for line in file.readlines():
- if line.startswith(("*", "#", "'")):
- pass
- elif line.startswith("FTAcqModeSlct"):
- line = line.split()
- DSC_parameters[line[0]] = line[-1]
- else:
- line = line.split()
- try:
- DSC_parameters[line[0]] = convert_parameter_type(line[1])
- except IndexError:
- pass
-
- DSC_parameters["path_to_folder"] = path_to_folder
-
- return DSC_parameters
-
-
-
-
-[docs]
-def convert_parameter_type(value: str) -> Tuple[bool, int, float, str]:
- """
- Convert the type of a given string to bool, int or float if possible.
-
- Parameters
- ----------
- value : str
- Parameter string which should be converted.
-
- Returns
- -------
- value : bool or int or float or str
- Parameter as bool, int or float. If none is possible, the stripped
- string will be returned.
-
- """
- try:
- value = bool(strtobool(value))
- return value
- except ValueError:
- pass
-
- try:
- value = int(value)
- return value
- except ValueError:
- pass
-
- try:
- value = float(value)
- return value
- except ValueError:
- value = value.strip("'")
- return value
-
-
-
-
-[docs]
-def get_byte_mode(DSC_dict: dict, data_key: str = "IRFMT") -> str:
- """
- Get the used byte mode of the BRUKER BES3T-data.
-
- For more information about BES3T go to BRUKER website or easyspin @ github.
-
- Parameters
- ----------
- DSC_dict : dict
- Dictionary with all parameters. Key is the same as in .DSC.
- data_key : str, optional
- Key for the data array. Either 'IRFMT' or 'IIFMT'.
- The default is 'IRFMT'.
-
- Raises
- ------
- ValueError
- Raised if key value is not C, S, I, F or D.
-
- Returns
- -------
- byte_mode : str
- Return the used byte mode as one string for
- numpy.fromfile(dtype=byte_mode).
-
- """
- byte_mode = ""
-
- if DSC_dict["BSEQ"] == "BIG":
- byte_mode += ">"
- elif DSC_dict["BSEQ"] == "LIT":
- byte_mode += "<"
- else:
- byte_mode += ">"
-
- if DSC_dict[data_key] in ("C", "S", "I"):
- byte_mode += "i"
- if DSC_dict[data_key] == "I":
- byte_mode += "4"
- elif DSC_dict[data_key] == "S":
- byte_mode += "2"
- elif DSC_dict[data_key] in ("F", "D"):
- byte_mode += "f"
- if DSC_dict[data_key] == "D":
- byte_mode += "8"
- elif DSC_dict[data_key] == "A":
- raise ValueError("Can't read BES3T data in ASCII format!")
- else:
- raise ValueError("Unknown value for keyword {} in .DSC file!".format(data_key))
-
- return byte_mode
-
-
-
-
-[docs]
-def load_data_vector(DSC_dict: dict) -> np.array:
- """
- Load the binary intensity vector(s) from BRUKER BES3T-File.
-
- Parameters
- ----------
- DSC_dict : dict
- Dictionary with all parameters. Key is the same as in .DSC.
-
- Raises
- ------
- ValueError
- Will be raised if IKKF isn't CPLX or REAL or if the dimension isn't 1
- or 2.
- KeyError
- Raised if an axis is given but not the corresponding number of points.
-
- Returns
- -------
- cmplx_data_vector : np.array, np.complex128
- Real and imaginary part of the measured intensities. If no imaginary
- part is measured, zeroes will be inserted.
-
- """
- get_data_dimension(DSC_dict)
- byte_mode = get_byte_mode(DSC_dict)
-
- DTA_file = os.path.join(DSC_dict["path_to_folder"], DSC_dict["TITL"] + ".DTA")
-
- data_vector = np.fromfile(DTA_file, dtype=byte_mode)
-
- if DSC_dict["IKKF"] == "CPLX":
- data_vector_real = data_vector[0::2]
- data_vector_imag = data_vector[1::2]
- elif DSC_dict["IKKF"] == "REAL":
- data_vector_real = 1 * data_vector
- data_vector_imag = np.zeros(len(data_vector))
- else:
- raise ValueError("Unknown value for keyword IKKF!")
-
- if DSC_dict["dimensions"] == 2:
- if "XPTS" and "YPTS" in DSC_dict:
- data_vector_real = data_vector_real.reshape(
- (DSC_dict["YPTS"], DSC_dict["XPTS"])
- )
- data_vector_imag = data_vector_imag.reshape(
- (DSC_dict["YPTS"], DSC_dict["XPTS"])
- )
-
- elif "XPTS" and "ZPTS" in DSC_dict:
- data_vector_real = data_vector_real.reshape(
- (DSC_dict["ZPTS"], DSC_dict["XPTS"])
- )
- data_vector_imag = data_vector_imag.reshape(
- (DSC_dict["ZPTS"], DSC_dict["XPTS"])
- )
- else:
- raise KeyError("Can't find needed keys from (XPTS, YPTS, ZPTS)!")
-
- elif DSC_dict["dimensions"] == 1:
- pass
- else:
- raise ValueError("Can't handle dimensions {0}".format(DSC_dict["dimensions"]))
-
- cmplx_data_vector = data_vector_real + 1j * data_vector_imag
-
- return cmplx_data_vector.T
-
-
-
-
-[docs]
-def load_axis_vector(axis: str, DSC_dict: dict) -> np.array:
- """
- Load the points of a given axis (x, y, z).
-
- Parameters
- ----------
- axis : str
- Name of the axis. Needs to start with x,X,y,Y or z,Z. E. g.: x-axis.
- DSC_dict : dict
- Dictionary with all parameters. Key is the same as in .DSC.
-
- Raises
- ------
- ValueError
- Will be raised if the given axis doesn't starts with an allowed
- character.
-
- Returns
- -------
- axis_vector : np.array
- Array with all axis points.
-
- """
- axis = axis.upper()
- if axis.startswith("X"):
- axis = "X"
- elif axis.startswith("Y"):
- axis = "Y"
- elif axis.startswith("Z"):
- axis = "Z"
- else:
- raise ValueError("Axis needs to start with 'x', 'y' or 'z'!")
-
- axis_file = os.path.join(
- DSC_dict["path_to_folder"], DSC_dict["TITL"] + "." + axis + "GF"
- )
- data_key = axis + "FMT"
-
- try:
- byte_mode = get_byte_mode(DSC_dict, data_key=data_key)
- axis_vector = np.fromfile(axis_file, dtype=byte_mode)
- except (KeyError, FileNotFoundError):
- try:
- minimum = DSC_dict[axis + "MIN"]
- except KeyError:
- axis_vector = np.array([])
- else:
- width = DSC_dict[axis + "WID"]
- points = DSC_dict[axis + "PTS"]
- axis_vector = np.linspace(minimum, minimum + width, points)
-
- return axis_vector
-
-
-
-
-[docs]
-def get_data_dimension(DSC_dict: dict) -> None:
- """
- Get the dimension of the measured spectrum (1d/2d).
-
- Parameters
- ----------
- DSC_dict : dict
- Dictionary with all parameters. Key is the same as in .DSC.
-
- Returns
- -------
- None
- The dimension will be safed in the given dictionary to the key
- 'dimensions'.
-
- """
- dim = 0
- for axis in ("XTYP", "YTYP", "ZTYP"):
- if DSC_dict[axis] != "NODATA":
- dim += 1
-
- DSC_dict["dimensions"] = dim
-
- return None
-
-
-
-
-[docs]
-def load_epr_bruker_bes3t(
- folder: str, start_directory: str = None
-) -> Tuple[tuple, np.array]:
- """
- Load the whole dataset from BRUKER BES3T data folder into numpy.arrays.
-
- Time axis will be rescaled for OOP-ESEEM experiments, if 'FTAcqModeSlct'
- is 'Run from Tables'.
-
- Parameters
- ----------
- folder : str
- Name of the data folder with the corresponding data files.
- start_directory : str, optional
- Give the path starting from your home folder which the search for the
- data folder should start at. The default is None.
- E. g.: data is at /home/cooluser/nice/data/this_folder
- and you know, that all your data is in /home/cooluser/nice/*, then you
- can give this start_directory with start_directory='nice'. So
- the function call would be:
- load_epr_bruker_bes3t(this_folder, 'nice')
-
- Recommended:
- Without this parameter, the search for the right folder will be
- much longer.
-
- Returns
- -------
- axis : tuple
- Tuple of all axis vectors as three numpy.arrays (x, y, z).
- data : np.array, np.complex128
- All intensity values as one complex numpy.array.
-
- """
- path = get_full_path(folder, start_directory)
- DSC_parameters = get_DSC_parameters(path)
-
- data = load_data_vector(DSC_parameters)
- x = load_axis_vector("x", DSC_parameters)
- try:
- if DSC_parameters["FTAcqModeSlct"] == "Tables":
- x /= 2
- except KeyError:
- pass
- y = load_axis_vector("y", DSC_parameters)
- z = load_axis_vector("z", DSC_parameters)
- axis = (x, y, z)
-
- return axis, data
-
-
-
-
-[docs]
-def load_epr_ESP_transient(
- folder: str, start_directory: str = None
-) -> Tuple[tuple, np.array]:
- """
- Load data from a transient epr experiment measured with ESP380E.
-
- Parameters
- ----------
- folder : str
- Name of the folder with all data files. Data files need to have the
- same basename as folder, e. g. '/home/user/exp_1/exp_1.001'.
- start_directory : str, optional
- Give the path starting from your home folder which the search for
- the data folder should start at. The default is None.
- E. g.: data is at /home/cooluser/nice/data/this_folder
- and you know, that all your data is in /home/cooluser/nice/*, then you
- can give this start_directory with start_directory='nice'. So
- the function call would be:
- load_epr_ESP_transient(this_folder, 'nice')
-
- Recommended:
- Without this parameter, the search for the right folder will be
- much longer.
-
- Returns
- -------
- axis : tuple
- Return a tuple with all axis vectors as two numpy.arrays
- (magnetic_field, time).
- data : np.array, np.complex128
- All intensity values as one complex numpy.array. Imaginary part is
- always 0.
-
-
- """
- path = get_full_path(folder, start_directory)
- # tr_info = get_transient_info(path)
- # time_axis = np.linspace(0, tr_info[0], tr_info[1])
- # points = int((abs(tr_info[3] - tr_info[2])) / tr_info[4] + 1)
- # magnetic_field = np.linspace(tr_info[2], tr_info[3], points)
- # axis = (magnetic_field, time_axis)
-
- # dimensions = (magnetic_field.shape[0], time_axis.shape[0])
- axis, data = get_transient_data(path)
-
- return axis, data
-
-
-
-
-[docs]
-def get_transient_info(fpath: str) -> tuple:
- """
- Get all informations about the time axis and magnetic field vector from the
- .info file.
-
- Parameters
- ----------
- fpath : str
- Full path to the folder with the experimental data.
-
- Raises
- ------
- KeyError
- Raised if section 'MAGNETIC FIELD' or 'TRANSIENT' could not be found in
- .info file. Take care that the sections must be all upper case.
-
- Returns
- -------
- transient_info : tuple
- Contains the needed informations in the following order (time_length,
- time_points, mag_field_start, mag_field_stop, mag_field_step).
-
- """
- fbasename = os.path.basename(fpath)
- fname = os.path.join(fpath, fbasename + ".info")
- transient_info = [None] * 5
-
- with open(fname, "r") as info_file:
- current_section = ""
- for line in info_file.readlines():
- line = line.strip().split()
- if line == []:
- pass
- else:
- if line[0].isupper():
- current_section = line[0]
- else:
- if current_section == "MAGNETIC":
- if line[0] == "Start:":
- transient_info[2] = float(line[1])
- elif line[0] == "Stop:":
- transient_info[3] = float(line[1])
- elif line[0] == "Step:":
- transient_info[4] = float(line[1])
- else:
- pass
- elif current_section == "TRANSIENT":
- if line[0] == "Points:":
- transient_info[1] = int(line[1])
- elif line[0] == "Length:":
- transient_info[0] = float(line[1])
- else:
- pass
- else:
- pass
-
- if None not in transient_info:
- break
-
- if transient_info[0] is None:
- raise KeyError("Couldn't find section 'TRANSIENT' in .info file!")
- if transient_info[2] is None:
- raise KeyError("Couldn't find section 'MAGNETIC FIELD' in .info file!")
-
- return tuple(transient_info)
-
-
-
-
-[docs]
-def get_transient_data(fpath: str) -> Tuple[tuple, np.array]:
- """
- Get the measured intensities of the whole spectrum from a transient epr
- experiment measured by ESP380E.
-
- Parameters
- ----------
- fpath : str
- Full path to the folder with the experimental data.
-
- Returns
- -------
- axis : tuple
- Return a tuple with all axis vectors as two numpy.arrays
- (magnetic_field, time).
- cmplx_data_vector : np.array, np.complex128
- Measured real intensities. All imaginary parts are zero.
-
- """
- data_vector_real = []
- magnetic_field = []
-
- file_found = False
- max_digits = 1
- while not file_found:
- fname = os.path.join(
- fpath,
- os.path.basename(fpath) + ".{0:0>{1}s}".format("1", str(max_digits)),
- )
- if os.path.isfile(fname):
- file_found = True
- else:
- max_digits += 1
-
- if max_digits >= 10:
- raise FileNotFoundError("No first ESP file could be found!")
-
- file_exists = True
- file = 1
- while file_exists:
- try:
- if file == 1:
- (field_point, time_axis), data = read_single_transient_file(
- fpath, file, max_digits, time=True
- )
- else:
- field_point, data = read_single_transient_file(fpath, file, max_digits)
- file += 1
- except OSError:
- file_exists = False
- else:
- data_vector_real.append(data)
- magnetic_field.append(round(field_point, 1))
-
- magnetic_field = np.array(magnetic_field)
- sorting = np.argsort(magnetic_field)
- magnetic_field = magnetic_field[sorting]
- cmplx_data_vector = np.array(data_vector_real, dtype=np.complex128)
- cmplx_data_vector = cmplx_data_vector[sorting]
- axis = (magnetic_field, time_axis)
-
- return axis, cmplx_data_vector.T
-
-
-
-
-[docs]
-def read_single_transient_file(
- fpath: str,
- filenumber: int,
- digits: int,
- time: bool = False,
-) -> Tuple[float, np.array]:
- """
- Get the measured intensities of a single field point from a transient epr
- experiment measured by ESP380E.
-
- Parameters
- ----------
- fpath : str
- Full path to the folder with the experimental data.
- filenumber : int
- Number of the dataset for the magnetic field point e. g. 003.
- digits : int
- Number of digits from the highest filenumber (ESP380E has an increasing
- number as fileextension for each magnetic field point e. g. data.001).
- time : bool, optional
- If True, the time axis will be returned.
-
- Returns
- -------
- field : float
- Magnetic field point.
- time_axis : np.array
- Linear time axis.
- data_vector : np.array
- Measured intensities.
-
- """
- fbasename = os.path.basename(fpath)
- ending = ".{0:0>{1}s}".format(str(filenumber), str(digits))
- fname = os.path.join(fpath, fbasename + ending)
-
- try:
- data_vector = np.genfromtxt(fname, skip_header=5)
- data_vector = data_vector.flatten()
- except ValueError:
- data_vector = []
- with open(fname) as file:
- for i, line in enumerate(file.readlines()):
- if i < 5:
- continue
- else:
- line = line.strip().split()
- data_vector.extend(line)
- data_vector = np.array(data_vector, dtype=np.complex128)
-
- with open(fname) as file:
- for i, line in enumerate(file.readlines()):
- if line.startswith("B0"):
- line = line.strip().split()
- field = float(line[2])
- if i == 3 and time:
- line = line.strip().split()
- points = int(line[1])
- start = float(line[2])
- stop = float(line[3])
- time_axis = np.linspace(start, stop, points)
- if i == 4:
- break
-
- if time:
- return (field, time_axis), data_vector
-
- return field, data_vector
-
-
-
-
-[docs]
-def load_simulated_data(
- folder: str, start_directory: str = None
-) -> Tuple[np.array, np.array, np.array]:
- """
- Load the simulated data from EasyPairSpin or data saved with
- saving.save_simulation(). Using numpy.loadtxt().
-
- Parameters
- ----------
- folder : str
- Name of the data folder with the corresponding data files.
- start_directory : str, optional
- Give the path starting from your home folder which the search for
- the data folder should start at. The default is None.
- E. g.: data is at /home/cooluser/nice/data/this_folder
- and you know, that all your data is in /home/cooluser/nice/*, then you
- can give this start_directory with start_directory='cooluser/nice'. So
- the function call would be:
- load_simulated_data(this_folder, 'cooluser/nice')
-
- Recommended:
- Without this parameter, the search for the right folder
- will be much longer.
-
- Returns
- -------
- x : np.array
- Axis vector for the x-axis.
- y : np.array
- Axis vector for the y-axis. Only returned, if the simulated data is 2d.
- intensity : np.array, np.complex128
- Simulated intensities. Either 1d or 2d.
-
- """
- path = get_full_path(folder, start_directory)
-
- x = np.loadtxt(os.path.join(path, "x_axis.txt"))
- try:
- intensity = np.loadtxt(os.path.join(path, "intensity.txt"))
- except ValueError:
- intensity = np.loadtxt(os.path.join(path, "intensity.txt"), dtype=np.complex_)
-
- try:
- y = np.loadtxt(os.path.join(path, "y_axis.txt"))
- except IOError:
- return (x, intensity)
- else:
- return (x, y, intensity)
-
-
-
-
-[docs]
-def load_matlab(
- folder: str,
- start_directory: str = None,
- field: str = "field",
- signal: str = "signal",
-) -> Tuple[np.array, np.array, np.array]:
- """
- Load EPR data from a matlab data file .mat
-
- Parameters
- ----------
- folder : str
- Name of the data folder with the corresponding data files.
- start_directory : str, optional
- Give the path starting from your home folder which the search for
- the data folder should start at. The default is None.
- E. g.: data is at /home/cooluser/nice/data/this_folder
- and you know, that all your data is in /home/cooluser/nice/*, then you
- can give this start_directory with start_directory='cooluser/nice'. So
- the function call would be:
- load_simulated_data(this_folder, 'cooluser/nice')
- field : str, optional
- Name of the field array. The default is 'field'.
- signal : str, optional
- Name of the signal array. The default is 'signal'.
-
- Returns
- -------
- axis : np.array
- Tuple of np.array containing the x and y axis.
- data : np.array
- np.array with the measured intensities.
-
- """
- path = get_full_path(folder, start_directory)
- mat_data = loadmat(path)
- x = mat_data[field].flatten()
- y = np.array([])
- data = mat_data[signal].flatten()
- axis = (x, y)
-
- return axis, data
-
-
-
-
-[docs]
-def load_txt(
- folder: str, start_directory: str = None
-) -> Tuple[np.array, np.array, np.array]:
- """
- Load EPR data from a .txt file.
-
- Parameters
- ----------
- folder : str
- Name of the data folder with the corresponding data files.
- start_directory : str, optional
- Give the path starting from your home folder which the search for
- the data folder should start at. The default is ''.
- E. g.: data is at /home/cooluser/nice/data/this_folder
- and you know, that all your data is in /home/cooluser/nice/*, then you
- can give this start_directory with start_directory='cooluser/nice'. So
- the function call would be:
- load_simulated_data(this_folder, 'cooluser/nice')
-
- Returns
- -------
- axis : np.array
- Tuple of np.array containing the x and y axis.
- data : np.array
- np.array with the measured intensities.
-
- """
- path = get_full_path(folder, start_directory)
- try:
- x, data = np.loadtxt(path)
- y = np.array([])
- except ValueError:
- try:
- x, data = np.loadtxt(path, unpack=True)
- y = np.array([])
- except ValueError:
- try:
- x, y, data = np.loadtxt(path)
- except ValueError:
- x, y, data = np.loadtxt(path, unpack=True)
- axis = (x, y)
-
- return axis, data
-
-
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-© M. Sc. Florian Quintes, 2021-2022
-
-@contact: florian.quintes@pc.uni.freiburg.de
-
-@author: Florian Quintes
-"""
-
-from matplotlib import cm
-import matplotlib.pyplot as plt
-import numpy as np
-import os
-import sys
-from spinanalysis import profiles
-
-
-
-[docs]
-class HiddenPrints:
- """Supress Error Messages in a context manager."""
-
- def __enter__(self):
- """Deactivate the error stream when entering the context manager."""
- self._original_stderr = sys.stderr
- sys.stderr = open(os.devnull, "w")
-
- def __exit__(self, exc_type, exc_val, exc_tb):
- """Activate the error stream when leaving the context manager."""
- sys.stderr.close()
- sys.stderr = self._original_stderr
-
-
-
-
-[docs]
-def plot_2D(
- x: np.array,
- y: np.array,
- mpl_stylesheet: str = "default_stylesheet",
- labels: list[str] = "no_label",
- ax: object = None,
- **kwargs,
-) -> object:
- """
- Plot the given y value(s) against the given x array.
-
- Using matplotlib.pylab.plot(). The plot can be configured via plot
- profiles.
-
- Parameters
- ----------
- x : np.array
- Array with values for the x axis.
- y : np.array
- 1D-Array or 2D-Array with values for y axis.
- mpl_stylesheet : str, optional
- Name of the matplotlib style sheet (see: matplotlib documentation).
- If no style sheet is given, the styles defined in the plotting profile
- will be used. The default is None.
- labels : list[str], optional
- List of labels for the legend. If only one label is given, all labels
- will be the same. The default is 'no_label'.
- ax : object, optional
- Axes object, used for the PySpin GUI.
- **kwargs : optional
- Keyword arguments passed to the matplotlib plot function. Overrides the
- arguments given in the stylesheet.
-
- Returns
- -------
- fig : object
- Figure object of matplotlib.pylab.
-
- """
- profile = profiles.load_plot_profile(mpl_stylesheet)
- profile.update((k, kwargs[k]) for k in profile.keys() & kwargs.keys())
- for k in profile.keys() & kwargs.keys():
- kwargs.pop(k)
- style = _get_style_path(mpl_stylesheet)
- default_styles = _load_default_styles()
- default_styles.append(style)
-
- with HiddenPrints():
- with plt.style.context(default_styles):
- if ax is None:
- # [NEW FIGURE]
- fig, ax = plt.subplots()
- else:
- fig, _ = plt.subplots()
-
- # [LABELS]
- if y.ndim > 1 and not (type(labels) is list):
- labels = _get_label_list(y.shape[0], labels)
-
- # [PLOT DATA]
- if y.ndim == 1:
- ax.plot(x, y, label=labels, **kwargs)
- else:
- for y_set in range(len(y)):
- ax.plot(x, y[y_set], label=labels[y_set], **kwargs)
-
- # [Axes]
- _set_axis(ax, profile, x, y)
- _set_figure(ax, profile, mplstylesheet=mpl_stylesheet)
-
- return fig
-
-
-
-
-[docs]
-def shifted_2D(
- x: np.array,
- Y: np.array,
- mpl_stylesheet: str = "default_stylesheet",
- labels: list[str] = "no_label",
- ax: object = None,
- **kwargs,
-) -> object:
- """
- Plot multiples lines in 2D, shifted vertically.
-
- The plot can be configured via plot profiles.
-
- Parameters
- ----------
- x : np.array
- Array with values for the x axis.
- Y : np.array
- 2D-Array with values for y axis.
- mpl_stylesheet : str, optional
- Name of the matplotlib style sheet (see: matplotlib documentation).
- If no style sheet is given, the styles defined in the plotting profile
- will be used. The default is None.
- labels : list[str], optional
- List of labels for the legend. If only one label is given, all labels
- will be the same. The default is 'no_label'.
- ax : object, optional
- Axes object, used for the PySpin GUI.
- **kwargs : optional
- Keyword arguments passed to the matplotlib plot function. Overrides the
- arguments given in the stylesheet.
-
- Returns
- -------
- fig : object
- Figure object of matplotlib.pylab.
-
- """
- profile = profiles.load_plot_profile(mpl_stylesheet)
- profile.update((k, kwargs[k]) for k in profile.keys() & kwargs.keys())
- for k in profile.keys() & kwargs.keys():
- kwargs.pop(k)
- style = _get_style_path(mpl_stylesheet)
- default_styles = _load_default_styles()
- default_styles.append(style)
-
- with HiddenPrints():
- with plt.style.context(default_styles):
- # [NORMALIZE DATA]
- Y = Y / np.max(np.abs(Y))
-
- # [SHIFT DATA]
- shift_matrix = _get_shift_matrix(Y.shape)
- Y += shift_matrix
-
- if ax is None:
- # [NEW FIGURE]
- fig, ax = plt.subplots()
- else:
- fig = None
-
- # [LABELS]
- if not type(labels) is list:
- labels = _get_label_list(Y.shape[0], labels)
-
- # [PLOT DATA]
- for y_set in range(Y.shape[0]):
- ax.plot(x, Y[y_set], label=labels[y_set], **kwargs)
-
- # [AXES]
- _set_axis(ax, profile, x, Y, y_axis=False)
- _set_figure(ax, profile, mplstylesheet=mpl_stylesheet)
-
- return fig
-
-
-
-
-[docs]
-def plot_3D(
- x: np.array,
- y: np.array,
- Z: np.array,
- mpl_stylesheet: str = "default_stylesheet",
- labels: str = "no_label",
- ax: object = None,
- **kwargs,
-) -> object:
- """
- Plot 2D Data in 3D using matplotlib.pylab.plot_surface().
-
- The plot can be configured via plot profiles.
-
- Parameters
- ----------
- x : np.array
- Array with values for the x axis.
- y : np.array
- Array with values for the y axis.
- Z : np.array
- 2D-Array with intensities.
- mpl_stylesheet : str, optional
- Name of the matplotlib style sheet (see: matplotlib documentation).
- If no style sheet is given, the styles defined in the plotting profile
- will be used. The default is None.
- labels : str, optional
- At the moment no function. The default is 'no_label'. # TODO
- ax : object, optional
- Axes object, used for the PySpin GUI.
- **kwargs : optional
- Keyword arguments passed to the matplotlib plot function. Overrides the
- arguments given in the stylesheet.
-
- Returns
- -------
- fig : object
- Figure object of matplotlib.pylab.
-
- """
- profile = profiles.load_plot_profile(mpl_stylesheet)
- profile.update((k, kwargs[k]) for k in profile.keys() & kwargs.keys())
- for k in profile.keys() & kwargs.keys():
- kwargs.pop(k)
- style = _get_style_path(mpl_stylesheet)
- default_styles = _load_default_styles()
- default_styles.append(style)
-
- kwargs_ = {"cmap": cm.coolwarm, "antialiased": True, "linewidth": 0}
- kwargs_.update(kwargs)
-
- with HiddenPrints():
- with plt.style.context(default_styles):
- if ax is None:
- # [NEW FIGURE]
- fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
- else:
- fig = None
-
- # [GRID]
- X, Y = np.meshgrid(x, y)
-
- # [PLOT DATA]
- try:
- ax.plot_surface(X, Y, Z, **kwargs_)
- except ValueError:
- ax.plot_surface(X, Y, Z.T, **kwargs_)
-
- # [AXES]
- _set_axis(ax, profile, X, Y, Z)
- _set_figure(ax, profile, mplstylesheet=mpl_stylesheet)
-
- return fig
-
-
-
-
-[docs]
-def plot_3D_multiple_lines(
- x: np.array,
- y: np.array,
- Z: np.array,
- mpl_stylesheet: str = "default_stylesheet",
- ax: object = None,
- **kwargs,
-) -> object:
- """
- Plot 2D Data in 3D using matplotlib.pylab.plot().
-
- Each y trace as a single line plot. The plot can be configured via plot
- profiles.
-
- Parameters
- ----------
- x : np.array
- Array with values for the x axis.
- y : np.array
- Array with values for the y axis.
- Z : np.array
- 2D-Array with intensities.
- mpl_stylesheet : str, optional
- Name of the matplotlib style sheet (see: matplotlib documentation).
- If no style sheet is given, the styles defined in the plotting profile
- will be used. The default is None.
- ax : object, optional
- Axes object, used for the PySpin GUI.
- **kwargs : optional
- Keyword arguments passed to the matplotlib plot function. Overrides the
- arguments given in the stylesheet.
-
- Returns
- -------
- fig : object
- Figure object of matplotlib.pylab.
-
- """
- profile = profiles.load_plot_profile(mpl_stylesheet)
- profile.update((k, kwargs[k]) for k in profile.keys() & kwargs.keys())
- for k in profile.keys() & kwargs.keys():
- kwargs.pop(k)
- style = _get_style_path(mpl_stylesheet)
- default_styles = _load_default_styles()
- default_styles.append(style)
-
- with HiddenPrints():
- with plt.style.context(default_styles):
- if ax is None:
- # [NEW FIGURE]
- fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
- else:
- fig = None
-
- # [PLOT DATA]
- # TODO: Hier muss drüber nachgedacht werden, wann Z transponiert werden
- # muss, damit man die x- und y-Achse in beliebiger Reihenfolge geben
- # kann.
- X = np.ones((len(y), len(x))) * x
- X = X.T
- for dataset in range(len(Z.T)):
- ax.plot(X[dataset], y, Z[:, dataset], **kwargs)
-
- # [AXES]
- _set_axis(ax, profile, x, y, Z)
- _set_figure(ax, profile, mplstylesheet=mpl_stylesheet)
-
- return fig
-
-
-
-
-[docs]
-def heatmap(
- x: np.array,
- y: np.array,
- Z: np.array,
- mpl_stylesheet: str = "default_stylesheet",
- ax: object = None,
- **kwargs,
-) -> object:
- """
- Plot 2D Data as a heatmap using matplotlib.pylab.pcolormesh().
-
- The plot can be configured via plot profiles.
-
- Parameters
- ----------
- x : np.array
- Array with values for the x axis.
- y : np.array
- Array with values for the y axis.
- Z : np.array
- 2D-Array with intensities.
- mpl_stylesheet : str, optional
- Name of the matplotlib style sheet (see: matplotlib documentation).
- If no style sheet is given, the styles defined in the plotting profile
- will be used. The default is None.
- ax : object, optional
- Axes object, used for the PySpin GUI.
- **kwargs : optional
- Keyword arguments passed to the matplotlib plot function. Overrides the
- arguments given in the stylesheet.
-
- Returns
- -------
- fig : object
- Figure object of matplotlib.pylab.
-
- """
- profile = profiles.load_plot_profile(mpl_stylesheet)
- profile.update((k, kwargs[k]) for k in profile.keys() & kwargs.keys())
- for k in profile.keys() & kwargs.keys():
- kwargs.pop(k)
- style = _get_style_path(mpl_stylesheet)
- default_styles = _load_default_styles()
- default_styles.append(style)
-
- kwargs_ = {"cmap": "RdBu", "shading": "auto"}
- kwargs_.update(kwargs)
-
- with HiddenPrints():
- with plt.style.context(default_styles):
- if ax is None:
- # [NEW FIGURE]
- fig, ax = plt.subplots()
- else:
- fig = None
-
- # [GRID]
- X, Y = np.meshgrid(x, y)
-
- # [PLOT DATA]
- try:
- c = ax.pcolormesh(X, Y, Z, **kwargs_)
- except TypeError:
- c = ax.pcolormesh(X, Y, Z.T, **kwargs_)
-
- # [PLOT COLORBAR]
- if profile["colorbar"]:
- fig.colorbar(c, ax=ax)
-
- # [AXES]
- _set_axis(ax, profile, X, Y)
- _set_figure(ax, profile, mplstylesheet=mpl_stylesheet)
-
- return fig
-
-
-
-def _get_shift_matrix(shape: tuple[int, int]) -> np.array:
- """
- Create a matrix to shift the single lines vertically.
-
- Needed for plot_shifted_2D().
-
- Parameters
- ----------
- shape : tuple[int, int]
- Shape of the data which will be shifted. Use np.array.shape to get the
- shape from your data.
-
- Returns
- -------
- shift_matrix : np.array
- Matrix to shift your data vertically by adding the shift matrix.
- E. g. : structure of the shift matrix for shape (3, 2):
- ::
-
- [[0, 0],
- [2, 2],
- [4, 4]]
-
- """
- shift_matrix = 2 * np.linspace(
- 0, np.full((1, shape[1]), (shape[0] - 1))[0], shape[0]
- )
-
- return shift_matrix
-
-
-def _set_axis(
- ax: object, figure_par: dict, *axes: np.array, y_axis: bool = True
-) -> object:
- """
- Set up all axis for a plot created by matplotlib.
-
- Parameters
- ----------
- ax : object
- Axis object of matplotlib.pylab.
- figure_par : dict
- Dictionary containing all settings for the figure.
- *axes : np.array
- All axes used for the plot. Used to determine the dimension an set up
- the z axis if needed.
- y_axis : bool, optional
- If a y axis is needed. If False, all y ticks will be removed. The
- default is True.
-
- Returns
- -------
- ax : object
- Modified axis object of matplotlib.pylab.
-
- """
- ax.set_xlabel(figure_par["xlabel"])
- ax.set_ylabel(figure_par["ylabel"])
- ax.set_xlim(
- _get_axis_limit(axes[0], figure_par["xlim"], figure_par["percentage_mode"])
- )
- if y_axis:
- ax.set_ylim(
- _get_axis_limit(axes[1], figure_par["ylim"], figure_par["percentage_mode"])
- )
- else:
- ax.set_yticks([])
-
- if len(axes) == 3:
- ax.set_zlabel(figure_par["zlabel"])
- ax.set_zlim(
- _get_axis_limit(axes[2], figure_par["zlim"], figure_par["percentage_mode"])
- )
-
- return ax
-
-
-def _set_figure(ax: object, figure_par: dict, mplstylesheet: str = None) -> object:
- """
- Set up the figure of a plot created by matplotlib.
-
- Parameters
- ----------
- ax : object
- Axis object of matplotlib.pylab.
- figure_par : dict
- Dictionary containing all settings for the figure.
- mplstylesheet : str, optional
- Name of the stylesheet which should be used instead of a plotting
- profile. If no filename is given, the settings from the given plotting
- profile will be used. The default is None.
-
- Returns
- -------
- ax : object
- Modified axis object of matplotlib.pylab.
-
- """
- if figure_par["show_title"]:
- ax.set_title(figure_par["title"])
- if figure_par["legend"]:
- ax.legend()
-
- return ax
-
-
-def _get_style_path(mplstylesheet: str = None) -> str:
- """
- Get the path to the choosen mpl stylesheet.
-
- Parameters
- ----------
- mplstylesheet : str, optional
- Name of the mpl stylesheet. The default is None.
-
- Returns
- -------
- str
- Path to the choosen stylesheet.
-
- """
- if mplstylesheet is None:
- style = os.path.join(
- sys.prefix,
- "easypairspin",
- "profiles",
- "plot",
- "default_stylesheet",
- )
- elif mplstylesheet in plt.style.available:
- style = mplstylesheet
- else:
- style = os.path.join(
- sys.prefix, "easypairspin", "profiles", "plot", mplstylesheet
- )
-
- return style
-
-
-def _load_default_styles() -> list:
- """
- Load the pathes for the default matplotlib styles.
-
- Returns
- -------
- default_styles : list
- Pathes for the default style sheetss.
-
- """
- default_styles = []
- for def_style in ["axis", "figure", "plot", "line", "text"]:
- def_style = def_style + "_style"
- default_styles.append(_get_style_path(def_style))
-
- return default_styles
-
-
-def _get_label_list(length: int, label: str) -> list[str]:
- """
- Get a list with length times the label.
-
- Parameters
- ----------
- length : int
- Number of labels.
- label : str
- String which will be multiplied.
-
- Returns
- -------
- list[str]
- Length times label as a list. E. g.: ['label', 'label', 'label'].
-
- """
- labels = [label] * length
-
- return labels
-
-
-def _get_axis_limit(
- data_axis: np.array, ax_lim: list, percentage_mode: bool
-) -> [float, float]:
- """
- Get the plot limits for a given data axis.
-
- If no limit is specified, the minimal and maximal value of the given
- dataset will be taken.
-
- Parameters
- ----------
- data_axis : np.array
- Data vector for an axis.
- ax_lim : list
- Limits for the data vector taken from a profile.
- percentage_mode : bool
- If True, the ax.lim will be handled as a percentage in context to the
- lowest and highest values with respect to the given axis.
- E. g.: x-vector with values [0, 200], ax_lim: [-2., 2.] -> [4, 204].
-
- Returns
- -------
- [float, float]
- Limits for plotting.
-
- """
- if percentage_mode:
- if ax_lim == "":
- min_val = data_axis.min()
- max_val = data_axis.max()
- limits = [min_val, max_val]
- else:
- value_range = data_axis.max() - data_axis.min()
- lower_diff = value_range * ax_lim[0] / 100
- upper_diff = value_range * ax_lim[1] / 100
- lower_border = data_axis.min() - lower_diff
- upper_border = data_axis.max() + upper_diff
- limits = [lower_border, upper_border]
- else:
- if ax_lim == "":
- min_val = data_axis.min()
- max_val = data_axis.max()
- limits = [min_val, max_val]
- else:
- limits = ax_lim
-
- return limits[0], limits[1]
-
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-© M. Sc. Florian Quintes, 2021-2022
-
-@contact: florian.quintes@pc.uni.freiburg.de
-
-@author: Florian Quintes
-"""
-
-import numpy as np
-from scipy.optimize import curve_fit
-from scipy import integrate
-from statsmodels.tsa.ar_model import AutoReg, ar_select_order
-
-
-
-[docs]
-def normalization(x: np.array, mode: str = None, dx: float = None) -> np.array:
- r"""
- Normalize the given data.
-
- .. math::
- x_{\mathrm{norm}} = \frac{x_i - min(x)}{max(x)-min(x)}
-
- Parameters
- ----------
- x : np.array
- Unnormalized data.
- mode : str, optional
- If 'area', the total AUC will be 1, if 'value', the maximum value will
- be 1 or -1, else, the formula mentioned above will be used. Default is
- None.
- dx : float, optional
- Distance between two points on the x axis. Only used for simpson
- integration. Default is None.
-
- Returns
- -------
- x_norm : np.array
- Normalized data.
-
- """
- if mode is None:
- if x.dtype == "complex":
- x_norm = np.zeros(x.shape, dtype=np.complex128)
- x_norm.real = (x.real) / (x.real.max() - x.real.min())
- x_norm.imag = (x.imag) / (x.imag.max() - x.imag.min())
- else:
- x_norm = (x) / (x.max() - x.min())
- elif mode == "area":
- if x.dtype == "complex":
- x_norm = np.zeros(x.shape, dtype=np.complex128)
- x_norm.real = x.real / abs(integrate.simpson(abs(x), dx=dx))
- try:
- x_norm.imag = x.imag / abs(integrate.simpson(abs(x), dx=dx))
- except ZeroDivisionError:
- pass
- else:
- x_norm = x / abs(integrate.simpson(x, dx=dx))
- elif mode == "value":
- if x.dtype == "complex":
- x_norm = np.zeros(x.shape, dtype=np.complex128)
- x_norm.real = x.real / max(abs(x.real))
- try:
- x_norm.imag = x.imag / max(abs(x.imag))
- except ZeroDivisionError:
- pass
- else:
- x_norm = x / max(abs(x))
-
- return x_norm
-
-
-
-
-[docs]
-def reduce_offset(x: np.array) -> np.array:
- """
- Eliminate the offset of the data by using the mean of the last quarter.
-
- Parameters
- ----------
- x : np.array
- Given data, e. g. measured intensities.
-
- Returns
- -------
- x_shifted : np.array
- Shifted data without offset.
-
- """
- start = 3 * x.shape[0] // 4
- if x.dtype == "complex":
- x_shifted = np.zeros(x.shape, dtype=np.complex128)
- x_shifted.real = x.real - x.real[start:].mean()
- x_shifted.imag = x.imag - x.imag[start:].mean()
- else:
- x_shifted = x - x[start:].mean()
-
- return x_shifted
-
-
-
-
-[docs]
-def background_corr(x: np.array, y: np.array, mode: str = "biexp") -> np.array:
- """
- Perform a background correction of measured data.
-
- Available correction modes are: biexp, exp, lin, poly2, poly3 and poly4.
- biexp and exp are using exponential models for the background. lin, poly2-4
- are polynominal models of first to fourth order. Default is 'biexp'.
-
- .. warning::
- poly3 and poly4 can lead to overfitting!
-
- Parameters
- ----------
- x : np.array
- x axis of the dataset.
- y : np.array
- y data which will be background corrected.
- mode : str, optional
- Select the type of the background. The default is 'biexp'.
-
- Returns
- -------
- y_corr : np.array
- Background corrected y data.
-
- """
- if mode == "biexp":
- p0 = [0.9, -0.002, 0.05, -0.0009, 0.0]
- popt, pcov = curve_fit(biexp_fun, x, y, p0=p0, maxfev=10000)
- y_corr = y - biexp_fun(x, *popt)
- elif mode == "exp":
- p0 = [1.0, -0.002, 0.0]
- popt, pcov = curve_fit(exp_fun, x, y, p0=p0, maxfev=10000)
- y_corr = y - exp_fun(x, *popt)
- elif mode == "lin":
- p0 = [1.0, -0.002]
- popt, pcov = curve_fit(lin_fun, x, y, p0=p0, maxfev=10000)
- y_corr = y - lin_fun(x, *popt)
- elif mode == "poly2":
- p0 = [1.0, -0.002, 0.0]
- popt, pcov = curve_fit(poly2_fun, x, y, p0=p0, maxfev=10000)
- y_corr = y - poly2_fun(x, *popt)
- elif mode == "poly3":
- p0 = [1.0, 1.0, -0.002, 0.0]
- popt, pcov = curve_fit(poly3_fun, x, y, p0=p0, maxfev=10000)
- y_corr = y - poly3_fun(x, *popt)
- elif mode == "poly4":
- p0 = [1.0, 1.0, 1.0, -0.002, -1.0]
- popt, pcov = curve_fit(poly4_fun, x, y, p0=p0, maxfev=10000)
- y_corr = y - poly4_fun(x, *popt)
-
- return y_corr
-
-
-
-
-[docs]
-def exp_fun(x: np.array, *coeff: float) -> np.array:
- """
- Generalized monoexponential function for background correction.
-
- Parameters
- ----------
- x : np.array
- x values used to calculate corresponding y values.
- *coeff : float
- Variables for the monoexponential function which will be fitted.
-
- Returns
- -------
- y : np.array
- Calculated y values.
-
- """
- a, b, c = coeff
- y = a * np.exp(b * x) + c
- return y
-
-
-
-
-[docs]
-def biexp_fun(x: np.array, *coeff: float) -> np.array:
- """
- Generalized biexponential function for background correction.
-
- Parameters
- ----------
- x : np.array
- x values used to calculate corresponding y values.
- *coeff : float
- Variables for the biexponential function which will be fitted.
-
- Returns
- -------
- y : np.array
- Calculated y values.
-
- """
- a, b, c, d, e = coeff
- y = a * np.exp(b * x) + c * np.exp(d * x) + e
- return y
-
-
-
-
-[docs]
-def lin_fun(x: np.array, *coeff: float) -> np.array:
- """
- Generalized linear function for background correction.
-
- Parameters
- ----------
- x : np.array
- x values used to calculate corresponding y values.
- *coeff : float
- Variables for the linear function which will be fitted.
-
- Returns
- -------
- y : np.array
- Calculated y values.
-
- """
- a, b = coeff
- y = a * x + b
- return y
-
-
-
-
-[docs]
-def poly2_fun(x: np.array, *coeff: float) -> np.array:
- """
- Generalized polynominal function of degree 2 for background correction.
-
- Parameters
- ----------
- x : np.array
- x values used to calculate corresponding y values.
- *coeff : float
- Variables for the polynominal function of degree 2 which will be
- fitted.
-
- Returns
- -------
- y : np.array
- Calculated y values.
-
- """
- a, b, c = coeff
- y = a * x**2 + b * x + c
- return y
-
-
-
-
-[docs]
-def poly3_fun(x: np.array, *coeff: float) -> np.array:
- """
- Generalized polynominal function of degree 3 for background correction.
-
- Parameters
- ----------
- x : np.array
- x values used to calculate corresponding y values.
- *coeff : float
- Variables for the polynominal function of degree 3 which will be
- fitted.
-
- Returns
- -------
- y : np.array
- Calculated y values.
-
- """
- a, b, c, d = coeff
- y = a * x**3 + b * x**2 + c * x + d
- return y
-
-
-
-
-[docs]
-def poly4_fun(x: np.array, *coeff: float) -> np.array:
- """
- Generalized polynominal function of degree 4 for background correction.
-
- Parameters
- ----------
- x : np.array
- x values used to calculate corresponding y values.
- *coeff : float
- Variables for the polynominal function of degree 4 which will be
- fitted.
-
- Returns
- -------
- y : np.array
- Calculated y values.
-
- """
- a, b, c, d, e = coeff
- y = a * x**4 + b * x**3 + c * x**2 + d * x + e
- return y
-
-
-
-
-[docs]
-def reconstruct(x: np.array, y: np.array) -> np.array:
- """
- Reconstruction of a time signal use the Yule-Walker algorithm.
-
- Parameters
- ----------
- x : np.array
- x axis.
- y : np.array
- Intensities.
-
- Returns
- -------
- x_new : np.array
- Reconstructed x axis.
- y : np.array
- Reconstructed intensities.
-
- """
- # prepare the new array
- x_step = x[1] - x[0]
- if x[0] % x_step != 0:
- x_fill_points = int(x[0] / x_step) + 1
- else:
- x_fill_points = int(x[0] / x_step)
- x_new = np.concatenate(
- (np.linspace(x[0] - x_step * x_fill_points, x[0] - x_step, x_fill_points), x)
- )
-
- # determine the order of the p value for the reconstruction
- order = ar_select_order(y[::-1], maxlag=40)
- nlag = len(order.ar_lags)
-
- # Fit the model to the data and make a predicition
- AutoRegFit = AutoReg(y[::-1], lags=order.ar_lags).fit()
- y_pred = AutoRegFit.predict(start=0, end=x_new.shape[0] + nlag - 1)
-
- y_pred = np.roll(y_pred[nlag:], nlag)
- y_flip = y_pred
- y_flip = np.concatenate((y[::-1], y_flip[len(y) :]))
-
- return x_new, y_flip[::-1]
-
-
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-© M. Sc. Florian Quintes, 2021-2022
-
-@contact: florian.quintes@pc.uni.freiburg.de
-
-@author: Florian Quintes
-"""
-
-from configobj import ConfigObj
-from matplotlib.pyplot import style
-from spinanalysis._utils import strtobool
-import validate
-import os
-import sys
-from zipfile import ZipFile
-
-
-
-[docs]
-def import_profiles(zipfile: str, override: bool = False) -> None:
- """
- Import profiles from a zip archive.
-
- Parameters
- ----------
- zipfile : str
- Path to the zip archive.
- override : bool, optional
- If True, existing profile with the same name will be overriden. The
- default is False.
-
- Returns
- -------
- None
- Nothing will be returned.
-
- """
- zipfile = os.path.abspath(zipfile)
- profiles_folder = os.path.join(sys.prefix, "easypairspin", "profiles")
-
- with ZipFile(zipfile, "r") as zipfile:
- for file in zipfile.namelist():
- if os.path.isfile(os.path.join(profiles_folder, file)) and not override:
- continue
-
- zipfile.extract(file, profiles_folder)
-
- zipfile.close()
-
-
-
-
-[docs]
-def export(path: str = None, pkind: [str, list] = "all", pname: str = "all") -> None:
- """
- Export the choosen profile(s) as a zip archive.
-
- Parameters
- ----------
- path : str, optional
- Path where the zip file will be stored. If no path given, the zip file
- will be stored in the current working directory. The default is None.
- pkind : str, optional
- Define which kind(s) of profiles should be exported. Multiple kinds of
- profiles possible. Options are 'plot', 'spinsystem', 'optimization',
- 'save', 'variation', 'simulation' and 'all'. The default is 'all'.
- pname : str, optional
- Give the basename of the profile. The default is 'all'.
-
- Returns
- -------
- None
- Nothing will be returned.
-
- """
- profiles_folder = os.path.join(sys.prefix, "easypairspin", "profiles")
-
- if pkind == "all":
- pkind = [
- "plot",
- "spinsystem",
- "optimization",
- "simulation",
- "variation",
- "save",
- ]
- else:
- if not isinstance(pkind, list):
- stack = []
- for el in pkind.lower().strip().split():
- if el in (
- "plot",
- "spinsystem",
- "optimization",
- "simulation",
- "variation",
- "save",
- ):
- stack.append(el)
- elif el == "optimisation":
- stack.append("optimization")
- else:
- continue
- pkind = stack
-
- else:
- stack = []
- for el in pkind:
- if el.lower() in (
- "plot",
- "spinsystem",
- "optimization",
- "simulation",
- "variation",
- "save",
- ):
- stack.append(el.lower())
- elif el.lower() == "optimisation":
- stack.append("optimization")
- else:
- continue
- pkind = stack
-
- file_paths = []
- for kind in pkind:
- file_paths.extend(_get_profile_paths(os.path.join(profiles_folder, kind)))
-
- if path is None:
- path = os.path.join(os.getcwd(), "profiles_easypairspin.zip")
- else:
- path = os.path.join(path, "profiles_easypairspin.zip")
-
- with ZipFile(path, "w") as zipfile:
- for file in file_paths:
- arcname = file[len(profiles_folder) + 1 :]
- zipfile.write(file, arcname)
-
- zipfile.close()
-
-
-
-def _get_profile_paths(pfolder: str, pname: str = "all") -> None:
- """
- Get the pathes of all profile.
-
- Parameters
- ----------
- pfolder: : str
- Folder with profiles.
- pname : str, optional
- Basename of the profile. The default is "all".
-
- Returns
- -------
- None
- Nothing will be returned.
-
- """
- paths = []
-
- for root, directories, files in os.walk(pfolder):
- for file in files:
- if file.endswith("configspec.ini"):
- continue
-
- if pname.lower() == "all":
- paths.append(os.path.join(pfolder, file))
- elif pname.lower() == os.path.basename(file):
- paths.append(os.path.join(pfolder, file))
- else:
- continue
-
- return paths
-
-
-
-[docs]
-def add_profile(profile: dict, pkind: str, pname: str = "") -> None:
- """
- Add a new profile for EasyPairSpin.
-
- Parameters
- ----------
- profile : dict
- Dictionary with all profile settings.
- pkind : str
- Give the kind of the kind of the profile. Not case sensitive. pkind can
- be 'plot', 'save', 'simulation', 'optimization', 'spinsystem' or
- 'variation'.
- pname : str, optional
- Name of the profile. If no profile name is given, a default one will
- be generated by _get_profile_name(). The default is ''.
-
-
- Raises
- ------
- ValueError
- Raised if pkind isn't 'plot', 'save', 'simulation', 'optimization',
- 'spinsystem' or 'variation'.
-
- Returns
- -------
- None
- Nothing will be returned.
-
- Examples
- --------
- Creating and adding a new profile:
-
- >>> Sys = epr_setup.Spinsystem()
- >>> Sys_profile = new_spinsystem_profile()
- >>> Sys_profile['g_1'] = [2.0034, 2.00156, 2.00228]
- >>> prom.add_profile(Sys.profile, 'spinsystem', 'Sys_prof_1')
-
- """
- pkind = pkind.lower()
- if pkind == "optimisation":
- pkind = "optimization"
-
- if pkind not in (
- "plot",
- "save",
- "simulation",
- "optimization",
- "spinsystem",
- "variation",
- ):
- raise ValueError(
- "pkind must be 'plot', 'save', 'simulation',"
- " 'optimization', 'spinsystem' or 'variation'! "
- )
-
- if pname == "":
- pname = _get_profile_name(pkind)
-
- config_path = os.path.join(sys.prefix, "easypairspin", "profiles", pkind)
- if pkind == "plot":
- config_name = os.path.join(config_path, pname)
- else:
- config_name = os.path.join(config_path, pname + ".ini")
- path_to_configspec = os.path.join(
- sys.prefix, "easypairspin", "profiles", pkind, "configspec.ini"
- )
-
- if not os.path.exists(config_path):
- os.makedirs(config_path)
-
- config = ConfigObj(config_name, configspec=path_to_configspec)
-
- # TODO Exception Handling, ob alle relevanten Werte (korrekt) gegeben sind
- # TODO match case Struktur einführen (wenn Python 3.10 möglich)
- if pkind == "plot":
- _save_config_plot(profile, config_name)
- elif pkind == "spinsystem":
- config = _set_configobj_spinsystem(profile, config)
- elif pkind == "variation":
- config = _set_configobj_variation(profile, config)
- elif pkind == "optimization":
- config = _set_configobj_optimization(profile, config)
- elif pkind == "save":
- config = _set_configobj_save(profile, config)
- elif pkind == "simulation":
- config = _set_configobj_simulation(profile, config)
-
- if not (pkind == "plot"):
- validator = validate.Validator()
- config.validate(validator)
- config.write()
-
- return None
-
-
-
-def _save_config_plot(profile: dict, path: str) -> object:
- """
- Write profile parameters into the ConfigObject for a plot profile.
-
- Parameters
- ----------
- profile : dict
- Plot profile.
- path : str
- Path, where the profile will be stored.
-
- Returns
- -------
- object
- ConfigObj from module configobj.
-
- """
- preamble = "## {:*^76s}\n## {:*^76s}\n## {:*^76s}\n".format(
- "", " EASYPAIRSPIN SPECIAL SETTINGS ", ""
- )
- with open(path, "w") as f:
- f.write(preamble)
- f.write("\n")
- for key in profile:
- if key.endswith("lim"):
- line = "{}: {}, {}".format(
- str(key), str(profile[key][0]), str(profile[key][1])
- )
- else:
- if isinstance(profile[key], (int, float, bool)):
- line = "{}: {}".format(str(key), str(profile[key]))
- else:
- line = '{}: "{}"'.format(str(key), str(profile[key]))
- f.write(line)
- f.write("\n")
- f.close()
-
-
-def _set_configobj_spinsystem(profile: dict, ConObj: object) -> object:
- """
- Write profile parameters into the ConfigObject for a spinsystem profile.
-
- Parameters
- ----------
- profile : dict
- Spinsystem profile.
- ConObj : object
- ConfigObj from module configobj.
-
- Returns
- -------
- object
- ConfigObj from module configobj.
-
- """
- ConObj["main"] = profile["main"]
-
- return ConObj
-
-
-def _set_configobj_variation(profile: dict, ConObj: object) -> object:
- """
- Write profile parameters into the ConfigObject for a variation profile.
-
- Parameters
- ----------
- profile : dict
- Variation profile.
- ConObj : object
- ConfigObj from module configobj.
-
- Returns
- -------
- object
- ConfigObj from module configobj.
-
- """
- ConObj["main"] = profile["main"]
-
- return ConObj
-
-
-def _set_configobj_optimization(profile: dict, ConObj: object) -> object:
- """
- Write profile parameters into the ConfigObject for an optimization profile.
-
- Parameters
- ----------
- profile : dict
- Optimization profile.
- ConObj : object
- ConfigObj from module configobj.
-
- Raises
- ------
- ValueError
- If no valid optimization routine is given.
-
- Returns
- -------
- object
- ConfigObj from module configobj.
-
- """
- ConObj["main"] = profile["main"]
-
- routine = profile["main"]["routine"]
- if routine in [
- "genetic",
- "minimize",
- "dual_annealing",
- "shgo",
- "differential_evolution",
- "basinhopping",
- "least_squares",
- ]:
- ConObj[routine] = profile[routine]
- else:
- raise ValueError("{} is no valid optimization routine!".format(routine))
-
- return ConObj
-
-
-def _set_configobj_simulation(profile: dict, ConObj: object) -> object:
- """
- Write profile parameters into the ConfigObject for a simulation profile.
-
- Parameters
- ----------
- profile : dict
- Simulation profile.
- ConObj : object
- ConfigObj from module configobj.
-
- Raises
- ------
- ValueError
- If no valid simulation routine is given.
-
- Returns
- -------
- object
- ConfigObj from module configobj.
-
- """
- ConObj["main"] = profile["main"]
-
- routine = profile["main"]["routine"]
- if routine in ["static_radpair", "teacups", "opossum", "didelphis"]:
- ConObj[routine] = profile[routine]
- else:
- raise ValueError("{} is no valid simulation routine!".format(routine))
-
- return ConObj
-
-
-def _set_configobj_save(profile: dict, ConObj: object) -> object:
- """
- Write profile parameters into the ConfigObject for a save profile.
-
- Parameters
- ----------
- profile : dict
- Save profile.
- ConObj : object
- ConfigObj from module configobj.
-
- Returns
- -------
- object
- ConfigObj from module configobj.
-
- """
- ConObj["main"] = profile["main"]
-
- return ConObj
-
-
-def _get_profile_name(pkind: str) -> str:
- """
- Search for the smallest number available for the default profile name.
-
- Scheme for default profile name is 'profile_[number]'
-
- Parameters
- ----------
- pkind : str
- Give the kind of the kind of the profile. Not case sensitive. pkind can
- be 'plot', 'save', 'simulation', 'optimization', 'spinsystem' or
- 'variation'.
-
- Returns
- -------
- name : str
- String with the available profile name e.g. 'profile_12'.
-
- """
- name = "profile_"
- suffix = "" if pkind == "plot" else ".ini"
- profile_number = 0
- profile_number_used = True
-
- while profile_number_used:
- profile_number += 1
- for root, dirs, files in os.walk(
- os.path.join(sys.prefix, "easypairspin", "profiles", pkind)
- ):
- counter = 0
- for file in files:
- if file.endswith("profile_" + str(profile_number) + suffix):
- counter += 1
-
- profile_number_used = counter
-
- name += str(profile_number)
-
- return name
-
-
-
-[docs]
-def load_profile(pname: str, pkind: str) -> dict:
- """
- Load a given plotting profile.
-
- Parameters
- ----------
- pname : str
- Name of the profile. Case sensitive. Either with .ini or not.
- E. g.: load_profile('test') or load_profile('test.ini').
- pkind : str
- Give the kind of the profile. Not case sensitive. pkind can be 'save',
- 'simulation', 'optimization', 'spinsystem' or 'variation'.
-
- Returns
- -------
- profile: dict
- Loaded profile as a dictionary.
-
- """
- if pname.endswith(".ini"):
- pname = pname[:-4]
-
- path_to_profile = os.path.join(
- sys.prefix, "easypairspin", "profiles", pkind, pname + ".ini"
- )
- path_to_configspec = os.path.join(
- sys.prefix, "easypairspin", "profiles", pkind, "configspec.ini"
- )
-
- config = ConfigObj(path_to_profile, configspec=path_to_configspec, file_error=True)
- validator = validate.Validator()
- config.validate(validator)
-
- profile = {}
-
- for section in config.sections:
- profile[section] = config[section]
-
- return profile
-
-
-
-
-[docs]
-def load_plot_profile(pname: str) -> dict:
- """
- Load a plotting profile from a mplstylesheet.
-
- Parameters
- ----------
- pname : str
- Name of the profile. Case sensitive.
-
- Returns
- -------
- dict
- Contains the settings for the plotting functions.
-
- """
- if pname is None or pname in style.available:
- pname = "default_stylesheet"
-
- path_to_profile = os.path.join(
- sys.prefix, "easypairspin", "profiles", "plot", pname
- )
-
- profile = {}
- with open(path_to_profile, "r") as file:
- for line in file.readlines():
- if line.startswith("#"):
- continue
- else:
- line = line.strip().split(":")
- if line[0] in (
- "percentage_mode",
- "xlim",
- "ylim",
- "zlim",
- "xlabel",
- "ylabel",
- "zlabel",
- "show_title",
- "title",
- "legend",
- "colorbar",
- ):
- if "lim" in line[0] and len(line[0]) == 4:
- bounds = line[1].split("#")[0].strip()
- bounds = bounds.split(",")
- lb = float(bounds[0])
- ub = float(bounds[1])
- profile[line[0]] = [lb, ub]
- elif ("label" in line[0] and len(line[0]) == 6) or (
- line[0] == "title"
- ):
- label = line[1].split("#")[0].strip()
- label = label.strip('"').strip("'")
- profile[line[0]] = label
- else:
- boolean = line[1].split("#")[0].strip()
- boolean = boolean.strip('"').strip("'")
- profile[line[0]] = bool(strtobool(boolean))
-
- return profile
-
-
-
-
-[docs]
-def new_plot_profile() -> dict:
- """
- Get an empty plotting profile.
-
- Returns
- -------
- default_profile: dict
- Dictionary with default settings for plottings.
-
- """
- default_profile = {
- "percentage_mode": False,
- "xlim": [0, 0],
- "ylim": [0, 0],
- "zlim": [0, 0],
- "xlabel": "",
- "ylabel": "",
- "zlabel": "",
- "legend": "off",
- "show_title": "off",
- "title": "",
- "colorbar": "on",
- }
-
- return default_profile
-
-
-
-
-[docs]
-def new_spinsystem_profile() -> dict:
- """
- Get a default spinsystem profile.
-
- Returns
- -------
- default_profile: dict
- Dictionary with default settings for a spinsystem.
-
- """
- default_profile = {
- "main": {
- "spin_system": "rp",
- "precursor": "triplet-zf",
- "population": [1.0, 0.0, 0.0],
- "g1": [2.002, 2.002, 2.002],
- "g2": [2.004, 2.004, 2.004],
- "g_tri": [2.002, 2.002, 2.002],
- "g": [2.004, 2.004, 2.004],
- "g1_frame": [0, 0, 0],
- "g2_frame": [0, 0, 0],
- "g_tri_frame": [0, 0, 0],
- "g_frame": [0, 0, 0],
- "width_gauss": 0.5,
- "acceptor_list": [1, 2, 3],
- "donor_list": [4, 5],
- "A1": [0, 0, 0],
- "A2": [0, 0, 0],
- "A3": [0, 0, 0],
- "A4": [0, 0, 0],
- "A5": [0, 0, 0],
- "A_eseem": 0.0,
- "omega_I": 0.0,
- "A1_frame": [0, 0, 0],
- "A2_frame": [0, 0, 0],
- "A3_frame": [0, 0, 0],
- "A4_frame": [0, 0, 0],
- "A5_frame": [0, 0, 0],
- "n1": 0,
- "I1": 0,
- "n2": 0,
- "I2": 0,
- "n3": 0,
- "I3": 0,
- "n4": 0,
- "I4": 0,
- "n5": 0,
- "I5": 0,
- "D": 0.0,
- "E": 0.0,
- "D_tri": 700.0,
- "E_tri": 0.0,
- "beta": 1.4,
- "J_0": 1e10,
- "J_ex": 0.1,
- "D_frame": [0, 0, 0],
- "D_tri_frame": [0, 0, 0],
- "T_relax_1": 0.0,
- "T_relax_2": 0.0,
- "decay": 0.0,
- "dynamics": [0.0, 0.0, 0.0, 0.0],
- "T_pm": 0.1,
- "amplitude": 0.0,
- "distribution": None,
- "distribution_order": 3,
- }
- }
-
- return default_profile
-
-
-
-
-[docs]
-def new_variation_profile() -> dict:
- """
- Get a default variation profile.
-
- Returns
- -------
- default_profile: dict
- Dictionary with default settings for variation.
-
- """
- default_profile = {
- "main": {
- "g1": [0, 0, 0],
- "g2": [0, 0, 0],
- "g_tri": [0, 0, 0],
- "g": [0, 0, 0],
- "A1": [0, 0, 0],
- "A2": [0, 0, 0],
- "A3": [0, 0, 0],
- "A4": [0, 0, 0],
- "A5": [0, 0, 0],
- "A_eseem": 0.0,
- "omega_I": 0.0,
- "D": 0.0,
- "D_tri": 0.0,
- "E": 0.0,
- "E_tri": 0.0,
- "beta": 0.0,
- "J_0": 0.0,
- "J_ex": 0.0,
- "g1_frame": [0, 0, 0],
- "g2_frame": [0, 0, 0],
- "g_tri_frame": [0, 0, 0],
- "g_frame": [0, 0, 0],
- "A1_frame": [0, 0, 0],
- "A2_frame": [0, 0, 0],
- "A3_frame": [0, 0, 0],
- "A4_frame": [0, 0, 0],
- "A5_frame": [0, 0, 0],
- "D_frame": [0, 0, 0],
- "D_tri_frame": [0, 0, 0],
- "width_gauss": 0.5,
- "T_relax_1": 0.0,
- "T_relax_2": 0.0,
- "T_pm": 0.0,
- "population": [0.0, 0.0, 0.0],
- "freq_mw": 0.0,
- "amplitude": 0,
- }
- }
-
- return default_profile
-
-
-
-
-[docs]
-def new_save_profile() -> dict:
- """
- Get a default save profile.
-
- Returns
- -------
- default_profile: dict
- Dictionary with default settings for saving.
-
- """
- default_profile = {"main": {}}
- # TODO Profil verwenden um Projektordner festzulegen -> Speicherort für
- # Bilder, Simulations/Optimierungsergebnisse, out-Files etc.
-
- return default_profile
-
-
-
-
-[docs]
-def new_optimization_profile() -> dict:
- """
- Get a default optimization profile.
-
- Returns
- -------
- default_profile: dict
- Dictionary with default settings for optimization routines.
-
- """
- default_profile = {
- "main": {"routine": "", "method": None, "cpu_cores": 0},
- "genetic": {
- "GAVaPS": True,
- "representation": "",
- "lifetime_mode": "",
- "crossover_type": "",
- "mutation_type": "",
- "min_lifetime": 0,
- "max_lifetime": 10,
- "reproduction_ratio": 0.2,
- "p_c": 0.5,
- "p_m": 0.001,
- "pop_size": 500,
- "min_pop_size": 1,
- "max_pop_size": 100,
- "convergence": 0.02,
- "peak_prominence": 0.5,
- "error_weight": [1.0, 0.2],
- "max_generation": 1000,
- "show_status": True,
- },
- "minimize": {"maxiter": ""},
- "dual_annealing": {
- "maxiter": None,
- "maxiter_minimizer": None,
- "initial_temp": None,
- "restart_temp_ratio": None,
- "visit": None,
- "accept": None,
- "maxfun": None,
- "no_local_search": None,
- },
- "shgo": {
- "maxiter": None,
- "maxiter_minimizer": None,
- "n": None,
- "iters": None,
- "maxfev": None,
- "ftol": None,
- "maxev": None,
- "maxtime": None,
- "minimize_every_iter": None,
- "local_iter": None,
- "sampling_method": None,
- },
- "differential_evolution": {
- "strategy": None,
- "maxiter": None,
- "popsize": None,
- "tol": None,
- "mutation": None,
- "recombination": None,
- "seed": None,
- "atol": None,
- "polish": None,
- "init": None,
- "disp": None,
- "updating": None,
- },
- "basinhopping": {
- "T": None,
- "stepsize": None,
- "disp": None,
- "stepwise_factor": None,
- "maxiter_minimizer": None,
- "seed": None,
- "interval": None,
- "niter_success": None,
- "niter": None,
- "target_accept_rate": None,
- },
- "least_squares": {
- "ftol": None,
- "xtol": None,
- "gtol": None,
- "loss": None,
- "f_scale": None,
- "max_nfev": None,
- "tr_solver": None,
- "verbose": None,
- },
- }
-
- return default_profile
-
-
-
-
-[docs]
-def new_simulation_profile() -> dict:
- """
- Get an empty simulation profile.
-
- Returns
- -------
- default_profile: dict
- Dictionary with default settings for simulation profiles.
-
- """
- default_profile = {
- "main": {"routine": "", "cpu_cores": 0},
- "static_radpair": {"grid_points": 500},
- "teacups": {
- "grid_points": 500,
- "space": "hilbert",
- },
- "opossum": {},
- "didelphis_tikhonov": {
- "min_r": 10,
- "max_r": 50,
- "r_points": 401,
- "force_cpu": False,
- "regularization_mode": 2,
- },
- "didelphis": {"min_r": 10, "max_r": 50, "r_points": 401},
- }
-
- return default_profile
-
-
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-© M. Sc. Florian Quintes, 2021-2022
-
-@contact: florian.quintes@pc.uni.freiburg.de
-
-@author: Florian Quintes
-"""
-
-import numpy as np
-import os
-
-
-
-[docs]
-def save_plot(fname: str, *figures: object, path: str = None, **kwargs: dict) -> None:
- """
- Save the figures plotted with matplotlib.
-
- Parameters
- ----------
- fname : str
- Filename for the figure(s). If multiple figures are given, '_[number]'
- will be append to the filename.
- *figures : object
- Matplotlib figure object(s).
- path : str, optional
- Absolute path where the figures will be stored. The default is
- '~/Results/Plots/'.
- **kwargs : dict
- Other keyword arguments. Will be passed to plt.savefig(). See
- matplotlib documentation for further informations.
-
- Returns
- -------
- None
- Nothing will be returned.
-
- """
- # [SETUP]
- if fname[-4] == ".":
- fmt = fname[-4:]
- fname = fname[:-4]
- elif fname[-5] == ".":
- fmt = fname[-5:]
- fname = fname[:-5]
- else:
- fmt = ""
-
- if type(figures[0]) == list:
- figures = tuple(*figures)
-
- if len(figures) == 1:
- multi_plot = False
- else:
- multi_plot = True
-
- if path is None:
- save_path = os.path.join(os.path.expanduser("~"), "Results", "Plots")
- else:
- save_path = path
-
- if not os.path.exists(save_path):
- os.makedirs(save_path)
-
- # [SAVE]
- for n, fig in enumerate(figures):
- if multi_plot:
- save_name = os.path.join(save_path, fname + "_" + str(n + 1) + fmt)
- else:
- save_name = os.path.join(save_path, fname + fmt)
-
- if "format" in kwargs:
- save_name += "." + kwargs["format"]
-
- fig.savefig(save_name, **kwargs)
-
- return None
-
-
-
-
-[docs]
-def save_simulation(name: str, *data: "np.array", path: str = None) -> None:
- """
- Save the simulated data at '[path]/[name]/[files]' using np.savetxt.
-
- Parameters
- ----------
- name : str
- Foldername for the dataset.
- *data : np.array
- Arrays with the simulated data. Must be 2d or 3d. If 2d: x_axis, int;
- if 3d: x_axis, y_axis, int.
- path : str, optional
- Full path where the data will be stored. The default is
- '~/Results/Simulated Data/[name]'.
-
- Raises
- ------
- ValueError
- Will be raised, if dimension of the data isn't 2d or 3d.
-
- Returns
- -------
- None
- Nothing will be returned.
-
- """
- # [SETUP]
- if name.lower().endswith(".txt"):
- name = name[:-4]
- if path is None:
- save_path = os.path.join(os.path.expanduser("~"), "Results", "Simulated Data")
- else:
- save_path = path
-
- if not os.path.exists(save_path):
- os.makedirs(save_path)
-
- save_folder = os.path.join(save_path, name)
- if not os.path.exists(save_folder):
- os.makedirs(save_folder)
-
- if len(data) == 2:
- x, intensity = data
- elif len(data) == 3:
- x, y, intensity = data
- else:
- raise ValueError("Can't handle {0}D Data. Need 2D or 3D.".format(len(data)))
-
- # [SAVE]
-
- np.savetxt(os.path.join(save_folder, "x_axis.txt"), x)
- np.savetxt(os.path.join(save_folder, "intensity.txt"), intensity)
-
- if len(data) == 3:
- np.savetxt(os.path.join(save_folder, "y_axis.txt"), y)
-
- return None
-
-
-
-
-[docs]
-def write_out_file(
- Sys: object,
- Exp: object,
- SimOpt: object,
- *FitOpt: object,
- current_best: bool = False,
-) -> None:
- """
- Write an output file with all datas from Sys, Exp, SimOpt and, if running
- in optimization mode, FitOpt.
-
- Parameters
- ----------
- Sys : object
- Spinsystem object of module 'epr_setup'.
- Exp : object
- Experimental object of module 'epr_setup'.
- SimOpt : object
- SimulationOptions object of module 'epr_setup'.
- *FitOpt : object
- FittingOptions object of module 'epr_setup'.
- current_best : bool, optional
- True if the given Sys and Exp are the current best while running in
- optimization mode. False if Sys and Exp are the final result / are the
- given Sys and Exp in normal simulation mode. The default is False.
-
- Returns
- -------
- None
- Nothing will be returned.
-
- """
-
- if current_best:
- title_part = "_current_best"
- else:
- title_part = "_result"
-
- # TODO Pfad überlegen um Ergebnisse zu speichern?
- if len(FitOpt) == 1:
- fit_mode = True
- out = open(FitOpt[0].routine + title_part + ".eps_out", "w")
- else:
- fit_mode = False
- out = open(SimOpt.routine + title_part + ".eps_out", "w")
-
- out.write("{:#^50s}\n".format(""))
-
- if current_best:
- out.write("{:#^50s}\n".format(" CURRENT BEST "))
- else:
- out.write("{:#^50s}\n".format(" OUTPUT-FILE "))
-
- if fit_mode:
- out.write("{:#^50s}\n".format(" " + FitOpt[0].routine + " "))
- else:
- out.write("{:#^50s}\n".format(" " + SimOpt.routine + " "))
-
- out.write("{:#^50s}\n".format(" AUTHOR: FLORIAN QUINTES "))
- out.write("{:#^50s}\n".format(""))
- out.write("\n")
-
- out.write("\n")
- out.write("\n")
- out.write("{:#^50s}\n".format(" SPINSYSTEM "))
- out.write("\n")
- out.write("\n")
-
- out.write("{: ^15s} {: ^20s}\n".format("g-Tensor", "Value"))
- np.set_printoptions(formatter={"float": "{:0.7f}".format})
-
- for key in vars(Sys):
- if key in ("g1", "g2"):
- out.write("{0:>14s}: {1}\n".format(key, vars(Sys)[key]))
-
- out.write("\n")
- out.write("{: ^15s} {: ^20s}\n".format("A-Tensor", "Value / MHz"))
- np.set_printoptions(formatter={"float": "{:2.2f}".format})
-
- for key in vars(Sys):
- if key.startswith("A") and len(key) < 3:
- out.write("{0:>14s}: {1}\n".format(key, vars(Sys)[key]))
-
- out.write("\n")
- out.write("{: ^15s} {: ^20s}\n".format("ZFS", "Value / MHz"))
-
- for key in vars(Sys):
- if key in ("D", "E", "J_ex"):
- out.write("{0:>14s}: {1}\n".format(key, vars(Sys)[key]))
-
- out.write("\n")
- out.write("{: ^15s} {: ^20s}\n".format("Orientations", "Angle / rad"))
- np.set_printoptions(formatter={"float": "{: 0.1f}".format})
-
- for key in vars(Sys):
- if key.endswith("_frame"):
- out.write("{0:>14s}: {1}\n".format(key, vars(Sys)[key]))
-
- out.write("\n")
- out.write("{: ^15s}|{: ^20s}\n".format("Nuclear spin", "Number of cores"))
-
- spins = []
- cores = []
- for key in vars(Sys):
- if (key.startswith("n") or key.startswith("I")) and len(key) < 3:
- if key.startswith("n"):
- cores.append(key)
- else:
- spins.append(key)
- cores.sort()
- spins.sort()
-
- atoms = zip(cores, spins)
- for core, spin in atoms:
- out.write(
- "{0:>7s}: {1:<6.1f}|{2:>9s}: {3:<9}\n".format(
- core, vars(Sys)[core], spin, vars(Sys)[spin]
- )
- )
-
- out.write("\n")
- out.write("{:>14s}: {:.2f} mT\n".format("width_gauss", Sys.width_gauss))
-
- out.write("\n")
- out.write("\n")
- out.write("{:#^50s}\n".format(" EXPERIMENTAL "))
- out.write("\n")
- out.write("\n")
-
- out.write("{0:>20s}: {1:<9.6f} mT\n".format("microwave amplitude", Exp.B_mw))
- out.write(
- "{0:>20s}: {1:<9.6f} GHz\n".format("microwave frequency", Exp.freq_mw / 1e9)
- )
- out.write("{0:>20s}: {1:<7.2f} mT\n".format("min B_z", Exp.B_z.min()))
- out.write("{0:>20s}: {1:<7.2f} mT\n".format("max B_z", Exp.B_z.max()))
- out.write("{0:>20s}: {1}\n".format("B_z points", len(Exp.B_z)))
-
- out.write("\n")
- out.write("\n")
- out.write("{:#^50s}\n".format(" SIMULATION OPTIONS "))
- out.write("\n")
- out.write("\n")
-
- out.write("{0:>20s}: {1:<20s}\n".format("routine", SimOpt.routine))
- out.write("{0:>20s}: {1} cores\n".format("run at", SimOpt.cpu_cores))
- out.write("{0:>20s}: {1:<20}\n".format("grid points", SimOpt.grid_points))
- out.write("{0:>20s}: {1} space\n".format("using", SimOpt.space))
-
- if fit_mode:
- out.write("\n")
- out.write("\n")
- out.write("{:#^50s}\n".format(" FITTING OPTIONS "))
- out.write("\n")
- out.write("\n")
-
- for key in vars(FitOpt[0]):
- if key is not None:
- out.write(
- ("{0:>20s}: " + "{1:<20s}\n").format(key, str(vars(FitOpt[0])[key]))
- )
-
- out.close()
-
- return None
-
-Short
- */ - .o-tooltip--left { - position: relative; - } - - .o-tooltip--left:after { - opacity: 0; - visibility: hidden; - position: absolute; - content: attr(data-tooltip); - padding: .2em; - font-size: .8em; - left: -.2em; - background: grey; - color: white; - white-space: nowrap; - z-index: 2; - border-radius: 2px; - transform: translateX(-102%) translateY(0); - transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); -} - -.o-tooltip--left:hover:after { - display: block; - opacity: 1; - visibility: visible; - transform: translateX(-100%) translateY(0); - transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); - transition-delay: .5s; -} - -/* By default the copy button shouldn't show up when printing a page */ -@media print { - button.copybtn { - display: none; - } -} diff --git a/docs/build/html/_static/copybutton.js b/docs/build/html/_static/copybutton.js deleted file mode 100644 index 2ea7ff3..0000000 --- a/docs/build/html/_static/copybutton.js +++ /dev/null @@ -1,248 +0,0 @@ -// Localization support -const messages = { - 'en': { - 'copy': 'Copy', - 'copy_to_clipboard': 'Copy to clipboard', - 'copy_success': 'Copied!', - 'copy_failure': 'Failed to copy', - }, - 'es' : { - 'copy': 'Copiar', - 'copy_to_clipboard': 'Copiar al portapapeles', - 'copy_success': '¡Copiado!', - 'copy_failure': 'Error al copiar', - }, - 'de' : { - 'copy': 'Kopieren', - 'copy_to_clipboard': 'In die Zwischenablage kopieren', - 'copy_success': 'Kopiert!', - 'copy_failure': 'Fehler beim Kopieren', - }, - 'fr' : { - 'copy': 'Copier', - 'copy_to_clipboard': 'Copier dans le presse-papier', - 'copy_success': 'Copié !', - 'copy_failure': 'Échec de la copie', - }, - 'ru': { - 'copy': 'Скопировать', - 'copy_to_clipboard': 'Скопировать в буфер', - 'copy_success': 'Скопировано!', - 'copy_failure': 'Не удалось скопировать', - }, - 'zh-CN': { - 'copy': '复制', - 'copy_to_clipboard': '复制到剪贴板', - 'copy_success': '复制成功!', - 'copy_failure': '复制失败', - }, - 'it' : { - 'copy': 'Copiare', - 'copy_to_clipboard': 'Copiato negli appunti', - 'copy_success': 'Copiato!', - 'copy_failure': 'Errore durante la copia', - } -} - -let locale = 'en' -if( document.documentElement.lang !== undefined - && messages[document.documentElement.lang] !== undefined ) { - locale = document.documentElement.lang -} - -let doc_url_root = DOCUMENTATION_OPTIONS.URL_ROOT; -if (doc_url_root == '#') { - doc_url_root = ''; -} - -/** - * SVG files for our copy buttons - */ -let iconCheck = `` - -// If the user specified their own SVG use that, otherwise use the default -let iconCopy = ``; -if (!iconCopy) { - iconCopy = `` -} - -/** - * Set up copy/paste for code blocks - */ - -const runWhenDOMLoaded = cb => { - if (document.readyState != 'loading') { - cb() - } else if (document.addEventListener) { - document.addEventListener('DOMContentLoaded', cb) - } else { - document.attachEvent('onreadystatechange', function() { - if (document.readyState == 'complete') cb() - }) - } -} - -const codeCellId = index => `codecell${index}` - -// Clears selected text since ClipboardJS will select the text when copying -const clearSelection = () => { - if (window.getSelection) { - window.getSelection().removeAllRanges() - } else if (document.selection) { - document.selection.empty() - } -} - -// Changes tooltip text for a moment, then changes it back -// We want the timeout of our `success` class to be a bit shorter than the -// tooltip and icon change, so that we can hide the icon before changing back. -var timeoutIcon = 2000; -var timeoutSuccessClass = 1500; - -const temporarilyChangeTooltip = (el, oldText, newText) => { - el.setAttribute('data-tooltip', newText) - el.classList.add('success') - // Remove success a little bit sooner than we change the tooltip - // So that we can use CSS to hide the copybutton first - setTimeout(() => el.classList.remove('success'), timeoutSuccessClass) - setTimeout(() => el.setAttribute('data-tooltip', oldText), timeoutIcon) -} - -// Changes the copy button icon for two seconds, then changes it back -const temporarilyChangeIcon = (el) => { - el.innerHTML = iconCheck; - setTimeout(() => {el.innerHTML = iconCopy}, timeoutIcon) -} - -const addCopyButtonToCodeCells = () => { - // If ClipboardJS hasn't loaded, wait a bit and try again. This - // happens because we load ClipboardJS asynchronously. - if (window.ClipboardJS === undefined) { - setTimeout(addCopyButtonToCodeCells, 250) - return - } - - // Add copybuttons to all of our code cells - const COPYBUTTON_SELECTOR = 'div.highlight pre'; - const codeCells = document.querySelectorAll(COPYBUTTON_SELECTOR) - codeCells.forEach((codeCell, index) => { - const id = codeCellId(index) - codeCell.setAttribute('id', id) - - const clipboardButton = id => - `` - codeCell.insertAdjacentHTML('afterend', clipboardButton(id)) - }) - -function escapeRegExp(string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string -} - -/** - * Removes excluded text from a Node. - * - * @param {Node} target Node to filter. - * @param {string} exclude CSS selector of nodes to exclude. - * @returns {DOMString} Text from `target` with text removed. - */ -function filterText(target, exclude) { - const clone = target.cloneNode(true); // clone as to not modify the live DOM - if (exclude) { - // remove excluded nodes - clone.querySelectorAll(exclude).forEach(node => node.remove()); - } - return clone.innerText; -} - -// Callback when a copy button is clicked. Will be passed the node that was clicked -// should then grab the text and replace pieces of text that shouldn't be used in output -function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { - var regexp; - var match; - - // Do we check for line continuation characters and "HERE-documents"? - var useLineCont = !!lineContinuationChar - var useHereDoc = !!hereDocDelim - - // create regexp to capture prompt and remaining line - if (isRegexp) { - regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') - } else { - regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') - } - - const outputLines = []; - var promptFound = false; - var gotLineCont = false; - var gotHereDoc = false; - const lineGotPrompt = []; - for (const line of textContent.split('\n')) { - match = line.match(regexp) - if (match || gotLineCont || gotHereDoc) { - promptFound = regexp.test(line) - lineGotPrompt.push(promptFound) - if (removePrompts && promptFound) { - outputLines.push(match[2]) - } else { - outputLines.push(line) - } - gotLineCont = line.endsWith(lineContinuationChar) & useLineCont - if (line.includes(hereDocDelim) & useHereDoc) - gotHereDoc = !gotHereDoc - } else if (!onlyCopyPromptLines) { - outputLines.push(line) - } else if (copyEmptyLines && line.trim() === '') { - outputLines.push(line) - } - } - - // If no lines with the prompt were found then just use original lines - if (lineGotPrompt.some(v => v === true)) { - textContent = outputLines.join('\n'); - } - - // Remove a trailing newline to avoid auto-running when pasting - if (textContent.endsWith("\n")) { - textContent = textContent.slice(0, -1) - } - return textContent -} - - -var copyTargetText = (trigger) => { - var target = document.querySelector(trigger.attributes['data-clipboard-target'].value); - - // get filtered text - let exclude = '.linenos'; - - let text = filterText(target, exclude); - return formatCopyText(text, '', false, true, true, true, '', '') -} - - // Initialize with a callback so we can modify the text before copy - const clipboard = new ClipboardJS('.copybtn', {text: copyTargetText}) - - // Update UI with error/success messages - clipboard.on('success', event => { - clearSelection() - temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_success']) - temporarilyChangeIcon(event.trigger) - }) - - clipboard.on('error', event => { - temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_failure']) - }) -} - -runWhenDOMLoaded(addCopyButtonToCodeCells) \ No newline at end of file diff --git a/docs/build/html/_static/copybutton_funcs.js b/docs/build/html/_static/copybutton_funcs.js deleted file mode 100644 index dbe1aaa..0000000 --- a/docs/build/html/_static/copybutton_funcs.js +++ /dev/null @@ -1,73 +0,0 @@ -function escapeRegExp(string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string -} - -/** - * Removes excluded text from a Node. - * - * @param {Node} target Node to filter. - * @param {string} exclude CSS selector of nodes to exclude. - * @returns {DOMString} Text from `target` with text removed. - */ -export function filterText(target, exclude) { - const clone = target.cloneNode(true); // clone as to not modify the live DOM - if (exclude) { - // remove excluded nodes - clone.querySelectorAll(exclude).forEach(node => node.remove()); - } - return clone.innerText; -} - -// Callback when a copy button is clicked. Will be passed the node that was clicked -// should then grab the text and replace pieces of text that shouldn't be used in output -export function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { - var regexp; - var match; - - // Do we check for line continuation characters and "HERE-documents"? - var useLineCont = !!lineContinuationChar - var useHereDoc = !!hereDocDelim - - // create regexp to capture prompt and remaining line - if (isRegexp) { - regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') - } else { - regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') - } - - const outputLines = []; - var promptFound = false; - var gotLineCont = false; - var gotHereDoc = false; - const lineGotPrompt = []; - for (const line of textContent.split('\n')) { - match = line.match(regexp) - if (match || gotLineCont || gotHereDoc) { - promptFound = regexp.test(line) - lineGotPrompt.push(promptFound) - if (removePrompts && promptFound) { - outputLines.push(match[2]) - } else { - outputLines.push(line) - } - gotLineCont = line.endsWith(lineContinuationChar) & useLineCont - if (line.includes(hereDocDelim) & useHereDoc) - gotHereDoc = !gotHereDoc - } else if (!onlyCopyPromptLines) { - outputLines.push(line) - } else if (copyEmptyLines && line.trim() === '') { - outputLines.push(line) - } - } - - // If no lines with the prompt were found then just use original lines - if (lineGotPrompt.some(v => v === true)) { - textContent = outputLines.join('\n'); - } - - // Remove a trailing newline to avoid auto-running when pasting - if (textContent.endsWith("\n")) { - textContent = textContent.slice(0, -1) - } - return textContent -} diff --git a/docs/build/html/_static/css/badge_only.css b/docs/build/html/_static/css/badge_only.css deleted file mode 100644 index 88ba55b..0000000 --- a/docs/build/html/_static/css/badge_only.css +++ /dev/null @@ -1 +0,0 @@ -.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions .rst-other-versions .rtd-current-item{font-weight:700}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}#flyout-search-form{padding:6px} \ No newline at end of file diff --git a/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff b/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff deleted file mode 100644 index 6cb6000..0000000 Binary files a/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 b/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 deleted file mode 100644 index 7059e23..0000000 Binary files a/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff b/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff deleted file mode 100644 index f815f63..0000000 Binary files a/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 b/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 deleted file mode 100644 index f2c76e5..0000000 Binary files a/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/fontawesome-webfont.eot b/docs/build/html/_static/css/fonts/fontawesome-webfont.eot deleted file mode 100644 index e9f60ca..0000000 Binary files a/docs/build/html/_static/css/fonts/fontawesome-webfont.eot and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/fontawesome-webfont.svg b/docs/build/html/_static/css/fonts/fontawesome-webfont.svg deleted file mode 100644 index 855c845..0000000 --- a/docs/build/html/_static/css/fonts/fontawesome-webfont.svg +++ /dev/null @@ -1,2671 +0,0 @@ - - - diff --git a/docs/build/html/_static/css/fonts/fontawesome-webfont.ttf b/docs/build/html/_static/css/fonts/fontawesome-webfont.ttf deleted file mode 100644 index 35acda2..0000000 Binary files a/docs/build/html/_static/css/fonts/fontawesome-webfont.ttf and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/fontawesome-webfont.woff b/docs/build/html/_static/css/fonts/fontawesome-webfont.woff deleted file mode 100644 index 400014a..0000000 Binary files a/docs/build/html/_static/css/fonts/fontawesome-webfont.woff and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/fontawesome-webfont.woff2 b/docs/build/html/_static/css/fonts/fontawesome-webfont.woff2 deleted file mode 100644 index 4d13fc6..0000000 Binary files a/docs/build/html/_static/css/fonts/fontawesome-webfont.woff2 and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/lato-bold-italic.woff b/docs/build/html/_static/css/fonts/lato-bold-italic.woff deleted file mode 100644 index 88ad05b..0000000 Binary files a/docs/build/html/_static/css/fonts/lato-bold-italic.woff and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/lato-bold-italic.woff2 b/docs/build/html/_static/css/fonts/lato-bold-italic.woff2 deleted file mode 100644 index c4e3d80..0000000 Binary files a/docs/build/html/_static/css/fonts/lato-bold-italic.woff2 and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/lato-bold.woff b/docs/build/html/_static/css/fonts/lato-bold.woff deleted file mode 100644 index c6dff51..0000000 Binary files a/docs/build/html/_static/css/fonts/lato-bold.woff and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/lato-bold.woff2 b/docs/build/html/_static/css/fonts/lato-bold.woff2 deleted file mode 100644 index bb19504..0000000 Binary files a/docs/build/html/_static/css/fonts/lato-bold.woff2 and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/lato-normal-italic.woff b/docs/build/html/_static/css/fonts/lato-normal-italic.woff deleted file mode 100644 index 76114bc..0000000 Binary files a/docs/build/html/_static/css/fonts/lato-normal-italic.woff and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/lato-normal-italic.woff2 b/docs/build/html/_static/css/fonts/lato-normal-italic.woff2 deleted file mode 100644 index 3404f37..0000000 Binary files a/docs/build/html/_static/css/fonts/lato-normal-italic.woff2 and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/lato-normal.woff b/docs/build/html/_static/css/fonts/lato-normal.woff deleted file mode 100644 index ae1307f..0000000 Binary files a/docs/build/html/_static/css/fonts/lato-normal.woff and /dev/null differ diff --git a/docs/build/html/_static/css/fonts/lato-normal.woff2 b/docs/build/html/_static/css/fonts/lato-normal.woff2 deleted file mode 100644 index 3bf9843..0000000 Binary files a/docs/build/html/_static/css/fonts/lato-normal.woff2 and /dev/null differ diff --git a/docs/build/html/_static/css/theme.css b/docs/build/html/_static/css/theme.css deleted file mode 100644 index a88467c..0000000 --- a/docs/build/html/_static/css/theme.css +++ /dev/null @@ -1,4 +0,0 @@ -html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden],audio:not([controls]){display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;text-decoration:none}ins,mark{color:#000}mark{background:#ff0;font-style:italic;font-weight:700}.rst-content code,.rst-content tt,code,kbd,pre,samp{font-family:monospace,serif;_font-family:courier new,monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:after,q:before{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}dl,ol,ul{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure,form{margin:0}label{cursor:pointer}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}textarea{resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none!important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{body,html,section{background:none!important}*{box-shadow:none!important;text-shadow:none!important;filter:none!important;-ms-filter:none!important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}.rst-content .toctree-wrapper>p.caption,h2,h3,p{orphans:3;widows:3}.rst-content .toctree-wrapper>p.caption,h2,h3{page-break-after:avoid}}.btn,.fa:before,.icon:before,.rst-content .admonition,.rst-content .admonition-title:before,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .code-block-caption .headerlink:before,.rst-content .danger,.rst-content .eqno .headerlink:before,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-alert,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before,input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}/*! - * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713);src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix&v=4.7.0) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa-pull-left.icon,.fa.fa-pull-left,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .eqno .fa-pull-left.headerlink,.rst-content .fa-pull-left.admonition-title,.rst-content code.download span.fa-pull-left:first-child,.rst-content dl dt .fa-pull-left.headerlink,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content p .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.wy-menu-vertical li.current>a button.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-left.toctree-expand,.wy-menu-vertical li button.fa-pull-left.toctree-expand{margin-right:.3em}.fa-pull-right.icon,.fa.fa-pull-right,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .eqno .fa-pull-right.headerlink,.rst-content .fa-pull-right.admonition-title,.rst-content code.download span.fa-pull-right:first-child,.rst-content dl dt .fa-pull-right.headerlink,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content p .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.wy-menu-vertical li.current>a button.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-right.toctree-expand,.wy-menu-vertical li button.fa-pull-right.toctree-expand{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pull-left.icon,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .eqno .pull-left.headerlink,.rst-content .pull-left.admonition-title,.rst-content code.download span.pull-left:first-child,.rst-content dl dt .pull-left.headerlink,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content p .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.wy-menu-vertical li.current>a button.pull-left.toctree-expand,.wy-menu-vertical li.on a button.pull-left.toctree-expand,.wy-menu-vertical li button.pull-left.toctree-expand{margin-right:.3em}.fa.pull-right,.pull-right.icon,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .eqno .pull-right.headerlink,.rst-content .pull-right.admonition-title,.rst-content code.download span.pull-right:first-child,.rst-content dl dt .pull-right.headerlink,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content p .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.wy-menu-vertical li.current>a button.pull-right.toctree-expand,.wy-menu-vertical li.on a button.pull-right.toctree-expand,.wy-menu-vertical li button.pull-right.toctree-expand{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-close:before,.fa-remove:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-cog:before,.fa-gear:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-repeat:before,.fa-rotate-right:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.rst-content .admonition-title:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-exclamation-triangle:before,.fa-warning:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-cogs:before,.fa-gears:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-floppy-o:before,.fa-save:before{content:""}.fa-square:before{content:""}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.icon-caret-down:before,.wy-dropdown .caret:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-sort:before,.fa-unsorted:before{content:""}.fa-sort-desc:before,.fa-sort-down:before{content:""}.fa-sort-asc:before,.fa-sort-up:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-gavel:before,.fa-legal:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-bolt:before,.fa-flash:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-clipboard:before,.fa-paste:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-chain-broken:before,.fa-unlink:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:""}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:""}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:""}.fa-eur:before,.fa-euro:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-inr:before,.fa-rupee:before{content:""}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:""}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:""}.fa-krw:before,.fa-won:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-try:before,.fa-turkish-lira:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li button.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-bank:before,.fa-institution:before,.fa-university:before{content:""}.fa-graduation-cap:before,.fa-mortar-board:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:""}.fa-file-archive-o:before,.fa-file-zip-o:before{content:""}.fa-file-audio-o:before,.fa-file-sound-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:""}.fa-empire:before,.fa-ge:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-paper-plane:before,.fa-send:before{content:""}.fa-paper-plane-o:before,.fa-send-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-bed:before,.fa-hotel:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-y-combinator:before,.fa-yc:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-television:before,.fa-tv:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:""}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-sign-language:before,.fa-signing:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-address-card:before,.fa-vcard:before{content:""}.fa-address-card-o:before,.fa-vcard-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{font-family:inherit}.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content .eqno .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before{font-family:FontAwesome;display:inline-block;font-style:normal;font-weight:400;line-height:1;text-decoration:inherit}.rst-content .code-block-caption a .headerlink,.rst-content .eqno a .headerlink,.rst-content a .admonition-title,.rst-content code.download a span:first-child,.rst-content dl dt a .headerlink,.rst-content h1 a .headerlink,.rst-content h2 a .headerlink,.rst-content h3 a .headerlink,.rst-content h4 a .headerlink,.rst-content h5 a .headerlink,.rst-content h6 a .headerlink,.rst-content p.caption a .headerlink,.rst-content p a .headerlink,.rst-content table>caption a .headerlink,.rst-content tt.download a span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li a button.toctree-expand,a .fa,a .icon,a .rst-content .admonition-title,a .rst-content .code-block-caption .headerlink,a .rst-content .eqno .headerlink,a .rst-content code.download span:first-child,a .rst-content dl dt .headerlink,a .rst-content h1 .headerlink,a .rst-content h2 .headerlink,a .rst-content h3 .headerlink,a .rst-content h4 .headerlink,a .rst-content h5 .headerlink,a .rst-content h6 .headerlink,a .rst-content p.caption .headerlink,a .rst-content p .headerlink,a .rst-content table>caption .headerlink,a .rst-content tt.download span:first-child,a .wy-menu-vertical li button.toctree-expand{display:inline-block;text-decoration:inherit}.btn .fa,.btn .icon,.btn .rst-content .admonition-title,.btn .rst-content .code-block-caption .headerlink,.btn .rst-content .eqno .headerlink,.btn .rst-content code.download span:first-child,.btn .rst-content dl dt .headerlink,.btn .rst-content h1 .headerlink,.btn .rst-content h2 .headerlink,.btn .rst-content h3 .headerlink,.btn .rst-content h4 .headerlink,.btn .rst-content h5 .headerlink,.btn .rst-content h6 .headerlink,.btn .rst-content p .headerlink,.btn .rst-content table>caption .headerlink,.btn .rst-content tt.download span:first-child,.btn .wy-menu-vertical li.current>a button.toctree-expand,.btn .wy-menu-vertical li.on a button.toctree-expand,.btn .wy-menu-vertical li button.toctree-expand,.nav .fa,.nav .icon,.nav .rst-content .admonition-title,.nav .rst-content .code-block-caption .headerlink,.nav .rst-content .eqno .headerlink,.nav .rst-content code.download span:first-child,.nav .rst-content dl dt .headerlink,.nav .rst-content h1 .headerlink,.nav .rst-content h2 .headerlink,.nav .rst-content h3 .headerlink,.nav .rst-content h4 .headerlink,.nav .rst-content h5 .headerlink,.nav .rst-content h6 .headerlink,.nav .rst-content p .headerlink,.nav .rst-content table>caption .headerlink,.nav .rst-content tt.download span:first-child,.nav .wy-menu-vertical li.current>a button.toctree-expand,.nav .wy-menu-vertical li.on a button.toctree-expand,.nav .wy-menu-vertical li button.toctree-expand,.rst-content .btn .admonition-title,.rst-content .code-block-caption .btn .headerlink,.rst-content .code-block-caption .nav .headerlink,.rst-content .eqno .btn .headerlink,.rst-content .eqno .nav .headerlink,.rst-content .nav .admonition-title,.rst-content code.download .btn span:first-child,.rst-content code.download .nav span:first-child,.rst-content dl dt .btn .headerlink,.rst-content dl dt .nav .headerlink,.rst-content h1 .btn .headerlink,.rst-content h1 .nav .headerlink,.rst-content h2 .btn .headerlink,.rst-content h2 .nav .headerlink,.rst-content h3 .btn .headerlink,.rst-content h3 .nav .headerlink,.rst-content h4 .btn .headerlink,.rst-content h4 .nav .headerlink,.rst-content h5 .btn .headerlink,.rst-content h5 .nav .headerlink,.rst-content h6 .btn .headerlink,.rst-content h6 .nav .headerlink,.rst-content p .btn .headerlink,.rst-content p .nav .headerlink,.rst-content table>caption .btn .headerlink,.rst-content table>caption .nav .headerlink,.rst-content tt.download .btn span:first-child,.rst-content tt.download .nav span:first-child,.wy-menu-vertical li .btn button.toctree-expand,.wy-menu-vertical li.current>a .btn button.toctree-expand,.wy-menu-vertical li.current>a .nav button.toctree-expand,.wy-menu-vertical li .nav button.toctree-expand,.wy-menu-vertical li.on a .btn button.toctree-expand,.wy-menu-vertical li.on a .nav button.toctree-expand{display:inline}.btn .fa-large.icon,.btn .fa.fa-large,.btn .rst-content .code-block-caption .fa-large.headerlink,.btn .rst-content .eqno .fa-large.headerlink,.btn .rst-content .fa-large.admonition-title,.btn .rst-content code.download span.fa-large:first-child,.btn .rst-content dl dt .fa-large.headerlink,.btn .rst-content h1 .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.btn .rst-content p .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.btn .wy-menu-vertical li button.fa-large.toctree-expand,.nav .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .code-block-caption .fa-large.headerlink,.nav .rst-content .eqno .fa-large.headerlink,.nav .rst-content .fa-large.admonition-title,.nav .rst-content code.download span.fa-large:first-child,.nav .rst-content dl dt .fa-large.headerlink,.nav .rst-content h1 .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.nav .rst-content p .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.nav .wy-menu-vertical li button.fa-large.toctree-expand,.rst-content .btn .fa-large.admonition-title,.rst-content .code-block-caption .btn .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.rst-content .eqno .btn .fa-large.headerlink,.rst-content .eqno .nav .fa-large.headerlink,.rst-content .nav .fa-large.admonition-title,.rst-content code.download .btn span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.rst-content dl dt .btn .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.rst-content p .btn .fa-large.headerlink,.rst-content p .nav .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.rst-content tt.download .btn span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.wy-menu-vertical li .btn button.fa-large.toctree-expand,.wy-menu-vertical li .nav button.fa-large.toctree-expand{line-height:.9em}.btn .fa-spin.icon,.btn .fa.fa-spin,.btn .rst-content .code-block-caption .fa-spin.headerlink,.btn .rst-content .eqno .fa-spin.headerlink,.btn .rst-content .fa-spin.admonition-title,.btn .rst-content code.download span.fa-spin:first-child,.btn .rst-content dl dt .fa-spin.headerlink,.btn .rst-content h1 .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.btn .rst-content p .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.btn .wy-menu-vertical li button.fa-spin.toctree-expand,.nav .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .code-block-caption .fa-spin.headerlink,.nav .rst-content .eqno .fa-spin.headerlink,.nav .rst-content .fa-spin.admonition-title,.nav .rst-content code.download span.fa-spin:first-child,.nav .rst-content dl dt .fa-spin.headerlink,.nav .rst-content h1 .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.nav .rst-content p .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.nav .wy-menu-vertical li button.fa-spin.toctree-expand,.rst-content .btn .fa-spin.admonition-title,.rst-content .code-block-caption .btn .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.rst-content .eqno .btn .fa-spin.headerlink,.rst-content .eqno .nav .fa-spin.headerlink,.rst-content .nav .fa-spin.admonition-title,.rst-content code.download .btn span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.rst-content dl dt .btn .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.rst-content p .btn .fa-spin.headerlink,.rst-content p .nav .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.rst-content tt.download .btn span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.wy-menu-vertical li .btn button.fa-spin.toctree-expand,.wy-menu-vertical li .nav button.fa-spin.toctree-expand{display:inline-block}.btn.fa:before,.btn.icon:before,.rst-content .btn.admonition-title:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content .eqno .btn.headerlink:before,.rst-content code.download span.btn:first-child:before,.rst-content dl dt .btn.headerlink:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content p .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.wy-menu-vertical li button.btn.toctree-expand:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.btn.icon:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content .eqno .btn.headerlink:hover:before,.rst-content code.download span.btn:first-child:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content p .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.wy-menu-vertical li button.btn.toctree-expand:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .icon:before,.btn-mini .rst-content .admonition-title:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.btn-mini .rst-content .eqno .headerlink:before,.btn-mini .rst-content code.download span:first-child:before,.btn-mini .rst-content dl dt .headerlink:before,.btn-mini .rst-content h1 .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.btn-mini .rst-content p .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.btn-mini .wy-menu-vertical li button.toctree-expand:before,.rst-content .btn-mini .admonition-title:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.rst-content .eqno .btn-mini .headerlink:before,.rst-content code.download .btn-mini span:first-child:before,.rst-content dl dt .btn-mini .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.rst-content p .btn-mini .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.rst-content tt.download .btn-mini span:first-child:before,.wy-menu-vertical li .btn-mini button.toctree-expand:before{font-size:14px;vertical-align:-15%}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.wy-alert{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.rst-content .admonition-title,.wy-alert-title{font-weight:700;display:block;color:#fff;background:#6ab0de;padding:6px 12px;margin:-12px -12px 12px}.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.admonition,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.wy-alert.wy-alert-danger{background:#fdf3f2}.rst-content .danger .admonition-title,.rst-content .danger .wy-alert-title,.rst-content .error .admonition-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .admonition-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.wy-alert.wy-alert-danger .wy-alert-title{background:#f29f97}.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .warning,.rst-content .wy-alert-warning.admonition,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.note,.rst-content .wy-alert-warning.seealso,.rst-content .wy-alert-warning.tip,.wy-alert.wy-alert-warning{background:#ffedcc}.rst-content .admonition-todo .admonition-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .attention .admonition-title,.rst-content .attention .wy-alert-title,.rst-content .caution .admonition-title,.rst-content .caution .wy-alert-title,.rst-content .warning .admonition-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.admonition .admonition-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.wy-alert.wy-alert-warning .wy-alert-title{background:#f0b37e}.rst-content .note,.rst-content .seealso,.rst-content .wy-alert-info.admonition,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.wy-alert.wy-alert-info{background:#e7f2fa}.rst-content .note .admonition-title,.rst-content .note .wy-alert-title,.rst-content .seealso .admonition-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .admonition-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.wy-alert.wy-alert-info .wy-alert-title{background:#6ab0de}.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.admonition,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.warning,.wy-alert.wy-alert-success{background:#dbfaf4}.rst-content .hint .admonition-title,.rst-content .hint .wy-alert-title,.rst-content .important .admonition-title,.rst-content .important .wy-alert-title,.rst-content .tip .admonition-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .admonition-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.wy-alert.wy-alert-success .wy-alert-title{background:#1abc9c}.rst-content .wy-alert-neutral.admonition,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.wy-alert.wy-alert-neutral{background:#f3f6f6}.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .admonition-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.wy-alert.wy-alert-neutral .wy-alert-title{color:#404040;background:#e1e4e5}.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.wy-alert.wy-alert-neutral a{color:#2980b9}.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .note p:last-child,.rst-content .seealso p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.wy-alert p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width:768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px;color:#fff;border:1px solid rgba(0,0,0,.1);background-color:#27ae60;text-decoration:none;font-weight:400;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:inset 0 -1px 0 0 rgba(0,0,0,.05),inset 0 2px 0 0 rgba(0,0,0,.1);padding:8px 12px 6px}.btn:visited{color:#fff}.btn-disabled,.btn-disabled:active,.btn-disabled:focus,.btn-disabled:hover,.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9!important}.btn-info:hover{background-color:#2e8ece!important}.btn-neutral{background-color:#f3f6f6!important;color:#404040!important}.btn-neutral:hover{background-color:#e5ebeb!important;color:#404040}.btn-neutral:visited{color:#404040!important}.btn-success{background-color:#27ae60!important}.btn-success:hover{background-color:#295!important}.btn-danger{background-color:#e74c3c!important}.btn-danger:hover{background-color:#ea6153!important}.btn-warning{background-color:#e67e22!important}.btn-warning:hover{background-color:#e98b39!important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f!important}.btn-link{background-color:transparent!important;color:#2980b9;box-shadow:none;border-color:transparent!important}.btn-link:active,.btn-link:hover{background-color:transparent!important;color:#409ad5!important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:after,.wy-btn-group:before{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:1px solid #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:1px solid #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=search]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned .wy-help-inline,.wy-form-aligned input,.wy-form-aligned label,.wy-form-aligned select,.wy-form-aligned textarea{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{margin:0}fieldset,legend{border:0;padding:0}legend{width:100%;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label,legend{display:block}label{margin:0 0 .3125em;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:after,.wy-control-group:before{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full input[type=color],.wy-control-group .wy-form-full input[type=date],.wy-control-group .wy-form-full input[type=datetime-local],.wy-control-group .wy-form-full input[type=datetime],.wy-control-group .wy-form-full input[type=email],.wy-control-group .wy-form-full input[type=month],.wy-control-group .wy-form-full input[type=number],.wy-control-group .wy-form-full input[type=password],.wy-control-group .wy-form-full input[type=search],.wy-control-group .wy-form-full input[type=tel],.wy-control-group .wy-form-full input[type=text],.wy-control-group .wy-form-full input[type=time],.wy-control-group .wy-form-full input[type=url],.wy-control-group .wy-form-full input[type=week],.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves input[type=color],.wy-control-group .wy-form-halves input[type=date],.wy-control-group .wy-form-halves input[type=datetime-local],.wy-control-group .wy-form-halves input[type=datetime],.wy-control-group .wy-form-halves input[type=email],.wy-control-group .wy-form-halves input[type=month],.wy-control-group .wy-form-halves input[type=number],.wy-control-group .wy-form-halves input[type=password],.wy-control-group .wy-form-halves input[type=search],.wy-control-group .wy-form-halves input[type=tel],.wy-control-group .wy-form-halves input[type=text],.wy-control-group .wy-form-halves input[type=time],.wy-control-group .wy-form-halves input[type=url],.wy-control-group .wy-form-halves input[type=week],.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds input[type=color],.wy-control-group .wy-form-thirds input[type=date],.wy-control-group .wy-form-thirds input[type=datetime-local],.wy-control-group .wy-form-thirds input[type=datetime],.wy-control-group .wy-form-thirds input[type=email],.wy-control-group .wy-form-thirds input[type=month],.wy-control-group .wy-form-thirds input[type=number],.wy-control-group .wy-form-thirds input[type=password],.wy-control-group .wy-form-thirds input[type=search],.wy-control-group .wy-form-thirds input[type=tel],.wy-control-group .wy-form-thirds input[type=text],.wy-control-group .wy-form-thirds input[type=time],.wy-control-group .wy-form-thirds input[type=url],.wy-control-group .wy-form-thirds input[type=week],.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full{float:left;display:block;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child,.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(odd){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child,.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control,.wy-control-no-input{margin:6px 0 0;font-size:90%}.wy-control-no-input{display:inline-block}.wy-control-group.fluid-input input[type=color],.wy-control-group.fluid-input input[type=date],.wy-control-group.fluid-input input[type=datetime-local],.wy-control-group.fluid-input input[type=datetime],.wy-control-group.fluid-input input[type=email],.wy-control-group.fluid-input input[type=month],.wy-control-group.fluid-input input[type=number],.wy-control-group.fluid-input input[type=password],.wy-control-group.fluid-input input[type=search],.wy-control-group.fluid-input input[type=tel],.wy-control-group.fluid-input input[type=text],.wy-control-group.fluid-input input[type=time],.wy-control-group.fluid-input input[type=url],.wy-control-group.fluid-input input[type=week]{width:100%}.wy-form-message-inline{padding-left:.3em;color:#666;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;*overflow:visible}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type=datetime-local]{padding:.34375em .625em}input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type=checkbox],input[type=radio],input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input[type=color]:focus,input[type=date]:focus,input[type=datetime-local]:focus,input[type=datetime]:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus{outline:0;outline:thin dotted\9;border-color:#333}input.no-focus:focus{border-color:#ccc!important}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=color][disabled],input[type=date][disabled],input[type=datetime-local][disabled],input[type=datetime][disabled],input[type=email][disabled],input[type=month][disabled],input[type=number][disabled],input[type=password][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=text][disabled],input[type=time][disabled],input[type=url][disabled],input[type=week][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,select:focus:invalid,textarea:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,select:focus:invalid:focus,textarea:focus:invalid:focus{border-color:#e74c3c}input[type=checkbox]:focus:invalid:focus,input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}input[readonly],select[disabled],select[readonly],textarea[disabled],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type=checkbox][disabled],input[type=radio][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:1px solid #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{left:0;top:0;width:36px;height:12px;background:#ccc}.wy-switch:after,.wy-switch:before{position:absolute;content:"";display:block;border-radius:4px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{width:18px;height:18px;background:#999;left:-3px;top:-3px}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27ae60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=color],.wy-control-group.wy-control-group-error input[type=date],.wy-control-group.wy-control-group-error input[type=datetime-local],.wy-control-group.wy-control-group-error input[type=datetime],.wy-control-group.wy-control-group-error input[type=email],.wy-control-group.wy-control-group-error input[type=month],.wy-control-group.wy-control-group-error input[type=number],.wy-control-group.wy-control-group-error input[type=password],.wy-control-group.wy-control-group-error input[type=search],.wy-control-group.wy-control-group-error input[type=tel],.wy-control-group.wy-control-group-error input[type=text],.wy-control-group.wy-control-group-error input[type=time],.wy-control-group.wy-control-group-error input[type=url],.wy-control-group.wy-control-group-error input[type=week],.wy-control-group.wy-control-group-error textarea{border:1px solid #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width:480px){.wy-form button[type=submit]{margin:.7em 0 0}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=text],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week],.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0}.wy-form-message,.wy-form-message-inline,.wy-form .wy-help-inline{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width:768px){.tablet-hide{display:none}}@media screen and (max-width:480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.rst-content table.docutils,.rst-content table.field-list,.wy-table{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.rst-content table.docutils caption,.rst-content table.field-list caption,.wy-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.rst-content table.docutils td,.rst-content table.docutils th,.rst-content table.field-list td,.rst-content table.field-list th,.wy-table td,.wy-table th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.rst-content table.docutils td:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list td:first-child,.rst-content table.field-list th:first-child,.wy-table td:first-child,.wy-table th:first-child{border-left-width:0}.rst-content table.docutils thead,.rst-content table.field-list thead,.wy-table thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.rst-content table.docutils thead th,.rst-content table.field-list thead th,.wy-table thead th{font-weight:700;border-bottom:2px solid #e1e4e5}.rst-content table.docutils td,.rst-content table.field-list td,.wy-table td{background-color:transparent;vertical-align:middle}.rst-content table.docutils td p,.rst-content table.field-list td p,.wy-table td p{line-height:18px}.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child,.wy-table td p:last-child{margin-bottom:0}.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min,.wy-table .wy-table-cell-min{width:1%;padding-right:0}.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:grey;font-size:90%}.wy-table-tertiary{color:grey;font-size:80%}.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,.wy-table-backed,.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td{background-color:#f3f6f6}.rst-content table.docutils,.wy-table-bordered-all{border:1px solid #e1e4e5}.rst-content table.docutils td,.wy-table-bordered-all td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.rst-content table.docutils tbody>tr:last-child td,.wy-table-bordered-all tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0!important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%}body,html{overflow-x:hidden}body{font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-weight:400;color:#404040;min-height:100%;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22!important}a.wy-text-warning:hover{color:#eb9950!important}.wy-text-info{color:#2980b9!important}a.wy-text-info:hover{color:#409ad5!important}.wy-text-success{color:#27ae60!important}a.wy-text-success:hover{color:#36d278!important}.wy-text-danger{color:#e74c3c!important}a.wy-text-danger:hover{color:#ed7669!important}.wy-text-neutral{color:#404040!important}a.wy-text-neutral:hover{color:#595959!important}.rst-content .toctree-wrapper>p.caption,h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif}p{line-height:24px;font-size:16px;margin:0 0 24px}h1{font-size:175%}.rst-content .toctree-wrapper>p.caption,h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}.rst-content code,.rst-content tt,code{white-space:nowrap;max-width:100%;background:#fff;border:1px solid #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#e74c3c;overflow-x:auto}.rst-content tt.code-large,code.code-large{font-size:90%}.rst-content .section ul,.rst-content .toctree-wrapper ul,.rst-content section ul,.wy-plain-list-disc,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.rst-content .section ul li,.rst-content .toctree-wrapper ul li,.rst-content section ul li,.wy-plain-list-disc li,article ul li{list-style:disc;margin-left:24px}.rst-content .section ul li p:last-child,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li p:last-child,.rst-content .toctree-wrapper ul li ul,.rst-content section ul li p:last-child,.rst-content section ul li ul,.wy-plain-list-disc li p:last-child,.wy-plain-list-disc li ul,article ul li p:last-child,article ul li ul{margin-bottom:0}.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,.rst-content section ul li li,.wy-plain-list-disc li li,article ul li li{list-style:circle}.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,.rst-content section ul li li li,.wy-plain-list-disc li li li,article ul li li li{list-style:square}.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,.rst-content section ul li ol li,.wy-plain-list-disc li ol li,article ul li ol li{list-style:decimal}.rst-content .section ol,.rst-content .section ol.arabic,.rst-content .toctree-wrapper ol,.rst-content .toctree-wrapper ol.arabic,.rst-content section ol,.rst-content section ol.arabic,.wy-plain-list-decimal,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.rst-content .section ol.arabic li,.rst-content .section ol li,.rst-content .toctree-wrapper ol.arabic li,.rst-content .toctree-wrapper ol li,.rst-content section ol.arabic li,.rst-content section ol li,.wy-plain-list-decimal li,article ol li{list-style:decimal;margin-left:24px}.rst-content .section ol.arabic li ul,.rst-content .section ol li p:last-child,.rst-content .section ol li ul,.rst-content .toctree-wrapper ol.arabic li ul,.rst-content .toctree-wrapper ol li p:last-child,.rst-content .toctree-wrapper ol li ul,.rst-content section ol.arabic li ul,.rst-content section ol li p:last-child,.rst-content section ol li ul,.wy-plain-list-decimal li p:last-child,.wy-plain-list-decimal li ul,article ol li p:last-child,article ol li ul{margin-bottom:0}.rst-content .section ol.arabic li ul li,.rst-content .section ol li ul li,.rst-content .toctree-wrapper ol.arabic li ul li,.rst-content .toctree-wrapper ol li ul li,.rst-content section ol.arabic li ul li,.rst-content section ol li ul li,.wy-plain-list-decimal li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:after,.wy-breadcrumbs:before{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs>li{display:inline-block;padding-top:5px}.wy-breadcrumbs>li.wy-breadcrumbs-aside{float:right}.rst-content .wy-breadcrumbs>li code,.rst-content .wy-breadcrumbs>li tt,.wy-breadcrumbs>li .rst-content tt,.wy-breadcrumbs>li code{all:inherit;color:inherit}.breadcrumb-item:before{content:"/";color:#bbb;font-size:13px;padding:0 6px 0 3px}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width:480px){.wy-breadcrumbs-extra,.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:after,.wy-menu-horiz:before{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz li,.wy-menu-horiz ul{display:inline-block}.wy-menu-horiz li:hover{background:hsla(0,0%,100%,.1)}.wy-menu-horiz li.divide-left{border-left:1px solid #404040}.wy-menu-horiz li.divide-right{border-right:1px solid #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#55a5d9;height:32px;line-height:32px;padding:0 1.618em;margin:12px 0 0;display:block;font-weight:700;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:1px solid #404040}.wy-menu-vertical li.divide-bottom{border-bottom:1px solid #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:grey;border-right:1px solid #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.rst-content .wy-menu-vertical li tt,.wy-menu-vertical li .rst-content tt,.wy-menu-vertical li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li button.toctree-expand{display:block;float:left;margin-left:-1.2em;line-height:18px;color:#4d4d4d;border:none;background:none;padding:0}.wy-menu-vertical li.current>a,.wy-menu-vertical li.on a{color:#404040;font-weight:700;position:relative;background:#fcfcfc;border:none;padding:.4045em 1.618em}.wy-menu-vertical li.current>a:hover,.wy-menu-vertical li.on a:hover{background:#fcfcfc}.wy-menu-vertical li.current>a:hover button.toctree-expand,.wy-menu-vertical li.on a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand{display:block;line-height:18px;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:1px solid #c9c9c9;border-top:1px solid #c9c9c9}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a,.wy-menu-vertical li.toctree-l5 a,.wy-menu-vertical li.toctree-l6 a,.wy-menu-vertical li.toctree-l7 a,.wy-menu-vertical li.toctree-l8 a,.wy-menu-vertical li.toctree-l9 a,.wy-menu-vertical li.toctree-l10 a{color:#404040}.wy-menu-vertical li.toctree-l2 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l3 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l4 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l5 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l6 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l7 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l8 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l9 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l10 a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{display:block}.wy-menu-vertical li.toctree-l2.current>a{padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{padding:.4045em 1.618em .4045em 4.045em}.wy-menu-vertical li.toctree-l3.current>a{padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{padding:.4045em 1.618em .4045em 5.663em}.wy-menu-vertical li.toctree-l4.current>a{padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a{padding:.4045em 1.618em .4045em 7.281em}.wy-menu-vertical li.toctree-l5.current>a{padding:.4045em 7.281em}.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a{padding:.4045em 1.618em .4045em 8.899em}.wy-menu-vertical li.toctree-l6.current>a{padding:.4045em 8.899em}.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a{padding:.4045em 1.618em .4045em 10.517em}.wy-menu-vertical li.toctree-l7.current>a{padding:.4045em 10.517em}.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a{padding:.4045em 1.618em .4045em 12.135em}.wy-menu-vertical li.toctree-l8.current>a{padding:.4045em 12.135em}.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a{padding:.4045em 1.618em .4045em 13.753em}.wy-menu-vertical li.toctree-l9.current>a{padding:.4045em 13.753em}.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a{padding:.4045em 1.618em .4045em 15.371em}.wy-menu-vertical li.toctree-l10.current>a{padding:.4045em 15.371em}.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{padding:.4045em 1.618em .4045em 16.989em}.wy-menu-vertical li.toctree-l2.current>a,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2 button.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3.current>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3 button.toctree-expand{color:#969696}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:400}.wy-menu-vertical a{line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover button.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-menu-vertical a:active button.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980b9;text-align:center;color:#fcfcfc}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search .wy-dropdown>a,.wy-side-nav-search>a{color:#fcfcfc;font-size:100%;font-weight:700;display:inline-block;padding:4px 6px;margin-bottom:.809em;max-width:100%}.wy-side-nav-search .wy-dropdown>a:hover,.wy-side-nav-search .wy-dropdown>aactive,.wy-side-nav-search .wy-dropdown>afocus,.wy-side-nav-search>a:hover,.wy-side-nav-search>aactive,.wy-side-nav-search>afocus{background:hsla(0,0%,100%,.1)}.wy-side-nav-search .wy-dropdown>a img.logo,.wy-side-nav-search>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search .wy-dropdown>a.icon,.wy-side-nav-search>a.icon{display:block}.wy-side-nav-search .wy-dropdown>a.icon img.logo,.wy-side-nav-search>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.switch-menus{position:relative;display:block;margin-top:-.4045em;margin-bottom:.809em;font-weight:400;color:hsla(0,0%,100%,.3)}.wy-side-nav-search>div.switch-menus>div.language-switch,.wy-side-nav-search>div.switch-menus>div.version-switch{display:inline-block;padding:.2em}.wy-side-nav-search>div.switch-menus>div.language-switch select,.wy-side-nav-search>div.switch-menus>div.version-switch select{display:inline-block;margin-right:-2rem;padding-right:2rem;max-width:240px;text-align-last:center;background:none;border:none;border-radius:0;box-shadow:none;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-size:1em;font-weight:400;color:hsla(0,0%,100%,.3);cursor:pointer;appearance:none;-webkit-appearance:none;-moz-appearance:none}.wy-side-nav-search>div.switch-menus>div.language-switch select:active,.wy-side-nav-search>div.switch-menus>div.language-switch select:focus,.wy-side-nav-search>div.switch-menus>div.language-switch select:hover,.wy-side-nav-search>div.switch-menus>div.version-switch select:active,.wy-side-nav-search>div.switch-menus>div.version-switch select:focus,.wy-side-nav-search>div.switch-menus>div.version-switch select:hover{background:hsla(0,0%,100%,.1);color:hsla(0,0%,100%,.5)}.wy-side-nav-search>div.switch-menus>div.language-switch select option,.wy-side-nav-search>div.switch-menus>div.version-switch select option{color:#000}.wy-side-nav-search>div.switch-menus>div.language-switch:has(>select):after,.wy-side-nav-search>div.switch-menus>div.version-switch:has(>select):after{display:inline-block;width:1.5em;height:100%;padding:.1em;content:"\f0d7";font-size:1em;line-height:1.2em;font-family:FontAwesome;text-align:center;pointer-events:none;box-sizing:border-box}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:after,.wy-nav-top:before{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:700}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:grey}footer p{margin-bottom:12px}.rst-content footer span.commit tt,footer span.commit .rst-content tt,footer span.commit code{padding:0;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:1em;background:none;border:none;color:grey}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:after,.rst-footer-buttons:before{width:100%;display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:after,.rst-breadcrumbs-buttons:before{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:1px solid #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:1px solid #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:grey;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width:768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-menu.wy-menu-vertical,.wy-side-nav-search,.wy-side-scroll{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width:1100px){.wy-nav-content-wrap{background:rgba(0,0,0,.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,.wy-nav-side,footer{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:after,.rst-versions .rst-current-version:before{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-content .eqno .rst-versions .rst-current-version .headerlink,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-content p .rst-versions .rst-current-version .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-versions .rst-current-version .rst-content .eqno .headerlink,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-versions .rst-current-version .rst-content p .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-versions .rst-current-version .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version button.toctree-expand{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions .rst-other-versions .rtd-current-item{font-weight:700}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}#flyout-search-form{padding:6px}.rst-content .toctree-wrapper>p.caption,.rst-content h1,.rst-content h2,.rst-content h3,.rst-content h4,.rst-content h5,.rst-content h6{margin-bottom:24px}.rst-content img{max-width:100%;height:auto}.rst-content div.figure,.rst-content figure{margin-bottom:24px}.rst-content div.figure .caption-text,.rst-content figure .caption-text{font-style:italic}.rst-content div.figure p:last-child.caption,.rst-content figure p:last-child.caption{margin-bottom:0}.rst-content div.figure.align-center,.rst-content figure.align-center{text-align:center}.rst-content .section>a>img,.rst-content .section>img,.rst-content section>a>img,.rst-content section>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"\f08e";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;display:block;overflow:auto}.rst-content div[class^=highlight],.rst-content pre.literal-block{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px}.rst-content div[class^=highlight] div[class^=highlight],.rst-content pre.literal-block div[class^=highlight]{padding:0;border:none;margin:0}.rst-content div[class^=highlight] td.code{width:100%}.rst-content .linenodiv pre{border-right:1px solid #e6e9ea;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^=highlight] pre{white-space:pre;margin:0;padding:12px;display:block;overflow:auto}.rst-content div[class^=highlight] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content .linenodiv pre,.rst-content div[class^=highlight] pre,.rst-content pre.literal-block{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight .gp,.rst-content div.highlight span.linenos{user-select:none;pointer-events:none}.rst-content div.highlight span.linenos{display:inline-block;padding-left:0;padding-right:12px;margin-right:12px;border-right:1px solid #e6e9ea}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^=highlight],.rst-content div[class^=highlight] pre{white-space:pre-wrap}}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning{clear:both}.rst-content .admonition-todo .last,.rst-content .admonition-todo>:last-child,.rst-content .admonition .last,.rst-content .admonition>:last-child,.rst-content .attention .last,.rst-content .attention>:last-child,.rst-content .caution .last,.rst-content .caution>:last-child,.rst-content .danger .last,.rst-content .danger>:last-child,.rst-content .error .last,.rst-content .error>:last-child,.rst-content .hint .last,.rst-content .hint>:last-child,.rst-content .important .last,.rst-content .important>:last-child,.rst-content .note .last,.rst-content .note>:last-child,.rst-content .seealso .last,.rst-content .seealso>:last-child,.rst-content .tip .last,.rst-content .tip>:last-child,.rst-content .warning .last,.rst-content .warning>:last-child{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent!important;border-color:rgba(0,0,0,.1)!important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li,.rst-content .toctree-wrapper ol.loweralpha,.rst-content .toctree-wrapper ol.loweralpha>li,.rst-content section ol.loweralpha,.rst-content section ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li,.rst-content .toctree-wrapper ol.upperalpha,.rst-content .toctree-wrapper ol.upperalpha>li,.rst-content section ol.upperalpha,.rst-content section ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*,.rst-content .toctree-wrapper ol li>*,.rst-content .toctree-wrapper ul li>*,.rst-content section ol li>*,.rst-content section ul li>*{margin-top:12px;margin-bottom:12px}.rst-content .section ol li>:first-child,.rst-content .section ul li>:first-child,.rst-content .toctree-wrapper ol li>:first-child,.rst-content .toctree-wrapper ul li>:first-child,.rst-content section ol li>:first-child,.rst-content section ul li>:first-child{margin-top:0}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child,.rst-content .toctree-wrapper ol li>p,.rst-content .toctree-wrapper ol li>p:last-child,.rst-content .toctree-wrapper ul li>p,.rst-content .toctree-wrapper ul li>p:last-child,.rst-content section ol li>p,.rst-content section ol li>p:last-child,.rst-content section ul li>p,.rst-content section ul li>p:last-child{margin-bottom:12px}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child,.rst-content .toctree-wrapper ol li>p:only-child,.rst-content .toctree-wrapper ol li>p:only-child:last-child,.rst-content .toctree-wrapper ul li>p:only-child,.rst-content .toctree-wrapper ul li>p:only-child:last-child,.rst-content section ol li>p:only-child,.rst-content section ol li>p:only-child:last-child,.rst-content section ul li>p:only-child,.rst-content section ul li>p:only-child:last-child{margin-bottom:0}.rst-content .section ol li>ol,.rst-content .section ol li>ul,.rst-content .section ul li>ol,.rst-content .section ul li>ul,.rst-content .toctree-wrapper ol li>ol,.rst-content .toctree-wrapper ol li>ul,.rst-content .toctree-wrapper ul li>ol,.rst-content .toctree-wrapper ul li>ul,.rst-content section ol li>ol,.rst-content section ol li>ul,.rst-content section ul li>ol,.rst-content section ul li>ul{margin-bottom:12px}.rst-content .section ol.simple li>*,.rst-content .section ol.simple li ol,.rst-content .section ol.simple li ul,.rst-content .section ul.simple li>*,.rst-content .section ul.simple li ol,.rst-content .section ul.simple li ul,.rst-content .toctree-wrapper ol.simple li>*,.rst-content .toctree-wrapper ol.simple li ol,.rst-content .toctree-wrapper ol.simple li ul,.rst-content .toctree-wrapper ul.simple li>*,.rst-content .toctree-wrapper ul.simple li ol,.rst-content .toctree-wrapper ul.simple li ul,.rst-content section ol.simple li>*,.rst-content section ol.simple li ol,.rst-content section ol.simple li ul,.rst-content section ul.simple li>*,.rst-content section ul.simple li ol,.rst-content section ul.simple li ul{margin-top:0;margin-bottom:0}.rst-content .line-block{margin-left:0;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0}.rst-content .topic-title{font-weight:700;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0 0 24px 24px}.rst-content .align-left{float:left;margin:0 24px 24px 0}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink{opacity:0;font-size:14px;font-family:FontAwesome;margin-left:.5em}.rst-content .code-block-caption .headerlink:focus,.rst-content .code-block-caption:hover .headerlink,.rst-content .eqno .headerlink:focus,.rst-content .eqno:hover .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink:focus,.rst-content .toctree-wrapper>p.caption:hover .headerlink,.rst-content dl dt .headerlink:focus,.rst-content dl dt:hover .headerlink,.rst-content h1 .headerlink:focus,.rst-content h1:hover .headerlink,.rst-content h2 .headerlink:focus,.rst-content h2:hover .headerlink,.rst-content h3 .headerlink:focus,.rst-content h3:hover .headerlink,.rst-content h4 .headerlink:focus,.rst-content h4:hover .headerlink,.rst-content h5 .headerlink:focus,.rst-content h5:hover .headerlink,.rst-content h6 .headerlink:focus,.rst-content h6:hover .headerlink,.rst-content p.caption .headerlink:focus,.rst-content p.caption:hover .headerlink,.rst-content p .headerlink:focus,.rst-content p:hover .headerlink,.rst-content table>caption .headerlink:focus,.rst-content table>caption:hover .headerlink{opacity:1}.rst-content p a{overflow-wrap:anywhere}.rst-content .wy-table td p,.rst-content .wy-table td ul,.rst-content .wy-table th p,.rst-content .wy-table th ul,.rst-content table.docutils td p,.rst-content table.docutils td ul,.rst-content table.docutils th p,.rst-content table.docutils th ul,.rst-content table.field-list td p,.rst-content table.field-list td ul,.rst-content table.field-list th p,.rst-content table.field-list th ul{font-size:inherit}.rst-content .btn:focus{outline:2px solid}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:1px solid #e1e4e5}.rst-content .sidebar dl,.rst-content .sidebar p,.rst-content .sidebar ul{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif;font-weight:700;background:#e1e4e5;padding:6px 12px;margin:-24px -24px 24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;box-shadow:0 0 0 2px #f1c40f;display:inline;font-weight:700}.rst-content .citation-reference,.rst-content .footnote-reference{vertical-align:baseline;position:relative;top:-.4em;line-height:0;font-size:90%}.rst-content .citation-reference>span.fn-bracket,.rst-content .footnote-reference>span.fn-bracket{display:none}.rst-content .hlist{width:100%}.rst-content dl dt span.classifier:before{content:" : "}.rst-content dl dt span.classifier-delimiter{display:none!important}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent!important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{display:grid;grid-template-columns:auto minmax(80%,95%)}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{display:inline-grid;grid-template-columns:max-content auto}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{display:grid;grid-template-columns:auto auto minmax(.65rem,auto) minmax(40%,95%)}html.writer-html5 .rst-content aside.citation>span.label,html.writer-html5 .rst-content aside.footnote>span.label,html.writer-html5 .rst-content div.citation>span.label{grid-column-start:1;grid-column-end:2}html.writer-html5 .rst-content aside.citation>span.backrefs,html.writer-html5 .rst-content aside.footnote>span.backrefs,html.writer-html5 .rst-content div.citation>span.backrefs{grid-column-start:2;grid-column-end:3;grid-row-start:1;grid-row-end:3}html.writer-html5 .rst-content aside.citation>p,html.writer-html5 .rst-content aside.footnote>p,html.writer-html5 .rst-content div.citation>p{grid-column-start:4;grid-column-end:5}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{margin-bottom:24px}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{padding-left:1rem}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.footnote>dt{margin-bottom:0}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.footnote>dt{margin:0 .5rem .5rem 0;line-height:1.2rem;word-break:break-all;font-weight:400}html.writer-html5 .rst-content dl.citation>dt>span.brackets:before,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.citation>dt>span.brackets:after,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a{word-break:keep-all}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a:not(:first-child):before,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.footnote>dd{margin:0 0 .5rem;line-height:1.2rem}html.writer-html5 .rst-content dl.citation>dd p,html.writer-html5 .rst-content dl.footnote>dd p{font-size:.9rem}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{padding-left:1rem;padding-right:1rem;font-size:.9rem;line-height:1.2rem}html.writer-html5 .rst-content aside.citation p,html.writer-html5 .rst-content aside.footnote p,html.writer-html5 .rst-content div.citation p{font-size:.9rem;line-height:1.2rem;margin-bottom:12px}html.writer-html5 .rst-content aside.citation span.backrefs,html.writer-html5 .rst-content aside.footnote span.backrefs,html.writer-html5 .rst-content div.citation span.backrefs{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content aside.citation span.backrefs>a,html.writer-html5 .rst-content aside.footnote span.backrefs>a,html.writer-html5 .rst-content div.citation span.backrefs>a{word-break:keep-all}html.writer-html5 .rst-content aside.citation span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content aside.footnote span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content div.citation span.backrefs>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content aside.citation span.label,html.writer-html5 .rst-content aside.footnote span.label,html.writer-html5 .rst-content div.citation span.label{line-height:1.2rem}html.writer-html5 .rst-content aside.citation-list,html.writer-html5 .rst-content aside.footnote-list,html.writer-html5 .rst-content div.citation-list{margin-bottom:24px}html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}.rst-content table.docutils.footnote,html.writer-html4 .rst-content table.docutils.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content aside.footnote-list aside.footnote,html.writer-html5 .rst-content div.citation-list>div.citation,html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{color:grey}.rst-content table.docutils.footnote code,.rst-content table.docutils.footnote tt,html.writer-html4 .rst-content table.docutils.citation code,html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html5 .rst-content aside.footnote-list aside.footnote code,html.writer-html5 .rst-content aside.footnote-list aside.footnote tt,html.writer-html5 .rst-content aside.footnote code,html.writer-html5 .rst-content aside.footnote tt,html.writer-html5 .rst-content div.citation-list>div.citation code,html.writer-html5 .rst-content div.citation-list>div.citation tt,html.writer-html5 .rst-content dl.citation code,html.writer-html5 .rst-content dl.citation tt,html.writer-html5 .rst-content dl.footnote code,html.writer-html5 .rst-content dl.footnote tt{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}html.writer-html5 .rst-content table.docutils th{border:1px solid #e1e4e5}html.writer-html5 .rst-content table.docutils td>p,html.writer-html5 .rst-content table.docutils th>p{line-height:1rem;margin-bottom:0;font-size:.9rem}.rst-content table.docutils td .last,.rst-content table.docutils td .last>:last-child{margin-bottom:0}.rst-content table.field-list,.rst-content table.field-list td{border:none}.rst-content table.field-list td p{line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content code,.rst-content tt{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;padding:2px 5px}.rst-content code big,.rst-content code em,.rst-content tt big,.rst-content tt em{font-size:100%!important;line-height:normal}.rst-content code.literal,.rst-content tt.literal{color:#e74c3c;white-space:normal}.rst-content code.xref,.rst-content tt.xref,a .rst-content code,a .rst-content tt{font-weight:700;color:#404040;overflow-wrap:normal}.rst-content kbd,.rst-content pre,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace}.rst-content a code,.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:700;margin-bottom:12px}.rst-content dl ol,.rst-content dl p,.rst-content dl table,.rst-content dl ul{margin-bottom:12px}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl dd>ol:last-child,.rst-content dl dd>p:last-child,.rst-content dl dd>table:last-child,.rst-content dl dd>ul:last-child{margin-bottom:0}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple){margin-bottom:24px}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:3px solid #6ab0de;padding:6px;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt:before,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:before{color:#6ab0de}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{margin-bottom:6px;border:none;border-left:3px solid #ccc;background:#f0f0f0;color:#555}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{background-color:transparent;border:none;padding:0;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .property{display:inline-block;padding-right:8px;max-width:100%}html.writer-html4 .rst-content dl:not(.docutils) .k,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .k{font-style:italic}html.writer-html4 .rst-content dl:not(.docutils) .descclassname,html.writer-html4 .rst-content dl:not(.docutils) .descname,html.writer-html4 .rst-content dl:not(.docutils) .sig-name,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .sig-name{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#000}.rst-content .viewcode-back,.rst-content .viewcode-link{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:700}.rst-content code.download,.rst-content tt.download{background:inherit;padding:inherit;font-weight:400;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content code.download span:first-child,.rst-content tt.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{margin-right:4px}.rst-content .guilabel,.rst-content .menuselection{font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content .guilabel,.rst-content .menuselection{border:1px solid #7fbbe3;background:#e7f2fa}.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>.kbd,.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>kbd{color:inherit;font-size:80%;background-color:#fff;border:1px solid #a6a6a6;border-radius:4px;box-shadow:0 2px grey;padding:2.4px 6px;margin:auto 0}.rst-content .versionmodified{font-style:italic}@media screen and (max-width:480px){.rst-content .sidebar{width:100%;float:none;margin-left:0}}span[id*=MathJax-Span]{color:#404040}.math{text-align:center}@font-face{font-family:Lato;src:url(fonts/lato-normal.woff2?bd03a2cc277bbbc338d464e679fe9942) format("woff2"),url(fonts/lato-normal.woff?27bd77b9162d388cb8d4c4217c7c5e2a) format("woff");font-weight:400;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold.woff2?cccb897485813c7c256901dbca54ecf2) format("woff2"),url(fonts/lato-bold.woff?d878b6c29b10beca227e9eef4246111b) format("woff");font-weight:700;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold-italic.woff2?0b6bb6725576b072c5d0b02ecdd1900d) format("woff2"),url(fonts/lato-bold-italic.woff?9c7e4e9eb485b4a121c760e61bc3707c) format("woff");font-weight:700;font-style:italic;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-normal-italic.woff2?4eb103b4d12be57cb1d040ed5e162e9d) format("woff2"),url(fonts/lato-normal-italic.woff?f28f2d6482446544ef1ea1ccc6dd5892) format("woff");font-weight:400;font-style:italic;font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:400;src:url(fonts/Roboto-Slab-Regular.woff2?7abf5b8d04d26a2cafea937019bca958) format("woff2"),url(fonts/Roboto-Slab-Regular.woff?c1be9284088d487c5e3ff0a10a92e58c) format("woff");font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:700;src:url(fonts/Roboto-Slab-Bold.woff2?9984f4a9bda09be08e83f2506954adbe) format("woff2"),url(fonts/Roboto-Slab-Bold.woff?bed5564a116b05148e3b3bea6fb1162a) format("woff");font-display:block} \ No newline at end of file diff --git a/docs/build/html/_static/doctools.js b/docs/build/html/_static/doctools.js deleted file mode 100644 index 807cdb1..0000000 --- a/docs/build/html/_static/doctools.js +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Base JavaScript utilities for all Sphinx HTML documentation. - */ -"use strict"; - -const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ - "TEXTAREA", - "INPUT", - "SELECT", - "BUTTON", -]); - -const _ready = (callback) => { - if (document.readyState !== "loading") { - callback(); - } else { - document.addEventListener("DOMContentLoaded", callback); - } -}; - -/** - * Small JavaScript module for the documentation. - */ -const Documentation = { - init: () => { - Documentation.initDomainIndexTable(); - Documentation.initOnKeyListeners(); - }, - - /** - * i18n support - */ - TRANSLATIONS: {}, - PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), - LOCALE: "unknown", - - // gettext and ngettext don't access this so that the functions - // can safely bound to a different name (_ = Documentation.gettext) - gettext: (string) => { - const translated = Documentation.TRANSLATIONS[string]; - switch (typeof translated) { - case "undefined": - return string; // no translation - case "string": - return translated; // translation exists - default: - return translated[0]; // (singular, plural) translation tuple exists - } - }, - - ngettext: (singular, plural, n) => { - const translated = Documentation.TRANSLATIONS[singular]; - if (typeof translated !== "undefined") - return translated[Documentation.PLURAL_EXPR(n)]; - return n === 1 ? singular : plural; - }, - - addTranslations: (catalog) => { - Object.assign(Documentation.TRANSLATIONS, catalog.messages); - Documentation.PLURAL_EXPR = new Function( - "n", - `return (${catalog.plural_expr})`, - ); - Documentation.LOCALE = catalog.locale; - }, - - /** - * helper function to focus on search bar - */ - focusSearchBar: () => { - document.querySelectorAll("input[name=q]")[0]?.focus(); - }, - - /** - * Initialise the domain index toggle buttons - */ - initDomainIndexTable: () => { - const toggler = (el) => { - const idNumber = el.id.substr(7); - const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); - if (el.src.substr(-9) === "minus.png") { - el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; - toggledRows.forEach((el) => (el.style.display = "none")); - } else { - el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; - toggledRows.forEach((el) => (el.style.display = "")); - } - }; - - const togglerElements = document.querySelectorAll("img.toggler"); - togglerElements.forEach((el) => - el.addEventListener("click", (event) => toggler(event.currentTarget)), - ); - togglerElements.forEach((el) => (el.style.display = "")); - if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); - }, - - initOnKeyListeners: () => { - // only install a listener if it is really needed - if ( - !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS - && !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS - ) - return; - - document.addEventListener("keydown", (event) => { - // bail for input elements - if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) - return; - // bail with special keys - if (event.altKey || event.ctrlKey || event.metaKey) return; - - if (!event.shiftKey) { - switch (event.key) { - case "ArrowLeft": - if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; - - const prevLink = document.querySelector('link[rel="prev"]'); - if (prevLink && prevLink.href) { - window.location.href = prevLink.href; - event.preventDefault(); - } - break; - case "ArrowRight": - if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; - - const nextLink = document.querySelector('link[rel="next"]'); - if (nextLink && nextLink.href) { - window.location.href = nextLink.href; - event.preventDefault(); - } - break; - } - } - - // some keyboard layouts may need Shift to get / - switch (event.key) { - case "/": - if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; - Documentation.focusSearchBar(); - event.preventDefault(); - } - }); - }, -}; - -// quick alias for translations -const _ = Documentation.gettext; - -_ready(Documentation.init); diff --git a/docs/build/html/_static/documentation_options.js b/docs/build/html/_static/documentation_options.js deleted file mode 100644 index 13d90ff..0000000 --- a/docs/build/html/_static/documentation_options.js +++ /dev/null @@ -1,13 +0,0 @@ -const DOCUMENTATION_OPTIONS = { - VERSION: '0.1.0', - LANGUAGE: 'en', - COLLAPSE_INDEX: false, - BUILDER: 'html', - FILE_SUFFIX: '.html', - LINK_SUFFIX: '.html', - HAS_SOURCE: true, - SOURCELINK_SUFFIX: '.txt', - NAVIGATION_WITH_KEYS: false, - SHOW_SEARCH_SUMMARY: true, - ENABLE_SEARCH_SHORTCUTS: true, -}; \ No newline at end of file diff --git a/docs/build/html/_static/english-stemmer.js b/docs/build/html/_static/english-stemmer.js deleted file mode 100644 index 056760e..0000000 --- a/docs/build/html/_static/english-stemmer.js +++ /dev/null @@ -1,1066 +0,0 @@ -// Generated from english.sbl by Snowball 3.0.1 - https://snowballstem.org/ - -/**@constructor*/ -var EnglishStemmer = function() { - var base = new BaseStemmer(); - - /** @const */ var a_0 = [ - ["arsen", -1, -1], - ["commun", -1, -1], - ["emerg", -1, -1], - ["gener", -1, -1], - ["later", -1, -1], - ["organ", -1, -1], - ["past", -1, -1], - ["univers", -1, -1] - ]; - - /** @const */ var a_1 = [ - ["'", -1, 1], - ["'s'", 0, 1], - ["'s", -1, 1] - ]; - - /** @const */ var a_2 = [ - ["ied", -1, 2], - ["s", -1, 3], - ["ies", 1, 2], - ["sses", 1, 1], - ["ss", 1, -1], - ["us", 1, -1] - ]; - - /** @const */ var a_3 = [ - ["succ", -1, 1], - ["proc", -1, 1], - ["exc", -1, 1] - ]; - - /** @const */ var a_4 = [ - ["even", -1, 2], - ["cann", -1, 2], - ["inn", -1, 2], - ["earr", -1, 2], - ["herr", -1, 2], - ["out", -1, 2], - ["y", -1, 1] - ]; - - /** @const */ var a_5 = [ - ["", -1, -1], - ["ed", 0, 2], - ["eed", 1, 1], - ["ing", 0, 3], - ["edly", 0, 2], - ["eedly", 4, 1], - ["ingly", 0, 2] - ]; - - /** @const */ var a_6 = [ - ["", -1, 3], - ["bb", 0, 2], - ["dd", 0, 2], - ["ff", 0, 2], - ["gg", 0, 2], - ["bl", 0, 1], - ["mm", 0, 2], - ["nn", 0, 2], - ["pp", 0, 2], - ["rr", 0, 2], - ["at", 0, 1], - ["tt", 0, 2], - ["iz", 0, 1] - ]; - - /** @const */ var a_7 = [ - ["anci", -1, 3], - ["enci", -1, 2], - ["ogi", -1, 14], - ["li", -1, 16], - ["bli", 3, 12], - ["abli", 4, 4], - ["alli", 3, 8], - ["fulli", 3, 9], - ["lessli", 3, 15], - ["ousli", 3, 10], - ["entli", 3, 5], - ["aliti", -1, 8], - ["biliti", -1, 12], - ["iviti", -1, 11], - ["tional", -1, 1], - ["ational", 14, 7], - ["alism", -1, 8], - ["ation", -1, 7], - ["ization", 17, 6], - ["izer", -1, 6], - ["ator", -1, 7], - ["iveness", -1, 11], - ["fulness", -1, 9], - ["ousness", -1, 10], - ["ogist", -1, 13] - ]; - - /** @const */ var a_8 = [ - ["icate", -1, 4], - ["ative", -1, 6], - ["alize", -1, 3], - ["iciti", -1, 4], - ["ical", -1, 4], - ["tional", -1, 1], - ["ational", 5, 2], - ["ful", -1, 5], - ["ness", -1, 5] - ]; - - /** @const */ var a_9 = [ - ["ic", -1, 1], - ["ance", -1, 1], - ["ence", -1, 1], - ["able", -1, 1], - ["ible", -1, 1], - ["ate", -1, 1], - ["ive", -1, 1], - ["ize", -1, 1], - ["iti", -1, 1], - ["al", -1, 1], - ["ism", -1, 1], - ["ion", -1, 2], - ["er", -1, 1], - ["ous", -1, 1], - ["ant", -1, 1], - ["ent", -1, 1], - ["ment", 15, 1], - ["ement", 16, 1] - ]; - - /** @const */ var a_10 = [ - ["e", -1, 1], - ["l", -1, 2] - ]; - - /** @const */ var a_11 = [ - ["andes", -1, -1], - ["atlas", -1, -1], - ["bias", -1, -1], - ["cosmos", -1, -1], - ["early", -1, 5], - ["gently", -1, 3], - ["howe", -1, -1], - ["idly", -1, 2], - ["news", -1, -1], - ["only", -1, 6], - ["singly", -1, 7], - ["skies", -1, 1], - ["sky", -1, -1], - ["ugly", -1, 4] - ]; - - /** @const */ var /** Array' - + '' - + _("Hide Search Matches") - + "
", - ), - ); - }, - - /** - * helper function to hide the search marks again - */ - hideSearchWords: () => { - document - .querySelectorAll("#searchbox .highlight-link") - .forEach((el) => el.remove()); - document - .querySelectorAll("span.highlighted") - .forEach((el) => el.classList.remove("highlighted")); - localStorage.removeItem("sphinx_highlight_terms"); - }, - - initEscapeListener: () => { - // only install a listener if it is really needed - if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; - - document.addEventListener("keydown", (event) => { - // bail for input elements - if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) - return; - // bail with special keys - if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) - return; - if ( - DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS - && event.key === "Escape" - ) { - SphinxHighlight.hideSearchWords(); - event.preventDefault(); - } - }); - }, -}; - -_ready(() => { - /* Do not call highlightSearchWords() when we are on the search page. - * It will highlight words from the *previous* search query. - */ - if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); - SphinxHighlight.initEscapeListener(); -}); diff --git a/docs/build/html/api.html b/docs/build/html/api.html deleted file mode 100644 index 4e39993..0000000 --- a/docs/build/html/api.html +++ /dev/null @@ -1,3781 +0,0 @@ - - - - - - - - -The API reference is generated automatically from the package source.
-© M. Sc. Florian Quintes, 2021-2022
-@contact: florian.quintes@pc.uni.freiburg.de
-@author: Florian Quintes
-Do various optimizations with all simulations available in -‘easypairspin()’.
-Sys (object) – Spinsystem object of module ‘epr_setup’.
Exp (object) – Experimental object of module ‘epr_setup’.
SimOpt (object) – SimulationOptions object of module ‘epr_setup’.
FitOpt (object) – FittingOptions object of module ‘epr_setup’.
Var (object) – Variation object of module ‘epr_setup’.
ValueError – Will be raised, if the given simulation routine in FitOpt.routine is an - invalid string.
-best_Spinsystem – The best Spinsystem found during optimization. Object is of class -Spinsystem of module ‘epr_setup’.
-object
Examples
-Basic optimization using genetic_radpair and static_radical_pair:
->>> from epr_setup import Spinsystem, Experimental, SimulationOptions,
-... FittingOptions, Variation
->>> Sys = Spinsystem()
->>> Exp = Experimental()
->>> SimOpt = SimulationOptions()
->>> SimOpt.routine = 'static_radpair'
->>> FitOpt = FittingOptions()
->>> Var = Variation()
->>> Var.g1 = np.array([0.001, 0.003, 0.002])
->>> best_Sys = optimize(Sys, Exp, SimOpt, FitOpt, Var)
-Plot your result:
->>> from plotting import plot_2D
->>> simulate(best_Sys, Exp, SimOpt)
->>> plot_2D(Exp.B_z , Exp.spec_sim)
-Do various simulations with (spinpolarized) radical pairs.
-Sys (object) – Spinsystem object of module ‘epr_setup’.
Exp (object) – Experimental object of module ‘epr_setup’. simulated_spectra will be
-saved in Exp.spec_sim.
SimOpt (object) – SimulationOptions object of module ‘epr_setup’.
ValueError – Will be raised, if the given simulation routine in SimOpt.routine is an - invalid string.
-simulated_spectra – Simulated spectra as a numpy.ndarray.
-ndarray
Examples
-Basic simulation using static_radical_pair:
->>> from epr_setup import Spinsystem, Experimental, SimulationOptions
->>> Sys = Spinsystem()
->>> Exp = Experimental()
->>> SimOpt = SimulationOptions()
->>> SimOpt.routine = 'static_radpair'
->>> simulate(Sys, Exp, SimOpt)
-Plot your result:
->>> from plotting import plot_2D
->>> plot_2D(Exp.B_z , Exp.spec_sim)
-© M. Sc. Florian Quintes, 2021-2022
-@contact: florian.quintes@pc.uni.freiburg.de
-@author: Florian Quintes
-Bases: object
A class containing all parameters for various radical pair simulations.
-g-Tensor of electron 1.
-1d-Array, np.float64
-g-Tensor of electron 2.
-1d-Array, np.float64
-g-Tensor of a triplet radical.
-1d-Array, np.float64
-g-Tensor of a radical.
-1d-Array, np.float64
-A-Tensor of nuclei 1 in Megahertz.
-1d-Array, np.float64
-A-Tensor of nuclei 2 in Megahertz.
-1d-Array, np.float64
-A-Tensor of nuclei 3 in Megahertz.
-1d-Array, np.float64
-A-Tensor of nuclei 4 in Megahertz.
-1d-Array, np.float64
-A-Tensor of nuclei 5 in Megahertz.
-1d-Array, np.float64
-Hyperfine coupling for the nuclei in OOP-ESEEM in Megahertz.
-float64
-Nuclei frequency in OOP-ESEEM in Megahertz.
-float64
-Zero field splitting parameter D in Megahertz.
-float64
-Zero field splitting parameter D of a triplet in Megahertz.
-float64
-Zero field splitting parameter E in Megahertz.
-float64
-Zero field splitting parameter E of a triplet in Megahertz.
-float64
-Decay rate of the exchange coupling in Å^-1.
-float64
-Zero distance exchange coupling constant in Megahertz.
-float64
-Exchange coupling in Megahertz.
-float64
-Orientation of electron spin 1 in radian.
-1d-Array, np.float64
-Orientation of electron spin 2 in radian.
-1d-Array, np.float64
-Orientation of g_tri in radian.
-1d-Array, np.float64
-Orientation of g in radian.
-1d-Array, np.float64
-Orientation of nuclei spin 1 in radian.
-1d-Array, np.float64
-Orientation of nuclei spin 2 in radian.
-1d-Array, np.float64
-Orientation of nuclei spin 3 in radian.
-1d-Array, np.float64
-Orientation of nuclei spin 4 in radian.
-1d-Array, np.float64
-Orientation of nuclei spin 5 in radian.
-1d-Array, np.float64
-Orientation of dipol coupling in radian.
-1d-Array, np.float64
-Orientation of triplet dipol coupling in radian.
-1d-Array, np.float64
-Corresponding nuclear spin.
-float64
-Corresponding nuclear spin.
-float64
-Corresponding nuclear spin.
-float64
-Corresponding nuclear spin.
-float64
-Corresponding nuclear spin.
-float64
-Gaussian linewidth in mT.
-float64
-Longitudinal relaxation time in s.
-float64
-Transversal relaxation time in s.
-float64
-Exponential decay time for hilbert space simulations in s.
-float64
-Phase memory time (OOP ESEEM).
-float64
-Populations of the initial density matrix of a triplet precursor in -zero field.
-1d-Array, np.float64
-Amplitude of the spectra for OOP-ESEEM.
-float64
-Bases: object
A class containing all experimental parameters and data.
-External magnetic field points in mT used for simulation. Conversions -allowed.
-1d-Array, np.float64
-Frequency of induced microwave radiation in Gigahertz.
-float64
-Same as B_z, but will never be changed.
-1d-Array, np.float64
-Real and imaginary part of the measured intensities. 1d or 2d.
-np.array, np.complex128
-Contains all experimental time points.
-1d-Array, optional
-Calculated spectrum. At initialisation empty.
-1d-Array, np.float64
-Get a linear time axis using the given boundaries from self.t_scale -with self.t_points points.
-Get a linear timea axis for transient simulations.
-Get a linear time axis using the given boundaries from self.t_scale -with self.t_points points.
-t_min (float) – Left boundary of the time axis. If None is given, the current value
-of self.t_scale[0] will be used. Else, the value of self.t_scale[0]
-will be replaced. The default is None.
t_max (float) – Right boundary of the time axis. If None is given, the current
-value of self.t_scale[1] will be used. Else, the value of
-self.t_scale[1] will be replaced. The default is None.
t_points (int) – Number of time points. If None is given, the current value of
-self.t_points will be used. Else, the value of self.t_points will
-be replaced. The default is None.
Nothing will be returned.
-None
Bases: object
A class containing all optimization options.
-Name of the optimization routine which will be used by -easypairspin_optimize().
-str
-Name of the optimization method used in the scipy.optimize routines.
-str
-Array containing the initial guess for the optimization routine for -the parameters which will be varied.
-numpy.array, np.float64
-Set to True if in GUI mode (PySpin). The default is False.
-bool
-Plot canvas. Only needed in GUI mode.
-object
-Load FittingOptions values from a config file (profile_name.ini).
-Recommended
-Save the current fitting options as a config file (profile_name.ini).
-Recommended
-Examples
-Initialize an object of class <FittingOptions>:
----->>> FitOpt = FittingOptions() ->>> FitOpt.GAVaPS -True -Change values:
--->>> FitOpt.GAVaPS = False ->>> FitOpt.GAVaPS -False -Save your current values as a new profile:
--->>> FitOpt.save_simulationoptions('FitOpt_prof_1') ->>> FitOpt_2 = FittingOptions() ->>> FitOpt_2.GAVaPS -True ->>> FitOpt.load_profile('FitOpt_prof_1') ->>> FitOpt_2.GAVaPS -False -You can also create a fitting options profile from an empty template:
--->>> fitopt_prof = profiles.new_optimization_profile() ->>> fitopt_prof['genetic']['GAVaPS'] = False ->>> profiles.add_profile(fitopt_prof, 'simulation', 'FitOpt_prof_2') ->>> FitOpt_3 = FittingOptions() ->>> FitOpt_3.GAVaPS -True ->>> FitOpt.load_profile('FitOpt_prof_2') ->>> FitOpt_3.GAVaPS -False -
Load fitting options from a profile.
-Load the settings from [sys.prefix]/easypairspin/profiles/optimization/ -[profile_name].ini into the FittingOptions object. Overwrites previous -settings. Only loads the section given in [‘main’][‘routine’].
-profile_name (str) – Name of the optimization profile which will be loaded.
None
Save the fitting options as a profile.
-Save the fitting options as a fitting options profile using -profile_management.add_profile(). Load the fitting options using -FittingOptions.load_profile(<profile_name>).
-profile_name (str) – Name of the profile. If no profile name is given, a default one
-will be generated by get_profile_name(). The default is ‘’.
Nothing will be returned.
-None
Bases: object
A class containing all simulation options.
-Name of the simulation routine which will be used by easypairspin() and -easypairspin_optimize().
-str
-Name of the mathematical space used for some calculations.
-str
-If set to True, the population evolution in calculated using teacups.
-boolean
-If set to True, only the eigenvalues of the system are calculated using -teacups.
-boolean
-If True, the simulation will be executed on the CPU, even if GPU is -available. Default is False.
-boolean
-Choose the regularization matrix used for the Tikhonov-Regularization. -0 : Unitary matrix -1 : First order derivative matrix -2 : Second order derivative matrix (default)
-Load SimulationOptions values from a config file (profile_name.ini).
-Recommended
-Save the current simulation options as a config file -(profile_name.ini).
-Examples
-Initialize an object of class <SimulationOptions>:
->>> SimOpt = SimulationOptions()
->>> SimOpt.grid_points
-500
-Change values:
->>> SimOpt.grid_points = 1000
->>> SimOpt.grid_points
-1000
-Save your current values as a new profile:
->>> SimOpt.save('SimOpt_prof_1')
->>> SimOpt_2 = SimulationOptions()
->>> SimOpt_2.grid_points
-500
->>> SimOpt.load('SimOpt_prof_1')
->>> SimOpt_2.grid_points
-1000
-You can also create a simulation options profile from an empty template:
->>> simopt_prof = profiles.new_simulation_profile()
->>> simopt_prof['static_radpair']['grid_points'] = 1100
->>> profiles.add_profile(simopt_prof, 'simulation', 'SimOpt_prof_2')
->>> SimOpt_3 = SimulationOptions()
->>> SimOpt_3.grid_points
-500
->>> SimOpt.load('SimOpt_prof_2')
->>> SimOpt_3.grid_points
-1100
-Load simulation options from a profile.
-Load the settings from [sys.prefix]/easypairspin/profiles/simulation/ -[profile_name].ini into the SimulationOptions object. Overwrites -previous settings.
-profile_name (str) – Name of the simulation profile which will be loaded.
None
Save the simulation optionas as a profile.
-Save the simulation options as a simulation options profile using -profile_management.add_profile(). Load the simulation options using -SimulationOptions.load_profile(<profile_name>).
-profile_name (str) – Name of the profile. If no profile name is given, a default one
-will be generated by get_profile_name(). The default is ‘’.
Nothing will be returned.
-None
Bases: EPR_Parameters
A class containing all parameters for various radical pair simulations.
-Isotropic g value of electron 1.
-float64
-Isotropic g value of electron 2.
-float64
-Corresponding nuclear spin.
-float64
-Corresponding nuclear spin.
-float64
-Corresponding nuclear spin.
-float64
-Corresponding nuclear spin.
-float64
-Corresponding nuclear spin.
-float64
-Defines which atom groups are donor groups.
-np.array
-Defines which atom groups are acceptor groups.
-np.array
-Define a frame_group which will be used in optimization mode. Each -frame group contains the names of the angle lists which always will -have same values during optimization. i is a variable and can be -whatever you want. You can define as many frame groups as you want. An -example frame group would be: frame_group_1 = [‘A1’, ‘A2’, ‘D’]. This -list means, that A2_frame and D_frame will always have the same values -as A1_frame, no matter which values were given to them.
-list
-Define the spin system by one out of: “rp” (radical pair), “doub” -(doublet), “trip” (triplet), “tdp” (triplet-doublet pair).
-str
-State of the precursor. One out of: “zf”, “eigen”, “singlet”, -“triplet-zf”, “triplet-eigen”, “coupled”, “basis”.
-str
-Matrix with rate constants of relaxation process in 1/s. For further -information see the documentation.
-np.array
-Distance distribution of the radical pair.
-np.array
-Load Spinsystem values from a config file (profile_name.ini).
-Recommended
-Save the current spinsystem values as a config file (profile_name.ini).
-Recommended
-Examples
-Initialize a new object of class <Spinsystem>:
->>> Sys = Spinsystem()
->>> Sys.g1
-np.array([2.002, 2.002, 2.002])
->>> Sys.g1_iso
-2.002
-Change values:
->>> Sys.g1 = np.array([2.0024, 2.00381, 2.0027])
->>> Sys.get_g_iso()
->>> Sys.g1
-np.array([2.0024 , 2.00381, 2.0027 ])
->>> Sys.g1_iso
-2.00297
-Create a new spinsystem profile from an empty template and load it:
->>> Sys_profile = profiles.new_spinsystem_profile()
->>> Sys_profile['g_1'] = [2.0034, 2.00156, 2.00228] # use list not array!
->>> profiles.add_profile(Sys.profile, 'spinsystem', 'Sys_prof_1')
->>> Sys_2 = Spinsystem()
->>> Sys_2.load_profile('Sys_prof_1')
->>> Sys_2.g1
-np.array([2.0034 , 2.00156, 2.00228])
-You can also save your current spinsystem as a new profile:
->>> Sys_3 = Spinsystem()
->>> Sys_3.g1 = np.array([1, 2, 3]) # either array or list
->>> Sys_3.save('Sys_prof_2')
->>> Sys_4 = Spinsystem()
->>> Sys_4.load('Sys_prof_2')
->>> Sys_4.g1
-np.array([1., 2., 3.])
-Load a spinsystem from a profile.
-Load the settings from [sys.prefix]/easypairspin/profiles/spinsystem/ -[profile_name].ini into the Spinsystem object. Overwrites previous -settings.
-profile_name (str) – Name of the Spinsystem profile which will be loaded.
degree (bool) – If True, the angle values in the profile are given in degree not
-radian. Thus, they will be converted to radian. If False, the
-angles are given in radian and will therefore not be converted. The
-default is ‘False’.
None
Save the spinsystem as a profile.
-Save the spinsystem object as a spinsystem profile using -profile_management.add_profile(). Load the spinsystem using -Spinsystem.load_profile(<profile_name>).
-profile_name (str) – Name of the profile. If no profile name is given, a default one
-will be generated by get_profile_name(). The default is ‘’.
Nothing will be returned.
-None
Bases: EPR_Parameters
A class containing variation ranges for all possible parameters.
- - - - -Array with all variation ranges greater 0.
-1d-Array, np.float64
-Sequence of tuples containing upper and lower bounds for all varied -parameters. Used for scipy.optimize.
-list
-Frequency of induced microwave radiation in Hertz.
-float64
-Bohr magneton in Hertz/Tesla.
-float64
-Load Spinsystem values from a config file (profile_name.ini).
-Recommended
-Save the current variation values as a config file (profile_name.ini).
-Recommended
-Get the number of needed digits for one varied parameter.
-Get the total number of genes. Is equal to the number of varied -parameters.
-Create an array with all used variation ranges. Only used in floating -point representation.
-Get a sequence of tuples containing the boundaries for the varied -parameters.
-Examples
-Initialize a new object of class <Variation>:
->>> Var = Variation()
->>> Var.g1
-np.array([0., 0., 0.])
-Change values:
->>> Var.g1 = np.array([0.003, 0.004, 0.003])
->>> Var.g1
-np.array([0.003, 0.004, 0.003])
-Create a new variation profile from an empty template and load it:
->>> Var_profile = profiles.new_variation_profile()
->>> Var_profile['g_1'] = [0.001, 0.007, 0.003] # use list not array!
->>> profiles.add_profile(Var.profile, 'variation', 'Var_prof_1')
->>> Var_2 = Variation()
->>> Var_2.load_profile('Var_prof_1')
->>> Var_2.g1
-np.array([0.001, 0.007, 0.003])
-You can also save your current variation object as a new profile:
->>> Var_3 = Variation()
->>> Var_3.g1 = np.array([1, 2, 3]) # either array or list
->>> Var_3.save('Var_prof_2')
->>> Var_4 = Variation()
->>> Var_4.load('Var_prof_2')
->>> Var_4.g1
-np.array([1., 2., 3.])
-Create a sequence of pairs with all bounds for the varied parameters.
-Used for the scipy optimization routines.
-Sys (object) – Spinsystem object.
Nothing will be returned.
-None
Get sum of needed digits for all varied parameters.
-None
Determine number of parameters which get varied.
-None
Put all variation ranges in one 1d-Array.
-None
Laod a variation object from a profile.
-Load the settings from [sys.prefix]/easypairspin/profiles/variation/ -[profile_name].ini into the Variation object. Overwrites previous -settings.
-profile_name (str) – Name of the Variation profile which will be loaded.
degree (bool) – If True, the angle values in the profile are given in degree not
-radian. Thus, they will be converted to radian. If False, the
-angles are given in radian and will therefore not be converted. The
-default is ‘False’.
None
Save a variation object as a profile.
-Save the variation object as a variation profile using -profile_management.add_profile(). Load the variation object using -Variation.load_profile(<profile_name>).
-profile_name (str) – Name of the profile. If no profile name is given, a default one
-will be generated by get_profile_name(). The default is ‘’.
Nothing will be returned.
-None
© M. Sc. Florian Quintes, 2021-2022
-@contact: florian.quintes@pc.uni.freiburg.de
-@author: Florian Quintes
-Convert the type of a given string to bool, int or float if possible.
-value (str) – Parameter string which should be converted.
value – Parameter as bool, int or float. If none is possible, the stripped -string will be returned.
-Tuple[bool, int, float, str]
Extract all parameters from the DSC File.
-path_to_folder (str) – Full path to the folder with .DSC and .DTA file. Files and path must
-have the same basename (BRUKER STANDARD).
DSC_parameters – Dictionary with all parameters. Key is the same as in .DSC.
-dict
Get the used byte mode of the BRUKER BES3T-data.
-For more information about BES3T go to BRUKER website or easyspin @ github.
-DSC_dict (dict) – Dictionary with all parameters. Key is the same as in .DSC.
data_key (str) – Key for the data array. Either ‘IRFMT’ or ‘IIFMT’.
-The default is ‘IRFMT’.
ValueError – Raised if key value is not C, S, I, F or D.
-byte_mode – Return the used byte mode as one string for -numpy.fromfile(dtype=byte_mode).
-str
Get the dimension of the measured spectrum (1d/2d).
-DSC_dict (dict) – Dictionary with all parameters. Key is the same as in .DSC.
The dimension will be safed in the given dictionary to the key -‘dimensions’.
-None
Get the full path of a given directory. Search starts at home directory.
-directory_name (str) – Name of the directory whose path is to be found.
start_directory (str) –
Directory at which the search starts. If given, the perfomance -increases very sharply. The default is None.
-Recommended.
-full_path – The full path of the directory.
-str
Get the measured intensities of the whole spectrum from a transient epr -experiment measured by ESP380E.
-fpath (str) – Full path to the folder with the experimental data.
Tuple[tuple, array]
axis (tuple) – Return a tuple with all axis vectors as two numpy.arrays -(magnetic_field, time).
cmplx_data_vector (np.array, np.complex128) – Measured real intensities. All imaginary parts are zero.
Get all informations about the time axis and magnetic field vector from the -.info file.
-fpath (str) – Full path to the folder with the experimental data.
KeyError – Raised if section ‘MAGNETIC FIELD’ or ‘TRANSIENT’ could not be found in - .info file. Take care that the sections must be all upper case.
-transient_info – Contains the needed informations in the following order (time_length, -time_points, mag_field_start, mag_field_stop, mag_field_step).
-tuple
Load the points of a given axis (x, y, z).
-axis (str) – Name of the axis. Needs to start with x,X,y,Y or z,Z. E. g.: x-axis.
DSC_dict (dict) – Dictionary with all parameters. Key is the same as in .DSC.
ValueError – Will be raised if the given axis doesn’t starts with an allowed - character.
-axis_vector – Array with all axis points.
-array
Load the binary intensity vector(s) from BRUKER BES3T-File.
-DSC_dict (dict) – Dictionary with all parameters. Key is the same as in .DSC.
ValueError – Will be raised if IKKF isn’t CPLX or REAL or if the dimension isn’t 1 - or 2.
KeyError – Raised if an axis is given but not the corresponding number of points.
cmplx_data_vector – Real and imaginary part of the measured intensities. If no imaginary -part is measured, zeroes will be inserted.
-array
Load data from a transient epr experiment measured with ESP380E.
-folder (str) – Name of the folder with all data files. Data files need to have the
-same basename as folder, e. g. ‘/home/user/exp_1/exp_1.001’.
start_directory (str) –
Give the path starting from your home folder which the search for -the data folder should start at. The default is None. -E. g.: data is at /home/cooluser/nice/data/this_folder -and you know, that all your data is in /home/cooluser/nice/*, then you -can give this start_directory with start_directory=’nice’. So -the function call would be:
---load_epr_ESP_transient(this_folder, ‘nice’)
-
Without this parameter, the search for the right folder will be -much longer.
-Tuple[tuple, array]
axis (tuple) – Return a tuple with all axis vectors as two numpy.arrays -(magnetic_field, time).
data (np.array, np.complex128) – All intensity values as one complex numpy.array. Imaginary part is -always 0.
Load the whole dataset from BRUKER BES3T data folder into numpy.arrays.
-Time axis will be rescaled for OOP-ESEEM experiments, if ‘FTAcqModeSlct’ -is ‘Run from Tables’.
-folder (str) – Name of the data folder with the corresponding data files.
start_directory (str) –
Give the path starting from your home folder which the search for the -data folder should start at. The default is None. -E. g.: data is at /home/cooluser/nice/data/this_folder -and you know, that all your data is in /home/cooluser/nice/*, then you -can give this start_directory with start_directory=’nice’. So -the function call would be:
---load_epr_bruker_bes3t(this_folder, ‘nice’)
-
Without this parameter, the search for the right folder will be -much longer.
-Tuple[tuple, array]
axis (tuple) – Tuple of all axis vectors as three numpy.arrays (x, y, z).
data (np.array, np.complex128) – All intensity values as one complex numpy.array.
Load EPR data from a matlab data file .mat
-folder (str) – Name of the data folder with the corresponding data files.
start_directory (str) –
Give the path starting from your home folder which the search for -the data folder should start at. The default is None. -E. g.: data is at /home/cooluser/nice/data/this_folder -and you know, that all your data is in /home/cooluser/nice/*, then you -can give this start_directory with start_directory=’cooluser/nice’. So -the function call would be:
---load_simulated_data(this_folder, ‘cooluser/nice’)
-
field (str) – Name of the field array. The default is ‘field’.
signal (str) – Name of the signal array. The default is ‘signal’.
Tuple[array, array, array]
axis (np.array) – Tuple of np.array containing the x and y axis.
data (np.array) – np.array with the measured intensities.
Load the simulated data from EasyPairSpin or data saved with -saving.save_simulation(). Using numpy.loadtxt().
-folder (str) – Name of the data folder with the corresponding data files.
start_directory (str) –
Give the path starting from your home folder which the search for -the data folder should start at. The default is None. -E. g.: data is at /home/cooluser/nice/data/this_folder -and you know, that all your data is in /home/cooluser/nice/*, then you -can give this start_directory with start_directory=’cooluser/nice’. So -the function call would be:
---load_simulated_data(this_folder, ‘cooluser/nice’)
-
Without this parameter, the search for the right folder -will be much longer.
-Tuple[array, array, array]
x (np.array) – Axis vector for the x-axis.
y (np.array) – Axis vector for the y-axis. Only returned, if the simulated data is 2d.
intensity (np.array, np.complex128) – Simulated intensities. Either 1d or 2d.
Load EPR data from a .txt file.
-folder (str) – Name of the data folder with the corresponding data files.
start_directory (str) –
Give the path starting from your home folder which the search for -the data folder should start at. The default is ‘’. -E. g.: data is at /home/cooluser/nice/data/this_folder -and you know, that all your data is in /home/cooluser/nice/*, then you -can give this start_directory with start_directory=’cooluser/nice’. So -the function call would be:
---load_simulated_data(this_folder, ‘cooluser/nice’)
-
Tuple[array, array, array]
axis (np.array) – Tuple of np.array containing the x and y axis.
data (np.array) – np.array with the measured intensities.
Get the measured intensities of a single field point from a transient epr -experiment measured by ESP380E.
-fpath (str) – Full path to the folder with the experimental data.
filenumber (int) – Number of the dataset for the magnetic field point e. g. 003.
digits (int) – Number of digits from the highest filenumber (ESP380E has an increasing
-number as fileextension for each magnetic field point e. g. data.001).
time (bool) – If True, the time axis will be returned.
Tuple[float, array]
field (float) – Magnetic field point.
time_axis (np.array) – Linear time axis.
data_vector (np.array) – Measured intensities.
© M. Sc. Florian Quintes, 2021-2022
-@contact: florian.quintes@pc.uni.freiburg.de
-@author: Florian Quintes
-Perform a background correction of measured data.
-Available correction modes are: biexp, exp, lin, poly2, poly3 and poly4. -biexp and exp are using exponential models for the background. lin, poly2-4 -are polynominal models of first to fourth order. Default is ‘biexp’.
-Warning
-poly3 and poly4 can lead to overfitting!
-x (array) – x axis of the dataset.
y (array) – y data which will be background corrected.
mode (str) – Select the type of the background. The default is ‘biexp’.
y_corr – Background corrected y data.
-array
Generalized biexponential function for background correction.
-x (array) – x values used to calculate corresponding y values.
*coeff (float) – Variables for the biexponential function which will be fitted.
y – Calculated y values.
-array
Generalized monoexponential function for background correction.
-x (array) – x values used to calculate corresponding y values.
*coeff (float) – Variables for the monoexponential function which will be fitted.
y – Calculated y values.
-array
Generalized linear function for background correction.
-x (array) – x values used to calculate corresponding y values.
*coeff (float) – Variables for the linear function which will be fitted.
y – Calculated y values.
-array
Normalize the given data.
-x (array) – Unnormalized data.
mode (str) – If ‘area’, the total AUC will be 1, if ‘value’, the maximum value will
-be 1 or -1, else, the formula mentioned above will be used. Default is
-None.
dx (float) – Distance between two points on the x axis. Only used for simpson
-integration. Default is None.
x_norm – Normalized data.
-array
Generalized polynominal function of degree 2 for background correction.
-x (array) – x values used to calculate corresponding y values.
*coeff (float) – Variables for the polynominal function of degree 2 which will be
-fitted.
y – Calculated y values.
-array
Generalized polynominal function of degree 3 for background correction.
-x (array) – x values used to calculate corresponding y values.
*coeff (float) – Variables for the polynominal function of degree 3 which will be
-fitted.
y – Calculated y values.
-array
Generalized polynominal function of degree 4 for background correction.
-x (array) – x values used to calculate corresponding y values.
*coeff (float) – Variables for the polynominal function of degree 4 which will be
-fitted.
y – Calculated y values.
-array
Reconstruction of a time signal use the Yule-Walker algorithm.
-x (array) – x axis.
y (array) – Intensities.
array
x_new (np.array) – Reconstructed x axis.
y (np.array) – Reconstructed intensities.
© M. Sc. Florian Quintes, 2021-2022
-@contact: florian.quintes@pc.uni.freiburg.de
-@author: Florian Quintes
-Bases: object
Supress Error Messages in a context manager.
-Plot 2D Data as a heatmap using matplotlib.pylab.pcolormesh().
-The plot can be configured via plot profiles.
-x (array) – Array with values for the x axis.
y (array) – Array with values for the y axis.
Z (array) – 2D-Array with intensities.
mpl_stylesheet (str) – Name of the matplotlib style sheet (see: matplotlib documentation).
-If no style sheet is given, the styles defined in the plotting profile
-will be used. The default is None.
ax (object) – Axes object, used for the PySpin GUI.
**kwargs (optional) – Keyword arguments passed to the matplotlib plot function. Overrides the -arguments given in the stylesheet.
fig – Figure object of matplotlib.pylab.
-object
Plot the given y value(s) against the given x array.
-Using matplotlib.pylab.plot(). The plot can be configured via plot -profiles.
-x (array) – Array with values for the x axis.
y (array) – 1D-Array or 2D-Array with values for y axis.
mpl_stylesheet (str) – Name of the matplotlib style sheet (see: matplotlib documentation).
-If no style sheet is given, the styles defined in the plotting profile
-will be used. The default is None.
labels (list[str]) – List of labels for the legend. If only one label is given, all labels
-will be the same. The default is ‘no_label’.
ax (object) – Axes object, used for the PySpin GUI.
**kwargs (optional) – Keyword arguments passed to the matplotlib plot function. Overrides the -arguments given in the stylesheet.
fig – Figure object of matplotlib.pylab.
-object
Plot 2D Data in 3D using matplotlib.pylab.plot_surface().
-The plot can be configured via plot profiles.
-x (array) – Array with values for the x axis.
y (array) – Array with values for the y axis.
Z (array) – 2D-Array with intensities.
mpl_stylesheet (str) – Name of the matplotlib style sheet (see: matplotlib documentation).
-If no style sheet is given, the styles defined in the plotting profile
-will be used. The default is None.
labels (str) – At the moment no function. The default is ‘no_label’. # TODO
ax (object) – Axes object, used for the PySpin GUI.
**kwargs (optional) – Keyword arguments passed to the matplotlib plot function. Overrides the -arguments given in the stylesheet.
fig – Figure object of matplotlib.pylab.
-object
Plot 2D Data in 3D using matplotlib.pylab.plot().
-Each y trace as a single line plot. The plot can be configured via plot -profiles.
-x (array) – Array with values for the x axis.
y (array) – Array with values for the y axis.
Z (array) – 2D-Array with intensities.
mpl_stylesheet (str) – Name of the matplotlib style sheet (see: matplotlib documentation).
-If no style sheet is given, the styles defined in the plotting profile
-will be used. The default is None.
ax (object) – Axes object, used for the PySpin GUI.
**kwargs (optional) – Keyword arguments passed to the matplotlib plot function. Overrides the -arguments given in the stylesheet.
fig – Figure object of matplotlib.pylab.
-object
Plot multiples lines in 2D, shifted vertically.
-The plot can be configured via plot profiles.
-x (array) – Array with values for the x axis.
Y (array) – 2D-Array with values for y axis.
mpl_stylesheet (str) – Name of the matplotlib style sheet (see: matplotlib documentation).
-If no style sheet is given, the styles defined in the plotting profile
-will be used. The default is None.
labels (list[str]) – List of labels for the legend. If only one label is given, all labels
-will be the same. The default is ‘no_label’.
ax (object) – Axes object, used for the PySpin GUI.
**kwargs (optional) – Keyword arguments passed to the matplotlib plot function. Overrides the -arguments given in the stylesheet.
fig – Figure object of matplotlib.pylab.
-object
© M. Sc. Florian Quintes, 2021-2022
-@contact: florian.quintes@pc.uni.freiburg.de
-@author: Florian Quintes
-Save the figures plotted with matplotlib.
-fname (str) – Filename for the figure(s). If multiple figures are given, ‘_[number]’
-will be append to the filename.
*figures (object) – Matplotlib figure object(s).
path (str) – Absolute path where the figures will be stored. The default is
-‘~/Results/Plots/’.
**kwargs (dict) – Other keyword arguments. Will be passed to plt.savefig(). See
-matplotlib documentation for further informations.
Nothing will be returned.
-None
Save the simulated data at ‘[path]/[name]/[files]’ using np.savetxt.
-name (str) – Foldername for the dataset.
*data (array) – Arrays with the simulated data. Must be 2d or 3d. If 2d: x_axis, int;
-if 3d: x_axis, y_axis, int.
path (str) – Full path where the data will be stored. The default is
-‘~/Results/Simulated Data/[name]’.
ValueError – Will be raised, if dimension of the data isn’t 2d or 3d.
-Nothing will be returned.
-None
Write an output file with all datas from Sys, Exp, SimOpt and, if running -in optimization mode, FitOpt.
-Sys (object) – Spinsystem object of module ‘epr_setup’.
Exp (object) – Experimental object of module ‘epr_setup’.
SimOpt (object) – SimulationOptions object of module ‘epr_setup’.
*FitOpt (object) – FittingOptions object of module ‘epr_setup’.
current_best (bool) – True if the given Sys and Exp are the current best while running in
-optimization mode. False if Sys and Exp are the final result / are the
-given Sys and Exp in normal simulation mode. The default is False.
Nothing will be returned.
-None
© M. Sc. Florian Quintes, 2021-2022
-@contact: florian.quintes@pc.uni.freiburg.de
-@author: Florian Quintes
-Add a new profile for EasyPairSpin.
-profile (dict) – Dictionary with all profile settings.
pkind (str) – Give the kind of the kind of the profile. Not case sensitive. pkind can
-be ‘plot’, ‘save’, ‘simulation’, ‘optimization’, ‘spinsystem’ or
-‘variation’.
pname (str) – Name of the profile. If no profile name is given, a default one will
-be generated by _get_profile_name(). The default is ‘’.
ValueError – Raised if pkind isn’t ‘plot’, ‘save’, ‘simulation’, ‘optimization’, - ‘spinsystem’ or ‘variation’.
-Nothing will be returned.
-None
Examples
-Creating and adding a new profile:
->>> Sys = epr_setup.Spinsystem()
->>> Sys_profile = new_spinsystem_profile()
->>> Sys_profile['g_1'] = [2.0034, 2.00156, 2.00228]
->>> prom.add_profile(Sys.profile, 'spinsystem', 'Sys_prof_1')
-Export the choosen profile(s) as a zip archive.
-path (str) – Path where the zip file will be stored. If no path given, the zip file
-will be stored in the current working directory. The default is None.
pkind ([<class ‘str’>, <class ‘list’>]) – Define which kind(s) of profiles should be exported. Multiple kinds of -profiles possible. Options are ‘plot’, ‘spinsystem’, ‘optimization’, -‘save’, ‘variation’, ‘simulation’ and ‘all’. The default is ‘all’.
pname (str) – Give the basename of the profile. The default is ‘all’.
Nothing will be returned.
-None
Import profiles from a zip archive.
-zipfile (str) – Path to the zip archive.
override (bool) – If True, existing profile with the same name will be overriden. The
-default is False.
Nothing will be returned.
-None
Load a plotting profile from a mplstylesheet.
-pname (str) – Name of the profile. Case sensitive.
Contains the settings for the plotting functions.
-dict
Load a given plotting profile.
-pname (str) – Name of the profile. Case sensitive. Either with .ini or not.
-E. g.: load_profile(‘test’) or load_profile(‘test.ini’).
pkind (str) – Give the kind of the profile. Not case sensitive. pkind can be ‘save’,
-‘simulation’, ‘optimization’, ‘spinsystem’ or ‘variation’.
profile – Loaded profile as a dictionary.
-dict
Get a default optimization profile.
-default_profile – Dictionary with default settings for optimization routines.
-dict
Get an empty plotting profile.
-default_profile – Dictionary with default settings for plottings.
-dict
Get a default save profile.
-default_profile – Dictionary with default settings for saving.
-dict
Get an empty simulation profile.
-default_profile – Dictionary with default settings for simulation profiles.
-dict
The following modules support internal execution and integration workflows.
-© M. Sc. Florian Quintes, 2021-2022
-@contact: florian.quintes@pc.uni.freiburg.de
-@author: Florian Quintes
-This decorateur will run the given function niter times and print the best, -the worst and the average runtime.
-func (callable) – Function which will be benchmarked.
niter (int) – Number of function calls.
Function with automatic benchmark.
-callable
Using multiprocessing.Pool() with starmap() for parallel computing of -various simulation routines using the easypairspin function interface -simulation(Sys, Exp, SimOpt).
-simulation (callable) – Simulation function which uses the easypairspin interface (Sys, Exp,
-SimOpt).
multicore_wrapper – The origin simulation callable as multicore version.
-callable
Sc. Florian Quintes, 2021-2026
@contact: florian.quintes@pc.uni.freiburg.de
-@author: Florian Quintes
-Bases: object
Acceptance test for the scipy.optimize.basinhopping algorithm.
-Lower bounds for the varied parameters.
-np.array
-Upper bounds for the varied parameters.
-np.array
-Object of class Variation from the epr_setup module.
-object
-Bases: object
Status callback for the scipy.optimize.basinhopping algorithm.
-Best objective-function value found so far.
-float
-Reference spin-system object.
-object
-Variation object containing the parameter ranges.
-object
-Controls whether status information is printed.
-bool, optional
-Lower bounds for the varied parameters.
-np.array
-Upper bounds for the varied parameters.
-np.array
-Bases: object
Step generator for the scipy.optimize.basinhopping algorithm.
-Relative size of the random step with respect to the variation range.
-float, optional
-NumPy random number generator.
-object
-Object of class Variation from the epr_setup module.
-object
-Parameter boundaries as a two-dimensional array.
-np.array
-Lower bounds.
-np.array
-Upper bounds.
-np.array
-Half the difference between lower and upper bounds.
-np.array
-Check whether the current parameter vector is within the bounds.
-Out-of-bound values are replaced by randomly generated values inside the bounds.
-x (GenericAlias[float64]) – Current guess.
x – Current guess.
-GenericAlias[float64]
Run scipy.optimize.basinhopping for global optimization.
-Sys (Any) – Reference spin-system object.
Exp (Any) – Experimental data object.
SimOpt (Any) – Simulation options object.
Var (Any) – Variation object describing the fitted parameters.
FitOpt (Any) – Fitting options object.
best_Sys – Best spin-system object found by the optimizer.
-Any
Run scipy.optimize.differential_evolution for global optimization.
-Sys (Any) – Reference spin-system object.
Exp (Any) – Experimental data object.
SimOpt (Any) – Simulation options object.
Var (Any) – Variation object describing the fitted parameters.
FitOpt (Any) – Fitting options object.
best_Sys – Best spin-system object found by the optimizer.
-Any
Run scipy.optimize.dual_annealing for global optimization.
-Sys (Any) – Reference spin-system object.
Exp (Any) – Experimental data object.
SimOpt (Any) – Simulation options object.
Var (Any) – Variation object describing the fitted parameters.
FitOpt (Any) – Fitting options object.
best_Sys – Best spin-system object found by the optimizer.
-Any
Generate a random initial guess within the variation boundaries.
-boundaries (Sequence[tuple[float, float]]) – Lower and upper bounds of the fitted parameters.
x0 – Randomly generated initial parameter vector.
-GenericAlias[float64]
Create a spin-system object from the current optimizer vector.
-x (GenericAlias[float64]) – Current parameter vector of the optimizer.
Sys (Any) – Reference spin-system object.
Var (Any) – Variation object.
SimOpt (Any) – Simulation options object.
Sys_mod – Spin-system object corresponding to the current optimizer vector.
-Any
Run scipy.optimize.least_squares for nonlinear optimization.
-Sys (Any) – Reference spin-system object.
Exp (Any) – Experimental data object.
SimOpt (Any) – Simulation options object.
Var (Any) – Variation object describing the fitted parameters.
FitOpt (Any) – Fitting options object.
best_Sys – Best spin-system object found by the optimizer.
-Any
Run scipy.optimize.minimize for local optimization.
-Minimize provides multiple local optimization routines such as Nelder-Mead, -COBYLA, Powell, CG and so on.
-Sys (Any) – Reference spin-system object.
Exp (Any) – Experimental data object.
SimOpt (Any) – Simulation options object.
Var (Any) – Variation object describing the fitted parameters.
FitOpt (Any) – Fitting options object.
Any
best_Sys (object) – Best spin-system object found by the optimizer.
results (str, optional) – Results of the scipy optimization. Only for the GUI.
Plot the current optimization state in the graphical user interface.
-xk (GenericAlias[float64]) – Current best guess vector.
*_ (arbitrary) – Unused arguments passed by some optimization routines to the callback -function.
Sys (Any) – Reference spin-system object.
Exp (Any) – Experimental data object.
SimOpt (Any) – Simulation options object.
Var (Any) – Variation object describing the fitted parameters.
FitOpt (Any) – Fitting options object.
*_kwargs (arbitrary) – Unused keyword arguments passed by some optimization routines to the -callback function.
ValueError – Raised if the selected simulation routine is unknown.
-None
Run scipy.optimize.shgo for global optimization.
-Sys (Any) – Reference spin-system object.
Exp (Any) – Experimental data object.
SimOpt (Any) – Simulation options object.
Var (Any) – Variation object describing the fitted parameters.
FitOpt (Any) – Fitting options object.
best_Sys – Best spin-system object found by the optimizer.
-Any
Objective function for the scipy.optimize interface.
-Used by scipy.optimize routines.
-x (GenericAlias[float64]) – Current parameter vector of the optimizer.
*objects (Any) – Additional objects required for the simulation and fitting interface.
ValueError – Raised if the selected simulation routine is unknown.
-error – Sum of squared residuals between experimental and simulated data.
-float
Residual function for scipy.optimize least-squares algorithms.
-Returns the absolute residuals between simulation and experiment.
-Used by scipy.optimize routines.
-x (GenericAlias[float64]) – Current parameter vector of the optimizer.
*objects (Any) – Additional objects required for the simulation and fitting interface.
ValueError – Raised if the selected simulation routine is unknown.
-error – One-dimensional array containing the absolute residuals.
-GenericAlias[float64]
Objective function for the scipy.optimize interface.
-Used by scipy.optimize.differential_evolution.
-x (GenericAlias[float64]) – Current parameter vector of the optimizer.
*objects (Any) – Additional objects required for the simulation and fitting interface.
ValueError – Raised if the selected simulation routine is unknown.
-error – Sum of squared residuals between experimental and simulated data.
-float
Thank you for contributing to spinanalysis. The project is a Python
-framework for loading, processing, plotting, simulating, optimizing, and
-saving electron paramagnetic resonance (EPR) spectra.
The main package modules are organized by workflow:
-core.py orchestrates simulation and optimization.
epr.py defines EPR parameter and workflow objects.
loading.py reads EPR data files.
processing.py transforms and reconstructs spectra.
plotting.py renders spectra and figures.
saving.py writes figures, simulated data, and output files.
profiles.py manages configuration and profile files.
_wrappers.py provides internal timing and multiprocessing wrappers.
_interface_handler.py provides internal simulation and optimization
-interfaces.
Profile templates and plotting styles are package data under
-src/spinanalysis/data/profiles. Changes to a profile schema should update
-the corresponding configspec.ini and relevant fixtures under
-tests/data.
The project requires Python 3.13 or newer. The repository uses uv for
-dependency and environment management.
Clone the repository and change into its directory:
-git clone https://github.com/florianquintes/spinanalysis.git
-cd spinanalysis
-Install the locked development environment from the repository root:
-uv sync --dev
-The package uses a src layout. Application code belongs under
-src/spinanalysis and tests belong under tests.
Create a focused branch for each change and keep unrelated changes separate. -The repository does not currently document a required branch naming scheme.
-Pull requests should explain the change and include the relevant validation -results. Before opening a pull request, run the checks that apply to the -change:
-uv run pytest
-uv run ruff check .
-uv build
-If documentation is changed, also build the documentation as described in the -Documentation section. Mention known baseline failures or warnings rather -than presenting them as regressions.
-Follow the existing Python and reStructuredText style in the surrounding
-files. Keep importable Python modules under src/spinanalysis and add tests
-under tests. The repository has no configured formatter or type checker.
Ruff is the configured code-quality tool, using its default rules:
-uv run ruff check .
-Documentation source files are under docs/source. The Sphinx configuration
-adds src to the module search path and reads the package version from
-pyproject.toml.
Build the documentation from the docs directory:
uv run sphinx-build -M html source build
-The generated HTML is written to docs/build/html. Add a title to every
-document included by the root index.rst so Sphinx can include it in the
-table of contents.
The project requires Python 3.13 or newer and uses uv for dependency and
-environment management. From the repository root, install the development
-environment with:
uv sync --dev
-Run the test suite from the repository root:
-uv run pytest
-Check the code with Ruff:
-uv run ruff check .
-The repository does not currently configure a separate formatter.
-Build the HTML documentation from the docs directory:
uv run sphinx-build -M html source build
-The generated documentation is written to docs/build/html.
| - | - |
| - | - |
| - | - |
| - | - |
| - | - |
| - | - |
| - | - |
| - | - |
| - | - |
| - | - |
| - | - |
| - | - |
| - | - |
| - | - |
| - | - |
| - | - |
spinanalysis is a high-level Python framework for the analysis of
-electron paramagnetic resonance (EPR) spectra.
The framework provides modules for loading, processing, plotting, simulating, -optimizing, and saving EPR spectra. It is designed to support complete -analysis workflows from experimental data to simulated and fitted spectra.
-Important
-The simulation routines for spin-correlated, singlet-born radical pairs, -out-of-phase ESEEM signals, and transiently coupled systems are currently -provided through external dependencies.
-Contents
-spinanalysis requires Python 3.13 or newer. uv or pip is required
-to install the package.
The following commands cover the common installation use cases:
-| - | uv |
-pip |
-
|---|---|---|
User |
-
|
-
|
-
Developer |
-
|
-
|
-
spinanalysis is a Python framework for the analysis of electron
-paramagnetic resonance (EPR) spectra. It is designed to support a complete
-workflow, from loading experimental data and applying common processing steps
-to visualizing, simulating, optimizing, and saving results.
The package supports common EPR data formats, including Bruker BES3T, -ESP380E, MATLAB, simulated, and plain-text data. Processing tools cover tasks -such as normalization, offset reduction, background correction, and -reconstruction of out-of-phase ESEEM spectra. Matplotlib-based plotting -functions provide consistent two-dimensional and three-dimensional figures -using configurable plot styles.
-Simulation and optimization workflows are organized around EPR parameter -objects and profile-based configuration. The profile system stores reusable -settings for spin systems, simulation and optimization routines, plotting, -and saving. Some specialized simulation routines are provided through -external dependencies and may require additional installation steps.
-The package is organized into modules that support different parts of an EPR -analysis workflow:
-Module |
-Description |
-
|---|---|
|
-Orchestrates simulation and optimization workflows. |
-
|
-Defines EPR parameter, experimental, simulation, and optimization -objects. |
-
|
-Loads EPR data from Bruker, ESP380E, MATLAB, simulated, and text files. |
-
|
-Normalizes, corrects, and reconstructs spectra. |
-
|
-Creates two-dimensional and three-dimensional spectrum plots. |
-
|
-Saves figures, simulated data, and simulation output files. |
-
|
-Creates, loads, saves, imports, and exports configuration profiles. |
-
|
-Provides internal timing and multiprocessing wrappers. |
-
|
-Provides internal interfaces for simulation and optimization routines. |
-
spinanalysis is licensed under the GNU General Public License v3.0 (GPLv3).
Important
-The GPLv3 allows commercial use. It does not prohibit others from selling
-software based on spinanalysis.
In summary, the GPLv3:
-allows use, modification, and redistribution;
requires modified or derived distributed software to remain licensed under -the GPLv3;
requires the corresponding source code to be made available when distributing -binaries;
permits commercial use, including selling GPLv3-licensed software;
provides no warranty.
The complete license text is available in the -LICENSE file.
-For more information, see the -official GPLv3 license text.
-| - s | ||
| - |
- spinanalysis | - |
| - |
- spinanalysis._interface_handler | - |
| - |
- spinanalysis._wrappers | - |
| - |
- spinanalysis.core | - |
| - |
- spinanalysis.epr | - |
| - |
- spinanalysis.loading | - |
| - |
- spinanalysis.plotting | - |
| - |
- spinanalysis.processing | - |
| - |
- spinanalysis.profiles | - |
| - |
- spinanalysis.saving | - |